Completed
Push — master ( 1a8ca7...b2e639 )
by Doug
01:57
created

LatLng::getLng()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
/**
3
 * PHPCoord
4
 * @package PHPCoord
5
 * @author Jonathan Stott
6
 * @author Doug Wright
7
 */
8
namespace PHPCoord;
9
10
/**
11
 * Latitude/Longitude reference
12
 * @author Jonathan Stott
13
 * @author Doug Wright
14
 * @package PHPCoord
15
 */
16
class LatLng
17
{
18
19
    /**
20
     * Latitude
21
     * @var float
22
     */
23
    protected $lat;
24
25
    /**
26
     * Longitude
27
     * @var float
28
     */
29
    protected $lng;
30
31
    /**
32
     * Height
33
     * @var float
34
     */
35
    protected $h;
36
37
    /**
38
     * Reference ellipsoid the co-ordinates are from
39
     * @var RefEll
40
     */
41
    protected $refEll;
42
43
    /**
44
     * Create a new LatLng object from the given latitude and longitude
45
     * @param float $lat
46
     * @param float $lng
47
     * @param float $height
48
     * @param RefEll $refEll
49
     */
50 30
    public function __construct($lat, $lng, $height, RefEll $refEll)
51
    {
52 30
        $this->lat = round($lat, 5);
53 30
        $this->lng = round($lng, 5);
54 30
        $this->h = round($height);
55 30
        $this->refEll = $refEll;
56 30
    }
57
58
    /**
59
     * Return a string representation of this LatLng object
60
     * @return string
61
     */
62 13
    public function __toString()
63
    {
64 13
        return "({$this->lat}, {$this->lng})";
65
    }
66
67
    /**
68
     * @return float
69
     */
70 15
    public function getLat()
71
    {
72 15
        return $this->lat;
73
    }
74
75
    /**
76
     * @param float $lat
77
     */
78
    public function setLat($lat)
79
    {
80
        $this->lat = $lat;
81
    }
82
83
    /**
84
     * @return float
85
     */
86 15
    public function getLng()
87
    {
88 15
        return $this->lng;
89
    }
90
91
    /**
92
     * @param float $lng
93
     */
94
    public function setLng($lng)
95
    {
96
        $this->lng = $lng;
97
    }
98
99
    /**
100
     * @return float
101
     */
102 9
    public function getH()
103
    {
104 9
        return $this->h;
105
    }
106
107
    /**
108
     * @param float $h
109
     */
110
    public function setH($h)
111
    {
112
        $this->h = $h;
113
    }
114
115
116
    /**
117
     * @return RefEll
118
     */
119 9
    public function getRefEll()
120
    {
121 9
        return $this->refEll;
122
    }
123
124
    /**
125
     * @param RefEll $refEll
126
     */
127
    public function setRefEll(RefEll $refEll)
128
    {
129
        $this->refEll = $refEll;
130
    }
131
132
133
134
    /**
135
     * Calculate the surface distance between this LatLng object and the one
136
     * passed in as a parameter.
137
     *
138
     * @param LatLng $to a LatLng object to measure the surface distance to
139
     * @return float
140
     */
141 2
    public function distance(LatLng $to)
142
    {
143 2
        if ($this->refEll != $to->refEll) {
144 1
            throw new \RuntimeException('Source and destination co-ordinates are not using the same ellipsoid');
145
        }
146
147
        //Mean radius definition from taken from Wikipedia
148 1
        $er = ((2 * $this->refEll->getMaj()) + $this->refEll->getMin()) / 3;
149
150 1
        $latFrom = deg2rad($this->lat);
151 1
        $latTo = deg2rad($to->lat);
152 1
        $lngFrom = deg2rad($this->lng);
153 1
        $lngTo = deg2rad($to->lng);
154
155 1
        $d = acos(sin($latFrom) * sin($latTo) + cos($latFrom) * cos($latTo) * cos($lngTo - $lngFrom)) * $er;
156
157 1
        return round($d, 5);
158
    }
159
160
161
    /**
162
     * Convert this LatLng object to OSGB36 datum.
163
     * Reference values for transformation are taken from OS document
164
     * "A Guide to Coordinate Systems in Great Britain"
165
     * @return void
166
     */
167 4 View Code Duplication
    public function toOSGB36()
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
168
    {
169 4
        if ($this->refEll == RefEll::airy1830()) {
170 2
            return;
171
        }
172
173 2
        $this->toWGS84();
174
175 1
        $tx = -446.448;
176 1
        $ty = 125.157;
177 1
        $tz = -542.060;
178 1
        $s = 0.0000204894;
179 1
        $rx = deg2rad(-0.1502 / 3600);
180 1
        $ry = deg2rad(-0.2470 / 3600);
181 1
        $rz = deg2rad(-0.8421 / 3600);
182
183 1
        $this->transformDatum(RefEll::airy1830(), $tx, $ty, $tz, $s, $rx, $ry, $rz);
184 1
    }
185
186
    /**
187
     * Convert this LatLng object to ED50 datum.
188
     * Reference values for transformation are taken from http://www.globalmapper.com/helpv9/datum_list.htm
189
     * @return void
190
     */
191 1 View Code Duplication
    public function toED50()
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
192
    {
193 1
        if ($this->refEll == RefEll::international1924()) {
194
            return;
195
        }
196
197 1
        $this->toWGS84();
198
199 1
        $tx = 87;
200 1
        $ty = 98;
201 1
        $tz = 121;
202 1
        $s = 0;
203 1
        $rx = deg2rad(0);
204 1
        $ry = deg2rad(0);
205 1
        $rz = deg2rad(0);
206
207 1
        $this->transformDatum(RefEll::international1924(), $tx, $ty, $tz, $s, $rx, $ry, $rz);
208 1
    }
209
210
    /**
211
     * Convert this LatLng object to NAD27 datum.
212
     * Reference values for transformation are taken from Wikipedia
213
     * @return void
214
     */
215 1 View Code Duplication
    public function toNAD27()
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
216
    {
217 1
        if ($this->refEll == RefEll::clarke1866()) {
218
            return;
219
        }
220
221 1
        $this->toWGS84();
222
223 1
        $tx = 8;
224 1
        $ty = -160;
225 1
        $tz = -176;
226 1
        $s = 0;
227 1
        $rx = deg2rad(0);
228 1
        $ry = deg2rad(0);
229 1
        $rz = deg2rad(0);
230
231 1
        $this->transformDatum(RefEll::clarke1866(), $tx, $ty, $tz, $s, $rx, $ry, $rz);
232 1
    }
233
234
    /**
235
     * Convert this LatLng object to Ireland 1975 datum.
236
     * Reference values for transformation are taken from OSI document
237
     * "Making maps compatible with GPS"
238
     * @return void
239
     */
240 1 View Code Duplication
    public function toIreland1975()
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
241
    {
242 1
        if ($this->refEll == RefEll::airyModified()) {
243
            return;
244
        }
245
246 1
        $this->toWGS84();
247
248 1
        $tx = -482.530;
249 1
        $ty = 130.596;
250 1
        $tz = -564.557;
251 1
        $s = -0.00000815;
252 1
        $rx = deg2rad(-1.042 / 3600);
253 1
        $ry = deg2rad(-0.214 / 3600);
254 1
        $rz = deg2rad(-0.631 / 3600);
255
256 1
        $this->transformDatum(RefEll::airyModified(), $tx, $ty, $tz, $s, $rx, $ry, $rz);
257 1
    }
258
259
    /**
260
     * Convert this LatLng object from WGS84 datum to Ireland 1975 datum.
261
     * Reference values for transformation are taken from OSI document
262
     * "Making maps compatible with GPS"
263
     * @return void
264
     */
265 14
    public function toWGS84() {
266
267 14
        switch ($this->refEll) {
268
269 14
            case RefEll::wgs84():
270 8
                return; //do nothing
271
272 7 View Code Duplication
            case RefEll::airy1830(): // values from OSGB document "A Guide to Coordinate Systems in Great Britain"
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
273 2
                $tx = 446.448;
274 2
                $ty = -125.157;
275 2
                $tz = 542.060;
276 2
                $s = -0.0000204894;
277 2
                $rx = deg2rad(0.1502 / 3600);
278 2
                $ry = deg2rad(0.2470 / 3600);
279 2
                $rz = deg2rad(0.8421 / 3600);
280 2
                break;
281
282 5 View Code Duplication
            case RefEll::airyModified(): // values from OSI document "Making maps compatible with GPS"
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
283 1
                $tx = 482.530;
284 1
                $ty = -130.596;
285 1
                $tz = 564.557;
286 1
                $s = 0.00000815;
287 1
                $rx = deg2rad(1.042 / 3600);
288 1
                $ry = deg2rad(0.214 / 3600);
289 1
                $rz = deg2rad(0.631 / 3600);
290 1
                break;
291
292 4 View Code Duplication
            case RefEll::clarke1866(): // assumes NAD27, values from Wikipedia
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
293 1
                $tx = -8;
294 1
                $ty = 160;
295 1
                $tz = 176;
296 1
                $s = 0;
297 1
                $rx = deg2rad(0);
298 1
                $ry = deg2rad(0);
299 1
                $rz = deg2rad(0);
300 1
                break;
301
302 3 View Code Duplication
            case RefEll::international1924(): // assumes ED50, values from http://www.globalmapper.com/helpv9/datum_list.htm
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
303 1
                $tx = -87;
304 1
                $ty = -98;
305 1
                $tz = -121;
306 1
                $s = 0;
307 1
                $rx = deg2rad(0);
308 1
                $ry = deg2rad(0);
309 1
                $rz = deg2rad(0);
310 1
                break;
311
312 2 View Code Duplication
            case RefEll::bessel1841(): // assumes Germany, values from Wikipedia
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
313
                $tx = 582;
314
                $ty = -105;
315
                $tz = -414;
316
                $s = 0.0000083;
317
                $rx = deg2rad(1.04 / 3600);
318
                $ry = deg2rad(0.35 / 3600);
319
                $rz = deg2rad(-3.08 / 3600);
320
                break;
321
322 2
            default:
323 2
                throw new \RuntimeException('Transform parameters not known for this ellipsoid');
324 7
        }
325
326 5
        $this->transformDatum(RefEll::wgs84(), $tx, $ty, $tz, $s, $rx, $ry, $rz);
327 5
    }
328
329
    /**
330
     * Transform co-ordinates from one datum to another using a Helmert transformation
331
     * @param RefEll $toRefEll
332
     * @param float $tranX
333
     * @param float $tranY
334
     * @param float $tranZ
335
     * @param float $scale
336
     * @param float $rotX  rotation about x-axis in seconds
337
     * @param float $rotY  rotation about y-axis in seconds
338
     * @param float $rotZ  rotation about z-axis in seconds
339
     * @return mixed
340
     */
341 15
    public function transformDatum(RefEll $toRefEll, $tranX, $tranY, $tranZ, $scale, $rotX, $rotY, $rotZ)
342
    {
343
344 15
        if ($this->refEll == $toRefEll) {
345 6
            return;
346
        }
347
348 9
        $cartesian = Cartesian::fromLatLong($this);
349 9
        $cartesian->transformDatum($toRefEll, $tranX, $tranY, $tranZ, $scale, $rotX, $rotY, $rotZ);
350 9
        $newLatLng = $cartesian->toLatitudeLongitude();
351
352 9
        $this->lat = $newLatLng->getLat();
353 9
        $this->lng = $newLatLng->getLng();
354 9
        $this->h = $newLatLng->getH();
355 9
        $this->refEll = $newLatLng->getRefEll();
356 9
    }
357
358
359
    /**
360
     * Convert this LatLng object into an OSGB grid reference. Note that this
361
     * function does not take into account the bounds of the OSGB grid -
362
     * beyond the bounds of the OSGB grid, the resulting OSRef object has no
363
     * meaning
364
     *
365
     * Reference values for transformation are taken from OS document
366
     * "A Guide to Coordinate Systems in Great Britain"
367
     *
368
     * @return OSRef
369
     */
370 3 View Code Duplication
    public function toOSRef()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
371
    {
372 3
        $this->toOSGB36();
373
374 2
        $OSGB = new OSRef(0, 0); //dummy to get reference data
375 2
        $scale = $OSGB->getScaleFactor();
376 2
        $N0 = $OSGB->getOriginNorthing();
377 2
        $E0 = $OSGB->getOriginEasting();
378 2
        $phi0 = $OSGB->getOriginLatitude();
379 2
        $lambda0 = $OSGB->getOriginLongitude();
380
381 2
        $coords = $this->toTransverseMercatorEastingNorthing($scale, $E0, $N0, $phi0, $lambda0);
382
383 2
        return new OSRef(round($coords['E']), round($coords['N']), $this->h);
384
    }
385
386
    /**
387
     * Convert this LatLng object into an ITM grid reference
388
     *
389
     * @return ITMRef
390
     */
391 View Code Duplication
    public function toITMRef()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
392
    {
393
        $this->toWGS84();
394
395
        $ITM = new ITMRef(0, 0); //dummy to get reference data
396
        $scale = $ITM->getScaleFactor();
397
        $N0 = $ITM->getOriginNorthing();
398
        $E0 = $ITM->getOriginEasting();
399
        $phi0 = $ITM->getOriginLatitude();
400
        $lambda0 = $ITM->getOriginLongitude();
401
402
        $coords = $this->toTransverseMercatorEastingNorthing($scale, $E0, $N0, $phi0, $lambda0);
403
404
        return new ITMRef(round($coords['E']), round($coords['N']), $this->h);
405
    }
406
407
408
    /**
409
     * Convert a WGS84 latitude and longitude to an UTM reference
410
     *
411
     * Reference values for transformation are taken from OS document
412
     * "A Guide to Coordinate Systems in Great Britain"
413
     * @return UTMRef
414
     */
415 4
    public function toUTMRef()
416
    {
417 4
        $this->toWGS84();
418
419 4
        $longitudeZone = (int)(($this->lng + 180) / 6) + 1;
420
421
        // Special zone for Norway
422 4
        if ($this->lat >= 56 && $this->lat < 64 && $this->lng >= 3 && $this->lng < 12) {
423
            $longitudeZone = 32;
424 4
        } elseif ($this->lat >= 72 && $this->lat < 84) { // Special zones for Svalbard
425
            if ($this->lng >= 0 && $this->lng < 9) {
426
                $longitudeZone = 31;
427
            } elseif ($this->lng >= 9 && $this->lng < 21) {
428
                $longitudeZone = 33;
429
            } elseif ($this->lng >= 21 && $this->lng < 33) {
430
                $longitudeZone = 35;
431
            } elseif ($this->lng >= 33 && $this->lng < 42) {
432
                $longitudeZone = 37;
433
            }
434
        }
435
436 4
        $UTMZone = $this->getUTMLatitudeZoneLetter($this->lat);
437
438 2
        $UTM = new UTMRef(0, 0, 0, $UTMZone, $longitudeZone); //dummy to get reference data
439 2
        $scale = $UTM->getScaleFactor();
440 2
        $N0 = $UTM->getOriginNorthing();
441 2
        $E0 = $UTM->getOriginEasting();
442 2
        $phi0 = $UTM->getOriginLatitude();
443 2
        $lambda0 = $UTM->getOriginLongitude();
444
445 2
        $coords = $this->toTransverseMercatorEastingNorthing($scale, $E0, $N0, $phi0, $lambda0);
446
447 2
        if ($this->lat < 0) {
448 1
            $coords['N'] += 10000000;
449 1
        }
450
451 2
        return new UTMRef(round($coords['E']), round($coords['N']), $this->h, $UTMZone, $longitudeZone);
452
    }
453
454
    /**
455
     * Work out the UTM latitude zone from the latitude
456
     * @param float $latitude
457
     * @return string
458
     */
459 4
    private function getUTMLatitudeZoneLetter($latitude)
460
    {
461
462 4
        if ($latitude < -80 || $latitude > 84) {
463 2
            throw new \OutOfRangeException('UTM zones do not apply in polar regions');
464
        }
465
466 2
        $zones = "CDEFGHJKLMNPQRSTUVWXX";
467 2
        $zoneIndex = (int)(($latitude + 80) / 8);
468 2
        return $zones[$zoneIndex];
469
    }
470
471
472
    /**
473
     * Convert a latitude and longitude to easting and northing using a Transverse Mercator projection
474
     * Formula for transformation is taken from OS document
475
     * "A Guide to Coordinate Systems in Great Britain"
476
     *
477
     * @param float $scale scale factor on central meridian
478
     * @param float $originEasting easting of true origin
479
     * @param float $originNorthing northing of true origin
480
     * @param float $originLat latitude of true origin
481
     * @param float $originLong longitude of true origin
482
     * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,double>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
483
     */
484 4
    public function toTransverseMercatorEastingNorthing(
485
        $scale,
486
        $originEasting,
487
        $originNorthing,
488
        $originLat,
489
        $originLong
490
    ) {
491
492 4
        $originLat = deg2rad($originLat);
493 4
        $originLong = deg2rad($originLong);
494
495 4
        $lat = deg2rad($this->lat);
496 4
        $sinLat = sin($lat);
497 4
        $cosLat = cos($lat);
498 4
        $tanLat = tan($lat);
499 4
        $tanLatSq = pow($tanLat, 2);
500 4
        $long = deg2rad($this->lng);
501
502 4
        $n = ($this->refEll->getMaj() - $this->refEll->getMin()) / ($this->refEll->getMaj() + $this->refEll->getMin());
503 4
        $nSq = pow($n, 2);
504 4
        $nCu = pow($n, 3);
505
506 4
        $v = $this->refEll->getMaj() * $scale * pow(1 - $this->refEll->getEcc() * pow($sinLat, 2), -0.5);
507 4
        $p = $this->refEll->getMaj() * $scale * (1 - $this->refEll->getEcc()) * pow(1 - $this->refEll->getEcc() * pow($sinLat, 2), -1.5);
508 4
        $hSq = (($v / $p) - 1);
509
510 4
        $latPlusOrigin = $lat + $originLat;
511 4
        $latMinusOrigin = $lat - $originLat;
512
513 4
        $longMinusOrigin = $long - $originLong;
514
515 4
        $M = $this->refEll->getMin() * $scale
516 4
            * ((1 + $n + 1.25 * ($nSq + $nCu)) * $latMinusOrigin
517 4
                - (3 * ($n + $nSq) + 2.625 * $nCu) * sin($latMinusOrigin) * cos($latPlusOrigin)
518 4
                + 1.875 * ($nSq + $nCu) * sin(2 * $latMinusOrigin) * cos(2 * $latPlusOrigin)
519 4
                - (35 / 24 * $nCu * sin(3 * $latMinusOrigin) * cos(3 * $latPlusOrigin)));
520
521 4
        $I = $M + $originNorthing;
522 4
        $II = $v / 2 * $sinLat * $cosLat;
523 4
        $III = $v / 24 * $sinLat * pow($cosLat, 3) * (5 - $tanLatSq + 9 * $hSq);
524 4
        $IIIA = $v / 720 * $sinLat * pow($cosLat, 5) * (61 - 58 * $tanLatSq + pow($tanLatSq, 2));
525 4
        $IV = $v * $cosLat;
526 4
        $V = $v / 6 * pow($cosLat, 3) * ($v / $p - $tanLatSq);
527 4
        $VI = $v / 120 * pow($cosLat, 5) * (5 - 18 * $tanLatSq + pow($tanLatSq, 2) + 14 * $hSq - 58 * $tanLatSq * $hSq);
528
529 4
        $E = $originEasting + $IV * $longMinusOrigin + $V * pow($longMinusOrigin, 3) + $VI * pow($longMinusOrigin, 5);
530 4
        $N = $I + $II * pow($longMinusOrigin, 2) + $III * pow($longMinusOrigin, 4) + $IIIA * pow($longMinusOrigin, 6);
531
532 4
        return array('E' => $E, 'N' => $N);
533
    }
534
}
535