Completed
Push — master ( 4c6b8b...d48a1a )
by Doug
03:34
created

TransverseMercator::convertToLatitudeLongitude()   B

Complexity

Conditions 2
Paths 1

Size

Total Lines 78
Code Lines 70

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 55
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 70
nc 1
nop 6
dl 0
loc 78
ccs 55
cts 55
cp 1
crap 2
rs 8.6545
c 0
b 0
f 0

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 PHPCoord;
6
7
/**
8
 * Abstract class representing a Tranverse Mercator Projection.
9
 * @author Doug Wright
10
 */
11
abstract class TransverseMercator
12
{
13
    /**
14
     * X.
15
     * @var int
16
     */
17
    protected $x;
18
19
    /**
20
     * Y.
21
     * @var int
22
     */
23
    protected $y;
24
25
    /**
26
     * H.
27
     * @var int
28
     */
29
    protected $h;
30
31
    /**
32
     * Reference ellipsoid used in this datum.
33
     * @var RefEll
34
     */
35
    protected $refEll;
36
37
    /**
38
     * Cartesian constructor.
39
     * @param int    $x
40
     * @param int    $y
41
     * @param int    $h
42
     * @param RefEll $refEll
43
     */
44 31
    public function __construct(int $x, int $y, int $h, RefEll $refEll)
45
    {
46 31
        $this->x = $x;
47 31
        $this->y = $y;
48 31
        $this->h = $h;
49 31
        $this->refEll = $refEll;
50 31
    }
51
52
    /**
53
     * String version of coordinate.
54
     * @return string
55
     */
56
    public function __toString(): string
57
    {
58
        return "({$this->x}, {$this->y}, {$this->h})";
59
    }
60
61
    /**
62
     * @return int
63
     */
64 2
    public function getX(): int
65
    {
66 2
        return $this->x;
67
    }
68
69
    /**
70
     * @return int
71
     */
72 2
    public function getY(): int
73
    {
74 2
        return $this->y;
75
    }
76
77
    /**
78
     * @return int
79
     */
80 1
    public function getH(): int
81
    {
82 1
        return $this->h;
83
    }
84
85
    /**
86
     * Reference ellipsoid used by this projection.
87
     * @return RefEll
88
     */
89
    abstract public function getReferenceEllipsoid(): RefEll;
90
91
    /**
92
     * Scale factor at central meridian.
93
     * @return float
94
     */
95
    abstract public function getScaleFactor(): float;
96
97
    /**
98
     * Northing of true origin.
99
     * @return int
100
     */
101
    abstract public function getOriginNorthing(): int;
102
103
    /**
104
     * Easting of true origin.
105
     * @return int
106
     */
107
    abstract public function getOriginEasting(): int;
108
109
    /**
110
     * Latitude of true origin.
111
     * @return float
112
     */
113
    abstract public function getOriginLatitude(): float;
114
115
    /**
116
     * Longitude of true origin.
117
     * @return float
118
     */
119
    abstract public function getOriginLongitude(): float;
120
121
    /**
122
     * Convert this grid reference into a latitude and longitude
123
     * Formula for transformation is taken from OS document
124
     * "A Guide to Coordinate Systems in Great Britain".
125
     *
126
     * @param  float  $N       map coordinate (northing) of point to convert
127
     * @param  float  $E       map coordinate (easting) of point to convert
128
     * @param  float  $N0      map coordinate (northing) of true origin
129
     * @param  float  $E0      map coordinate (easting) of true origin
130
     * @param  float  $phi0    map coordinate (latitude) of true origin
131
     * @param  float  $lambda0 map coordinate (longitude) of true origin and central meridian
132
     * @return LatLng
133
     */
134 7
    public function convertToLatitudeLongitude(float $N, float $E, float $N0, float $E0, float $phi0, float $lambda0): LatLng
135
    {
136 7
        $phi0 = deg2rad($phi0);
137 7
        $lambda0 = deg2rad($lambda0);
138
139 7
        $refEll = $this->getReferenceEllipsoid();
140 7
        $F0 = $this->getScaleFactor();
141
142 7
        $a = $refEll->getMaj();
143 7
        $b = $refEll->getMin();
144 7
        $eSquared = $refEll->getEcc();
145 7
        $n = ($a - $b) / ($a + $b);
146 7
        $phiPrime = (($N - $N0) / ($a * $F0)) + $phi0;
147
148
        do {
149
            $M =
150 7
                ($b * $F0)
151 7
                * (((1 + $n + ((5 / 4) * $n * $n) + ((5 / 4) * $n * $n * $n))
152 7
                        * ($phiPrime - $phi0))
153 7
                    - (((3 * $n) + (3 * $n * $n) + ((21 / 8) * $n * $n * $n))
154 7
                        * sin($phiPrime - $phi0)
155 7
                        * cos($phiPrime + $phi0))
156 7
                    + ((((15 / 8) * $n * $n) + ((15 / 8) * $n * $n * $n))
157 7
                        * sin(2 * ($phiPrime - $phi0))
158 7
                        * cos(2 * ($phiPrime + $phi0)))
159 7
                    - (((35 / 24) * $n * $n * $n)
160 7
                        * sin(3 * ($phiPrime - $phi0))
161 7
                        * cos(3 * ($phiPrime + $phi0))));
162 7
            $phiPrime += ($N - $N0 - $M) / ($a * $F0);
163 7
        } while (($N - $N0 - $M) >= 0.00001);
164 7
        $v = $a * $F0 * ((1 - $eSquared * (sin($phiPrime) ** 2)) ** -0.5);
165
        $rho =
166
            $a
167 7
            * $F0
168 7
            * (1 - $eSquared)
169 7
            * ((1 - $eSquared * (sin($phiPrime) ** 2)) ** -1.5);
170 7
        $etaSquared = ($v / $rho) - 1;
171 7
        $VII = tan($phiPrime) / (2 * $rho * $v);
172
        $VIII =
173 7
            (tan($phiPrime) / (24 * $rho * ($v ** 3)))
174
            * (5
175 7
                + (3 * (tan($phiPrime) ** 2))
176 7
                + $etaSquared
177 7
                - (9 * (tan($phiPrime) ** 2) * $etaSquared));
178
        $IX =
179 7
            (tan($phiPrime) / (720 * $rho * ($v ** 5)))
180
            * (61
181 7
                + (90 * (tan($phiPrime) ** 2))
182 7
                + (45 * (tan($phiPrime) ** 2) * (tan($phiPrime) ** 2)));
183 7
        $X = (1 / cos($phiPrime)) / $v;
184
        $XI =
185 7
            ((1 / cos($phiPrime)) / (6 * $v * $v * $v))
186 7
            * (($v / $rho) + (2 * (tan($phiPrime) ** 2)));
187
        $XII =
188 7
            ((1 / cos($phiPrime)) / (120 * ($v ** 5)))
189
            * (5
190 7
                + (28 * (tan($phiPrime) ** 2))
191 7
                + (24 * (tan($phiPrime) ** 4)));
192
        $XIIA =
193 7
            ((1 / cos($phiPrime)) / (5040 * ($v ** 7)))
194
            * (61
195 7
                + (662 * (tan($phiPrime) ** 2))
196 7
                + (1320 * (tan($phiPrime) ** 4))
197
                + (720
198 7
                    * (tan($phiPrime) ** 6)));
199
        $phi =
200
            $phiPrime
201 7
            - ($VII * (($E - $E0) ** 2))
202 7
            + ($VIII * (($E - $E0) ** 4))
203 7
            - ($IX * (($E - $E0) ** 6));
204
        $lambda =
205
            $lambda0
206 7
            + ($X * ($E - $E0))
207 7
            - ($XI * (($E - $E0) ** 3))
208 7
            + ($XII * (($E - $E0) ** 5))
209 7
            - ($XIIA * (($E - $E0) ** 7));
210
211 7
        return new LatLng(rad2deg($phi), rad2deg($lambda), 0, $refEll);
212
    }
213
214
    /**
215
     * Calculate the surface distance between this object and the one
216
     * passed in as a parameter.
217
     *
218
     * @param  self $to object to measure the distance to
219
     * @return int
220
     */
221 1
    public function distance(self $to): int
222
    {
223 1
        if ($this->refEll != $to->refEll) {
224
            throw new \RuntimeException('Source and destination co-ordinates are not using the same ellipsoid');
225
        }
226
227
        //Because this is a 2D grid, we can use simple Pythagoras
228 1
        $distanceX = $to->getX() - $this->getX();
229 1
        $distanceY = $to->getY() - $this->getY();
230
231 1
        return (int) round((($distanceX ** 2) + ($distanceY ** 2)) ** 0.5);
232
    }
233
}
234