Using Turf.js to find Carbon Monoxide Levels in Albquerque

12 Feb

I have posted several times on Turf.js and as I explore the possibilities, I found something that peaked my interested and had to find a way to integrate it with Albuquerque Open Data. In this post, I will show you how to click a map and get back the Carbon Monoxide levels for Albuquerque at the location.

The Goal

The goal of this application is to find the carbon monoxide levels at a given point in Albuquerque. To accomplish this, we need to first map the Air Pollution Sources data from the City of Albuquerque and then find a way to interpolate the values between each location. The finished application will look like the image below.

TIN created from CO levels

TIN created from CO levels

How to Accomplish the Goal

Turf has a function planepoint that takes a triangle with values at each corner and can interpolate the value at any point on the plane. This is cool. But where do I get a triangle with some values at each corner? Well, the best way to get a bunch of triangles with values it to create a TIN from a series of points. Now I just needed some data. Albuquerque publishes a data set called Air Pollution Sources that has 1000 records. This is a great start. Let me issue a warning before continuing:

The level of CO at any point may or may not be accurate. The application is a proof of concept showing that for a series of points, values can be interpolated between them. This assumes that the values actually change over a distance and do not exist solely at said location.

Now that I know you and the City are not going to sue me, let’s continue.

Using Triangulated Irregular Networks and Planepoint

The logic of the application is to go from points, to a TIN, to a triangle, then add a point and get back a value. Getting the points on the map is easy. Make an AJAX call to the REST endpoint and add a marker.

var params=”where=FAC_NAME not in (‘COLORS ON PARADE’)&f=json&outSR=4326&outFields=*”; var url = “http://coagisweb.cabq.gov/arcgis/rest/services/public/environmentalissues/MapServer/1/query&#8221;; console.log(params); http=new XMLHttpRequest(); http.open(“POST”, url, true); http.setRequestHeader(“Content-type”, “application/x-www-form-urlencoded”); http.onreadystatechange = function() {//Call a function when the state changes. if(http.readyState == 4 && http.status == 200) { var result= JSON.parse(http.responseText); for(x=0;x<result.features.length;x++){ L.marker([result.features[x].geometry.y,result.features[x].geometry.x]).bindPopup(“<h3>”+result.features[x].attributes.FAC_NAME+”</h3>”).addTo(map); } }} http.send(params);

Notice that my parameter has a where clause removing any record named “COLORS ON PARADE.” After inspecting the data, I noticed that the coordinates for these location were bad – some where 0 and crashed the application. Otherwise, the code above is the same block I have used in several other posts on mapping with Leaflet.js and the ESRI REST API. Having the points on the map accomplishes step 1. Now we can create a TIN. To make the TIN, I need the points to be in a Feature Collection. I will setup an empty feature collection.

var features = { “type”: “FeatureCollection”, “features”: [] };

Then, in the code where we added the markers previously, we will add a turf.point feature to the feature collection instead of adding a marker.

for(x=0;x<result.features.length;x++){ features.features.push(turf.point([result.features[x].geometry.x,result.features[x].geometry.y],{“z”:result.features[x].attributes.CO_TPY})); }

A turf.point allows attributes – it’s geoJSON – so we will pass the Z value needed to create the TIN. The Z value will be the CO_TPY reading of the data set. Now create the TIN and add it to the map.

var tin = turf.tin(features, ‘z’)

var t = L.geoJson(tin).addTo(map);

The user needs to be able to click on the TIN and get a value from an individual plane (triangle). Leaflet allows us to setup an “onclick” event so this is where our code will go.

t.on(“click”,function(e){ …..CODE HERE… }

To get to a specific plane(triangle) in the TIN, we need to take the user click and perform a point in polygon operation. So let’s get the users click location and convert it to GeoJSON so we can use it in Turf.js. a=L.marker(e.latlng); b=a.toGeoJSON(); Now we can use turf.inside to find the plane(triangle) the user clicked in. We will loop through all the planes(triangles) and perform turf.inside until we find the one, then we will call it theT.

for (var i = 0; i<tin.features.length;i++){ if(turf.inside(b, tin.features[i])) {theT = tin.features[i];} }

We now have a point and a plane. We can grab the value of the point using turf.planepoint and attach it to a popup.

var zValue = turf.planepoint(b,theT); a.bindPopup(“<h3>”+zValue+”</h3>”).addTo(map).openPopup();

We are done. We

  1. Loaded a series of points
  2. Created a TIN
  3. Used point in polygon to grab a plane
  4. Used planepoint to get the value

And we did it all on the client side! Below is an image of the map with all the locations added. points

Advertisements

One Response to “Using Turf.js to find Carbon Monoxide Levels in Albquerque”

Trackbacks/Pingbacks

  1. Density Map with Turf.js | Architecture and Planning - February 18, 2015

    […] a previous post, I showed how to interpolate using a TIN and Turf.js. Turf.js has other interpolation tools and in the post, I will show you how to construct a dot […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: