web-dev-qa-db-fra.com

Utiliser les extensions spatiales MySQL pour sélectionner des points dans un cercle

J'ai une table appelée flags qui contient une colonne appelée coordinates qui est pleine de «points» MySQL. J'ai besoin d'effectuer une requête où tous les drapeaux d'un cercle sont basés sur une position de latitude et de longitude avec un rayon de 100 m.

Du point de vue de l'utilisation, ceci est basé sur la position de l'utilisateur. Par exemple, le téléphone mobile indiquerait la latitude et la longitude de l'utilisateur, puis le transmettrait à cette partie de l'API. Il appartient ensuite à l’API de créer un cercle invisible autour de l’utilisateur d’un rayon de 100 mètres, puis de renvoyer les indicateurs figurant dans ce cercle.

C'est cette partie de l'API que je ne sais pas créer, car je ne sais pas comment utiliser SQL pour créer ce cercle invisible et sélectionner des points uniquement dans ce rayon.

Est-ce possible? Y at-il une fonction spatiale MySQL qui m'aidera à faire cela?

Je pense que la fonction Buffer() peut le faire, mais je ne trouve aucune documentation sur son utilisation (exemple: SQL). Idéalement, j'ai besoin d'une réponse qui me montre comment utiliser cette fonction ou la plus proche de celle-ci. Là où je stocke ces coordonnées sous forme de points géospatiaux, je devrais utiliser une fonction géospatiale pour faire ce que je demande afin de maximiser l'efficacité.

Tableau des drapeaux:

  • identifiant
  • les coordonnées
  • prénom

Exemple de ligne:

1 | [GÉOMÉTRIE - 25B] | Tenacy AB

Pour la table des drapeaux, j'ai la latitude, la longitude et les directions est et nord (UTM)

L'emplacement de l'utilisateur est juste la latitude/longitude standard, mais j'ai une bibliothèque qui peut convertir cette position en UTM

14
jskidd3

MySQL ne prend pas en charge les calculs de distance latitude/longitude dans les fonctions d'extension géospatiales. Il existe une version de MySQL 5.7 .

Vous demandez des cercles de proximité à la surface de la terre. Vous avez mentionné dans votre question que vous aviez des valeurs lat/long pour chaque ligne de votre table flags, ainsi que valeurs transversales universelles de Mercator (UTM) projetées dans l'une de plusieurs différentes Zones UTM . Si je me souviens bien de mes cartes UK Ordnance Survey, UTM est utile pour localiser des éléments sur ces cartes.

Calculer la distance entre deux points dans la même zone en UTM est simple: la distance cartésienne fait l'affaire. Mais, lorsque les points sont dans des zones différentes, le calcul ne fonctionne pas.

En conséquence, pour l’application décrite dans votre question, il est nécessaire d’utiliser la Grande distance du cercle , calculée à l’aide de la courbe haversine ou d’une autre formule appropriée.

MySQL, complété par des extensions géospatiales, permet de représenter diverses formes planaires (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. Donc tout à fait inapproprié pour les calculs basés sur la latitude et la longitude. Aux latitudes tempérées, un degré de latitude sous-tend deux fois plus de distance en surface (nord-sud) qu'un degré de longitude (est-ouest), car les lignes de latitude se rapprochent les plus près des pôles.

Ainsi, une formule de proximité circulaire doit utiliser une latitude et une longitude authentiques.

Dans votre application, vous pouvez trouver tous les flags points situés dans un rayon de dix miles législatifs d'un latpoint,longpoint donné avec une requête comme celle-ci:

 SELECT id, coordinates, name, r,
        units * DEGREES( ACOS(
                   COS(RADIANS(latpoint)) 
                 * COS(RADIANS(X(coordinates))) 
                 * COS(RADIANS(longpoint) - RADIANS(Y(coordinates))) 
                 + SIN(RADIANS(latpoint)) 
                 * SIN(RADIANS(X(coordinates))))) 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

à cela, par exemple

               20.0 AS r, 111.045 AS units

r est le rayon dans lequel vous souhaitez effectuer la recherche. 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 nettement trop éloignés de votre point de départ, puis utilise la formule de la grande distance du cercle pour générer les distances pour les points restants. Un l'explication de tout cela peut être trouvé 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 d'effectuer une recherche rapide.

Enfin, la requête ci-dessus sélectionne tous les points du rectangle. Pour limiter cela aux seuls points du cercle et les classer par proximité, résumez la requête comme suit:

 SELECT id, coordinates, name
   FROM (
         /* the query above, paste it in here */
        ) AS d
  WHERE d.distance <= d.r
  ORDER BY d.distance ASC 
18
O. Jones

Cela suppose que les coordonnées de la table sont stockées en tant que type de données POINT () dans une colonne intitulée "point". La fonction X(point) et Y(point) extraient les valeurs de latitude et de longitude de la valeur du point, respectivement.

SET @lat = the latitude of the point
SET @lon = the longitude of the point
SET @rad = radius in Kilometers to search from the point
SET @table = name of your table

SELECT
    X(point),Y(point),*, (
      6373 * acos (
      cos ( radians( @lat ) )
      * cos( radians( X(point) ) )
      * cos( radians( Y(point) ) - radians( @lon ) )
      + sin ( radians( @lat ) )
      * sin( radians( X(point) ) )
    )
) AS distance
FROM @table
HAVING distance < @rad

Si vous voulez le faire en miles, remplacez la constante 6373 par 3959

Pour ceux qui souhaitent réduire la syntaxe de la requête, voici une implémentation commune d’une fonction MySQL définie par l’utilisateur pour implémenter une fonction de distance basée sur les formules Haversine.

CREATE FUNCTION HAVERSINE ( coord1 POINT, coord2 POINT )
RETURNS DOUBLE
DETERMINISTIC
BEGIN
    DECLARE dist DOUBLE;
    SET rlat1 = radians( X( coord1 ) );
    SET rlat2 = radians( X( coord2 ) );
    SET rlon1 = radians( Y( coord1 ) );
    SET rlon2 = radians( Y( coord2 ) );
    SET dist  = ACOS( COS( rlat1 ) * COS( rlon1 ) * COS( rlat2 ) * COS( rlon2 ) + COS( rlat1 ) * SIN( rlon1 ) * COS( rlat2 ) * SIN( rlon2 ) + SIN( rlat1 ) * SIN( rlat2 ) ) * 6372.8;
    RETURN dist;
END
10

UPDATE

Utilisez ST_Distance_Sphere () pour calculer des distances à l'aide de lat/long  

http://dev.mysql.com/doc/refman/5.7/en/spatial-convenience-functions.html#function_st-distance-sphere

7
Mihado

Les tampons ne vous aideront pas beaucoup dans MySQL <5.6, car tampon est un polygone, et les opérations de polygone dans MySQL <5.6 sont implémentées sous la forme de "MBR (Minimal Bounding Rectangles)", qui sont plutôt inutiles. 

Depuis MySQL 5.6, les opérations complètes non-MBR st_* ont été implémentées . Mais la meilleure solution pour vous, en cas de cercle, consiste à utiliser non documenté fonction st_distance:

select *
from waypoints
where st_distance(point(@center_lon, @center_lat), coordinates) <= radius;

C'était difficile à trouver car non documenté :-) Mais c'est mentionné sur ce blog , dont l'auteur a également renseigné le le rapport de bogue mentionné . Il y a cependant des mises en garde (citant le blog):

La mauvaise nouvelle est:

1) Toutes les fonctions utilisent encore uniquement les coordonnées système planaires. Différents SRID ne sont pas pris en charge.

2) Les index spatiaux (RTREE) ne sont pris en charge que pour les tables MyISAM. Un Peut utiliser les fonctions des tables InnoDB, mais il n’utilisera pas de touches spatiales .

Le point 1) signifie que l'unité de distance sera la même que l'unité de coordonnées (degrés dans le cas de WGS84). Si vous avez besoin d’une distance en mètres, vous devez utiliser un système de coordination projeté (par exemple, UTM ou similaire) comportant des unités correspondant à des mètres.

Donc, si vous ne voulez pas y aller avec ces avertissements, ou si MySQL <5.6, vous devrez écrire votre propre fonction de distance personnalisée.

5
TMS

J'espère que ma version aide

SELECT 
    *
FROM 
    `locator`
WHERE
    SQRT(POW(X(`center`) - 49.843317 , 2) + POW(Y(`center`) - 24.026642, 2)) * 100 < `radius`

détails ici http://dexxtr.com/post/83498801191/how-to-determine-point-inside-circle-using-mysql

1
dexxtr

de: https://gis.stackexchange.com/questions/31628/find-points-within-a-distance-using-mysql

SELECT
    id, (
      6371 * acos (
      cos ( radians(78.3232) )
      * cos( radians( lat ) )
      * cos( radians( lng ) - radians(65.3234) )
      + sin ( radians(78.3232) )
      * sin( radians( lat ) )
    )
) AS distance
FROM markers
HAVING distance < 30
ORDER BY distance
LIMIT 0 , 20;

(n'oubliez pas de remplacer toutes les constantes, cet exemple concerne les kilomètres)

1
Michal Wrd

à des fins d’exhaustivité, à partir de MySQL 5.7.6. vous pouvez utiliser la fonction ST_Distance_Sphere qui donne le même résultat:

SET @pt1 = ST_GeomFromText('POINT(12.3456 34.5678)');

SELECT * from 
(SELECT * ,(ST_Distance_Sphere(@pt1, location, 6373)) AS distance FROM mydb.Event ORDER BY distance) x WHERE x.distance <= 30;

Dans ce cas, nous fournissons le rayon approximatif de la Terre en kilomètres (6373) et un point (@ pt1). Ce code calculera la distance (en kilomètres) entre ce point (longueur 12.3456, lat 34.5678) et tous les points contenus dans la base de données où la distance est inférieure ou égale à 30 km.

0
AlterX

Vous pouvez utiliser: 

SELECT name, lat, lng   
FROM vw_mytable 
WHERE ST_Contains(ST_Buffer(
          ST_GeomFromText('POINT(12.3456 34.5678)'), (0.00001*1000)) , mypoint) = 1  

L’expression: 0.00001 * 1000 à l’intérieur de la déclaration ci-dessus vous donne un cercle de 1000 mètres de diamètre, S’applique à une vue ici, la colonne name n’est qu’une étiquette à une autre, mypoint est le nom de ma colonne. , lat a été calculé à l’intérieur de la vue avec ST_X (mytable.mypoint) et Lng avec ST_Y (mytable.mypoint) et n’affiche que les valeurs littérales de lat et lng. Il vous donnera toutes les coordonnées qui appartiennent au cercle.

0
newway