Real-time vehicle tracker for De Lijn (Belgian public transport) displayed on a dark OpenStreetMap layer, served entirely via a Cloudflare Worker.
- 🗺 Dark CartoDB map with live vehicle markers (bearing arrows + route colours)
- 🚌 Decodes GTFS-Realtime protobuf on the edge — no client-side binary parsing
- ⏱ Auto-refreshes every 15 seconds with a progress bar
- 📊 Side panel: vehicle count, trip count, on-time vs delayed stats
- 🔍 Click any marker or list item to zoom in and inspect delay / speed / bearing
- 🔑 API key stored as a Cloudflare Secret — never exposed to the browser
Browser ──GET /──────────────────▶ Cloudflare Worker ──serves──▶ public/index.html
Browser ──GET /api/gtfs ──────────▶ Worker (src/worker.js)
│
├─ adds Ocp-Apim-Subscription-Key header
├─ fetches binary protobuf from De Lijn API
├─ decodes with gtfs-realtime-bindings
└─ returns clean JSON to browser
- Node.js ≥ 18
- Wrangler CLI v3+
- A Cloudflare account (free tier works)
- A De Lijn API subscription key (request here)
git clone https://github.com/your-org/dl-gtfs-rt-map.git
cd dl-gtfs-rt-map
npm installnpm run secret
# Paste your Ocp-Apim-Subscription-Key when promptedThis stores it as DL_GTFSRT — it is never committed to source control.
npm run devOpen http://localhost:8787.
Tip: For local dev you can put your key in a
.dev.varsfile (already git-ignored):DL_GTFSRT=your_key_here
npm run deployYour worker will be live at https://dl-gtfs-rt-map.<your-subdomain>.workers.dev.
dl-gtfs-rt-map/
├── src/
│ └── worker.js # Cloudflare Worker — API proxy + protobuf decoder
├── public/
│ └── index.html # Single-page map UI (Leaflet + vanilla JS)
├── wrangler.toml # Worker configuration
├── package.json
└── .gitignore
GET /api/gtfs — proxies the De Lijn GTFS-RT feed and returns decoded JSON:
{
"header": { "gtfsRealtimeVersion": "2.0", "timestamp": 1234567890 },
"entity": [
{
"id": "...",
"vehicle": {
"trip": { "tripId": "2026-02-25_2850_181", "routeId": "2850" },
"position": { "latitude": 51.05, "longitude": 3.72, "bearing": 270, "speed": 12.5 },
"vehicle": { "id": "8622", "label": "8622" }
}
}
]
}| Variable | Where | Description |
|---|---|---|
DL_GTFSRT |
Cloudflare Secret | Ocp-Apim-Subscription-Key value |
MIT