Etiquetas

Reac - Leaflet (medir distancia)

 Usar herramientas de medición en un mapa creado con React-Leaflet es extremadamente útil por varias razones. Estas herramientas permiten a los usuarios interactuar con el mapa de manera más dinámica y obtener información precisa sobre las distancias y áreas geográficas directamente en la interfaz del usuario. Aquí te explico para qué son útiles y cómo pueden mejorar la experiencia del usuario.

Utilidades de las Herramientas de Medición

  1. Medición de Distancias:

    • Propósito: Permite a los usuarios medir la distancia entre dos o más puntos en el mapa.
    • Aplicaciones: Esto es útil en aplicaciones como planificación de rutas, cálculo de distancias de viaje, y en contextos educativos donde los usuarios pueden querer aprender más sobre la geografía de un área.
  2. Medición de Áreas:

    • Propósito: Permite a los usuarios medir el área de una región definida en el mapa.
    • Aplicaciones: Útil para la gestión de terrenos, la agricultura, la planificación urbana y la evaluación de propiedades. Los usuarios pueden calcular el tamaño de una parcela de tierra o una región específica en un proyecto de desarrollo.
  3. Interacción Mejorada:

    • Proporciona a los usuarios una forma interactiva de explorar datos espaciales, haciendo el mapa más informativo y atractivo.
    • Permite a los usuarios realizar tareas personalizadas que se adapten a sus necesidades específicas, como medir el área de un parque para un evento o determinar la distancia entre dos puntos de interés.
  4. Apoyo a Decisiones:

    • Facilita la toma de decisiones basada en datos precisos directamente desde el mapa, como determinar la viabilidad de proyectos de construcción o analizar el impacto ambiental de un desarrollo planificado.
  5. Educación y Aprendizaje:

    • En entornos educativos, permite a estudiantes y profesores realizar mediciones y análisis geográficos que pueden ser vitales para cursos de geografía, ciencias ambientales y estudios urbanos.

Primero, asegúrate de tener instalados React-Leaflet y leaflet-ruler en tu proyecto:

npm install react-leaflet leaflet leaflet-ruler

Codigo de App.js



import { MapContainer, TileLayer } from "react-leaflet";
import "leaflet/dist/leaflet.css";
import "./styles.css";
import LeafletRuler from "./LeafletRuler";

export default function App() {
  const position = [51.505, -0.09];

  return (
    <MapContainer center={position} zoom={13} style={{ height: "100vh" }}>
      <TileLayer
        attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
        url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
      />
      <LeafletRuler />
    </MapContainer>
  );
}


Codigo LeafletRuler.js


import { useEffect } from "react";
import { useMap } from "react-leaflet";
import L from "leaflet";
import "./leaflet-ruler.css";
import "./leaflet-ruler";

export default function LeafletRuler() {
  const map = useMap();

  useEffect(() => {
    if (!map) return;
    L.control.ruler().addTo(map);
  }, [map]);

  return null;
}

Codigo de lealfet-ruler.js

import L from "leaflet";

(function (factory, window) {
  /* eslint-disable */
  if (typeof define === "function" && define.amd) {
    define(["leaflet"], factory);
  } else if (typeof exports === "object") {
    module.exports = factory(require("leaflet"));
  }
  if (typeof window !== "undefined" && window.L) {
    window.L.Ruler = factory(L);
  }
})(function (L) {
  L.Control.Ruler = L.Control.extend({
    options: {
      position: "topright",
      circleMarker: {
        color: "red",
        radius: 2
      },
      lineStyle: {
        color: "red",
        dashArray: "1,6"
      },
      lengthUnit: {
        display: "km",
        decimal: 2,
        factor: null,
        label: "Distance:"
      },
      angleUnit: {
        display: "&deg;",
        decimal: 2,
        factor: null,
        label: "Bearing:"
      }
    },
    onAdd: function (map) {
      this._map = map;
      this._container = L.DomUtil.create("div", "leaflet-bar");
      this._container.classList.add("leaflet-ruler");
      L.DomEvent.disableClickPropagation(this._container);
      L.DomEvent.on(this._container, "click", this._toggleMeasure, this);
      this._choice = false;
      this._defaultCursor = this._map._container.style.cursor;
      this._allLayers = L.layerGroup();
      return this._container;
    },
    onRemove: function () {
      L.DomEvent.off(this._container, "click", this._toggleMeasure, this);
    },
    _toggleMeasure: function () {
      this._choice = !this._choice;
      this._clickedLatLong = null;
      this._clickedPoints = [];
      this._totalLength = 0;
      if (this._choice) {
        this._map.doubleClickZoom.disable();
        L.DomEvent.on(this._map._container, "keydown", this._escape, this);
        L.DomEvent.on(this._map._container, "dblclick", this._closePath, this);
        this._container.classList.add("leaflet-ruler-clicked");
        this._clickCount = 0;
        this._tempLine = L.featureGroup().addTo(this._allLayers);
        this._tempPoint = L.featureGroup().addTo(this._allLayers);
        this._pointLayer = L.featureGroup().addTo(this._allLayers);
        this._polylineLayer = L.featureGroup().addTo(this._allLayers);
        this._allLayers.addTo(this._map);
        this._map._container.style.cursor = "crosshair";
        this._map.on("click", this._clicked, this);
        this._map.on("mousemove", this._moving, this);
      } else {
        this._map.doubleClickZoom.enable();
        L.DomEvent.off(this._map._container, "keydown", this._escape, this);
        L.DomEvent.off(this._map._container, "dblclick", this._closePath, this);
        this._container.classList.remove("leaflet-ruler-clicked");
        this._map.removeLayer(this._allLayers);
        this._allLayers = L.layerGroup();
        this._map._container.style.cursor = this._defaultCursor;
        this._map.off("click", this._clicked, this);
        this._map.off("mousemove", this._moving, this);
      }
    },
    _clicked: function (e) {
      this._clickedLatLong = e.latlng;
      this._clickedPoints.push(this._clickedLatLong);
      L.circleMarker(this._clickedLatLong, this.options.circleMarker).addTo(
        this._pointLayer
      );
      if (
        this._clickCount > 0 &&
        !e.latlng.equals(this._clickedPoints[this._clickedPoints.length - 2])
      ) {
        if (this._movingLatLong) {
          L.polyline(
            [this._clickedPoints[this._clickCount - 1], this._movingLatLong],
            this.options.lineStyle
          ).addTo(this._polylineLayer);
        }
        var text;
        this._totalLength += this._result.Distance;
        if (this._clickCount > 1) {
          text =
            "<b>" +
            this.options.angleUnit.label +
            "</b>&nbsp;" +
            this._result.Bearing.toFixed(this.options.angleUnit.decimal) +
            "&nbsp;" +
            this.options.angleUnit.display +
            "<br><b>" +
            this.options.lengthUnit.label +
            "</b>&nbsp;" +
            this._totalLength.toFixed(this.options.lengthUnit.decimal) +
            "&nbsp;" +
            this.options.lengthUnit.display;
        } else {
          text =
            "<b>" +
            this.options.angleUnit.label +
            "</b>&nbsp;" +
            this._result.Bearing.toFixed(this.options.angleUnit.decimal) +
            "&nbsp;" +
            this.options.angleUnit.display +
            "<br><b>" +
            this.options.lengthUnit.label +
            "</b>&nbsp;" +
            this._result.Distance.toFixed(this.options.lengthUnit.decimal) +
            "&nbsp;" +
            this.options.lengthUnit.display;
        }
        L.circleMarker(this._clickedLatLong, this.options.circleMarker)
          .bindTooltip(text, { permanent: true, className: "result-tooltip" })
          .addTo(this._pointLayer)
          .openTooltip();
      }
      this._clickCount++;
    },
    _moving: function (e) {
      if (this._clickedLatLong) {
        L.DomEvent.off(this._container, "click", this._toggleMeasure, this);
        this._movingLatLong = e.latlng;
        if (this._tempLine) {
          this._map.removeLayer(this._tempLine);
          this._map.removeLayer(this._tempPoint);
        }
        var text;
        this._addedLength = 0;
        this._tempLine = L.featureGroup();
        this._tempPoint = L.featureGroup();
        this._tempLine.addTo(this._map);
        this._tempPoint.addTo(this._map);
        this._calculateBearingAndDistance();
        this._addedLength = this._result.Distance + this._totalLength;
        L.polyline(
          [this._clickedLatLong, this._movingLatLong],
          this.options.lineStyle
        ).addTo(this._tempLine);
        if (this._clickCount > 1) {
          text =
            "<b>" +
            this.options.angleUnit.label +
            "</b>&nbsp;" +
            this._result.Bearing.toFixed(this.options.angleUnit.decimal) +
            "&nbsp;" +
            this.options.angleUnit.display +
            "<br><b>" +
            this.options.lengthUnit.label +
            "</b>&nbsp;" +
            this._addedLength.toFixed(this.options.lengthUnit.decimal) +
            "&nbsp;" +
            this.options.lengthUnit.display +
            '<br><div class="plus-length">(+' +
            this._result.Distance.toFixed(this.options.lengthUnit.decimal) +
            ")</div>";
        } else {
          text =
            "<b>" +
            this.options.angleUnit.label +
            "</b>&nbsp;" +
            this._result.Bearing.toFixed(this.options.angleUnit.decimal) +
            "&nbsp;" +
            this.options.angleUnit.display +
            "<br><b>" +
            this.options.lengthUnit.label +
            "</b>&nbsp;" +
            this._result.Distance.toFixed(this.options.lengthUnit.decimal) +
            "&nbsp;" +
            this.options.lengthUnit.display;
        }
        L.circleMarker(this._movingLatLong, this.options.circleMarker)
          .bindTooltip(text, {
            sticky: true,
            offset: L.point(0, -40),
            className: "moving-tooltip"
          })
          .addTo(this._tempPoint)
          .openTooltip();
      }
    },
    _escape: function (e) {
      if (e.keyCode === 27) {
        if (this._clickCount > 0) {
          this._closePath();
        } else {
          this._choice = true;
          this._toggleMeasure();
        }
      }
    },
    _calculateBearingAndDistance: function () {
      var f1 = this._clickedLatLong.lat,
        l1 = this._clickedLatLong.lng,
        f2 = this._movingLatLong.lat,
        l2 = this._movingLatLong.lng;
      var toRadian = Math.PI / 180;
      // haversine formula
      // bearing
      var y = Math.sin((l2 - l1) * toRadian) * Math.cos(f2 * toRadian);
      var x =
        Math.cos(f1 * toRadian) * Math.sin(f2 * toRadian) -
        Math.sin(f1 * toRadian) *
          Math.cos(f2 * toRadian) *
          Math.cos((l2 - l1) * toRadian);
      var brng =
        Math.atan2(y, x) *
        ((this.options.angleUnit.factor
          ? this.options.angleUnit.factor / 2
          : 180) /
          Math.PI);
      brng +=
        brng < 0
          ? this.options.angleUnit.factor
            ? this.options.angleUnit.factor
            : 360
          : 0;
      // distance
      var R = this.options.lengthUnit.factor
        ? 6371 * this.options.lengthUnit.factor
        : 6371; // kilometres
      var deltaF = (f2 - f1) * toRadian;
      var deltaL = (l2 - l1) * toRadian;
      var a =
        Math.sin(deltaF / 2) * Math.sin(deltaF / 2) +
        Math.cos(f1 * toRadian) *
          Math.cos(f2 * toRadian) *
          Math.sin(deltaL / 2) *
          Math.sin(deltaL / 2);
      var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
      var distance = R * c;
      this._result = {
        Bearing: brng,
        Distance: distance
      };
    },
    _closePath: function () {
      this._map.removeLayer(this._tempLine);
      this._map.removeLayer(this._tempPoint);
      if (this._clickCount <= 1) this._map.removeLayer(this._pointLayer);
      this._choice = false;
      L.DomEvent.on(this._container, "click", this._toggleMeasure, this);
      this._toggleMeasure();
    }
  });
  L.control.ruler = function (options) {
    return new L.Control.Ruler(options);
  };
}, window);
    


Codigo de styles.css

.leaflet-ruler {
    height: 35px;
    width: 35px;
    background-image: url("./dist/icon.png"); /* <div>Icons made by <a href="http://www.freepik.com" title="Freepik">Freepik</a> from <a href="http://www.flaticon.com" title="Flaticon">www.flaticon.com</a> is licensed by <a href="http://creativecommons.org/licenses/by/3.0/" title="Creative Commons BY 3.0" target="_blank">CC 3.0 BY</a></div> */
    background-repeat: no-repeat;
    background-position: center;
  }
  .leaflet-ruler:hover {
    background-image: url("./dist/icon-colored.png"); /* <div>Icons made by <a href="http://www.freepik.com" title="Freepik">Freepik</a> from <a href="http://www.flaticon.com" title="Flaticon">www.flaticon.com</a> is licensed by <a href="http://creativecommons.org/licenses/by/3.0/" title="Creative Commons BY 3.0" target="_blank">CC 3.0 BY</a></div> */
  }
  .leaflet-ruler-clicked {
    height: 35px;
    width: 35px;
    background-repeat: no-repeat;
    background-position: center;
    background-image: url("./dist/icon-colored.png");
    border-color: chartreuse !important;
  }
  .leaflet-bar {
    background-color: #ffffff;
  }
  .leaflet-control {
    cursor: pointer;
  }
  .result-tooltip {
    background-color: white;
    border-width: medium;
    border-color: #de0000;
    font-size: smaller;
  }
  .moving-tooltip {
    background-color: rgba(255, 255, 255, 0.7);
    background-clip: padding-box;
    opacity: 0.5;
    border: dotted;
    border-color: red;
    font-size: smaller;
  }
  .plus-length {
    padding-left: 45px;
  }
 


Codigo de leaflet - ruler.css

.leaflet-ruler {
    height: 35px;
    width: 35px;
    background-image: url("./dist/icon.png"); /* <div>Icons made by <a href="http://www.freepik.com" title="Freepik">Freepik</a> from <a href="http://www.flaticon.com" title="Flaticon">www.flaticon.com</a> is licensed by <a href="http://creativecommons.org/licenses/by/3.0/" title="Creative Commons BY 3.0" target="_blank">CC 3.0 BY</a></div> */
    background-repeat: no-repeat;
    background-position: center;
  }
  .leaflet-ruler:hover {
    background-image: url("./dist/icon-colored.png"); /* <div>Icons made by <a href="http://www.freepik.com" title="Freepik">Freepik</a> from <a href="http://www.flaticon.com" title="Flaticon">www.flaticon.com</a> is licensed by <a href="http://creativecommons.org/licenses/by/3.0/" title="Creative Commons BY 3.0" target="_blank">CC 3.0 BY</a></div> */
  }
  .leaflet-ruler-clicked {
    height: 35px;
    width: 35px;
    background-repeat: no-repeat;
    background-position: center;
    background-image: url("./dist/icon-colored.png");
    border-color: chartreuse !important;
  }
  .leaflet-bar {
    background-color: #ffffff;
  }
  .leaflet-control {
    cursor: pointer;
  }
  .result-tooltip {
    background-color: white;
    border-width: medium;
    border-color: #de0000;
    font-size: smaller;
  }
  .moving-tooltip {
    background-color: rgba(255, 255, 255, 0.7);
    background-clip: padding-box;
    opacity: 0.5;
    border: dotted;
    border-color: red;
    font-size: smaller;
  }
  .plus-length {
    padding-left: 45px;
  }
 


Crea una carpeta con el nombre dist y coloca los iconos de tu boton que va presentarse en el mapa 






El mapa tendrea el siguiente aspecto