prix-carburant / README.md
SamDNX's picture
Deeper SEO, Apache-2.0 metadata, full ADEME car scrape, chargers when zoomed out
4ccb3e1
|
Raw
History Blame Contribute Delete
5.67 kB
---
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).