Calcular distancia entre dos ciudades o puntos con PHP, Ruby…

Distancia entre dos puntos con PHP y MySQL dada una latitud y longitud.

En los proyectos donde utilizamos Google Maps y en general cualquier proyecto “geolocalizado” una formula imprescindible para conocer y aplicar es la de Haversine, no tengo ni idea cuantos años tendrá, pero sospecho que muchos. El caso es que no tenemos que aprenderla ni entenderla a fondo. Solo necesitamos tener un código fiable de apenas 5 lineas que entendamos “minimamente” para poder aplicar. De hecho era una de mis tareas pendientes desde hace mucho tiempo. Si quieres saltarte las explicaciones la he subido en PHP aquí. Posteriormente añadiré el código en Ruby y investigare alguna forma para hacerlo en MYSQL y cómo podría optimizarla para no realizar estos cálculos bestiales en nuestro querido servidor o hosting. Aquí tienes el ejemplo de búsqueda de posiciones cercanas en un radio de “X” kilómetros.

Imaginate que tienes una aplicación de restaurantes y un usuario “localizado” en Alicante buscando sitios cercanos. Básicamente lo único que tienes que hacer es una consulta a la base de datos sacando sitios de Alicante y realizar el calculo sobre cada uno de ellos y mostrar los 10 mas cercanos. Lógicamente esto tendría una eficiencia que tiraría cualquier servidor si realizas el calculo sobre todos tus registros cada vez que un usuario visite el sitio…

Recientemente he añadido una base de datos SQL con todos los municipios, provincias y comunidades autónomas con su correspondiente latitud y longitud que te podría ser bastante útil. Lo puedes encontrar y descargar aquí, “by the face”.

¿Como se calcula? Código en PHP:

Antes de nada, este código es una ligera modificación de este post de Taringa. La formula dada ahí funciona perfectamente, de hecho en el código de github que os dejo hay dos comprobaciones y da en el blanco. Simplemente es una función, no merece convertirla en una clase.. Lets keep it Simple.

Los cálculos en trigonometría esférica generalmente necesitan de radianes en vez de grados, si mal no recuerdo. De ahí los constantes para convertir de grado a radian y al revés. Los datos que tienes que proporcionar debes tenerlo en notación DECIMAL. Si quieres obtener salida en millas o millas náuticas solo tienes que cambiar la constante. Es decir, la formula como tal te proporciona la distancia en GRADOS. Cada grado o “degree” en la tierra equivale a 111 kms o unas 69 millas.

Cabe destacar que la distancia obtenida no es exacta al 100% por una simple razón. La tierra no es una esfera perfecta. Digamos es una aproximación muy fiel. Si necesitas mas precisión debes contactar con la NASA.

Como tu y yo sabemos que en nuestra base de datos podemos tener miles o millones de registros con localizaciones, esta formula es inútil sin algún tipo de apaño para optimizar el rendimiento. Es decir, lo suyo seria sacar una cantidad finita de candidatos (10 máximo), realizar el calculo y ordenar el resultado. Aquí tienes un ejemplo de como puedes hacer la consulta con algo de MySQL avanzado. Personalmente me parece ineficiente ya que a simple vista parece que realiza el mismo calculo en TODOS los registros, aunque tampoco lo he investigado demasiado… Aquí tienes la segunda parte del experimento, donde creo que se resuelve en parte el problema.

¿Porque no hacerlo con la API de Google Maps?

Puedes hacer la misma tarea con la API de Google MAPS gratis y sin utilizar el procesador de tu servidor. Es decir lo harías asíncronamente y en el ordenador del cliente. Es decir, seria infinitamente mas rápido y mas eficiente para nosotros. Todo lo que tienes que saber esta aquí. Sin embargo para sitios web esto supone un ligero problema ya que las peticiones son Asíncronas. AJAX y APIs externas en general son un problema para el SEO. Cuando el crawler de Google, Yahoo o Bing pase por tu sitio encontrara a mogollón de código JavaScript. Algo que los robots no saben interpretar muy bien. Como puedes entender sin optimización para los buscadores a tu pagina vas a entrar tu y 3 colegas, algo que no interesa a nadie… De ahí que prefiero hacerlo en mi servidor y sacrificar rendimiento por visitas.