POST /v1/match
Vue d'ensemble
Le map matching prend une séquence de coordonnées GPS (par ex. issues d'un traceur de véhicule ou d'une application de fitness) et la recale sur l'itinéraire le plus probable du réseau routier réel. Le résultat est une géométrie propre, alignée sur les routes, avec des estimations de distance et de durée.
Requête
POST https://api.geocodeme.net/v1/match
Content-Type: application/json
Headers requis :
X-Api-Key: gm_YOUR_API_KEYContent-Type: application/json
Corps de la requête
| Champ | Type | Requis | Défaut | Description |
|---|---|---|---|---|
coordinates | array | Oui | aucun | Tableau de paires [longitude, latitude] (min 2, max 100 points) |
timestamps | array | Non | aucun | Tableau de timestamps UNIX (secondes) ; doit avoir la même longueur que coordinates |
radiuses | array | Non | aucun | Rayon de recherche en mètres par point de coordonnée (défaut : 5 m par point) |
profile | string | Non | car | Profil de routage : car, bike, foot, truck ou moto, soumis au plan (voir plus bas) |
geometries | string | Non | geojson | Format de la géométrie de sortie : geojson, polyline ou polyline6 |
overview | string | Non | full | Niveau de détail de l'aperçu de l'itinéraire : full, simplified ou false |
attributes | array | Non | aucun | Enrichit la réponse de segments par tronçon : street_names, speed_limit, road_class (sans surcoût en crédits) |
use_tolls | boolean | Non | aucun | Préférer (true) ou éviter (false) les péages, profils motorisés uniquement (400 sur bike/foot) |
truck_height / truck_width / truck_weight | number | Non | aucun | Gabarit du véhicule (m / m / tonnes), exige profile: "truck" (400 sinon) |
Coût : 1 crédit, plus 1 par bloc de 25 points entamé au-delà des 25 premiers (25 points → 1 crédit, 26 → 2, 100 → 4). Voir Crédits & quotas.
Gating par plan : car est disponible sur tous les plans ; bike et
foot exigent Startup ou plus ; truck et moto exigent Growth ou plus.
Un profil hors plan renvoie 403 PLAN_UPGRADE_REQUIRED, sans jamais
consommer de crédit. Avec attributes, la réponse porte un tableau
segments (length_meters + les attributs demandés) ; si l'enrichissement
est temporairement indisponible, segments vaut null et confidence
0.0 ; la géométrie recalée, elle, n'est pas affectée.
Les coordonnées sont dans l'ordre [longitude, latitude] (standard GeoJSON), et non [lat, lon]. C'est l'inverse des paramètres de requête de /v1/geocode et /v1/reverse.
Le tableau coordinates doit contenir au moins 2 et au plus 100 points. L'API renvoie une erreur INVALID_PARAMETER si le tableau est en dehors de cette plage. Pour un point unique, utilisez plutôt /v1/nearest.
Le paramètre de requête format n'est pas pris en charge sur cet endpoint. Le format de la géométrie est contrôlé par le champ geometries du corps de la requête (geojson, polyline ou polyline6).
Exemples de code
- cURL
- Python
- JavaScript
- PHP
curl -X POST \
-H "X-Api-Key: gm_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"coordinates": [
[2.3522, 48.8566],
[2.3488, 48.8534],
[2.3470, 48.8521]
],
"profile": "car",
"geometries": "geojson"
}' \
"https://api.geocodeme.net/v1/match"
import os
import requests
api_key = os.environ["GEOCODEME_API_KEY"]
response = requests.post(
"https://api.geocodeme.net/v1/match",
json={
"coordinates": [
[2.3522, 48.8566],
[2.3488, 48.8534],
[2.3470, 48.8521],
],
"profile": "car",
"geometries": "geojson",
},
headers={"X-Api-Key": api_key},
)
response.raise_for_status()
data = response.json()
print(f"Confidence: {data['confidence']}")
print(f"Distance: {data['distance_meters']}m")
const apiKey = process.env.GEOCODEME_API_KEY;
const response = await fetch("https://api.geocodeme.net/v1/match", {
method: "POST",
headers: {
"X-Api-Key": apiKey,
"Content-Type": "application/json",
},
body: JSON.stringify({
coordinates: [
[2.3522, 48.8566],
[2.3488, 48.8534],
[2.3470, 48.8521],
],
profile: "car",
geometries: "geojson",
}),
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
console.log(`Confidence: ${data.confidence}`);
console.log(`Distance: ${data.distance_meters}m`);
<?php
$apiKey = getenv('GEOCODEME_API_KEY');
$payload = json_encode([
'coordinates' => [
[2.3522, 48.8566],
[2.3488, 48.8534],
[2.3470, 48.8521],
],
'profile' => 'car',
'geometries' => 'geojson',
]);
$ch = curl_init('https://api.geocodeme.net/v1/match');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"X-Api-Key: $apiKey",
'Content-Type: application/json',
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
throw new \RuntimeException("HTTP $httpCode: $response");
}
$data = json_decode($response, true);
echo "Confidence: " . $data['confidence'] . "\n";
echo "Distance: " . $data['distance_meters'] . "m\n";
Réponse
Toutes les réponses incluent les headers de limitation de débit (X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Monthly-Remaining, etc.).
Avec geometries=geojson (défaut) :
{
"matched_coordinates": [
[2.3522, 48.8566],
[2.3510, 48.8555],
[2.3495, 48.8540],
[2.3488, 48.8534],
[2.3475, 48.8525],
[2.3470, 48.8521]
],
"geometry_polyline": null,
"confidence": 0.95,
"distance_meters": 620.4,
"duration_seconds": 78.2,
"attribution": "Route data (c) OpenStreetMap contributors",
"meta": {
"took_ms": 62,
"cache_hit": false
}
}
Avec geometries=polyline (ou polyline6), la géométrie est renvoyée sous forme de chaîne encodée à la place :
{
"matched_coordinates": null,
"geometry_polyline": "u`fiHiuqM^`BhAtEjBdI",
"confidence": 0.95,
"distance_meters": 620.4,
"duration_seconds": 78.2,
"attribution": "Route data (c) OpenStreetMap contributors",
"meta": {
"took_ms": 62,
"cache_hit": false
}
}
Schéma
| Champ | Type | Description |
|---|---|---|
matched_coordinates | array | null | Itinéraire recalé sous forme de paires [[longitude, latitude], ...], renseigné quand geometries=geojson, null sinon |
geometry_polyline | string | null | Chaîne polyline encodée, renseignée quand geometries=polyline ou polyline6, null sinon |
confidence | float | Confiance de l'appariement entre 0 et 1 |
distance_meters | float | Distance totale de l'itinéraire recalé en mètres |
duration_seconds | float | Durée de trajet en secondes. Estimée d'après le réseau routier, ou, si vous envoyez timestamps, lue dans ceux-ci : le temps écoulé entre votre premier et votre dernier point apparié, arrêts compris |
attribution | string | Toujours "Route data (c) OpenStreetMap contributors" |
meta.took_ms | integer | Temps de traitement serveur en millisecondes |
meta.cache_hit | boolean | Indique si la réponse a été servie depuis le cache |
Avec overview=false, matched_coordinates et geometry_polyline valent tous deux null ; seuls la confiance, la distance et la durée sont renvoyées.
Traces comportant des coupures
Certaines traces ne peuvent pas être appariées comme un trajet continu. Le cas courant est le demi-tour : la route ne peut pas être raccordée à elle-même en sens inverse, le moteur parcourt donc la trace en plusieurs morceaux. Les allers-retours et les boucles comportent ainsi au moins une coupure.
La réponse décrit malgré tout la trace entière. matched_coordinates et
geometry_polyline la couvrent d'un bout à l'autre, reliés par une ligne
droite au droit de chaque coupure, et distance_meters compte tous les
morceaux. Seuls les index de segments portent la trace de la couture, comme
décrit ci-dessous.
Pour savoir où sont les coupures, repérez les ruptures dans les index de
segments : un segment dont le from_index n'est pas le to_index précédent
ouvre un nouveau morceau.
Segments (attributes)
Lorsque attributes est demandé, la réponse porte un tableau segments, une
entrée par tronçon de route apparié :
{
"segments": [
{
"street_names": ["Quai Perrache"],
"road_class": "primary",
"length_meters": 650.0,
"speed_limit_kph": null,
"from_index": 0,
"to_index": 12
},
{
"street_names": ["A7", "E15"],
"road_class": "motorway",
"length_meters": 4300.0,
"speed_limit_kph": 130,
"from_index": 12,
"to_index": 47
}
]
}
| Champ | Type | Description |
|---|---|---|
length_meters | float | Longueur du tronçon en mètres |
street_names | array | null | Noms de rue, si street_names a été demandé |
speed_limit_kph | integer | null | Vitesse limite, si speed_limit a été demandé et est connue |
road_class | string | null | Classe de route, si road_class a été demandé |
from_index | integer | null | Index du premier sommet du tronçon dans la géométrie renvoyée par cette réponse |
to_index | integer | null | Index du dernier sommet du tronçon dans la même géométrie |
from_index et to_index situent le tronçon sur la géométrie renvoyée dans
la même réponse, décodée. Les deux bornes sont inclusives : deux segments
consécutifs partagent donc normalement leur sommet de jonction, soit
segments[i].to_index == segments[i+1].from_index. Le premier segment commence
à 0, le dernier se termine à len(geometry) - 1.
Seule exception, une coupure (voir plus haut) : là où la trace n'a pas pu être
appariée d'un bout à l'autre, le segment suivant démarre un sommet plus loin
(segments[i+1].from_index == segments[i].to_index + 1). Parcourez le tableau
avec from_index plutôt qu'en supposant le to_index précédent.
La géométrie et les attributs proviennent de deux appels moteur distincts.
Lorsque leurs formes ne peuvent pas être prouvées identiques, from_index et
to_index sont omis plutôt qu'approximés : un index désignant une forme qui
n'a pas été servie nommerait le mauvais tronçon de route tout en paraissant
parfaitement valide. Ils sont également absents avec overview=false,
puisqu'aucune géométrie n'est renvoyée.
Traitez leur absence comme « position inconnue » et repliez-vous en
conséquence. Ne reconstruisez jamais une position en cumulant length_meters :
cette valeur vaut 0 quand la donnée manque, le résultat serait faux sans
que rien ne le signale.
Réponses d'erreur
{
"error": {
"code": "INVALID_PARAMETER",
"message": "Parameter 'coordinates' must contain between 2 and 100 coordinate pairs",
"field": "coordinates"
}
}
Tous les endpoints sont également soumis aux erreurs INVALID_API_KEY (401) et RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED (429). Consultez Codes d'erreur pour la référence complète.