Map Matching
Dieses Beispiel gleicht eine Reihe von rohen Punkten mit dem Straßennetz ab – nützlich, um verrauschte GPS-Tracks eines Trackinggeräts zu bereinigen. Klicken Sie entlang einer Straße, um Punkte zu setzen, und gleichen Sie sie anschließend mit dem Straßennetz ab.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://cdn.smartmaps.cloud/packages/smartmaps/smartmaps-gl/v2/umd/smartmaps-gl.min.js"></script>
<style>
body {
margin: 0;
padding: 0;
}
#map {
height: 100vh;
}
#controls {
position: absolute;
top: 10px;
left: 10px;
z-index: 1;
background: rgba(255, 255, 255, 0.9);
padding: 10px;
border-radius: 4px;
font: 13px sans-serif;
max-width: 240px;
}
#match-button,
#reset-button {
margin-top: 8px;
margin-right: 6px;
background-color: #18345c;
color: white;
border: none;
padding: 8px 14px;
cursor: pointer;
border-radius: 4px;
}
#match-button:disabled {
background-color: #9aa5b1;
cursor: default;
}
.raw-point {
width: 10px;
height: 10px;
border-radius: 50%;
background: #9aa5b1;
border: 2px solid #fff;
}
</style>
</head>
<body>
<div id="controls">
<strong>Click along a street to add GPS points</strong>
<div id="status">0 points added.</div>
<button id="match-button" disabled>Match to road</button>
<button id="reset-button">Reset</button>
</div>
<div id="map"></div>
<script>
const map = new smartmapsgl.Map({
apiKey: '[INSERT API-KEY]',
container: 'map',
center: { lat: 49.02164948779226, lng: 8.439330018049352 },
zoom: 16,
style: smartmapsgl.MapStyle.LIGHT
});
const statusEl = document.getElementById('status');
const matchButton = document.getElementById('match-button');
let points = [];
let rawMarkers = [];
function removeMatchLayer() {
if (map.getLayer('matched-route')) map.removeLayer('matched-route');
if (map.getSource('matched-route')) map.removeSource('matched-route');
}
function reset() {
points = [];
rawMarkers.forEach((marker) => marker.remove());
rawMarkers = [];
removeMatchLayer();
matchButton.disabled = true;
statusEl.textContent = '0 points added.';
}
document.getElementById('reset-button').addEventListener('click', reset);
map.on('click', (event) => {
points.push({ lng: event.lngLat.lng, lat: event.lngLat.lat });
const el = document.createElement('div');
el.className = 'raw-point';
rawMarkers.push(new smartmapsgl.Marker({ element: el }).setLngLat(event.lngLat).addTo(map));
statusEl.textContent = `${points.length} point${points.length === 1 ? '' : 's'} added.`;
matchButton.disabled = points.length < 2;
});
matchButton.addEventListener('click', async () => {
statusEl.textContent = 'Matching to road network…';
try {
const response = await fetch(
'https://www.yellowmap.de/api_rst/v2/geojson/route?apiKey=[INSERT API-KEY]',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
type: 'FeatureCollection',
routingparams: {
type: 'MATCH',
isoLocale: 'en-GB',
coordFormatOut: 'GEODECIMAL_POINT',
speedProfile: 'FAST',
routingTimeMode: 'ARRIVAL',
channel: ''
},
authentication: { channel: '' },
crs: { type: 'name', properties: { name: 'urn:ogc:def:crs:OGC::CRS84' } },
features: [
{
type: 'Feature',
geometry: {
type: 'MultiPoint',
coordinates: points.map((p) => [p.lng, p.lat])
}
}
]
})
}
);
if (!response.ok) throw new Error(`Server error: ${response.status}`);
const result = await response.json();
const matched = result.features[0];
const destinations = matched.properties.matchDestinations;
const matchedCount = destinations.filter((d) => d.isMatched).length;
removeMatchLayer();
map.addSource('matched-route', { type: 'geojson', data: matched });
map.addLayer({
id: 'matched-route',
type: 'line',
source: 'matched-route',
layout: { 'line-join': 'round', 'line-cap': 'round' },
paint: { 'line-color': '#0A5FA0', 'line-width': 5 }
});
// distance/duration live on the FeatureCollection's top-level
// properties, NOT on the matched feature's properties
// (which only holds matchDestinations).
const km = (result.properties.distance / 1000).toFixed(2);
statusEl.textContent = `Matched ${matchedCount}/${destinations.length} points. Route: ${km} km.`;
} catch (error) {
console.error('Error matching points:', error);
statusEl.textContent = 'Failed to match points. Please try again.';
}
});
</script>
</body>
</html>
Hinweise
- Die Eingabe ist ein einzelnes
MultiPoint-Feature, das alle rohen Punkte zusammenfasst – nicht einPoint-Feature pro Punkt. - Die Antwort enthält ein Feature: eine
LineString-Geometrie für die abgeglichene Route, sowieproperties.matchDestinationsmit einem Eintrag pro Eingabepunkt in derselben Reihenfolge, in der sie gesendet wurden – jeweils mitisMatched,distance(Entfernung vom ursprünglichen Punkt zum abgeglichenen Punkt in Metern),inPointundmatchedPoint. - Ein Punkt kann nicht zugeordnet werden (
isMatched: false), wenn er zu weit von jeder Straße entfernt liegt – prüfen Sie das, statt davon auszugehen, dass jeder Punkt zugeordnet wurde.
Die vollständige Parameterliste finden Sie in der Map-Matching-Referenz.
Anders als Route, Trip, Isochrone und Matrix gibt es für Map Matching keine Wrapper-Klasse im
@smartmaps/routing-npm-Paket – rufen Sie den REST-Endpunkt direkt auf, wie oben gezeigt.