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

brick / geo / 13971277707

20 Mar 2025 02:05PM UTC coverage: 65.568% (+0.1%) from 65.472%
13971277707

push

github

BenMorel
Accept $prettyPrint in AbstractWktWriter constructor

1889 of 2881 relevant lines covered (65.57%)

1480.09 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\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 Override;
14

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

62
        $this->patches = array_values($patches);
3,280✔
63

64
        if ($isEmpty) {
3,280✔
65
            return;
1,584✔
66
        }
67

68
        CoordinateSystem::check($this, ...$patches);
1,728✔
69

70
        $patchType = $this->patchType();
1,728✔
71

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

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

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

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

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

133
        return $this->patches[$n - 1];
136✔
134
    }
135

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

146
    #[NoProxy, Override]
147
    public function geometryType() : string
148
    {
149
        return 'PolyhedralSurface';
784✔
150
    }
151

152
    #[NoProxy, Override]
153
    public function geometryTypeBinary() : int
154
    {
155
        return Geometry::POLYHEDRALSURFACE;
392✔
156
    }
157

158
    #[Override]
159
    public function getBoundingBox() : BoundingBox
160
    {
161
        return array_reduce(
×
162
            $this->patches,
×
163
            fn (BoundingBox $boundingBox, Polygon $patch) => $boundingBox->extendedWithBoundingBox($patch->getBoundingBox()),
×
164
            BoundingBox::new(),
×
165
        );
×
166
    }
167

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

180
    #[Override]
181
    public function project(Projector $projector): PolyhedralSurface
182
    {
183
        return new PolyhedralSurface(
×
184
            $projector->getTargetCoordinateSystem($this->coordinateSystem),
×
185
            ...array_map(
×
186
                fn (Polygon $patch) => $patch->project($projector),
×
187
                $this->patches,
×
188
            ),
×
189
        );
×
190
    }
191

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

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

212
    /**
213
     * Returns a copy of this PolyhedralSurface, with the given patches added.
214
     *
215
     * @psalm-suppress UnsafeInstantiation
216
     */
217
    public function withAddedPatches(Polygon ...$patches) : PolyhedralSurface
218
    {
219
        return new static($this->coordinateSystem, ...$this->patches, ...$patches);
120✔
220
    }
221
}
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