Rincón JavaScript: Turf.js

Tiempo de lectura: 4 minutos

Para compartir artículos en Maule Devs decidí comenzar una pequeña sección sobre bibliotecas de JavaScript con las que he trabajado. En este caso me gustaría hablar de Turf.js, una biblioteca para múltiples tareas geográficas y que facilita la interacción con objetos GeoJSON, un estándar para representar información geográfica en distintas aplicaciones. Se define como una biblioteca para análisis espacial y es mantenida por la empresa MapBox.

Muchas veces me ha pasado al desarrollar software que subestimo el esfuerzo que requiere el realizar ciertas cosas. Así fue como en un proyecto del trabajo tuve que trabajar calculando distancias entre coordenadas de latitud y longitud. Siempre he sabido que la latitud y la longitud tienen que ver con los paralelos y los meridianos de la tierra, y que no son lo mismo que un plano cartesiano pero no le tomé el peso hasta que me tocó calcular una distancia. Había una parte de mi cerebro que imaginaba que el cálculo no sería más complejo que una pequeña variación de la distancia euclidiana. Nada más alejado de la realidad. Busqué y busqué hasta que dí con la fórmula de Haversine que sirve para calcular la curvatura en un globo, pero al implementarla había que considerar constantes asociadas a la curvatura de la Tierra y bueno, mi implementación resultó en una mezcla de la fórmula con ciertas aproximaciones basadas en comentarios de StackOverflow… Ahí dije “No. Definitivamente no me manejo bien en esto, así que debo confiar en alguien que sepa.” y ahí encontré Turf.js.

Cuando uno está en un proyecto de software y tiene que entrar en un área que no domina, debe apelar a conocimiento ajeno que se pueda importar (no tenía el tiempo para aprender a usar una herramienta como ArcGIS o otras por el estilo). Por eso, busqué bibliotecas y esta me llamó la atención.

Lo que yo sabía de información geográfica era principalmente sobre las representaciones GeoJSON que puede almacenar MongoDB y algo de consultas geoespaciales. Y eso sería todo. Para los que no sepan, GeoJSON es un estándar que permite definir información geográfica como puntos, líneas o polígonos usando objetos JSON. Y es la estructura con la que almaceno objetos geográficos en la base de datos.

Turf.js llegó como anillo al dedo a integrarse con mi implementación. Integrarla en una aplicación web es facilísimo. Basta con integrar el script en la página y listo:

<script src='https://npmcdn.com/@turf/turf/turf.min.js'></script>

Si quieren utilizarlo como un módulo dentro de un framework o importarlo en Node.js, pueden utilizar la sintaxis correspondiente:

// Para importar la biblioteca completa
import * as turf from '@turf/turf'

O pueden importar algún módulo específico de Turf con alguna de estas sintaxis:

// Usando require
var MODULO = require('@turf/MODULO');

// O en ES6
import MODULO from '@turf/MODULO';

Pero bueno, hay más detalle sobre eso en la página. De lo que me interesa hablar es de cómo resolvió mis problemas.

Para realizar cálculos en Turf.js, tenemos que utilizar sus estructuras de datos. La más básica es un punto. Si queremos declarar un punto en el centro de Talca, podemos declararlo así (usando el orden “longitud, latitud”):

let point = turf.point([-71.6656202,-35.4265661]);

Si escribimos inspeccionamos la variable point, veremos que es un objeto GeoJSON al que además se le agregan ciertos métodos. Pero básicamente es algo así:

{
  type: 'Feature',
  geometry: {
    type: 'Point',
    coordinates: [ -71.6656202, -35.4265661 ]
  },
  properties: {}
}

Existen distintos tipos de objetos GeoJSON para distintas aplicaciones: Point , LineString , Polygon , MultiPoint , MultiLineString , y MultiPolygon. Por ahora sólo nos interesa Point.

Si queremos agregar información adicional al punto, podemos especificar un objeto de propiedades al inicializarlo. Esto sirve cuando el punto lleva información de lugares o variables asociadas a la ubicación como mediciones de algún sensor. Para este caso definiremos dos puntos, uno en Talca y otro en Pencahue.

let talca = turf.point(
  [-71.6656202,-35.4265661],
  {
    city: ‘Talca’
  }
);
let pencahue = turf.point(
  [-71.7990855,-35.3964126],
  {
    city: ‘Pencahue’
  }
);

Ahora, si queremos calcular la distancia entre ambos puntos podemos usar la función distance. Esta función recibe dos puntos como argumento y un objeto con un parámetro describiendo la unidad.

let distance = turf.distance(talca, pencahue, { units: ‘kilometers’ }); // 12.551456767278939

También podríamos hacer otras operaciones como calcular el punto medio entre ambos puntos.

let midpoint = turf.midpoint(talca, pencahue);

Lo que nos devolverá un punto GeoJSON en el centro entre ambos.

“aerial photography of city” by Dennis Kummer on Unsplash

Y así hay muchas otras funciones que Turf.js ofrece para resolver una gran cantidad de problemas de análisis geográfico. Un gran ejemplo que me ha tocado ver en el trabajo es el problema de interpolar variables geográficamente.

Imaginen el siguiente escenario: Tengo tres estaciones de monitoreo en los extremos de la ciudad que miden la calidad del aire y me interesa saber el estado del aire en el centro usando los tres valores como referencia. Uno de los algoritmos más utilizados para este tipo de problemas es IDW o Inverse Distance Weighting, que busca generar un valor ponderando los valores disponibles según la distancia a la que se encuentren del punto. Esto quiere decir que se obtendrá un resultado más similar al valor más cercano y que los valores lejanos tendrán poca influencia.

Esta y otras funciones más ya vienen listas en Turf.js, lo que nos facilita mucho la vida a la hora de tener que desarrollar sistemas que procesen información geográfica, especialmente cuando no tenemos el conocimiento específico del área. Los invito a echarle un ojo a la biblioteca y a comentar si la han utilizado o conocen otra alternativa.

¡Hasta el próximo artículo!

También te podría gustar...