Completed
Push — master ( 723696...0885cd )
by Antoine
02:44
created

Coordinate   B

Complexity

Total Complexity 37

Size/Duplication

Total Lines 250
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 93.24%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 37
lcom 1
cbo 3
dl 0
loc 250
ccs 69
cts 74
cp 0.9324
rs 8.6
c 1
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
A normalizeLatitude() 0 4 1
A normalizeLongitude() 0 11 4
A setLatitude() 0 4 1
A getLatitude() 0 4 1
A setLongitude() 0 4 1
A getLongitude() 0 4 1
A getEllipsoid() 0 4 1
A setFromString() 0 14 3
A getPrecision() 0 4 1
A setPrecision() 0 6 1
A jsonSerialize() 0 4 1
A isEqual() 0 3 2
B __construct() 0 18 6
C toDecimalDegrees() 0 58 13
1
<?php
2
3
/*
4
 * This file is part of the Geotools library.
5
 *
6
 * (c) Antoine Corcy <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace League\Geotools\Coordinate;
13
14
use Geocoder\Model\Address;
15
use League\Geotools\Exception\InvalidArgumentException;
16
17
/**
18
 * Coordinate class
19
 *
20
 * @author Antoine Corcy <[email protected]>
21
 */
22
class Coordinate implements CoordinateInterface, \JsonSerializable
23
{
24
    /**
25
     * The latitude of the coordinate.
26
     *
27
     * @var double
28
     */
29
    protected $latitude;
30
31
    /**
32
     * The longitude of the coordinate.
33
     *
34
     * @var double
35
     */
36
    protected $longitude;
37
38
    /**
39
     * The selected ellipsoid.
40
     *
41
    * @var Ellipsoid
42
    */
43
    protected $ellipsoid;
44
45
46
    /**
47
     * The precision to use to compare big numbers
48
     *
49
     * @var integer
50
     */
51
    private $precision = 8;
52
53
    /**
54
     * Set the latitude and the longitude of the coordinates into an selected ellipsoid.
55
     *
56
     * @param Address|array|string         $coordinates The coordinates.
57
     * @param Ellipsoid                    $ellipsoid   The selected ellipsoid (WGS84 by default).
58
     *
59
     * @throws InvalidArgumentException
60
     */
61 231
    public function __construct($coordinates, Ellipsoid $ellipsoid = null)
62
    {
63 231
        if ($coordinates instanceof Address) {
64 37
            $this->setLatitude($coordinates->getLatitude());
65 37
            $this->setLongitude($coordinates->getLongitude());
66 194
        } elseif (is_array($coordinates) && 2 === count($coordinates)) {
67 41
            $this->setLatitude($coordinates[0]);
68 41
            $this->setLongitude($coordinates[1]);
69 159
        } elseif (is_string($coordinates)) {
70 154
            $this->setFromString($coordinates);
71
        } else {
72 5
            throw new InvalidArgumentException(
73 5
                'It should be a string, an array or a class which implements Geocoder\Model\Address !'
74
            );
75
        }
76
77 200
        $this->ellipsoid = $ellipsoid ?: Ellipsoid::createFromName(Ellipsoid::WGS84);
78 200
    }
79
80
    /**
81
     * {@inheritDoc}
82
     */
83 200
    public function normalizeLatitude($latitude)
84
    {
85 200
        return (double) max(-90, min(90, $latitude));
86
    }
87
88
    /**
89
     * {@inheritDoc}
90
     */
91 200
    public function normalizeLongitude($longitude)
92
    {
93 200
        if (180 === $longitude % 360) {
94 3
            return 180.0;
95
        }
96
97 198
        $mod       = fmod($longitude, 360);
98 198
        $longitude = $mod < -180 ? $mod + 360 : ($mod > 180 ? $mod - 360 : $mod);
99
100 198
        return (double) $longitude;
101
    }
102
103
    /**
104
     * {@inheritDoc}
105
     */
106 200
    public function setLatitude($latitude)
107
    {
108 200
        $this->latitude = $this->normalizeLatitude($latitude);
109 200
    }
110
111
    /**
112
     * {@inheritDoc}
113
     */
114 175
    public function getLatitude()
115
    {
116 175
        return $this->latitude;
117
    }
118
119
    /**
120
     * {@inheritDoc}
121
     */
122 200
    public function setLongitude($longitude)
123
    {
124 200
        $this->longitude = $this->normalizeLongitude($longitude);
125 200
    }
126
127
    /**
128
     * {@inheritDoc}
129
     */
130 175
    public function getLongitude()
131
    {
132 175
        return $this->longitude;
133
    }
134
135
    /**
136
     * {@inheritDoc}
137
     */
138 77
    public function getEllipsoid()
139
    {
140 77
        return $this->ellipsoid;
141
    }
142
143
    /**
144
     * Creates a valid and acceptable geographic coordinates.
145
     *
146
     * @param string $coordinates
147
     *
148
     * @throws InvalidArgumentException
149
     */
150 157
    public function setFromString($coordinates)
151
    {
152 157
        if (!is_string($coordinates)) {
153 1
            throw new InvalidArgumentException('The given coordinates should be a string !');
154
        }
155
156
        try {
157 156
            $inDecimalDegree = $this->toDecimalDegrees($coordinates);
158 129
            $this->setLatitude($inDecimalDegree[0]);
159 129
            $this->setLongitude($inDecimalDegree[1]);
160 27
        } catch (InvalidArgumentException $e) {
161 27
            throw $e;
162
        }
163 129
    }
164
165
    /**
166
     * @return integer
167
     */
168 3
    public function getPrecision()
169
    {
170 3
        return $this->precision;
171
    }
172
173
    /**
174
     * @param  integer $precision
175
     * @return $this
176
     */
177
    public function setPrecision($precision)
178
    {
179
        $this->precision = $precision;
180
181
        return $this;
182
    }
183
184
185
    /**
186
     * Converts a valid and acceptable geographic coordinates to decimal degrees coordinate.
187
     *
188
     * @param string $coordinates A valid and acceptable geographic coordinates.
189
     *
190
     * @return array An array of coordinate in decimal degree.
191
     *
192
     * @throws InvalidArgumentException
193
     *
194
     * @see http://en.wikipedia.org/wiki/Geographic_coordinate_conversion
195
     */
196 156
    private function toDecimalDegrees($coordinates)
197
    {
198
        // 40.446195, -79.948862
199 156
        if (preg_match('/(\-?[0-9]{1,2}\.?\d*)[, ] ?(\-?[0-9]{1,3}\.?\d*)$/', $coordinates, $match)) {
200 45
            return array($match[1], $match[2]);
201
        }
202
203
        // 40° 26.7717, -79° 56.93172
204 111
        if (preg_match('/(\-?[0-9]{1,2})\D+([0-9]{1,2}\.?\d*)[, ] ?(\-?[0-9]{1,3})\D+([0-9]{1,2}\.?\d*)$/i',
205
            $coordinates, $match)) {
206
            return array(
207 55
                $match[1] + $match[2] / 60,
208 55
                $match[3] < 0
209 55
                    ? $match[3] - $match[4] / 60
210 55
                    : $match[3] + $match[4] / 60
211
            );
212
        }
213
214
        // 40.446195N 79.948862W
215 94
        if (preg_match('/([0-9]{1,2}\.?\d*)\D*([ns]{1})[, ] ?([0-9]{1,3}\.?\d*)\D*([we]{1})$/i', $coordinates, $match)) {
216
            return array(
217 6
                'N' === strtoupper($match[2]) ? $match[1] : -$match[1],
218 6
                'E' === strtoupper($match[4]) ? $match[3] : -$match[3]
219
            );
220
        }
221
222
        // 40°26.7717S 79°56.93172E
223
        // 25°59.86′N,21°09.81′W
224 88
        if (preg_match('/([0-9]{1,2})\D+([0-9]{1,2}\.?\d*)\D*([ns]{1})[, ] ?([0-9]{1,3})\D+([0-9]{1,2}\.?\d*)\D*([we]{1})$/i',
225
            $coordinates, $match)) {
226 3
            $latitude  = $match[1] + $match[2] / 60;
227 3
            $longitude = $match[4] + $match[5] / 60;
228
229
            return array(
230 3
                'N' === strtoupper($match[3]) ? $latitude  : -$latitude,
231 3
                'E' === strtoupper($match[6]) ? $longitude : -$longitude
232
            );
233
        }
234
235
        // 40:26:46N, 079:56:55W
236
        // 40:26:46.302N 079:56:55.903W
237
        // 40°26′47″N 079°58′36″W
238
        // 40d 26′ 47″ N 079d 58′ 36″ W
239 85
        if (preg_match('/([0-9]{1,2})\D+([0-9]{1,2})\D+([0-9]{1,2}\.?\d*)\D*([ns]{1})[, ] ?([0-9]{1,3})\D+([0-9]{1,2})\D+([0-9]{1,2}\.?\d*)\D*([we]{1})$/i',
240
            $coordinates, $match)) {
241 58
            $latitude  = $match[1] + ($match[2] * 60 + $match[3]) / 3600;
242 58
            $longitude = $match[5] + ($match[6] * 60 + $match[7]) / 3600;
243
244
            return array(
245 58
                'N' === strtoupper($match[4]) ? $latitude  : -$latitude,
246 58
                'E' === strtoupper($match[8]) ? $longitude : -$longitude
247
            );
248
        }
249
250 27
        throw new InvalidArgumentException(
251 27
            'It should be a valid and acceptable ways to write geographic coordinates !'
252
        );
253
    }
254
255
    /**
256
     * {@inheritDoc}
257
     */
258
    public function jsonSerialize()
259
    {
260
        return [$this->latitude, $this->longitude];
261
    }
262
263
    /**
264
     * Returns a boolean determining coordinates equality
265
     * @param  Coordinate  $coordinate
266
     * @return boolean
267
     */
268 3
    public function isEqual(Coordinate $coordinate) {
269 3
        return bccomp($this->latitude, $coordinate->getLatitude(), $this->getPrecision()) === 0 && bccomp($this->longitude, $coordinate->getLongitude(), $this->getPrecision()) === 0;
270
    }
271
}
272