Turf: GIS en la web

Los chicos de mapbox nos han traído un regalito muy especial este fin de año, una nueva librería de javascript llamada Turf.  Turf permite hacer operaciones geospaciales  (geoprocesamiento) directamente desde el navegador (#inabrowserbaby).

Turf es ligero, rápido y opensource (chupate esa ArcgisOnline)


Muchas de las operaciones GIS clásicas están permitidas: buffering, within, merge, union, contouring, TINs, etc. Turf permite usar Geojson directamente y funciona perfectamente con nuestro gran amigo LeafletJS.

En este ejemplo vamos a localizar las gasolineras que nos encontraremos en la ruta Barcelona-Zaragoza. Primero crearemos un área de influencia, o buffer, de 500m a partir de la linea de la ruta y luego calcularemos los puntos (gasolineras) que caen dentro de esa área usando la función within.

Los datos con el precio (a fecha 31/12/2014) y la localización de las gasolineras los he sacado de geoportalgasolineras.es/. Los datos están en xls por lo que primero los he pasado a csv y luego con QGIS los he transformado en GeoJson.

Las librerías que vamos a necesitar son leaflet y turf. Las añadimos:

<link href="http://cdn.leafletjs.com/leaflet-0.7.1/leaflet.css" rel="stylesheet" />
<script type="text/javascript" src="http://cdn.leafletjs.com/leaflet-0.7.1/leaflet.js?2"></script>
<script type="text/javascript" src="https://api.tiles.mapbox.com/mapbox.js/plugins/turf/v1.3.0/turf.min.js"></script>

Añadimos u mapa base, en este caso OSM, y creamos el mapa:

var InitialCenter = new L.LatLng(40.366194,-3.655639);
var map = L.map('map');

//create base maps layers
var osmlayer =  L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png',  
                { }).addTo(map);

Luego añadimos las gasolineras y la ruta. A las gasolineras les asignamos un circulo azul, la ruta la ponemos en rosa.

var gasolinerasStyle = {
    radius: 5,
    fillColor: "blue",
    color: "#000",
    weight: 1,
    opacity: 1,
    fillOpacity: 0.7
};

var gasolineras_fcLayer =L.geoJson(gasolineras_geojson, {
    pointToLayer: function (feature, latlng) {
        return L.circleMarker(latlng, gasolinerasStyle);
    }
}).addTo(map);

var ruta_fcLayer =L.geoJson(ruta_geojson_file)
ruta_fcLayer.setStyle({ color: 'hotpink', weight: 3 });
ruta_fcLayer.addTo(map);

Ahora viene el geoprocesamiento propiamente dicho, vamos a añadir un buffer entorno a la polilínea formada por la ruta que vamos a seguir. Inicialmente este buffer tendrá un radio de 500m. En Turf la función buffer necesita que le pasemos como parámetros la feature (el Geojson con la ruta), la distancia, y las unidades.

turf.buffer(feature, distance, unit)

Nota: creamos el buffer a partir de una feature collection (Geojson) y el resultado  después de realizar la operación será también una feature collection.

Podemos realizar un buffer a partir de un punto, una linea o un polígono. Las unidades pueden ser millas o kilómetros o grados (miles, kilometers, degrees). En nuestro caso el radio está en metros por lo que lo tenemos que dividir por mil para obtener kilómetros Una vez hecho esto solo hay que añadir el buffer al mapa como si fuera un Geojson normal. Le podemos añadir estilo, una popup .. lo que queramos.

var buffer = turf.buffer(ruta, radius/1000, 'kilometers');
var bufferLayer =L.geoJson(buffer, {
    onEachFeature: function (feature, layer) {
       layer.bindPopup('Barcelona-Zaragoza Route')
    }
}).addTo(map);

El siguiente paso es calcular las gasolineras que quedan dentro de ese radio de 500m alrededor de la ruta.  Para ello usaremos la función within, donde los puntos serán las gasolineras (feature collection) y el polígono será el buffer (feature collection) :

turf.within(points, polygons)

Con within hacemos una selección espacial de las gasolineras, quedándonos solo con las que caen dentro de buffer que hemos creado.

En rojo las gasolineras que quedan dentro de un radio de 500m de la ruta seleccionada.

var stationsInside = turf.within(gasolineras_geojson, buffer);

A estas gasolineras les vamos a poner un color rojo para distinguirlas de las que caen fuera del buffer y les añadiremos una popup con la información acerca del precio y los horarios.

var stationsNumber=0;
var gasstationsinside =L.geoJson(stationsInside, {
      onEachFeature: function(feature, layer) {
           stationsNumber++
           layer.bindPopup("<h4>"+feature.properties.Rotulo+"</h4>
           <b>Localidad:</b>" + feature.properties.Localidad + 
           "<b>Gasolina95:</b>" + feature.properties.Gasolina95 + 
           "<b>Gasolina98:</b>" + feature.properties.Gasolina98 + 
           "<b>Gasoleo A: </b>" + feature.properties.Gasoleo_A + 
           "<b>Nuevo Gasoleo A:</b>"+ feature.properties.NuevoGasoleo_A+
           "<b>Horario:</b>" + feature.properties.Horario); 
      }, 
      pointToLayer: function (feature, latlng) { 
          return L.circleMarker(latlng); 
      }, 
      style:{ radius: 8, fillColor: "red", weight: 1 } 
}).addTo(map);

El resultado final:

El código completo de este ejemplo lo tenéis aquí http://bl.ocks.org/sigon426/6ace38a598add764a90b, esta basado en este mapa: https://www.mapbox.com/bites/00082/

Teneis muchos más ejemplos en su web http://turfjs.org/examples.html