Search by

andydefer / php-locationiq

andydefer

PHP SDK for LocationIQ and Nominatim APIs: balance, timezone, directions, and reverse geocoding with typed requests, responses, and data objects

Package info

github.com/andydefer/php-locationiq

pkg:composer/andydefer/php-locationiq

Statistics

Installs: 137

Dependents: 1

Suggesters: 0

Stars: 0

Open Issues: 0

v0.3.3 2026-10-17 06:07 UTC

This package is auto-updated.

Last update: 2026-10-17 06:07:53 UTC


README

SDK PHP pour l'intégration des services LocationIQ (Balance, Timezone, Directions, Matrix) et Nominatim (Reverse Geocoding).

PHP Version License

Table des matières

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 (pour NominatimClient)

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 de durations[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 InvalidArgumentException est levée au-delà.
  • Au moins une annotation. DURATION est 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

Requests

Responses

Records & Data

Value Objects & Enums

Licence

MIT © Andy Defer