Geometry::getGeomType()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 0
1
<?php
2
3
namespace geoPHP\Geometry;
4
5
use geoPHP\geoPHP;
6
use geoPHP\Exception\UnsupportedMethodException;
7
8
/**
9
 * Geometry is the root class of the hierarchy. Geometry is an abstract (non-instantiable) class.
10
 *
11
 * OGC 06-103r4 6.1.2 specification:
12
 * The instantiable subclasses of Geometry defined in this Standard are restricted to
13
 * 0, 1 and 2-dimensional geometric objects that exist in 2, 3 or 4-dimensional coordinate space.
14
 *
15
 * Geometry values in R^2 have points with coordinate values for x and y.
16
 * Geometry values in R^3 have points with coordinate values for x, y and z or for x, y and m.
17
 * Geometry values in R^4 have points with coordinate values for x, y, z and m.
18
 * The interpretation of the coordinates is subject to the coordinate reference systems associated to the point.
19
 * All coordinates within a geometry object should be in the same coordinate reference systems.
20
 * Each coordinate shall be unambiguously associated to a coordinate reference system
21
 * either directly or through its containing geometry.
22
 *
23
 * The z coordinate of a point is typically, but not necessarily, represents altitude or elevation.
24
 * The m coordinate represents a measurement.
25
 */
26
abstract class Geometry
27
{
28
29
    /**
30
     * Type constants
31
     */
32
    const POINT = 'Point';
33
    const LINESTRING = 'LineString';
34
    const POLYGON = 'Polygon';
35
    const MULTI_POINT = 'MultiPoint';
36
    const MULTI_LINESTRING = 'MultiLineString';
37
    const MULTI_POLYGON = 'MultiPolygon';
38
    const GEOMETRY_COLLECTION = 'GeometryCollection';
39
    const CIRCULAR_STRING = 'CircularString';
40
    const COMPOUND_CURVE = 'CompoundCurve';
41
    const CURVE_POLYGON = 'CurvePolygon';
42
    const MULTI_CURVE = 'MultiCurve'; // Abstract
43
    const MULTI_SURFACE = 'MultiSurface'; // Abstract
44
    const CURVE = 'Curve'; // Abstract
45
    const SURFACE = 'Surface'; // Abstract
46
    const POLYHEDRAL_SURFACE = 'PolyhedralSurface';
47
    const TIN = 'TIN';
48
    const TRIANGLE = 'Triangle';
49
50
    /**
51
     * @var bool True if Geometry has Z (altitude) value
52
     */
53
    protected $hasZ = false;
54
55
    /**
56
     * @var bool True if Geometry has M (measure) value
57
     */
58
    protected $isMeasured = false;
59
60
    /**
61
     * @var int|null $srid Spatial Reference System Identifier (http://en.wikipedia.org/wiki/SRID)
62
     */
63
    protected $srid;
64
65
    /**
66
     * @var mixed|null Custom (meta)data
67
     */
68
    protected $data;
69
70
    /**
71
     * @var \GEOSGeometry|null|false
72
     */
73
    private $geos;
74
75
    /** **************************************
76
     * Basic methods on geometric objects  *
77
     * ************************************* */
78
79
    /**
80
     * The inherent dimension of the geometric object, which must be less than or equal to the coordinate dimension.
81
     * In non-homogeneous collections, this will return the largest topological dimension of the contained objects.
82
     *
83
     * @return int
84
     */
85
    abstract public function dimension(): int;
86
87
    /**
88
     * Returns the name of the instantiable subtype of Geometry of which the geometric object is an instantiable member.
89
     *
90
     * @return string
91
     */
92
    abstract public function geometryType(): string;
93
94
    /**
95
     * Returns true whether the set of points covered by this Geometry is empty.
96
     *
97
     * @return bool
98
     */
99
    abstract public function isEmpty(): bool;
100
101
    /**
102
     * Tests whether this geometry is simple. The SFS definition of simplicity follows the general rule that a Geometry
103
     * is simple if it has no points of self-tangency, self-intersection or other anomalous points.
104
     * Be aware: a geometry can be simple but also not valid! E.g. "LINESTRING(1 1,1 1)" or "POLYGON((1 1,1 1,1 1,1 1))"
105
     * are simple but not valid.
106
     *
107
     * Simplicity is defined for each Geometry subclass as follows:
108
     * - Valid polygonal geometries are simple, since their rings must not self-intersect. isSimple tests for this
109
     *   condition and reports false if it is not met. (This is a looser test than checking for validity).
110
     * - Linear rings have the same semantics.
111
     * - Linear geometries are simple if they do not self-intersect at points except on its end-points.
112
     * - Zero-dimensional geometries (MultiPoints) are simple if they have no repeated points.
113
     * - Empty Geometries are always simple.
114
     *
115
     * @return bool
116
     */
117
    abstract public function isSimple(): bool;
118
119
    /**
120
     * Returns the boundary, or an empty geometry of appropriate dimension if this Geometry is empty.
121
     * In the case of zero-dimensional geometries, an empty GeometryCollection is returned.
122
     * The boundary of a Geometry is a set of Geometries of the next lower dimension.
123
     * By default, the boundary of a collection is the boundary of it's components.
124
     *
125
     * @return Geometry the closure of the combinatorial boundary of this Geometry
126
     */
127
    abstract public function boundary(): Geometry;
128
129
    /**
130
     * @return Geometry[]
131
     */
132
    abstract public function getComponents(): array;
133
134
    /** ***********************************************
135
     * Methods applicable on certain geometry types *
136
     * ********************************************** */
137
138
    /**
139
     * Returns the area of this Geometry.
140
     * Areal Geometries have a non-zero area. They override this function to compute the area. Others return 0.0
141
     *
142
     * @return float
143
     */
144
    public function area(): float
145
    {
146
        return $this->getArea();
147
    }
148
149
    /**
150
     *
151
     * @return Point
152
     */
153
    public function centroid(): Point
154
    {
155
        return $this->getCentroid();
156
    }
157
158
    /**
159
     * @see Geometry::getLength()
160
     * @return float
161
     */
162
    public function length(): float
163
    {
164
        return $this->getLength();
165
    }
166
167
    abstract public function length3D(): float;
168
169
    /**
170
     * @see Geometry::getX()
171
     *
172
     * @return float|null
173
     */
174
    public function x()
175
    {
176
        return $this->getX();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getX() targeting geoPHP\Geometry\Geometry::getX() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
177
    }
178
179
    /**
180
     * @see Geometry::getY()
181
     *
182
     * @return float|null
183
     */
184
    public function y()
185
    {
186
        return $this->getY();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getY() targeting geoPHP\Geometry\Geometry::getY() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
187
    }
188
189
    /**
190
     * @see Geometry::getZ()
191
     *
192
     * @return float|null
193
     */
194
    public function z()
195
    {
196
        return $this->getZ();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getZ() targeting geoPHP\Geometry\Geometry::getZ() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
197
    }
198
199
    /**
200
     * @see Geometry::getM()
201
     *
202
     * @return float|null
203
     */
204
    public function m()
205
    {
206
        return $this->getM();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getM() targeting geoPHP\Geometry\Geometry::getM() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
207
    }
208
209
    /**
210
     * @return int
211
     */
212
    abstract public function numGeometries(): int;
213
214
    /**
215
     * @param  int $n One-based index.
216
     * @return Geometry|null The geometry or null if not found.
217
     */
218
    abstract public function geometryN(int $n);
219
220
    /**
221
     * @return Point|null
222
     */
223
    public function startPoint()
224
    {
225
        return null;
226
    }
227
228
    /**
229
     * @return Point|null
230
     */
231
    public function endPoint()
232
    {
233
        return null;
234
    }
235
236
    /**
237
     * @return bool
238
     * @throws UnsupportedMethodException
239
     */
240
    public function isRing(): bool
241
    {
242
        throw new UnsupportedMethodException(
243
            get_called_class() . '::isRing',
244
            0,
245
            "It should only be called on a linear feature."
246
        );
247
    }
248
249
    abstract public function isClosed(): bool;
250
251
    abstract public function numPoints(): int;
252
253
    /**
254
     * @param  int $n Nth point
255
     * @return Point|null
256
     */
257
    public function pointN(int $n)
0 ignored issues
show
Unused Code introduced by
The parameter $n is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

257
    public function pointN(/** @scrutinizer ignore-unused */ int $n)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
258
    {
259
        return null;
260
    }
261
262
    /**
263
     * @return Geometry|null
264
     */
265
    public function exteriorRing()
266
    {
267
        return null;
268
    }
269
270
    /**
271
     * @return int|null
272
     */
273
    public function numInteriorRings()
274
    {
275
        return null;
276
    }
277
278
    /**
279
     * @return Geometry|null
280
     */
281
    public function interiorRingN(int $n)
0 ignored issues
show
Unused Code introduced by
The parameter $n is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

281
    public function interiorRingN(/** @scrutinizer ignore-unused */ int $n)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
282
    {
283
        return null;
284
    }
285
286
    /**
287
     * Returns the shortest distance between g1 and g2.
288
     * The Geometry inputs for distance can be any combination of 2D or 3D geometries.
289
     * If both geometries are 3D then a 3D distance is computed.
290
     * If both geometries are 2D then a 2D distance is computed.
291
     * If one geometry is 2D and the other is 3D then a 2D distance is computed (as if the 2D object was infinitely
292
     * extended along the Z axis).
293
     *
294
     * @return float|null the distance between the geometries. null if input geometry is empty.
295
     */
296
    abstract public function distance(Geometry $geom);
297
298
    abstract public function equals(Geometry $geom): bool;
299
300
    // Abstract: Non-Standard
301
    // ----------------------------------------------------------
302
303
    /**
304
     * @return array{'minx'?:float|null, 'miny'?:float|null, 'maxx'?:float|null, 'maxy'?:float|null}
305
     */
306
    abstract public function getBBox(): array;
307
308
    /**
309
     * @return array<int, array>
310
     */
311
    abstract public function asArray(): array;
312
313
    /**
314
     * @return Point[]
315
     */
316
    abstract public function getPoints(): array;
317
318
    /**
319
     * @return self
320
     */
321
    abstract public function invertXY();
322
323
    /**
324
     * @param  bool $toArray return underlying components as LineStrings/Points or as array.
325
     * @return LineString[]|Point[]|array{}|array[]
326
     */
327
    abstract public function explode(bool $toArray = false): array;
328
329
    /**
330
     * @param  float|int $radius
331
     * @return float 0.0
332
     */
333
    abstract public function greatCircleLength($radius = geoPHP::EARTH_WGS84_SEMI_MAJOR_AXIS): float; //meters
334
335
    abstract public function haversineLength(): float; //degrees
336
337
    /**
338
     * 3D to 2D
339
     * @return void
340
     */
341
    abstract public function flatten();
342
343
    // Elevations statistics
344
345
    /**
346
     * @return int|float|null
347
     */
348
    public function minimumZ()
349
    {
350
        return null;
351
    }
352
353
    /**
354
     * @return int|float|null
355
     */
356
    public function maximumZ()
357
    {
358
        return null;
359
    }
360
361
    /**
362
     * @return int|float|null
363
     */
364
    public function minimumM()
365
    {
366
        return null;
367
    }
368
369
    /**
370
     * @return int|float|null
371
     */
372
    public function maximumM()
373
    {
374
        return null;
375
    }
376
377
    /**
378
     * @return int|float|null
379
     */
380
    public function zDifference()
381
    {
382
        return null;
383
    }
384
385
    /**
386
     * @param int|float $verticalTolerance
387
     * @return int|float|null
388
     */
389
    public function elevationGain($verticalTolerance = 0)
0 ignored issues
show
Unused Code introduced by
The parameter $verticalTolerance is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

389
    public function elevationGain(/** @scrutinizer ignore-unused */ $verticalTolerance = 0)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
390
    {
391
        return null;
392
    }
393
394
    /**
395
     * @param int|float $verticalTolerance
396
     * @return int|float|null
397
     */
398
    public function elevationLoss($verticalTolerance = 0)
0 ignored issues
show
Unused Code introduced by
The parameter $verticalTolerance is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

398
    public function elevationLoss(/** @scrutinizer ignore-unused */ $verticalTolerance = 0)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
399
    {
400
        return null;
401
    }
402
403
    // Public: Standard -- Common to all geometries
404
    // ----------------------------------------------------------
405
406
    /**
407
     * check if Geometry has Z (altitude) coordinate
408
     *
409
     * @return bool true if collection has Z value.
410
     */
411
    public function is3D(): bool
412
    {
413
        return $this->hasZ();
414
    }
415
    
416
    /**
417
     * check if Geometry has a measure value
418
     *
419
     * @return bool true if collection has measure values
420
     */
421
    abstract public function isMeasured(): bool;
422
423
    /**
424
     * @param int|null $srid Spatial Reference System Identifier
425
     * @return void
426
     */
427
    public function setSRID($srid)
428
    {
429
        $geosObj = $this->getGeos();
430
        if (is_object($geosObj)) {
431
            // @codeCoverageIgnoreStart
432
            /** @noinspection PhpUndefinedMethodInspection */
433
            $geosObj->setSRID($srid ?? 0);
434
            // @codeCoverageIgnoreEnd
435
        }
436
        $this->srid = $srid;
437
    }
438
439
    /**
440
     * Adds custom data to the geometry
441
     *
442
     * @param string|array<mixed> $property The name of the data or an associative array
443
     * @param mixed|null   $value    The data. Can be any type (string, integer, array, etc.)
444
     * @return void
445
     */
446
    public function setData($property, $value = null)
447
    {
448
        if (is_array($property)) {
449
            $this->data = $property;
450
        } else {
451
            $this->data[$property] = $value;
452
        }
453
    }
454
455
    /**
456
     * Returns the requested data by property name, or all data of the geometry
457
     *
458
     * @param  string|null $property The name of the data. If omitted, all data will be returned
459
     * @return mixed|null The data or null if not exists
460
     */
461
    public function getData($property = null)
462
    {
463
        if ($property) {
464
            return $this->hasDataProperty($property) ? $this->data[$property] : null;
465
        }
466
467
        return $this->data;
468
    }
469
470
    /**
471
     * Tells whether the geometry has data with the specified name
472
     *
473
     * @param  string $property The name of the property
474
     * @return bool True if the geometry has data with the specified name, otherwise false.
475
     */
476
    public function hasDataProperty($property): bool
477
    {
478
        return array_key_exists($property, $this->data ?: []);
479
    }
480
481
    /**
482
     * returns the envelope of a geometry or the geometry itself if it is a point
483
     *
484
     * @return \geoPHP\Geometry\Geometry|null
485
     */
486
    public function envelope()
487
    {
488
        if ($this->isEmpty()) {
489
            $type = '\\geoPHP\\Geometry\\' . $this->geometryType();
490
            return new $type();
491
        }
492
        if ($this->geometryType() === Geometry::POINT) {
493
            return $this;
494
        }
495
496
        $geosObj = $this->getGeos();
497
        if (is_object($geosObj)) {
498
            // @codeCoverageIgnoreStart
499
            /** @noinspection PhpUndefinedMethodInspection */
500
            return geoPHP::geosToGeometry($geosObj->envelope());
501
            // @codeCoverageIgnoreEnd
502
        }
503
504
        $boundingBox = $this->getBBox();
505
        
506
        if (empty($boundingBox)) {
507
            return new Polygon([new LineString()]);
508
        }
509
        
510
        $points = [
511
            new Point($boundingBox['maxx'], $boundingBox['miny']),
512
            new Point($boundingBox['maxx'], $boundingBox['maxy']),
513
            new Point($boundingBox['minx'], $boundingBox['maxy']),
514
            new Point($boundingBox['minx'], $boundingBox['miny']),
515
            new Point($boundingBox['maxx'], $boundingBox['miny']),
516
        ];
517
518
        return new Polygon([new LineString($points)]);
519
    }
520
521
    /**
522
     * Public: Non-Standard -- Common to all geometries
523
     * $this->out($format, $other_args);
524
     *
525
     * @return string
526
     */
527
    public function out(): string
528
    {
529
        $args = func_get_args();
530
        $format = strtolower(array_shift($args));
531
532
        // Big Endian WKB
533
        if (strstr($format, 'xdr')) {
534
            $args[] = true;
535
            $format = str_replace('xdr', '', $format);
536
        }
537
538
        $processorType = '\\geoPHP\\Adapter\\' . geoPHP::getAdapterMap()[$format];
539
        $processor = new $processorType;
540
        array_unshift($args, $this);
541
        $result = call_user_func_array([$processor, 'write'], $args);
542
543
        return $result;
544
    }
545
546
    /**
547
     * @return int
548
     */
549
    public function coordinateDimension(): int
550
    {
551
        return 2 + ($this->hasZ() ? 1 : 0) + ($this->isMeasured() ? 1 : 0);
552
    }
553
554
    /**
555
     * Utility function to check if any line segments intersect
556
     * Derived from @source http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
557
     *
558
     * @param  Point $segment1Start
559
     * @param  Point $segment1End
560
     * @param  Point $segment2Start
561
     * @param  Point $segment2End
562
     * @return bool
563
     */
564
    public static function segmentIntersects($segment1Start, $segment1End, $segment2Start, $segment2End): bool
565
    {
566
        $p0x = $segment1Start->getX();
567
        $p0y = $segment1Start->getY();
568
        $p1x = $segment1End->getX();
569
        $p1y = $segment1End->getY();
570
        $p2x = $segment2Start->getX();
571
        $p2y = $segment2Start->getY();
572
        $p3x = $segment2End->getX();
573
        $p3y = $segment2End->getY();
574
575
        $s1x = $p1x - $p0x;
576
        $s1y = $p1y - $p0y;
577
        $s2x = $p3x - $p2x;
578
        $s2y = $p3y - $p2y;
579
580
        $fps = (-$s2x * $s1y) + ($s1x * $s2y);
581
        $fpt = (-$s2x * $s1y) + ($s1x * $s2y);
582
583
        if ($fps == 0 || $fpt == 0) {
584
            return false;
585
        }
586
587
        $s = (-$s1y * ($p0x - $p2x) + $s1x * ($p0y - $p2y)) / $fps;
588
        $t = ($s2x * ($p0y - $p2y) - $s2y * ($p0x - $p2x)) / $fpt;
589
590
        // Return true if collision is detected
591
        return ($s > 0 && $s < 1 && $t > 0 && $t < 1);
592
593
        /*
594
          // x und y is for line 1
595
          // u und v is for line 2
596
          $x1 = $segment1Start->getX();
597
          $y1 = $segment1Start->getY();
598
          $x2 = $segment1End->getX();
599
          $y2 = $segment1End->getY();
600
          $u1 = $segment2Start->getX();
601
          $v1 = $segment2Start->getY();
602
          $u2 = $segment2End->getX();
603
          $v2 = $segment2End->getY();
604
605
          $dx = $x2 - $x1;
606
          $dy = $y2 - $y1;
607
608
          // avoid division with 0
609
          if ($dx != 0 && ($u2 - $u1) != 0) {
610
          $b1 = $dy / $dx;
611
          $b2 = ($v2 - $v1) / ($u2 - $u1);
612
          } else {
613
          $b1 = $b2 = 1;
614
          }
615
616
          // lines with identical inclines cannot cross, but can lie on top of each other.
617
          // So calculation is done with their distances.
618
          if ($b1 == $b2) {
619
          if (($dx * ($v1 - $y1) - ($u1 - $x1) * $dy) == 0) {
620
          return true;
621
          }
622
          // $u1, $v1 are on one line
623
          elseif (($dx * ($v2 - $y1) - ($u2 - $x1) * $dy) == 0) {
624
          return true;
625
          }
626
627
          return false;
628
          }
629
630
          $a1 = $y1 - $b1 * $x1;
631
          $a2 = $v1 - $b2 * $u1;
632
633
          $xi = -($a1 - $a2) / ($b1 - $b2); #(C)
634
          $yi = $a1 + $b1 * $xi;
635
636
          # lines cross at $xi,$yi
637
          if (($x1 - $xi) * ($xi - $x2) >= 0 &&
638
          ($u1 - $xi) * ($xi - $u2) >= 0 &&
639
          ($y1 - $yi) * ($yi - $y2) >= 0 &&
640
          ($v1 - $yi) * ($yi - $v2) >= 0) {
641
          return true;
642
          }
643
644
          return false; */
645
    }
646
647
    // Public: Aliases
648
    // ------------------------------------------------
649
650
    /**
651
     * check if Geometry has Z (altitude) coordinate
652
     *
653
     * @return bool true if geometry has a Z-value
654
     */
655
    abstract public function hasZ(): bool;
656
    
657
    /**
658
     * @return float|null
659
     */
660
    public function getX()
661
    {
662
        return null;
663
        /*throw new UnsupportedMethodException(
664
                        get_called_class() . '::getX',
665
                        null,
666
                        "Geometry has to be a point."
667
        );*/
668
    }
669
670
    /**
671
     * @return float|null
672
     */
673
    public function getY()
674
    {
675
        return null;
676
        /*throw new UnsupportedMethodException(
677
                        get_called_class() . '::getY',
678
                        null,
679
                        "Geometry has to be a point."
680
        );*/
681
    }
682
683
    /**
684
     * @return float|null
685
     */
686
    public function getZ()
687
    {
688
        return null;
689
        /*throw new UnsupportedMethodException(
690
                        get_called_class() . '::getZ',
691
                        null,
692
                        "Geometry has to be a point."
693
        );*/
694
    }
695
696
    /**
697
     * @return float|null
698
     */
699
    public function getM()
700
    {
701
        return null;
702
        /*throw new UnsupportedMethodException(
703
                        get_called_class() . '::getM',
704
                        null,
705
                        "Geometry has to be a point."
706
        );*/
707
    }
708
709
    /**
710
     *
711
     * @return array{'minx'?:float|null, 'miny'?:float|null, 'maxx'?:float|null, 'maxy'?:float|null}
712
     */
713
    public function getBoundingBox(): array
714
    {
715
        return $this->getBBox();
716
    }
717
718
    /**
719
     *
720
     * @return Geometry[]
721
     */
722
    public function dump(): array
723
    {
724
        return $this->getComponents();
725
    }
726
727
    /**
728
     * @return \geoPHP\Geometry\Point
729
     */
730
    abstract public function getCentroid(): Point;
731
732
    /**
733
     * @see Geometry::area()
734
     * @return float
735
     */
736
    abstract public function getArea(): float;
737
738
    /**
739
     * Returns the length of this Geometry.
740
     * Linear geometries return their length. Areal geometries return their perimeter. Others return 0.0
741
     *
742
     * @return float
743
     */
744
    public function getLength(): float
745
    {
746
        return 0.0;
747
    }
748
749
    /**
750
     * @see Geometry::getGeos()
751
     * @return \GEOSGeometry|false
752
     */
753
    public function geos()
754
    {
755
        return $this->getGeos();
756
    }
757
758
    /**
759
     *
760
     * @return string
761
     */
762
    public function getGeomType(): string
763
    {
764
        return $this->geometryType();
765
    }
766
767
    /**
768
     *
769
     * @return int|null
770
     */
771
    public function getSRID()
772
    {
773
        return $this->srid;
774
    }
775
776
    /**
777
     *
778
     * @return string
779
     */
780
    public function asText(): string
781
    {
782
        return $this->out('wkt');
783
    }
784
785
    /**
786
     *
787
     * @return string
788
     */
789
    public function asBinary(): string
790
    {
791
        return $this->out('wkb');
792
    }
793
794
    /*     * **************************************
795
     * Public: GEOS Only Functions  *
796
     * ************************************* */
797
798
    /**
799
     * Returns the GEOS representation of Geometry if GEOS is installed
800
     *
801
     * @return             \GEOSGeometry|false
802
     * @codeCoverageIgnore
803
     */
804
    public function getGeos()
805
    {
806
        // If it's already been set, just return it
807
        if (isset($this->geos)) {
808
            return $this->geos;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->geos also could return the type boolean which is incompatible with the documented return type GEOSGeometry|false.
Loading history...
809
        }
810
811
        // It hasn't been set yet, generate it
812
        if (geoPHP::geosInstalled()) {
813
            /** @noinspection PhpUndefinedClassInspection */
814
            // Attention: EMPTY Points are not supported in WKB!
815
            $reader = new \GEOSWKBReader();
816
            /** @noinspection PhpUndefinedMethodInspection */
817
            $this->geos = $reader->read($this->out('wkb'));
818
        } else {
819
            $this->geos = false;
820
        }
821
822
        return $this->geos;
823
    }
824
825
    /**
826
     * @param \GEOSGeometry|null $geos
827
     * @return void
828
     */
829
    public function setGeos($geos = null)
830
    {
831
        $this->geos = $geos;
832
    }
833
834
    /**
835
     * @return             Geometry|Point|null
836
     * @throws             UnsupportedMethodException
837
     * @codeCoverageIgnore
838
     */
839
    public function pointOnSurface()
840
    {
841
        if ($this->isEmpty()) {
842
            return new Point();
843
        }
844
        $geosObj = $this->getGeos();
845
        if (is_object($geosObj)) {
846
            /** @noinspection PhpUndefinedMethodInspection */
847
            return geoPHP::geosToGeometry($geosObj->pointOnSurface());
848
        }
849
        // help for implementation: http://gis.stackexchange.com/questions/76498/how-is-st-pointonsurface-calculated
850
        throw UnsupportedMethodException::geos(__METHOD__);
851
    }
852
853
    /**
854
     * @param              Geometry $geometry
855
     * @return             bool
856
     * @throws             UnsupportedMethodException
857
     * @codeCoverageIgnore
858
     */
859
    public function equalsExact(Geometry $geometry)
860
    {
861
        $geosObj = $this->getGeos();
862
        if (is_object($geosObj)) {
863
            /** @noinspection PhpUndefinedMethodInspection */
864
            $geosObj2 = $geometry->getGeos();
865
            return is_object($geosObj2) ? $geosObj->equalsExact($geosObj2) : false;
866
        }
867
        throw UnsupportedMethodException::geos(__METHOD__);
868
    }
869
870
    /**
871
     * @param              Geometry    $geometry
872
     * @param              string|null $pattern
873
     * @return             string|null
874
     * @throws             UnsupportedMethodException
875
     * @codeCoverageIgnore
876
     */
877
    public function relate(Geometry $geometry, $pattern = null)
878
    {
879
        $geosObj = $this->getGeos();
880
        if (is_object($geosObj)) {
881
            $geosObj2 = $geometry->getGeos();
882
            if ($pattern !== null) {
883
                /** @noinspection PhpUndefinedMethodInspection */
884
                return is_object($geosObj2) ? $geosObj->relate($geosObj2, $pattern) : null;
0 ignored issues
show
Bug Best Practice introduced by
The expression return is_object($geosOb...sObj2, $pattern) : null also could return the type true which is incompatible with the documented return type null|string.
Loading history...
885
            } else {
886
                /** @noinspection PhpUndefinedMethodInspection */
887
                return is_object($geosObj2) ? $geosObj->relate($geosObj2) : null;
0 ignored issues
show
Bug Best Practice introduced by
The expression return is_object($geosOb...elate($geosObj2) : null also could return the type true which is incompatible with the documented return type null|string.
Loading history...
888
            }
889
        }
890
        throw UnsupportedMethodException::geos(__METHOD__);
891
    }
892
893
    /**
894
     * @return array<string,mixed> ['valid' => (bool)..., 'reason' => (string)...]
895
     * @throws UnsupportedMethodException
896
     * @codeCoverageIgnore
897
     */
898
    public function checkValidity(): array
899
    {
900
        $geosObj = $this->getGeos();
901
        if (is_object($geosObj)) {
902
            /** @noinspection PhpUndefinedMethodInspection */
903
            return $geosObj->checkValidity();
904
        }
905
        throw UnsupportedMethodException::geos(__METHOD__);
906
    }
907
908
    /**
909
     * @return             bool
910
     * @throws             UnsupportedMethodException
911
     * @codeCoverageIgnore
912
     */
913
    public function isValid(): bool
914
    {
915
        $geosObj = $this->getGeos();
916
        if (is_object($geosObj)) {
917
            /** @noinspection PhpUndefinedMethodInspection */
918
            return $geosObj->checkValidity()['valid'];
919
        }
920
        throw UnsupportedMethodException::geos(__METHOD__);
921
    }
922
923
    /**
924
     * @param              float|int $distance
925
     * @return             Geometry|null
926
     * @throws             UnsupportedMethodException
927
     * @codeCoverageIgnore
928
     */
929
    public function buffer($distance)
930
    {
931
        $geosObj = $this->getGeos();
932
        if (is_object($geosObj)) {
933
            /** @noinspection PhpUndefinedMethodInspection */
934
            return geoPHP::geosToGeometry($geosObj->buffer($distance));
935
        }
936
        throw UnsupportedMethodException::geos(__METHOD__);
937
    }
938
939
    /**
940
     * @param              Geometry $geometry
941
     * @return             Geometry|null
942
     * @throws             UnsupportedMethodException
943
     * @codeCoverageIgnore
944
     */
945
    public function intersection(Geometry $geometry)
946
    {
947
        $geosObj = $this->getGeos();
948
        if (is_object($geosObj)) {
949
            $geosObj2 = $geometry->getGeos();
950
            /** @noinspection PhpUndefinedMethodInspection */
951
            return is_object($geosObj2) ? geoPHP::geosToGeometry($geosObj->intersection($geosObj2)) : null;
952
        }
953
        throw UnsupportedMethodException::geos(__METHOD__);
954
    }
955
956
    /**
957
     * @param              int|float $x1
958
     * @param              int|float $y1
959
     * @param              int|float $x2
960
     * @param              int|float $y2
961
     * @return             Geometry|null
962
     * @throws             UnsupportedMethodException
963
     * @codeCoverageIgnore
964
     */
965
    public function clipByRect($x1, $y1, $x2, $y2)
966
    {
967
        $geosObj = $this->getGeos();
968
        if (is_object($geosObj)) {
969
            /** @noinspection PhpUndefinedMethodInspection */
970
            return geoPHP::geosToGeometry($geosObj->clipByRect($x1, $y1, $x2, $y2));
971
        }
972
        throw UnsupportedMethodException::geos(__METHOD__);
973
    }
974
975
    /**
976
     * @return             Geometry|null
977
     * @throws             UnsupportedMethodException
978
     * @codeCoverageIgnore
979
     */
980
    public function convexHull()
981
    {
982
        $geosObj = $this->getGeos();
983
        if (is_object($geosObj)) {
984
            /** @noinspection PhpUndefinedMethodInspection */
985
            return geoPHP::geosToGeometry($geosObj->convexHull());
986
        }
987
        throw UnsupportedMethodException::geos(__METHOD__);
988
    }
989
990
    /**
991
     * @param              float|int  $tolerance snapping tolerance to use for improved robustness
992
     * @param              bool|false $onlyEdges if true will return a MULTILINESTRING, otherwise (the default) it will return a
993
     *                                           GEOMETRYCOLLECTION containing triangular POLYGONs.
994
     * @return             Geometry|null
995
     * @throws             UnsupportedMethodException
996
     * @codeCoverageIgnore
997
     */
998
    public function delaunayTriangulation($tolerance = 0.0, bool $onlyEdges = false)
999
    {
1000
        $geosObj = $this->getGeos();
1001
        if (is_object($geosObj)) {
1002
            /** @noinspection PhpUndefinedMethodInspection */
1003
            return geoPHP::geosToGeometry($geosObj->delaunayTriangulation($tolerance, $onlyEdges));
1004
        }
1005
        throw UnsupportedMethodException::geos(__METHOD__);
1006
    }
1007
1008
    /**
1009
     * @param              float|int  $tolerance snapping tolerance to use for improved robustness
1010
     * @param              bool|false $onlyEdges if true will return a MULTILINESTRING, otherwise (the default) it will return a
1011
     *                                           GEOMETRYCOLLECTION containing POLYGONs.
1012
     * @return             Geometry|null
1013
     * @throws             UnsupportedMethodException
1014
     * @codeCoverageIgnore
1015
     */
1016
    public function voronoiDiagram($tolerance = 0.0, bool $onlyEdges = false)
1017
    {
1018
        $geosObj = $this->getGeos();
1019
        if (is_object($geosObj)) {
1020
            /** @noinspection PhpUndefinedMethodInspection */
1021
            return geoPHP::geosToGeometry($geosObj->voronoiDiagram($tolerance, $onlyEdges));
1022
        }
1023
        throw UnsupportedMethodException::geos(__METHOD__);
1024
    }
1025
1026
    /**
1027
     * @param              Geometry $geometry
1028
     * @return             Geometry|null
1029
     * @throws             UnsupportedMethodException
1030
     * @codeCoverageIgnore
1031
     */
1032
    public function difference(Geometry $geometry)
1033
    {
1034
        $geosObj = $this->getGeos();
1035
        if (is_object($geosObj)) {
1036
            /** @noinspection PhpUndefinedMethodInspection */
1037
            return geoPHP::geosToGeometry($geosObj->difference($geometry->getGeos()));
1038
        }
1039
        throw UnsupportedMethodException::geos(__METHOD__);
1040
    }
1041
1042
    /**
1043
     * Snaps the vertices and segments of a geometry to another Geometry's vertices. A snap distance tolerance is used
1044
     * to control where snapping is performed. The result geometry is the input geometry with the vertices snapped.
1045
     * If no snapping occurs then the input geometry is returned unchanged.
1046
     *
1047
     * Snapping one geometry to another can improve robustness for overlay operations by eliminating nearly-coincident
1048
     * edges (which cause problems during noding and intersection calculation).
1049
     *
1050
     * Too much snapping can result in invalid topology being created, so the number and location of snapped vertices
1051
     * is decided using heuristics to determine when it is safe to snap. This can result in some potential snaps being
1052
     * omitted, however.
1053
     *
1054
     * @param              Geometry $geometry
1055
     * @param              float    $snapTolerance
1056
     * @return             Geometry|null
1057
     * @throws             UnsupportedMethodException
1058
     * @codeCoverageIgnore
1059
     */
1060
    public function snapTo(Geometry $geometry, $snapTolerance = 0.0)
1061
    {
1062
        $geosObj = $this->getGeos();
1063
        if (is_object($geosObj)) {
1064
            /** @noinspection PhpUndefinedMethodInspection */
1065
            return geoPHP::geosToGeometry($geosObj->snapTo($geometry->getGeos(), $snapTolerance));
1066
        }
1067
        throw UnsupportedMethodException::geos(__METHOD__);
1068
    }
1069
1070
    /**
1071
     * @param              Geometry $geometry
1072
     * @return             Geometry|null
1073
     * @throws             UnsupportedMethodException
1074
     * @codeCoverageIgnore
1075
     */
1076
    public function symDifference(Geometry $geometry)
1077
    {
1078
        $geosObj = $this->getGeos();
1079
        if (is_object($geosObj)) {
1080
            /** @noinspection PhpUndefinedMethodInspection */
1081
            return geoPHP::geosToGeometry($geosObj->symDifference($geometry->getGeos()));
1082
        }
1083
        throw UnsupportedMethodException::geos(__METHOD__);
1084
    }
1085
1086
    /**
1087
     * Can pass in a geometry or an array of geometries
1088
     *
1089
     * @param              Geometry|Geometry[] $geometry
1090
     * @return             Geometry|GeometryCollection|null
1091
     * @throws             UnsupportedMethodException
1092
     * @codeCoverageIgnore
1093
     */
1094
    public function union($geometry)
1095
    {
1096
        $geom = $this->getGeos();
1097
        if (is_object($geom)) {
1098
            if (is_array($geometry)) {
1099
                foreach ($geometry as $item) {
1100
                    /** @noinspection PhpUndefinedMethodInspection */
1101
                    $geom = $geom->union($item->geos());
1102
                }
1103
                return geoPHP::geosToGeometry($geom);
1104
            } else {
1105
                /** @noinspection PhpUndefinedMethodInspection */
1106
                return geoPHP::geosToGeometry($geom->union($geometry->getGeos()));
1107
            }
1108
        }
1109
        throw UnsupportedMethodException::geos(__METHOD__);
1110
    }
1111
1112
    /**
1113
     * @param              float      $tolerance
1114
     * @param              bool|false $preserveTopology
1115
     * @return             Geometry|null
1116
     * @throws             UnsupportedMethodException
1117
     * @codeCoverageIgnore
1118
     */
1119
    public function simplify($tolerance, $preserveTopology = false)
1120
    {
1121
        $geosObj = $this->getGeos();
1122
        if (is_object($geosObj)) {
1123
            /** @noinspection PhpUndefinedMethodInspection */
1124
            return geoPHP::geosToGeometry($geosObj->simplify($tolerance, $preserveTopology));
1125
        }
1126
        throw UnsupportedMethodException::geos(__METHOD__);
1127
    }
1128
1129
    /**
1130
     * @param int|float $dx
1131
     * @param int|float $dy
1132
     * @param int|float $dz
1133
     * @return void
1134
     */
1135
    abstract public function translate($dx = 0, $dy = 0, $dz = 0);
1136
1137
    /**
1138
     * The function attempts to create a valid representation of a given invalid geometry without losing any of the
1139
     * input vertices. Already-valid geometries are returned without further intervention.
1140
     * Supported inputs are: POINTS, MULTIPOINTS, LINESTRINGS, MULTILINESTRINGS, POLYGONS, MULTIPOLYGONS and
1141
     * GEOMETRYCOLLECTIONS containing any mix of them.
1142
     * In case of full or partial dimensional collapses, the output geometry may be a collection of lower-to-equal
1143
     * dimension geometries or a geometry of lower dimension. Single polygons may become multi-geometries in case of
1144
     * self-intersections.
1145
     *
1146
     * @return             Geometry|null
1147
     * @throws             UnsupportedMethodException
1148
     * @codeCoverageIgnore
1149
     */
1150
//    public function makeValid()
1151
//    {
1152
//        $geosObj = $this->getGeos();
1153
//        if (is_object($geosObj)) {
1154
//            /** @noinspection PhpUndefinedMethodInspection */
1155
//            return geoPHP::geosToGeometry($geosObj->makeValid());
1156
//        }
1157
//        throw UnsupportedMethodException::geos(__METHOD__);
1158
//    }
1159
1160
    /**
1161
     * Creates an areal geometry formed by the constituent linework of given geometry. The return type can be a
1162
     * Polygon or MultiPolygon, depending on input. If the input lineworks do not form polygons NULL is returned.
1163
     * The inputs can be LINESTRINGS, MULTILINESTRINGS, POLYGONS, MULTIPOLYGONS and GeometryCollections.
1164
     *
1165
     * @return             Geometry|null
1166
     * @throws             UnsupportedMethodException
1167
     * @codeCoverageIgnore
1168
     */
1169
//    public function buildArea()
1170
//    {
1171
//        $geosObj = $this->getGeos();
1172
//        if (is_object($geosObj)) {
1173
//            /** @noinspection PhpUndefinedMethodInspection */
1174
//            return geoPHP::geosToGeometry($geosObj->buildArea());
1175
//        }
1176
//        throw UnsupportedMethodException::geos(__METHOD__);
1177
//    }
1178
1179
    /**
1180
     * @param              Geometry $geometry
1181
     * @return             bool
1182
     * @throws             UnsupportedMethodException
1183
     * @codeCoverageIgnore
1184
     */
1185
    public function disjoint(Geometry $geometry)
1186
    {
1187
        $geosObj = $this->getGeos();
1188
        if (is_object($geosObj)) {
1189
            /** @noinspection PhpUndefinedMethodInspection */
1190
            return $geosObj->disjoint($geometry->getGeos());
1191
        }
1192
        throw UnsupportedMethodException::geos(__METHOD__);
1193
    }
1194
1195
    /**
1196
     * @param              Geometry $geometry
1197
     * @return             bool
1198
     * @throws             UnsupportedMethodException
1199
     * @codeCoverageIgnore
1200
     */
1201
    public function touches(Geometry $geometry)
1202
    {
1203
        $geosObj = $this->getGeos();
1204
        if (is_object($geosObj)) {
1205
            /** @noinspection PhpUndefinedMethodInspection */
1206
            return $geosObj->touches($geometry->getGeos());
1207
        }
1208
        throw UnsupportedMethodException::geos(__METHOD__);
1209
    }
1210
1211
    /**
1212
     * @param              Geometry $geometry
1213
     * @return             bool
1214
     * @throws             UnsupportedMethodException
1215
     * @codeCoverageIgnore
1216
     */
1217
    public function intersects(Geometry $geometry)
1218
    {
1219
        $geosObj = $this->getGeos();
1220
        if (is_object($geosObj)) {
1221
            /** @noinspection PhpUndefinedMethodInspection */
1222
            return $geosObj->intersects($geometry->getGeos());
1223
        }
1224
        throw UnsupportedMethodException::geos(__METHOD__);
1225
    }
1226
1227
    /**
1228
     * @param              Geometry $geometry
1229
     * @return             bool
1230
     * @throws             UnsupportedMethodException
1231
     * @codeCoverageIgnore
1232
     */
1233
    public function crosses(Geometry $geometry)
1234
    {
1235
        $geosObj = $this->getGeos();
1236
        if (is_object($geosObj)) {
1237
            /** @noinspection PhpUndefinedMethodInspection */
1238
            return $geosObj->crosses($geometry->getGeos());
1239
        }
1240
        throw UnsupportedMethodException::geos(__METHOD__);
1241
    }
1242
1243
    /**
1244
     * @param              Geometry $geometry
1245
     * @return             bool
1246
     * @throws             UnsupportedMethodException
1247
     * @codeCoverageIgnore
1248
     */
1249
    public function within(Geometry $geometry)
1250
    {
1251
        $geosObj = $this->getGeos();
1252
        if (is_object($geosObj)) {
1253
            /** @noinspection PhpUndefinedMethodInspection */
1254
            return $geosObj->within($geometry->getGeos());
1255
        }
1256
        throw UnsupportedMethodException::geos(__METHOD__);
1257
    }
1258
1259
    /**
1260
     * @param              Geometry $geometry
1261
     * @return             bool
1262
     * @throws             UnsupportedMethodException
1263
     * @codeCoverageIgnore
1264
     */
1265
    public function contains(Geometry $geometry)
1266
    {
1267
        $geosObj = $this->getGeos();
1268
        if (is_object($geosObj)) {
1269
            /** @noinspection PhpUndefinedMethodInspection */
1270
            return $geosObj->contains($geometry->getGeos());
1271
        }
1272
        throw UnsupportedMethodException::geos(get_called_class() . '::contains');
1273
    }
1274
1275
    /**
1276
     * @param              Geometry $geometry
1277
     * @return             bool
1278
     * @throws             UnsupportedMethodException
1279
     * @codeCoverageIgnore
1280
     */
1281
    public function overlaps(Geometry $geometry)
1282
    {
1283
        $geosObj = $this->getGeos();
1284
        if (is_object($geosObj)) {
1285
            /** @noinspection PhpUndefinedMethodInspection */
1286
            return $geosObj->overlaps($geometry->getGeos());
1287
        }
1288
        throw UnsupportedMethodException::geos(get_called_class() . '::overlaps');
1289
    }
1290
1291
    /**
1292
     * @param              Geometry $geometry
1293
     * @return             bool
1294
     * @throws             UnsupportedMethodException
1295
     * @codeCoverageIgnore
1296
     */
1297
    public function covers(Geometry $geometry)
1298
    {
1299
        $geosObj = $this->getGeos();
1300
        if (is_object($geosObj)) {
1301
            /** @noinspection PhpUndefinedMethodInspection */
1302
            return $geosObj->covers($geometry->getGeos());
1303
        }
1304
        throw UnsupportedMethodException::geos(get_called_class() . '::covers');
1305
    }
1306
1307
    /**
1308
     * @param              Geometry $geometry
1309
     * @return             bool
1310
     * @throws             UnsupportedMethodException
1311
     * @codeCoverageIgnore
1312
     */
1313
    public function coveredBy(Geometry $geometry)
1314
    {
1315
        $geosObj = $this->getGeos();
1316
        if (is_object($geosObj)) {
1317
            /** @noinspection PhpUndefinedMethodInspection */
1318
            return $geosObj->coveredBy($geometry->getGeos());
1319
        }
1320
        throw UnsupportedMethodException::geos(get_called_class() . '::coveredBy');
1321
    }
1322
1323
    /**
1324
     * @param              Geometry $geometry
1325
     * @return             float
1326
     * @throws             UnsupportedMethodException
1327
     * @codeCoverageIgnore
1328
     */
1329
    public function hausdorffDistance(Geometry $geometry)
1330
    {
1331
        $geosObj = $this->getGeos();
1332
        if (is_object($geosObj)) {
1333
            /** @noinspection PhpUndefinedMethodInspection */
1334
            return $geosObj->hausdorffDistance($geometry->getGeos());
1335
        }
1336
        throw UnsupportedMethodException::geos(get_called_class() . '::hausdorffDistance');
1337
    }
1338
1339
    /**
1340
     * @param float|int $distance
1341
     * @param array{quad_segs?:int|float,join?:int,mitre_limit?:int|float} $styleArray
1342
     * styleArray keys supported:
1343
     * - 'quad_segs'
1344
     *       (integer) Number of segments used to approximate a quarter circle (defaults to 8).
1345
     * - 'join'
1346
     *       (float) Join style (defaults to GEOSBUF_JOIN_ROUND)
1347
     * - 'mitre_limit'
1348
     *       (float) mitre ratio limit (only affects joins with GEOSBUF_JOIN_MITRE style)
1349
     *       'miter_limit' is also accepted as a synonym for 'mitre_limit'.
1350
     *
1351
     * @return             Geometry|null
1352
     * @throws             UnsupportedMethodException
1353
     * @codeCoverageIgnore
1354
     */
1355
    public function offsetCurve($distance = 0.0, array $styleArray = [])
1356
    {
1357
        $geosObj = $this->getGeos();
1358
        if (is_object($geosObj)) {
1359
            /** @noinspection PhpUndefinedMethodInspection */
1360
            return geoPHP::geosToGeometry($geosObj->offsetCurve($distance, $styleArray));
1361
        }
1362
        throw UnsupportedMethodException::geos(__METHOD__);
1363
    }
1364
1365
    /**
1366
     * @param              Geometry $point
1367
     * @return             \GEOSGeometry
1368
     * @throws             UnsupportedMethodException
1369
     * @codeCoverageIgnore
1370
     */
1371
    public function project(Geometry $point)
1372
    {
1373
        $geosObj = $this->getGeos();
1374
        if (is_object($geosObj)) {
1375
            /** @noinspection PhpUndefinedMethodInspection */
1376
            return $geosObj->project($point->getGeos());
1377
        }
1378
        throw UnsupportedMethodException::geos(__METHOD__);
1379
    }
1380
}
1381