Skip to content
Gérits Aurélien edited this page Feb 19, 2026 · 5 revisions

Le GeoTool fournit des utilitaires mathématiques pour les calculs géographiques. Il est optimisé pour travailler avec des coordonnées (Latitude/Longitude) et permet de réaliser des recherches de proximité performantes ("Store Locator", recherche autour d'un point).

  1. Mécanique de recherche Pour garantir la performance sur de gros volumes de données, nous utilisons une approche en deux étapes : Dégrossissage (SQL) : Utilisation de la Bounding Box pour limiter les résultats à une zone carrée via les index SQL (très rapide). Précision (PHP) : Utilisation de la formule de Haversine pour filtrer les résultats dans le rayon réel (cercle) et calculer la distance exacte.

Namespace : Magepattern\Component\Tool\GeoTool

  1. Exemple d'implémentation complète Voici comment créer une recherche de points de vente dans un rayon de X km.
use Magepattern\Component\Tool\GeoTool;
use Magepattern\Component\Database\QueryBuilder;
use Magepattern\Component\Database\Layer;

// 1. Définition du centre et du rayon
$lat = 50.5181;
$lon = 5.2370;
$radius = 20; // km

// 2. Récupération des limites du carré (Bounding Box)
$box = GeoTool::getBoxLimits($lat, $lon, $radius);

// 3. Requête SQL de pré-filtrage
$qb = (new QueryBuilder())
    ->select(['id_store', 'name', 'lat', 'lon'])
    ->from('mc_stores')
    ->where('lat BETWEEN :minLat AND :maxLat', [
        'minLat' => $box['minLat'], 
        'maxLat' => $box['maxLat']
    ])
    ->where('lon BETWEEN :minLon AND :maxLon', [
        'minLon' => $box['minLon'], 
        'maxLon' => $box['maxLon']
    ]);

$results = Layer::getInstance()->fetchAll($qb->getSql(), $qb->getParams());

// 4. Filtrage final et calcul des distances exactes en PHP
$stores = [];
if ($results) {
    foreach ($results as $row) {
        $dist = GeoTool::calculateDistance($lat, $lon, $row['lat'], $row['lon']);
        
        if ($dist <= $radius) {
            $row['distance'] = round($dist, 2);
            $stores[] = $row;
        }
    }

    // Tri du plus proche au plus loin
    usort($stores, fn($a, $b) => $a['distance'] <=> $b['distance']);
}

Retour de getBoxLimits()

Cette méthode retourne les coordonnées nécessaires pour construire le carré de recherche SQL.

[
    'lat_rad' => 0.8817,   // Latitude en radians (utilisé en interne)
    'lon_rad' => 0.0914,   // Longitude en radians (utilisé en interne)
    'minLat'  => 50.3382,  // Latitude minimale (Sud)
    'minLon'  => 4.9548,   // Longitude minimale (Ouest)
    'maxLat'  => 50.6980,  // Latitude maximale (Nord)
    'maxLon'  => 5.5192,   // Longitude maximale (Est)
    'radius'  => 20        // Le rayon utilisé pour le calcul
]

Structure recommandée pour MagixMaps

Lorsque tu traites tes résultats (Étape 4 de l'exemple), voici l'objet JSON idéal à envoyer à ton script de carte. En incluant la distance, MagixMaps peut afficher des étiquettes du type "À 1.2 km de vous".

[
  {
    "id_store": 42,
    "name": "Boutique Huy Centre",
    "lat": 50.5181,
    "lon": 5.2370,
    "distance": 0.85
  },
  {
    "id_store": 12,
    "name": "Point de vente Liège",
    "lat": 50.6326,
    "lon": 5.5797,
    "distance": 24.12
  }
]

Exemple : Requête SQL générée Pour une recherche autour de Huy (50.51, 5.23) avec un rayon de 20 km, voici comment le code PHP se traduit en SQL.

Le Code PHP (QueryBuilder)

$box = GeoTool::getBoxLimits(50.5181, 5.2370, 20);

$qb = (new QueryBuilder())
    ->select(['id_store', 'name', 'lat', 'lon'])
    ->from('mc_stores')
    ->where('lat BETWEEN :minLat AND :maxLat', [
        'minLat' => $box['minLat'], 
        'maxLat' => $box['maxLat']
    ])
    ->where('lon BETWEEN :minLon AND :maxLon', [
        'minLon' => $box['minLon'], 
        'maxLon' => $box['maxLon']
    ]);

// Pour le Wiki, on peut afficher le SQL généré :
echo $qb->getSql();

La Requête SQL générée (Vue par la base de données) Le QueryBuilder va produire une requête standardisée qui utilise les index de colonnes de manière optimale :

SELECT id_store, name, lat, lon 
FROM mc_stores 
WHERE lat BETWEEN 50.338243 AND 50.697957 
  AND lon BETWEEN 4.954812 AND 5.519188

Exemple de structure SQL recommandée

CREATE TABLE `mc_stores` (
  `id_store` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `lat` DOUBLE NOT NULL,
  `lon` DOUBLE NOT NULL,
  PRIMARY KEY (`id_store`),
  INDEX `idx_coords` (`lat`, `lon`)
) ENGINE=InnoDB;

Pourquoi c'est performant ?

Indexation : Si tes colonnes lat et lon sont indexées, MySQL trouve les lignes en une fraction de seconde car il s'agit d'une simple comparaison numérique.

Protection : Bien que l'exemple affiche les valeurs en dur pour la clarté, le Layer envoie ces valeurs via des "prepared statements" (:minLat, etc.), protégeant ainsi contre toute injection SQL.

Légèreté : On ne demande pas à MySQL de calculer des racines carrées ou des sinus. On lui demande juste de vérifier si un nombre est compris entre deux autres.

  1. Référence des méthodes getBoxLimits(float $lat, float $lon, float $radius) Retourne un tableau contenant les coordonnées minLat, maxLat, minLon, maxLon.

Usage : À utiliser impérativement dans une clause WHERE ... BETWEEN pour éviter de parcourir toute la table SQL.

calculateDistance(float $lat1, float $lon1, float $lat2, float $lon2) Retourne la distance en kilomètres entre deux points.

Usage : Calcul de précision et affichage de la distance pour l'utilisateur final.

Pourquoi ne pas tout faire en SQL ?

Faire le calcul de distance complexe (Cosinus/Sinus) directement dans le SELECT force MySQL à ignorer les index et à calculer la formule pour chaque ligne de la base de données. Sur 100 000 lignes, le temps de réponse s'effondre. L'approche hybride de Magepattern 3 reste fluide quel que soit le nombre d'enregistrements.

Clone this wiki locally