# Points and labels

A point is a location with a marker and an optional label. Up to 50 per map. In GET, repeat `poi=` once per point; in POST, use the `pois` array.

## Coordinates or place names

Give `lat,lon` when you know where it is, or `place:Name` to look it up.

```http
GET /v1/map.png?area=Zlínský+kraj&poi=49.13,17.33|label:Chřiby&w=400&h=300
```

![A point from coordinates](/docs/images/examples/poi-coordinates.png)

```http
GET /v1/map.png?area=Zlínský+kraj&poi=place:Zlín&poi=place:Kroměříž&poi=place:Uherské+Hradiště&w=400&h=300
```

![Three points looked up by name](/docs/images/examples/poi-places.png)

Place lookups cover cities, peaks, mountain ranges and similar names, and prefer a match inside the framed `area`. The label defaults to the place name; set `label:` to override it. Coordinates are used exactly as given, with no lookup. Find place names with `GET /v1/places/search?q=…`.

Names are looked up in the server's imported OpenStreetMap places first. A server can also configure a geocoder (Nominatim or Photon) for names its data does not have, such as places outside the imported region. When the server has none, an unknown name returns `404 not-found` with close matches in `suggestions`. If the geocoder itself fails, the response is `502 upstream-failed` or `504 upstream-timeout`, so retry those rather than treating them as "no such place". Self-hosters: see [Geocoder fallback](/docs/guides/self-hosting.md#geocoder-fallback).

After the location, a point takes `|key:value` options. All of them are in the [`poi` reference](/docs/reference/parameters.md#param-poi). In JSON the same options are fields of each `pois` entry:

```json
{
  "area": "Zlínský kraj",
  "pois": [
    { "lat": 49.13, "lon": 17.33, "label": "Chřiby", "marker": { "shape": "triangle", "color": "#e02020" } },
    { "place": "Zlín", "marker": { "shape": "circle", "color": "#222222", "size": 9 } }
  ],
  "width": 400,
  "height": 300
}
```

## Markers

A plain point is a pin. Pick another preset with `marker:`, and tune `color:`, `size:` and `stroke:`/`strokewidth:`.

```http
GET /v1/map.png?area=Zlínský+kraj&w=480&h=320&poi=49.40,17.60|marker:star|color:%23e0a000|label:star&poi=49.40,17.95|marker:diamond|color:%231565c0|label:diamond&poi=49.20,17.45|marker:square|color:%232e7d32|label:square&poi=49.20,17.80|marker:circle|color:%23222222|label:circle&poi=49.05,17.30|marker:triangle|label:triangle&poi=49.05,17.70|marker:dot|label:dot
```

![Several marker presets](/docs/images/examples/marker-presets.png)

Which presets exist, their default sizes and which one a point, a route start or a route finish gets by default is in the [markers reference](/docs/reference/markers.md). The role matters: a plain point is a pin, a route start a ring, a route finish a flag (see [Routes](/docs/guides/routes.md)). Setting `marker:` replaces the role default.

## Custom icons

Use `icon` instead of a preset marker for logos or your own pins. It takes an SVG or PNG as a `data:` URI, or an `https` URL on a host the server operator allows. `anchor:bottom` puts the bottom edge of the icon on the location, which is what pin-shaped icons need; the default is the center.

```json
{
  "area": "Zlínský kraj",
  "pois": [
    {
      "lat": 49.2266,
      "lon": 17.6667,
      "label": "Zlín",
      "marker": {
        "icon": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMiIgaGVpZ2h0PSI0MCIgdmlld0JveD0iMCAwIDMyIDQwIj48cGF0aCBkPSJNMTYgMEM3IDAgMCA3IDAgMTZjMCAxMiAxNiAyNCAxNiAyNHMxNi0xMiAxNi0yNEMzMiA3IDI1IDAgMTYgMHoiIGZpbGw9IiMxNTY1YzAiLz48Y2lyY2xlIGN4PSIxNiIgY3k9IjE2IiByPSI2IiBmaWxsPSIjZmZmIi8+PC9zdmc+",
        "anchor": "bottom",
        "size": 30
      }
    }
  ],
  "width": 400,
  "height": 300
}
```

![A custom SVG pin](/docs/images/examples/custom-icon.png)

`size` is the icon height in CSS pixels. In a GET URL the icon must be URL-encoded (base64 contains `+`, `/` and `=`), so POST is the practical way to send one.

### Security notes for icons

Fetching a user-supplied URL is how servers get attacked, so the rules are strict. They are the server's, not yours, but they decide what works:

- Remote icons are **off** unless the operator lists hosts in `WHEREBOX_ICON_HOSTS`. Until then only `data:` URIs work. See the [configuration reference](/docs/reference/configuration.md).
- Remote icons must be `https`, are never fetched through redirects, and are capped in size and time.
- The type must really be an image (PNG signature check); SVGs containing a DOCTYPE or entities, external references, or scripts are rejected.
- A rejected icon is an error, not a silent fallback: you get `400 bad-request` (see [Errors and limits](/docs/guides/errors-and-limits.md)).

If you embed untrusted SVG from your own users, sanitize it before it reaches the API as well; the server's checks are a safety net, not a content policy.

## Labels

Set the text with `label:`. A `\n` starts a new line. Labels avoid each other, the markers, the route lines and the image edge.

```http
GET /v1/map.png?area=Moravskoslezský+kraj&w=420&h=300&poi=49.5465,18.4472|label:Lysá hora\n1323 m|weight:700|marker:triangle|color:%23202020
```

![A two-line, bold label](/docs/images/examples/label-multiline.png)

In GET, `|` cannot appear inside a label; send a POST if you need one. In JSON write a real newline: `"label": "Lysá hora\n1323 m"`.

### Position, size, weight and color

`pos:` puts the label next to the marker (`right`, `left`, `top`, `bottom`, the four corners, or `auto`, the default). `labelsize:`, `weight:` and `labelcolor:` style it. `weight:` overrides the map-wide `fontweight` for this label only; see [Typography](/docs/guides/typography-and-titles.md).

```http
GET /v1/map.png?area=Zlínský+kraj&w=420&h=300&poi=place:Zlín|pos:bottom|labelsize:18|weight:700&poi=place:Kroměříž|pos:left|labelcolor:%231565c0
```

![Labels placed below and to the left](/docs/images/examples/label-position.png)

## Crowded points and leader lines

With `pos:auto`, a label first tries the spots next to its marker. If they are all taken, the label moves further out and a thin leader line connects it back. That keeps nearby points readable without you placing anything.

```http
GET /v1/map.png?bounds=17.636,49.212,17.698,49.241&w=480&h=340&poi=49.2266,17.6693|label:Náměstí+Míru&poi=49.2277,17.6685|label:Zámek&poi=49.2282,17.6667|label:Divadlo&poi=49.2277,17.6649|label:Nádraží&poi=49.2266,17.6641|label:Radnice&poi=49.2255,17.6649|label:Muzeum&poi=49.2250,17.6667|label:Park&poi=49.2255,17.6685|label:Škola
```

![Eight close points: labels moved out with leader lines](/docs/images/examples/leader-lines.png)

Turn this off per point with `leader:false`; the label then sits at the nearest position even if it overlaps something:

```http
GET /v1/map.png?bounds=17.636,49.212,17.698,49.241&w=480&h=340&poi=49.2266,17.6693|label:Náměstí+Míru|leader:false&poi=49.2277,17.6685|label:Zámek|leader:false&poi=49.2282,17.6667|label:Divadlo|leader:false&poi=49.2277,17.6649|label:Nádraží|leader:false&poi=49.2266,17.6641|label:Radnice|leader:false&poi=49.2255,17.6649|label:Muzeum|leader:false&poi=49.2250,17.6667|label:Park|leader:false&poi=49.2255,17.6685|label:Škola|leader:false
```

![The same points without leader lines](/docs/images/examples/leader-off.png)

Options: [`poi`](/docs/reference/parameters.md#param-poi) (`pos`, `labelsize`, `weight`, `labelcolor`, `leader`).

## Legend

With many points, or long names, labels on the map get crowded. A legend moves them off the map: every labeled point becomes a numbered circle, and a panel lists the numbers with their labels, like a printed map. Set `legend=` to the side the panel goes on: `right`, `left`, `top` or `bottom`.

```http
GET /v1/map.png?area=Zlínský+kraj&w=500&h=320&poi=place:Zlín&poi=49.3,17.4|label:Kroměříž&poi=49.07,17.46|label:Uherské Hradiště&poi=49.13,17.33|label:Chřiby&poi=49.34,17.99|label:Vsetín&legend=right&legendtitle=Places
```

![Five numbered points with a legend on the right](/docs/images/examples/legend-right.png)

How it works:

- Points are numbered in order: the `poi`s first, then route ends (start, then finish). Only labeled points get a number, and that includes place names used as default labels (`place:Zlín` above). A point without a label keeps its normal marker.
- The circle replaces the marker on the map, custom icons included. Its color is the point's `color` (route ends use the route color); `legendbadge` sets one color for all of them.
- The panel is inside the image size you asked for, as the [title](/docs/guides/typography-and-titles.md) is; the map gets the rest. It has the theme's background and text colors (dark on the dark preset), or set `legendbg` and `legendcolor`. `legendsize` sets the text size, and the circles grow with it.
- On the left or right, the panel is one column, as wide as the longest entry but at most 40 % of the image width. Longer labels end in "…". When the entries don't fit the height, the text gets smaller, then the panel uses more columns, and as a last resort the remaining entries become "+N more".
- At the top or bottom, the entries flow in rows across the image, with the legend title first. The band takes at most 40 % of the height.

A legend below the map suits routes. Route ends count as labeled points:

```http
GET /v1/map.png?from=40.4168,-3.7038&to=37.3891,-5.9845&profile=straight&fromlabel=Madrid&tolabel=Seville&poi=38.98,-3.93|label:Ciudad Real&poi=38.25,-4.75|label:Puertollano&poi=37.88,-4.78|label:Córdoba&legend=bottom&w=500&h=400
```

![A route and three points with a legend below the map](/docs/images/examples/legend-bottom-route.png)

The same in JSON:

```json
{
  "area": "Zlínský kraj",
  "pois": [
    { "place": "Zlín" },
    { "lat": 49.3, "lon": 17.4, "label": "Kroměříž" },
    { "lat": 49.07, "lon": 17.46, "label": "Uherské Hradiště\n(old town)" }
  ],
  "legend": { "position": "left", "title": "Towns" },
  "basemap": { "preset": "dark" },
  "width": 480,
  "height": 320
}
```

![A legend on the left, on the dark preset](/docs/images/examples/legend-post.png)

Options: [`legend`](/docs/reference/parameters.md#param-legend) (`legendtitle`, `legendsize`, `legendcolor`, `legendbg`, `legendbadge`).
