Completed
Pull Request — master (#87)
by Rémi
04:56
created

Polygon::remove()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 4
Bugs 1 Features 1
Metric Value
c 4
b 1
f 1
dl 0
loc 11
ccs 0
cts 6
cp 0
rs 9.4285
cc 2
eloc 6
nc 2
nop 1
crap 6
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\Polygon;
13
14
use League\Geotools\BoundingBox\BoundingBox;
15
use League\Geotools\BoundingBox\BoundingBoxInterface;
16
use League\Geotools\Coordinate\Coordinate;
17
use League\Geotools\Coordinate\CoordinateCollection;
18
use League\Geotools\Coordinate\CoordinateInterface;
19
20
/**
21
 * @author Gabriel Bull <[email protected]>
22
 */
23
class Polygon extends \League\Geotools\AbstractGeotools implements PolygonInterface, \Countable, \IteratorAggregate,
24
    \ArrayAccess, \JsonSerializable
25
{
26
    /**
27
     * @var CoordinateCollection|CoordinateInterface[]
28
     */
29
    private $coordinates;
30
31
    /**
32
     * @var BoundingBoxInterface
33
     */
34
    private $boundingBox;
35
36
    /**
37
     * @var boolean
38
     */
39
    private $hasCoordinate = false;
40
41
    /**
42
     * @var integer
43
     */
44
    private $precision = 8;
45
46
    /**
47
     * @param null|array|CoordinateCollection $coordinates
48
     */
49 15
    public function __construct($coordinates = null)
50
    {
51 15
        if (is_array($coordinates) || null === $coordinates) {
52 15
            $this->coordinates = new CoordinateCollection;
53
        } elseif ($coordinates instanceof CoordinateCollection) {
54
            $this->coordinates = $coordinates;
55
        } else {
56 2
            throw new \InvalidArgumentException;
57
        }
58
59 15
        $this->boundingBox = new BoundingBox($this);
60
61 15
        if (is_array($coordinates)) {
62 1
            $this->set($coordinates);
63
        }
64 15
    }
65
66
    /**
67
     * @param  CoordinateInterface $coordinate
68
     * @return boolean
69
     */
70 2
    public function pointInPolygon(CoordinateInterface $coordinate)
71
    {
72 2
        if (!$this->hasCoordinate) {
73
            return false;
74
        }
75
76 2
        if (!$this->boundingBox->pointInBoundingBox($coordinate)) {
77 1
            return false;
78
        }
79
80 1
        if ($this->pointOnVertex($coordinate)) {
81
            return true;
82
        }
83
84 1
        if ($this->pointOnBoundary($coordinate)) {
85
            return true;
86
        }
87
88 1
        $total = $this->count();
89 1
        $intersections = 0;
90 1
        for ($i = 1; $i < $total; $i++) {
91 1
            $currentVertex = $this->get($i - 1);
92 1
            $nextVertex = $this->get($i);
93
94
            if (
95 1
                bccomp(
96 1
                    $coordinate->getLatitude(),
97 1
                    min($currentVertex->getLatitude(), $nextVertex->getLatitude()),
98 1
                    $this->getPrecision()
99 1
                ) === 1 &&
100 1
                bccomp(
101 1
                    $coordinate->getLatitude(),
102 1
                    max($currentVertex->getLatitude(), $nextVertex->getLatitude()),
103 1
                    $this->getPrecision()
104 1
                ) <= 0 &&
105 1
                bccomp(
106 1
                    $coordinate->getLongitude(),
107 1
                    max($currentVertex->getLongitude(), $nextVertex->getLongitude()),
108 1
                    $this->getPrecision()
109 1
                ) <= 0 &&
110 1
                bccomp(
111 1
                    $currentVertex->getLatitude(),
112 1
                    $nextVertex->getLatitude(),
113 1
                    $this->getPrecision()
114 1
                ) !== 0
115
            ) {
116
                $xinters =
117 1
                    ($coordinate->getLatitude() - $currentVertex->getLatitude()) *
118 1
                    ($nextVertex->getLongitude() - $currentVertex->getLongitude()) /
119 1
                    ($nextVertex->getLatitude() - $currentVertex->getLatitude()) +
120 1
                    $currentVertex->getLongitude();
121
122
                if (
123 1
                    bccomp(
124 1
                        $currentVertex->getLongitude(),
125 1
                        $nextVertex->getLongitude(),
126 1
                        $this->getPrecision()
127 1
                    ) === 0 ||
128 1
                    bccomp(
129 1
                        $coordinate->getLongitude(),
130
                        $xinters,
131 1
                        $this->getPrecision()
132 1
                    ) <= 0
133
                ) {
134 1
                    $intersections++;
135
                }
136
            }
137
        }
138
139 1
        if ($intersections % 2 != 0) {
140 1
            return true;
141
        }
142
143
        return false;
144
    }
145
146
    /**
147
     * @param  CoordinateInterface $coordinate
148
     * @return boolean
149
     */
150 3
    public function pointOnBoundary(CoordinateInterface $coordinate)
151
    {
152 3
        $total = $this->count();
153 3
        for ($i = 1; $i <= $total; $i++) {
154 3
            $currentVertex = $this->get($i - 1);
155 3
            $nextVertex = $this->get($i);
156
157 3
            if (null === $nextVertex) {
158 3
                $nextVertex = $this->get(0);
159
            }
160
161
            // Check if coordinate is on a horizontal boundary
162
            if (
163 3
                bccomp(
164 3
                    $currentVertex->getLatitude(),
165 3
                    $nextVertex->getLatitude(),
166 3
                    $this->getPrecision()
167 3
                ) === 0 &&
168
                bccomp(
169
                    $currentVertex->getLatitude(),
170
                    $coordinate->getLatitude(),
171
                    $this->getPrecision()
172 3
                ) === 0 &&
173
                bccomp(
174
                    $coordinate->getLongitude(),
175
                    min($currentVertex->getLongitude(), $nextVertex->getLongitude()),
176
                    $this->getPrecision()
177 3
                ) === 1 &&
178
                bccomp(
179
                    $coordinate->getLongitude(),
180
                    max($currentVertex->getLongitude(), $nextVertex->getLongitude()),
181
                    $this->getPrecision()
182 3
                ) === -1
183
            ) {
184
                return true;
185
            }
186
187
            // Check if coordinate is on a boundary
188
            if (
189 3
                bccomp(
190 3
                    $coordinate->getLatitude(),
191 3
                    min($currentVertex->getLatitude(), $nextVertex->getLatitude()),
192 3
                    $this->getPrecision()
193 3
                ) === 1 &&
194 2
                bccomp(
195 2
                    $coordinate->getLatitude(),
196 2
                    max($currentVertex->getLatitude(), $nextVertex->getLatitude()),
197 2
                    $this->getPrecision()
198 3
                ) <= 0 &&
199 2
                bccomp(
200 2
                    $coordinate->getLongitude(),
201 2
                    max($currentVertex->getLongitude(), $nextVertex->getLongitude()),
202 2
                    $this->getPrecision()
203 3
                ) <= 0 &&
204 2
                bccomp(
205 2
                    $currentVertex->getLatitude(),
206 2
                    $nextVertex->getLatitude(),
207 2
                    $this->getPrecision()
208 3
                ) !== 0
209
            ) {
210
                $xinters =
211 2
                    ($coordinate->getLatitude() - $currentVertex->getLatitude()) *
212 2
                    ($nextVertex->getLongitude() - $currentVertex->getLongitude()) /
213 2
                    ($nextVertex->getLatitude() - $currentVertex->getLatitude()) +
214 2
                    $currentVertex->getLongitude();
215
216 2
                if (bccomp($xinters, $coordinate->getLongitude(), $this->getPrecision()) === 0) {
217 1
                    return true;
218
                }
219
            }
220
        }
221
222 2
        return false;
223
    }
224
225
    /**
226
     * @param  CoordinateInterface $coordinate
227
     * @return boolean
228
     */
229 3
    public function pointOnVertex(CoordinateInterface $coordinate)
230
    {
231 3
        foreach ($this->coordinates as $vertexCoordinate) {
232
            if (
233 3
                bccomp(
234 3
                    $vertexCoordinate->getLatitude(),
235 3
                    $coordinate->getLatitude(),
236 3
                    $this->getPrecision()
237 3
                ) === 0 &&
238 2
                bccomp(
239 2
                    $vertexCoordinate->getLongitude(),
240 2
                    $coordinate->getLongitude(),
241 2
                    $this->getPrecision()
242 3
                ) === 0
243
            ) {
244 3
                return true;
245
            }
246
        }
247
248 2
        return false;
249
    }
250
251
    /**
252
     * {@inheritDoc}
253
     */
254 15
    public function getCoordinates()
255
    {
256 15
        return $this->coordinates;
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->coordinates; of type League\Geotools\Coordina...e\CoordinateInterface[] adds the type League\Geotools\Coordinate\CoordinateInterface[] to the return on line 256 which is incompatible with the return type declared by the interface League\Geotools\Polygon\...terface::getCoordinates of type League\Geotools\Coordinate\CoordinateCollection.
Loading history...
257
    }
258
259
    /**
260
     * {@inheritDoc}
261
     */
262
    public function setCoordinates(CoordinateCollection $coordinates)
263
    {
264
        $this->coordinates = $coordinates;
265
        $this->boundingBox->setPolygon($this);
266
267
        return $this;
268
    }
269
270
    /**
271
     * @return array
272
     */
273
    public function toArray()
274
    {
275
        return $this->coordinates->toArray();
276
    }
277
278
    /**
279
     * {@inheritDoc}
280
     */
281
    public function jsonSerialize()
282
    {
283
        return $this->coordinates->jsonSerialize();
284
    }
285
286
    /**
287
     * {@inheritDoc}
288
     */
289
    public function offsetExists($offset)
290
    {
291
        return $this->coordinates->offsetExists($offset);
292
    }
293
294
    /**
295
     * {@inheritDoc}
296
     */
297
    public function offsetGet($offset)
298
    {
299
        return $this->coordinates->offsetGet($offset);
300
    }
301
302
    /**
303
     * {@inheritDoc}
304
     */
305
    public function offsetSet($offset, $value)
306
    {
307
        $this->coordinates->offsetSet($offset, $value);
308
        $this->boundingBox->setPolygon($this);
309
    }
310
311
    /**
312
     * {@inheritDoc}
313
     */
314
    public function offsetUnset($offset)
315
    {
316
        $retval = $this->coordinates->offsetUnset($offset);
317
        $this->boundingBox->setPolygon($this);
318
        return $retval;
319
    }
320
321
    /**
322
     * {@inheritDoc}
323
     */
324 4
    public function count()
325
    {
326 4
        return $this->coordinates->count();
327
    }
328
329
    /**
330
     * {@inheritDoc}
331
     */
332
    public function getIterator()
333
    {
334
        return $this->coordinates->getIterator();
335
    }
336
337
    /**
338
     * {@inheritDoc}
339
     */
340 4
    public function get($key)
341
    {
342 4
        return $this->coordinates->get($key);
343
    }
344
345
    /**
346
     * {@inheritDoc}
347
     */
348 8
    public function set($key, CoordinateInterface $coordinate = null)
349
    {
350 8
        if (is_array($key)) {
351 8
            $values = $key;
352
        } elseif (null !== $coordinate) {
353
            $values = [$key => $coordinate];
354
        } else {
355
            throw new \InvalidArgumentException;
356
        }
357
358 8
        foreach ($values as $key => $value) {
359 8
            if (!$value instanceof CoordinateInterface) {
360 8
                $value = new Coordinate($value);
361
            }
362 8
            $this->coordinates->set($key, $value);
363
        }
364
365 8
        $this->hasCoordinate = true;
366 8
        $this->boundingBox->setPolygon($this);
367 8
    }
368
369
    /**
370
     * {@inheritDoc}
371
     */
372 1
    public function add(CoordinateInterface $coordinate)
373
    {
374 1
        $retval = $this->coordinates->add($coordinate);
375
376 1
        $this->hasCoordinate = true;
377 1
        $this->boundingBox->setPolygon($this);
378
379 1
        return $retval;
380
    }
381
382
    /**
383
     * {@inheritDoc}
384
     */
385
    public function remove($key)
386
    {
387
        $retval = $this->coordinates->remove($key);
388
389
        if (!count($this->coordinates)) {
390
            $this->hasCoordinate = false;
391
        }
392
        $this->boundingBox->setPolygon($this);
393
394
        return $retval;
395
    }
396
397
    /**
398
     * @return integer
399
     */
400 5
    public function getPrecision()
401
    {
402 5
        return $this->precision;
403
    }
404
405
    /**
406
     * @param  integer $precision
407
     * @return $this
408
     */
409
    public function setPrecision($precision)
410
    {
411
        $this->boundingBox->setPrecision($precision);
412
        $this->precision = $precision;
413
414
        return $this;
415
    }
416
417
    /**
418
     * @return BoundingBoxInterface
419
     */
420 1
    public function getBoundingBox()
421
    {
422 1
        return $this->boundingBox;
423
    }
424
425
    /**
426
     * @param  BoundingBoxInterface $boundingBox
427
     * @return $this
428
     */
429
    public function setBoundingBox(BoundingBoxInterface $boundingBox)
430
    {
431
        $this->boundingBox = $boundingBox;
432
433
        return $this;
434
    }
435
}
436