• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

brick / geo / 17334945423

29 Aug 2025 09:48PM UTC coverage: 50.432%. First build
17334945423

push

github

BenMorel
Apply ECS

203 of 232 new or added lines in 29 files covered. (87.5%)

1867 of 3702 relevant lines covered (50.43%)

1140.21 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.77
/src/GeometryCollection.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Brick\Geo;
6

7
use ArrayIterator;
8
use Brick\Geo\Attribute\NoProxy;
9
use Brick\Geo\Exception\CoordinateSystemException;
10
use Brick\Geo\Exception\NoSuchGeometryException;
11
use Brick\Geo\Exception\UnexpectedGeometryException;
12
use Brick\Geo\Projector\Projector;
13
use Countable;
14
use IteratorAggregate;
15
use Override;
16

17
use function array_map;
18
use function array_values;
19
use function count;
20
use function sprintf;
21

22
/**
23
 * A GeometryCollection is a geometric object that is a collection of some number of geometric objects.
24
 *
25
 * All the elements in a GeometryCollection shall be in the same Spatial Reference System. This is also the Spatial
26
 * Reference System for the GeometryCollection.
27
 *
28
 * GeometryCollection places no other constraints on its elements. Subclasses of GeometryCollection may restrict
29
 * membership based on dimension and may also place other constraints on the degree of spatial overlap between
30
 * elements.
31
 *
32
 * By the nature of digital representations, collections are inherently ordered by the underlying storage mechanism.
33
 * Two collections whose difference is only this order are spatially equal and will return equivalent results in any
34
 * geometric-defined operations.
35
 *
36
 * @template T of Geometry
37
 *
38
 * @template-implements IteratorAggregate<int<0, max>, T>
39
 */
40
class GeometryCollection extends Geometry implements Countable, IteratorAggregate
41
{
42
    /**
43
     * The geometries that compose this GeometryCollection.
44
     *
45
     * This array can be empty.
46
     *
47
     * @var list<T>
48
     */
49
    protected array $geometries = [];
50

51
    /**
52
     * @param T ...$geometries
53
     *
54
     * @throws CoordinateSystemException   If different coordinate systems are used.
55
     * @throws UnexpectedGeometryException If a geometry is not a valid type for a subclass of GeometryCollection.
56
     */
57
    public function __construct(CoordinateSystem $cs, Geometry ...$geometries)
58
    {
59
        $isEmpty = true;
8,938✔
60

61
        foreach ($geometries as $geometry) {
8,938✔
62
            if (! $geometry->isEmpty()) {
5,753✔
63
                $isEmpty = false;
5,665✔
64

65
                break;
5,665✔
66
            }
67
        }
68

69
        parent::__construct($cs, $isEmpty);
8,938✔
70

71
        if (! $geometries) {
8,938✔
72
            return;
3,241✔
73
        }
74

75
        CoordinateSystem::check($this, ...$geometries);
5,753✔
76

77
        $containedGeometryType = $this->containedGeometryType();
5,753✔
78

79
        foreach ($geometries as $geometry) {
5,753✔
80
            /**
81
             * @psalm-suppress DocblockTypeContradiction We do want to enforce this in code, as not everyone uses static analysis!
82
             * @psalm-suppress MixedArgument It looks like due to this check, Psalm considers that $geometry no longer has a type.
83
             */
84
            if (! $geometry instanceof $containedGeometryType) {
5,753✔
85
                throw new UnexpectedGeometryException(sprintf(
×
86
                    '%s expects instance of %s, instance of %s given.',
×
87
                    static::class,
×
88
                    $containedGeometryType,
×
NEW
89
                    $geometry::class,
×
90
                ));
×
91
            }
92
        }
93

94
        $this->geometries = array_values($geometries);
5,753✔
95
    }
96

97
    /**
98
     * Creates a non-empty GeometryCollection composed of the given geometries.
99
     *
100
     * @param Geometry $geometry1    The first geometry.
101
     * @param Geometry ...$geometryN The subsequent geometries, if any.
102
     *
103
     * @return static
104
     *
105
     * @throws CoordinateSystemException   If the geometries use different coordinate systems.
106
     * @throws UnexpectedGeometryException If a geometry is not a valid type for a subclass of GeometryCollection.
107
     *
108
     * @psalm-suppress UnsafeInstantiation
109
     */
110
    public static function of(Geometry $geometry1, Geometry ...$geometryN): GeometryCollection
111
    {
112
        return new static($geometry1->coordinateSystem(), $geometry1, ...$geometryN);
249✔
113
    }
114

115
    /**
116
     * Returns the number of geometries in this GeometryCollection.
117
     */
118
    public function numGeometries(): int
119
    {
120
        return count($this->geometries);
4,025✔
121
    }
122

123
    /**
124
     * Returns the specified geometry N in this GeometryCollection.
125
     *
126
     * @param int $n The geometry number, 1-based.
127
     *
128
     * @return T
129
     *
130
     * @throws NoSuchGeometryException If there is no Geometry at this index.
131
     */
132
    public function geometryN(int $n): Geometry
133
    {
134
        if (! isset($this->geometries[$n - 1])) {
144✔
135
            throw new NoSuchGeometryException('There is no Geometry in this GeometryCollection at index ' . $n);
96✔
136
        }
137

138
        return $this->geometries[$n - 1];
48✔
139
    }
140

141
    /**
142
     * Returns the geometries that compose this GeometryCollection.
143
     *
144
     * @return list<T>
145
     */
146
    public function geometries(): array
147
    {
148
        return $this->geometries;
96✔
149
    }
150

151
    #[NoProxy, Override]
152
    public function geometryType(): string
153
    {
154
        return 'GeometryCollection';
974✔
155
    }
156

157
    #[NoProxy, Override]
158
    public function geometryTypeBinary(): int
159
    {
160
        return Geometry::GEOMETRYCOLLECTION;
454✔
161
    }
162

163
    #[Override]
164
    public function dimension(): int
165
    {
166
        $dimension = 0;
64✔
167

168
        foreach ($this->geometries as $geometry) {
64✔
169
            $dim = $geometry->dimension();
32✔
170

171
            if ($dim > $dimension) {
32✔
172
                $dimension = $dim;
24✔
173
            }
174
        }
175

176
        return $dimension;
64✔
177
    }
178

179
    #[Override]
180
    public function getBoundingBox(): BoundingBox
181
    {
182
        $boundingBox = BoundingBox::new();
×
183

184
        foreach ($this->geometries as $geometry) {
×
185
            $boundingBox = $boundingBox->extendedWithBoundingBox($geometry->getBoundingBox());
×
186
        }
187

188
        return $boundingBox;
×
189
    }
190

191
    #[Override]
192
    public function toArray(): array
193
    {
194
        return array_map(
400✔
195
            fn (Geometry $geometry) => $geometry->toArray(),
400✔
196
            $this->geometries,
400✔
197
        );
400✔
198
    }
199

200
    #[Override]
201
    public function project(Projector $projector): GeometryCollection
202
    {
203
        return new GeometryCollection(
32✔
204
            $projector->getTargetCoordinateSystem($this->coordinateSystem),
32✔
205
            ...array_map(
32✔
206
                fn (Geometry $geometry) => $geometry->project($projector),
32✔
207
                $this->geometries,
32✔
208
            ),
32✔
209
        );
32✔
210
    }
211

212
    /**
213
     * Returns the number of geometries in this GeometryCollection.
214
     */
215
    #[Override]
216
    public function count(): int
217
    {
218
        return count($this->geometries);
2,413✔
219
    }
220

221
    /**
222
     * Returns an iterator for the geometries in this GeometryCollection.
223
     *
224
     * @return ArrayIterator<int<0, max>, T>
225
     */
226
    #[Override]
227
    public function getIterator(): ArrayIterator
228
    {
229
        return new ArrayIterator($this->geometries);
5,044✔
230
    }
231

232
    /**
233
     * Returns a copy of this GeometryCollection, with the given geometries added.
234
     *
235
     * @param T ...$geometries
236
     *
237
     * @return GeometryCollection<T>
238
     *
239
     * @psalm-suppress UnsafeInstantiation
240
     */
241
    public function withAddedGeometries(Geometry ...$geometries): GeometryCollection
242
    {
243
        return new static($this->coordinateSystem, ...$this->geometries, ...$geometries);
168✔
244
    }
245

246
    /**
247
     * Returns the FQCN of the contained Geometry type.
248
     *
249
     * @return class-string<T>
250
     */
251
    protected function containedGeometryType(): string
252
    {
253
        return Geometry::class;
1,276✔
254
    }
255
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc