]> git.street.me.uk Git - andy/gpx.git/commitdiff
Add length measurement to geographic classes
authorAndy Street <andy@street.me.uk>
Wed, 19 Dec 2018 19:48:45 +0000 (19:48 +0000)
committerAndy Street <andy@street.me.uk>
Wed, 19 Dec 2018 19:48:45 +0000 (19:48 +0000)
src/libgpx/geographic.php
src/libgpx/gpx.php
src/libgpx/libgpx.php
src/libgpx/point.php
src/libgpx/route.php
src/libgpx/track.php
src/libgpx/tracksegment.php

index 2d5e54915699f9859b0fd1025778db74e2dbdb76..7e692f1853753fb99e72aa286bdea5f4ef9c94d3 100644 (file)
@@ -39,4 +39,12 @@ interface Geographic
    *         null if not applicable.
    */
   public function getBounds();
+
+  /**
+   * Fetch the length of a geographic element.
+   *
+   * @return float The length in meters.
+   */
+  public function getLength();
+
 }
index 61e9e58db098aeb26e4636026bd387a88b1b2624..1ec64990222251ea59fbff0c4038730e478c205f 100644 (file)
@@ -368,4 +368,25 @@ class GPX implements Geographic
     return $result;
   }
 
+  /**
+   * Fetch the length of all features in this GPX file.
+   *
+   * @return float The length in meters.
+   */
+  public function getLength()
+  {
+    $length = 0;
+    $lists = [
+      $this->routes,
+      $this->tracks
+    ];
+    foreach ($lists as $list) {
+      if ($list === null) continue;
+      foreach ($list as $geo) {
+        $length += $geo->getLength();
+      }
+    }
+    return $length;
+  }
+
 }
index 4259e9bf07c91c734592d85f2de9a03549adb07b..449e5e3e15e5eee38688abf402d515171eef764a 100644 (file)
@@ -135,4 +135,46 @@ final class libGPX
     self::$initialized = true;
   }
 
+  /**
+   * Measure the distance between two points.
+   *
+   * Haversine formula, great circle distance.
+   *
+   * @param float $lat1 The latitude of the first point.
+   * @param float $lon1 The longitude of the first point.
+   * @param float $lat2 The latitude of the second point.
+   * @param float $lon2 The longitude of the second point.
+   * @return float The distance in meters.
+   */
+  public static function distance(
+    float $lat1,
+    float $lon1,
+    float $lat2,
+    float $lon2
+  ) {
+    $deg2rad = pi() / 180;
+    $r = 6371000; // Radius of the Earth in meters
+    $dlat = ($lat2 - $lat1) * $deg2rad;
+    $dlon = ($lon2 - $lon1) * $deg2rad;
+    $a =
+      sin($dlat / 2) * sin($dlat / 2) +
+      cos($lat1 * $deg2rad) * cos($lat2 * $deg2rad) *
+      sin($dlon / 2) * sin($dlon / 2);
+    $c = 2 * atan2(sqrt($a), sqrt(1-$a));
+    return $r * $c;
+  }
+
+  /**
+   * Convert meters in to miles.
+   *
+   * @see https://en.wikipedia.org/wiki/Mile
+   *
+   * @param float $meters A distance in meters.
+   * @return float A distance in miles.
+   */
+  public static function meters2miles(float $meters)
+  {
+    return $meters / 1609.344;
+  }
+
 }
index 4b20ffa04b6d52a39163d14e5ff11bfa2d73d5b9..cd1af148ff8bec1b6eb2e63451077f3343bd79d9 100644 (file)
@@ -497,4 +497,35 @@ class Point extends DataType implements Geographic
   {
     return new Bounds($this->lat, $this->lon, $this->lat, $this->lon);
   }
+
+  /**
+   * Fetch the length of a point.
+   *
+   * Note: Length is the distance between two points so calling this function
+   *       on a single point makes little sense. It is implemented to complete
+   *       the `Geographic` interface and will always return a value of 0.
+   *
+   * @return float The length in meters.
+   */
+  public function getLength()
+  {
+    return 0.0;
+  }
+
+  /**
+   * Find the distance to another point.
+   *
+   * @param Point $point The point to find the distance to.
+   * @return float The distance in meters.
+   */
+  public function distanceTo(Point $point)
+  {
+    return libgpx::distance(
+      $this->lat,
+      $this->lon,
+      $point->getLatitude(),
+      $point->getLongitude()
+    );
+  }
+
 }
index bdb721270fb859422b52fcb43d644fd1eea7838b..6cecabc311feb599e707b9697a9d45ceec0a5a4f 100644 (file)
@@ -107,4 +107,29 @@ class Route extends DataType implements Geographic
     return $result;
   }
 
+  /**
+   * Fetch the length of a route.
+   *
+   * @return float The length in meters.
+   */
+  public function getLength()
+  {
+    $length = 0;
+    $points = $this->getPoints(false);
+    if ($points !== null && !$points->isEmpty()) {
+      $last_point = null;
+      foreach ($points as $current_point) {
+        if ($last_point !== null)
+          $length += libgpx::distance(
+            $last_point->getLatitude(),
+            $last_point->getLongitude(),
+            $current_point->getLatitude(),
+            $current_point->getLongitude()
+          );
+        $last_point = $current_point;
+      }
+    }
+    return $length;
+  }
+
 }
index 266d32b5643c65701693ab8e8863463e6acb9be6..ea19a1d55b11041779a8e47c619f08ecb8f9dc24 100644 (file)
@@ -106,4 +106,21 @@ class Track extends DataType implements Geographic
     return $result;
   }
 
+  /**
+   * Fetch the length of a track.
+   *
+   * @return float The length in meters.
+   */
+  public function getLength()
+  {
+    $length = 0;
+    $segments = $this->getSegments(false);
+    if ($segments !== null) {
+      foreach ($segments as $segment) {
+        $length += $segment->getLength();
+      }
+    }
+    return $length;
+  }
+
 }
index ccb30789a807858987ee2c94d5b68ae553a4b7ab..c48b70d45be93cc127a79f49f424908224a3e05a 100644 (file)
@@ -90,4 +90,29 @@ class TrackSegment implements Geographic
     return $result;
   }
 
+  /**
+   * Fetch the length of a track segment.
+   *
+   * @return float The length in meters.
+   */
+  public function getLength()
+  {
+    $length = 0;
+    $points = $this->getPoints(false);
+    if ($points !== null && !$points->isEmpty()) {
+      $last_point = null;
+      foreach ($points as $current_point) {
+        if ($last_point !== null)
+          $length += libgpx::distance(
+            $last_point->getLatitude(),
+            $last_point->getLongitude(),
+            $current_point->getLatitude(),
+            $current_point->getLongitude()
+          );
+        $last_point = $current_point;
+      }
+    }
+    return $length;
+  }
+
 }