如何使用 leafletjs 计算当前速度、平均速度和总距离?

How can I compute current speed, average speed and total distance with leafletjs?

我有一个使用 Leaflet.js 的跟踪地图,我想显示:

a) 当前速度,

b) 平均速度和

c) 总行驶距离。

我该怎么做? https://jsfiddle.net/chovy/rfvtwed5/

<!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>
    <script src="https://cdn.jsdelivr.net/npm/@turf/turf@6/turf.min.js"></script>
    <link
      rel="stylesheet"
      href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css"
      integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A=="
      crossorigin=""
    />
    <link
      rel="stylesheet"
      href="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.css"
    />
    <style>
      html {
        font-size: 62.5%;
      }
      body {
        margin: 0;
        padding: 0;
        font-size: 1.6rem;
        font-family: sans-serif;
        width: 95vw;
        height: 90vh;
        margin: 0 auto;
      }

      header {
        padding: 1rem 0;
      }

      #map {
        width: 100%;
        height: 100%;
      }

      .marker img {
        width: 3rem;
      }

      .marker span {
        display: block;
        background: #fff;
        width: 10rem;
        padding: 1rem;
        border-radius: 0.4rem;
        border: 1px solid black;
      }

      @media (max-width: 481px) {
        /* portrait e-readers (Nook/Kindle), smaller tablets @ 600 or @ 640 wide. */
        #status {
          display: block;
        }
      }
    </style>
  </head>

  <body>
    <header>
      <button id="start">start</button>
      <button id="stop">stop</button>
      <button id="marker">add marker</button>
      <button id="export">export</button>
      <span id="status"></span>
    </header>
    <div id="map"></div>

    <script
      src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"
      integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA=="
      crossorigin=""
    ></script>
    <script src="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.js"></script>
    <script>
      const exportButton = document.getElementById("export");
      let intv;

      const status = document.getElementById("status");

      var map = L.map("map", {
        center: [9.082, 8.6753],
        zoom: 8,
      });
      var osm = L.tileLayer(
        "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
        {
          attribution:
                                '&copy; <a href="https://skatespot.com">SkateSpot, Inc.</a>',
        }
      ).addTo(map);
      L.Control.geocoder().addTo(map);
      if (!navigator.geolocation) {
        console.log("Your browser doesn't support geolocation feature!");
      }

      const trackingPath = [];
      const polyline = L.polyline([], {
        color: "red",
        weight: 3,
        className: "path",
      }).addTo(map);

      function start() {
        navigator.geolocation.getCurrentPosition(getPosition);
        intv = setInterval(() => {
          navigator.geolocation.getCurrentPosition(getPosition);
        }, 1000);
      }

      function getPosition(position) {
        // console.log(position)
        const { latitude, longitude, accuracy } = position.coords;
        const pos = [latitude, longitude];
        //const pos = turf.randomPosition([49, 23, 43, 20])
        trackingPath.push(pos);
        polyline.addLatLng(pos);
        map.fitBounds(polyline.getBounds());
        console.log(
          "Your coordinate is: Lat: " +
            latitude +
            " Long: " +
            longitude +
            " Accuracy: " +
            accuracy
        );
      }

      exportButton.addEventListener("click", function () {
        console.log("trackingPath", trackingPath);
        save("data.txt", JSON.stringify(trackingPath));
      });

      function save(filename, data) {
        const blob = new Blob([data], { type: "text/csv" });
        if (window.navigator.msSaveOrOpenBlob) {
          window.navigator.msSaveBlob(blob, filename);
        } else {
          const elem = window.document.createElement("a");
          elem.href = window.URL.createObjectURL(blob);
          elem.download = filename;
          document.body.appendChild(elem);
          elem.click();
          document.body.removeChild(elem);
        }
      }

      function addMarker(e) {
        e.preventDefault();
        console.log("add marker");
        status.innerText = "adding marker...";
        navigator.geolocation.getCurrentPosition((position) => {
          const { latitude, longitude, accuracy } = position.coords;
          const pos = [latitude, longitude];

          var marker = L.marker(pos, {
            icon: new L.DivIcon({
              className: "marker",
              html: `<img src="/marker.svg" alt="" />
                                        <span contenteditable>click to edit</span>`,
            }),
          }).addTo(map);
          var Overlaymaps = {
            Marker: marker,
          };
          L.control.layers(Overlaymaps).addTo(map);

          status.innerText = `Marker added at ${pos.join(", ")}`;
        });
      }

      function startTracking(e) {
        status.innerText = "Started tracking...";
        e.preventDefault();
        start();
      }

      function stopTracking(e) {
        status.innerText = "Stopped tracking.";
        e.preventDefault();
        clearInterval(intv);
      }

      document.getElementById("start").addEventListener("click", startTracking);
      document.getElementById("stop").addEventListener("click", stopTracking);
      document.getElementById("marker").addEventListener("click", addMarker);
    </script>
  </body>
</html>

我只是在猜测,但我认为对于“当前速度”,我必须读取最后 2 个读数(每 1 秒一次)并计算距离和经过的时间以获得速度。至于平均速度,我只取第一个和最后一个数据点并做同样的事情。

总距离我必须将我假设的所有数据点相加,因为路径并不总是一条直线。

我只是不知道如何使用 gps 坐标来完成所有这些操作。

Leaflet 提供了一个有用的 latlngA.distanceTo(latlngB) method 将一对 GPS 坐标转换为实际距离:

Returns the distance (in meters) to the given LatLng calculated using the Spherical Law of Cosines.

有了这个,你可以做这样的事情:

let totalElapsedTime = 0;
let totalDistance = 0;
let previousTime;
let previousLatLng;

function getPosition(position) {
  const time = new Date();
  const latLng = L.latLng(
    position.latitude,
    position.longitude
  );

  // Skip first point (no computation is possible)
  if (previousTime && previousLatLng) {
    const elapsedTime = (time.getTime() - previousTime.getTime()) / 1000; // In seconds
    const distance = previousLatLng.distanceTo(latLng);

    const currentSpeed = distance / elapsedTime; // In meters / second

    totalElapsedTime += elapsedTime;
    totalDistance += distance;

    const averageSpeed = totalDistance / totalElapsedTime; // In meters / second
  }

  // Record time and position for next computation
  previousTime = time;
  previousLatLng = position;

  // Graphic stuff...
}

注意:对于平均速度,您还需要考虑非直线路径的总行驶距离,除非您想计算“飞行距离”而不考虑实际路径,因此可以在路径变为 0 的情况下减少回 0循环回到起点。