/** * @param Coordinate $point * @param Line $line * * @return float */ public function getPerpendicularDistance(Coordinate $point, Line $line) { $ellipsoid = $point->getEllipsoid(); $ellipsoidRadius = $ellipsoid->getArithmeticMeanRadius(); $firstLinePointLat = $this->deg2radLatitude($line->getPoint1()->getLat()); $firstLinePointLng = $this->deg2radLongitude($line->getPoint1()->getLng()); $firstLinePointX = $ellipsoidRadius * cos($firstLinePointLng) * sin($firstLinePointLat); $firstLinePointY = $ellipsoidRadius * sin($firstLinePointLng) * sin($firstLinePointLat); $firstLinePointZ = $ellipsoidRadius * cos($firstLinePointLat); $secondLinePointLat = $this->deg2radLatitude($line->getPoint2()->getLat()); $secondLinePointLng = $this->deg2radLongitude($line->getPoint2()->getLng()); $secondLinePointX = $ellipsoidRadius * cos($secondLinePointLng) * sin($secondLinePointLat); $secondLinePointY = $ellipsoidRadius * sin($secondLinePointLng) * sin($secondLinePointLat); $secondLinePointZ = $ellipsoidRadius * cos($secondLinePointLat); $pointLat = $this->deg2radLatitude($point->getLat()); $pointLng = $this->deg2radLongitude($point->getLng()); $pointX = $ellipsoidRadius * cos($pointLng) * sin($pointLat); $pointY = $ellipsoidRadius * sin($pointLng) * sin($pointLat); $pointZ = $ellipsoidRadius * cos($pointLat); $normalizedX = $firstLinePointY * $secondLinePointZ - $firstLinePointZ * $secondLinePointY; $normalizedY = $firstLinePointZ * $secondLinePointX - $firstLinePointX * $secondLinePointZ; $normalizedZ = $firstLinePointX * $secondLinePointY - $firstLinePointY * $secondLinePointX; $length = sqrt($normalizedX * $normalizedX + $normalizedY * $normalizedY + $normalizedZ * $normalizedZ); $normalizedX /= $length; $normalizedY /= $length; $normalizedZ /= $length; $thetaPoint = $normalizedX * $pointX + $normalizedY * $pointY + $normalizedZ * $pointZ; $length = sqrt($pointX * $pointX + $pointY * $pointY + $pointZ * $pointZ); $thetaPoint /= $length; $distance = abs(M_PI / 2 - acos($thetaPoint)); return $distance * $ellipsoidRadius; }
/** * Calculates a destination point for the given point, bearing angle, * and distance. * * @param \Location\Coordinate $point * @param float $bearing the bearing angle between 0 and 360 degrees * @param float $distance the distance to the destination point in meters * * @return Coordinate */ public function calculateDestination(Coordinate $point, $bearing, $distance) { $D = $distance / static::EARTH_RADIUS; $B = deg2rad($bearing); $φ = deg2rad($point->getLat()); $λ = deg2rad($point->getLng()); $Φ = asin(sin($φ) * cos($D) + cos($φ) * sin($D) * cos($B)); $Λ = $λ + atan2(sin($B) * sin($D) * cos($φ), cos($D) - sin($φ) * sin($φ)); return new Coordinate(rad2deg($Φ), rad2deg($Λ)); }
/** * @param Coordinate $point1 * @param Coordinate $point2 * * @throws NotMatchingEllipsoidException * @throws NotConvergingException * * @return float */ public function getDistance(Coordinate $point1, Coordinate $point2) { if ($point1->getEllipsoid() != $point2->getEllipsoid()) { throw new NotMatchingEllipsoidException("The ellipsoids for both coordinates must match"); } $lat1 = deg2rad($point1->getLat()); $lat2 = deg2rad($point2->getLat()); $lng1 = deg2rad($point1->getLng()); $lng2 = deg2rad($point2->getLng()); $a = $point1->getEllipsoid()->getA(); $b = $point1->getEllipsoid()->getB(); $f = 1 / $point1->getEllipsoid()->getF(); $L = $lng2 - $lng1; $U1 = atan((1 - $f) * tan($lat1)); $U2 = atan((1 - $f) * tan($lat2)); $iterationLimit = 100; $lambda = $L; $sinU1 = sin($U1); $sinU2 = sin($U2); $cosU1 = cos($U1); $cosU2 = cos($U2); 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.0; } $cosSigma = $sinU1 * $sinU2 + $cosU1 * $cosU2 * $cosLambda; $sigma = atan2($sinSigma, $cosSigma); $sinAlpha = $cosU1 * $cosU2 * $sinLambda / $sinSigma; $cosSqAlpha = 1 - $sinAlpha * $sinAlpha; if ($cosSqAlpha == 0) { $cos2SigmaM = 0; } else { $cos2SigmaM = $cosSigma - 2 * $sinU1 * $sinU2 / $cosSqAlpha; } $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) > 1.0E-12 && --$iterationLimit > 0); if ($iterationLimit == 0) { throw new NotConvergingException(); } $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); return round($s, 3); }
public function testIfFromStringWithDecimalMinutesWorksAsExpected() { $expected = new Coordinate(52.20575, 13.576116667); $expectedLat = $expected->getLat(); $expectedLng = $expected->getLng(); $this->assertEquals($expectedLat, CoordinateFactory::fromString("52 12.345, 13 34.567")->getLat(), '', 0.0001); $this->assertEquals($expectedLng, CoordinateFactory::fromString("52 12.345, 13 34.567")->getLng(), '', 0.0001); $this->assertEquals(-$expectedLat, CoordinateFactory::fromString("-52 12.345, 13 34.567")->getLat(), '', 0.0001); $this->assertEquals($expectedLng, CoordinateFactory::fromString("-52 12.345, 13 34.567")->getLng(), '', 0.0001); $this->assertEquals(-$expectedLat, CoordinateFactory::fromString("-52 12.345, -13 34.567")->getLat(), '', 0.0001); $this->assertEquals(-$expectedLng, CoordinateFactory::fromString("-52 12.345, -13 34.567")->getLng(), '', 0.0001); $this->assertEquals($expectedLat, CoordinateFactory::fromString("52 12.345, -13 34.567")->getLat(), '', 0.0001); $this->assertEquals(-$expectedLng, CoordinateFactory::fromString("52 12.345, -13 34.567")->getLng(), '', 0.0001); $this->assertEquals($expectedLat, CoordinateFactory::fromString("52° 12.345, 13° 34.567")->getLat(), '', 0.0001); $this->assertEquals($expectedLng, CoordinateFactory::fromString("52° 12.345, 13° 34.567")->getLng(), '', 0.0001); $this->assertEquals(-$expectedLat, CoordinateFactory::fromString("-52° 12.345, 13° 34.567")->getLat(), '', 0.0001); $this->assertEquals($expectedLng, CoordinateFactory::fromString("-52° 12.345, 13° 34.567")->getLng(), '', 0.0001); $this->assertEquals(-$expectedLat, CoordinateFactory::fromString("-52° 12.345, -13° 34.567")->getLat(), '', 0.0001); $this->assertEquals(-$expectedLng, CoordinateFactory::fromString("-52° 12.345, -13° 34.567")->getLng(), '', 0.0001); $this->assertEquals($expectedLat, CoordinateFactory::fromString("52° 12.345, -13° 34.567")->getLat(), '', 0.0001); $this->assertEquals(-$expectedLng, CoordinateFactory::fromString("52° 12.345, -13° 34.567")->getLng(), '', 0.0001); $this->assertEquals($expectedLat, CoordinateFactory::fromString("N52 12.345, E13 34.567")->getLat(), '', 0.0001); $this->assertEquals($expectedLng, CoordinateFactory::fromString("N52 12.345, E13 34.567")->getLng(), '', 0.0001); $this->assertEquals($expectedLat, CoordinateFactory::fromString("N 52 12.345, E 13 34.567")->getLat(), '', 0.0001); $this->assertEquals($expectedLng, CoordinateFactory::fromString("N 52 12.345, E 13 34.567")->getLng(), '', 0.0001); $this->assertEquals($expectedLat, CoordinateFactory::fromString("52 12.345N, E13 34.567E")->getLat(), '', 0.0001); $this->assertEquals($expectedLng, CoordinateFactory::fromString("52 12.345N, E13 34.567E")->getLng(), '', 0.0001); $this->assertEquals($expectedLat, CoordinateFactory::fromString("52 12.345 N, E13 34.567 E")->getLat(), '', 0.0001); $this->assertEquals($expectedLng, CoordinateFactory::fromString("52 12.345 N, E13 34.567 E")->getLng(), '', 0.0001); $this->assertEquals($expectedLat, CoordinateFactory::fromString("N52° 12.345, E13° 34.567")->getLat(), '', 0.0001); $this->assertEquals($expectedLng, CoordinateFactory::fromString("N52° 12.345, E13° 34.567")->getLng(), '', 0.0001); $this->assertEquals($expectedLat, CoordinateFactory::fromString("N 52° 12.345, E 13° 34.567")->getLat(), '', 0.0001); $this->assertEquals($expectedLng, CoordinateFactory::fromString("N 52° 12.345, E 13° 34.567")->getLng(), '', 0.0001); $this->assertEquals($expectedLat, CoordinateFactory::fromString("52° 12.345N, E13° 34.567E")->getLat(), '', 0.0001); $this->assertEquals($expectedLng, CoordinateFactory::fromString("52° 12.345N, E13° 34.567E")->getLng(), '', 0.0001); $this->assertEquals($expectedLat, CoordinateFactory::fromString("52° 12.345 N, E13° 34.567 E")->getLat(), '', 0.0001); $this->assertEquals($expectedLng, CoordinateFactory::fromString("52° 12.345 N, E13° 34.567 E")->getLng(), '', 0.0001); $this->assertEquals($expectedLat, CoordinateFactory::fromString("N52° 12.345', E13° 34.567'")->getLat(), '', 0.0001); $this->assertEquals($expectedLng, CoordinateFactory::fromString("N52° 12.345', E13° 34.567'")->getLng(), '', 0.0001); $this->assertEquals($expectedLat, CoordinateFactory::fromString("N 52° 12.345', E 13° 34.567'")->getLat(), '', 0.0001); $this->assertEquals($expectedLng, CoordinateFactory::fromString("N 52° 12.345', E 13° 34.567'")->getLng(), '', 0.0001); $this->assertEquals($expectedLat, CoordinateFactory::fromString("52° 12.345' N, E13° 34.567' E")->getLat(), '', 0.0001); $this->assertEquals($expectedLng, CoordinateFactory::fromString("52° 12.345' N, E13° 34.567' E")->getLng(), '', 0.0001); $this->assertEquals($expectedLat, CoordinateFactory::fromString("N52° 12.345′, E13° 34.567′")->getLat(), '', 0.0001); $this->assertEquals($expectedLng, CoordinateFactory::fromString("N52° 12.345′, E13° 34.567′")->getLng(), '', 0.0001); $this->assertEquals($expectedLat, CoordinateFactory::fromString("N 52° 12.345′, E 13° 34.567′")->getLat(), '', 0.0001); $this->assertEquals($expectedLng, CoordinateFactory::fromString("N 52° 12.345′, E 13° 34.567′")->getLng(), '', 0.0001); $this->assertEquals($expectedLat, CoordinateFactory::fromString("52° 12.345′ N, E13° 34.567′ E")->getLat(), '', 0.0001); $this->assertEquals($expectedLng, CoordinateFactory::fromString("52° 12.345′ N, E13° 34.567′ E")->getLng(), '', 0.0001); $this->assertEquals(new Coordinate(52.2333, 20.9756), CoordinateFactory::fromString("52° 13.998′ 020° 58.536′")); }
/** * @param Coordinate $point1 * @param Coordinate $point2 * * @throws NotMatchingEllipsoidException * * @return float */ public function getDistance(Coordinate $point1, Coordinate $point2) { if ($point1->getEllipsoid() != $point2->getEllipsoid()) { throw new NotMatchingEllipsoidException("The ellipsoids for both coordinates must match"); } $lat1 = deg2rad($point1->getLat()); $lat2 = deg2rad($point2->getLat()); $lng1 = deg2rad($point1->getLng()); $lng2 = deg2rad($point2->getLng()); $dLat = $lat2 - $lat1; $dLng = $lng2 - $lng1; $radius = $point1->getEllipsoid()->getArithmeticMeanRadius(); $s = 2 * $radius * asin(sqrt(pow(sin($dLat / 2), 2) + cos($lat1) * cos($lat2) * pow(sin($dLng / 2), 2))); return round($s, 3); }
/** * Determine if given point is contained inside the polygon. Uses the PNPOLY * algorithm by W. Randolph Franklin. Therfore some edge cases may not give the * expected results, e. g. if the point resides on the polygon boundary. * * @see http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html * * For special cases this calculation leads to wrong results: * * - if the polygons spans over the longitude boundaries at 180/-180 degrees * * @param Coordinate $point * * @return boolean */ public function contains(Coordinate $point) { $numberOfPoints = $this->getNumberOfPoints(); $polygonLats = $this->getLats(); $polygonLngs = $this->getLngs(); $polygonContainsPoint = false; for ($node = 0, $altNode = $numberOfPoints - 1; $node < $numberOfPoints; $altNode = $node++) { if ($polygonLngs[$node] > $point->getLng() != $polygonLngs[$altNode] > $point->getLng() && $point->getLat() < ($polygonLats[$altNode] - $polygonLats[$node]) * ($point->getLng() - $polygonLngs[$node]) / ($polygonLngs[$altNode] - $polygonLngs[$node]) + $polygonLats[$node]) { $polygonContainsPoint = !$polygonContainsPoint; } } return $polygonContainsPoint; }
/** * @param Coordinate $coordinate * * @return string */ public function format(Coordinate $coordinate) { return json_encode(array('type' => 'Point', 'coordinates' => array($coordinate->getLng(), $coordinate->getLat()))); }
/** * @return float */ public function getSouth() { return $this->southEast->getLat(); }
private function inverseVincenty(Coordinate $point1, Coordinate $point2) { $φ1 = deg2rad($point1->getLat()); $φ2 = deg2rad($point2->getLat()); $λ1 = deg2rad($point1->getLng()); $λ2 = deg2rad($point2->getLng()); $a = $point1->getEllipsoid()->getA(); $b = $point1->getEllipsoid()->getB(); $f = 1 / $point1->getEllipsoid()->getF(); $L = $λ2 - $λ1; $tanU1 = (1 - $f) * tan($φ1); $cosU1 = 1 / sqrt(1 + $tanU1 * $tanU1); $sinU1 = $tanU1 * $cosU1; $tanU2 = (1 - $f) * tan($φ2); $cosU2 = 1 / sqrt(1 + $tanU2 * $tanU2); $sinU2 = $tanU2 * $cosU2; $λ = $L; $iterations = 0; do { $sinλ = sin($λ); $cosλ = cos($λ); $sinSqσ = $cosU2 * $sinλ * ($cosU2 * $sinλ) + ($cosU1 * $sinU2 - $sinU1 * $cosU2 * $cosλ) * ($cosU1 * $sinU2 - $sinU1 * $cosU2 * $cosλ); $sinσ = sqrt($sinSqσ); if ($sinσ == 0) { return 0; } $cosσ = $sinU1 * $sinU2 + $cosU1 * $cosU2 * $cosλ; $σ = atan2($sinσ, $cosσ); $sinα = $cosU1 * $cosU2 * $sinλ / $sinσ; $cosSqα = 1 - $sinα * $sinα; if ($cosSqα == 0.0) { $cos2σM = 0; } else { $cos2σM = $cosσ - 2 * $sinU1 * $sinU2 / $cosSqα; } $C = $f / 16 * $cosSqα * (4 + $f * (4 - 3 * $cosSqα)); $λp = $λ; $λ = $L + (1 - $C) * $f * $sinα * ($σ + $C * $sinσ * ($cos2σM + $C * $cosσ * (-1 + 2 * $cos2σM * $cos2σM))); } while (abs($λ - $λp) > 1.0E-12 && ++$iterations < 200); if ($iterations >= 200) { throw new NotConvergingException('Inverse Vincenty Formula did not converge'); } $uSq = $cosSqα * ($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))); $Δσ = $B * $sinσ * ($cos2σM + $B / 4 * ($cosσ * (-1 + 2 * $cos2σM * $cos2σM) - $B / 6 * $cos2σM * (-3 + 4 * $sinσ * $sinσ) * (-3 + 4 * $cos2σM * $cos2σM))); $s = $b * $A * ($σ - $Δσ); $α1 = atan2($cosU2 * $sinλ, $cosU1 * $sinU2 - $sinU1 * $cosU2 * $cosλ); $α2 = atan2($cosU1 * $sinλ, -$sinU1 * $cosU2 + $cosU1 * $sinU2 * $cosλ); $α1 = fmod($α1 + 2 * M_PI, 2 * M_PI); $α2 = fmod($α2 + 2 * M_PI, 2 * M_PI); $s = round($s, 3); return ['distance' => $s, 'bearing_initial' => rad2deg($α1), 'bearing_final' => rad2deg($α2)]; }
/** * @param Coordinate $coordinate * * @return string */ public function format(Coordinate $coordinate) { $lat = $coordinate->getLat(); $lng = $coordinate->getLng(); $latValue = abs($lat); $latDegrees = intval($latValue); $latMinutesDecimal = $latValue - $latDegrees; $latMinutes = 60 * $latMinutesDecimal; $lngValue = abs($lng); $lngDegrees = intval($lngValue); $lngMinutesDecimal = $lngValue - $lngDegrees; $lngMinutes = 60 * $lngMinutesDecimal; return sprintf("%s%02d%s %s%s%s%s%s%03d%s %s%s%s", $this->getLatPrefix($lat), abs($latDegrees), $this->units[$this->unitType]['deg'], number_format($latMinutes, $this->digits, $this->decimalPoint, $this->decimalPoint), $this->units[$this->unitType]['min'], $this->getLatSuffix($lat), $this->separator, $this->getLngPrefix($lng), abs($lngDegrees), $this->units[$this->unitType]['deg'], number_format($lngMinutes, $this->digits, $this->decimalPoint, $this->decimalPoint), $this->units[$this->unitType]['min'], $this->getLngSuffix($lng)); }
/** * @covers Location\Coordinate::getLat */ public function testGetLat() { $this->assertEquals(52.5, $this->coordinate->getLat()); }
/** * @param Coordinate $coordinate * * @return string */ public function format(Coordinate $coordinate) { $lat = $coordinate->getLat(); $lng = $coordinate->getLng(); $latValue = abs($lat); $latDegrees = intval($latValue); $latMinutesDecimal = $latValue - $latDegrees; $latMinutes = intval(60 * $latMinutesDecimal); $latSeconds = 60 * (60 * $latMinutesDecimal - $latMinutes); $lngValue = abs($lng); $lngDegrees = intval($lngValue); $lngMinutesDecimal = $lngValue - $lngDegrees; $lngMinutes = intval(60 * $lngMinutesDecimal); $lngSeconds = 60 * (60 * $lngMinutesDecimal - $lngMinutes); return sprintf("%s%02d%s %02d%s %02d%s%s%s%s%03d%s %02d%s %02d%s%s", $this->getLatPrefix($lat), abs($latDegrees), $this->units[$this->unitType]['deg'], $latMinutes, $this->units[$this->unitType]['min'], round($latSeconds, 0), $this->units[$this->unitType]['sec'], $this->getLatSuffix($lat), $this->separator, $this->getLngPrefix($lng), abs($lngDegrees), $this->units[$this->unitType]['deg'], $lngMinutes, $this->units[$this->unitType]['min'], round($lngSeconds, 0), $this->units[$this->unitType]['sec'], $this->getLngSuffix($lng)); }
/** * @param Coordinate $coordinate * * @return string */ public function format(Coordinate $coordinate) { return sprintf("%.5f%s%.5f", $coordinate->getLat(), $this->separator, $coordinate->getLng()); }
/** * Determine if given point is contained inside the polygon. * * @param Coordinate $point * * @return boolean */ public function contains(Coordinate $point) { $numberOfPoints = $this->getNumberOfPoints(); $polygonLats = $this->getLats(); $polygonLngs = $this->getLngs(); $i = $j = $c = 0; for ($i = 0, $j = $numberOfPoints - 1; $i < $numberOfPoints; $j = $i++) { if ($polygonLngs[$i] > $point->getLng() != $polygonLngs[$j] > $point->getLng() && $point->getLat() < ($polygonLats[$j] - $polygonLats[$i]) * ($point->getLng() - $polygonLngs[$i]) / ($polygonLngs[$j] - $polygonLngs[$i]) + $polygonLats[$i]) { $c = !$c; } } return $c == 0 ? false : $c; }
/** * @param Coordinate $coordinate * * @return string */ public function format(Coordinate $coordinate) { return json_encode(['type' => 'Point', 'coordinates' => [$coordinate->getLng(), $coordinate->getLat()]]); }