3 de marzo, 2020

GIS sin GIS: cómo encontrar ubicaciones cercanas sólo con MySQL o con Javascript y Turf

Antes de ir al código vamos a repasar algunos conceptos de geometría básica y no tan básica, todo esto es importante para entender cómo funciona la fórmula mágica que encontrarás más abajo.

Hace unos dos mil quinientos sesenta años (lo escribo sin números para que veas que pasó mucho tiempo) un señor llamado Pitágoras nos explicó una increíble fórmula para calcular la hipotenusa de un triángulo, según él «en todo triángulo rectángulo el cuadrado de la hipotenusa es igual a la suma de los cuadrados de los catetos».

Esta antigua definición nos sirve hoy para calcular la distancia entre dos puntos en un plano.

Unos años más acá, pero en esa misma época, Eratóstenes aseguraba que la tierra era redonda… y no sólo eso! También estimó su circunferencia en unos 40.000 km.

Ahora que tenemos GPS y que nadie discute que la tierra es redonda (excepto los terraplanistas, claro) podemos hacer algunos ajustes y usar la fórmula de Pitágoras para calcular la distancia entre dos puntos sobre la superficie de una esfera.

Como veremos más abajo, la fórmula que usaremos requiere del radio terrestre, no de la circunferencia. Sabemos que el perímetro de un círculo se calcula como:

l = 2 𝜋 * rl” es el perímetro de la circunferencia
“𝜋” es el número "pi" = 3.14 aprox.

Si despejamos «r«, tendremos:

r = l / (2 𝜋)
r = 40.000 / (2 * 3,14)r = 6.369

Ok! con una fórmula de hace dos mil años, ahora sabemos que el radio de la tierra es de 6.369km.

Pero… también sabemos que la tierra no es exactamente una esfera y que está achatada en los polos, y como el radio no es el mismo sobre el ecuador que hacia los polos, haremos un promedio y asumiremos que la tierra tiene un radio de 6.371 km, ese es el número que hoy la mayoría del mundo acuerda usar para estas cosas.

Vamos bien. Con la fórmula de Pitágoras podíamos conocer la distancia entre dos puntos en el plano trazando un triángulo rectángulo.

Ahora vamos a ver que para calcular la distancia sobre una circunferencia, esa fórmula se complica muchísimo! jajaja. Para quienes tengan interés pueden visitar la fórmula de Haversine y ahí encontrarán todos los detalles. Su definición es así:

Lo importante para entender aquí es que estamos manteniendo «la forma» de Pitágoras, pero agregamos senos y cosenos porque estamos trabajando con circunferencias. Por último, aquí hay más detalles de la fórmula final para calcular la distancia mínima entre dos puntos sobre la superficie de una esfera.

A BUSCAR PUNTOS CON MYSQL!

Supongamos que tenemos una tabla de MySQL que se llama «locations» y tiene tres campos {id, lat, lng}

La latitud y Longitud de Península Valdés en Chubut, Argentina es -42.581, -64.249. Supongamos que queremos calcular los bares y restaurantes que están a una distancia menor a 5 km de ese lugar, nuestra consulta será:

SELECT id, ( 6371 * acos( cos( radians(-42.581) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(-64.249) ) + sin( radians(-42.581) ) * sin( radians( lat ) ) ) ) AS distance 
FROM locations 
HAVING distance < 5;

Y QUÉ PASA SI QUIERO HACERLO CON JAVASCRIPT?

Desde hace un tiempo Javascript tiene disponible SQLite y indexedDB, aunque con soporte limitado. En este ejemplo vamos a suponer que no disponemos de base de datos y que sólo tenemos la información disponible en un Array de objetos, con la forma:

[ {lat, lng} … {lat, lng} ] o también podría ser [ [lat, lng] … [lat, lng] ]

En este caso no usaremos SQL, sino que la solución será recorrer nuestro array de locations y para cada una medir la distancia que tenemos desde nuestro punto-centro. Si la distancia es menor a cierto valor, consideraremos que ese punto está «dentro del círculo».

Para medir la distancia entre dos puntos con Javascript te recomiendo usar la librería Turf.js que es super sencilla y tiene muchísimas funciones para el análisis geoespacial (GIS) con Javascript.

Para medir la distancia usaremos la función distance de Turf que usa nuestra fórmula de Haversine para calcular la distancia entre dos puntos, en grados, radianes, millas o kilómetros.

Nuestro código sería:

// cuidado que aquí es lng, lat !!
const from = turf.point([-64.249, -42.581]); 

const locations = [ 
  {lat: -42.346, lng: -64.194}, 
  {lat: -42.510, lng: -64.240}, 
  ... 
]; 

const radio = 5; 

let result = []; 

const options = {units: 'kilometers'};

let point = null;locations.forEach(item => { 
  point = turf.point([item.lng, item.lat]); 
  if (turf.distance(from, point, options)) < radio {
    result.push(point); 
  } 
});

Espero que los ejemplos te hayan gustado! durante los próximos días veremos más de Turf y su utilidad para mapas, aplicaciones web y teléfonos.

 

ÚLTIMOS ARTÍCULOS