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

brick / geo / 13589433057

28 Feb 2025 01:45PM UTC coverage: 46.506% (-1.0%) from 47.546%
13589433057

Pull #55

github

web-flow
Merge dca38c140 into 5c954d80e
Pull Request #55: Add LineInterpolatePoint for Postgis

9 of 24 new or added lines in 3 files covered. (37.5%)

33 existing lines in 2 files now uncovered.

1584 of 3406 relevant lines covered (46.51%)

828.48 hits per line

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

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

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

35
    private readonly EWKBReader $ewkbReader;
36
    private readonly EWKBWriter $ewkbWriter;
37

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

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

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

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

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

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

68
            return $geosGeometry;
10✔
69
        }
70

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

288
            // giving a matrix should always return a boolean
289
            assert(is_bool($result));
290

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

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

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

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

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

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

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

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

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

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

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

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

376
    public function split(Geometry $g, Geometry $blade) : Geometry
377
    {
378
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
×
379
    }
380

381
    public function lineInterpolatePoint(LineString $linestring, float $fraction) : Point
382
    {
383
        try {
384
            $result = $this->fromGEOS($this->toGEOS($linestring)->interpolate($fraction, true));
4✔
NEW
385
        } catch (\Exception $e) {
×
NEW
386
            throw GeometryEngineException::operationNotSupportedByEngine($e);
×
387
        }
388

389
        if (! $result instanceof Point) {
4✔
NEW
390
            throw new GeometryEngineException('This operation yielded the wrong geometry type: ' . $result::class);
×
391
        }
392

393
        return $result;
4✔
394
    }
395

396
    public function lineInterpolateEquidistantPoints(LineString $linestring, float $fraction) : Point|MultiPoint
397
    {
NEW
398
        throw GeometryEngineException::unimplementedMethod(__METHOD__);
×
399
    }
400
}
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