Mysql
 sql >> Base de données >  >> RDS >> Mysql

Utiliser les extensions spatiales MySQL pour sélectionner des points à l'intérieur du cercle

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