gridloc / scripts /build-roads.mjs
lassanmonster's picture
GridLoc: Bengaluru parking congestion digital twin
db3dbe1
Raw
History Blame Contribute Delete
2.88 kB
// 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.`)