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

brick / geo / 13721695253

07 Mar 2025 01:26PM UTC coverage: 49.758% (+1.0%) from 48.719%
13721695253

push

github

BenMorel
Unconditionally catch exceptions in PDOEngine & SQLite3Engine

+ include underlying engine message in exception message.

23 of 28 new or added lines in 4 files covered. (82.14%)

168 existing lines in 6 files now uncovered.

1746 of 3509 relevant lines covered (49.76%)

974.92 hits per line

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

89.68
/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\Engine\Internal\TypeChecker;
9
use Brick\Geo\Exception\GeometryEngineException;
10
use Brick\Geo\IO\EWKBReader;
11
use Brick\Geo\IO\EWKBWriter;
12
use Brick\Geo\Geometry;
13
use Brick\Geo\LineString;
14
use Brick\Geo\MultiCurve;
15
use Brick\Geo\MultiPoint;
16
use Brick\Geo\MultiSurface;
17
use Brick\Geo\Point;
18
use Brick\Geo\Surface;
19
use Brick\Geo\MultiPolygon;
20
use Brick\Geo\Polygon;
21
use GEOSWKBReader;
22
use GEOSWKBWriter;
23
use GEOSWKTReader;
24
use GEOSWKTWriter;
25
use Override;
26

27
/**
28
 * GeometryEngine implementation based on the GEOS PHP bindings.
29
 */
30
final class GEOSEngine implements GeometryEngine
31
{
32
    private readonly GEOSWKBReader $geosWkbReader;
33
    private readonly GEOSWKBWriter $geosWkbWriter;
34
    private readonly GEOSWKTReader $geosWktReader;
35
    private readonly GEOSWKTWriter $geosWktWriter;
36

37
    private readonly EWKBReader $ewkbReader;
38
    private readonly EWKBWriter $ewkbWriter;
39

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

47
    public function __construct()
48
    {
49
        $this->geosWkbReader = new \GEOSWKBReader();
×
50
        $this->geosWkbWriter = new \GEOSWKBWriter();
×
51

52
        $this->geosWktReader = new \GEOSWKTReader();
×
53
        $this->geosWktWriter = new \GEOSWKTWriter();
×
54

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

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

64
    private function toGEOS(Geometry $geometry) : \GEOSGeometry
65
    {
UNCOV
66
        if ($geometry->isEmpty()) {
201✔
UNCOV
67
            $geosGeometry = $this->geosWktReader->read($geometry->asText());
18✔
UNCOV
68
            $geosGeometry->setSRID($geometry->SRID());
10✔
69

UNCOV
70
            return $geosGeometry;
10✔
71
        }
72

UNCOV
73
        if ($this->hasBinaryReadWrite) {
185✔
UNCOV
74
            return $this->geosWkbReader->read($this->ewkbWriter->write($geometry));
185✔
75
        }
76

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

80
    private function fromGEOS(\GEOSGeometry $geometry) : Geometry
81
    {
UNCOV
82
        if ($geometry->isEmpty()) {
60✔
UNCOV
83
            return Geometry::fromText($this->geosWktWriter->write($geometry), $geometry->getSRID());
3✔
84
        }
85

UNCOV
86
        if ($this->hasBinaryReadWrite) {
57✔
UNCOV
87
            return $this->ewkbReader->read($this->geosWkbWriter->write($geometry));
57✔
88
        }
89

90
        $ewkb = hex2bin($this->geosWkbWriter->writeHEX($geometry));
×
UNCOV
91
        assert($ewkb !== false);
92

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

96
    #[Override]
97
    public function union(Geometry $a, Geometry $b) : Geometry
98
    {
UNCOV
99
        return $this->execute(
4✔
UNCOV
100
            fn() => $this->fromGEOS($this->toGEOS($a)->union($this->toGEOS($b))),
4✔
UNCOV
101
        );
4✔
102
    }
103

104
    #[Override]
105
    public function difference(Geometry $a, Geometry $b) : Geometry
106
    {
UNCOV
107
        return $this->execute(
2✔
UNCOV
108
            fn() => $this->fromGEOS($this->toGEOS($a)->difference($this->toGEOS($b))),
2✔
UNCOV
109
        );
2✔
110
    }
111

112
    #[Override]
113
    public function envelope(Geometry $g) : Geometry
114
    {
UNCOV
115
        return $this->execute(
3✔
UNCOV
116
            fn() => $this->fromGEOS($this->toGEOS($g)->envelope()),
3✔
UNCOV
117
        );
3✔
118
    }
119

120
    #[Override]
121
    public function length(Curve|MultiCurve $g) : float
122
    {
UNCOV
123
        return $this->execute(
16✔
UNCOV
124
            fn() => $this->toGEOS($g)->length(),
16✔
UNCOV
125
        );
16✔
126
    }
127

128
    #[Override]
129
    public function area(Surface|MultiSurface $g) : float
130
    {
UNCOV
131
        return $this->execute(
11✔
UNCOV
132
            fn() => $this->toGEOS($g)->area(),
11✔
UNCOV
133
        );
11✔
134
    }
135

136
    #[Override]
137
    public function azimuth(Point $observer, Point $subject) : float
138
    {
UNCOV
139
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
6✔
140
    }
141

142
    #[Override]
143
    public function centroid(Geometry $g) : Point
144
    {
UNCOV
145
        $centroid = $this->execute(
8✔
UNCOV
146
            fn() => $this->fromGEOS($this->toGEOS($g)->centroid()),
8✔
UNCOV
147
        );
8✔
148

UNCOV
149
        TypeChecker::check($centroid, Point::class);
8✔
150

UNCOV
151
        return $centroid;
8✔
152
    }
153

154
    #[Override]
155
    public function pointOnSurface(Surface|MultiSurface $g) : Point
156
    {
UNCOV
157
        $pointOnSurface = $this->execute(
7✔
UNCOV
158
            fn() => $this->fromGEOS($this->toGEOS($g)->pointOnSurface()),
7✔
UNCOV
159
        );
7✔
160

UNCOV
161
        TypeChecker::check($pointOnSurface, Point::class);
7✔
162

UNCOV
163
        return $pointOnSurface;
7✔
164
    }
165

166
    #[Override]
167
    public function boundary(Geometry $g) : Geometry
168
    {
UNCOV
169
        return $this->execute(
4✔
UNCOV
170
            fn() => $this->fromGEOS($this->toGEOS($g)->boundary()),
4✔
UNCOV
171
        );
4✔
172
    }
173

174
    #[Override]
175
    public function isValid(Geometry $g) : bool
176
    {
UNCOV
177
        return $this->execute(
6✔
UNCOV
178
            fn() => $this->toGEOS($g)->checkValidity()['valid'],
6✔
UNCOV
179
        );
6✔
180
    }
181

182
    #[Override]
183
    public function isClosed(Geometry $g) : bool
184
    {
UNCOV
185
        return $this->execute(
39✔
UNCOV
186
            fn() => $this->toGEOS($g)->isClosed(),
39✔
UNCOV
187
        );
39✔
188
    }
189

190
    #[Override]
191
    public function isSimple(Geometry $g) : bool
192
    {
UNCOV
193
        return $this->execute(
10✔
UNCOV
194
            fn() => $this->toGEOS($g)->isSimple(),
10✔
UNCOV
195
        );
10✔
196
    }
197

198
    #[Override]
199
    public function isRing(Curve $curve) : bool
200
    {
UNCOV
201
        return $this->execute(
11✔
UNCOV
202
            fn() => $this->toGEOS($curve)->isRing(),
11✔
UNCOV
203
        );
11✔
204
    }
205

206
    #[Override]
207
    public function makeValid(Geometry $g): Geometry
208
    {
UNCOV
209
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
6✔
210
    }
211

212
    #[Override]
213
    public function equals(Geometry $a, Geometry $b) : bool
214
    {
UNCOV
215
        return $this->execute(
20✔
UNCOV
216
            fn() => $this->toGEOS($a)->equals($this->toGEOS($b)),
20✔
UNCOV
217
        );
20✔
218
    }
219

220
    #[Override]
221
    public function disjoint(Geometry $a, Geometry $b) : bool
222
    {
UNCOV
223
        return $this->execute(
6✔
UNCOV
224
            fn() => $this->toGEOS($a)->disjoint($this->toGEOS($b)),
6✔
UNCOV
225
        );
6✔
226
    }
227

228
    #[Override]
229
    public function intersects(Geometry $a, Geometry $b) : bool
230
    {
UNCOV
231
        return $this->execute(
6✔
UNCOV
232
            fn() => $this->toGEOS($a)->intersects($this->toGEOS($b)),
6✔
UNCOV
233
        );
6✔
234
    }
235

236
    #[Override]
237
    public function touches(Geometry $a, Geometry $b) : bool
238
    {
UNCOV
239
        return $this->execute(
8✔
UNCOV
240
            fn() => $this->toGEOS($a)->touches($this->toGEOS($b)),
8✔
UNCOV
241
        );
8✔
242
    }
243

244
    #[Override]
245
    public function crosses(Geometry $a, Geometry $b) : bool
246
    {
UNCOV
247
        return $this->execute(
8✔
UNCOV
248
            fn() => $this->toGEOS($a)->crosses($this->toGEOS($b)),
8✔
UNCOV
249
        );
8✔
250
    }
251

252
    #[Override]
253
    public function within(Geometry $a, Geometry $b) : bool
254
    {
UNCOV
255
        return $this->execute(
5✔
UNCOV
256
            fn() => $this->toGEOS($a)->within($this->toGEOS($b)),
5✔
UNCOV
257
        );
5✔
258
    }
259

260
    #[Override]
261
    public function contains(Geometry $a, Geometry $b) : bool
262
    {
UNCOV
263
        return $this->execute(
15✔
UNCOV
264
            fn() => $this->toGEOS($a)->contains($this->toGEOS($b)),
15✔
UNCOV
265
        );
15✔
266
    }
267

268
    #[Override]
269
    public function overlaps(Geometry $a, Geometry $b) : bool
270
    {
UNCOV
271
        return $this->execute(
2✔
UNCOV
272
            fn() => $this->toGEOS($a)->overlaps($this->toGEOS($b)),
2✔
UNCOV
273
        );
2✔
274
    }
275

276
    #[Override]
277
    public function relate(Geometry $a, Geometry $b, string $matrix) : bool
278
    {
UNCOV
279
        $result = $this->execute(
4✔
UNCOV
280
            fn() => $this->toGEOS($a)->relate($this->toGEOS($b), $matrix),
4✔
UNCOV
281
        );
4✔
282

283
        // giving a matrix should always return a boolean
UNCOV
284
        assert(is_bool($result));
285

UNCOV
286
        return $result;
4✔
287
    }
288

289
    #[Override]
290
    public function locateAlong(Geometry $g, float $mValue) : Geometry
291
    {
UNCOV
292
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
2✔
293
    }
294

295
    #[Override]
296
    public function locateBetween(Geometry $g, float $mStart, float $mEnd) : Geometry
297
    {
UNCOV
298
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
2✔
299
    }
300

301
    #[Override]
302
    public function distance(Geometry $a, Geometry $b) : float
303
    {
UNCOV
304
        return $this->execute(
5✔
UNCOV
305
            fn() => $this->toGEOS($a)->distance($this->toGEOS($b)),
5✔
UNCOV
306
        );
5✔
307
    }
308

309
    #[Override]
310
    public function buffer(Geometry $g, float $distance) : Geometry
311
    {
UNCOV
312
        return $this->execute(
3✔
UNCOV
313
            fn() => $this->fromGEOS($this->toGEOS($g)->buffer($distance)),
3✔
UNCOV
314
        );
3✔
315
    }
316

317
    #[Override]
318
    public function convexHull(Geometry $g) : Geometry
319
    {
UNCOV
320
        return $this->execute(
3✔
UNCOV
321
            fn() => $this->fromGEOS($this->toGEOS($g)->convexHull()),
3✔
UNCOV
322
        );
3✔
323
    }
324

325
    #[Override]
326
    public function intersection(Geometry $a, Geometry $b) : Geometry
327
    {
UNCOV
328
        return $this->execute(
2✔
UNCOV
329
            fn() => $this->fromGEOS($this->toGEOS($a)->intersection($this->toGEOS($b))),
2✔
UNCOV
330
        );
2✔
331
    }
332

333
    #[Override]
334
    public function symDifference(Geometry $a, Geometry $b) : Geometry
335
    {
UNCOV
336
        return $this->execute(
1✔
UNCOV
337
            fn() => $this->fromGEOS($this->toGEOS($a)->symDifference($this->toGEOS($b))),
1✔
UNCOV
338
        );
1✔
339
    }
340

341
    #[Override]
342
    public function snapToGrid(Geometry $g, float $size) : Geometry
343
    {
UNCOV
344
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
4✔
345
    }
346

347
    #[Override]
348
    public function simplify(Geometry $g, float $tolerance) : Geometry
349
    {
UNCOV
350
        return $this->execute(
2✔
UNCOV
351
            fn() => $this->fromGEOS($this->toGEOS($g)->simplify($tolerance)),
2✔
UNCOV
352
        );
2✔
353
    }
354

355
    #[Override]
356
    public function maxDistance(Geometry $a, Geometry $b) : float
357
    {
UNCOV
358
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
3✔
359
    }
360

361
    #[Override]
362
    public function transform(Geometry $g, int $srid) : Geometry
363
    {
UNCOV
364
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
7✔
365
    }
366

367
    #[Override]
368
    public function split(Geometry $g, Geometry $blade) : Geometry
369
    {
UNCOV
370
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
4✔
371
    }
372

373
    #[Override]
374
    public function lineInterpolatePoint(LineString $lineString, float $fraction) : Point
375
    {
UNCOV
376
        $result = $this->execute(
21✔
UNCOV
377
            fn() => $this->fromGEOS($this->toGEOS($lineString)->interpolate($fraction, true)),
21✔
UNCOV
378
        );
21✔
379

UNCOV
380
        if (! $result instanceof Point) {
21✔
381
            throw new GeometryEngineException('This operation yielded the wrong geometry type: ' . $result::class);
×
382
        }
383

UNCOV
384
        return $result;
21✔
385
    }
386

387
    #[Override]
388
    public function lineInterpolatePoints(LineString $lineString, float $fraction) : MultiPoint
389
    {
UNCOV
390
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
30✔
391
    }
392

393
    /**
394
     * @template T
395
     *
396
     * @param \Closure(): T $action
397
     *
398
     * @return T
399
     *
400
     * @throws GeometryEngineException
401
     */
402
    private function execute(\Closure $action) : mixed
403
    {
404
        try {
UNCOV
405
            return $action();
201✔
UNCOV
406
        } catch (\Exception $e) {
19✔
NEW
407
            throw GeometryEngineException::wrap($e);
19✔
408
        }
409
    }
410
}
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