1<?php
2
3namespace geoPHP\Geometry;
4
5use geoPHP\geoPHP;
6
7/**
8 * MultiGeometry is an abstract collection of geometries
9 *
10 * @package geoPHP\Geometry
11 */
12abstract class MultiGeometry extends Collection
13{
14
15    public function __construct($components = [], $allowEmptyComponents = true, $allowedComponentType = Geometry::class)
16    {
17        parent::__construct($components, $allowEmptyComponents, $allowedComponentType);
18    }
19
20    /**
21     * @return bool|null
22     */
23    public function isSimple()
24    {
25        if ($this->getGeos()) {
26            // @codeCoverageIgnoreStart
27            /** @noinspection PhpUndefinedMethodInspection */
28            return $this->getGeos()->isSimple();
29            // @codeCoverageIgnoreEnd
30        }
31
32        // A collection is simple if all it's components are simple
33        foreach ($this->components as $component) {
34            if (!$component->isSimple()) {
35                return false;
36            }
37        }
38
39        return true;
40    }
41
42    // By default, the boundary of a collection is the boundary of it's components
43    public function boundary()
44    {
45        if ($this->isEmpty()) {
46            return new LineString();
47        }
48
49        if ($this->getGeos()) {
50            // @codeCoverageIgnoreStart
51            /** @noinspection PhpUndefinedMethodInspection */
52            return $this->getGeos()->boundary();
53            // @codeCoverageIgnoreEnd
54        }
55
56        $componentsBoundaries = [];
57        foreach ($this->components as $component) {
58            $componentsBoundaries[] = $component->boundary();
59        }
60        return geoPHP::buildGeometry($componentsBoundaries);
61    }
62
63    public function area()
64    {
65        if ($this->getGeos()) {
66            // @codeCoverageIgnoreStart
67            /** @noinspection PhpUndefinedMethodInspection */
68            return $this->getGeos()->area();
69            // @codeCoverageIgnoreEnd
70        }
71
72        $area = 0;
73        foreach ($this->components as $component) {
74            $area += $component->area();
75        }
76        return $area;
77    }
78
79    /**
80     *  Returns the length of this Collection in its associated spatial reference.
81     * Eg. if Geometry is in geographical coordinate system it returns the length in degrees
82     * @return float|int
83     */
84    public function length()
85    {
86        $length = 0;
87        foreach ($this->components as $component) {
88            $length += $component->length();
89        }
90        return $length;
91    }
92
93    public function length3D()
94    {
95        $length = 0;
96        foreach ($this->components as $component) {
97            $length += $component->length3D();
98        }
99        return $length;
100    }
101
102    /**
103     * Returns the degree based Geometry' length in meters
104     * @param float|null $radius Default is the semi-major axis of WGS84
105     * @return int the length in meters
106     */
107    public function greatCircleLength($radius = geoPHP::EARTH_WGS84_SEMI_MAJOR_AXIS)
108    {
109        $length = 0;
110        foreach ($this->components as $component) {
111            $length += $component->greatCircleLength($radius);
112        }
113        return $length;
114    }
115
116    public function haversineLength()
117    {
118        $length = 0;
119        foreach ($this->components as $component) {
120            $length += $component->haversineLength();
121        }
122        return $length;
123    }
124
125    public function minimumZ()
126    {
127        $min = PHP_INT_MAX;
128        foreach ($this->components as $component) {
129            $componentMin = $component->minimumZ();
130            if ($componentMin < $min) {
131                $min = $componentMin;
132            }
133        }
134        return $min < PHP_INT_MAX ? $min : null;
135    }
136
137    public function maximumZ()
138    {
139        $max = ~PHP_INT_MAX;
140        foreach ($this->components as $component) {
141            $componentMax = $component->maximumZ();
142            if ($componentMax > $max) {
143                $max = $componentMax;
144            }
145        }
146        return $max > ~PHP_INT_MAX ? $max : null;
147    }
148
149    public function zDifference()
150    {
151        $startPoint = $this->startPoint();
152        $endPoint = $this->endPoint();
153        if ($startPoint && $endPoint && $startPoint->hasZ() && $endPoint->hasZ()) {
154            return abs($startPoint->z() - $endPoint->z());
155        } else {
156            return null;
157        }
158    }
159
160    public function elevationGain($verticalTolerance = 0)
161    {
162        $gain = null;
163        foreach ($this->components as $component) {
164            $gain += $component->elevationGain($verticalTolerance);
165        }
166        return $gain;
167    }
168
169    public function elevationLoss($verticalTolerance = 0)
170    {
171        $loss = null;
172        foreach ($this->components as $component) {
173            $loss += $component->elevationLoss($verticalTolerance);
174        }
175        return $loss;
176    }
177
178    public function minimumM()
179    {
180        $min = PHP_INT_MAX;
181        foreach ($this->components as $component) {
182            $componentMin = $component->minimumM();
183            if ($componentMin < $min) {
184                $min = $componentMin;
185            }
186        }
187        return $min < PHP_INT_MAX ? $min : null;
188    }
189
190    public function maximumM()
191    {
192        $max = ~PHP_INT_MAX;
193        foreach ($this->components as $component) {
194            $componentMax = $component->maximumM();
195            if ($componentMax > $max) {
196                $max = $componentMax;
197            }
198        }
199        return $max > ~PHP_INT_MAX ? $max : null;
200    }
201
202
203
204    public function startPoint()
205    {
206        return null;
207    }
208
209    public function endPoint()
210    {
211        return null;
212    }
213
214    public function isRing()
215    {
216        return null;
217    }
218
219    public function isClosed()
220    {
221        return null;
222    }
223
224    public function pointN($n)
225    {
226        return null;
227    }
228
229    public function exteriorRing()
230    {
231        return null;
232    }
233
234    public function numInteriorRings()
235    {
236        return null;
237    }
238
239    public function interiorRingN($n)
240    {
241        return null;
242    }
243}
244