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

brick / geo / 14057076899

25 Mar 2025 10:14AM UTC coverage: 52.154% (-13.7%) from 65.828%
14057076899

push

github

BenMorel
Wip

0 of 383 new or added lines in 14 files covered. (0.0%)

141 existing lines in 12 files now uncovered.

1634 of 3133 relevant lines covered (52.15%)

396.62 hits per line

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

0.0
/src/Engine/PostgisEngine.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\Database\DatabaseDriver;
9
use Brick\Geo\Engine\Database\Query\BinaryValue;
10
use Brick\Geo\Engine\Database\Query\Query;
11
use Brick\Geo\Engine\Database\Query\ScalarValue;
12
use Brick\Geo\Engine\Database\Result\Row;
13
use Brick\Geo\Engine\Internal\TypeChecker;
14
use Brick\Geo\Exception\GeometryEngineException;
15
use Brick\Geo\Geometry;
16
use Brick\Geo\Io\EwkbReader;
17
use Brick\Geo\Io\EwkbWriter;
18
use Brick\Geo\LineString;
19
use Brick\Geo\MultiCurve;
20
use Brick\Geo\MultiPoint;
21
use Brick\Geo\MultiSurface;
22
use Brick\Geo\Point;
23
use Brick\Geo\Surface;
24
use Override;
25

26
/**
27
 * Database engine based on PostgreSQL with the PostGIS extension.
28
 */
29
final readonly class PostgisEngine implements GeometryEngine
30
{
31
    private EwkbReader $ewkbReader;
32
    private EwkbWriter $ewkbWriter;
33

34
    public function __construct(
35
        private DatabaseDriver $driver,
36
        // TODO
37
        private bool $useProxy = true,
38
    ) {
NEW
39
        $this->ewkbReader = new EwkbReader();
×
NEW
40
        $this->ewkbWriter = new EwkbWriter();
×
41
    }
42

43
    /**
44
     * Builds and executes a SQL query for a GIS function.
45
     *
46
     * @param string                $function        The SQL GIS function to execute.
47
     * @param list<Geometry|scalar> $parameters      The Geometry objects or scalar values to pass as parameters.
48
     * @param bool                  $returnsGeometry Whether the GIS function returns a Geometry.
49
     *
50
     * @throws GeometryEngineException
51
     */
52
    private function query(string $function, array $parameters, bool $returnsGeometry) : Row
53
    {
NEW
54
        $query = ['SELECT '];
×
55

NEW
56
        if ($returnsGeometry) {
×
NEW
57
            $query[] = 'ST_AsEWKB(';
×
58
        }
59

NEW
60
        $query[] = $function . '(';
×
61

NEW
62
        foreach ($parameters as $key => $parameter) {
×
NEW
63
            if ($key !== 0) {
×
NEW
64
                $query[] = ',';
×
65
            }
66

NEW
67
            if ($parameter instanceof Geometry) {
×
NEW
68
                $query[] = 'ST_GeomFromEWKB(';
×
NEW
69
                $query[] = new BinaryValue($this->ewkbWriter->write($parameter));
×
NEW
70
                $query[] = ')';
×
71
            } else {
NEW
72
                $query[] = new ScalarValue($parameter);
×
73
            }
74
        }
75

NEW
76
        $query[] = ')';
×
77

NEW
78
        if ($returnsGeometry) {
×
NEW
79
            $query[] = ')';
×
80
        }
81

NEW
82
        return $this->driver->executeQuery(...$query);
×
83
    }
84

85
    /**
86
     * Queries a GIS function returning a boolean value.
87
     *
88
     * @param string          $function      The SQL GIS function to execute.
89
     * @param Geometry|scalar ...$parameters The Geometry objects or scalar values to pass as parameters.
90
     *
91
     * @throws GeometryEngineException
92
     */
93
    private function queryBool(string $function, Geometry|string|float|int|bool ...$parameters) : bool
94
    {
NEW
95
        return $this->query($function, $parameters, false)->get(0)->asBool();
×
96
    }
97

98
    /**
99
     * Queries a GIS function returning a floating point value.
100
     *
101
     * @param string          $function      The SQL GIS function to execute.
102
     * @param Geometry|scalar ...$parameters The Geometry objects or scalar values to pass as parameters.
103
     *
104
     * @throws GeometryEngineException
105
     */
106
    private function queryFloat(string $function, Geometry|string|float|int|bool ...$parameters) : float
107
    {
NEW
108
        return $this->query($function, $parameters, false)->get(0)->asFloat();
×
109
    }
110

111
    /**
112
     * Queries a GIS function returning a Geometry object.
113
     *
114
     * @param string             $function   The SQL GIS function to execute.
115
     * @param Geometry|scalar ...$parameters The Geometry objects or scalar values to pass as parameters.
116
     *
117
     * @throws GeometryEngineException
118
     */
119
    private function queryGeometry(string $function, Geometry|string|float|int|bool ...$parameters) : Geometry
120
    {
NEW
121
        $ewkb = $this->query($function, $parameters, true)->get(0)->asBinary();
×
122

NEW
123
        return $this->ewkbReader->read($ewkb);
×
124
    }
125

126
//    /**
127
//     * @return class-string<Proxy\ProxyInterface&Geometry>
128
//     *
129
//     * @throws GeometryEngineException
130
//     */
131
//    private function getProxyClassName(string $geometryType) : string
132
//    {
133
//        $proxyClasses = [
134
//            'CIRCULARSTRING'     => Proxy\CircularStringProxy::class,
135
//            'COMPOUNDCURVE'      => Proxy\CompoundCurveProxy::class,
136
//            'CURVE'              => Proxy\CurveProxy::class,
137
//            'CURVEPOLYGON'       => Proxy\CurvePolygonProxy::class,
138
//            'GEOMCOLLECTION'     => Proxy\GeometryCollectionProxy::class, /* MySQL 8 - https://github.com/brick/geo/pull/33 */
139
//            'GEOMETRY'           => Proxy\GeometryProxy::class,
140
//            'GEOMETRYCOLLECTION' => Proxy\GeometryCollectionProxy::class,
141
//            'LINESTRING'         => Proxy\LineStringProxy::class,
142
//            'MULTICURVE'         => Proxy\MultiCurveProxy::class,
143
//            'MULTILINESTRING'    => Proxy\MultiLineStringProxy::class,
144
//            'MULTIPOINT'         => Proxy\MultiPointProxy::class,
145
//            'MULTIPOLYGON'       => Proxy\MultiPolygonProxy::class,
146
//            'MULTISURFACE'       => Proxy\MultiSurfaceProxy::class,
147
//            'POINT'              => Proxy\PointProxy::class,
148
//            'POLYGON'            => Proxy\PolygonProxy::class,
149
//            'POLYHEDRALSURFACE'  => Proxy\PolyhedralSurfaceProxy::class,
150
//            'SURFACE'            => Proxy\SurfaceProxy::class,
151
//            'TIN'                => Proxy\TinProxy::class,
152
//            'TRIANGLE'           => Proxy\TriangleProxy::class
153
//        ];
154
//
155
//        $geometryType = strtoupper($geometryType);
156
//        $geometryType = preg_replace('/^ST_/', '', $geometryType);
157
//        assert($geometryType !== null);
158
//        $geometryType = preg_replace('/ .*/', '', $geometryType);
159
//        assert($geometryType !== null);
160
//
161
//        if (! isset($proxyClasses[$geometryType])) {
162
//            throw new GeometryEngineException('Unknown geometry type: ' . $geometryType);
163
//        }
164
//
165
//        return $proxyClasses[$geometryType];
166
//    }
167

168
    #[Override]
169
    public function contains(Geometry $a, Geometry $b) : bool
170
    {
NEW
171
        return $this->queryBool('ST_Contains', $a, $b);
×
172
    }
173

174
    #[Override]
175
    public function intersects(Geometry $a, Geometry $b) : bool
176
    {
NEW
177
        return $this->queryBool('ST_Intersects', $a, $b);
×
178
    }
179

180
    #[Override]
181
    public function union(Geometry $a, Geometry $b) : Geometry
182
    {
NEW
183
        return $this->queryGeometry('ST_Union', $a, $b);
×
184
    }
185

186
    #[Override]
187
    public function intersection(Geometry $a, Geometry $b) : Geometry
188
    {
NEW
189
        return $this->queryGeometry('ST_Intersection', $a, $b);
×
190
    }
191

192
    #[Override]
193
    public function difference(Geometry $a, Geometry $b) : Geometry
194
    {
NEW
195
        return $this->queryGeometry('ST_Difference', $a, $b);
×
196
    }
197

198
    #[Override]
199
    public function envelope(Geometry $g) : Geometry
200
    {
NEW
201
        return $this->queryGeometry('ST_Envelope', $g);
×
202
    }
203

204
    #[Override]
205
    public function centroid(Geometry $g) : Point
206
    {
NEW
207
        $centroid = $this->queryGeometry('ST_Centroid', $g);
×
NEW
208
        TypeChecker::check($centroid, Point::class);
×
209

NEW
210
        return $centroid;
×
211
    }
212

213
    #[Override]
214
    public function pointOnSurface(Surface|MultiSurface $g) : Point
215
    {
NEW
216
        $pointOnSurface = $this->queryGeometry('ST_PointOnSurface', $g);
×
NEW
217
        TypeChecker::check($pointOnSurface, Point::class);
×
218

NEW
219
        return $pointOnSurface;
×
220
    }
221

222
    #[Override]
223
    public function length(Curve|MultiCurve $g) : float
224
    {
NEW
225
        return $this->queryFloat('ST_Length', $g);
×
226
    }
227

228
    #[Override]
229
    public function area(Surface|MultiSurface $g) : float
230
    {
NEW
231
        return $this->queryFloat('ST_Area', $g);
×
232
    }
233

234
    #[Override]
235
    public function azimuth(Point $observer, Point $subject) : float
236
    {
NEW
237
        return $this->queryFloat('ST_Azimuth', $observer, $subject);
×
238
    }
239

240
    #[Override]
241
    public function boundary(Geometry $g) : Geometry
242
    {
NEW
243
        return $this->queryGeometry('ST_Boundary', $g);
×
244
    }
245

246
    #[Override]
247
    public function isValid(Geometry $g) : bool
248
    {
NEW
249
        return $this->queryBool('ST_IsValid', $g);
×
250
    }
251

252
    #[Override]
253
    public function isClosed(Geometry $g) : bool
254
    {
NEW
255
        return $this->queryBool('ST_IsClosed', $g);
×
256
    }
257

258
    #[Override]
259
    public function isSimple(Geometry $g) : bool
260
    {
NEW
261
        return $this->queryBool('ST_IsSimple', $g);
×
262
    }
263

264
    #[Override]
265
    public function isRing(Curve $curve) : bool
266
    {
NEW
267
        return $this->queryBool('ST_IsRing', $curve);
×
268
    }
269

270
    #[Override]
271
    public function makeValid(Geometry $g) : Geometry
272
    {
NEW
273
        return $this->queryGeometry('ST_MakeValid', $g);
×
274
    }
275

276
    #[Override]
277
    public function equals(Geometry $a, Geometry $b) : bool
278
    {
NEW
279
        return $this->queryBool('ST_Equals', $a, $b);
×
280
    }
281

282
    #[Override]
283
    public function disjoint(Geometry $a, Geometry $b) : bool
284
    {
NEW
285
        return $this->queryBool('ST_Disjoint', $a, $b);
×
286
    }
287

288
    #[Override]
289
    public function touches(Geometry $a, Geometry $b) : bool
290
    {
NEW
291
        return $this->queryBool('ST_Touches', $a, $b);
×
292
    }
293

294
    #[Override]
295
    public function crosses(Geometry $a, Geometry $b) : bool
296
    {
NEW
297
        return $this->queryBool('ST_Crosses', $a, $b);
×
298
    }
299

300
    #[Override]
301
    public function within(Geometry $a, Geometry $b) : bool
302
    {
NEW
303
        return $this->queryBool('ST_Within', $a, $b);
×
304
    }
305

306
    #[Override]
307
    public function overlaps(Geometry $a, Geometry $b) : bool
308
    {
NEW
309
        return $this->queryBool('ST_Overlaps', $a, $b);
×
310
    }
311

312
    #[Override]
313
    public function relate(Geometry $a, Geometry $b, string $matrix) : bool
314
    {
NEW
315
        return $this->queryBool('ST_Relate', $a, $b, $matrix);
×
316
    }
317

318
    #[Override]
319
    public function locateAlong(Geometry $g, float $mValue) : Geometry
320
    {
NEW
321
        return $this->queryGeometry('ST_LocateAlong', $g, $mValue);
×
322
    }
323

324
    #[Override]
325
    public function locateBetween(Geometry $g, float $mStart, float $mEnd) : Geometry
326
    {
NEW
327
        return $this->queryGeometry('ST_LocateBetween', $g, $mStart, $mEnd);
×
328
    }
329

330
    #[Override]
331
    public function distance(Geometry $a, Geometry $b) : float
332
    {
NEW
333
        return $this->queryFloat('ST_Distance', $a, $b);
×
334
    }
335

336
    #[Override]
337
    public function buffer(Geometry $g, float $distance) : Geometry
338
    {
NEW
339
        return $this->queryGeometry('ST_Buffer', $g, $distance);
×
340
    }
341

342
    #[Override]
343
    public function convexHull(Geometry $g) : Geometry
344
    {
NEW
345
        return $this->queryGeometry('ST_ConvexHull', $g);
×
346
    }
347

348
    #[Override]
349
    public function concaveHull(Geometry $g, float $convexity, bool $allowHoles) : Geometry
350
    {
NEW
351
        return $this->queryGeometry('ST_ConcaveHull', $g, $convexity, $allowHoles);
×
352
    }
353

354
    #[Override]
355
    public function symDifference(Geometry $a, Geometry $b) : Geometry
356
    {
NEW
357
        return $this->queryGeometry('ST_SymDifference', $a, $b);
×
358
    }
359

360
    #[Override]
361
    public function snapToGrid(Geometry $g, float $size) : Geometry
362
    {
NEW
363
        return $this->queryGeometry('ST_SnapToGrid', $g, $size);
×
364
    }
365

366
    #[Override]
367
    public function simplify(Geometry $g, float $tolerance) : Geometry
368
    {
NEW
369
        return $this->queryGeometry('ST_Simplify', $g, $tolerance);
×
370
    }
371

372
    #[Override]
373
    public function maxDistance(Geometry $a, Geometry $b) : float
374
    {
NEW
375
        return $this->queryFloat('ST_MaxDistance', $a, $b);
×
376
    }
377

378
    #[Override]
379
    public function transform(Geometry $g, int $srid) : Geometry
380
    {
NEW
381
        return $this->queryGeometry('ST_Transform', $g, $srid);
×
382
    }
383

384
    #[Override]
385
    public function split(Geometry $g, Geometry $blade) : Geometry
386
    {
NEW
387
        return $this->queryGeometry('ST_Split', $g, $blade);
×
388
    }
389

390
    #[Override]
391
    public function lineInterpolatePoint(LineString $lineString, float $fraction) : Point
392
    {
NEW
393
        $result = $this->queryGeometry('ST_LineInterpolatePoint', $lineString, $fraction);
×
NEW
394
        TypeChecker::check($result, Point::class);
×
395

NEW
396
        return $result;
×
397
    }
398

399
    #[Override]
400
    public function lineInterpolatePoints(LineString $lineString, float $fraction) : MultiPoint
401
    {
NEW
402
        $result = $this->queryGeometry('ST_LineInterpolatePoints', $lineString, $fraction);
×
403

NEW
404
        if ($result instanceof MultiPoint) {
×
NEW
405
            return $result;
×
406
        }
407

NEW
408
        TypeChecker::check($result, Point::class);
×
409

410
        // POINT EMPTY
NEW
411
        if ($result->isEmpty()) {
×
NEW
412
            return new MultiPoint($result->coordinateSystem());
×
413
        }
414

415
        // POINT
NEW
416
        return MultiPoint::of($result);
×
417
    }
418
}
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