{"id":1457,"date":"2022-09-26T23:53:27","date_gmt":"2022-09-26T22:53:27","guid":{"rendered":"https:\/\/theroamingworkshop.cloud\/b\/?p=1457"},"modified":"2022-10-24T19:52:25","modified_gmt":"2022-10-24T18:52:25","slug":"turfjs-interactive-geospatial-tools-for-your-web-maps","status":"publish","type":"post","link":"https:\/\/theroamingworkshop.cloud\/b\/en\/1457\/turfjs-interactive-geospatial-tools-for-your-web-maps\/","title":{"rendered":"TurfJS: interactive geospatial tools for your web maps"},"content":{"rendered":"\n<p>If you&#8217;ve used desktop GIS software, you will be surprised that many geospatial tools are also available to use in your web maps using a free and open-source javascript library.<\/p>\n\n\n\n<p>Today I show you <strong>TurfJS<\/strong>. Press the button  \ud83c\udf24  in the map below and see one of many possibilities: <strong>inverse distance weighting<\/strong> or <strong>IDW<\/strong>.<\/p>\n\n\n\n<iframe style=\"margin:0;padding:0;height:300px;width:100%;border:none\" src=\"https:\/\/theroamingworkshop.cloud\/leafMET\/leafMET+Turf.html\"><\/iframe>\n\n\n\n<div id=\"menu\" style=\"padding:20px 20px 20px 20px; border-left:2px solid darkgrey;\">\n<p style=\"font-weight:bold;\">Content<\/p>\n<\/div>\n<script>\nvar text;\nvar element;\n\nfunction fillmenu(){\n\nfor (let i=0; i<window.document.getElementsByTagName(\"h2\").length; i++){\nelement = window.document.getElementsByTagName(\"h2\")[i];\ntext = \"\u25b9 \"+element.innerHTML;\nvar newelement = document.createElement(\"a\");\nnewelement.innerHTML=text;\nvar postid=window.document.getElementsByTagName(\"article\")[0].id;\nvar url = \"https:\/\/theroamingworkshop.cloud\/b\/?p=\"+postid.substr(5,postid.length)+\"#\"+element.id;\nnewelement.setAttribute(\"href\", url);\nnewelement.appendChild(document.createElement(\"br\"));\nwindow.document.getElementById(\"menu\").appendChild(newelement);\nconsole.log(text);\n}\n}\n\nwindow.setTimeout(fillmenu,2000);\n\n<\/script>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"installation\">Installation<\/h2>\n\n\n\n<p><strong>TurfJS<\/strong> hosts a practical website explaining all available functions, as well as the initial setup:<\/p>\n\n\n\n<p><a href=\"https:\/\/turfjs.org\/getting-started\">https:\/\/turfjs.org\/getting-started<\/a><\/p>\n\n\n\n<p>To use <strong>Turf<\/strong> you only need to link the library in your map using the CDN link:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;script src='https:\/\/unpkg.com\/@turf\/turf@6\/turf.min.js'&gt;&lt;\/script&gt;<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"base-map\">Base map<\/h2>\n\n\n\n<p>You can create a basemap, for example, with Leaflet. <strong>TurfJS<\/strong> uses <strong>JSON<\/strong> sintaxis, so it's highly compatible with the <strong>geoJSON<\/strong> capabilities in Leaflet.<\/p>\n\n\n\n<p style=\"border-left:3px solid orange;padding-left:5px;font-size:14px;\"><i><b>TIP! <\/b>Leaflet has been recently updated to version 1.9.1. This post hasn't been updated and still uses version 1.7.1.<\/i><\/p>\n\n\n\n<p>I will recycle <a rel=\"noreferrer noopener\" href=\"https:\/\/theroamingworkshop.cloud\/b\/?p=862\" target=\"_blank\">this example from one of my first posts<\/a> with a few changes:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Add the Turf library link in the <code>&lt;head&gt;<\/code>.<\/li><li>Define the markers in a data matrix or array, similar to how they are obtained from any data source in JSON format:<\/li><\/ul>\n\n\n\n<p><a href=\"https:\/\/www.w3schools.com\/js\/js_arrays.asp\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/www.w3schools.com\/js\/js_arrays.asp<\/a><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>var markers=&#91;\n{\"name\":\"Estacion1\",\n\"lat\":25.77,\n\"lon\":-80.13,\n\"dato\":26.4},\n{\"name\":\"Estacion2\",\n\"lat\":25.795,\n\"lon\":-80.21,\n\"dato\":29.8},\n{\"name\":\"Estacion3\",\n\"lat\":25.70,\n\"lon\":-80.275,\n\"dato\":31.35}\n];<\/code><\/pre>\n\n\n\n<p style=\"border-left:3px solid orange;padding-left:5px;font-size:14px;\"><i><b>TIP! <\/b>Numeric values <b>won't<\/b> go in quotation marks, otherwise Turf will take it as a String (something that doesn't happen in Leaflet).<\/i><\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Add the markers to the map in a loop, using <code>forEach()<\/code>, widely used in these cases:<\/li><\/ul>\n\n\n\n<p><a href=\"https:\/\/www.w3schools.com\/jsref\/jsref_foreach.asp\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/www.w3schools.com\/jsref\/jsref_foreach.asp<\/a><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>markers.forEach(function(m){\n    var marker=L.marker(&#91;m.lat, m.lon]).addTo(map);\n    marker.bindTooltip(\"&lt;b&gt;\"+m.name+\"&lt;\/b&gt;&lt;br&gt;\"+m.dato).openTooltip();\n});\n\n\/\/WE ADDED A SINGLE MARKER LIKE THIS ON LEAFLET\n\/\/var marker = L.marker(&#91;25.77, -80.13]).addTo(map);\n\/\/marker.bindTooltip(\"&lt;b&gt;Estacion1&lt;\/b&gt;&lt;br&gt;m.dato\").openTooltip();<\/code><\/pre>\n\n\n\n<p>Looking like this:<\/p>\n\n\n\n<head>\n<link rel=\"stylesheet\" href=\"https:\/\/unpkg.com\/leaflet@1.7.1\/dist\/leaflet.css\"\n  integrity=\"sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6\/keqq\/sMZMZ19scR4PsZChSR7A==\"\n  crossorigin=\"\"\/>\n<script src=\"https:\/\/unpkg.com\/leaflet@1.7.1\/dist\/leaflet.js\"\n  integrity=\"sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==\"\n  crossorigin=\"\"><\/script>\n<script src='https:\/\/unpkg.com\/@turf\/turf@6\/turf.min.js'><\/script>\n<\/head>\n<style>\n#map1{border-radius: 30px;}\n<\/style>\n<body>\n<div id=\"map1\" style=\"height:500px;\"><\/div>\n<\/body>\n<script>\nvar map1 = L.map('map1').setView([25.75, -80.2], 12);\nL.tileLayer(\"http:\/\/a.tile.openstreetmap.org\/{z}\/{x}\/{y}.png\", {\/*no properties*\/\n}).addTo(map1);\nvar markers=[\n{\"name\":\"Estacion1\",\n\"lat\":25.77,\n\"lon\":-80.13,\n\"dato\":26.4},\n{\"name\":\"Estacion2\",\n\"lat\":25.795,\n\"lon\":-80.21,\n\"dato\":29.8},\n{\"name\":\"Estacion3\",\n\"lat\":25.70,\n\"lon\":-80.275,\n\"dato\":31.35}\n];\nmarkers.forEach(function(m){\n    var marker=L.marker([m.lat, m.lon]).addTo(map1);\n    marker.bindTooltip(\"<b>\"+m.name+\"<\/b><br>\"+m.dato).openTooltip();\n});\n<\/script>\n\n\n\n<p>and this one being its code:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;head&gt;\n&lt;link rel=\"stylesheet\" href=\"https:\/\/unpkg.com\/leaflet@1.7.1\/dist\/leaflet.css\"\n  integrity=\"sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6\/keqq\/sMZMZ19scR4PsZChSR7A==\"\n  crossorigin=\"\"\/&gt;\n&lt;script src=\"https:\/\/unpkg.com\/leaflet@1.7.1\/dist\/leaflet.js\"\n  integrity=\"sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==\"\n  crossorigin=\"\"&gt;&lt;\/script&gt;\n&lt;script src='https:\/\/unpkg.com\/@turf\/turf@6\/turf.min.js'&gt;&lt;\/script&gt;\n&lt;\/head&gt;\n\n&lt;style&gt;\n#map{border-radius: 30px;}\n&lt;\/style&gt;\n\n&lt;body&gt;\n&lt;div id=\"map\" style=\"height:500px;\"&gt;&lt;\/div&gt;\n&lt;\/body&gt;\n\n&lt;script&gt;\nvar map = L.map('map').setView(&#91;25.75, -80.2], 12);\nL.tileLayer(\"http:\/\/a.tile.openstreetmap.org\/{z}\/{x}\/{y}.png\", {\/*no properties*\/\n}).addTo(map);\n\nvar markers=&#91;\n{\"name\":\"Estacion1\",\n\"lat\":25.77,\n\"lon\":-80.13,\n\"dato\":26.4},\n{\"name\":\"Estacion2\",\n\"lat\":25.795,\n\"lon\":-80.21,\n\"dato\":29.8},\n{\"name\":\"Estacion3\",\n\"lat\":25.70,\n\"lon\":-80.275,\n\"dato\":31.35}\n];\n\nmarkers.forEach(function(m){\n    var marker=L.marker(&#91;m.lat, m.lon]).addTo(map);\n    marker.bindTooltip(\"&lt;b&gt;\"+m.name+\"&lt;\/b&gt;&lt;br&gt;\"+m.dato).openTooltip();\n});\n&lt;\/script&gt;<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"idw-turfjs\">IDW interpolation with TurfJS<\/h2>\n\n\n\n<p>In the form above, data is now easily used by <strong>TurfJS<\/strong>.<\/p>\n\n\n\n<p>One of the most interesting tools is the <strong>interpolation<\/strong> of data in a <strong>regular grid<\/strong>. <\/p>\n\n\n\n<p><a href=\"https:\/\/turfjs.org\/docs\/#interpolate\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/turfjs.org\/docs\/#interpolate<\/a><\/p>\n\n\n\n<p>This is done using a very usual method: <strong>inverse distance weighting<\/strong> (<strong>IDW<\/strong>).<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><a href=\"https:\/\/theroamingworkshop.cloud\/b\/wp-content\/uploads\/2022\/09\/imagen.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img loading=\"lazy\" decoding=\"async\" width=\"147\" height=\"48\" src=\"https:\/\/theroamingworkshop.cloud\/b\/wp-content\/uploads\/2022\/09\/imagen.png\" alt=\"\" class=\"wp-image-1442\"\/><\/a><figcaption>IDW from <a rel=\"noreferrer noopener\" href=\"https:\/\/en.wikipedia.org\/wiki\/Inverse_distance_weighting\" target=\"_blank\">Wikipedia<\/a><\/figcaption><\/figure>\n<\/div>\n\n\n<p>Following the example given by TurfJS:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>Convert data into a Turf point layer, a featureCollection:<\/li><\/ol>\n\n\n\n<p><a rel=\"noreferrer noopener\" href=\"https:\/\/turfjs.org\/docs\/#featureCollection\" target=\"_blank\">https:\/\/turfjs.org\/docs\/#featureCollection<\/a><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/empty points array\nvar points=&#91;];\n\n\/\/loop to fill the array with data\nmarkers.forEach(function(m){\n    points.push(turf.point(&#91;m.lon, m.lat],{name: m.name, dato: m.dato}));\n});\n\n\/\/convert to Turf featureCollection\npoints=turf.featureCollection(points);<\/code><\/pre>\n\n\n\n<p style=\"border-left:3px solid orange;padding-left:5px;font-size:14px;\"><i><b>TIP! <\/b>Note that Turf taked coordinates as longitude-latitude, instead of the usual latitude-longitude!<\/i><\/p>\n\n\n\n<ol class=\"wp-block-list\" start=\"2\"><li>Define interpolation options, having the following:<ul><li><strong>gridType<\/strong>: or cell shape<ul><li>points<\/li><li>square<\/li><li>hex<\/li><li>triangle<\/li><\/ul><\/li><li><strong>property<\/strong>: field that contains the interpolation values.<\/li><li><strong>units<\/strong>: spatial units used in the interpolation:<ul><li>miles<\/li><li>kilometers<\/li><li>radians<\/li><li>degrees<\/li><\/ul><\/li><li><strong>weight<\/strong>: power used in the inverse distance weighting, usually 2, although higher values will make a smoother result.<\/li><\/ul><\/li><\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/interpolation options: rectangular grid using 'dato' variable in kilometers using the power of 2\n\nvar options = {gridType: 'square', property: 'dato', units: 'kilometers', weight: 2};<\/code><\/pre>\n\n\n\n<ol class=\"wp-block-list\" start=\"3\"><li>Finally, generate the interpolated grid and add it to the map, being the numeric value the cell size of the grid (0.5 Km):<\/li><\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/create Turf grid\nvar malla=turf.interpolate(points, 0.5, options);\n\n\/\/add to Leaflet\nL.geoJSON(malla).addTo(map);<\/code><\/pre>\n\n\n\n<p>Generating this result (nothing conclusive):<\/p>\n\n\n\n<head>\n<link rel=\"stylesheet\" href=\"https:\/\/unpkg.com\/leaflet@1.7.1\/dist\/leaflet.css\"\n  integrity=\"sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6\/keqq\/sMZMZ19scR4PsZChSR7A==\"\n  crossorigin=\"\"\/>\n<script src=\"https:\/\/unpkg.com\/leaflet@1.7.1\/dist\/leaflet.js\"\n  integrity=\"sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==\"\n  crossorigin=\"\"><\/script>\n<script src='https:\/\/unpkg.com\/@turf\/turf@6\/turf.min.js'><\/script>\n<\/head>\n<style>\n#map2{border-radius: 30px;}\n<\/style>\n<body>\n<div id=\"map2\" style=\"height:500px;\"><\/div>\n<\/body>\n<script>\nvar map2 = L.map('map2').setView([25.75, -80.2], 12);\nL.tileLayer(\"http:\/\/a.tile.openstreetmap.org\/{z}\/{x}\/{y}.png\", {\/*no properties*\/\n}).addTo(map2);\nvar markers=[\n{\"name\":\"Estacion1\",\n\"lat\":25.77,\n\"lon\":-80.13,\n\"dato\":26.4},\n{\"name\":\"Estacion2\",\n\"lat\":25.795,\n\"lon\":-80.21,\n\"dato\":29.8},\n{\"name\":\"Estacion3\",\n\"lat\":25.70,\n\"lon\":-80.275,\n\"dato\":31.35}\n];\nmarkers.forEach(function(m){\n    var marker=L.marker([m.lat, m.lon]).addTo(map2);\n    marker.bindTooltip(\"<b>\"+m.name+\"<\/b><br>\"+m.dato).openTooltip();\n});\n\/\/AS\u00cd A\u00d1AD\u00cdAMOS UN MARCADOR \u00daNICO A LEAFLET\n\/\/var marker1a = L.marker([25.77, -80.13]).addTo(map1);\n\/\/marker1a.bindTooltip(\"<b>Estacion1<\/b><br>Dato1\").openTooltip();\n\/\/Datos para interpolaci\u00f3n de Turf\n\/\/vector de puntos vac\u00edo\nvar points=[];\n\/\/bucle para rellenar el vector de puntos con los datos\nmarkers.forEach(function(m){\npoints.push(turf.point([m.lon, m.lat],{name: m.name, dato: m.dato}));\n});\n\/\/convertir a featureCollection de Turf\npoints=turf.featureCollection(points);\n\/\/opciones de interpolaci\u00f3n\nvar options = {gridType:\"square\",property:\"dato\",units:\"kilometers\",weight:2};\n\/\/crear malla de Turf\nvar malla=turf.interpolate(points, 0.5, options);\n\/\/a\u00f1adir a Leaflet\nmalla=L.geoJSON(malla).addTo(map2);\n<\/script>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"format-grid\">Format grid and show values<\/h2>\n\n\n\n<p>Seems not, but the interpolation is done, it's just shown incorrectly.<\/p>\n\n\n\n<p>I'll add a color ramp to represent data, and also show values when hovering the cells. Let's modify the Leaflet <strong>geoJSON object properties<\/strong> for this:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>style<\/strong>: function to modify object style, according to its value.<\/li><li><strong>onEachFeature<\/strong>: function added to each object to interact with it.<\/li><\/ul>\n\n\n\n<p><a href=\"https:\/\/leafletjs.com\/reference.html#geojson\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/leafletjs.com\/reference.html#geojson<\/a><\/p>\n\n\n\n<p><a href=\"https:\/\/leafletjs.com\/examples\/choropleth\/\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/leafletjs.com\/examples\/choropleth\/<\/a><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>L.geoJSON(malla,{\n  style:function(feature){\n  var val=feature.properties.dato;\n  if(val&gt;30){\n    return {fillColor: \"orangered\", fillOpacity: 0.5, weight:0};\n  }else if(val&gt;28 &amp;&amp; val &lt; 30){\n    return {fillColor: \"yellowgreen\", fillOpacity: 0.5, weight:0};\n  }else if(val&gt;26 &amp;&amp; val &lt; 28){\n    return {fillColor: \"darkgreen\", fillOpacity: 0.5, weight:0};\n  }\n},\nonEachFeature:function(feature,layer){\n  layer.on({\n  mouseover:function(e){\n    val=e.target.feature.properties.dato.toFixed(2);\n    e.target.bindTooltip(val).openTooltip();\n  }\n  });\n}\n}).addTo(map);\n<\/code><\/pre>\n\n\n\n<p>Ending like this:<\/p>\n\n\n\n<head>\n<link rel=\"stylesheet\" href=\"https:\/\/unpkg.com\/leaflet@1.7.1\/dist\/leaflet.css\"\n  integrity=\"sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6\/keqq\/sMZMZ19scR4PsZChSR7A==\"\n  crossorigin=\"\"\/>\n<script src=\"https:\/\/unpkg.com\/leaflet@1.7.1\/dist\/leaflet.js\"\n  integrity=\"sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==\"\n  crossorigin=\"\"><\/script>\n<script src='https:\/\/unpkg.com\/@turf\/turf@6\/turf.min.js'><\/script>\n<\/head>\n<style>\n#map3{border-radius: 30px;}\n<\/style>\n<body>\n<div id=\"map3\" style=\"height:500px;\"><\/div>\n<\/body>\n<script>\nvar map3 = L.map('map3').setView([25.75, -80.2], 12);\nL.tileLayer(\"http:\/\/a.tile.openstreetmap.org\/{z}\/{x}\/{y}.png\", {\/*no properties*\/\n}).addTo(map3);\nvar markers=[\n{\"name\":\"Estacion1\",\n\"lat\":25.77,\n\"lon\":-80.13,\n\"dato\":26.4},\n{\"name\":\"Estacion2\",\n\"lat\":25.795,\n\"lon\":-80.21,\n\"dato\":29.8},\n{\"name\":\"Estacion3\",\n\"lat\":25.70,\n\"lon\":-80.275,\n\"dato\":31.35}\n];\nmarkers.forEach(function(m){\n    var marker=L.marker([m.lat, m.lon]).addTo(map3);\n    marker.bindTooltip(\"<b>\"+m.name+\"<\/b><br>\"+m.dato).openTooltip();\n});\n\/\/Datos para interpolaci\u00f3n de Turf\n\/\/vector de puntos vac\u00edo\nvar points=[];\n\/\/bucle para rellenar el vector de puntos con los datos\nmarkers.forEach(function(m){\npoints.push(turf.point([m.lon, m.lat],{name: m.name, dato: m.dato}));\n});\n\/\/convertir a featureCollection de Turf\npoints=turf.featureCollection(points);\n\/\/opciones de interpolaci\u00f3n\nvar options = {gridType:\"square\",property:\"dato\",units:\"kilometers\",weight:2};\n\/\/crear malla de Turf\nvar malla2=turf.interpolate(points, 0.5, options);\n\/\/a\u00f1adir a Leaflet\nmalla2=L.geoJSON(malla2,{\n  style:function(feature){\n  var val=feature.properties.dato;\n  if(val>30){\n    return {fillColor: \"orangered\", fillOpacity: 0.5, weight:0};\n  }else if(val>28 && val < 30){\n    return {fillColor: \"yellowgreen\", fillOpacity: 0.5, weight:0};\n  }else if(val>26 && val < 28){\n    return {fillColor: \"darkgreen\", fillOpacity: 0.5, weight:0};\n  }\n},\nonEachFeature:function(feature,layer){\n  layer.on({\n  mouseover:function(e){\n    val=e.target.feature.properties.dato.toFixed(2);\n    e.target.bindTooltip(val).openTooltip();\n  }\n  });\n}\n}).addTo(map3);\n<\/script>\n\n\n\n<p>And here is the full code:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;head&gt;\n&lt;link rel=\"stylesheet\" href=\"https:\/\/unpkg.com\/leaflet@1.7.1\/dist\/leaflet.css\"\n  integrity=\"sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6\/keqq\/sMZMZ19scR4PsZChSR7A==\"\n  crossorigin=\"\"\/&gt;\n&lt;script src=\"https:\/\/unpkg.com\/leaflet@1.7.1\/dist\/leaflet.js\"\n  integrity=\"sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==\"\n  crossorigin=\"\"&gt;&lt;\/script&gt;\n&lt;script src='https:\/\/unpkg.com\/@turf\/turf@6\/turf.min.js'&gt;&lt;\/script&gt;\n&lt;\/head&gt;\n\n&lt;style&gt;\n#map{border-radius: 30px;}\n&lt;\/style&gt;\n\n&lt;body&gt;\n&lt;div id=\"map\" style=\"height:500px;\"&gt;&lt;\/div&gt;\n&lt;\/body&gt;\n\n&lt;script&gt;\nvar map = L.map('map').setView(&#91;25.75, -80.2], 12);\nL.tileLayer(\"http:\/\/a.tile.openstreetmap.org\/{z}\/{x}\/{y}.png\", {\/*no properties*\/\n}).addTo(map);\n\nvar markers=&#91;\n{\"name\":\"Estacion1\",\n\"lat\":25.77,\n\"lon\":-80.13,\n\"dato\":26.4},\n{\"name\":\"Estacion2\",\n\"lat\":25.795,\n\"lon\":-80.21,\n\"dato\":29.8},\n{\"name\":\"Estacion3\",\n\"lat\":25.70,\n\"lon\":-80.275,\n\"dato\":31.35}\n];\n\nmarkers.forEach(function(m){\n    var marker=L.marker(&#91;m.lat, m.lon]).addTo(map);\n    marker.bindTooltip(\"&lt;b&gt;\"+m.name+\"&lt;\/b&gt;&lt;br&gt;\"+m.dato).openTooltip();\n});\n\n\/\/empty points array\nvar points=&#91;];\n\n\/\/loop to fill the array with data\nmarkers.forEach(function(m){\n    points.push(turf.point(&#91;m.lon, m.lat],{name: m.name, dato: m.dato}));\n});\n\n\/\/convert to Turf featureCollection\npoints=turf.featureCollection(points);\n\n\/\/interpolation options: rectangular grid using 'dato' variable in kilometers using the power of 2\n\nvar options = {gridType: 'square', property: 'dato', units: 'kilometers', weight: 2};\n\n\/\/create Turf grid\nvar malla=turf.interpolate(points, 0.5, options);\n\nmalla=L.geoJSON(malla,{\n  style:function(feature){\n  var val=feature.properties.dato;\n  if(val&gt;30){\n    return {fillColor: \"orangered\", fillOpacity: 0.5, weight:0};\n  }else if(val&gt;28 &amp;&amp; val &lt; 30){\n    return {fillColor: \"yellowgreen\", fillOpacity: 0.5, weight:0};\n  }else if(val&gt;26 &amp;&amp; val &lt; 28){\n    return {fillColor: \"darkgreen\", fillOpacity: 0.5, weight:0};\n  }\n},\nonEachFeature:function(feature,layer){\n  layer.on({\n  mouseover:function(e){\n    val=e.target.feature.properties.dato.toFixed(2);\n    e.target.bindTooltip(val).openTooltip();\n  }\n  });\n}\n}).addTo(map);\n\n&lt;\/script&gt;<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"applications\">Applications<\/h2>\n\n\n\n<p>Applying this to real data, I modified the <a rel=\"noreferrer noopener\" href=\"https:\/\/theroamingworkshop.cloud\/b\/?p=1018\" target=\"_blank\">leafMET library<\/a> to add the interpolation using isobands.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/theroamingworkshop.cloud\/b\/wp-content\/uploads\/2022\/09\/turf1.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"692\" src=\"https:\/\/theroamingworkshop.cloud\/b\/wp-content\/uploads\/2022\/09\/turf1-1024x692.png\" alt=\"\" class=\"wp-image-1455\" srcset=\"https:\/\/theroamingworkshop.cloud\/b\/wp-content\/uploads\/2022\/09\/turf1-1024x692.png 1024w, https:\/\/theroamingworkshop.cloud\/b\/wp-content\/uploads\/2022\/09\/turf1-300x203.png 300w, https:\/\/theroamingworkshop.cloud\/b\/wp-content\/uploads\/2022\/09\/turf1-768x519.png 768w, https:\/\/theroamingworkshop.cloud\/b\/wp-content\/uploads\/2022\/09\/turf1-676x457.png 676w, https:\/\/theroamingworkshop.cloud\/b\/wp-content\/uploads\/2022\/09\/turf1.png 1030w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p>It's the map you saw at the beginning which you can see fullscreen:<\/p>\n\n\n\n<p><a href=\"https:\/\/theroamingworkshop.cloud\/leafMET\/leafMET+Turf.html\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/theroamingworkshop.cloud\/leafMET\/leafMET+Turf.html<\/a><\/p>\n\n\n\n<p>The code is also available as a new branch of leafMET in github:<\/p>\n\n\n\n<p><a href=\"https:\/\/github.com\/TheRoam\/leafMET\/tree\/leafMET+Turf\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/github.com\/TheRoam\/leafMET\/tree\/leafMET+Turf<\/a><\/p>\n\n\n\n<p>And that's all! Hope you find it useful!<\/p>\n\n\n\n<p>Any doubts or comments are awaited on \ud83d\udc26 Twitter!<\/p>\n\n\n\n<p>\ud83d\udc26 <code><a rel=\"noreferrer noopener\" href=\"https:\/\/twitter.com\/roamingworkshop\" target=\"_blank\">@RoamingWorkshop<\/a><\/code><\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you&#8217;ve used desktop GIS software, you will be surprised that many geospatial tools are also available to use in your web maps using a free and open-source javascript library. Today I show you TurfJS. Press the button \ud83c\udf24 in the map below and see one of many possibilities: inverse distance weighting or IDW. Content [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":1435,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[32],"tags":[],"class_list":["post-1457","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-map-en","post-preview"],"_links":{"self":[{"href":"https:\/\/theroamingworkshop.cloud\/b\/wp-json\/wp\/v2\/posts\/1457","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/theroamingworkshop.cloud\/b\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/theroamingworkshop.cloud\/b\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/theroamingworkshop.cloud\/b\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/theroamingworkshop.cloud\/b\/wp-json\/wp\/v2\/comments?post=1457"}],"version-history":[{"count":6,"href":"https:\/\/theroamingworkshop.cloud\/b\/wp-json\/wp\/v2\/posts\/1457\/revisions"}],"predecessor-version":[{"id":1576,"href":"https:\/\/theroamingworkshop.cloud\/b\/wp-json\/wp\/v2\/posts\/1457\/revisions\/1576"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/theroamingworkshop.cloud\/b\/wp-json\/wp\/v2\/media\/1435"}],"wp:attachment":[{"href":"https:\/\/theroamingworkshop.cloud\/b\/wp-json\/wp\/v2\/media?parent=1457"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/theroamingworkshop.cloud\/b\/wp-json\/wp\/v2\/categories?post=1457"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/theroamingworkshop.cloud\/b\/wp-json\/wp\/v2\/tags?post=1457"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}