¿Cómo busco eficientemente todos los puntos de referencia dentro de un rango de cierto punto de referencia?

14

Estoy tratando de comenzar con un proyecto de búsqueda geográfica que encontrará todos los puntos de referencia en los 10 km / millas (no importantes para esta historia) de un punto de referencia particular.

Entonces, por ejemplo, digamos que tengo una base de datos de 1,000,000 de puntos de referencia. Para encontrar todos los puntos de referencia en el rango de 10 millas de un punto de referencia con ciertas coordenadas, tendría que calcular la distancia entre un punto de referencia de mi búsqueda y 1,000,000 de puntos de referencia.

¿Hay una mejor manera de hacer eso?

La alternativa que estaba pensando es categorizar puntos de referencia como país, región, ciudad, vecindario, negocios, histórico, etc., de tal manera que los negocios puedan ser parte de un vecindario o ciudad. La ciudad es parte de una región, un país, etc. Esto puede reducir una lista de cálculos, pero aún así parece que se necesita mucho trabajo para que la búsqueda sea rápida y precisa.

¿Podría ayudar la API de Google Maps?

Dario Granich
fuente
55
Probablemente podría eliminar una buena cantidad simplemente realizando un cálculo rápido de la distancia de Manhattan y luego realizando un segundo filtro para excluir los puntos de referencia que se encuentran dentro de un cuadrado de 10 km pero están fuera del radio de 10 km.
Neil
3
¿Qué tecnología de base de datos estás usando? La respuesta no es independiente de la base de datos.
jpmc26
1
@Neil Como segunda pasada, puede incluir cualquier punto de referencia en el que caiga la xey en 7 km del origen sin calcular la distancia real.
JimmyJames

Respuestas:

10

Desde SQL Server 2008, existe un tipo de datos de geografía que almacena ubicaciones (pares de lat / lon) y le facilita la escritura de consultas relacionadas con la ubicación.

Existe una respuesta StackOverflow existente que analiza esto en profundidad.

Una consulta básica para encontrar los 7 elementos más cercanos :

USE AdventureWorks2012  
GO  
DECLARE @g geography = 'POINT(-121.626 47.8315)';  
SELECT TOP(7) SpatialLocation.ToString(), City FROM Person.Address  
WHERE SpatialLocation.STDistance(@g) IS NOT NULL  
ORDER BY SpatialLocation.STDistance(@g);  

Una consulta básica para encontrar todo dentro de los 100 m (segunda respuesta a la pregunta)

-- Get the center point
DECLARE @g geography
SELECT @g = geo FROM yourTable WHERE PointId = something

-- Get the results, radius 100m
SELECT * FROM yourTable WHERE @g.STDistance(geo) <= 100
Flater
fuente
11
@KonradRudolph: como es el caso de cualquier columna SQL que se utiliza para consultar en una tabla con un recuento de filas masivo. Tiene razón, pero ese comentario se aplicaría a prácticamente cualquier consulta SQL que se publique como respuesta.
Flater
2
¿Dónde leíste "MS SQL Server" en la pregunta?
Doc Brown
3
@Flater Estoy de acuerdo en que normalmente sería obvio y redundante, pero la redacción de OP parece sugerir que desconocen tales mecanismos.
Konrad Rudolph el
2
@ jpmc26: ¿Le horroriza que haya enumerado una opción válida y no haya incluido alguna otra opción? ¿Qué? Si cree que es relevante agregar PostGIS, agregue la respuesta usted mismo (lo que hizo) y no recurra a criticar a otros por no tener la misma idea que usted.
Flater
3
Su respuesta me parece básicamente como un argumento de venta de MS SQL. Sus comentarios sugieren que cambian las bases de datos a algo que costaría 10s de miles de dólares sin preguntar realmente cuál es su situación que lo hace parecer aún más. Ni siquiera describe cómo el OP puede realmente implementar su consulta o discutir el hecho de que hacerlo y asegurar el uso del índice espacial no es tan sencillo en MS SQL como en otros DB. Tampoco discute ninguno de los conceptos subyacentes. Es una mala respuesta, independientemente de si es "válida". Por eso me molesta.
jpmc26
29

Utilice una base de datos con soporte para consultas SIG (sistemas de información geográfica) . La mayoría de las bases de datos admiten esto directamente o tienen extensiones, pero los detalles serán específicos de la base de datos (en su respuesta , Flater muestra la sintaxis para el servidor SQL).

Si necesita implementar tales consultas dentro de su aplicación, puede implementar una estructura de datos que permita consultas espaciales, por ejemplo, un árbol kd . Esto es como un árbol de búsqueda binario, excepto que cada nivel de las particiones del árbol en una dimensión de coordenadas diferente. Esto le permite restringir la búsqueda a un conjunto más pequeño de candidatos factibles. Efectivamente, traduce su búsqueda "radio de 10 km" en límites para cada dimensión de coordenadas, y aprieta los límites a medida que recurre en el árbol.

amon
fuente
55
También hay un intercambio de pila SIG
BlueRaja - Danny Pflughoeft
8
PostGIS es la principal opción gratuita. Admite mucho, mucho más que los tipos y funciones GIS muy básicos de SQL Server. Pero esta es la funcionalidad básica.
jpmc26
@amon Encuentro el comentario de jpmc26 como una buena adición, y no tanto como criticar tu ejemplo. "Si desea comenzar desde cero, no necesita pagar por una base de datos con licencia; esta fuente gratuita y de código abierto también funcionará muy bien".
mgarciaisaia
11

Sí, hay una mejor manera. Necesita usar un índice espacial . Estos índices organizan metadatos sobre geometrías para filtrar geometrías lejanas muy rápidamente, ahorrando muchos ciclos de CPU al evitar los cálculos que usted describe. No debería molestarse en implementar uno usted mismo, ya que todas las principales bases de datos relacionales proporcionan un tipo de geometría espacial e índices para acompañarlas.

Lo que desea examinar son consultas "a distancia" (consultas de geometrías dentro de una cierta distancia de alguna otra geometría). Estos son un problema muy estándar y muy resuelto y son posibles en todas las bases de datos anteriores (e integradas en varias):

  • PostGIS: ST_DWithin
  • SQL Server: STDistance(no está claro que el uso del índice en la versión de geografía 3D de esta función sea compatible)
  • Oracle: SDO_WITHIN_DISTANCE(Esto no dice explícitamente que activará el uso del índice. Verificaría dos veces el plan de consulta. Es posible que deba aplicar un SDO_FILTERpara que use el índice).
  • MySQL: Todavía estoy resolviendo esto.

Solución alternativa para activar el uso del índice

En el peor de los casos en el que tiene problemas para que el sistema use el índice espacial con estas consultas, puede agregar un filtro adicional. Crearía un cuadro delimitador cuadrado con lados de longitud 2 * (distancia de búsqueda) centrados en su punto de búsqueda y compararía los cuadros delimitadores de las geometrías de la tabla con eso antes de verificar la distancia real. Eso es lo que PostGIS ST_DWithinhace arriba internamente de todos modos.


Distancia en SIG

Si bien los índices espaciales son fantásticos y absolutamente la solución correcta para su problema, el cálculo de la distancia puede ser lógicamente complicado. En particular, debe preocuparse sobre en qué proyección (básicamente todos los parámetros para el sistema de coordenadas) se almacenan sus datos. La mayoría de las proyecciones 2D (que no sean sistemas de coordenadas angulares como las diversas proyecciones lat / long) distorsionan la longitud significativamente. Por ejemplo, la proyección de Web Mercator (la utilizada por Google, Bing y cualquier otro proveedor de mapas base importante) expande áreas y distancias cada vez más a medida que la ubicación se aleja del ecuador . Podría estar equivocado ya que no tengo educación formal en SIG, pero lo mejor que he visto para proyecciones 2D son algunas específicas que prometen distancias correctas desde unpunto único y constante en todo el mundo. (No, no es práctico usar una proyección diferente para cada consulta; eso haría que sus índices sean inútiles).

La conclusión es que debe asegurarse de que sus cálculos sean precisos. La forma más simple de hacerlo desde una perspectiva de desarrollo es usar proyecciones angulares (a menudo se las denomina "geográficas") y funciones que apoyan hacer las matemáticas usando un modelo de esferoides, pero estos cálculos son un poco más caros que las contrapartes 2D y algunos DB pueden no admitir su indexación. Sin embargo, si puede obtener un rendimiento aceptable al usarlos, ese es probablemente el camino a seguir. Otra opción común son las proyecciones regionales (como las zonas UTM) que consiguen distancias y áreas bastante cercanas para corregir si sus datos se limitan a una parte particular del mundo. Lo mejor para su aplicación dependerá de sus requisitos específicos,

Esto se aplica incluso si no utiliza índices espaciales integrados. Sus datos tienen cierta proyección, independientemente de la tecnología o técnica que esté utilizando o use actualmente en el futuro, y ya está afectando cualquier consulta y cálculo que esté haciendo.

jpmc26
fuente
3

Estoy de acuerdo en que, de ser posible, utilizar el soporte específico en una base de datos sería la forma más sensata de hacerlo.

Sin embargo, si tuviera que hacer esto en una base de datos sin soporte específico, comenzaría preguntando por un cuadrado que encierra el circuito, por ejemplo (y> (y1 - rad)) AND (y <(y1 + rad)) AND (x> ( x1 - rad)) AND (x <(x1 + rad)). Suponiendo que sus puntos tengan una distribución más o menos uniforme, la consulta de un cuadrado le dará sus coincidencias verdaderas más un 30% de coincidencias falsas adicionales. Luego puede eliminar las coincidencias falsas.

Peter Green
fuente
Pero sin un índice espacial apropiado, dicha consulta escaneará en el peor de los casos la base de datos completa, en el mejor de los casos, todos los elementos dentro del rango de latitud O longitud dada dependiendo de su índice, es decir, una "banda" en lugar de un cuadrado. Si no desea eliminar el rendimiento, ¡use una base de datos que admita índices espaciales!
jcaron
@jcaron Creo que esta consulta podría optimizarse con un índice B-tree ordinario en xy y. (Quizás combinado, quizás separado. Perfil un poco para averiguar cuál funciona mejor en la práctica.)
jpmc26
@ jpmc26 No, no puede. Piénselo bien, ya lo verá.
jcaron
@jcaron Quizás sería mejor si no fueras críptico sobre algo que claramente no es sencillo. Los árboles B se pueden usar para BETWEENconsultas. No veo por qué, en el peor de los casos, no podría tener 2 índices y luego los resultados filtrados de cada índice se unen. (Eso es algo que los RDBMS hacen internamente cuando consideran que vale la pena usar múltiples índices). Si un índice combinado funciona, debería filtrar una dimensión por completo en el primer nivel y luego reducirla relativamente rápido en el segundo nivel.
jpmc26
2
@jcaron en realidad puedes usar el índice para algo como, y between -68 and -69 and x between 10 and 11pero por supuesto, el índice espacial hace un mejor trabajo para esa tarea
Juan Carlos Oropeza