Routes
Routes draw curved A→B lines that follow the great circle — the shortest path on a sphere. Use them for trade flows, supply chains, migration corridors, or any map that needs to show a connection between two places. Endpoints can be ISO country codes (resolved to centroids) or explicit lat,lon coordinates.
Examples
Three routes with mixed styles:
https://api.maproll.io/map.svg?scope=world&theme=dark&routes=DE%3EBR:%2360a5fa:2:dashed:arrow%3BRO%3EAU:%23ec4899:2%3BJP%3EIN
Hub-and-spoke: six routes radiating from the US:
https://api.maproll.io/map.svg?scope=world&theme=light&routes=US%3EGB:arrow%3BUS%3EFR:arrow%3BUS%3EDE:arrow%3BUS%3EJP:arrow%3BUS%3EAU:arrow%3BUS%3EBR:arrow
Literal lat/lon endpoints — useful when the destination is a city, not a country centroid:
https://api.maproll.io/map.svg?scope=world&theme=dark&routes=44.43,26.10%3E52.52,13.40:%23ff8c00:3:arrow
Routes with marker icons at each endpoint:
https://api.maproll.io/map.svg?scope=world&theme=dark&routes=DE%3EBR:arrow%3BUS%3ECN:dashed:arrow&markers=52.52,13.40:airport:Berlin%3B-23.55,-46.63:pin:S%C3%A3o+Paulo:bottom%3B38.90,-77.04:flag:Washington%3B39.90,116.40:star:Beijing:right
Parameters
| Param | Type | Default | Allowed | Notes |
|---|---|---|---|---|
routes | string | — | See URL syntax | Semicolon-separated route definitions |
color | string | theme text color | 6-digit hex #rrggbb | Must start with # |
width | number | 2 | 1–20 (px) | Integer stroke width |
style | string | solid | solid, dashed | |
arrow | flag | off | arrow (presence) | Terminal arrowhead at the destination |
sea | flag | off | sea (presence) | Thread sea lanes instead of a great circle. See Sea routes |
URL syntax
routes=from>to[:options];from>to[:options];...
Options after the >to endpoint are type-distinguished — specify them in any order:
| Option | Detected by | Example |
|---|---|---|
| Color | Starts with # | #ff0000 |
| Width | All digits, 1–20 | 3 |
| Style | Literal solid or dashed | dashed |
| Arrow | Literal arrow | arrow |
| Sea mode | Literal sea | sea |
Endpoints are either:
- ISO codes — case-insensitive, resolved to the country's geographic centroid. Example:
DE,US,RO-B(subnational). lat,lonliterals — a comma in the token signals a coordinate. Example:44.43,26.10. Mix ISO and coordinates freely:DE>44.43,26.10.
The > separator was chosen over - so subnational ISO codes like RO-AG or US-CA remain unambiguous.
URL-encode > as %3E, # as %23, and ; as %3B in query strings.
# Simple ISO pair
routes=RO%3EDE
# With color and width
routes=RO%3EDE:%23ff0000:3
# Dashed with arrow
routes=RO%3EDE:dashed:arrow
# Lat/lon literal endpoints
routes=44.43,26.10%3E52.52,13.40
# Multiple routes
routes=DE%3EBR:dashed%3BUS%3ECN:arrow
JSON body syntax
Use POST /render/map with routes: RouteInput[]. The fromLatLon/toLatLon fields accept [lat, lon] and override the ISO lookup:
{
"scope": "world",
"theme": "dark",
"routes": [
{ "from": "RO", "to": "DE", "color": "#ff0000", "width": 3, "arrow": true },
{ "from": "US", "to": "CN", "style": "dashed" },
{ "from": "XX", "to": "YY", "fromLatLon": [44.43, 26.10], "toLatLon": [52.52, 13.40] }
]
}
Sea routes
Great circles draw the shortest path on a sphere, which is correct for flow visualization and ~accurate for flight paths. For ships it's wrong: a great circle from Shanghai to Rotterdam draws over Siberia. Adding the sea token threads the Eurostat marnet — through Suez, Panama, Malacca, etc.
https://api.maproll.io/map.svg?scope=world&theme=dark&width=1400&routes=31.23,121.47%3E51.92,4.48:sea:%230066cc:3:arrow
Transpacific routes split cleanly at the antimeridian, same as great circles:
https://api.maproll.io/map.svg?scope=world&theme=dark&width=1400&routes=35.44,139.64%3E33.77,-118.20:sea:%23f5b25c:3
You can mix sea and great-circle routes on the same map — the sea routes get their polyline, the rest stay great circles:
https://api.maproll.io/map.svg?scope=world&theme=dark&width=1400&routes=CN%3ENL:sea:%230066cc:3:arrow%3BUS%3ECN:%23f59e0b:2:arrow
In JSON body, set mode: "sea" on the route object:
{
"scope": "world",
"theme": "dark",
"routes": [
{
"from": "Shanghai",
"to": "Rotterdam",
"fromLatLon": [31.23, 121.47],
"toLatLon": [51.92, 4.48],
"mode": "sea",
"color": "#0066cc"
}
]
}
Sea route notes
- This is network path-finding, not navigational routing. The route is a shortest path across the Eurostat marnet graph — a real lane network through Suez, Panama, Malacca and the rest, not a great circle. It is equally not a voyage plan: the network is coarse (~100 km between vertices), so the line approximates the corridor rather than a sailed track, and nothing accounts for weather, traffic separation, or territorial waters.
- Snap to the nearest lane vertex — not to a port. Endpoints snap to the closest vertex of the marnet graph. There is no port database behind this, so the snapped vertex is simply the nearest bit of network, which may not be the intended port. For a specific port, pass
lat,lonliterals. - No land leg from the true origin. The drawn polyline begins and ends at the snapped vertices, not at the coordinates you passed. An inland endpoint's line starts out at sea, with no connecting leg from the original point — draw that yourself as a separate route if you need it.
- Snap radius is 500 km. Endpoints further from any sea lane than that are rejected — the route lands in
X-Geo-Warnings: unknown-routes:sea:A>Band the render returns 200 with no line drawn. This catches truly landlocked pairs (Mongolia, Kazakhstan, Bolivia interior) rather than silently snapping to a faraway coast. - First-call latency. The maritime graph builds on the first sea-route request in a given server process (~100–500 ms). Subsequent calls reuse it. Cache-warm renders are fast.
- Cache key includes
mode. Sea and great-circle versions of the same endpoints are independent cache entries. - Restrictions, vessel draft, ETA — not exposed. The underlying library supports forcing routes to avoid specific canals (e.g. Suez disruption), gating canals by ship draft, an arctic toggle, and computing voyage duration. None of these are reachable through the API today, so a sea route cannot be made to avoid a canal or reflect a vessel's draft. These will land later when there is demand.
Notes
- Great-circle curves. Routes are interpolated with 64 samples via
d3.geoInterpolate, giving a smooth arc at world scale. - Antimeridian split. When a great circle crosses the ±180° meridian (the date line), the renderer automatically splits it into two segments meeting at the map edges. No client-side handling needed.
- Routes draw below markers. Marker icons sit on top of route endpoints, so the line tip doesn't poke through the icon.
- Unknown ISO codes. If an endpoint can't be resolved to a feature in the current scope, the route is skipped and the pair is reported in the
X-Geo-Warnings: unknown-routes:A>Bresponse header. The render still returns 200. - Centroid vs. city position.
from=DEresolves to Germany's geographic centroid (central Germany), not Berlin. For routes that must connect city markers, pass explicitlat,lonliterals matching the marker's coordinates. - Limit: 500 routes per render. Exceeding it returns
400 too_many_routes. The separateinvalid_routescode covers malformed route syntax in the URL, not the count cap. - No multi-stop routes. Workaround: pass
A>BandB>Cas separate routes. - Arrow markers are deduped by
(color, width). Twenty routes sharing a color and width share one SVG<marker>definition.
Related
- Markers — place icons at route endpoints
- Annotations — add hover text to regions along the route
GET /map.{svg,png}— full query parameter reference- POST /render/map — JSON body schema
- Supply-chain recipe — routes + markers + annotations together