Vincenty::getDistance()   B
last analyzed

Complexity

Conditions 7
Paths 6

Size

Total Lines 82

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 82
rs 7.4593
c 0
b 0
f 0
cc 7
nc 6
nop 2

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Location\Distance;
6
7
use Location\Coordinate;
8
use Location\Exception\NotConvergingException;
9
use Location\Exception\NotMatchingEllipsoidException;
10
11
/**
12
 * Implementation of distance calculation with Vincenty Method
13
 *
14
 * @see http://www.movable-type.co.uk/scripts/latlong-vincenty.html
15
 *
16
 * @author Marcus Jaschen <[email protected]>
17
 */
18
class Vincenty implements DistanceInterface
19
{
20
    /**
21
     * @param Coordinate $point1
22
     * @param Coordinate $point2
23
     *
24
     * @throws NotMatchingEllipsoidException
25
     * @throws NotConvergingException
26
     *
27
     * @return float
28
     */
29
    public function getDistance(Coordinate $point1, Coordinate $point2): float
30
    {
31
        if ($point1->getEllipsoid()->getName() !== $point2->getEllipsoid()->getName()) {
32
            throw new NotMatchingEllipsoidException('The ellipsoids for both coordinates must match');
33
        }
34
35
        $lat1 = deg2rad($point1->getLat());
36
        $lat2 = deg2rad($point2->getLat());
37
        $lng1 = deg2rad($point1->getLng());
38
        $lng2 = deg2rad($point2->getLng());
39
40
        $a = $point1->getEllipsoid()->getA();
41
        $b = $point1->getEllipsoid()->getB();
42
        $f = 1 / $point1->getEllipsoid()->getF();
43
44
        $L  = $lng2 - $lng1;
45
        $U1 = atan((1 - $f) * tan($lat1));
46
        $U2 = atan((1 - $f) * tan($lat2));
47
48
        $iterationsLeft = 100;
49
        $lambda         = $L;
50
51
        $sinU1 = sin($U1);
52
        $sinU2 = sin($U2);
53
        $cosU1 = cos($U1);
54
        $cosU2 = cos($U2);
55
56
        do {
57
            $sinLambda = sin($lambda);
58
            $cosLambda = cos($lambda);
59
60
            $sinSigma = sqrt(
61
                ($cosU2 * $sinLambda) * ($cosU2 * $sinLambda) +
62
                ($cosU1 * $sinU2 - $sinU1 * $cosU2 * $cosLambda) * ($cosU1 * $sinU2 - $sinU1 * $cosU2 * $cosLambda)
63
            );
64
65
            if (abs($sinSigma) < 1E-12) {
66
                return 0.0;
67
            }
68
69
            $cosSigma = $sinU1 * $sinU2 + $cosU1 * $cosU2 * $cosLambda;
70
71
            $sigma = atan2($sinSigma, $cosSigma);
72
73
            $sinAlpha = $cosU1 * $cosU2 * $sinLambda / $sinSigma;
74
75
            $cosSqAlpha = 1 - $sinAlpha * $sinAlpha;
76
77
            $cos2SigmaM = 0;
78
            if (abs($cosSqAlpha) > 1E-12) {
79
                $cos2SigmaM = $cosSigma - 2 * $sinU1 * $sinU2 / $cosSqAlpha;
80
            }
81
82
            $C = $f / 16 * $cosSqAlpha * (4 + $f * (4 - 3 * $cosSqAlpha));
83
84
            $lambdaP = $lambda;
85
86
            $lambda = $L
87
                + (1 - $C)
88
                * $f
89
                * $sinAlpha
90
                * ($sigma + $C * $sinSigma * ($cos2SigmaM + $C * $cosSigma * (- 1 + 2 * $cos2SigmaM * $cos2SigmaM)));
91
92
            $iterationsLeft--;
93
        } while (abs($lambda - $lambdaP) > 1e-12 && $iterationsLeft > 0);
94
95
        if ($iterationsLeft === 0) {
96
            throw new NotConvergingException('Vincenty calculation does not converge');
97
        }
98
99
        $uSq        = $cosSqAlpha * ($a * $a - $b * $b) / ($b * $b);
100
        $A          = 1 + $uSq / 16384 * (4096 + $uSq * (- 768 + $uSq * (320 - 175 * $uSq)));
101
        $B          = $uSq / 1024 * (256 + $uSq * (- 128 + $uSq * (74 - 47 * $uSq)));
102
        $deltaSigma = $B * $sinSigma * (
103
            $cos2SigmaM
104
            + $B / 4 * ($cosSigma * (- 1 + 2 * $cos2SigmaM * $cos2SigmaM)
105
            - $B / 6 * $cos2SigmaM * (- 3 + 4 * $sinSigma * $sinSigma) * (- 3 + 4 * $cos2SigmaM * $cos2SigmaM))
106
        );
107
        $s          = $b * $A * ($sigma - $deltaSigma);
108
109
        return round($s, 3);
110
    }
111
}
112