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

brick / geo / 13642551372

03 Mar 2025 11:25PM UTC coverage: 84.406% (+0.8%) from 83.61%
13642551372

push

github

BenMorel
Merge WKTParser & EWKTParser

31 of 32 new or added lines in 3 files covered. (96.88%)

69 existing lines in 8 files now uncovered.

1548 of 1834 relevant lines covered (84.41%)

1988.83 hits per line

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

56.1
/src/PolyhedralSurface.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Brick\Geo;
6

7
use ArrayIterator;
8
use Brick\Geo\Exception\CoordinateSystemException;
9
use Brick\Geo\Exception\NoSuchGeometryException;
10
use Brick\Geo\Exception\UnexpectedGeometryException;
11
use Brick\Geo\Projector\Projector;
12
use Override;
13

14
/**
15
 * A PolyhedralSurface is a contiguous collection of polygons, which share common boundary segments.
16
 *
17
 * For each pair of polygons that "touch", the common boundary shall be expressible as a finite collection
18
 * of LineStrings. Each such LineString shall be part of the boundary of at most 2 Polygon patches.
19
 *
20
 * For any two polygons that share a common boundary, the "top" of the polygon shall be consistent. This means
21
 * that when two linear rings from these two Polygons traverse the common boundary segment, they do so in
22
 * opposite directions. Since the Polyhedral surface is contiguous, all polygons will be thus consistently oriented.
23
 * This means that a non-oriented surface (such as Möbius band) shall not have single surface representations.
24
 * They may be represented by a MultiSurface.
25
 *
26
 * If each such LineString is the boundary of exactly 2 Polygon patches, then the PolyhedralSurface is a simple,
27
 * closed polyhedron and is topologically isomorphic to the surface of a sphere. By the Jordan Surface Theorem
28
 * (Jordan’s Theorem for 2-spheres), such polyhedrons enclose a solid topologically isomorphic to the interior of a
29
 * sphere; the ball. In this case, the "top" of the surface will either point inward or outward of the enclosed
30
 * finite solid. If outward, the surface is the exterior boundary of the enclosed surface. If inward, the surface
31
 * is the interior of the infinite complement of the enclosed solid. A Ball with some number of voids (holes) inside
32
 * can thus be presented as one exterior boundary shell, and some number in interior boundary shells.
33
 *
34
 * @template T of Polygon
35
 */
36
class PolyhedralSurface extends Surface
37
{
38
    /**
39
     * The polygons that compose this PolyhedralSurface.
40
     *
41
     * An empty PolyhedralSurface contains no polygons.
42
     *
43
     * @psalm-var list<T>
44
     *
45
     * @var Polygon[]
46
     */
47
    protected array $patches = [];
48

49
    /**
50
     * The coordinate system of each of the patches must match the one of the PolyhedralSurface.
51
     *
52
     * @param CoordinateSystem $cs         The coordinate system of the PolyhedralSurface.
53
     * @param T                ...$patches The patches that compose the PolyhedralSurface.
54
     *
55
     * @throws CoordinateSystemException If different coordinate systems are used.
56
     */
57
    final public function __construct(CoordinateSystem $cs, Polygon ...$patches)
58
    {
59
        parent::__construct($cs, ! $patches);
3,094✔
60

61
        if (! $patches) {
3,094✔
62
            return;
1,498✔
63
        }
64

65
        CoordinateSystem::check($this, ...$patches);
1,624✔
66

67
        $patchType = $this->patchType();
1,624✔
68

69
        foreach ($patches as $patch) {
1,624✔
70
            /**
71
             * @psalm-suppress DocblockTypeContradiction We do want to enforce this in code, as not everyone uses static analysis!
72
             * @psalm-suppress MixedArgument It looks like due to this check, Psalm considers that $geometry has no type anymore.
73
             */
74
            if (! $patch instanceof $patchType) {
1,624✔
UNCOV
75
                throw new UnexpectedGeometryException(sprintf(
×
UNCOV
76
                    '%s expects instance of %s, instance of %s given.',
×
UNCOV
77
                    static::class,
×
UNCOV
78
                    $patchType,
×
UNCOV
79
                    $patch::class
×
UNCOV
80
                ));
×
81
            }
82
        }
83

84
        $this->patches = array_values($patches);
1,624✔
85
    }
86

87
    /**
88
     * Creates a non-empty PolyhedralSurface composed of the given patches.
89
     *
90
     * @param Polygon    $patch1 The first patch.
91
     * @param Polygon ...$patchN The subsequent patches, if any.
92
     *
93
     * @throws CoordinateSystemException If the patches use different coordinate systems.
94
     *
95
     * @psalm-suppress UnsafeGenericInstantiation Not sure how to fix this.
96
     */
97
    public static function of(Polygon $patch1, Polygon ...$patchN) : static
98
    {
99
        return new static($patch1->coordinateSystem(), $patch1, ...$patchN);
7✔
100
    }
101

102
    /**
103
     * Returns the FQCN of the contained patch type.
104
     *
105
     * @psalm-return class-string<T>
106
     */
107
    protected function patchType() : string
108
    {
109
        return Polygon::class;
952✔
110
    }
111

112
    public function numPatches() : int
113
    {
114
        return count($this->patches);
56✔
115
    }
116

117
    /**
118
     * Returns the specified patch N in this PolyhedralSurface.
119
     *
120
     * @param int $n The patch number, 1-based.
121
     *
122
     * @return T
123
     *
124
     * @throws NoSuchGeometryException If there is no patch at this index.
125
     */
126
    public function patchN(int $n) : Polygon
127
    {
128
        if (! isset($this->patches[$n - 1])) {
343✔
129
            throw new NoSuchGeometryException('There is no patch in this PolyhedralSurface at index ' . $n);
224✔
130
        }
131

132
        return $this->patches[$n - 1];
119✔
133
    }
134

135
    /**
136
     * Returns the patches that compose this PolyhedralSurface.
137
     *
138
     * @psalm-return list<T>
139
     *
140
     * @return Polygon[]
141
     */
142
    public function patches() : array
143
    {
UNCOV
144
        return $this->patches;
×
145
    }
146

147
    #[Override]
148
    public function geometryType() : string
149
    {
150
        return 'PolyhedralSurface';
798✔
151
    }
152

153
    #[Override]
154
    public function geometryTypeBinary() : int
155
    {
156
        return Geometry::POLYHEDRALSURFACE;
343✔
157
    }
158

159
    #[Override]
160
    public function getBoundingBox() : BoundingBox
161
    {
UNCOV
162
        $boundingBox = BoundingBox::new();
×
163

UNCOV
164
        foreach ($this->patches as $patch) {
×
UNCOV
165
            $boundingBox = $boundingBox->extendedWithBoundingBox($patch->getBoundingBox());
×
166
        }
167

UNCOV
168
        return $boundingBox;
×
169
    }
170

171
    #[Override]
172
    public function toArray() : array
173
    {
174
        $result = [];
448✔
175

176
        foreach ($this->patches as $patch) {
448✔
177
            $result[] = $patch->toArray();
224✔
178
        }
179

180
        return $result;
448✔
181
    }
182

183
    /**
184
     * @psalm-suppress UnsafeGenericInstantiation Not sure how to fix this.
185
     */
186
    #[Override]
187
    public function project(Projector $projector): static
188
    {
UNCOV
189
        return new static(
×
UNCOV
190
            $projector->getTargetCoordinateSystem($this->coordinateSystem),
×
UNCOV
191
            ...array_map(
×
UNCOV
192
                fn (Polygon $patch) => $patch->project($projector),
×
UNCOV
193
                $this->patches,
×
UNCOV
194
            ),
×
UNCOV
195
        );
×
196
    }
197

198
    /**
199
     * Returns the number of patches in this PolyhedralSurface.
200
     *
201
     * Required by interface Countable.
202
     */
203
    #[Override]
204
    public function count() : int
205
    {
206
        return count($this->patches);
686✔
207
    }
208

209
    /**
210
     * Returns an iterator for the patches in this PolyhedralSurface.
211
     *
212
     * Required by interface IteratorAggregate.
213
     *
214
     * @psalm-return ArrayIterator<int<0, max>, T>
215
     */
216
    #[Override]
217
    public function getIterator() : ArrayIterator
218
    {
219
        return new ArrayIterator($this->patches);
1,491✔
220
    }
221

222
    /**
223
     * Returns a copy of this PolyhedralSurface, with the given patches added.
224
     *
225
     * @psalm-suppress UnsafeGenericInstantiation Not sure how to fix this.
226
     */
227
    public function withAddedPatches(Polygon ...$patches) : static
228
    {
229
        return new static($this->coordinateSystem, ...$this->patches, ...$patches);
105✔
230
    }
231
}
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