Wherebox
On this page

Routes

A route is a line on the map with a marker and label at each end. There are three ways to get the line:

  1. You provide it: a GeoJSON line or an encoded polyline.
  2. The server draws it locally: straight segments or a geodesic great circle between your points.
  3. A routing engine computes it: car, bike or foot along real roads.

GET describes one route. POST takes up to 10 in routes, plus extra lines and polygons in shapes. Without an area, the map is framed on the routes and points, so a route alone is a complete request.

A line you already have#

If you route elsewhere, send the result. As GeoJSON (POST):

POST/v1/map
{
  "routes": [
    {
      "geometry": {
        "type": "LineString",
        "coordinates": [[16.6068, 49.1951], [16.9, 49.24], [17.2, 49.23], [17.6667, 49.2266]]
      },
      "start": { "label": "Brno" },
      "finish": { "label": "Zlín" }
    }
  ],
  "width": 480,
  "height": 280
}
A given GeoJSON line from Brno to Zlín

Or as an encoded polyline (Google algorithm). Precision 5 is the default; Valhalla and OSRM use 6, which you set with polylineprecision. URL-encode the polyline: it can contain @, {, | and other characters that mean something in a URL.

GET/v1/map.png
GET /v1/map.png?polyline=klgkHoojdBswGogx%40n%7D%40_ry%40fT%7BczA&fromlabel=Brno&tolabel=Zlín&w=480&h=280
A polyline route

Parameters: polyline, polylineprecision.

Straight and geodesic lines#

profile=straight connects from, the via points and to with straight segments. profile=geodesic follows the great circle, which is what you want for flights. Neither needs a routing engine. geodesic is the default, so a route without profile always renders; road routes need an explicit profile=car, bike or foot.

from, to and via take lat,lon or place:Name. Repeat via for more waypoints; they are visited in order.

GET/v1/map.png
GET /v1/map.png?from=49.1951,16.6068&to=49.2266,17.6667&via=49.24,16.9&profile=straight&fromlabel=Brno&tolabel=Zlín&w=480&h=280
A straight route with a via point
GET/v1/map.png
GET /v1/map.png?from=50.1,14.26&to=40.64,-73.78&profile=geodesic&fromlabel=Prague&tolabel=New+York&routedash=2,1.5&w=520&h=300
A great-circle flight line

With place names, the start and finish labels default to the names, so from=place:Brno&to=place:Zlín is already labeled.

GET/v1/map.png
GET /v1/map.png?from=place:Brno&to=place:Zlín&profile=straight&w=480&h=280
Map rendered by the route-places example

The same in POST, with a title and a point of interest on the way:

POST/v1/map
{
  "routes": [
    {
      "from": { "lat": 40.4168, "lon": -3.7038 },
      "to": { "lat": 37.3891, "lon": -5.9845 },
      "via": [{ "lat": 38.98, "lon": -3.93 }, { "lat": 38.25, "lon": -4.75 }],
      "profile": "straight",
      "start": { "label": "Madrid" },
      "finish": { "label": "Seville" }
    }
  ],
  "pois": [
    { "lat": 39.42, "lon": -3.78, "label": "Crash Site\n(near Urda)", "labelWeight": 700,
      "marker": { "shape": "dot", "size": 16, "strokeColor": "#000000", "strokeWidth": 2 } }
  ],
  "title": { "text": "Madrid – Seville" },
  "width": 500,
  "height": 440
}
Madrid to Seville with a marked point

Routed profiles (car, bike, foot)#

car, bike and foot ask a routing engine for a real route. This needs one of:

  • A configured server. The operator points WHEREBOX_VALHALLA_URL at a Valhalla instance, or sets a key for openrouteservice or GraphHopper. See the configuration reference and Self-hosting. With nothing configured, a routed profile returns 400 bad-request.
  • Your own key. For openrouteservice and GraphHopper, send your key in the X-Routing-Key request header, and choose the provider with routeprovider (GET) or provider (POST). Like X-Provider-Key, the header keeps the secret out of URLs and logs, and is scrubbed from error messages.

If the provider finds no route you get 422 no-route; if it is down or slow, 502 or 504 (Errors and limits). Public routing demo servers are rate-limited and not for production use.

This request is parsed but not rendered in the docs build, because it needs a routing engine:

POST/v1/map
{
  "routes": [
    {
      "from": { "place": "Brno" },
      "via": [{ "place": "Jihlava" }],
      "to": { "place": "Praha" },
      "profile": "car",
      "provider": "openrouteservice"
    }
  ],
  "width": 480,
  "height": 320
}

Send it with the key in a header, for example curl -H 'Content-Type: application/json' -H 'X-Routing-Key: YOUR_KEY' --data @route-car.json https://YOUR-HOST/v1/map -o route.png, where route-car.json holds the body above.

The response's meta (with ?response=json) includes the routed distance and duration per route.

Via points#

via forces the route through intermediate points, in order, up to 20. In GET repeat the parameter; in POST use an array of endpoints. The straight example above uses one via point. For routed profiles each via point becomes a waypoint the engine must pass.

Styling the line#

Color, width, dash pattern and opacity of the line, and a casing (an outline under the line that helps it read on busy basemaps):

GET/v1/map.png
GET /v1/map.png?from=49.1951,16.6068&to=49.2266,17.6667&profile=straight&routecolor=%231565c0&routewidth=4&routedash=2,1.5&routeopacity=0.9&fromlabel=Brno&tolabel=Zlín&w=480&h=280
A dashed blue route

Parameters: routecolor, routewidth, routedash, routeopacity, routecasing.

Start and finish#

Each end gets a marker and a label. By default the start is a ring and the finish a flag (see markers), labeled with the place name if you gave one. Change the label or marker, or hide an end:

GET/v1/map.png
GET /v1/map.png?from=49.1951,16.6068&to=49.2266,17.6667&profile=straight&fromlabel=Start&tolabel=Finish&frommarker=dot&tomarker=pin&w=480&h=280
Route ends with custom markers
GET/v1/map.png
GET /v1/map.png?from=49.1951,16.6068&to=49.2266,17.6667&profile=straight&showfrom=false&tolabel=Zlín&w=480&h=280
Map rendered by the route-hide-start example

Parameters: fromlabel, tolabel, frommarker, tomarker, showfrom, showto. In POST these are start and finish objects with the full marker and label options.

Route plus area#

A route on its own frames itself. Add an area to keep the locator look and frame the whole area instead:

POST/v1/map
{
  "area": "Zlínský kraj",
  "routes": [
    {
      "from": { "place": "Zlín" },
      "to": { "place": "Kroměříž" },
      "profile": "straight",
      "color": "#d01c1c"
    }
  ],
  "width": 420,
  "height": 300
}
A route inside a highlighted region

Forcing the frame with bounds#

bounds=west,south,east,north fixes the frame, overriding automatic framing. Use it to keep a series of maps at the same zoom, or to show context around a short route.

GET/v1/map.png
GET /v1/map.png?from=49.1951,16.6068&to=49.2266,17.6667&profile=straight&bounds=15.5,48.6,18.5,49.8&w=480&h=280
A route in a fixed frame

See bounds.

Globe projection#

Web Mercator stretches everything away from the equator: Greenland looks the size of Africa, and a flight from Alaska to Europe bends into a long arc. For routes that span continents, projection=globe draws an orthographic globe centered on your content instead. Shapes keep their true proportions, and great-circle legs look like the shortest paths they are.

GET/v1/map.png
GET /v1/map.png?from=64.5011,-165.4064&to=52.52,13.405&via=47.6062,-122.3321&via=40.7128,-74.0060&profile=geodesic&fromlabel=Nome&tolabel=Berlin&poi=47.6062,-122.3321|label:Seattle|marker:dot&poi=40.7128,-74.0060|label:New+York|marker:dot&projection=globe&w=600&h=420
Nome to Berlin via Seattle and New York on the whole globe

By default the whole globe is shown, centered on your content, so the map reads as a globe at a glance. The same request without projection=globe gives the familiar Mercator map, with Greenland inflated and the route curving far north.

Whole globe or zoomed in#

globeview=fit zooms in until the content fills the image. Only part of the globe is visible, but the route area gets a larger scale and more detail:

GET/v1/map.png
GET /v1/map.png?from=64.5011,-165.4064&to=52.52,13.405&via=47.6062,-122.3321&via=40.7128,-74.0060&profile=geodesic&fromlabel=Nome&tolabel=Berlin&poi=47.6062,-122.3321|label:Seattle|marker:dot&poi=40.7128,-74.0060|label:New+York|marker:dot&projection=globe&globeview=fit&w=600&h=420
The same route, zoomed in to fill the image

Which one to use:

  • full (default) when the point is where on Earth the route goes: a flight across the Pacific, an expedition, a world tour. Everyone recognizes a globe.
  • fit when the route itself matters more than the planet: its exact path, the labels along it, smaller images where every pixel counts.

In JSON the same option is "globe": { "view": "fit" }.

projection=auto decides for you: the globe when the content spans a large part of the Earth or would be stretched a lot by Mercator, Mercator otherwise (the thresholds are in the projection reference). A country-sized map stays Mercator, and when auto picks the globe it shows the whole globe unless you pass globeview=fit.

Things to know:

  • Everything framed must fit on one side of the globe. Content too spread out for that is a 400 bad-request; split it into several maps.
  • Neighbor labels are not available on the globe yet.
  • Markers, labels and route lines are drawn directly on the globe, so they stay sharp. The basemap is rendered as a flat map and wrapped onto the sphere, so its finest lines get slightly softer toward the globe's edge and near the poles.