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

brick / geo / 13687942239

05 Mar 2025 11:54PM UTC coverage: 84.507% (+0.07%) from 84.44%
13687942239

push

github

BenMorel
Remove Psalm-specific annotations

1560 of 1846 relevant lines covered (84.51%)

1977.43 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
 * @template-implements \IteratorAggregate<int<0, max>, T>
36
 */
37
class PolyhedralSurface extends Surface implements \Countable, \IteratorAggregate
38
{
39
    /**
40
     * The polygons that compose this PolyhedralSurface.
41
     *
42
     * An empty PolyhedralSurface contains no polygons.
43
     *
44
     * @var list<T>
45
     */
46
    protected array $patches = [];
47

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

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

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

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

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

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

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

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

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

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

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

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

144
    #[Override]
145
    public function geometryType() : string
146
    {
147
        return 'PolyhedralSurface';
798✔
148
    }
149

150
    #[Override]
151
    public function geometryTypeBinary() : int
152
    {
153
        return Geometry::POLYHEDRALSURFACE;
343✔
154
    }
155

156
    #[Override]
157
    public function getBoundingBox() : BoundingBox
158
    {
159
        $boundingBox = BoundingBox::new();
×
160

161
        foreach ($this->patches as $patch) {
×
162
            $boundingBox = $boundingBox->extendedWithBoundingBox($patch->getBoundingBox());
×
163
        }
164

165
        return $boundingBox;
×
166
    }
167

168
    /**
169
     * @return list<list<list<list<float>>>>
170
     */
171
    #[Override]
172
    public function toArray() : array
173
    {
174
        return array_map(
448✔
175
            fn (Polygon $patch) => $patch->toArray(),
448✔
176
            $this->patches,
448✔
177
        );
448✔
178
    }
179

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

195
    /**
196
     * Returns the number of patches in this PolyhedralSurface.
197
     */
198
    #[Override]
199
    public function count() : int
200
    {
201
        return count($this->patches);
686✔
202
    }
203

204
    /**
205
     * Returns an iterator for the patches in this PolyhedralSurface.
206
     *
207
     * @return ArrayIterator<int<0, max>, T>
208
     */
209
    #[Override]
210
    public function getIterator() : ArrayIterator
211
    {
212
        return new ArrayIterator($this->patches);
1,491✔
213
    }
214

215
    /**
216
     * Returns a copy of this PolyhedralSurface, with the given patches added.
217
     *
218
     * @psalm-suppress UnsafeGenericInstantiation Not sure how to fix this.
219
     */
220
    public function withAddedPatches(Polygon ...$patches) : static
221
    {
222
        return new static($this->coordinateSystem, ...$this->patches, ...$patches);
105✔
223
    }
224
}
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