--- title: Map matching | Plaza Docs description: Snap noisy GPS traces to the road network. Clean up fleet tracks, activity recordings, and raw sensor data. --- GPS points drift into buildings, jump across rivers, and wander off roads. Map matching snaps a raw trace to the most likely path on the road network using a Hidden Markov Model, returning snapped tracepoints with matched road segments. ## Basic request Send the trace as a GeoJSON LineString in the `geometry` field. Coordinates are `[longitude, latitude]` pairs. Optional `radiuses` controls the search radius per point (meters, default 50): Terminal window ``` curl -X POST https://plaza.fyi/api/v1/map-match \ -H "x-api-key: pk_live_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{ "geometry": { "type": "LineString", "coordinates": [ [13.3888, 52.5170], [13.3910, 52.5168], [13.3935, 52.5172], [13.3961, 52.5165], [13.3990, 52.5161] ] } }' ``` ``` import Plaza from "@plazafyi/sdk"; const client = new Plaza(); const matched = await client.v1.mapMatch({ geometry: { type: "LineString", coordinates: [ [13.3888, 52.517], [13.391, 52.5168], [13.3935, 52.5172], [13.3961, 52.5165], [13.399, 52.5161], ], }, }); ``` ``` import plaza client = plaza.Client() matched = client.v1.map_match( geometry={ "type": "LineString", "coordinates": [ [13.3888, 52.5170], [13.3910, 52.5168], [13.3935, 52.5172], [13.3961, 52.5165], [13.3990, 52.5161], ], }, ) ``` ``` import "github.com/plazafyi/plaza-go" client := plaza.NewClient() matched, _ := client.V1.MapMatch(ctx, plaza.MapMatchParams{ Geometry: plaza.GeoJSONLineString{ Type: "LineString", Coordinates: [][]float64{ {13.3888, 52.5170}, {13.3910, 52.5168}, {13.3935, 52.5172}, {13.3961, 52.5165}, {13.3990, 52.5161}, }, }, }) ``` ## Response Response — a GeoJSON FeatureCollection. Each feature is a snapped tracepoint; the top-level `matchings` array contains matched road segments. ``` { "type": "FeatureCollection", "features": [ { "type": "Feature", "geometry": { "type": "Point", "coordinates": [13.3889, 52.5170] }, "properties": { "original": [13.3888, 52.5170], "distance_m": 1.2, "edge_id": 48201, "name": "Unter den Linden", "matchings_index": 0, "waypoint_index": 0 } } ], "matchings": [ { "distance": 720, "geometry": { "type": "LineString", "coordinates": "..." } } ] } ``` | Field | What it means | | ----------------- | ------------------------------------------------------------------------------ | | `original` | The original `[lng, lat]` coordinate you sent. | | `distance_m` | Distance from the original point to the snapped location in meters. | | `edge_id` | The road edge the point was snapped to. | | `name` | Street name of the matched edge (when available). | | `matchings_index` | Index into the top-level `matchings` array for this point’s matched sub-route. | | `waypoint_index` | This point’s index in the original input. | ## Custom radiuses Control how far each GPS point searches for candidate roads: ``` { "geometry": { "type": "LineString", "coordinates": [ [13.3888, 52.5170], [13.3910, 52.5168], [13.3935, 52.5172] ] }, "radiuses": [100, 50, 75] } ``` Each value is a search radius in meters for the corresponding coordinate (default: 50m). ## Tips for good results **Point density matters.** One point every few seconds works well. One per minute produces poor matches — too much ambiguity between points. **Trim cold-start junk.** GPS receivers produce noise for the first few seconds. Cut scattered initial points before sending. **Coordinate limit.** Max 50 points per request. For longer traces, split into overlapping segments and stitch. ## Use cases **Fleet tracking.** Snap delivery vehicle traces to actual roads for accurate distance and replay. **Activity recording.** Clean up running/cycling GPS tracks that zigzag through buildings. **GPS cleanup before analysis.** Map match probe data before travel time or speed studies — raw noise pollutes results.