Aucune fonction d'extension géospatiale dans MySQL ne prend en charge les calculs de distance latitude/longitude. Il existe depuis MySQL 5.7
.
Vous demandez des cercles de proximité à la surface de la terre. Vous mentionnez dans votre question que vous avez des valeurs lat/long pour chaque ligne dans vos flags
table, et aussi universal transverse Mercator
(UTM) valeurs projetées dans l'une des différentes zones UTM
. Si je me souviens correctement de mes cartes UK Ordnance Survey, UTM est utile pour localiser des éléments sur ces cartes.
C'est simple de calculer la distance entre deux points dans la même zone en UTM :la distance cartésienne fait l'affaire. Mais, lorsque les points sont dans des zones différentes, ce calcul ne fonctionne pas.
Par conséquent, pour l'application décrite dans votre question, il est nécessaire d'utiliser la Great Circle Distance , qui est calculé à l'aide du haversine ou d'une autre formule appropriée.
MySQL, complété par des extensions géospatiales, prend en charge un moyen de représenter diverses formes planes (points, polylignes, polygones, etc.) sous forme de primitives géométriques. MySQL 5.6 implémente une fonction de distance non documentée st_distance(p1, p2)
. Cependant, cette fonction renvoie des distances cartésiennes. C'est donc totalement inadapté pour les calculs basés sur la latitude et la longitude. Aux latitudes tempérées, un degré de latitude sous-tend presque deux fois plus de distance de surface (nord-sud) qu'un degré de longitude (est-ouest), car les lignes de latitude se rapprochent plus près des pôles.
Ainsi, une formule de proximité circulaire doit utiliser une véritable latitude et longitude.
Dans votre application, vous pouvez retrouver tous les flags
points situés à moins de dix milles terrestres d'un latpoint,longpoint
donné avec une requête comme celle-ci :
SELECT id, coordinates, name, r,
units * DEGREES(ACOS(LEAST(1.0, COS(RADIANS(latpoint))
* COS(RADIANS(latitude))
* COS(RADIANS(longpoint) - RADIANS(longitude))
+ SIN(RADIANS(latpoint))
* SIN(RADIANS(latitude))))) AS distance
FROM flags
JOIN (
SELECT 42.81 AS latpoint, -70.81 AS longpoint,
10.0 AS r, 69.0 AS units
) AS p ON (1=1)
WHERE MbrContains(GeomFromText (
CONCAT('LINESTRING(',
latpoint-(r/units),' ',
longpoint-(r /(units* COS(RADIANS(latpoint)))),
',',
latpoint+(r/units) ,' ',
longpoint+(r /(units * COS(RADIANS(latpoint)))),
')')), coordinates)
Si vous souhaitez rechercher des points dans un rayon de 20 km, modifiez cette ligne de la requête
20.0 AS r, 69.0 AS units
à ceci, par exemple
20.0 AS r, 111.045 AS units
r
est le rayon dans lequel vous voulez chercher. units
sont les unités de distance (miles, km, stades, tout ce que vous voulez) par degré de latitude à la surface de la terre.
Cette requête utilise une limite lat/long avec MbrContains
pour exclure les points qui sont définitivement trop éloignés de votre point de départ, puis utilise la formule de distance orthodromique pour générer les distances pour les points restants. Une explication de tout cela peut être trouvée ici
. Si votre table utilise la méthode d'accès MyISAM et possède un index spatial, MbrContains
exploitera cet index pour vous permettre une recherche rapide.
Enfin, la requête ci-dessus sélectionne tous les points dans le rectangle. Pour limiter cela aux seuls points du cercle et les classer par proximité, résumez la requête comme ceci :
SELECT id, coordinates, name
FROM (
/* the query above, paste it in here */
) AS d
WHERE d.distance <= d.r
ORDER BY d.distance ASC