Completed
Push — master ( 8dc1e9...7ba148 )
by Antoine
23:27 queued 08:42
created

Coordinate::setPrecision()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

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