Saltar a contenido

Turf.js

Análisis geoespacial avanzado para navegadores y Node.js

https://turfjs.org/

Ejemplo

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" />

    <script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet-hash/0.2.1/leaflet-hash.min.js" crossorigin="anonymous"></script>
    <script src="https://unpkg.com/@turf/turf@7/turf.min.js" crossorigin="anonymous"></script>

    <style>
        body {
            margin: 0;
            padding: 0;
        }
        #map {
            position: absolute;
            top: 0;
            bottom: 0;
            width: 100%;
        }
        #features {
            position: absolute;
            top: 0;
            right: 0;
            bottom: 0;
            width: 350px;
            overflow: auto;
            background: rgba(255, 255, 255, 0.8);
            z-index: 10000;
        }
    </style>
</head>
<body>
    <div id="features">
        <div>
            <select name="" id="objetivos">

            </select>
        </div>
        <div id="alumnos">

        </div>
        <div>
            <button id="resultados">Resultados</button>
        </div>
      </div>
    <div id='map'></div>

    <script>
        const map = L.map('map').setView([37.88437176085360, -4.779524803161621], 14);

        const objetivos = [
          {
            name: "Pedraforca",
            pos: "42.23993;1.70375",
            view: "9/41.7355/2.3703"
          },
          {
            name: "Zaragoza",
            pos: "41.6570;-0.8621",
            view: "8/41.098/-0.176"
          },
          {
            name: "Córdoba",
            pos: "37.8842;-4.7639",
            view: "7/39.880/-0.956"
          },
          {
            name: "Giza Necropolis",
            pos: "29.97684;31.13684",
            view: "9/30.1938/31.6187"
          },
          {
            name: "Great Wall of China",
            pos: "40.4316544;116.56464",
            view: "5/31.747/114.082"
          },
          {
            name: "Mt Everest",
            pos: "28.014503;86.90959",
            view: "6/31.812/92.340"
          },

        ]

        document.getElementById('objetivos').innerHTML = [`<option value="-----">Seleccionar objetivo</option>`, ...objetivos.map((item) => {
            return `<option value="${item.pos}@${item.view}">${item.name}</option>`;
        })].join("");

        const hash = new L.Hash(map);

        // Capas base
        const osmLayer = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
            attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap<\/a> contributors'
        }).addTo(map);

        const satelite = L.tileLayer('https://api.maptiler.com/tiles/satellite-v2/{z}/{x}/{y}.jpg?key=YOUR_MAPTILER_API_KEY',{
          tileSize: 512,
          zoomOffset: -1,
          minZoom: 1,
          attribution: "\u003ca href=\"https://www.maptiler.com/copyright/\" target=\"_blank\"\u003e\u0026copy; MapTiler\u003c/a\u003e \u003ca href=\"https://www.openstreetmap.org/copyright\" target=\"_blank\"\u003e\u0026copy; OpenStreetMap contributors\u003c/a\u003e",
          crossOrigin: true
        }).addTo(map);

        function getColor(d) {
          return d > 15  ? '#cf597e' :
           d > 10  ? '#e88471' :
           d > 5  ? '#eeb479' :
           d > 3   ? '#e9e29c' :
           d > 2   ? '#9ccb86' :
           d > 1   ? '#39b185' :
                      '#009392';
        }

        const gridLayer = L.geoJSON(null, {
          style: function(feature){
            return {
              fillColor: getColor(feature.properties.count),
              weight: 2,
              opacity: 1,
              color: 'white',
              dashArray: '3',
              fillOpacity: feature.properties.count ? 0.7 : 0
            };
          }
        }).addTo(map);

        document.getElementById('objetivos').addEventListener("change", () => {
          if (document.getElementById("objetivos").value !== "-----") {
            const view = document.getElementById("objetivos").value.split("@")[1].split("/");
            map.setView([view[1], view[2]], view[0]);
            gridLayer.clearLayers();
            respMarkers.forEach(item => {
              item.remove();
            });
            est.forEach((item) => {
              if (item.latlng) item.latlng = null;
            });
          }
        });

        L.control.layers({
          osm: osmLayer,
          sat: satelite
        }, {}, {position: 'topleft'}).addTo(map);

        const est = [
          {
            id: "1",
            nom: "Inna Arroyo Kochachi",
            icon: "abduction",
            score: 0
          },
          {
            id: "2",
            nom: "Maria Ayelen Calvet",
            icon: "alien",
            score: 0
          },
          {
            id: "3",
            nom: "Marina Casero Garcia",
            icon: "anon",
            score: 0
          },
          {
            id: "4",
            nom: "Sandra X. De Fex Sierra",
            icon: "avalanche1",
            score: 0
          },
          {
            id: "5",
            nom: "Alejandro García Menéndez",
            icon: "battlefield",
            score: 0
          },
          {
            id: "6",
            nom: "Carmen Garrido Antolino",
            icon: "blast",
            score: 0
          },
          {
            id: "7",
            nom: "Joan Molina Navas",
            icon: "bomb",
            score: 0
          },
          {
            id: "8",
            nom: "Genís Rulló Capdevila",
            icon: "caraccident",
            score: 0
          }/*,
          {
            id: "9",
            nom: "",
            icon: "cowabduction",
            score: 0
          },
          {
            id: "10",
            nom: "",
            icon: "crimescene",
            score: 0
          },
          {
            id: "11",
            nom: "",
            icon: "earthquake-3",
            score: 0
          },
          {
            id: "12",
            nom: "",
            icon: "fire",
            score: 0
          },
          {
            id: "13",
            nom: "",
            icon: "flood",
            score: 0
          },
          {
            id: "14",
            nom: "",
            icon: "love_date",
            score: 0
          },
          {
            id: "15",
            nom: "",
            icon: "peace",
            score: 0
          },
          {
            id: "16",
            nom: "",
            icon: "phantom",
            score: 0
          },
          {
            id: "17",
            nom: "",
            icon: "pirates",
            score: 0
          },
          {
            id: "18",
            nom: "",
            icon: "planecrash",
            score: 0
          },
          {
            id: "19",
            nom: "",
            icon: "radiation",
            score: 0
          }*/
        ];

        const iconBaseUrl = "http://betaserver.icgc.cat/mapicons/";

        function createTable() {
          document.getElementById("alumnos").innerHTML = est.map(item => {
            return `<div>
              <input type="radio" name="estudiante" value="${item.id}">
              <span><img src="${iconBaseUrl}${item.icon}.png" alt=""></span> ${item.nom}: 
              <span>${item.distance ? `${item.distance.toFixed(4).replace('.', ',')} Km` : item.score}</span>
            </div>`;
          }).join("");
        }

        createTable();

        map.on('click', function(evt){


            const objetivo = document.getElementById("objetivos").value.split("@")[0].split(";");

            const pt1 = turf.point([objetivo[1], objetivo[0]]);
            const pt2 = turf.point([evt.latlng.lng, evt.latlng.lat]);
            const options = {units: 'kilometers'};

            const distance = turf.distance(pt1, pt2, options);

            const activeEstId = document.querySelector('input[name="estudiante"]:checked').value;

            const activeEst = est.find((item) => {
              return item.id === activeEstId;
            });

            activeEst.distance = distance;
            activeEst.latlng = evt.latlng

            const marker = L.marker(activeEst.latlng, {icon: crearIcono(activeEst.icon)}).addTo(map);

            setTimeout(() => {
              marker.remove()
            }, 1000);

        });

        function crearIcono(id){
          return L.icon({
              iconUrl: `${iconBaseUrl}${id}.png`,
              iconSize: [37, 37],
          });
        }

        const respMarkers = [];

        function mostrarResultados() {
            est.sort((a,b) => {
              if (!a.distance) return 1;
              if (!b.distance) return -1;
              return a.distance - b.distance
            });
            createTable();

            const respuestas = {
              type: "FeatureCollection",
              features: []
            }

            est.forEach((item) => {
              if (item.latlng) {
                const mark = L.marker(item.latlng, {icon: crearIcono(item.icon)}).addTo(map);
                respuestas.features.push({
                  type: "Feature",
                  geometry: {
                    "type": "Point",
                    "coordinates": [item.latlng.lng, item.latlng.lat]
                  }
                });
                respMarkers.push(mark);
              }
            });

            const objetivo = document.getElementById("objetivos").value.split("@")[0].split(";");
            map.setView(objetivo, 15);
            const mark = L.marker(objetivo).addTo(map);
            respMarkers.push(mark);

            const bboxpol = turf.bboxPolygon(turf.bbox(respuestas))
            const respArea = turf.transformScale(bboxpol, 2);
            const bbox = turf.bbox(respArea);
            const hexgrid = turf.hexGrid(bbox, 5);

            turf.featureEach(hexgrid, function (currentFeature, featureIndex) {
              const ptsWithin = turf.pointsWithinPolygon(respuestas, currentFeature);
              currentFeature.properties.count = ptsWithin.features.length;
            });

            gridLayer.addData(hexgrid);
        }

        document.getElementById('resultados').addEventListener('click', mostrarResultados);

    </script>

</body>
</html>