Map Data & Risk
Map Data & Risk
Everything that layers on top of the base map: the map configuration manifest, category marker styles, icon sprites, vector/GeoJSON overlays, weather, and route-risk scoring that fuses traffic, weather, incidents, logistics zones, and enforcement assets.
Scope required: tiles for configuration, overlays, and weather; routing for the route-risk/enforcement POST endpoints.
[lng, lat]; bbox is west,south,east,north. Heavy overlays support a bbox/zoom/limit filter and a tile-cover planner for bounded loading. Most endpoints degrade gracefully (empty FeatureCollection with hasData:false) rather than erroring on bad input.GET /map/config
The full first-party map configuration manifest: base styles, overlays, categories, icons, weather, 3D, logistics zones, and a capabilities block describing public endpoints and performance limits. Fetch this once at startup to discover what's available and how to load it.
Top-level keys: layers, baseStyles, overlays, categories, icons, iconSpriteUrl, iconSpriteManifestUrl, categoryStyleManifest, threeD, logisticsZones, capabilities.
{
"baseStyles": [ { "key": "standard", "name": "Navvo", "kind": "base", "endpoint": "/api/v1/tiles/style?style=standard" } ],
"overlays": [ { "key": "traffic", "kind": "overlay", "layerType": "traffic", "tileUrl": "/api/v1/traffic/lines/tiles/{z}/{x}/{y}" } ],
"iconSpriteUrl": "/api/v1/map/icons/sprite.svg",
"capabilities": {
"publicEndpoints": { "config": "/api/v1/map/config", "routeRisk": "/api/v1/map/route-risk" },
"performance": { "defaultTileCoverCap": 64, "maxTileCoverCap": 256, "tileAddressedOverlays": true },
"vectorOverlays": { "enabled": true, "format": "mvt" }
}
}
GET /map/layers
A flat catalog of all configured layers: { items, baseStyles, overlays, total, requiredOverlays }. requiredOverlays lists the overlay layer types Navvo expects (e.g. traffic, transit, incidents, weather, logistics_zones).
GET /map/category-styles
Renderer-ready styling for place categories — marker colors, pin styles, icons, multilingual names, and aliases. Use it to draw category-colored markers that match Navvo's own map.
{
"version": 1,
"iconSpriteUrl": "/api/v1/map/icons/sprite.svg",
"categories": [
{
"slug": "restaurants",
"name": { "en": "Restaurants", "ar": "مطاعم" },
"color": "#F97316",
"pinStyle": "teardrop",
"zoomRange": { "min": 11, "max": 22 },
"icon": { "name": "utensils", "href": "/api/v1/map/icons/sprite.svg#navvo-icon-utensils" }
}
]
}
GET /map/icons/sprite.svg & sprite.json
sprite.svg returns an SVG symbol sprite (image/svg+xml) of all active icons (<symbol id="navvo-icon-…">). sprite.json returns the manifest describing each icon (name, id, href, size).
GET /map/readiness
A self-audit of the map surface — a list of boolean checks plus a summary (counts of base styles, overlays, categories, icons). Useful for diagnostics.
GET /map/poi
Decides which places a viewport should actually show. Given a bounding box and a zoom, it returns a priority-ordered, label-budgeted set of POIs — not simply the nearest N rows.
Scope required: places
curl -s "https://navvo.io/api/v1/map/poi?bbox=35.85,31.93,35.97,32.00&zoom=15" \
-H "x-api-key: nvvo_YOUR_KEY"
Query parameters
west,south,east,north in degrees. Required — without it the response is { "error": "bbox required as west,south,east,north", "items": [], "clusters": [] } with HTTP 200.0–22. Defaults to 14. This is the main control: it selects the label budget, the priority floor and the icon-only threshold.hospital,pharmacy). Omit for all categories.en (default) or ar — selects which name is used for label-collision measurement.::::field{name="vw" / name="vh" type="number"}
Viewport width/height in CSS pixels (200–8000, defaults 1440×900). Used to size the label budget to the real screen.
::::
1–4. Defaults to 1.0.3–1 (default 1). Lower values shrink the budget for weaker devices.Response
{
"zoom": 15,
"budget": 180,
"minPriority": 35,
"profile": { "clusterRadius": 0, "labelSpacing": 12, "iconOnlyBelow": 40 },
"items": [
{
"id": "98506109-374d-4810-a148-e0b7add768ac",
"slug": "telescope-medical-center-…-fad9f9",
"name": "Telescope Medical Center",
"nameEn": "Telescope Medical Center",
"nameAr": null,
"canonicalCategorySlug": "medical-center",
"categories": ["medical-center"],
"priority": 73,
"rating": 4.9,
"reviewCount": 2594,
"lng": 35.867637,
"lat": 31.960408,
"labelHidden": false,
"iconLightUrl": "https://navvo.io/uploads/category-icons/medical-center-light.svg",
"iconDarkUrl": "https://navvo.io/uploads/category-icons/medical-center-dark.svg"
}
],
"clusters": [],
"truncated": false
}
items is already trimmed to it.true means draw the icon without its text label: the place earned a pin but lost the label-collision contest at this zoom. Honour it or your map will overlap.labelHidden.true when more places qualified than the budget allowed — zoom in for the rest.items straight into your renderer as symbol data and let the GPU do collision on top; the budget and labelHidden flags are there so you do not have to run your own server-side collision pass. Responses are cacheable for ~20 s with a 120 s stale-while-revalidate window.Overlays
Generic overlays serve stored map features as GeoJSON, vector tiles, or raster. Known layer types include traffic, transit, biking, terrain, incidents, speed_cameras, traffic_cameras, traffic_lights, weather, and logistics_zones.
GET /map/overlays/{layerType}
GeoJSON FeatureCollection for one overlay. Accepts bbox, limit, zoom.
{
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "geometry": { "type": "Point", "coordinates": [35.91, 31.95] },
"properties": { "id": 12, "name": "Amman central speed camera", "layerType": "speed_cameras", "color": "#B91C1C" } }
],
"metadata": { "layerType": "speed_cameras", "name": "Speed cameras", "hasData": true }
}
biking (cycling network) and transit return live, viewport-driven network data (cached per area). For biking, each LineString/MultiLineString carries network (protected | shared | trail), surface, lit, oneway, and color. For transit, features carry kind (line | stop), mode (bus | metro | tram | rail), color, and (for lines) routeRef, lineName, network. Pass a bbox to scope the request.Tiled overlay access
| Endpoint | Returns |
|---|---|
GET /map/overlays/{layerType}/tile-cover | Tile URLs covering a bbox (bbox, zoom, maxTiles). |
GET /map/overlays/{layerType}/tiles/{z}/{x}/{y} | One tile as GeoJSON. |
GET /map/overlays/{layerType}/vector/{z}/{x}/{y} | One tile as a binary vector tile (application/x-protobuf, layer navvo). |
GET /map/logistics-zones.geojson
Logistics / SLA zone polygons as GeoJSON. Each feature carries zoneType, riskScore, costMultiplier, and routingPolicy — the same zones surfaced in /routing/plan results.
Weather
Weather overlays are stored polygons plus a live point lookup. type ∈ precipitation, temperature, clouds, storms, alerts; time ∈ -1h, now, +1h, +3h.
| Endpoint | Returns |
|---|---|
GET /map/weather/geojson | Weather polygons as GeoJSON (filters: type, time, bbox, limit, zoom). |
GET /map/weather/geojson/tiles/{z}/{x}/{y} | Weather GeoJSON for one tile. |
GET /map/weather/vector/{z}/{x}/{y} | Weather as a binary MVT (layer navvo_weather). |
GET /map/weather/tiles/{z}/{x}/{y} | A rendered SVG raster weather tile (image/svg+xml). |
GET /map/weather/tile-cover | Tile URLs covering a bbox. |
GET /map/weather-risk | Aggregate weather-risk preview (score, severity, warnings). |
Weather icons
Every weather payload carries a ready-to-render icon, so a native app shows the same glyph the web map shows without reimplementing the condition mapping:
| Field | Type | Notes |
|---|---|---|
icon | string | Art slug — the file stem. See the list below. |
iconLightUrl | string | Absolute URL to the SVG drawn for light interfaces. |
iconDarkUrl | string | Absolute URL to the SVG drawn for dark interfaces. |
Slugs: clear, clear-night, partly, cloud, rain, storm, snow, fog for conditions, and
heat, cold, wind for derived alerts.
https://navvo.io/uploads/weather-icons/{slug}-{light|dark}.svg
- 24 × 24 SVG, no external references, so they can be cached, tinted, or rasterised at any size.
- The URLs are absolute because the files are served from the site root, not from under
/api/v1— resolving a bare path against the API prefix would 404. - Pick the variant by your interface theme, not by day/night. Day and night are already carried
by the slug: a clear sky returns
clearby day andclear-nightafter dark. iconmay be more specific than the condition text:partlyis returned for fair and partly-cloudy skies, which both read as "cloud" at the coarsest level.- Treat the slug list as open. New families may be added; fall back to
cloudon an unknown value rather than failing. - Weather overlay polygons (
/map/weather/geojson) carry icons once onmetadata.icons, keyed by weather type, rather than repeating two URLs on every one of thousands of cells. The binary MVT and rendered SVG tile endpoints carry no icons — they are already pixels.
GET /map/weather/point
Current point weather (from Navvo's weather service).
lon also accepted).metric (default) or imperial.{
"ok": true, "lat": 31.95, "lon": 35.91, "city": "Amman",
"temperature": 28, "unit": "C", "feelsLike": 30, "humidity": 35,
"windSpeed": 12, "windUnit": "km/h", "visibility": 16, "visibilityUnit": "km",
"condition": "Clear sky",
"icon": "clear",
"iconLightUrl": "https://navvo.io/uploads/weather-icons/clear-light.svg",
"iconDarkUrl": "https://navvo.io/uploads/weather-icons/clear-dark.svg",
"aqi": 40, "aqiCategory": "Good", "pm25": 9, "pm10": 16, "dust": 1,
"source": "Navvo weather point service"
}
The response also carries live air quality: aqi (US AQI), aqiCategory (Good…Hazardous), pm25/pm10 (µg/m³), and dust (µg/m³, desert dust concentration). These are null if the air-quality source is briefly unavailable. This endpoint returns 400 on invalid coordinates (the only one in this module that does); upstream failures return 200 with ok:false.
GET /map/weather/forecast
Hourly weather forecast for a point — the next N hours (default 24, max 72).
lon also accepted).metric (default) or imperial.{
"ok": true, "lat": 31.95, "lon": 35.91, "city": "Amman",
"units": "metric", "unit": "C", "windUnit": "km/h", "hours": 6,
"forecast": [
{ "time": "2026-06-27T19:00:00.000Z", "temperature": 22.8, "condition": "Clear", "icon": "clear",
"iconLightUrl": "https://navvo.io/uploads/weather-icons/clear-light.svg",
"iconDarkUrl": "https://navvo.io/uploads/weather-icons/clear-dark.svg",
"precipitationMm": 0, "windSpeed": 9, "humidity": 38 },
{ "time": "2026-06-27T20:00:00.000Z", "temperature": 22, "condition": "Clear night", "icon": "clear-night",
"iconLightUrl": "https://navvo.io/uploads/weather-icons/clear-night-light.svg",
"iconDarkUrl": "https://navvo.io/uploads/weather-icons/clear-night-dark.svg",
"precipitationMm": 0, "windSpeed": 8, "humidity": 41 }
]
}
| Field | Type | Notes |
|---|---|---|
forecast[].time | string | ISO hour |
forecast[].temperature | number | in unit (C/F) |
forecast[].condition | string | human label |
forecast[].icon | string | art slug — switches to the night variant after dark |
forecast[].iconLightUrl / iconDarkUrl | string | absolute SVG URLs |
forecast[].precipitationMm | number | null | rain in the hour |
forecast[].windSpeed | number | in windUnit |
400 on invalid coordinates; upstream failures return 200 with ok:false and an empty forecast.
GET /map/weather/alerts
Derived weather warnings for a point — scans the next N hours of forecast and returns alerts for threshold breaches: heat (≥37 °C, especially relevant for MENA), cold/freezing (≤1 °C), heavy rain (≥4 mm/h), and strong wind (≥40 km/h). Each alert carries a severity (moderate/high/severe), a peak value, and the hour it occurs.
?lat=&lng=&hours= (default 24, max 72).
{
"ok": true, "provider": "navvo-weather", "lat": 24.63, "lon": 46.72, "city": "…",
"checkedHours": 48, "count": 1,
"alerts": [
{ "type": "heat", "severity": "severe", "message": "High temperatures up to 43°C", "valueC": 43,
"at": "2026-06-28T12:00:00.000Z", "icon": "heat",
"iconLightUrl": "https://navvo.io/uploads/weather-icons/heat-light.svg",
"iconDarkUrl": "https://navvo.io/uploads/weather-icons/heat-dark.svg" }
]
}
count: 0 (empty alerts) means no thresholds were breached in the window. 400 on invalid coordinates.
GET /map/weather/cities
Weather pins for the cities/towns inside a viewport — used to label current conditions on the map at city zoom. Returns a GeoJSON FeatureCollection of points; only meaningful at zoom ≥ 7 (lower zooms return an empty collection quickly), and the count is capped so the map stays readable.
west,south,east,north.< 7 returns empty).metric (default) or imperial.{
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "geometry": { "type": "Point", "coordinates": [35.93, 31.95] },
"properties": { "name": "Amman", "temperature": 30, "unit": "C", "condition": "Clear",
"code": 0, "icon": "clear",
"iconLightUrl": "https://navvo.io/uploads/weather-icons/clear-light.svg",
"iconDarkUrl": "https://navvo.io/uploads/weather-icons/clear-dark.svg",
"color": "#386BFF" } }
],
"metadata": { "layerType": "weather_cities", "name": "Weather", "hasData": true }
}
icon is a neutral family — one of clear, cloud, rain, storm, snow, fog — and code is its numeric equivalent (0–5).
GET /map/airquality/cities
Air-quality pins (US AQI + dust) for the cities/towns inside a viewport — the AQI counterpart of /map/weather/cities, for labelling air quality on the map at city zoom. Returns a GeoJSON FeatureCollection of points; only meaningful at zoom ≥ 7, and the count is capped (hard cap 20). Values are cached ~15 min per cell.
west,south,east,north.< 7 returns empty).{
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "geometry": { "type": "Point", "coordinates": [35.92, 31.95] },
"properties": { "name": "Amman", "aqi": 40, "aqiCategory": "Good",
"pm25": 10, "pm10": 16, "dust": 1 } }
],
"metadata": { "layerType": "air_quality_cities", "name": "Air quality", "hasData": true }
}
aqi is the US AQI; aqiCategory is its band (Good, Moderate, Unhealthy (sensitive), Unhealthy, Very unhealthy, Hazardous). pm25/pm10/dust are µg/m³. The same per-point values back the aqi/dust fields on /map/weather/point.
Route risk & enforcement
Score a route against live conditions across five layers — traffic, weather, incidents, logistics zones, and enforcement assets (cameras/signals) — using geometry intersection.
POST /map/route-risk
Scope: routing.
Request body
LineString/Feature/FeatureCollection, a raw array of [lng, lat] pairs, or any object with a coordinates field. (route is accepted as an alias.)preview.-1h, now (default), +1h, or +3h — which weather snapshot to score against.{
"routeId": "trip-42",
"timeOffset": "+1h",
"geometry": { "type": "LineString", "coordinates": [[35.91,31.95],[35.93,31.97],[35.95,31.99]] }
}
Response (200)
{
"ok": true,
"routeId": "trip-42",
"hasRouteGeometry": true,
"timeOffset": "+1h",
"scores": { "overall": 24, "traffic": 40, "weather": 10, "incidents": 0, "logistics": 32, "cameras": 16 },
"severity": "low",
"stack": [
{ "key": "traffic", "label": "Traffic speed", "score": 40, "severity": "medium", "detail": "Moderate congestion exposure on this route.", "count": 3 }
],
"warnings": [
{ "type": "traffic", "severity": "high", "title": "Heavy traffic segment", "detail": "18 km/h average · 42 samples", "location": { "lng": 35.93, "lat": 31.97 } },
{ "type": "logistics", "severity": "medium", "title": "Amman core congestion SLA", "location": { "lng": 35.91, "lat": 31.97 } }
]
}
| Field | Description |
|---|---|
scores | 0–100 per layer plus a weighted overall. |
severity | low / medium / high from the overall score. |
stack[] | Per-layer breakdown (score, severity, detail, count, source). |
warnings[] | Specific hazards along the route, each with a location anchor { lng, lat }. |
GET /map/route-risk (scope tiles) that takes the route via a coords query string of lng,lat pairs separated by ; — handy for quick previews without a request body.POST /map/route-enforcement
Scope: routing. Returns just the enforcement checkpoints (speed cameras, traffic cameras, traffic lights) along the route. Same request body as route-risk.
{
"ok": true,
"routeId": "trip-42",
"count": 2,
"items": [
{ "id": "speed_camera-0", "type": "speed_camera", "title": "Amman central speed camera", "detail": "Speed camera on or near route · 60 km/h", "confidence": 0.84, "location": { "lng": 35.92, "lat": 31.96 } }
]
}