Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
88.89% |
8 / 9 |
CRAP | |
93.62% |
88 / 94 |
Calculator | |
0.00% |
0 / 1 |
|
88.89% |
8 / 9 |
25.16 | |
93.62% |
88 / 94 |
distanceBetweenCoordinates($coord1, $coord2) | |
100.00% |
1 / 1 |
1 | |
100.00% |
5 / 5 |
|||
getFirstCoordinate($track) | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 6 |
|||
distVincenty($lat1, $lon1, $lat2, $lon2) | |
100.00% |
1 / 1 |
6 | |
100.00% |
37 / 37 |
|||
trackDistance($track) | |
100.00% |
1 / 1 |
5 | |
100.00% |
15 / 15 |
|||
elevationChangeBetweenCoordinates($point1, $point2) | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
totalTrackElevation($track) | |
100.00% |
1 / 1 |
3 | |
100.00% |
9 / 9 |
|||
timePassedBetweenCoordinates($point1, $point2) | |
100.00% |
1 / 1 |
2 | |
100.00% |
4 / 4 |
|||
totalTrackTime($track) | |
100.00% |
1 / 1 |
3 | |
100.00% |
12 / 12 |
|||
averageTrackSpeed($track) | |
100.00% |
1 / 1 |
1 | |
100.00% |
4 / 4 |
<?php | |
/** | |
* | |
*/ | |
namespace aae\geo { | |
/** | |
* @author Axel Ancona Esselmann | |
* @package aae\geo | |
*/ | |
class Calculator { | |
/** | |
* Calculates geodetic distance between two Points specified by latitude/longitude using | |
* Vincenty inverse formula for ellipsoids | |
* | |
* @param [type] $coord1 [description] | |
* @param [type] $coord2 [description] | |
* @return [type] [description] | |
*/ | |
public static function distanceBetweenCoordinates($coord1, $coord2) { | |
$lat1 = $coord1->lat; | |
$lon1 = $coord1->lon; | |
$lat2 = $coord2->lat; | |
$lon2 = $coord2->lon; | |
return self::distVincenty($lat1, $lon1, $lat2, $lon2); | |
} | |
public static function getFirstCoordinate($track) { | |
foreach ($track as $element) { | |
if ($element instanceof \aae\math\geospatial\Point) { | |
return $element; | |
} else { | |
return Calculator::getFirstCoordinate($element); | |
} | |
} | |
} | |
/* Calculates geodetic distance between two points specified by latitude/longitude using | |
* Vincenty inverse formula for ellipsoids | |
* | |
* | |
* Vincenty Inverse Solution of Geodesics on the Ellipsoid | |
* Adapted from a version created by Chris Veness | |
* | |
* from: Vincenty inverse formula - T Vincenty, | |
* "Direct and Inverse Solutions of Geodesics on the Ellipsoid with | |
* application of nested equations", Survey Review, vol XXII no 176, 1975 | |
* http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf | |
* | |
* @param float lat1, lon1: first point in decimal degrees | |
* @param float lat2, lon2: second point in decimal degrees | |
* @returns float distance in km between points | |
*/ | |
public static function distVincenty($lat1, $lon1, $lat2, $lon2) { | |
// WGS-84 ellipsoid params | |
$a = 6378137; | |
$b = 6356752.314245; | |
$f = 1/298.257223563; | |
$L = deg2rad(($lon2-$lon1)); | |
$U1 = atan((1-$f) * tan(deg2rad($lat1))); | |
$U2 = atan((1-$f) * tan(deg2rad($lat2))); | |
$sinU1 = sin($U1); | |
$cosU1 = cos($U1); | |
$sinU2 = sin($U2); | |
$cosU2 = cos($U2); | |
$lambda = $L; | |
$iterLimit = 100; | |
do { | |
$sinLambda = sin($lambda); | |
$cosLambda = cos($lambda); | |
$sinSigma = sqrt(($cosU2*$sinLambda) * ($cosU2*$sinLambda) + | |
($cosU1*$sinU2-$sinU1*$cosU2*$cosLambda) * | |
($cosU1*$sinU2-$sinU1*$cosU2*$cosLambda)); | |
if ($sinSigma==0) return 0; // co-incident points | |
$cosSigma = $sinU1*$sinU2 + $cosU1*$cosU2*$cosLambda; | |
$sigma = atan2($sinSigma, $cosSigma); | |
$sinAlpha = $cosU1 * $cosU2 * $sinLambda / $sinSigma; | |
$cosSqAlpha = 1 - $sinAlpha*$sinAlpha; | |
$cos2SigmaM = $cosSigma - 2*$sinU1*$sinU2/$cosSqAlpha; | |
if ($cos2SigmaM == NAN) $cos2SigmaM = 0; // equatorial line: $cosSqAlpha=0 (§6) | |
$C = $f/16*$cosSqAlpha*(4+$f*(4-3*$cosSqAlpha)); | |
$lambdaP = $lambda; | |
$lambda = $L + (1-$C) * $f * $sinAlpha * | |
($sigma + $C*$sinSigma*($cos2SigmaM+$C*$cosSigma*(-1+2*$cos2SigmaM*$cos2SigmaM))); | |
} while (abs($lambda-$lambdaP) > 1e-12 && --$iterLimit>0); | |
if ($iterLimit==0) return NAN; // formula failed to converge | |
$uSq = $cosSqAlpha * ($a*$a - $b*$b) / ($b*$b); | |
$A = 1 + $uSq/16384*(4096+$uSq*(-768+$uSq*(320-175*$uSq))); | |
$B = $uSq/1024 * (256+$uSq*(-128+$uSq*(74-47*$uSq))); | |
$deltaSigma = $B*$sinSigma*($cos2SigmaM+$B/4*($cosSigma*(-1+2*$cos2SigmaM*$cos2SigmaM) - | |
$B/6*$cos2SigmaM*(-3+4*$sinSigma*$sinSigma)*(-3+4*$cos2SigmaM*$cos2SigmaM))); | |
$s = $b*$A*($sigma-$deltaSigma); | |
//$s = round($s, 3); // round to 1mm precision | |
return $s/1000.0; | |
// note: to return initial/final bearings in addition to distance, use something like: | |
/* | |
$fwdAz = atan2($cosU2*$sinLambda, $cosU1*$sinU2-$sinU1*$cosU2*$cosLambda); | |
$revAz = atan2($cosU1*$sinLambda, -$sinU1*$cosU2+$cosU1*$sinU2*$cosLambda); | |
return { $distance: $s, $initialBearing: deg2rad($fwdAz), $finalBearing: deg2rad($revAz) };*/ | |
} | |
/** | |
* [trackDistance description] | |
* @param [type] $track [description] | |
* @return [type] [description] | |
*/ | |
public static function trackDistance($track) { | |
$total = 0; | |
$paused = 0; | |
for ($i=1; $i < count($track->segmentStarts); $i++) { | |
$distance = self::distanceBetweenCoordinates($track[$track->segmentStarts[$i]], $track[$track->segmentStarts[$i]-1]); | |
if ($distance > 0) { | |
$paused += $distance; | |
} | |
} | |
for ($i=0; $i < count($track) - 1; $i++) { | |
$distance = self::distanceBetweenCoordinates($track[$i], $track[$i+1]); | |
if ($distance > 0) { | |
$total += $distance; | |
} | |
} | |
return $total - $paused; | |
} | |
/** | |
* [elevationChangeBetweenCoordinates description] | |
* @param [type] $point1 [description] | |
* @param [type] $point2 [description] | |
* @return [type] [description] | |
*/ | |
public static function elevationChangeBetweenCoordinates($point1, $point2) { | |
$result = $point2->ele - $point1->ele; | |
return $result; | |
} | |
/** | |
* [trackElevation description] | |
* @param [type] $track [description] | |
* @return [type] [description] | |
*/ | |
public static function totalTrackElevation($track) { | |
$result = array("elevGain" => 0, "elevLoss" => 0); | |
for ($i=0; $i < count($track) - 1; $i++) { | |
$elevation = self::elevationChangeBetweenCoordinates($track[$i], $track[$i+1]); | |
if ($elevation > 0) { | |
$result["elevGain"] += $elevation; | |
} else { | |
$result["elevLoss"] += $elevation; | |
} | |
} | |
return $result; | |
} | |
/** | |
* Returns the amount of time passed from point1 to point2. Throws an exception when | |
* point2 has an earlier time than point1. | |
* | |
* @param Point __parameterDescription__ | |
* @param Point __parameterDescription__ | |
*/ | |
public static function timePassedBetweenCoordinates($point1, $point2) { | |
$result = $point2->time - $point1->time; | |
if ($result < 0) { | |
throw new \Exception("point2 has an earlier time than point1", 1); | |
} | |
return $result; | |
} | |
/** | |
* __functionDescription__ | |
* @param __type__ __parameterDescription__ | |
*/ | |
public static function totalTrackTime($track) { | |
$timePaused = 0; | |
foreach ($track->segmentStarts as $segmentStart) { | |
if ($segmentStart > 0) { | |
$lastPoint = $track[$segmentStart]; | |
$firstPoint = $track[$segmentStart-1]; | |
$timePaused += self::timePassedBetweenCoordinates($firstPoint, $lastPoint); | |
} | |
} | |
$lastPoint = $track[count($track)-1]; | |
$firstPoint = $track[0]; | |
$total = self::timePassedBetweenCoordinates($firstPoint, $lastPoint) - $timePaused; | |
return $total; | |
} | |
/** | |
* Returns the average speed in km/h | |
* @param __type__ __parameterDescription__ | |
*/ | |
public static function averageTrackSpeed($track) { | |
$distance = self::trackDistance($track); | |
$time = self::totalTrackTime($track) / (60 * 60); | |
$result = $distance / ($time); | |
return $result; | |
} | |
} | |
} |