Spaces:
Sleeping
Sleeping
| // Fetch real Bengaluru roads (primary/secondary/trunk/tertiary) ONCE from the | |
| // OpenStreetMap Overpass API and cache them as a static GeoJSON FeatureCollection | |
| // of LineStrings, so the runtime never depends on the flaky public Overpass | |
| // instance. Writes to public/ (fetchable) and src/data/ (bundled import). | |
| // | |
| // Run: node scripts/build-roads.mjs | |
| import { writeFileSync, mkdirSync } from 'fs' | |
| const OVERPASS_ENDPOINTS = [ | |
| 'https://overpass-api.de/api/interpreter', | |
| 'https://overpass.kumi.systems/api/interpreter', | |
| 'https://maps.mail.ru/osm/tools/overpass/api/interpreter', | |
| ] | |
| // Bengaluru bbox (south, west, north, east). | |
| const QUERY = | |
| 'data=[out:json][timeout:60];way["highway"~"primary|secondary|trunk|tertiary"](12.85,77.48,13.08,77.72);out geom;' | |
| function widthForHighway(highway) { | |
| switch (highway) { | |
| case 'trunk': | |
| case 'primary': | |
| return 14 | |
| case 'secondary': | |
| return 9 | |
| case 'tertiary': | |
| return 8 | |
| default: | |
| return 8 | |
| } | |
| } | |
| async function fetchRoads() { | |
| let lastErr | |
| for (const url of OVERPASS_ENDPOINTS) { | |
| try { | |
| console.log(`Fetching roads from ${url} ...`) | |
| const res = await fetch(url, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, | |
| body: QUERY, | |
| }) | |
| console.log(` → HTTP ${res.status}`) | |
| if (!res.ok) { | |
| lastErr = new Error(`Overpass HTTP ${res.status} @ ${url}`) | |
| continue | |
| } | |
| const json = await res.json() | |
| const ways = (json.elements ?? []).filter( | |
| (w) => w.type === 'way' && Array.isArray(w.geometry) && w.geometry.length >= 2, | |
| ) | |
| console.log(` → ${ways.length} ways returned`) | |
| return ways | |
| } catch (err) { | |
| console.warn(` → failed: ${err.message}`) | |
| lastErr = err | |
| } | |
| } | |
| throw lastErr ?? new Error('All Overpass endpoints failed') | |
| } | |
| const ways = await fetchRoads() | |
| const features = ways.map((way) => { | |
| const highway = way.tags?.highway ?? 'road' | |
| return { | |
| type: 'Feature', | |
| properties: { | |
| id: way.id, | |
| name: way.tags?.name ?? way.tags?.ref ?? 'Unnamed road', | |
| highway, | |
| widthMeters: widthForHighway(highway), | |
| }, | |
| geometry: { | |
| type: 'LineString', | |
| // GeoJSON order is [lng, lat]. | |
| coordinates: way.geometry.map((p) => [p.lon, p.lat]), | |
| }, | |
| } | |
| }) | |
| const fc = { type: 'FeatureCollection', features } | |
| // Sanity print: first 3 segments' first coordinate. | |
| for (let i = 0; i < Math.min(3, features.length); i++) { | |
| console.log( | |
| `sample ${i}: ${features[i].properties.name} first=[${features[i].geometry.coordinates[0].join(', ')}]`, | |
| ) | |
| } | |
| mkdirSync('public', { recursive: true }) | |
| writeFileSync('public/bengaluru-roads.geojson', JSON.stringify(fc)) | |
| writeFileSync('src/data/bengaluru-roads.geojson', JSON.stringify(fc)) | |
| console.log(`Wrote ${features.length} road features.`) | |