sql >> Base de Datos >  >> RDS >> Mysql

Use las extensiones espaciales de MySQL para seleccionar puntos dentro del círculo

No hay funciones de extensión geoespacial en MySQL que admitan cálculos de distancia de latitud/longitud. Existe a partir de MySQL 5.7 .

Estás preguntando por círculos de proximidad en la superficie de la tierra. Usted menciona en su pregunta que tiene valores de latitud/longitud para cada fila en sus flags table, y también universal transversal Mercator (UTM) valores proyectados en una de varias zonas UTM . Si recuerdo correctamente mis mapas de la Encuesta de Artillería del Reino Unido, UTM es útil para ubicar elementos en esos mapas.

Es muy sencillo calcular la distancia entre dos puntos en la misma zona en UTM:la distancia cartesiana hace el truco. Pero, cuando los puntos están en diferentes zonas, ese cálculo no funciona.

En consecuencia, para la aplicación descrita en su pregunta, es necesario usar la Great Circle Distance , que se calcula mediante el haversine u otra fórmula adecuada.

MySQL, aumentado con extensiones geoespaciales, admite una forma de representar varias formas planas (puntos, polilíneas, polígonos, etc.) como primitivas geométricas. MySQL 5.6 implementa una función de distancia no documentada st_distance(p1, p2) . Sin embargo, esta función devuelve distancias cartesianas. Así que es totalmente inadecuado para cálculos basados ​​en latitud y longitud. En latitudes templadas, un grado de latitud subtiende casi el doble de la distancia superficial (norte-sur) que un grado de longitud (este-oeste), porque las líneas de latitud se acercan más cerca de los polos.

Por lo tanto, una fórmula de proximidad circular debe usar latitud y longitud genuinas.

En su aplicación, puede encontrar todas las flags puntos dentro de diez millas terrestres de un latpoint,longpoint dado con una consulta como esta:

 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 desea buscar puntos dentro de los 20 km, cambie esta línea de la consulta

               20.0 AS r, 69.0 AS units

a esto, por ejemplo

               20.0 AS r, 111.045 AS units

r es el radio en el que desea buscar. units son las unidades de distancia (millas, km, estadios, lo que quieras) por grado de latitud en la superficie de la tierra.

Esta consulta utiliza un límite de latitud/longitud junto con MbrContains para excluir puntos que definitivamente están demasiado lejos de su punto de partida, luego usa la fórmula de distancia del gran círculo para generar las distancias para los puntos restantes. Una explicación de todo esto se puede encontrar aquí . Si su tabla usa el método de acceso MyISAM y tiene un índice espacial, MbrContains explotará ese índice para que puedas realizar búsquedas más rápidas.

Finalmente, la consulta anterior selecciona todos los puntos dentro del rectángulo. Para limitar eso a solo los puntos en el círculo y ordenarlos por proximidad, envuelva la consulta de esta manera:

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