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

brick / geo / 13831530637

13 Mar 2025 08:44AM UTC coverage: 44.548% (-5.2%) from 49.787%
13831530637

push

github

BenMorel
Merge WKTParser & EWKTParser, add WKTTokenType

31 of 32 new or added lines in 3 files covered. (96.88%)

208 existing lines in 11 files now uncovered.

1573 of 3531 relevant lines covered (44.55%)

298.16 hits per line

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

96.84
/src/IO/AbstractWKTReader.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Brick\Geo\IO;
6

7
use Brick\Geo\Curve;
8
use Brick\Geo\Geometry;
9
use Brick\Geo\Point;
10
use Brick\Geo\LineString;
11
use Brick\Geo\CircularString;
12
use Brick\Geo\CompoundCurve;
13
use Brick\Geo\Polygon;
14
use Brick\Geo\CurvePolygon;
15
use Brick\Geo\MultiPoint;
16
use Brick\Geo\MultiLineString;
17
use Brick\Geo\MultiPolygon;
18
use Brick\Geo\GeometryCollection;
19
use Brick\Geo\PolyhedralSurface;
20
use Brick\Geo\TIN;
21
use Brick\Geo\Triangle;
22
use Brick\Geo\CoordinateSystem;
23
use Brick\Geo\Exception\GeometryIOException;
24

25
/**
26
 * Base class for WKTReader and EWKTReader.
27
 *
28
 * @internal
29
 */
30
abstract class AbstractWKTReader
31
{
32
    /**
33
     * @throws GeometryIOException
34
     */
35
    protected function readGeometry(WKTParser $parser, int $srid) : Geometry
36
    {
37
        $geometryType = $parser->getNextWord();
4,350✔
38
        $word = $parser->getOptionalNextWord();
4,350✔
39

40
        $hasZ = false;
4,350✔
41
        $hasM = false;
4,350✔
42
        $isEmpty    = false;
4,350✔
43

44
        if ($word !== null) {
4,350✔
45
            if ($word === 'Z') {
3,186✔
46
                $hasZ = true;
968✔
47
            } elseif ($word === 'M') {
2,226✔
48
                $hasM = true;
888✔
49
            } elseif ($word === 'ZM') {
1,340✔
50
                $hasZ = true;
852✔
51
                $hasM = true;
852✔
52
            } elseif ($word === 'EMPTY') {
488✔
53
                $isEmpty = true;
488✔
54
            } else {
55
                throw new GeometryIOException('Unexpected word in WKT: ' . $word);
×
56
            }
57

58
            if (! $isEmpty) {
3,186✔
59
                $word = $parser->getOptionalNextWord();
2,702✔
60

61
                if ($word === 'EMPTY') {
2,702✔
62
                    $isEmpty = true;
1,104✔
63
                } elseif ($word !== null) {
1,610✔
64
                    throw new GeometryIOException('Unexpected word in WKT: ' . $word);
×
65
                }
66
            }
67
        }
68

69
        $cs = new CoordinateSystem($hasZ, $hasM, $srid);
4,350✔
70

71
        switch ($geometryType) {
72
            case 'POINT':
4,350✔
73
                if ($isEmpty) {
576✔
74
                    return new Point($cs);
122✔
75
                }
76

77
                return $this->readPointText($parser, $cs);
460✔
78

79
            case 'LINESTRING':
3,996✔
80
                if ($isEmpty) {
880✔
81
                    return new LineString($cs);
168✔
82
                }
83

84
                return $this->readLineStringText($parser, $cs);
714✔
85

86
            case 'CIRCULARSTRING':
3,340✔
87
                if ($isEmpty) {
674✔
88
                    return new CircularString($cs);
136✔
89
                }
90

91
                return $this->readCircularStringText($parser, $cs);
538✔
92

93
            case 'COMPOUNDCURVE':
3,030✔
94
                if ($isEmpty) {
444✔
95
                    return new CompoundCurve($cs);
168✔
96
                }
97

98
                return $this->readCompoundCurveText($parser, $cs);
276✔
99

100
            case 'POLYGON':
2,646✔
101
                if ($isEmpty) {
536✔
102
                    return new Polygon($cs);
122✔
103
                }
104

105
                return $this->readPolygonText($parser, $cs);
414✔
106

107
            case 'CURVEPOLYGON':
2,148✔
108
                if ($isEmpty) {
318✔
109
                    return new CurvePolygon($cs);
132✔
110
                }
111

112
                return $this->readCurvePolygonText($parser, $cs);
186✔
113

114
            case 'MULTIPOINT':
1,830✔
115
                if ($isEmpty) {
294✔
116
                    return new MultiPoint($cs);
102✔
117
                }
118

119
                return $this->readMultiPointText($parser, $cs);
192✔
120

121
            case 'MULTILINESTRING':
1,536✔
122
                if ($isEmpty) {
300✔
123
                    return new MultiLineString($cs);
100✔
124
                }
125

126
                return $this->readMultiLineStringText($parser, $cs);
200✔
127

128
            case 'MULTIPOLYGON':
1,238✔
129
                if ($isEmpty) {
304✔
130
                    return new MultiPolygon($cs);
102✔
131
                }
132

133
                return $this->readMultiPolygonText($parser, $cs);
202✔
134

135
            case 'GEOMETRYCOLLECTION':
934✔
136
                if ($isEmpty) {
278✔
137
                    return new GeometryCollection($cs);
126✔
138
                }
139

140
                return $this->readGeometryCollectionText($parser, $cs);
152✔
141

142
            case 'POLYHEDRALSURFACE':
656✔
143
                if ($isEmpty) {
302✔
144
                    return new PolyhedralSurface($cs);
138✔
145
                }
146

147
                return $this->readPolyhedralSurfaceText($parser, $cs);
164✔
148

149
            case 'TIN':
354✔
150
                if ($isEmpty) {
182✔
151
                    return new TIN($cs);
92✔
152
                }
153

154
                return $this->readTINText($parser, $cs);
90✔
155

156
            case 'TRIANGLE':
182✔
157
                if ($isEmpty) {
182✔
158
                    return new Triangle($cs);
88✔
159
                }
160

161
            return $this->readTriangleText($parser, $cs);
94✔
162
        }
163

164
        throw new GeometryIOException('Unknown geometry type: ' . $geometryType);
×
165
    }
166

167
    /**
168
     * x y
169
     */
170
    private function readPoint(WKTParser $parser, CoordinateSystem $cs) : Point
171
    {
172
        $dim = $cs->coordinateDimension();
2,812✔
173
        $coords = [];
2,812✔
174

175
        for ($i = 0; $i < $dim; $i++) {
2,812✔
176
            $coords[] = $parser->getNextNumber();
2,812✔
177
        }
178

179
        return new Point($cs, ...$coords);
2,812✔
180
    }
181

182
    /**
183
     * (x y)
184
     */
185
    private function readPointText(WKTParser $parser, CoordinateSystem $cs) : Point
186
    {
187
        $parser->matchOpener();
460✔
188
        $point = $this->readPoint($parser, $cs);
460✔
189
        $parser->matchCloser();
460✔
190

191
        return $point;
460✔
192
    }
193

194
    /**
195
     * (x y, ...)
196
     * ((x, y), ...)
197
     *
198
     * @return Point[]
199
     */
200
    private function readMultiPoint(WKTParser $parser, CoordinateSystem $cs) : array
201
    {
202
        $parser->matchOpener();
2,528✔
203
        $points = [];
2,528✔
204

205
        do {
206
            $hasExtraParentheses = $parser->matchOptionalOpener();
2,528✔
207
            $points[] = $this->readPoint($parser, $cs);
2,528✔
208
            if ($hasExtraParentheses) {
2,528✔
209
                $parser->matchCloser();
24✔
210
            }
211

212
            $nextToken = $parser->getNextCloserOrComma();
2,528✔
213
        } while ($nextToken === ',');
2,528✔
214

215
        return $points;
2,528✔
216
    }
217

218
    /**
219
     * (x y, ...)
220
     */
221
    private function readLineStringText(WKTParser $parser, CoordinateSystem $cs) : LineString
222
    {
223
        $points = $this->readMultiPoint($parser, $cs);
2,168✔
224

225
        return new LineString($cs, ...$points);
2,168✔
226
    }
227

228
    /**
229
     * (x y, ...)
230
     */
231
    private function readCircularStringText(WKTParser $parser, CoordinateSystem $cs) : CircularString
232
    {
233
        $points = $this->readMultiPoint($parser, $cs);
538✔
234

235
        return new CircularString($cs, ...$points);
538✔
236
    }
237

238
    /**
239
     * @throws GeometryIOException
240
     */
241
    private function readCompoundCurveText(WKTParser $parser, CoordinateSystem $cs) : CompoundCurve
242
    {
243
        $parser->matchOpener();
276✔
244
        $curves = [];
276✔
245

246
        do {
247
            if ($parser->isNextOpenerOrWord()) {
276✔
248
                $curves[] = $this->readLineStringText($parser, $cs);
270✔
249
            } else {
250
                $curve = $this->readGeometry($parser, $cs->SRID());
258✔
251

252
                if (! $curve instanceof LineString && ! $curve instanceof CircularString) {
256✔
UNCOV
253
                    throw new GeometryIOException('Expected LineString|CircularString, got ' . $curve->geometryType());
×
254
                }
255

256
                $curves[] = $curve;
256✔
257
            }
258

259
            $nextToken = $parser->getNextCloserOrComma();
272✔
260
        } while ($nextToken === ',');
272✔
261

262
        return new CompoundCurve($cs, ...$curves);
272✔
263
    }
264

265
    /**
266
     * (x y, ...)
267
     */
268
    private function readMultiPointText(WKTParser $parser, CoordinateSystem $cs) : MultiPoint
269
    {
270
        $points = $this->readMultiPoint($parser, $cs);
192✔
271

272
        return new MultiPoint($cs, ...$points);
192✔
273
    }
274

275
    /**
276
     * ((x y, ...), ...)
277
     *
278
     * @return LineString[]
279
     */
280
    private function readMultiLineString(WKTParser $parser, CoordinateSystem $cs) : array
281
    {
282
        $parser->matchOpener();
1,148✔
283
        $lineStrings = [];
1,148✔
284

285
        do {
286
            $lineStrings[] = $this->readLineStringText($parser, $cs);
1,148✔
287
            $nextToken = $parser->getNextCloserOrComma();
1,148✔
288
        } while ($nextToken === ',');
1,148✔
289

290
        return $lineStrings;
1,148✔
291
    }
292

293
    /**
294
     * ((x y, ...), ...)
295
     */
296
    private function readPolygonText(WKTParser $parser, CoordinateSystem $cs) : Polygon
297
    {
298
        $rings = $this->readMultiLineString($parser, $cs);
770✔
299

300
        return new Polygon($cs, ...$rings);
770✔
301
    }
302

303
    /**
304
     * @throws GeometryIOException
305
     */
306
    private function readCurvePolygonText(WKTParser $parser, CoordinateSystem $cs) : CurvePolygon
307
    {
308
        $parser->matchOpener();
186✔
309
        $curves = [];
186✔
310

311
        do {
312
            if ($parser->isNextOpenerOrWord()) {
186✔
313
                $curves[] = $this->readLineStringText($parser, $cs);
176✔
314
            } else {
315
                $curve = $this->readGeometry($parser, $cs->SRID());
148✔
316

317
                if (! $curve instanceof Curve) {
148✔
UNCOV
318
                    throw new GeometryIOException('Expected Curve, got ' . $curve->geometryType());
×
319
                }
320

321
                $curves[] = $curve;
148✔
322
            }
323

324
            $nextToken = $parser->getNextCloserOrComma();
186✔
325
        } while ($nextToken === ',');
186✔
326

327
        return new CurvePolygon($cs, ...$curves);
186✔
328
    }
329

330
    /**
331
     * ((x y, ...), ...)
332
     */
333
    private function readTriangleText(WKTParser $parser, CoordinateSystem $cs) : Triangle
334
    {
335
        $rings = $this->readMultiLineString($parser, $cs);
178✔
336

337
        return new Triangle($cs, ...$rings);
178✔
338
    }
339

340
    /**
341
     * ((x y, ...), ...)
342
     */
343
    private function readMultiLineStringText(WKTParser $parser, CoordinateSystem $cs) : MultiLineString
344
    {
345
        $lineStrings = $this->readMultiLineString($parser, $cs);
200✔
346

347
        return new MultiLineString($cs, ...$lineStrings);
200✔
348
    }
349

350
    /**
351
     * (((x y, ...), ...), ...)
352
     */
353
    private function readMultiPolygonText(WKTParser $parser, CoordinateSystem $cs) : MultiPolygon
354
    {
355
        $parser->matchOpener();
202✔
356
        $polygons = [];
202✔
357

358
        do {
359
            $polygons[] = $this->readPolygonText($parser, $cs);
202✔
360
            $nextToken = $parser->getNextCloserOrComma();
202✔
361
        } while ($nextToken === ',');
202✔
362

363
        return new MultiPolygon($cs, ...$polygons);
202✔
364
    }
365

366
    private function readGeometryCollectionText(WKTParser $parser, CoordinateSystem $cs) : GeometryCollection
367
    {
368
        $parser->matchOpener();
152✔
369
        $geometries = [];
152✔
370

371
        do {
372
            $geometries[] = $this->readGeometry($parser, $cs->SRID());
152✔
373
            $nextToken = $parser->getNextCloserOrComma();
152✔
374
        } while ($nextToken === ',');
152✔
375

376
        return new GeometryCollection($cs, ...$geometries);
152✔
377
    }
378

379
    private function readPolyhedralSurfaceText(WKTParser $parser, CoordinateSystem $cs) : PolyhedralSurface
380
    {
381
        $parser->matchOpener();
164✔
382
        $patches = [];
164✔
383

384
        do {
385
            $patches[] = $this->readPolygonText($parser, $cs);
164✔
386
            $nextToken = $parser->getNextCloserOrComma();
164✔
387
        } while ($nextToken === ',');
164✔
388

389
        return new PolyhedralSurface($cs, ...$patches);
164✔
390
    }
391

392
    private function readTINText(WKTParser $parser, CoordinateSystem $cs) : TIN
393
    {
394
        $parser->matchOpener();
90✔
395
        $patches = [];
90✔
396

397
        do {
398
            $patches[] = $this->readTriangleText($parser, $cs);
90✔
399
            $nextToken = $parser->getNextCloserOrComma();
90✔
400
        } while ($nextToken === ',');
90✔
401

402
        return new TIN($cs, ...$patches);
90✔
403
    }
404
}
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