-
Notifications
You must be signed in to change notification settings - Fork 0
GeoTool
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).
- 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
- 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']);
}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
]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.
$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.519188CREATE 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;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.
- 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.
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.