Coordinate::setPrecision()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 0
cts 1
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 2
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 250
    public function __construct($coordinates, Ellipsoid $ellipsoid = null)
62
    {
63 250
        if ($coordinates instanceof Location) {
64 36
            if (null !== $locationCoordinates = $coordinates->getCoordinates()) {
65 4
                $this->setLatitude($locationCoordinates->getLatitude());
66 36
                $this->setLongitude($locationCoordinates->getLongitude());
67
            }
68 214
        } elseif (is_array($coordinates) && 2 === count($coordinates)) {
69 61
            $this->setLatitude($coordinates[0]);
70 61
            $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 219
        $this->ellipsoid = $ellipsoid ?: Ellipsoid::createFromName(Ellipsoid::WGS84);
80 219
    }
81
82
    /**
83
     * {@inheritDoc}
84
     */
85 199
    public function normalizeLatitude($latitude)
86
    {
87 199
        $latitude = rtrim(sprintf('%.13f', max(-90, min(90, $latitude))), 0);
88
89
        return '.' === substr($latitude, -1) ? $latitude . '0' : $latitude;
90
    }
91
92
    /**
93 205
     * {@inheritDoc}
94
     */
95 205
    public function normalizeLongitude($longitude)
96 3
    {
97
        if (180 === $longitude % 360) {
98
            return '180.0';
99 202
        }
100 202
101
        $mod       = fmod($longitude, 360);
102 202
        $longitude = $mod < -180 ? $mod + 360 : ($mod > 180 ? $mod - 360 : $mod);
103
        $longitude = rtrim(sprintf('%.13f', $longitude), 0);
104
105
        return '.' === substr($longitude, -1) ? $longitude . '0' : $longitude;
106
    }
107
108 196
    /**
109
     * {@inheritDoc}
110 196
     */
111 196
    public function setLatitude($latitude)
112
    {
113
        $this->latitude = $this->normalizeLatitude($latitude);
0 ignored issues
show
Documentation Bug introduced by
The property $latitude was declared of type double, but $this->normalizeLatitude($latitude) is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
114
    }
115
116 195
    /**
117
     * {@inheritDoc}
118 195
     */
119
    public function getLatitude()
120
    {
121
        return $this->latitude;
122
    }
123
124 196
    /**
125
     * {@inheritDoc}
126 196
     */
127 196
    public function setLongitude($longitude)
128
    {
129
        $this->longitude = $this->normalizeLongitude($longitude);
0 ignored issues
show
Documentation Bug introduced by
The property $longitude was declared of type double, but $this->normalizeLongitude($longitude) is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
130
    }
131
132 195
    /**
133
     * {@inheritDoc}
134 195
     */
135
    public function getLongitude()
136
    {
137
        return $this->longitude;
138
    }
139
140 106
    /**
141
     * {@inheritDoc}
142 106
     */
143
    public function getEllipsoid()
144
    {
145
        return $this->ellipsoid;
146
    }
147
148
    /**
149
     * Creates a valid and acceptable geographic coordinates.
150
     *
151
     * @param string $coordinates
152 157
     *
153
     * @throws InvalidArgumentException
154 157
     */
155 1
    public function setFromString($coordinates)
156
    {
157
        if (!is_string($coordinates)) {
158
            throw new InvalidArgumentException('The given coordinates should be a string !');
159 156
        }
160 129
161 129
        try {
162 27
            $inDecimalDegree = $this->toDecimalDegrees($coordinates);
163 27
            $this->setLatitude($inDecimalDegree[0]);
164
            $this->setLongitude($inDecimalDegree[1]);
165 129
        } catch (InvalidArgumentException $e) {
166
            throw $e;
167
        }
168
    }
169
170 3
    /**
171
     * @return integer
172 3
     */
173
    public function getPrecision()
174
    {
175
        return $this->precision;
176
    }
177
178
    /**
179
     * @param  integer $precision
180
     * @return $this
181
     */
182
    public function setPrecision($precision)
183
    {
184
        $this->precision = $precision;
185
186
        return $this;
187
    }
188
189
190
    /**
191
     * Converts a valid and acceptable geographic coordinates to decimal degrees coordinate.
192
     *
193
     * @param string $coordinates A valid and acceptable geographic coordinates.
194
     *
195
     * @return array An array of coordinate in decimal degree.
196
     *
197
     * @throws InvalidArgumentException
198 156
     *
199
     * @see http://en.wikipedia.org/wiki/Geographic_coordinate_conversion
200
     */
201 156
    private function toDecimalDegrees($coordinates)
202 45
    {
203
        // 40.446195, -79.948862
204
        if (preg_match('/(\-?[0-9]{1,2}\.?\d*)[, ] ?(\-?[0-9]{1,3}\.?\d*)$/', $coordinates, $match)) {
205
            return array($match[1], $match[2]);
206 111
        }
207 111
208
        // 40° 26.7717, -79° 56.93172
209 55
        if (preg_match('/(\-?[0-9]{1,2})\D+([0-9]{1,2}\.?\d*)[, ] ?(\-?[0-9]{1,3})\D+([0-9]{1,2}\.?\d*)$/i',
210
            $coordinates, $match)) {
211 55
            return array(
212 55
                $match[1] < 0
213 55
                    ? $match[1] - $match[2] / 60
214 55
                    : $match[1] + $match[2] / 60,
215
                $match[3] < 0
216
                    ? $match[3] - $match[4] / 60
217
                    : $match[3] + $match[4] / 60
218
            );
219 94
        }
220
221 6
        // 40.446195N 79.948862W
222 6
        if (preg_match('/([0-9]{1,2}\.?\d*)\D*([ns]{1})[, ] ?([0-9]{1,3}\.?\d*)\D*([we]{1})$/i', $coordinates, $match)) {
223
            return array(
224
                'N' === strtoupper($match[2]) ? $match[1] : -$match[1],
225
                'E' === strtoupper($match[4]) ? $match[3] : -$match[3]
226
            );
227
        }
228 88
229 88
        // 40°26.7717S 79°56.93172E
230 3
        // 25°59.86′N,21°09.81′W
231 3
        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',
232
            $coordinates, $match)) {
233
            $latitude  = $match[1] + $match[2] / 60;
234 3
            $longitude = $match[4] + $match[5] / 60;
235 3
236
            return array(
237
                'N' === strtoupper($match[3]) ? $latitude  : -$latitude,
238
                'E' === strtoupper($match[6]) ? $longitude : -$longitude
239
            );
240
        }
241
242
        // 40:26:46N, 079:56:55W
243 85
        // 40:26:46.302N 079:56:55.903W
244 85
        // 40°26′47″N 079°58′36″W
245 58
        // 40d 26′ 47″ N 079d 58′ 36″ W
246 58
        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',
247
            $coordinates, $match)) {
248
            $latitude  = $match[1] + ($match[2] * 60 + $match[3]) / 3600;
249 58
            $longitude = $match[5] + ($match[6] * 60 + $match[7]) / 3600;
250 58
251
            return array(
252
                'N' === strtoupper($match[4]) ? $latitude  : -$latitude,
253
                'E' === strtoupper($match[8]) ? $longitude : -$longitude
254 27
            );
255 27
        }
256
257
        throw new InvalidArgumentException(
258
            'It should be a valid and acceptable ways to write geographic coordinates !'
259
        );
260
    }
261
262
    /**
263
     * {@inheritDoc}
264
     */
265
    public function jsonSerialize()
266
    {
267
        return [$this->latitude, $this->longitude];
268
    }
269
270
    /**
271
     * Returns a boolean determining coordinates equality
272 3
     * @param  Coordinate  $coordinate
273 3
     * @return boolean
274
     */
275
    public function isEqual(Coordinate $coordinate) {
276
        return bccomp($this->latitude, $coordinate->getLatitude(), $this->getPrecision()) === 0 && bccomp($this->longitude, $coordinate->getLongitude(), $this->getPrecision()) === 0;
277
    }
278
}
279