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

brick / geo / 13655507172

04 Mar 2025 02:07PM UTC coverage: 84.44% (+0.04%) from 84.402%
13655507172

push

github

BenMorel
Restrict CompoundCurve to contain LineString|CircularString

It looks like nested CompoundCurves are, in fact, forbidden.

3 of 5 new or added lines in 3 files covered. (60.0%)

88 existing lines in 4 files now uncovered.

1552 of 1838 relevant lines covered (84.44%)

1985.26 hits per line

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

43.75
/src/Engine/GEOSEngine.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Brick\Geo\Engine;
6

7
use Brick\Geo\Curve;
8
use Brick\Geo\Exception\GeometryEngineException;
9
use Brick\Geo\IO\EWKBReader;
10
use Brick\Geo\IO\EWKBWriter;
11
use Brick\Geo\Geometry;
12
use Brick\Geo\MultiCurve;
13
use Brick\Geo\MultiSurface;
14
use Brick\Geo\Point;
15
use Brick\Geo\Surface;
16
use Brick\Geo\MultiPolygon;
17
use Brick\Geo\Polygon;
18
use GEOSWKBReader;
19
use GEOSWKBWriter;
20
use GEOSWKTReader;
21
use GEOSWKTWriter;
22
use Override;
23

24
/**
25
 * GeometryEngine implementation based on the GEOS PHP bindings.
26
 */
27
final readonly class GEOSEngine implements GeometryEngine
28
{
29
    private GEOSWKBReader $wkbReader;
30
    private GEOSWKBWriter $wkbWriter;
31
    private GEOSWKTReader $wktReader;
32
    private GEOSWKTWriter $wktWriter;
33

34
    private EWKBReader $ewkbReader;
35
    private EWKBWriter $ewkbWriter;
36

37
    /**
38
     * Whether the GEOS version in use has support for binary read() and write() methods.
39
     *
40
     * These methods are available since GEOS 3.5.0.
41
     */
42
    private bool $hasBinaryReadWrite;
43

44
    public function __construct()
45
    {
46
        $this->wkbReader = new \GEOSWKBReader();
×
47
        $this->wkbWriter = new \GEOSWKBWriter();
×
48

49
        $this->wktReader = new \GEOSWKTReader();
×
50
        $this->wktWriter = new \GEOSWKTWriter();
×
51

52
        $this->ewkbReader = new EWKBReader();
×
53
        $this->ewkbWriter = new EWKBWriter();
×
54

55
        /** @psalm-suppress RedundantCondition These methods are not available before GEOS 3.5.0 */
56
        $this->hasBinaryReadWrite =
×
57
            method_exists($this->wkbReader, 'read') &&
×
58
            method_exists($this->wkbWriter, 'write');
×
59
    }
60

61
    private function toGEOS(Geometry $geometry) : \GEOSGeometry
62
    {
UNCOV
63
        if ($geometry->isEmpty()) {
180✔
UNCOV
64
            $geosGeometry = $this->wktReader->read($geometry->asText());
18✔
UNCOV
65
            $geosGeometry->setSRID($geometry->SRID());
10✔
66

UNCOV
67
            return $geosGeometry;
10✔
68
        }
69

UNCOV
70
        if ($this->hasBinaryReadWrite) {
164✔
UNCOV
71
            return $this->wkbReader->read($this->ewkbWriter->write($geometry));
164✔
72
        }
73

74
        return $this->wkbReader->readHEX(bin2hex($this->ewkbWriter->write($geometry)));
×
75
    }
76

77
    private function fromGEOS(\GEOSGeometry $geometry) : Geometry
78
    {
UNCOV
79
        if ($geometry->isEmpty()) {
39✔
UNCOV
80
            return Geometry::fromText($this->wktWriter->write($geometry), $geometry->getSRID());
3✔
81
        }
82

UNCOV
83
        if ($this->hasBinaryReadWrite) {
36✔
UNCOV
84
            return $this->ewkbReader->read($this->wkbWriter->write($geometry));
36✔
85
        }
86

87
        $ewkb = hex2bin($this->wkbWriter->writeHEX($geometry));
×
UNCOV
88
        assert($ewkb !== false);
89

90
        return $this->ewkbReader->read($ewkb);
×
91
    }
92

93
    #[Override]
94
    public function union(Geometry $a, Geometry $b) : Geometry
95
    {
96
        try {
UNCOV
97
            return $this->fromGEOS($this->toGEOS($a)->union($this->toGEOS($b)));
4✔
98
        } catch (\Exception $e) {
×
99
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
100
        }
101
    }
102

103
    #[Override]
104
    public function difference(Geometry $a, Geometry $b) : Geometry
105
    {
106
        try {
UNCOV
107
            return $this->fromGEOS($this->toGEOS($a)->difference($this->toGEOS($b)));
2✔
108
        } catch (\Exception $e) {
×
109
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
110
        }
111
    }
112

113
    #[Override]
114
    public function envelope(Geometry $g) : Geometry
115
    {
116
        try {
UNCOV
117
            return $this->fromGEOS($this->toGEOS($g)->envelope());
3✔
118
        } catch (\Exception $e) {
×
119
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
120
        }
121
    }
122

123
    #[Override]
124
    public function length(Curve|MultiCurve $g) : float
125
    {
126
        try {
UNCOV
127
            return $this->toGEOS($g)->length();
16✔
UNCOV
128
        } catch (\Exception $e) {
5✔
UNCOV
129
            throw GeometryEngineException::operationNotSupportedByEngine($e);
5✔
130
        }
131
    }
132

133
    #[Override]
134
    public function area(Surface|MultiSurface $g) : float
135
    {
136
        try {
UNCOV
137
            return $this->toGEOS($g)->area();
11✔
UNCOV
138
        } catch (\Exception $e) {
2✔
UNCOV
139
            throw GeometryEngineException::operationNotSupportedByEngine($e);
2✔
140
        }
141
    }
142

143
    #[Override]
144
    public function azimuth(Point $observer, Point $subject) : float
145
    {
146
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
×
147
    }
148

149
    #[Override]
150
    public function centroid(Geometry $g) : Point
151
    {
152
        try {
153
            /** @var Point */
UNCOV
154
            return $this->fromGEOS($this->toGEOS($g)->centroid());
8✔
155
        } catch (\Exception $e) {
×
156
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
157
        }
158
    }
159

160
    #[Override]
161
    public function pointOnSurface(Surface|MultiSurface $g) : Point
162
    {
163
        try {
164
            /** @var Point */
UNCOV
165
            return $this->fromGEOS($this->toGEOS($g)->pointOnSurface());
7✔
166
        } catch (\Exception $e) {
×
167
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
168
        }
169
    }
170

171
    #[Override]
172
    public function boundary(Geometry $g) : Geometry
173
    {
174
        try {
UNCOV
175
            return $this->fromGEOS($this->toGEOS($g)->boundary());
4✔
176
        } catch (\Exception $e) {
×
177
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
178
        }
179
    }
180

181
    #[Override]
182
    public function isValid(Geometry $g) : bool
183
    {
184
        try {
UNCOV
185
            return $this->toGEOS($g)->checkValidity()['valid'];
6✔
186
        } catch (\Exception $e) {
×
187
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
188
        }
189
    }
190

191
    #[Override]
192
    public function isClosed(Geometry $g) : bool
193
    {
194
        try {
UNCOV
195
            return $this->toGEOS($g)->isClosed();
39✔
UNCOV
196
        } catch (\Exception $e) {
12✔
UNCOV
197
            throw GeometryEngineException::operationNotSupportedByEngine($e);
12✔
198
        }
199
    }
200

201
    #[Override]
202
    public function isSimple(Geometry $g) : bool
203
    {
204
        try {
UNCOV
205
            return $this->toGEOS($g)->isSimple();
10✔
206
        } catch (\Exception $e) {
×
207
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
208
        }
209
    }
210

211
    #[Override]
212
    public function isRing(Curve $curve) : bool
213
    {
214
        try {
UNCOV
215
            return $this->toGEOS($curve)->isRing();
11✔
216
        } catch (\Exception $e) {
×
217
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
218
        }
219
    }
220

221
    #[Override]
222
    public function makeValid(Geometry $g): Geometry
223
    {
224
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
×
225
    }
226

227
    #[Override]
228
    public function equals(Geometry $a, Geometry $b) : bool
229
    {
230
        try {
UNCOV
231
            return $this->toGEOS($a)->equals($this->toGEOS($b));
20✔
232
        } catch (\Exception $e) {
×
233
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
234
        }
235
    }
236

237
    #[Override]
238
    public function disjoint(Geometry $a, Geometry $b) : bool
239
    {
240
        try {
UNCOV
241
            return $this->toGEOS($a)->disjoint($this->toGEOS($b));
6✔
242
        } catch (\Exception $e) {
×
243
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
244
        }
245
    }
246

247
    #[Override]
248
    public function intersects(Geometry $a, Geometry $b) : bool
249
    {
250
        try {
UNCOV
251
            return $this->toGEOS($a)->intersects($this->toGEOS($b));
6✔
252
        } catch (\Exception $e) {
×
253
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
254
        }
255
    }
256

257
    #[Override]
258
    public function touches(Geometry $a, Geometry $b) : bool
259
    {
260
        try {
UNCOV
261
            return $this->toGEOS($a)->touches($this->toGEOS($b));
8✔
262
        } catch (\Exception $e) {
×
263
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
264
        }
265
    }
266

267
    #[Override]
268
    public function crosses(Geometry $a, Geometry $b) : bool
269
    {
270
        try {
UNCOV
271
            return $this->toGEOS($a)->crosses($this->toGEOS($b));
8✔
272
        } catch (\Exception $e) {
×
273
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
274
        }
275
    }
276

277
    #[Override]
278
    public function within(Geometry $a, Geometry $b) : bool
279
    {
280
        try {
UNCOV
281
            return $this->toGEOS($a)->within($this->toGEOS($b));
5✔
282
        } catch (\Exception $e) {
×
283
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
284
        }
285
    }
286

287
    #[Override]
288
    public function contains(Geometry $a, Geometry $b) : bool
289
    {
290
        try {
UNCOV
291
            return $this->toGEOS($a)->contains($this->toGEOS($b));
15✔
292
        } catch (\Exception $e) {
×
293
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
294
        }
295
    }
296

297
    #[Override]
298
    public function overlaps(Geometry $a, Geometry $b) : bool
299
    {
300
        try {
UNCOV
301
            return $this->toGEOS($a)->overlaps($this->toGEOS($b));
2✔
302
        } catch (\Exception $e) {
×
303
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
304
        }
305
    }
306

307
    #[Override]
308
    public function relate(Geometry $a, Geometry $b, string $matrix) : bool
309
    {
310
        try {
UNCOV
311
            $result = $this->toGEOS($a)->relate($this->toGEOS($b), $matrix);
4✔
312

313
            // giving a matrix should always return a boolean
UNCOV
314
            assert(is_bool($result));
315

UNCOV
316
            return $result;
4✔
317
        } catch (\Exception $e) {
×
318
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
319
        }
320
    }
321

322
    #[Override]
323
    public function locateAlong(Geometry $g, float $mValue) : Geometry
324
    {
UNCOV
325
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
2✔
326
    }
327

328
    #[Override]
329
    public function locateBetween(Geometry $g, float $mStart, float $mEnd) : Geometry
330
    {
UNCOV
331
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
2✔
332
    }
333

334
    #[Override]
335
    public function distance(Geometry $a, Geometry $b) : float
336
    {
337
        try {
UNCOV
338
            return $this->toGEOS($a)->distance($this->toGEOS($b));
5✔
339
        } catch (\Exception $e) {
×
340
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
341
        }
342
    }
343

344
    #[Override]
345
    public function buffer(Geometry $g, float $distance) : Geometry
346
    {
347
        try {
UNCOV
348
            return $this->fromGEOS($this->toGEOS($g)->buffer($distance));
3✔
349
        } catch (\Exception $e) {
×
350
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
351
        }
352
    }
353

354
    #[Override]
355
    public function convexHull(Geometry $g) : Geometry
356
    {
357
        try {
UNCOV
358
            return $this->fromGEOS($this->toGEOS($g)->convexHull());
3✔
359
        } catch (\Exception $e) {
×
360
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
361
        }
362
    }
363

364
    #[Override]
365
    public function intersection(Geometry $a, Geometry $b) : Geometry
366
    {
367
        try {
UNCOV
368
            return $this->fromGEOS($this->toGEOS($a)->intersection($this->toGEOS($b)));
2✔
369
        } catch (\Exception $e) {
×
370
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
371
        }
372
    }
373

374
    #[Override]
375
    public function symDifference(Geometry $a, Geometry $b) : Geometry
376
    {
377
        try {
UNCOV
378
            return $this->fromGEOS($this->toGEOS($a)->symDifference($this->toGEOS($b)));
1✔
379
        } catch (\Exception $e) {
×
380
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
381
        }
382
    }
383

384
    #[Override]
385
    public function snapToGrid(Geometry $g, float $size) : Geometry
386
    {
UNCOV
387
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
4✔
388
    }
389

390
    #[Override]
391
    public function simplify(Geometry $g, float $tolerance) : Geometry
392
    {
393
        try {
UNCOV
394
            return $this->fromGEOS($this->toGEOS($g)->simplify($tolerance));
2✔
395
        } catch (\Exception $e) {
×
396
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
397
        }
398
    }
399

400
    #[Override]
401
    public function maxDistance(Geometry $a, Geometry $b) : float
402
    {
UNCOV
403
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
3✔
404
    }
405

406
    #[Override]
407
    public function transform(Geometry $g, int $srid) : Geometry
408
    {
UNCOV
409
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
4✔
410
    }
411

412
    #[Override]
413
    public function split(Geometry $g, Geometry $blade) : Geometry
414
    {
415
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
×
416
    }
417
}
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