cd /news/developer-tools/how-i-built-an-interactive-atlas-of-… · home topics developer-tools article
[ARTICLE · art-59312] src=dev.to ↗ pub= topic=developer-tools verified=true sentiment=↑ positive

How I built an interactive atlas of 767 train routes (React + MapLibre + open data)

A developer built TrainRouter, a free interactive map of 767 notable train routes across 118 countries using React, MapLibre GL JS, and open data. The project prerenders ~1,500 static HTML pages for SEO and AI crawler discoverability, and includes features like centripetal Catmull-Rom spline smoothing and content-hashed lastmod dates. The underlying route data is published as open data on GitHub.

read3 min views1 publishedJul 14, 2026

I spent a summer building TrainRouter — a free, no-signup interactive map of 767 of the world's notable train routes across 118 countries, from the Glacier Express and the Trans-Siberian to the new night trains stitching Europe back together. Every line carries its distance, fastest journey time, top speed, operator, rolling stock and a short story.

It's a solo project, and the interesting engineering wasn't the map — it was everything around it: keeping 700+ routes fast, making a JavaScript map discoverable by search engines and AI crawlers, and turning the whole thing into open data. Here's how it went together.

Everything derives from one flat list of routes. Each record is deliberately boring:

id, name, from, to, category, train, operator,
distance_km, top_speed_kmh, duration, opened,
pax_per_year, countries_iso, highlight, fame_rank

category

is one of high-speed · classic · night · scenic

, which drives the colour-coding. fame_rank

(1 = most famous) is a hand-curated renown ordering — it decides what you see first when the map is zoomed out, so the Shinkansen and the Orient Express surface before a regional branch line.

The geometry is separate: a hand-traced LineString

of lon/lat waypoints per route. I smooth those with a centripetal Catmull-Rom spline rather than a plain Catmull-Rom — the centripetal variant never overshoots hairpins like the Glacier Express' Chur reversal, which a naive spline turns into a loop.

The map is MapLibre GL JS on ** OpenFreeMap** vector tiles — a free, no-API-key, OpenStreetMap-based tile host. That combination means the whole map costs nothing to run and has no per-tile billing surprise.

Two things that mattered for making 700+ overlapping lines readable:

A single-page app is invisible to crawlers that don't run JavaScript — which includes most AI crawlers. My homepage was a <div id="root"></div>

and nothing else.

So a build step prerenders ~1,500 static HTML pages — one per route, plus country hubs, city-pair pages, and index hubs — each with unique titles/descriptions, canonical URLs, Open Graph + Twitter cards, and JSON-LD (TouristTrip

, BreadcrumbList

, FAQPage

, ItemList

). The homepage #root

gets a static atlas index injected at build time; createRoot().render()

replaces it the moment the interactive map hydrates, so users get the app and crawlers get real content.

One subtlety worth stealing: don't stamp every page's lastmod with the build date. That teaches Google your sitemap is noise. Instead I hash each page's content and only bump

lastmod

when the content actually changes.Once the atlas is structured data, you can ask it questions. My favourite: ** the fastest train in every country** — ranked not by headline top speed but by the speed each country's quickest train

The results are counter-intuitive: China's Beijing–Shanghai averages 307 km/h over ~1,300 km; the Shanghai Maglev tops 431 km/h but averages just 225 as a 30 km airport hop; and the US's fastest train, the Acela, averages 109 km/h — behind 33 other countries. The page and its chart are generated from the same module that feeds the map, so it updates itself as the atlas grows.

Two things I'd recommend to anyone building a data-driven site:

The map is at ** trainrouter.com** and the data is at

── more in #developer-tools 4 stories · sorted by recency
── more on @trainrouter 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/how-i-built-an-inter…] indexed:0 read:3min 2026-07-14 ·