Spaces:
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
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.
75011orLyonto 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,
vehicle consumption ADEME Car Labelling,
chargers IRVE via ODRE,
geocoding BAN, routing OSRM,
basemap IGN Plan v2.
Run locally
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:
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://orhttp://localhost. Opening the file directly (file://) shows the map but not the installable app.
Deploy to Hugging Face Spaces (Docker)
Create a new Space → SDK: Docker.
Push these files to the Space repo (the YAML header above configures it):
git init && git add . && git commit -m "Petrol map PWA" git remote add origin https://huggingface.co/spaces/<user>/<space> git push origin mainThe Space builds the
Dockerfile, serves on port7860, 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:
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 — 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).