Spaces:
Sleeping
Sleeping
| title: Prix des Carburants France | |
| emoji: ⛽ | |
| colorFrom: green | |
| colorTo: red | |
| sdk: docker | |
| app_port: 7860 | |
| pinned: false | |
| license: apache-2.0 | |
| # ⛽ Petrol Map — live French fuel prices (PWA) | |
| An installable, mobile-friendly map of every petrol station in France, | |
| colour-coded by price and refreshed twice a day from the official open-data feed. | |
| **Data source:** [`prix_des_carburants_j_7`](https://public.opendatasoft.com/explore/dataset/prix_des_carburants_j_7) | |
| on opendatasoft (the last 7 days of prices reported by ~9 800 stations). | |
| ## Features | |
| - **Installable PWA** — add it to your phone's home screen and launch it like a | |
| native app; works offline (shows the last cached prices) via a service worker. | |
| - **Responsive** — full-screen map with touch-friendly controls and safe-area | |
| handling for notched phones. | |
| - **One layer per fuel** — Gazole, SP95, SP98, E10, E85, GPLc — via the selector. | |
| - **Price colour scale** — green = cheaper, red = pricier, scaled to each fuel's | |
| national 5th–95th percentile; the legend updates per fuel. | |
| - **Search by postal code / city** — type e.g. `75011` or `Lyon` to filter the | |
| map to that area and zoom to it. | |
| - **Itinerary maker** — enter a *ville de départ* and *ville d'arrivée* (with city | |
| autocomplete via the Base Adresse Nationale); the route is drawn (OSRM) and the | |
| map shows only the stations along the way, flagging the cheapest for the fuel. | |
| - **Real vehicle picker + journey cost** — choose an actual model (≈550 from the | |
| ADEME database, with their exact WLTP L/100 km, hybrids included) to estimate the | |
| trip's fuel cost from the cheapest along-route price. | |
| - **Electric vehicles** — pick an EV (WLTP range), see the number of recharge stops | |
| needed and energy used, with stops placed at real chargers along the route. | |
| - **EV charging stations** — toggle the national IRVE layer (≈224 000 points, loaded | |
| live by viewport), colour-coded by power (fast ≥ 50 kW). | |
| - **Marker clustering** keeps ~10 000 stations smooth; zoom in to split clusters. | |
| - **Click a station** for all prices, address, last-reported date, and a | |
| one-click *Directions* link, plus hover tooltips. | |
| Data sources: fuel prices [`prix_des_carburants_j_7`](https://public.opendatasoft.com/explore/dataset/prix_des_carburants_j_7), | |
| vehicle consumption [ADEME Car Labelling](https://data.ademe.fr/datasets/ademe-car-labelling), | |
| chargers [IRVE via ODRE](https://odre.opendatasoft.com/explore/dataset/bornes-irve), | |
| geocoding [BAN](https://adresse.data.gouv.fr/), routing [OSRM](https://project-osrm.org/), | |
| basemap [IGN Plan v2](https://geoservices.ign.fr/). | |
| ## Run locally | |
| ```bash | |
| pip install -r requirements.txt | |
| python app.py # builds, then serves http://localhost:7860 | |
| ``` | |
| Open <http://localhost:7860> — on a phone (same network or behind HTTPS) you can | |
| then "Add to Home Screen" to install it. | |
| Other entrypoints: | |
| ```bash | |
| python build_map.py # build output/index.html once, no server | |
| python scheduler.py # rebuild twice daily without serving (for static hosting) | |
| python scheduler.py --once | |
| ``` | |
| > **PWA note:** install/offline features need a secure context — i.e. `https://` | |
| > or `http://localhost`. Opening the file directly (`file://`) shows the map but | |
| > not the installable app. | |
| ## Deploy to Hugging Face Spaces (Docker) | |
| 1. Create a new Space → **SDK: Docker**. | |
| 2. Push these files to the Space repo (the YAML header above configures it): | |
| ```bash | |
| git init && git add . && git commit -m "Petrol map PWA" | |
| git remote add origin https://huggingface.co/spaces/<user>/<space> | |
| git push origin main | |
| ``` | |
| 3. The Space builds the `Dockerfile`, serves on port `7860`, and rebuilds the | |
| prices twice a day. The public Space URL is HTTPS, so the PWA is installable | |
| straight from your phone. | |
| Run the same container anywhere else: | |
| ```bash | |
| docker build -t petrol-map . | |
| docker run -p 7860:7860 petrol-map # http://localhost:7860 | |
| ``` | |
| ## Files | |
| **Backend (Python, build/serve):** | |
| | File | Purpose | | |
| |------|---------| | |
| | `fuel_data.py` | Fetch + normalise fuel prices (drops implausible values) | | |
| | `car_data.py` | Fetch real vehicle models + WLTP L/100 km from ADEME | | |
| | `build_map.py` | Inject the dataset into the HTML shell, copy static assets | | |
| | `app.py` | Build + serve on `$PORT` + twice-daily refresh (container entrypoint) | | |
| | `scheduler.py` | Twice-daily rebuild without serving | | |
| | `generate_icons.py` | One-off PWA icon generator (needs Pillow; not used at runtime) | | |
| | `Dockerfile` | Container image for Hugging Face Spaces / anywhere | | |
| **Frontend (served from `static/`, separated for clean architecture):** | |
| | File | Purpose | | |
| |------|---------| | |
| | `map_template.html` | Slim HTML shell; injects the build's data as `window.APP_DATA` | | |
| | `static/app.js` | All map logic (one IIFE, sectioned by concern) | | |
| | `static/styles.css` | All styles | | |
| | `static/sw.js` | Service worker (offline + installable) | | |
| | `static/manifest.webmanifest`, icons | PWA metadata | | |
| | `output/` | Generated site (`index.html` + copied static assets + `stations.json`) | | |
| ## How the refresh works | |
| The upstream dataset is updated continuously by the French administration. | |
| `app.py` re-fetches the full feed and regenerates the page twice daily (07:00 and | |
| 19:00, container local time — set `TZ`), so the map always reflects a recent | |
| snapshot. Change `REFRESH_TIMES` to refresh on a different cadence. | |
| ## License | |
| Licensed under the [Apache License, Version 2.0](LICENSE) — see the `LICENSE` | |
| file for the full text. © 2026 Samuel Adone. | |
| Open data used by this project remains under its own licences (Licence Ouverte / | |
| Etalab for the French government feeds). | |