Skip to main content

Map matching

Map matching takes an ordered GPS trace and snaps it to roads in the installed Valhalla region. It differs from routing: routing creates a path between points, while map matching reconstructs the roads followed by a recorded track.

Valhalla must be installed, ready and contain both the trace and nearby roads. Each request needs at least two points. Atlas accepts up to 10,000 points by default.

Send a trace

Send coordinates in recording order. Include epoch-second timestamps and accuracy in metres whenever the source provides them; they help Valhalla match noisy or ambiguous tracks.

curl --request POST 'http://localhost:8484/api/v1/map-match' \
--header 'content-type: application/json' \
--data '{
"shape": [
{"lat": 52.5000, "lon": 13.4000, "time": 1714032000, "accuracy": 8},
{"lat": 52.5100, "lon": 13.4100, "time": 1714032030, "accuracy": 6}
],
"mode": "auto",
"shape_match": "map_snap",
"format": "geojson"
}'

mode accepts auto, bicycle or pedestrian. shape_match accepts map_snap, walk_or_snap or edge_walk. Optional search_radius, gps_accuracy and breakage_distance values tune Valhalla's matcher; use the API reference for the complete schema.

Read the result

Every successful response includes:

  • data.summary: total matched length in kilometres and estimated time in seconds.
  • data.matched_points: one correlation result for each input point, in the same order. Its type is matched, interpolated or unmatched.
  • data.stats: counts, number of segments, distance error statistics and Valhalla confidence scores when available.

The geometry depends on format:

  • polyline6 is the default. data.legs contains one independently drawable encoded polyline per matched segment.
  • geojson returns data.geometry plus independently drawable data.segments. One continuous match is a LineString; a trace with gaps is a MultiLineString. This prevents false straight lines across unmatched gaps.

Request directions only when needed

Set include_directions to true when a client needs narrative maneuvers:

{
"shape": [
{"lat": 52.5000, "lon": 13.4000},
{"lat": 52.5100, "lon": 13.4100}
],
"include_directions": true
}

Atlas then makes a second Valhalla request. Read the primary and alternate direction results from data.directions.paths. Without this option, data.legs contains matched geometry segments and no narrative maneuvers.

This response contract changed in 0.6.0. Clients written for earlier versions that read maneuvers from data.legs must opt into directions and use the new path above.

Capacity and retries

Map matching is CPU-intensive and occupies a Valhalla worker for the request. Atlas admits four map matches concurrently by default and does not queue excess work. When all slots are occupied it returns:

HTTP/1.1 429 Too Many Requests
Retry-After: 1

The JSON error code is MAP_MATCH_BUSY. Wait for the indicated delay before retrying, and use exponential backoff if the instance remains busy.

Operators can change the limit with MAP_MATCH_CONCURRENCY. Raising it above Valhalla's practical capacity can increase latency for every request. The MAP_MATCH_MAX_POINTS setting changes the per-request point limit; splitting a long recording into sensible time or distance windows is usually safer than raising it.

An unmatchable trace returns 422 VALIDATION_ERROR. Check that its coordinates are valid, fall inside the installed region and are neither too sparse nor too noisy. An unavailable Valhalla instance returns 503 UPSTREAM_UNAVAILABLE.