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

Polygon   C

Complexity

Total Complexity 57

Size/Duplication

Total Lines 412
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 70.47%

Importance

Changes 4
Bugs 0 Features 0
Metric Value
wmc 57
lcom 1
cbo 4
dl 0
loc 412
ccs 136
cts 193
cp 0.7047
rs 6.433
c 4
b 0
f 0

22 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 16 5
C pointInPolygon() 0 75 13
C pointOnBoundary() 0 74 12
A pointOnVertex() 0 21 4
A getCoordinates() 0 4 1
A setCoordinates() 0 7 1
A toArray() 0 4 1
A jsonSerialize() 0 4 1
A offsetExists() 0 4 1
A offsetGet() 0 4 1
A offsetSet() 0 5 1
A offsetUnset() 0 6 1
A count() 0 4 1
A getIterator() 0 4 1
A get() 0 4 1
B set() 0 20 5
A add() 0 9 1
A remove() 0 11 2
A getPrecision() 0 4 1
A setPrecision() 0 7 1
A getBoundingBox() 0 4 1
A setBoundingBox() 0 6 1

How to fix   Complexity   

Complex Class

Complex classes like Polygon often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Polygon, and based on these observations, apply Extract Interface, too.

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