Para utilizar todas las funcionalidades que ofrece este sitio, es necesario tener JavaScript habilitado.

Actualmente es muy fácil mostrar una dirección en un mapa, herramientas como Google Maps hacen que esta tarea sea sencilla. Tampoco es difícil encontrar lugares en un radio cercano a una posición en un mapa, sinembargo aveces es fácil confundirse con los cálculos que hay que hacer con las coordenadas.

Estoy trabajando en un proyecto en PHP que en términos muy generales es un directorio. Tengo una base de datos con nombres de sitios, su dirección, y coordenadas geográficas (latitud y longitud).  

Como ejemplo hice una tabla  con algunas direcciones, me quedo así:

Tomemos un sitio, como por ejemplo el Centro Comercial Andino y busquemos otros lugares que se encuentren en un radio de 1 Kilometro, como lo hacemos? Hay varias alternativas siendo la formula de Haversine y la Trigonométria Esférica (Teorema/Formula del coseno) las más populares, sin embargo hay muchas más formulas.

Para esta entrada vamos a explorar El teorema del coseno y Haversine. Antes de empezar clarifico que estos ejemplos están basados en Kilómetros y no en millas, así que usamos el radio medio de la tierra que es de 6,371 KM.

Al final de esta entrada menciono otras alternativas que se pueden usar para lograr nuestro objetivo.

El Teorema/Formula del Coseno

En teoría lo único que hay que hacer es calcular la distancia que hay entre los demás sitios y un punto en especifico, luego ordenar por distancia. Eso lo puede hacer MySQL con facilidad.

Estamos en el Centro Comercial Andino (Lat: 4.6665578 | Long: -74.0524521)

El resultado de esta consulta sería:

Hasta ahí estamos bien. Pero si la tabla con las direcciones tiene un número alto de entradas, el rendimiento de nuestra consulta va a ser horrible, pues por cada entrada se va a calcular su distancia, independientemente si esta a 1 KM o a 200 KM. Entonces la idea es delimitar un área antes de empezar con el calculo de distancias.

Eso es relativamente fácil de solucionar, trazamos una linea que vaya de este a oeste y otra de norte a sur. Luego buscamos los puntos más distantes dentro del radio que hemos especificado. Con una función en PHP lo podemos lograr fácilmente.

Veamos en el mapa como quedaría el area de búsqueda (aproximadamente):

Y ahora lo implementamos en la consulta a la base de datos 

El resultado debería ser el mismo que tuvimos con la consulta anterior, pero la diferencia es que vamos a obtener un mejor rendimiento, sobretodo si las columnas lat y lng tienen un indice.

La Formula de Haversine

Generalmente se argumenta que Haversine puede ser un poquito más exacta que la del coseno. En mi experiencia personal, la diferencia entre una y otra es minima.

Volviendo al ejemplo anterior, estamos en el Centro Comercial Andino (Lat: 4.6665578 | Long: -74.0524521) y queremos encontrar lugares que se encuentren a 1KM a la redonda.

En MySQL sería:

El resultado:

Nuevamente para mejorar el rendimiento de la consulta, lo mejor es delimitar el área de búsqueda, así que usamos la función que definimos anteriormente y hacemos el mismo proceso.

De esta forma la consulta es más eficáz.

Hasta aquí tenemos 2 maneras distintas para encontrar sitios en un radio cercano, pero veamos otro par de alternativas que también nos podrían ser útiles.

La Formula de Vicenty

Esta es la fórmula más precisa para hacer estos tipos de cálculos, ya que en las formulas anteriores se ve a la tierra como una esfera perfecta y Vicenty ve a la tierra como un elipsoide. A su vez esta formula usa más recursos que las   anteriores.

Por otro lado, para los tipos de cálculos que nosotros estamos haciendo no necesitamos una precisión métrica completamente exacta (no estamos creando un sistema de misiles a control remoto), así que la ley de los cosenos o Haversine funcionan perfectamente para nuestro cometido.

En wikipedia puedes encontrar más información en inglés

Extensiones espaciales de MySQL

Aprovechando que estamos usando MySQL, debo mencionar que hay una extension que nos permite encontrar lugares cercanos de una forma eficaz y sencilla.

En términos generales solo hay que crear una tabla con capacidades espaciales, las columnas lat y lng deben ser del tipo POINT y deben tener un indice SPATIAL. Luego hay que usar la función MBRContains.

En las referencias hay una presentación que habla más al respecto (en inglés). 

En Resumen

Para terminar con esta entrada, hay un par de cosas que hay que tener en cuenta:

  • Hay muchas formas para lograr nuestro objetivo, aquí solo estoy exponiendo las más populares.
  • Si se comparan los resultados de distancia entre la formula del coseno y haversine, no hay mucha diferencia. La decisión de usar una o la otra, depende de las preferencias personales.
  • Hay que revisar la exactitud de las formulas si uno se encuentra muy cerca a los polos. Si quieres buscar sitios cercanos a la casa de Santa Claus en el polo norte, pueden haber irregularidades con el cálculo del area de búsqueda.

Aquí termina esta entrada, gracias por leer. Si hay alguna inconsistencia o algún error házmelo saber en los comentarios!

Si no puedes ver el código, puedes ir a https://gist.github.com/mpratt/3177700.

Referencias (en Inglés)

Comentarios