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

brick / geo / 13715715561

06 Mar 2025 10:47PM UTC coverage: 44.086% (-40.4%) from 84.507%
13715715561

push

github

BenMorel
Remove Psalm-specific annotations

1543 of 3500 relevant lines covered (44.09%)

270.91 hits per line

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

0.0
/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\LineString;
13
use Brick\Geo\MultiCurve;
14
use Brick\Geo\MultiPoint;
15
use Brick\Geo\MultiSurface;
16
use Brick\Geo\Point;
17
use Brick\Geo\Surface;
18
use Brick\Geo\MultiPolygon;
19
use Brick\Geo\Polygon;
20
use GEOSWKBReader;
21
use GEOSWKBWriter;
22
use GEOSWKTReader;
23
use GEOSWKTWriter;
24
use Override;
25

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

36
    private EWKBReader $ewkbReader;
37
    private EWKBWriter $ewkbWriter;
38

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

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

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

54
        $this->ewkbReader = new EWKBReader();
×
55
        $this->ewkbWriter = new EWKBWriter();
×
56

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

63
    private function toGEOS(Geometry $geometry) : \GEOSGeometry
64
    {
65
        if ($geometry->isEmpty()) {
×
66
            $geosGeometry = $this->wktReader->read($geometry->asText());
×
67
            $geosGeometry->setSRID($geometry->SRID());
×
68

69
            return $geosGeometry;
×
70
        }
71

72
        if ($this->hasBinaryReadWrite) {
×
73
            return $this->wkbReader->read($this->ewkbWriter->write($geometry));
×
74
        }
75

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

79
    private function fromGEOS(\GEOSGeometry $geometry) : Geometry
80
    {
81
        if ($geometry->isEmpty()) {
×
82
            return Geometry::fromText($this->wktWriter->write($geometry), $geometry->getSRID());
×
83
        }
84

85
        if ($this->hasBinaryReadWrite) {
×
86
            return $this->ewkbReader->read($this->wkbWriter->write($geometry));
×
87
        }
88

89
        $ewkb = hex2bin($this->wkbWriter->writeHEX($geometry));
×
90
        assert($ewkb !== false);
×
91

92
        return $this->ewkbReader->read($ewkb);
×
93
    }
94

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

309
    #[Override]
310
    public function relate(Geometry $a, Geometry $b, string $matrix) : bool
311
    {
312
        try {
313
            $result = $this->toGEOS($a)->relate($this->toGEOS($b), $matrix);
×
314

315
            // giving a matrix should always return a boolean
316
            assert(is_bool($result));
×
317

318
            return $result;
×
319
        } catch (\Exception $e) {
×
320
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
321
        }
322
    }
323

324
    #[Override]
325
    public function locateAlong(Geometry $g, float $mValue) : Geometry
326
    {
327
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
×
328
    }
329

330
    #[Override]
331
    public function locateBetween(Geometry $g, float $mStart, float $mEnd) : Geometry
332
    {
333
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
×
334
    }
335

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

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

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

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

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

386
    #[Override]
387
    public function snapToGrid(Geometry $g, float $size) : Geometry
388
    {
389
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
×
390
    }
391

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

402
    #[Override]
403
    public function maxDistance(Geometry $a, Geometry $b) : float
404
    {
405
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
×
406
    }
407

408
    #[Override]
409
    public function transform(Geometry $g, int $srid) : Geometry
410
    {
411
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
×
412
    }
413

414
    #[Override]
415
    public function split(Geometry $g, Geometry $blade) : Geometry
416
    {
417
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
×
418
    }
419

420
    #[Override]
421
    public function lineInterpolatePoint(LineString $linestring, float $fraction) : Point
422
    {
423
        try {
424
            $result = $this->fromGEOS($this->toGEOS($linestring)->interpolate($fraction, true));
×
425
        } catch (\Exception $e) {
×
426
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
427
        }
428

429
        if (! $result instanceof Point) {
×
430
            throw new GeometryEngineException('This operation yielded the wrong geometry type: ' . $result::class);
×
431
        }
432

433
        return $result;
×
434
    }
435

436
    #[Override]
437
    public function lineInterpolatePoints(LineString $linestring, float $fraction) : MultiPoint
438
    {
439
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
×
440
    }
441
}
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