Navvo
API Reference

Map Data & Risk

Map configuration, category styles, overlays, weather, and condition-aware route risk scoring.

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.

Coordinates are GeoJSON [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

Why this is not just "places in a bbox". At city zoom a viewport can contain tens of thousands of places. Drawing them all produces an unreadable wall of pins; drawing the nearest few produces a map that hides the hospital and shows three barbers. This endpoint applies a per-zoom label budget and a minimum priority floor, so what you get back is what a cartographer would keep at that zoom — landmarks first, then majors, then long-tail — already thinned for you.
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

bbox
string required
Viewport as 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.
zoom
number
Map zoom, 0–22. Defaults to 14. This is the main control: it selects the label budget, the priority floor and the icon-only threshold.
categories
string
Comma-separated category slugs to restrict the result to (e.g. hospital,pharmacy). Omit for all categories.
locale
string
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. ::::

dpr
number
Device pixel ratio, 1–4. Defaults to 1.
perf
number
Performance dial, 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
}
budget
number
How many labels this zoom/viewport combination allows. items is already trimmed to it.
minPriority
number
The priority floor applied at this zoom. Nothing below it was returned.
items[].priority
number
A materialised 0–100 importance score combining category weight, popularity, rating, review confidence and any manual business-importance override. Render in this order — it is already sorted.
items[].labelHidden
boolean
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.
profile.iconOnlyBelow
number
Priority under which items are icon-only by policy, mirroring labelHidden.
truncated
boolean
true when more places qualified than the budget allowed — zoom in for the rest.
Rendering. Feed 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

EndpointReturns
GET /map/overlays/{layerType}/tile-coverTile 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.

EndpointReturns
GET /map/weather/geojsonWeather 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-coverTile URLs covering a bbox.
GET /map/weather-riskAggregate 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:

FieldTypeNotes
iconstringArt slug — the file stem. See the list below.
iconLightUrlstringAbsolute URL to the SVG drawn for light interfaces.
iconDarkUrlstringAbsolute 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 clear by day and clear-night after dark.
  • icon may be more specific than the condition text: partly is 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 cloud on an unknown value rather than failing.
  • Weather overlay polygons (/map/weather/geojson) carry icons once on metadata.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).

lat
number required
Latitude.
lng
number required
Longitude (lon also accepted).
units
string
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).

lat
number required
Latitude.
lng
number required
Longitude (lon also accepted).
hours
number
How many hours to return (default 24, max 72).
units
string
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 }
  ]
}
FieldTypeNotes
forecast[].timestringISO hour
forecast[].temperaturenumberin unit (C/F)
forecast[].conditionstringhuman label
forecast[].iconstringart slug — switches to the night variant after dark
forecast[].iconLightUrl / iconDarkUrlstringabsolute SVG URLs
forecast[].precipitationMmnumber | nullrain in the hour
forecast[].windSpeednumberin 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.

bbox
string required
Viewport bounds west,south,east,north.
zoom
number required
Current map zoom (< 7 returns empty).
units
string
metric (default) or imperial.
limit
number
Max pins (auto-scaled by zoom; hard cap 24).
{
  "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.

bbox
string required
Viewport bounds west,south,east,north.
zoom
number required
Current map zoom (< 7 returns empty).
limit
number
Max pins (auto-scaled by zoom; hard cap 20).
{
  "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

geometry
object | array required
The route geometry. Accepts a GeoJSON LineString/Feature/FeatureCollection, a raw array of [lng, lat] pairs, or any object with a coordinates field. (route is accepted as an alias.)
routeId
string
Echoed in the response. Defaults to preview.
timeOffset
string
-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 } }
  ]
}
FieldDescription
scores0–100 per layer plus a weighted overall.
severitylow / 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 }.
There's also a 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 } }
  ]
}
Copyright © 2026