Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,16 @@ PUMPERLY_DEFAULT_COUNTRY=ES

# Set to 0 to disable EV charger scraping (default: enabled when OCM key is set)
# PUMPERLY_EV_ENABLED=1

# Germany EV chargers: which of the two sources supplies them. Defaults to the
# BNetzA Ladesäulenregister — the official register every operator of a public
# charge point must file into, ~74,000 locations, no API key and no signup.
# Set to "ocm" to keep OpenChargeMap instead. The two never run together: the
# BNetzA scraper retires OpenChargeMap's German rows on its first healthy run,
# and running both would double-pin the country.
# PUMPERLY_DE_EV_SOURCE=bnetza

# Stations a run must refresh before the BNetzA scraper is allowed to prune
# stale rows or retire OpenChargeMap's German ones. A safety floor: a degraded
# fetch must not be able to delete a working map.
# PUMPERLY_BNETZA_MIN_STATIONS=10000
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Pumperly combines route planning with real-time fuel prices and EV charging stat

- **Route planning** — Geocoding via [Photon](https://github.com/komoot/photon), routing via [Valhalla](https://github.com/valhalla/valhalla), with alternative routes
- **Real-time fuel prices** — From government open data APIs and community sources
- **EV charging stations** — Via [Open Charge Map](https://openchargemap.org) across all supported countries, and the official [Mapa REVE](https://www.mapareve.es) registry in Spain
- **EV charging stations** — Via [Open Charge Map](https://openchargemap.org) across all supported countries, plus the official [Mapa REVE](https://www.mapareve.es) registry in Spain and the [BNetzA Ladesäulenregister](https://www.bundesnetzagentur.de/DE/Fachthemen/ElektrizitaetundGas/E-Mobilitaet/Ladesaeulenkarte/start.html) in Germany
Comment thread
yellowhat marked this conversation as resolved.
- **Detour calculation** — Each station shows estimated detour time from your route
- **"Cheapest within N min"** — Slider filters stations by maximum detour, highlights the best deal
- **Corridor station list** — Sorted by position along route, with price deltas vs average
Expand Down Expand Up @@ -98,9 +98,12 @@ Pumperly combines route planning with real-time fuel prices and EV charging stat
|---|---|---|
| [Open Charge Map](https://openchargemap.org) | All supported countries + United States (EV-only) | ODbL |
| [Mapa REVE](https://www.mapareve.es) (Red Eléctrica de España) | Spain — official operator-reported registry | Non-commercial, attribution required |
| [Ladesäulenregister](https://www.bundesnetzagentur.de/DE/Fachthemen/ElektrizitaetundGas/E-Mobilitaet/Ladesaeulenkarte/start.html) (Bundesnetzagentur) | Germany — official operator-reported registry, ~74,000 locations | CC BY 4.0, attribution "Bundesnetzagentur.de" |

Spain uses Mapa REVE when `PUMPERLY_REVE_API_KEY` is set: it is the registry every Spanish charge point operator files into, so it is authoritative where Open Charge Map is crowdsourced. The two overlap heavily, so Open Charge Map stops being scraped for Spain immediately, its existing Spanish rows stay visible while REVE backfills, and they are deleted once REVE reaches 95% of the registry. Expect duplicate Spanish pins until then.

Germany uses the BNetzA Ladesäulenregister by default — no API key, no signup. Every operator of a publicly accessible charge point must file into it under §5 Ladesäulenverordnung, so like REVE it is authoritative where Open Charge Map is crowdsourced, and it is several times larger. It arrives as one daily bulk file, so there is no backfill period: Open Charge Map stops being scraped for Germany, and its existing German rows are retired on the first healthy run. Set `PUMPERLY_DE_EV_SOURCE=ocm` to keep Open Charge Map instead.

### Map & routing

| Service | Purpose | License |
Expand Down Expand Up @@ -413,6 +416,8 @@ volumes:
| `PUMPERLY_PRICE_MIN` / `PUMPERLY_PRICE_MAX` | Price bounds for scraper validation (EUR/L) | `0.30` / `4.00` |
| `PUMPERLY_SCRAPE_INTERVAL_HOURS` | Global scrape interval override (hours, 0=disable) | Per-country |
| `PUMPERLY_EV_ENABLED` | Enable EV charger scraping (`0` to disable) | `1` |
| `PUMPERLY_DE_EV_SOURCE` | Germany's EV source: `bnetza` (official registry) or `ocm` | `bnetza` |
| `PUMPERLY_BNETZA_MIN_STATIONS` | Stations a run must refresh before the BNetzA scraper prunes stale rows or retires Open Charge Map's German ones | `10000` |
| `VALHALLA_URL` | Valhalla routing endpoint | — |
| `PHOTON_URL` | Photon geocoding endpoint | — |

Expand Down
1 change: 1 addition & 0 deletions src/components/nav/legal-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ function SourcesContent() {
<ul>
<li><strong>Open Charge Map</strong> &mdash; EV charging station locations across all supported countries. Community-maintained, Open Data Commons Open Database License (ODbL). <a href="https://openchargemap.org" target="_blank" rel="noopener noreferrer">openchargemap.org</a></li>
<li><strong>Mapa REVE</strong> &mdash; EV charging station locations in Spain. Source: Red El&eacute;ctrica de Espa&ntilde;a, S.A.U. Used non-commercially and reproduced without alteration. <a href="https://www.mapareve.es" target="_blank" rel="noopener noreferrer">mapareve.es</a></li>
<li><strong>Ladesäulenregister</strong> &mdash; EV charging station locations in Germany, from the register operators must file under &sect;5 Ladesäulenverordnung. Source: Bundesnetzagentur.de, Creative Commons Attribution 4.0 (CC BY 4.0). <a href="https://www.bundesnetzagentur.de/DE/Fachthemen/ElektrizitaetundGas/E-Mobilitaet/Ladesaeulenkarte/start.html" target="_blank" rel="noopener noreferrer">bundesnetzagentur.de</a></li>
Comment thread
coderabbitai[bot] marked this conversation as resolved.
</ul>

<h3>Map and routing</h3>
Expand Down
15 changes: 15 additions & 0 deletions src/instrumentation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ const DEFAULT_INTERVALS: Record<string, number> = {
// Spain Mapa REVE — the API allows only 5 requests/hour, so this crawls a
// few pages at a time and must run hourly to get through the registry.
EV_ES_REVE: 1,
// Germany BNetzA Ladesäulenregister — one bulk TSV, regenerated daily.
EV_DE_BNETZA: 24,
};

export async function register() {
Expand Down Expand Up @@ -105,6 +107,8 @@ export async function register() {
const { OCMScraper } = await import("./scrapers/ocm");
const { REVEScraper } = await import("./scrapers/reve");
const { resolveSpainEvSource } = await import("./scrapers/spain-ev-source");
const { BNetzAScraper } = await import("./scrapers/bnetza");
const { resolveGermanyEvSource } = await import("./scrapers/germany-ev-source");
const { StaticScraper } = await import("./scrapers/static");
const { STATIC_DATASETS } = await import("./scrapers/data");

Expand Down Expand Up @@ -187,6 +191,8 @@ export async function register() {
EV_US: () => new OCMScraper("US"),
// Spain's official EV registry — supersedes EV_ES when a key is set (#121)
EV_ES_REVE: () => new REVEScraper(),
// Germany's official EV registry — supersedes EV_DE by default, keyless
EV_DE_BNETZA: () => new BNetzAScraper(),
};

// Register community-contributed static datasets (see scrapers/data/README.md).
Expand Down Expand Up @@ -239,6 +245,15 @@ export async function register() {
);
}

// Germany likewise — see scrapers/germany-ev-source.ts, and scrapers/bnetza.ts
// for the sweep that retires the OpenChargeMap rows once BNetzA has landed.
countries = resolveGermanyEvSource(countries);
if (countries.includes("EV_DE_BNETZA")) {
console.log(
"[scraper] Germany EV: using the BNetzA Ladesäulenregister instead of OpenChargeMap",
);
}

// Resolve per-country intervals
for (const code of countries) {
// Priority: PUMPERLY_SCRAPE_INTERVAL_XX > PUMPERLY_SCRAPE_INTERVAL_HOURS > DEFAULT_INTERVALS
Expand Down
Loading
Loading