Completed
Push — master ( ef8e35...28395c )
by Antoine
05:19
created

Coordinate   B

Complexity

Total Complexity 38

Size/Duplication

Total Lines 252
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 93.33%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 38
lcom 1
cbo 4
dl 0
loc 252
ccs 70
cts 75
cp 0.9333
rs 8.3999
c 2
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 20 7
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\Location;
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 Location|array|string         $coordinates The coordinates.
57
     * @param Ellipsoid                    $ellipsoid   The selected ellipsoid (WGS84 by default).
58
     *
59
     * @throws InvalidArgumentException
60
     */
61 253
    public function __construct($coordinates, Ellipsoid $ellipsoid = null)
62
    {
63 253
        if ($coordinates instanceof Location) {
64 37
            if (null !== $locationCoordinates = $coordinates->getCoordinates()) {
65 4
                $this->setLatitude($locationCoordinates->getLatitude());
66 37
                $this->setLongitude($locationCoordinates->getLongitude());
67
            }
68 216
        } elseif (is_array($coordinates) && 2 === count($coordinates)) {
69 63
            $this->setLatitude($coordinates[0]);
70 63
            $this->setLongitude($coordinates[1]);
71 159
        } elseif (is_string($coordinates)) {
72 154
            $this->setFromString($coordinates);
73
        } else {
74 5
            throw new InvalidArgumentException(
75 5
                'It should be a string, an array or a class which implements Geocoder\Model\Address !'
76
            );
77
        }
78
79 222
        $this->ellipsoid = $ellipsoid ?: Ellipsoid::createFromName(Ellipsoid::WGS84);
80 222
    }
81
82
    /**
83
     * {@inheritDoc}
84
     */
85 201
    public function normalizeLatitude($latitude)
86
    {
87 201
        return (double) max(-90, min(90, $latitude));
88
    }
89
90
    /**
91
     * {@inheritDoc}
92
     */
93 207
    public function normalizeLongitude($longitude)
94
    {
95 207
        if (180 === $longitude % 360) {
96 3
            return 180.0;
97
        }
98
99 204
        $mod       = fmod($longitude, 360);
100 204
        $longitude = $mod < -180 ? $mod + 360 : ($mod > 180 ? $mod - 360 : $mod);
101
102 204
        return (double) $longitude;
103
    }
104
105
    /**
106
     * {@inheritDoc}
107
     */
108 198
    public function setLatitude($latitude)
109
    {
110 198
        $this->latitude = $this->normalizeLatitude($latitude);
111 198
    }
112
113
    /**
114
     * {@inheritDoc}
115
     */
116 197
    public function getLatitude()
117
    {
118 197
        return $this->latitude;
119
    }
120
121
    /**
122
     * {@inheritDoc}
123
     */
124 198
    public function setLongitude($longitude)
125
    {
126 198
        $this->longitude = $this->normalizeLongitude($longitude);
127 198
    }
128
129
    /**
130
     * {@inheritDoc}
131
     */
132 197
    public function getLongitude()
133
    {
134 197
        return $this->longitude;
135
    }
136
137
    /**
138
     * {@inheritDoc}
139
     */
140 108
    public function getEllipsoid()
141
    {
142 108
        return $this->ellipsoid;
143
    }
144
145
    /**
146
     * Creates a valid and acceptable geographic coordinates.
147
     *
148
     * @param string $coordinates
149
     *
150
     * @throws InvalidArgumentException
151
     */
152 157
    public function setFromString($coordinates)
153
    {
154 157
        if (!is_string($coordinates)) {
155 1
            throw new InvalidArgumentException('The given coordinates should be a string !');
156
        }
157
158
        try {
159 156
            $inDecimalDegree = $this->toDecimalDegrees($coordinates);
160 129
            $this->setLatitude($inDecimalDegree[0]);
161 129
            $this->setLongitude($inDecimalDegree[1]);
162 27
        } catch (InvalidArgumentException $e) {
163 27
            throw $e;
164
        }
165 129
    }
166
167
    /**
168
     * @return integer
169
     */
170 3
    public function getPrecision()
171
    {
172 3
        return $this->precision;
173
    }
174
175
    /**
176
     * @param  integer $precision
177
     * @return $this
178
     */
179
    public function setPrecision($precision)
180
    {
181
        $this->precision = $precision;
182
183
        return $this;
184
    }
185
186
187
    /**
188
     * Converts a valid and acceptable geographic coordinates to decimal degrees coordinate.
189
     *
190
     * @param string $coordinates A valid and acceptable geographic coordinates.
191
     *
192
     * @return array An array of coordinate in decimal degree.
193
     *
194
     * @throws InvalidArgumentException
195
     *
196
     * @see http://en.wikipedia.org/wiki/Geographic_coordinate_conversion
197
     */
198 156
    private function toDecimalDegrees($coordinates)
199
    {
200
        // 40.446195, -79.948862
201 156
        if (preg_match('/(\-?[0-9]{1,2}\.?\d*)[, ] ?(\-?[0-9]{1,3}\.?\d*)$/', $coordinates, $match)) {
202 45
            return array($match[1], $match[2]);
203
        }
204
205
        // 40° 26.7717, -79° 56.93172
206 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',
207
            $coordinates, $match)) {
208
            return array(
209 55
                $match[1] + $match[2] / 60,
210 55
                $match[3] < 0
211 55
                    ? $match[3] - $match[4] / 60
212 55
                    : $match[3] + $match[4] / 60
213
            );
214
        }
215
216
        // 40.446195N 79.948862W
217 94
        if (preg_match('/([0-9]{1,2}\.?\d*)\D*([ns]{1})[, ] ?([0-9]{1,3}\.?\d*)\D*([we]{1})$/i', $coordinates, $match)) {
218
            return array(
219 6
                'N' === strtoupper($match[2]) ? $match[1] : -$match[1],
220 6
                'E' === strtoupper($match[4]) ? $match[3] : -$match[3]
221
            );
222
        }
223
224
        // 40°26.7717S 79°56.93172E
225
        // 25°59.86′N,21°09.81′W
226 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',
227
            $coordinates, $match)) {
228 3
            $latitude  = $match[1] + $match[2] / 60;
229 3
            $longitude = $match[4] + $match[5] / 60;
230
231
            return array(
232 3
                'N' === strtoupper($match[3]) ? $latitude  : -$latitude,
233 3
                'E' === strtoupper($match[6]) ? $longitude : -$longitude
234
            );
235
        }
236
237
        // 40:26:46N, 079:56:55W
238
        // 40:26:46.302N 079:56:55.903W
239
        // 40°26′47″N 079°58′36″W
240
        // 40d 26′ 47″ N 079d 58′ 36″ W
241 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',
242
            $coordinates, $match)) {
243 58
            $latitude  = $match[1] + ($match[2] * 60 + $match[3]) / 3600;
244 58
            $longitude = $match[5] + ($match[6] * 60 + $match[7]) / 3600;
245
246
            return array(
247 58
                'N' === strtoupper($match[4]) ? $latitude  : -$latitude,
248 58
                'E' === strtoupper($match[8]) ? $longitude : -$longitude
249
            );
250
        }
251
252 27
        throw new InvalidArgumentException(
253 27
            'It should be a valid and acceptable ways to write geographic coordinates !'
254
        );
255
    }
256
257
    /**
258
     * {@inheritDoc}
259
     */
260
    public function jsonSerialize()
261
    {
262
        return [$this->latitude, $this->longitude];
263
    }
264
265
    /**
266
     * Returns a boolean determining coordinates equality
267
     * @param  Coordinate  $coordinate
268
     * @return boolean
269
     */
270 3
    public function isEqual(Coordinate $coordinate) {
271 3
        return bccomp($this->latitude, $coordinate->getLatitude(), $this->getPrecision()) === 0 && bccomp($this->longitude, $coordinate->getLongitude(), $this->getPrecision()) === 0;
272
    }
273
}
274