Interactive elevation map of Seoul for cyclists and hikers. Visualize terrain, contour lines, and bike-friendly routes at a glance.
- 3D terrain β Exaggerated elevation with MapLibre GL terrain
- 3D buildings β OpenFreeMap extruded building footprints (toggleable, hidden by default)
- Contour lines β Vector contour lines via
maplibre-contour, with minor/major intervals and elevation labels - Hypsometric tinting β GPU-side
raster-colorshader for elevation-based color gradient
- Flat route overlays β Tanchen, Yangjae-cheon, Han River bike paths
- Slope arrows β Direction and steepness indicators on roads
- Caution zones β Highlighted steep areas (Daemo-san, Guryong-san, etc.)
- 175 metro stations β Lines 1β9, Sinbundang, Bundang, Gyeongui β with official line colors, rendered as GPU symbol layers
- Desktop β WASD panning (IME-safe
e.code), Space/Shift to ascend/descend zoom, right-click drag to rotate, middle-mouse drag to rotate + tilt - Mobile β Touch gestures, GPS location button
- Layer panel β Toggle terrain, 3D buildings, contours, stations, routes, slope arrows
- Contour opacity slider β Adjustable contour line intensity
| Library | Purpose |
|---|---|
| MapLibre GL JS 4.7.1 | Map renderer + GPU terrain/tiling |
| maplibre-contour 0.0.7 | Vector contour line generation from DEM |
| CARTO Voyager | Base map tiles |
| OpenFreeMap | 3D building footprints |
| AWS Terrarium DEM | Elevation tiles (Terrarium encoding) |
| OpenMapTiles Fonts | Glyphs for contour labels and station names |
| Service Worker | Offline tile caching, network-first for app shell |
| GA4 | Anonymous usage analytics |
Single index.html (~1100 lines). No build step, no bundler.
Contour lines are vector tiles generated client-side by maplibre-contour:
DEM tiles (Terrarium PNG, AWS S3)
β DemSource (maplibre-contour, maxzoom 13, worker thread)
β Vector MVT via custom protocol handler
β MapLibre symbol/line layers (contour-lines, contour-lines-major, contour-labels)
Zoom-dependent intervals:
| Zoom | Minor | Major |
|---|---|---|
| 11 | β | 50 m |
| 12 | 20 m | 100 m |
| 13β15 | 10 m | 50 m |
- Protocol handler registered before
map = new Map(...)(required for MapLibre GL 4.x) - Terrain DEM source added before
elev-colorsource inmap.on('load')to avoid silent protocol failures raster-color/raster-color-rangewrapped intry/catch(MapLibre GL 4.7.1 validation throws but is non-fatal)- Service Worker:
CACHE_NAME = 'seoul-elevation-map-v13', network-first forindex.html
# Open directly in browser
open index.html
# Or serve locally
python3 -m http.server 8080Runtime perf telemetry is exposed in-browser:
window.__seoulMapPerf.metrics
window.__seoulMapPerf.report()Enable full runtime perf instrumentation with ?perf=1:
http://127.0.0.1:8080/?perf=1
| Input | Desktop | Mobile |
|---|---|---|
| Pan | Drag / WASD | One-finger drag |
| Ascend / Descend | Space / Shift | β |
| Rotate | Right-click drag | Two-finger rotate |
| Tilt | Middle-mouse drag | Two-finger tilt |
| Zoom | Scroll wheel | Pinch |
| Location | β | π button |
| Reset view | βΊ button | βΊ button |
| 3D toggle | ποΈ button | ποΈ button |
| Layers | πΊοΈ button | πΊοΈ button |
| Legend | π button | π button |
MIT
Made with π΄ for Seoul cyclists.