# 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):

```json
{
  "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](/docs/images/examples/route-geojson.png)

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.

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

![A polyline route](/docs/images/examples/route-polyline.png)

Parameters: [`polyline`](/docs/reference/parameters.md#param-polyline), [`polylineprecision`](/docs/reference/parameters.md#param-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.

```http
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](/docs/images/examples/route-straight.png)

```http
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](/docs/images/examples/route-geodesic.png)

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

```http
GET /v1/map.png?from=place:Brno&to=place:Zlín&profile=straight&w=480&h=280
```

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

```json
{
  "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](/docs/images/examples/madrid-seville-post.png)

## 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](/docs/reference/configuration.md) and [Self-hosting](/docs/guides/self-hosting.md). 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](/docs/guides/errors-and-limits.md)). 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:

```json
{
  "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):

```http
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](/docs/images/examples/route-styled.png)

Parameters: [`routecolor`](/docs/reference/parameters.md#param-routecolor), [`routewidth`](/docs/reference/parameters.md#param-routewidth), [`routedash`](/docs/reference/parameters.md#param-routedash), [`routeopacity`](/docs/reference/parameters.md#param-routeopacity), [`routecasing`](/docs/reference/parameters.md#param-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](/docs/reference/markers.md)), labeled with the place name if you gave one. Change the label or marker, or hide an end:

```http
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](/docs/images/examples/route-ends.png)

```http
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
```

Parameters: [`fromlabel`](/docs/reference/parameters.md#param-fromlabel), [`tolabel`](/docs/reference/parameters.md#param-tolabel), [`frommarker`](/docs/reference/parameters.md#param-frommarker), [`tomarker`](/docs/reference/parameters.md#param-tomarker), [`showfrom`](/docs/reference/parameters.md#param-showfrom), [`showto`](/docs/reference/parameters.md#param-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:

```json
{
  "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](/docs/images/examples/route-with-area.png)

## 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.

```http
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](/docs/images/examples/route-bounds.png)

See [`bounds`](/docs/reference/parameters.md#param-bounds).

## Your own lines and polygons

Shapes are lines and polygons you draw yourself, without start and finish markers: a delivery zone, a border you want to emphasize, a track. In GET, write the style options as `key:value|`, then the geometry as the last part (`line:`, `polygon:`, `polyline:` or `geojson:`), and repeat `shape` for more. They are drawn in order, so later shapes go on top, and the map is framed to include them.

```http
GET /v1/map.png?shape=fill:%23ffcc00|fillopacity:0.5|color:%23b08800|width:1|polygon:49.5,14.6;50.3,14.6;50.3,16.2;49.5,16.2&shape=color:%23d01c1c|width:4|line:49.3,14.0;50.5,16.6&preset=light&w=468&h=300
```

![A yellow rectangle with a red line drawn across it](/docs/images/examples/shapes-chained.png)

POST takes up to 50 shapes as `shapes`, each with a `geojson` geometry and the same style fields. See [`shape`](/docs/reference/parameters.md#param-shape).

## 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.

```http
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](/docs/images/examples/route-globe.png)

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:

```http
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](/docs/images/examples/route-globe-fit.png)

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](/docs/reference/parameters.md#param-projection)). 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.
