andydefer / php-locationiq
PHP SDK for LocationIQ and Nominatim APIs: balance, timezone, directions, and reverse geocoding with typed requests, responses, and data objects
Requires
- php: ^8.2
- andydefer/php-services: ^1.6.35
- jenssegers/agent: ^2.6
Requires (Dev)
- guzzlehttp/guzzle: ^7.8
- larastan/larastan: ^3.8
- laravel/pint: ^1.26
- phpunit/phpunit: ^12.5
- rector/rector: *
- symfony/var-dumper: ^7.0
- vimeo/psalm: ^6.14
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
SDK PHP pour l'intégration des services LocationIQ (Balance, Timezone, Directions, Matrix) et Nominatim (Reverse Geocoding).
Table des matières
- Introduction
- Installation
- Configuration
- Architecture
- Opérations LocationIQ
- Opérations Nominatim
- Value Objects
- Enums
- Gestion des erreurs
- Intégration Laravel
- Références techniques
- Licence
Introduction
Qu'est-ce que LocationIQ ?
LocationIQ est une plateforme de services géospatiaux : géocodage, reverse geocoding, calcul d'itinéraires, fuseaux horaires, matrices de distances, équilibrage et quotas. Elle s'appuie sur les données OpenStreetMap et propose une API REST simple, avec une clé unique.
Qu'est-ce que Nominatim ?
Nominatim est le service de géocodage officiel d'OpenStreetMap. Il propose du geocoding et du reverse geocoding gratuits, sans clé API, soumis à une politique d'usage stricte (User-Agent identifiable, rate limit d'une requête par seconde). Ce SDK utilise l'instance publique nominatim.openstreetmap.org.
Ce que fait ce SDK
Ce SDK transforme les appels HTTP bruts vers LocationIQ et Nominatim en objets PHP typés, validés et documentés. Il expose deux clients haut niveau :
LocationIqClient— pour les services Balance, Timezone, Directions et Matrix.NominatimClient— pour le reverse geocoding OpenStreetMap.
Bénéfices
| Sans SDK | Avec SDK |
|---|---|
json_decode() manuel |
Objets PHP typés (TimezoneData, BalanceData, DirectionsData, MatrixResponseData, ReverseData) |
| Validation manuelle | Value Objects auto-validants (LocationVO, BoundingBoxVO, MatrixOptionsVO, FloatMatrixVO) |
| Strings magiques | Enums (DirectionsProfile, OverviewType, GeometriesType, MatrixAnnotation, FallbackCoordinate, NominatimFormat) |
| Tableaux imbriqués non typés | Collections et Graphs typés (WaypointGraphCollection, FloatMatrixCollection, AddressGraph) |
| Mélange HTTP / parsing | Séparation stricte Request / Response / Graph / Data |
| Format polymorphe ignoré | Détection automatique objet vs. tableau |
| Erreurs HTTP non gérées | hasError() / getError() uniformes |
| User-Agent Nominatim géré à la main | Généré automatiquement via jenssegers/agent |
Compatibilité PHP
| Version | Support |
|---|---|
| PHP 8.2+ | ✅ Complet |
| PHP 8.1 | ❌ Non supporté (propriétés readonly promues requises) |
Installation
composer require andydefer/php-locationiq
Prérequis :
- PHP 8.2 ou supérieur
- Extension
json - Extension
curl(via Guzzle) - Une clé API LocationIQ valide (pour
LocationIqClient) jenssegers/agent(pourNominatimClient)
Configuration
Création du client LocationIQ
<?php declare(strict_types=1); use AndyDefer\PhpLocationIq\Enums\LocationIqBaseUrl; use AndyDefer\PhpLocationIq\LocationIqClient; $client = new LocationIqClient( apiKey: $_ENV['LOCATIONIQ_API_KEY'], baseUrl: LocationIqBaseUrl::US1, // ou EU1 );
Création du client Nominatim
<?php declare(strict_types=1); use AndyDefer\PhpLocationIq\NominatimClient; use Jenssegers\Agent\Agent; $client = new NominatimClient(new Agent()); $client->setUserAgent('MonApp/1.0 (contact@exemple.com)');
Nominatim impose un User-Agent identifiable en production. Utiliser
setUserAgent()pour annoncer explicitement l'application émettrice.
Variables d'environnement recommandées
LOCATIONIQ_API_KEY=pk.xxxxxxxxxxxxxxxxxxxxxxxx LOCATIONIQ_BASE_URL=us1 NOMINATIM_USER_AGENT=MonApp/1.0 (contact@exemple.com)
Ne jamais committer une clé API dans le code source. Utiliser un gestionnaire de secrets en production.
Changement de région à la volée
$client->setBaseUrl(LocationIqBaseUrl::EU1);
Architecture
┌─────────────────────────────────────────────────────────────────┐
│ Votre code métier │
└──────────────┬──────────────────────────────────┬───────────────┘
│ │
▼ ▼
┌──────────────────────────┐ ┌──────────────────────────┐
│ LocationIqClient │ │ NominatimClient │
│ (Balance / Timezone / │ │ (Reverse Geocoding) │
│ Directions / Matrix) │ │ │
└─────────────┬────────────┘ └─────────────┬────────────┘
│ │
▼ ▼
┌──────────────────────────┐ ┌──────────────────────────┐
│ Request → Response │ │ Request → Response │
│ → Graph → Data │ │ → Graph → Data │
└─────────────┬────────────┘ └─────────────┬────────────┘
│ │
▼ ▼
┌──────────────────────────┐ ┌──────────────────────────┐
│ API LocationIQ │ │ API Nominatim │
│ us1/e u1.locationiq.com │ │ nominatim.osm.org │
└──────────────────────────┘ └──────────────────────────┘
Composants
| Couche | Composant | Rôle |
|---|---|---|
| Client | LocationIqClient, NominatimClient |
Communication HTTP, Record → Response |
| Requests | BalanceRequest, TimezoneRequest, DirectionsRequest, MatrixRequest, ReverseRequest |
Construction des URLs et query strings |
| Responses | BalanceResponse, TimezoneResponse, DirectionsResponse, MatrixResponse, ReverseResponse |
Parsing et hydratation des réponses |
| Records | TimezoneRecord, DirectionsRecord, MatrixRecord, ReverseRecord |
Entrées des clients |
| Datas | TimezoneData, BalanceData, DirectionsData, MatrixResponseData, MatrixWaypointData, ReverseData, AddressData |
Sorties typées métier |
| Graphs | WaypointGraph, RouteGraph, LegGraph, StepGraph, ManeuverGraph, IntersectionGraph, AddressGraph, ReverseGraph |
Portions de réponse |
| Collections | WaypointGraphCollection, RouteGraphCollection, LegGraphCollection, StepGraphCollection, IntersectionGraphCollection, LocationVOCollection, MatrixAnnotationCollection, FloatMatrixCollection, FloatTypedCollection, IntTypedCollection, MatrixWaypointDataCollection |
Ensembles typés |
| Value Objects | LocationVO, BoundingBoxVO, MatrixOptionsVO, FloatMatrixVO |
Coordonnées et options validées |
| Enums | LocationIqBaseUrl, DirectionsProfile, OverviewType, GeometriesType, MatrixAnnotation, FallbackCoordinate, Endpoint, NominatimBaseUrl, NominatimEndpoint, NominatimFormat |
Choix typés |
Opérations LocationIQ
1. Récupérer le solde
Retourne le nombre de crédits de requêtes restants pour la journée UTC courante.
Endpoint : GET /v1/balance
<?php declare(strict_types=1); use AndyDefer\PhpLocationIq\Enums\LocationIqBaseUrl; use AndyDefer\PhpLocationIq\LocationIqClient; $client = new LocationIqClient( apiKey: $_ENV['LOCATIONIQ_API_KEY'], baseUrl: LocationIqBaseUrl::US1, ); $response = $client->getBalance(); if ($response->hasError()) { throw new RuntimeException($response->getError()); } echo $response->getStatus(); // 'ok' echo $response->getDayBalance(); // 30000
Champs du BalanceData retourné
| Propriété | Type | Description |
|---|---|---|
$day |
int |
Crédits restants pour la journée UTC |
2. Résoudre un fuseau horaire
Retourne le fuseau horaire (nom, offset, DST) pour un couple de coordonnées GPS.
Endpoint : GET /v1/timezone
<?php declare(strict_types=1); use AndyDefer\PhpLocationIq\Records\TimezoneRecord; use AndyDefer\PhpVo\ValueObjects\CoordinatesVO; use AndyDefer\PhpVo\ValueObjects\Types\FloatVO; $response = $client->getTimezone(new TimezoneRecord( coordinates: new CoordinatesVO( FloatVO::from(19.0760), FloatVO::from(72.8777), ), )); if ($response->hasError()) { throw new RuntimeException($response->getError()); } echo $response->getName(); // 'Asia/Kolkata' echo $response->getShortName(); // 'IST' echo $response->getFullName(); // 'India Standard Time' echo $response->getOffsetSeconds(); // 19800 echo $response->isInDst() ? 'DST' : 'STD';
Timestamp optionnel
$response = $client->getTimezone(new TimezoneRecord( coordinates: new CoordinatesVO( FloatVO::from(19.0760), FloatVO::from(72.8777), ), timestamp: 1609459200, // 1er janvier 2021 UTC ));
Champs du TimezoneData retourné
| Propriété | Type | Description |
|---|---|---|
$name |
string |
Nom IANA du fuseau (Asia/Kolkata) |
$nowInDst |
bool |
Le point est-il actuellement en DST |
$offsetSeconds |
int |
Décalage UTC en secondes |
$shortName |
string |
Abréviation (IST) |
$fullName |
string |
Nom complet (India Standard Time) |
3. Calculer un itinéraire
Calcule une route entre 2 et 25 coordonnées selon un profil de transport.
Endpoint : GET /v1/directions/{profile}/{coordinates}
<?php declare(strict_types=1); use AndyDefer\PhpLocationIq\Collections\LocationVOCollection; use AndyDefer\PhpLocationIq\Enums\DirectionsProfile; use AndyDefer\PhpLocationIq\Enums\GeometriesType; use AndyDefer\PhpLocationIq\Enums\OverviewType; use AndyDefer\PhpLocationIq\Records\DirectionsRecord; use AndyDefer\PhpLocationIq\ValueObjects\LocationVO; $coordinates = new LocationVOCollection; $coordinates->add(LocationVO::fromArray([15.3222, -4.3250])); $coordinates->add(LocationVO::fromArray([15.4446, -4.3858])); $response = $client->getDirections(new DirectionsRecord( coordinates: $coordinates, profile: DirectionsProfile::DRIVING, overview: OverviewType::FULL, steps: true, alternatives: false, geometries: GeometriesType::POLYLINE, )); if ($response->hasError()) { throw new RuntimeException($response->getError()); } foreach ($response->getRoutes() as $route) { printf("Distance: %.2f m, Durée: %.2f s\n", $route->distance, $route->duration); foreach ($route->legs as $leg) { foreach ($leg->steps as $step) { echo $step->maneuver?->type . ' — ' . $step->name . "\n"; } } }
Profils supportés
| Profil | Description |
|---|---|
DirectionsProfile::DRIVING |
Voiture |
DirectionsProfile::WALKING |
Piéton |
Options
| Paramètre | Type | Valeurs | Description |
|---|---|---|---|
$profile |
DirectionsProfile |
DRIVING, WALKING |
Mode de transport |
$overview |
OverviewType |
SIMPLIFIED, FULL, FALSE |
Précision de la géométrie globale |
$steps |
bool |
true, false |
Inclure les étapes détaillées |
$alternatives |
bool |
true, false |
Retourner des itinéraires alternatifs |
$geometries |
GeometriesType |
POLYLINE, POLYLINE6, GEOJSON |
Format de la géométrie |
Champs du DirectionsData retourné
| Propriété | Type | Description |
|---|---|---|
$code |
string |
ok ou code d'erreur (InvalidOptions, etc.) |
$waypoints |
WaypointDataCollection |
Points de passage |
$routes |
RouteDataCollection |
Itinéraires calculés |
Polymorphisme géré : LocationIQ retourne tantôt un objet unique, tantôt un tableau. Le SDK normalise toujours en collection typée.
4. Calculer une matrice de durées et distances
Calcule les durées et/ou distances des trajets les plus rapides entre toutes les paires de coordonnées fournies. Utile pour comparer rapidement plusieurs points (livraisons, tournées, répartition géographique).
Endpoint : GET /v1/matrix/{profile}/{coordinates}
⚠️ Les coordonnées doivent être fournies dans l'ordre
longitude,latitude— comme pour Directions.
Exemple minimal — matrice 3×3 de durées
<?php declare(strict_types=1); use AndyDefer\PhpLocationIq\Collections\LocationVOCollection; use AndyDefer\PhpLocationIq\Records\MatrixRecord; use AndyDefer\PhpLocationIq\ValueObjects\LocationVO; use AndyDefer\PhpLocationIq\ValueObjects\MatrixOptionsVO; $coordinates = new LocationVOCollection; $coordinates->add(LocationVO::fromArray([-0.127627, 51.503355])); $coordinates->add(LocationVO::fromArray([-0.087199, 51.509562])); $coordinates->add(LocationVO::fromArray([-0.142001, 51.501284])); $options = MatrixOptionsVO::create(coordinates: $coordinates); $response = $client->getMatrix(new MatrixRecord(options: $options)); if ($response->hasError()) { throw new RuntimeException($response->getError()); } $durations = $response->getDurations(); if ($durations !== null) { foreach ($durations->getRows() as $rowIndex => $row) { foreach ($row as $colIndex => $value) { if ($durations->isNoRoute($rowIndex, $colIndex)) { echo "[$rowIndex][$colIndex] : pas de route\n"; continue; } echo sprintf("[%d][%d] : %.1f s\n", $rowIndex, $colIndex, $value); } } }
Demander durées et distances
use AndyDefer\PhpLocationIq\Collections\MatrixAnnotationCollection; use AndyDefer\PhpLocationIq\Enums\MatrixAnnotation; $annotations = new MatrixAnnotationCollection; $annotations->add(MatrixAnnotation::DURATION); $annotations->add(MatrixAnnotation::DISTANCE); $options = MatrixOptionsVO::create( coordinates: $coordinates, annotations: $annotations, ); $response = $client->getMatrix(new MatrixRecord(options: $options)); $durations = $response->getDurations(); // secondes $distances = $response->getDistances(); // mètres
Restreindre les sources et destinations
Par défaut, LocationIQ calcule la matrice complète N×N. Tu peux restreindre avec des index de coordonnées :
use AndyDefer\DomainStructures\Collections\Utility\IntTypedCollection; $sources = new IntTypedCollection; $sources->add(0); // utilise uniquement la coordonnée #0 comme source $options = MatrixOptionsVO::create( coordinates: $coordinates, sources: $sources, ); // Résultat : matrice 1×3 au lieu de 3×3
C'est utile quand tu as un entrepôt fixe et plusieurs destinations. Tu économises du quota et du temps de calcul.
Options complètes
| Paramètre | Type | Valeurs | Description |
|---|---|---|---|
$coordinates |
LocationVOCollection |
2 à 25 coordonnées | Points à comparer |
$profile |
DirectionsProfile |
DRIVING, WALKING |
Profil de transport (sur MatrixRecord) |
$annotations |
MatrixAnnotationCollection |
DURATION, DISTANCE |
Matrices à calculer (au moins une) |
$sources |
?IntTypedCollection |
Index 0..N-1 | Restreindre les sources |
$destinations |
?IntTypedCollection |
Index 0..N-1 | Restreindre les destinations |
$fallbackSpeed |
?float |
> 0 | Vitesse de secours (m/s) pour les paires non routables |
$fallbackCoordinate |
?FallbackCoordinate |
INPUT, SNAPPED |
Coordonnée utilisée en mode fallback |
Fallback : que faire quand aucune route n'existe ?
Par défaut, si LocationIQ ne trouve pas de route entre deux points, la cellule est null. Avec un fallbackSpeed, il calcule la distance à vol d'oiseau et divise par cette vitesse pour estimer une durée :
use AndyDefer\PhpLocationIq\Enums\FallbackCoordinate; $options = MatrixOptionsVO::create( coordinates: $coordinates, fallbackSpeed: 15.5, // 15.5 m/s ≈ 56 km/h fallbackCoordinate: FallbackCoordinate::SNAPPED, // utilise la coordonnée snappée );
INPUT: utilise la coordonnée que tu as envoyée.SNAPPED: utilise la coordonnée projetée sur le réseau routier le plus proche (plus cohérent avec le reste de la matrice).
Le fallback garantit qu'aucune cellule n'est vide — tu obtiens toujours une estimation, jamais un trou.
Lire une cellule précise
Le VO FloatMatrixVO expose deux helpers pour lire une cellule et tester le sentinel :
$durations = $response->getDurations(); if ($durations !== null) { $value = $durations->getCell(0, 1); // float ou NO_ROUTE_SENTINEL (-1.0) $noRoute = $durations->isNoRoute(0, 1); // bool }
Champs du MatrixResponseData retourné
| Propriété | Type | Description |
|---|---|---|
$durations |
?FloatMatrixVO |
Matrice des durées en secondes, ou null |
$distances |
?FloatMatrixVO |
Matrice des distances en mètres, ou null |
$sources |
MatrixWaypointDataCollection |
Waypoints sources résolus (nom, coordonnée snappée, distance au point saisi) |
$destinations |
MatrixWaypointDataCollection |
Waypoints destinations résolus |
$error |
?string |
Code d'erreur (NoTable, NotImplemented) ou null |
Champs d'un MatrixWaypointData
| Propriété | Type | Description |
|---|---|---|
$name |
?string |
Nom du lieu le plus proche (Downing Street) |
$distance |
?float |
Distance entre la coordonnée saisie et la coordonnée snappée, en mètres |
$longitude |
?float |
Longitude de la coordonnée snappée |
$latitude |
?float |
Latitude de la coordonnée snappée |
$hint |
?string |
Hint opaque retourné par LocationIQ |
Points importants
- La matrice n'est pas symétrique.
durations[0][1]peut différer dedurations[1][0]à cause des sens uniques, priorités aux carrefours et feux de circulation. C'est la réalité du trafic — et c'est précisément l'intérêt de l'endpoint Matrix. - La diagonale est toujours à
0.0. Le trajet d'un point vers lui-même est nul. - Les distances ne sont pas à vol d'oiseau. Ce sont les longueurs du chemin routier le plus rapide.
- 2 à 25 coordonnées. Une
InvalidArgumentExceptionest levée au-delà. - Au moins une annotation.
DURATIONest la valeur par défaut.
Codes d'erreur spécifiques
| Code | Signification |
|---|---|
NoTable |
Aucune route trouvée entre les coordonnées fournies |
NotImplemented |
La requête n'est pas supportée pour ce profil ou ces options |
Opérations Nominatim
1. Reverse Geocoding
Résout une adresse structurée (rue, ville, commune, pays) à partir d'un couple de coordonnées GPS.
Endpoint : GET https://nominatim.openstreetmap.org/reverse
<?php declare(strict_types=1); use AndyDefer\PhpLocationIq\NominatimClient; use AndyDefer\PhpLocationIq\Records\Nominatim\ReverseRecord; use AndyDefer\PhpVo\ValueObjects\CoordinatesVO; use AndyDefer\PhpVo\ValueObjects\Types\FloatVO; use Jenssegers\Agent\Agent; $client = new NominatimClient(new Agent()); $client->setUserAgent('MonApp/1.0 (contact@exemple.com)'); $response = $client->reverse(new ReverseRecord( coordinates: new CoordinatesVO( FloatVO::from(-4.3617), FloatVO::from(15.2183), ), )); if ($response->hasError()) { throw new RuntimeException($response->getError()); } echo $response->getDisplayName() . "\n"; // 'Kasi, Lukunga, Ngaliema, Kinshasa, République démocratique du Congo' $address = $response->getAddress(); if ($address !== null) { echo $address->cityDistrict . "\n"; // 'Kasi' echo $address->municipality . "\n"; // 'Ngaliema' echo $address->state . "\n"; // 'Kinshasa' echo $address->country . "\n"; // 'République démocratique du Congo' echo $address->countryCode . "\n"; // 'cd' }
Options
| Paramètre | Type | Valeurs | Description |
|---|---|---|---|
$coordinates |
CoordinatesVO |
— | Latitude et longitude du point |
$format |
NominatimFormat |
JSON, JSONV2, GEOJSON, GEOCOD EJSON |
Format de la réponse |
$acceptLanguage |
?string |
Code ISO 639-1 | Langue préférée de la réponse |
$zoom |
?int |
0 à 18 | Niveau de détail administratif |
$addressDetails |
?bool |
true, false |
Inclure le bloc address |
Champs du ReverseData retourné
| Propriété | Type | Description |
|---|---|---|
$licence |
string |
Licence des données OSM |
$osmType |
string |
Type OSM (node, way, relation) |
$osmId |
int |
Identifiant OSM |
$location |
?LocationVO |
Coordonnées résolues |
$category |
string |
Catégorie OSM (highway, place, etc.) |
$type |
string |
Type OSM (residential, city, etc.) |
$placeRank |
int |
Importance relative du lieu |
$importance |
float |
Score d'importance |
$addressType |
string |
Type d'adresse |
$name |
string |
Nom du lieu (peut être vide) |
$displayName |
string |
Adresse complète lisible |
$address |
?AddressData |
Adresse décomposée |
$boundingBox |
?BoundingBoxVO |
Zone englobante |
Champs du AddressData retourné
Toutes les propriétés sont nullables. Nominatim ne renvoie que les niveaux administratifs disponibles pour la zone géographique.
| Propriété | Type | Description |
|---|---|---|
$houseNumber |
?string |
Numéro de rue |
$road |
?string |
Nom de rue |
$neighbourhood |
?string |
Quartier |
$suburb |
?string |
Banlieue |
$cityDistrict |
?string |
District urbain |
$city |
?string |
Ville |
$municipality |
?string |
Municipalité |
$county |
?string |
Comté |
$stateDistrict |
?string |
District d'État |
$state |
?string |
État / région |
$iso3166Lvl4 |
?string |
Code ISO 3166-2 niveau 4 |
$postcode |
?string |
Code postal |
$country |
?string |
Pays |
$countryCode |
?string |
Code pays ISO 3166-1 alpha-2 |
Value Objects
Les Value Objects valident les données à leur construction. Une valeur invalide déclenche immédiatement une InvalidArgumentException.
| VO | Validation | Exemple |
|---|---|---|
LocationVO |
Exactement 2 floats numériques, ordre [longitude, latitude] |
LocationVO::fromArray([15.3222, -4.3250]) |
BoundingBoxVO |
Exactement 4 floats numériques, ordre [minLat, maxLat, minLon, maxLon] |
BoundingBoxVO::fromArray([-4.36, -4.35, 15.21, 15.22]) |
MatrixOptionsVO |
2 à 25 coordonnées, au moins une annotation, index sources/destinations dans la plage, fallbackSpeed > 0 |
MatrixOptionsVO::create(coordinates: $coords) |
FloatMatrixVO |
Matrice rectangulaire (toutes les lignes ont la même longueur) | new FloatMatrixVO($rows) |
Exemple d'utilisation
use AndyDefer\PhpLocationIq\ValueObjects\LocationVO; use AndyDefer\PhpLocationIq\ValueObjects\BoundingBoxVO; $location = LocationVO::fromArray([15.3222, -4.3250]); echo $location->getLongitude(); // 15.3222 echo $location->getLatitude(); // -4.325 echo (string) $location; // "15.322200,-4.325000" $bbox = BoundingBoxVO::fromArray([-4.3631, -4.3619, 15.2171, 15.2186]); echo $bbox->getMinLatitude(); // -4.3631 echo $bbox->getMaxLatitude(); // -4.3619 echo $bbox->getMinLongitude(); // 15.2171 echo $bbox->getMaxLongitude(); // 15.2186
Constantes exposées
| VO | Constante | Valeur | Description |
|---|---|---|---|
MatrixOptionsVO |
MIN_COORDINATES |
2 |
Nombre minimum de coordonnées acceptées |
MatrixOptionsVO |
MAX_COORDINATES |
25 |
Nombre maximum de coordonnées acceptées |
FloatMatrixVO |
NO_ROUTE_SENTINEL |
-1.0 |
Valeur utilisée pour les cellules sans route |
Enums
| Enum | Description | Exemples |
|---|---|---|
LocationIqBaseUrl |
URLs régionales LocationIQ | US1, EU1 |
DirectionsProfile |
Modes de transport | DRIVING, WALKING |
OverviewType |
Précision de la géométrie | SIMPLIFIED, FULL, FALSE |
GeometriesType |
Format de la géométrie | POLYLINE, POLYLINE6, GEOJSON |
MatrixAnnotation |
Matrices à calculer pour l'endpoint Matrix | DURATION, DISTANCE |
FallbackCoordinate |
Coordonnée utilisée en mode fallback | INPUT, SNAPPED |
Endpoint |
Chemins d'API LocationIQ | TIMEZONE, DIRECTIONS, MATRIX, BALANCE |
NominatimBaseUrl |
URLs Nominatim | PUBLIC |
NominatimEndpoint |
Chemins d'API Nominatim | REVERSE |
NominatimFormat |
Formats de réponse Nominatim | JSON, JSONV2, GEOJSON, GEOCODEJSON |
Gestion des erreurs
Aucune exception métier n'est levée pour les erreurs renvoyées par LocationIQ ou Nominatim. Elles sont accessibles via les méthodes hasError() et getError() de la Response.
Codes d'erreur LocationIQ
| Code HTTP | getError() |
Signification |
|---|---|---|
| 400 | Invalid Request |
Paramètres manquants ou malformés |
| 401 | Invalid Key |
Clé API absente ou invalide |
| 403 | Access restricted |
Clé non autorisée pour l'endpoint |
| 404 | Unable to geocode |
Aucun résultat pour ces coordonnées |
| 429 | Rate Limited Day |
Quota journalier épuisé |
| 429 | Rate Limited Second |
Trop de requêtes dans la même seconde |
| 500 | Unknown error - Please try again after some time |
Erreur serveur |
Codes d'erreur Nominatim
| Code HTTP | getError() |
Signification |
|---|---|---|
| 400 | Unable to geocode |
Coordonnées invalides ou hors zone |
| 403 | (message Nominatim) | Requête bloquée (User-Agent manquant ou rate limit) |
| 429 | (message Nominatim) | Trop de requêtes |
| 500 | (message Nominatim) | Erreur serveur |
Erreurs Directions et Matrix (HTTP 200)
LocationIQ peut renvoyer un code d'erreur dans le champ code même avec un HTTP 200 :
$response = $client->getDirections($record); if ($response->hasError()) { echo $response->getError(); // 'InvalidOptions' } $response = $client->getMatrix($matrixRecord); if ($response->hasError()) { echo $response->getError(); // 'NoTable' ou 'NotImplemented' }
Rate limiting
LocationIQ impose un délai minimum entre deux requêtes consécutives sur les plans gratuits (Rate Limited Second). Sur les plans payants, cette limite disparaît. Si tu enchaînes plusieurs appels (par exemple plusieurs getMatrix()), ajoute un délai :
usleep(1_100_000); // 1,1 seconde
Nominatim applique la même politique : une requête par seconde maximum. Ne jamais paralléliser les appels Nominatim.
Erreurs de validation côté SDK
| Situation | Exception | Message |
|---|---|---|
| Coordonnées Directions < 2 | InvalidArgumentException |
Directions require at least 2 coordinates. |
| Coordonnées Directions > 25 | InvalidArgumentException |
Directions accept at most 25 coordinates, {n} given. |
| Coordonnées Matrix < 2 | InvalidArgumentException |
Matrix requires at least 2 coordinates, {n} given. |
| Coordonnées Matrix > 25 | InvalidArgumentException |
Matrix accepts at most 25 coordinates, {n} given. |
| Annotations Matrix vides | InvalidArgumentException |
Matrix requires at least one annotation (duration or distance). |
| Index source Matrix hors plage | InvalidArgumentException |
Matrix sources index {i} is out of range [0, {max}]. |
| Index destination Matrix hors plage | InvalidArgumentException |
Matrix destinations index {i} is out of range [0, {max}]. |
| Vitesse fallback Matrix ≤ 0 | InvalidArgumentException |
Matrix fallback_speed must be greater than 0, {value} given. |
| Matrice non rectangulaire | InvalidArgumentException |
Matrix rows must have the same length. Expected {n}, got {m}. |
| Coordonnées LocationVO ≠ 2 floats | InvalidArgumentException |
Location must contain exactly 2 floats, {n} given. |
| Coordonnée LocationVO non numérique | InvalidArgumentException |
Location coordinate must be numeric, {type} given. |
| BoundingBoxVO ≠ 4 floats | InvalidArgumentException |
BoundingBox must contain exactly 4 floats, {n} given. |
| BoundingBoxVO non numérique | InvalidArgumentException |
BoundingBox coordinate must be numeric, {type} given. |
| Erreur réseau (Guzzle) | RuntimeException |
HTTP request failed: {message} |
Intégration Laravel
Service Provider
<?php declare(strict_types=1); namespace App\Providers; use AndyDefer\PhpLocationIq\Contracts\LocationIqClientInterface; use AndyDefer\PhpLocationIq\Contracts\NominatimClientInterface; use AndyDefer\PhpLocationIq\Enums\LocationIqBaseUrl; use AndyDefer\PhpLocationIq\LocationIqClient; use AndyDefer\PhpLocationIq\NominatimClient; use Illuminate\Support\ServiceProvider; use Jenssegers\Agent\Agent; final class GeoServiceProvider extends ServiceProvider { public function register(): void { $this->app->singleton(LocationIqClientInterface::class, function () { return new LocationIqClient( apiKey: config('geo.locationiq.api_key'), baseUrl: config('geo.locationiq.base_url') === 'eu1' ? LocationIqBaseUrl::EU1 : LocationIqBaseUrl::US1, ); }); $this->app->singleton(NominatimClientInterface::class, function () { $client = new NominatimClient(new Agent()); if ($userAgent = config('geo.nominatim.user_agent')) { $client->setUserAgent($userAgent); } return $client; }); } }
Fichier de configuration
<?php // config/geo.php return [ 'locationiq' => [ 'api_key' => env('LOCATIONIQ_API_KEY'), 'base_url' => env('LOCATIONIQ_BASE_URL', 'us1'), ], 'nominatim' => [ 'user_agent' => env('NOMINATIM_USER_AGENT'), ], ];
Injection dans un contrôleur
<?php declare(strict_types=1); namespace App\Http\Controllers; use AndyDefer\PhpLocationIq\Contracts\LocationIqClientInterface; use AndyDefer\PhpLocationIq\Contracts\NominatimClientInterface; use AndyDefer\PhpLocationIq\Collections\LocationVOCollection; use AndyDefer\PhpLocationIq\Records\DirectionsRecord; use AndyDefer\PhpLocationIq\Records\Nominatim\ReverseRecord; use AndyDefer\PhpLocationIq\ValueObjects\LocationVO; use AndyDefer\PhpVo\ValueObjects\CoordinatesVO; use AndyDefer\PhpVo\ValueObjects\Types\FloatVO; use Illuminate\Http\JsonResponse; final class GeoController { public function __construct( private readonly LocationIqClientInterface $locationiq, private readonly NominatimClientInterface $nominatim, ) {} public function route(Request $request): JsonResponse { $coordinates = new LocationVOCollection; $coordinates->add(LocationVO::fromArray([$request->from_lon, $request->from_lat])); $coordinates->add(LocationVO::fromArray([$request->to_lon, $request->to_lat])); $response = $this->locationiq->getDirections( new DirectionsRecord(coordinates: $coordinates), ); if ($response->hasError()) { return response()->json(['error' => $response->getError()], 422); } return response()->json(['routes' => $response->getRoutes()->toArray()]); } public function reverse(Request $request): JsonResponse { $response = $this->nominatim->reverse(new ReverseRecord( coordinates: new CoordinatesVO( FloatVO::from((float) $request->lat), FloatVO::from((float) $request->lon), ), )); if ($response->hasError()) { return response()->json(['error' => $response->getError()], 422); } return response()->json(['address' => $response->getData()->reverse?->address?->toArray()]); } }
Références techniques
Documentation détaillée de chaque composant :
Clients
LocationIqClient— client HTTP LocationIQNominatimClient— client HTTP Nominatim
Requests
Responses
Records & Data
TimezoneRecordDirectionsRecordMatrixRecordReverseRecordTimezoneDataBalanceDataDirectionsDataMatrixResponseDataMatrixWaypointDataReverseDataAddressData
Value Objects & Enums
LocationVOBoundingBoxVOMatrixOptionsVOFloatMatrixVOLocationIqBaseUrlDirectionsProfileOverviewTypeGeometriesTypeMatrixAnnotationFallbackCoordinateNominatimBaseUrlNominatimFormat