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

brick / geo / 13418525576

19 Feb 2025 05:15PM UTC coverage: 47.546% (+0.2%) from 47.345%
13418525576

push

github

BenMorel
Add withers to geometries

11 of 11 new or added lines in 7 files covered. (100.0%)

60 existing lines in 4 files now uncovered.

1608 of 3382 relevant lines covered (47.55%)

971.56 hits per line

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

44.14
/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

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

33
    private readonly EWKBReader $ewkbReader;
34
    private readonly EWKBWriter $ewkbWriter;
35

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

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

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

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

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

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

UNCOV
66
            return $geosGeometry;
10✔
67
        }
68

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

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

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

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

86
        return $this->ewkbReader->read(hex2bin($this->wkbWriter->writeHEX($geometry)));
×
87
    }
88

89
    public function union(Geometry $a, Geometry $b) : Geometry
90
    {
91
        try {
UNCOV
92
            return $this->fromGEOS($this->toGEOS($a)->union($this->toGEOS($b)));
4✔
93
        } catch (\Exception $e) {
×
94
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
95
        }
96
    }
97

98
    public function difference(Geometry $a, Geometry $b) : Geometry
99
    {
100
        try {
UNCOV
101
            return $this->fromGEOS($this->toGEOS($a)->difference($this->toGEOS($b)));
2✔
102
        } catch (\Exception $e) {
×
103
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
104
        }
105
    }
106

107
    public function envelope(Geometry $g) : Geometry
108
    {
109
        try {
UNCOV
110
            return $this->fromGEOS($this->toGEOS($g)->envelope());
3✔
111
        } catch (\Exception $e) {
×
112
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
113
        }
114
    }
115

116
    public function length(Curve|MultiCurve $g) : float
117
    {
118
        try {
UNCOV
119
            return $this->toGEOS($g)->length();
16✔
UNCOV
120
        } catch (\Exception $e) {
5✔
UNCOV
121
            throw GeometryEngineException::operationNotSupportedByEngine($e);
5✔
122
        }
123
    }
124

125
    public function area(Surface|MultiSurface $g) : float
126
    {
127
        try {
UNCOV
128
            return $this->toGEOS($g)->area();
11✔
UNCOV
129
        } catch (\Exception $e) {
2✔
UNCOV
130
            throw GeometryEngineException::operationNotSupportedByEngine($e);
2✔
131
        }
132
    }
133

134
    public function azimuth(Point $observer, Point $subject) : float
135
    {
136
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
×
137
    }
138

139
    public function centroid(Geometry $g) : Point
140
    {
141
        try {
142
            /** @var Point */
UNCOV
143
            return $this->fromGEOS($this->toGEOS($g)->centroid());
8✔
144
        } catch (\Exception $e) {
×
145
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
146
        }
147
    }
148

149
    public function pointOnSurface(Surface|MultiSurface $g) : Point
150
    {
151
        try {
152
            /** @var Point */
UNCOV
153
            return $this->fromGEOS($this->toGEOS($g)->pointOnSurface());
7✔
154
        } catch (\Exception $e) {
×
155
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
156
        }
157
    }
158

159
    public function boundary(Geometry $g) : Geometry
160
    {
161
        try {
UNCOV
162
            return $this->fromGEOS($this->toGEOS($g)->boundary());
4✔
163
        } catch (\Exception $e) {
×
164
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
165
        }
166
    }
167

168
    public function isValid(Geometry $g) : bool
169
    {
170
        try {
UNCOV
171
            return $this->toGEOS($g)->checkValidity()['valid'];
6✔
172
        } catch (\Exception $e) {
×
173
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
174
        }
175
    }
176

177
    public function isClosed(Geometry $g) : bool
178
    {
179
        try {
UNCOV
180
            return $this->toGEOS($g)->isClosed();
39✔
UNCOV
181
        } catch (\Exception $e) {
12✔
UNCOV
182
            throw GeometryEngineException::operationNotSupportedByEngine($e);
12✔
183
        }
184
    }
185

186
    public function isSimple(Geometry $g) : bool
187
    {
188
        try {
UNCOV
189
            return $this->toGEOS($g)->isSimple();
10✔
190
        } catch (\Exception $e) {
×
191
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
192
        }
193
    }
194

195
    public function isRing(Curve $curve) : bool
196
    {
197
        try {
UNCOV
198
            return $this->toGEOS($curve)->isRing();
11✔
199
        } catch (\Exception $e) {
×
200
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
201
        }
202
    }
203

204
    public function makeValid(Geometry $g): Geometry
205
    {
206
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
×
207
    }
208

209
    public function equals(Geometry $a, Geometry $b) : bool
210
    {
211
        try {
UNCOV
212
            return $this->toGEOS($a)->equals($this->toGEOS($b));
20✔
213
        } catch (\Exception $e) {
×
214
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
215
        }
216
    }
217

218
    public function disjoint(Geometry $a, Geometry $b) : bool
219
    {
220
        try {
UNCOV
221
            return $this->toGEOS($a)->disjoint($this->toGEOS($b));
6✔
222
        } catch (\Exception $e) {
×
223
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
224
        }
225
    }
226

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

236
    public function touches(Geometry $a, Geometry $b) : bool
237
    {
238
        try {
UNCOV
239
            return $this->toGEOS($a)->touches($this->toGEOS($b));
8✔
240
        } catch (\Exception $e) {
×
241
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
242
        }
243
    }
244

245
    public function crosses(Geometry $a, Geometry $b) : bool
246
    {
247
        try {
UNCOV
248
            return $this->toGEOS($a)->crosses($this->toGEOS($b));
8✔
249
        } catch (\Exception $e) {
×
250
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
251
        }
252
    }
253

254
    public function within(Geometry $a, Geometry $b) : bool
255
    {
256
        try {
UNCOV
257
            return $this->toGEOS($a)->within($this->toGEOS($b));
5✔
258
        } catch (\Exception $e) {
×
259
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
260
        }
261
    }
262

263
    public function contains(Geometry $a, Geometry $b) : bool
264
    {
265
        try {
UNCOV
266
            return $this->toGEOS($a)->contains($this->toGEOS($b));
15✔
267
        } catch (\Exception $e) {
×
268
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
269
        }
270
    }
271

272
    public function overlaps(Geometry $a, Geometry $b) : bool
273
    {
274
        try {
UNCOV
275
            return $this->toGEOS($a)->overlaps($this->toGEOS($b));
2✔
276
        } catch (\Exception $e) {
×
277
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
278
        }
279
    }
280

281
    public function relate(Geometry $a, Geometry $b, string $matrix) : bool
282
    {
283
        try {
UNCOV
284
            $result = $this->toGEOS($a)->relate($this->toGEOS($b), $matrix);
4✔
285

286
            // giving a matrix should always return a boolean
UNCOV
287
            assert(is_bool($result));
288

UNCOV
289
            return $result;
4✔
290
        } catch (\Exception $e) {
×
291
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
292
        }
293
    }
294

295
    public function locateAlong(Geometry $g, float $mValue) : Geometry
296
    {
UNCOV
297
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
2✔
298
    }
299

300
    public function locateBetween(Geometry $g, float $mStart, float $mEnd) : Geometry
301
    {
UNCOV
302
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
2✔
303
    }
304

305
    public function distance(Geometry $a, Geometry $b) : float
306
    {
307
        try {
UNCOV
308
            return $this->toGEOS($a)->distance($this->toGEOS($b));
5✔
309
        } catch (\Exception $e) {
×
310
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
311
        }
312
    }
313

314
    public function buffer(Geometry $g, float $distance) : Geometry
315
    {
316
        try {
UNCOV
317
            return $this->fromGEOS($this->toGEOS($g)->buffer($distance));
3✔
318
        } catch (\Exception $e) {
×
319
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
320
        }
321
    }
322

323
    public function convexHull(Geometry $g) : Geometry
324
    {
325
        try {
UNCOV
326
            return $this->fromGEOS($this->toGEOS($g)->convexHull());
3✔
327
        } catch (\Exception $e) {
×
328
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
329
        }
330
    }
331

332
    public function intersection(Geometry $a, Geometry $b) : Geometry
333
    {
334
        try {
UNCOV
335
            return $this->fromGEOS($this->toGEOS($a)->intersection($this->toGEOS($b)));
2✔
336
        } catch (\Exception $e) {
×
337
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
338
        }
339
    }
340

341
    public function symDifference(Geometry $a, Geometry $b) : Geometry
342
    {
343
        try {
UNCOV
344
            return $this->fromGEOS($this->toGEOS($a)->symDifference($this->toGEOS($b)));
1✔
345
        } catch (\Exception $e) {
×
346
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
347
        }
348
    }
349

350
    public function snapToGrid(Geometry $g, float $size) : Geometry
351
    {
UNCOV
352
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
4✔
353
    }
354

355
    public function simplify(Geometry $g, float $tolerance) : Geometry
356
    {
357
        try {
UNCOV
358
            return $this->fromGEOS($this->toGEOS($g)->simplify($tolerance));
2✔
359
        } catch (\Exception $e) {
×
360
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
361
        }
362
    }
363

364
    public function maxDistance(Geometry $a, Geometry $b) : float
365
    {
UNCOV
366
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
3✔
367
    }
368

369
    public function transform(Geometry $g, int $srid) : Geometry
370
    {
UNCOV
371
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
4✔
372
    }
373

374
    public function split(Geometry $g, Geometry $blade) : Geometry
375
    {
376
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
×
377
    }
378
}
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