{"id":1385,"date":"2022-09-26T23:18:48","date_gmt":"2022-09-26T22:18:48","guid":{"rendered":"https:\/\/theroamingworkshop.cloud\/b\/?p=1385"},"modified":"2022-10-24T19:53:01","modified_gmt":"2022-10-24T18:53:01","slug":"turfjs-herramientas-geoespaciales-interactivas-para-tus-mapas-web","status":"publish","type":"post","link":"https:\/\/theroamingworkshop.cloud\/b\/1385\/turfjs-herramientas-geoespaciales-interactivas-para-tus-mapas-web\/","title":{"rendered":"TurfJS: herramientas geoespaciales interactivas para tus mapas web"},"content":{"rendered":"\n<p>Si has usado software GIS de escritorio, te sorprender\u00e1 saber que muchas de esas herramientas geoespaciales tambi\u00e9n est\u00e1n disponibles para tus mapas web mediante una librer\u00eda javascript gratuita y de c\u00f3digo abierto.<\/p>\n\n\n\n<p>Hoy te ense\u00f1o <strong>TurfJS<\/strong>. Pulsa el bot\u00f3n  \ud83c\udf24  del mapa incrustado y ver\u00e1s una de sus muchas posibilidades: la <strong>interpolaci\u00f3n por inverso de la distancia al cuadrado<\/strong> o <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;\">Contenido<\/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);\n}\n}\n\nif(window.location.href.includes(\"tag\")){\n\n}else{\nwindow.setTimeout(fillmenu,2000);\n}\n\n<\/script>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"instalacion\">Instalaci\u00f3n<\/h2>\n\n\n\n<p><strong>TurfJS<\/strong> cuenta con una pr\u00e1ctica web que nos explica todas las funciones disponibles, as\u00ed como la configuraci\u00f3n inicial:<\/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>Para usar <strong>Turf<\/strong> s\u00f3lo necesitamos enlazar la librer\u00eda a nuestro mapa usando el enlace CDN que dan en su web:<\/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=\"mapa-base\">Mapa base<\/h2>\n\n\n\n<p>Puedes crear un mapa base, por ejemplo, con Leaflet. Adem\u00e1s, <strong>TurfJS<\/strong> utiliza sintaxis <strong>JSON<\/strong>, por lo que es altamente compatible con las funciones <strong>geoJSON<\/strong> de Leaflet.<\/p>\n\n\n\n<p style=\"border-left:3px solid orange;padding-left:5px;font-size:14px;\"><i><b>TIP! <\/b>Leaflet ha sido recientemente actualizado a la versi\u00f3n 1.9.1. Este post todav\u00eda no est\u00e1 actualizado y utiliza la versi\u00f3n 1.7.1.<\/i><\/p>\n\n\n\n<p>Yo voy a reciclar <a rel=\"noreferrer noopener\" href=\"https:\/\/theroamingworkshop.cloud\/b\/?p=862\" target=\"_blank\">este ejemplo de uno de mis primeros posts<\/a> con algunos cambios:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>A\u00f1ado el enlace a la librer\u00eda Turf en el <code>&lt;head&gt;<\/code>.<\/li><li>Defino los marcadores en una matriz o array de datos, similar a como se obtendr\u00edan de una fuente de datos en formato JSON:<\/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>Los valores num\u00e9ricos <b>NO<\/b> ir\u00e1n \"entrecomillados\", o de lo contrario Turf los reconocer\u00e1 como String (en Leaflet no ocurre).<\/i><\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>A\u00f1ado los marcadores al mapa con un bucle, usando la funci\u00f3n <code>forEach()<\/code>, muy habitual para estos casos:<\/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\/\/AS\u00cd A\u00d1AD\u00cdAMOS UN MARCADOR \u00daNICO A 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>Quedando as\u00ed:<\/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>y siendo \u00e9ste su c\u00f3digo:<\/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\">Interpolaci\u00f3n IDW con TurfJS<\/h2>\n\n\n\n<p>En la forma anterior, los datos se pueden usar muy f\u00e1cilmente con <strong>TurfJS<\/strong>.<\/p>\n\n\n\n<p>Una de las herramientas m\u00e1s interesantes que facilita <strong>Turf<\/strong> es la <strong>interpolaci\u00f3n<\/strong> de los datos en una <strong>malla regular<\/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>Esto lo hace empleando uno de los m\u00e9todos m\u00e1s habituales: el <strong>inverso de la distancia al cuadrado<\/strong> (<strong>IDW<\/strong>) o inverse distance weighting para una potencia de 2.<\/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 de <a href=\"https:\/\/en.wikipedia.org\/wiki\/Inverse_distance_weighting\" target=\"_blank\" rel=\"noreferrer noopener\">Wikipedia<\/a><\/figcaption><\/figure>\n<\/div>\n\n\n<p>Seguimos el ejemplo que nos da la web de TurfJS:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>Convertir los datos a una capa de puntos de Turf, una 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>\/\/vector de puntos vac\u00edo\nvar points=&#91;];\n\n\/\/bucle para rellenar el vector de puntos con los datos\nmarkers.forEach(function(m){\n    points.push(turf.point(&#91;m.lon, m.lat],{name: m.name, dato: m.dato}));\n});\n\n\/\/convertir a featureCollection de Turf\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>F\u00edjate que Turf recibe las coordenadas en longitud-latitud, en lugar del habitual latitud-longitud!<\/i><\/p>\n\n\n\n<ol class=\"wp-block-list\" start=\"2\"><li>Definir las opciones de la interpolaci\u00f3n, entre las que podemos elegir las siguientes:<ul><li><strong>gridType<\/strong>: o tipo de malla<ul><li>points (puntos)<\/li><li>square (cuadrados)<\/li><li>hex (hex\u00e1gonos)<\/li><li>triangle (tri\u00e1ngulos)<\/li><\/ul><\/li><li><strong>property<\/strong>: propiedad (o campo) del objeto de puntos que contiene los valores a interpolar.<\/li><li><strong>units<\/strong>: unidades espaciales usadas en la interpolaci\u00f3n<ul><li>miles: millas<\/li><li>kilometers: kil\u00f3metros<\/li><li>radians: radianes<\/li><li>degrees: grados<\/li><\/ul><\/li><li><strong>weight<\/strong>: peso o potencia a la que se eleva la distancia, habitualmente 2, aunque valores m\u00e1s altos dar\u00e1n un resultado m\u00e1s suavizado.<\/li><\/ul><\/li><\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/opciones de interpolaci\u00f3n: malla rectangular de la variable 'dato' en kil\u00f3metros usando potencia de 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>Por \u00faltimo, generar la malla interpolada y a\u00f1adirla al mapa, siendo el valor num\u00e9rico el tama\u00f1o de las celdas de la malla interpolada (0.5 Km):<\/li><\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/crear malla de Turf\nvar malla=turf.interpolate(points, 0.5, options);\n\n\/\/a\u00f1adir a Leaflet\nL.geoJSON(malla).addTo(map);<\/code><\/pre>\n\n\n\n<p>Siendo este el resultado (nada concluyente):<\/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=\"formato-malla\">Dar formato a la malla y mostrar valores<\/h2>\n\n\n\n<p>Aunque no lo parezca, la interpolaci\u00f3n est\u00e1 hecha, aunque no se est\u00e1 mostrando correctamente.<\/p>\n\n\n\n<p>Voy a a\u00f1adir una escala de color que represente los datos, y tambi\u00e9n a mostrar los valores al pasar por encima de las celdas. Para ello, modificamos las <strong>propiedades del objeto geoJSON<\/strong> de Leaflet:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>style<\/strong>: funci\u00f3n para modificar el estilo de los objetos, evaluando su valor.<\/li><li><strong>onEachFeature<\/strong>: funci\u00f3n que se a\u00f1ade a cada objeto para interactuar con \u00e9l.<\/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>Quedando as\u00ed:<\/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>Y aqu\u00ed el c\u00f3digo completo:<\/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(map3);\n    marker.bindTooltip(\"&lt;b&gt;\"+m.name+\"&lt;\/b&gt;&lt;br&gt;\"+m.dato).openTooltip();\n});\n\n\/\/Datos para interpolaci\u00f3n de Turf\n\/\/vector de puntos vac\u00edo\nvar points=&#91;];\n\/\/bucle para rellenar el vector de puntos con los datos\nmarkers.forEach(function(m){\npoints.push(turf.point(&#91;m.lon, m.lat],{name: m.name, dato: m.dato}));\n});\n\/\/convertir a featureCollection de Turf\n\npoints=turf.featureCollection(points);\n\n\/\/opciones de interpolaci\u00f3n\nvar options = {gridType:\"square\",property:\"dato\",units:\"kilometers\",weight:2};\n\n\/\/crear malla de Turf\nvar malla=turf.interpolate(points, 0.5, options);\n\n\/\/a\u00f1adir a Leaflet\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=\"aplicaciones\">Aplicaciones<\/h2>\n\n\n\n<p>Como ejemplo aplicado a datos reales, he modificado la <a rel=\"noreferrer noopener\" href=\"https:\/\/theroamingworkshop.cloud\/b\/?p=1018\" target=\"_blank\">librer\u00eda leafMET<\/a> para incorporar la interpolaci\u00f3n y a\u00f1adido isobandas.<\/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>Es el mapa que has visto al inicio y que puedes abrir en pantalla completa:<\/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>Adem\u00e1s, el c\u00f3digo est\u00e1 disponible en github como variante de leafMET:<\/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>Y eso es todo! Espero que te sea \u00fatil!<\/p>\n\n\n\n<p>Cualquier duda o comentario te espero en \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>Si has usado software GIS de escritorio, te sorprender\u00e1 saber que muchas de esas herramientas geoespaciales tambi\u00e9n est\u00e1n disponibles para tus mapas web mediante una librer\u00eda javascript gratuita y de c\u00f3digo abierto. Hoy te ense\u00f1o TurfJS. Pulsa el bot\u00f3n \ud83c\udf24 del mapa incrustado y ver\u00e1s una de sus muchas posibilidades: la interpolaci\u00f3n por inverso de [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":1435,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[26],"tags":[],"class_list":["post-1385","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-map-es","post-preview"],"_links":{"self":[{"href":"https:\/\/theroamingworkshop.cloud\/b\/wp-json\/wp\/v2\/posts\/1385","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=1385"}],"version-history":[{"count":21,"href":"https:\/\/theroamingworkshop.cloud\/b\/wp-json\/wp\/v2\/posts\/1385\/revisions"}],"predecessor-version":[{"id":1577,"href":"https:\/\/theroamingworkshop.cloud\/b\/wp-json\/wp\/v2\/posts\/1385\/revisions\/1577"}],"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=1385"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/theroamingworkshop.cloud\/b\/wp-json\/wp\/v2\/categories?post=1385"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/theroamingworkshop.cloud\/b\/wp-json\/wp\/v2\/tags?post=1385"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}