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

brick / geo / 13766209603

09 Mar 2025 10:35PM UTC coverage: 87.414% (+3.3%) from 84.117%
13766209603

push

github

BenMorel
Add Point::isEqualTo() (WIP: finish? keep?)

8 of 8 new or added lines in 2 files covered. (100.0%)

73 existing lines in 16 files now uncovered.

1653 of 1891 relevant lines covered (87.41%)

1946.79 hits per line

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

55.81
/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
readonly 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
    public 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
        $isEmpty = (count($patches) === 0);
3,094✔
59
        parent::__construct($cs, $isEmpty);
3,094✔
60

61
        $this->patches = array_values($patches);
3,094✔
62

63
        if ($isEmpty) {
3,094✔
64
            return;
1,498✔
65
        }
66

67
        CoordinateSystem::check($this, ...$patches);
1,624✔
68

69
        $patchType = $this->patchType();
1,624✔
70

71
        foreach ($patches as $patch) {
1,624✔
72
            /**
73
             * @psalm-suppress DocblockTypeContradiction We do want to enforce this in code, as not everyone uses static analysis!
74
             * @psalm-suppress MixedArgument It looks like due to this check, Psalm considers that $geometry no longer has a type.
75
             */
76
            if (! $patch instanceof $patchType) {
1,624✔
77
                throw new UnexpectedGeometryException(sprintf(
×
78
                    '%s expects instance of %s, instance of %s given.',
×
79
                    static::class,
×
80
                    $patchType,
×
81
                    $patch::class
×
82
                ));
×
83
            }
84
        }
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
     * @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
     * @return list<T>
139
     *
140
     * @deprecated Use $patches property instead.
141
     */
142
    public function patches() : array
143
    {
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
    {
162
        return array_reduce(
×
UNCOV
163
            $this->patches,
×
164
            fn (BoundingBox $boundingBox, Polygon $patch) => $boundingBox->extendedWithBoundingBox($patch->getBoundingBox()),
×
165
            BoundingBox::new(),
×
UNCOV
166
        );
×
167
    }
168

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

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

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

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

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