Geoprocessing on the Client Side with Turf.js

7 Jan

Geoprocessing has been primarily a desktop activity. Using ArcServer, you can publish geoprocessing services. This takes geoprocessing off the desktop but requires communication between a client and server. I don’t mind you downloading my data and processing it on your desktop, but I really don’t like the idea of you using my CPU and memory running some harebrained geoprocessing task off my server. Given the advances in web technology, especially JavaScript, can’t we come up with something better? Can’t we let the client handle the work?

We can with Turf.js.

Using Turf.js you can perform a large number of commonly used geoprocessing functions client side.  In this post, I will show you how to buffer, point in polygon and sum a field for points in a polygon.

Buffer a Point

1. Using Leaflet.js, create a map and add a tile layer:

  • var map = L.map(‘map’, { center: [35.10418, -106.62987],zoom: 9});
  • L.tileLayer(‘http://{s}.tile.osm.org/{z}/{x}/{y}.png’).addTo(map);

2. Create two points using turf.point and Long,Lat.

  • pointOne = turf.point(-106.32568,35.11542);
  • pointTwo = turf.point(-106.33,35.22)

3. The points are now in GeoJSON. to add them to Leaflet.js us L.geoJson.

  • L.geoJson(pointOne).addTo(map);
  • L.geoJson(pointTwo).addTo(map);

4. Buffer a point and assign the result to a variable. Then add the buffer to the map. the buffer function takes a feature (point, line, polygon, feature collection), a distance, and the units (miles, kilometers or degrees).

  • var b = turf.buffer(pointOne,2,”miles”);
  • L.geoJson(b).addTo(map);

Now you should have a map that looks like the one below.

Two points with one buffered.

Two points with one buffered.

Point in Polygon

Now that we have two points and a buffer, let’s perform a point in polygon.

1. Create a polygon from the buffer.

  • var polygon = turf.polygon(b.features[0].geometry.coordinates, {
    “fill”: “#6BC65F”,
    “stroke”: “#6BC65F”,
    “stroke-width”: 5,
    “title”:”Polygon”,
    “description”:”A sample polygon”
    });

2. To PIP, use turf.inside() passing the point and polygon as parameters. the result will be true or false.

  • alert(“pointTwo is inside? “+turf.inside(pointTwo, polygon));

Now you will be alerted that the point is not inside the polygon.

Point not in Polygon

Point not in Polygon

In the previous example, the features did not have any attributes. In the next geoprocessing example, we will calculate a value from points in a polygon.

Using Statistics: Sum

1. This example starts with a Leaflet.js map.

  • var map = L.map(‘map’, {center: [35.10418, -106.62987],zoom: 9});
  • L.tileLayer(‘http://{s}.tile.osm.org/{z}/{x}/{y}.png’).addTo(map);

2. Add a function for iterating through features so we can add a popup.

  • function onEachFeature(feature, layer) {
    layer.bindPopup(“<h3>Add this number: “+feature.properties.title+”</h3>”+feature.properties.description);}

3. Now add your points, but this time we will add properties to the points.

  • var p1 = turf.point(-106,35, {“marker-color”: “#6BC65F”,”title”: 100, “description”: “Not in Polygon”, “someOtherProperty”:”I am another property” });
  • var p2 = turf.point(-106.62987,35.10418, {“marker-color”: “#6BC65F”,”title”: 4, “description”: “In Polygon”, “someOtherProperty”:”I am another property” });
  • var p3 = turf.point(-106.64429,35.14125, {“marker-color”: “#6BC65F”,”title”: 1, “description”: “Also in Polygon”, “someOtherProperty”:”I am another property” });

4. To sum a filed, you will need at least one polygon – you can use multiple as well.

  • var polygon = turf.polygon([ [
    [-106.73355,35.21197],[-106.73355,35.04911],[ -106.51932,35.04911],[-106.49872,35.19177]
    ]], {
    “fill”: “#6BC65F”,
    “stroke”: “#6BC65F”,
    “stroke-width”: 5,
    “title”:”Polygon”,
    “description”:”A sample polygon”
    });

5. Create feature collections for the polygon(s) and points. Add them to the map using an option to call your onEachFeature function.

  • var p = turf.featurecollection([polygon]);
    var t = turf.featurecollection([p1,p2,p3]);
  • L.geoJson(p).addTo(map);
  • L.geoJson(t, {
    onEachFeature: onEachFeature
    }).addTo(map);

6. Now pass the sum function the polygon, points, the field to sum and the name of the output field.

  • var sum = turf.sum(p,t,”title”,”output”);

7. when you click the map you will get the result. Notice the marker with the value of 100 is ignored since it is outside the polygon.

  • map.on(“click”,function(){alert(sum.features[0].properties.output);});

Lastly, when you can click a marker and the popup information is displayed.

sum

sum2

 

Running geoprocessing tasks without having to pass data back and forth from client to server is the way to go. It also means your browser can now work as a simple desktop GIS application.

16 Responses to “Geoprocessing on the Client Side with Turf.js”

  1. KamalOgudah June 2, 2015 at 7:56 am #

    Reblogged this on PRAGMATIC URBANISM.

  2. aizquierdo021ier October 26, 2015 at 7:18 pm #

    Is there a way to choose my own buffer while i am in the map?

    • paulcrickard October 26, 2015 at 7:23 pm #

      What are you trying to do with it? You can get the GeoJSON of the buffer by assigning it to a variable:
      buffered = turf.buffer(b,.05,”miles”); Or a Leaflet object by var :buff=L.geoJson(buffered).addTo(map);
      Is that what you need?

      • aizquierdo021ier October 26, 2015 at 8:26 pm #

        what I am trying to do (using your example) is to create a form or sth where the user is able to write the buffer distance but I do not know how to do it

      • paulcrickard October 26, 2015 at 8:34 pm #

        Ok, I got it. You need to add a marker to the map. Open a popup on the marker with a form that allows you to enter a value for the buffer. Then have a submit button. When clicked, grab the value in the box and run turf.buffer with that value.

      • aizquierdo021ier October 26, 2015 at 8:42 pm #

        In this case we have only two point, but in the case of having more than two points and if I want to create a buffer from a layer of 200 points….I would like to know if you kwnow how to create a form where writting a distance, you can obtain a buffer

        Thak u very much

      • paulcrickard October 26, 2015 at 8:47 pm #

        You want it for each point individually or for all points the same distance?

      • paulcrickard October 26, 2015 at 8:50 pm #

        Here is Point by Point
        var k;
        var map = L.map(‘map’, {center: [35.10418, -106.62987], zoom:10 });
        L.tileLayer(‘http://{s}.tile.osm.org/{z}/{x}/{y}.png’).addTo(map);
        map.on(“click”,function(e){

        a=L.marker(e.latlng).bindPopup(‘Buffer Distance: Submit’).addTo(map).openPopup();

        b=a.toGeoJSON();

        });//end on

        function getDist(){
        var a=document.getElementById(“dist”).value;
        console.log(a);
        var buffered = turf.buffer(b,a,”miles”);
        var k=L.geoJson(buffered).addTo(map);

        }

      • paulcrickard October 26, 2015 at 8:59 pm #

        Here is how to do all the same. You need to disable the map default on clicks or propogation on the textbox. I didnt do that. Double click a bunch of markers. Then in lower left, enter a number then click the submit. It will buffer them all
        var mkers=[];
        var k;
        var map = L.map(‘map’, {center: [35.10418, -106.62987], zoom:10 });
        L.tileLayer(‘http://{s}.tile.osm.org/{z}/{x}/{y}.png’).addTo(map);
        map.on(“dblclick”,function(e){

        L.marker(e.latlng).addTo(map);
        mkers.push(L.marker(e.latlng));

        });//end on

        function getDist(){
        var a=document.getElementById(“dist”).value;
        for(var x = 0;x<mkers.length;x++){
        var b=mkers[x].toGeoJSON();
        var buffered = turf.buffer(b,a,"miles");
        var k=L.geoJson(buffered).addTo(map);

        }

        }

        var done = L.control({ position: 'bottomleft' });

        done.onAdd = function(map) {
        this._div = L.DomUtil.create('div', 'mylegend-css-class');
        this._div.innerHTML = 'Buffer Distance: Submit’;
        return this._div;
        };

        done.addTo(map);

      • aizquierdo021ier October 26, 2015 at 9:26 pm #

        Thank u very much. I supose that the process is the same if I work with a *.js layer…don´t I?

      • paulcrickard October 26, 2015 at 9:35 pm #

        I would think so.

  3. aizquierdo021ier October 26, 2015 at 8:55 pm #

    I would like for all points…

    • aizquierdo021ier October 26, 2015 at 8:55 pm #

      the same distance

      • paulcrickard October 26, 2015 at 9:00 pm #

        Here is how to do all the same. You need to disable the map default on clicks or propogation on the textbox. I didnt do that. Double click a bunch of markers. Then in lower left, enter a number then click the submit. It will buffer them all
        var mkers=[];
        var k;
        var map = L.map(‘map’, {center: [35.10418, -106.62987], zoom:10 });
        L.tileLayer(‘http://{s}.tile.osm.org/{z}/{x}/{y}.png’).addTo(map);
        map.on(“dblclick”,function(e){

        L.marker(e.latlng).addTo(map);
        mkers.push(L.marker(e.latlng));

        });//end on

        function getDist(){
        var a=document.getElementById(“dist”).value;
        for(var x = 0;x<mkers.length;x++){
        var b=mkers[x].toGeoJSON();
        var buffered = turf.buffer(b,a,"miles");
        var k=L.geoJson(buffered).addTo(map);

        }

        }

        var done = L.control({ position: 'bottomleft' });

        done.onAdd = function(map) {
        this._div = L.DomUtil.create('div', 'mylegend-css-class');
        this._div.innerHTML = 'Buffer Distance: Submit’;
        return this._div;
        };

        done.addTo(map);

      • paulcrickard October 26, 2015 at 9:02 pm #

        Here is a working example
        http://paulcrickard.github.io/buffer.html

Trackbacks/Pingbacks

  1. Query Albuquerque Open Data with Turf.js | Architecture and Planning - February 9, 2015

    […] showing how to use Python and for converting the Date field. I have also demostrated how to use Turf.js for analysis. In this post, I will show you how to combine the two to limit your query of the Albuquerque Crime […]

Leave a comment