Coverage Report

Created: 2026-09-26 08:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/flatgeobuf/geometryreader.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  FlatGeobuf driver
4
 * Purpose:  Implements GeometryReader class.
5
 * Author:   Björn Harrtell <bjorn at wololo dot org>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2018-2019, Björn Harrtell <bjorn at wololo dot org>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "ogrsf_frmts.h"
14
#include "ogr_p.h"
15
16
#include "geometryreader.h"
17
#include "cplerrors.h"
18
#include "ogr_flatgeobuf.h"
19
20
using namespace flatbuffers;
21
using namespace FlatGeobuf;
22
using namespace ogr_flatgeobuf;
23
24
static std::nullptr_t CPLErrorInvalidLength(const char *message)
25
84
{
26
84
    CPLError(CE_Failure, CPLE_AppDefined, "Invalid length detected: %s",
27
84
             message);
28
84
    return nullptr;
29
84
}
30
31
OGRPoint *GeometryReader::readPoint()
32
1.27k
{
33
1.27k
    const auto offsetXy = m_offset * 2;
34
    // +1 because we need to read x and y
35
1.27k
    if (offsetXy + 1 >= m_length)
36
2
        return CPLErrorInvalidLength("XY data");
37
1.27k
    if (m_hasZ)
38
761
    {
39
761
        const auto z = m_geometry->z();
40
761
        if (z == nullptr)
41
0
            return CPLErrorInvalidPointer("Z data");
42
761
        if (m_offset >= z->size())
43
1
            return CPLErrorInvalidLength("Z data");
44
760
        const auto aZ = z->data();
45
760
        if (m_hasM)
46
568
        {
47
568
            const auto pM = m_geometry->m();
48
568
            if (pM == nullptr)
49
0
                return CPLErrorInvalidPointer("M data");
50
568
            if (m_offset >= pM->size())
51
1
                return CPLErrorInvalidLength("M data");
52
567
            const auto aM = pM->data();
53
567
            return new OGRPoint{EndianScalar(m_xy[offsetXy + 0]),
54
567
                                EndianScalar(m_xy[offsetXy + 1]),
55
567
                                EndianScalar(aZ[m_offset]),
56
567
                                EndianScalar(aM[m_offset])};
57
568
        }
58
192
        else
59
192
        {
60
192
            return new OGRPoint{EndianScalar(m_xy[offsetXy + 0]),
61
192
                                EndianScalar(m_xy[offsetXy + 1]),
62
192
                                EndianScalar(aZ[m_offset])};
63
192
        }
64
760
    }
65
511
    else if (m_hasM)
66
154
    {
67
154
        const auto pM = m_geometry->m();
68
154
        if (pM == nullptr)
69
0
            return CPLErrorInvalidPointer("M data");
70
154
        if (m_offset >= pM->size())
71
1
            return CPLErrorInvalidLength("M data");
72
153
        const auto aM = pM->data();
73
153
        return OGRPoint::createXYM(EndianScalar(m_xy[offsetXy + 0]),
74
153
                                   EndianScalar(m_xy[offsetXy + 1]),
75
153
                                   EndianScalar(aM[m_offset]));
76
154
    }
77
357
    else
78
357
    {
79
357
        return new OGRPoint{EndianScalar(m_xy[offsetXy + 0]),
80
357
                            EndianScalar(m_xy[offsetXy + 1])};
81
357
    }
82
1.27k
}
83
84
OGRMultiPoint *GeometryReader::readMultiPoint()
85
233
{
86
233
    auto length = m_length / 2;
87
233
    if (length >= feature_max_buffer_size)
88
0
        return CPLErrorInvalidLength("MultiPoint");
89
233
    auto mp = std::make_unique<OGRMultiPoint>();
90
1.36k
    for (uint32_t i = 0; i < length; i++)
91
1.13k
    {
92
1.13k
        m_offset = i;
93
1.13k
        const auto p = readPoint();
94
1.13k
        if (p == nullptr)
95
3
            return nullptr;
96
1.13k
        mp->addGeometryDirectly(p);
97
1.13k
    }
98
230
    return mp.release();
99
233
}
100
101
OGRMultiLineString *GeometryReader::readMultiLineString()
102
72
{
103
72
    const auto ends = m_geometry->ends();
104
72
    auto mls = std::make_unique<OGRMultiLineString>();
105
72
    if (ends == nullptr || ends->size() < 2)
106
42
    {
107
42
        m_length = m_length / 2;
108
42
        const auto part = readSimpleCurve<OGRLineString>();
109
42
        if (part == nullptr)
110
1
            return nullptr;
111
41
        mls->addGeometryDirectly(part);
112
41
    }
113
30
    else
114
30
    {
115
30
        m_offset = 0;
116
354
        for (uint32_t i = 0; i < ends->size(); i++)
117
353
        {
118
353
            const auto e = ends->Get(i);
119
353
            if (e < m_offset)
120
1
                return CPLErrorInvalidLength("MultiLineString");
121
352
            m_length = e - m_offset;
122
352
            const auto ls = readSimpleCurve<OGRLineString>();
123
352
            if (ls == nullptr)
124
28
                return nullptr;
125
324
            mls->addGeometryDirectly(ls);
126
324
            m_offset = e;
127
324
        }
128
30
    }
129
42
    return mls.release();
130
72
}
131
132
OGRErr GeometryReader::readSimpleCurve(OGRSimpleCurve *sc)
133
6.58k
{
134
6.58k
    if (m_offset > feature_max_buffer_size ||
135
5.58k
        m_length > feature_max_buffer_size - m_offset)
136
1.29k
        return CPLErrorInvalidSize("curve offset max");
137
5.28k
    const uint32_t offsetLen = m_length + m_offset;
138
5.28k
    if (offsetLen > m_xylength / 2)
139
370
        return CPLErrorInvalidSize("curve XY offset");
140
4.91k
    const auto ogrXY = reinterpret_cast<const OGRRawPoint *>(m_xy) + m_offset;
141
4.91k
    if (m_hasZ)
142
2.83k
    {
143
2.83k
        const auto pZ = m_geometry->z();
144
2.83k
        if (pZ == nullptr)
145
0
        {
146
0
            CPLErrorInvalidPointer("Z data");
147
0
            return OGRERR_CORRUPT_DATA;
148
0
        }
149
2.83k
        if (offsetLen > pZ->size())
150
197
            return CPLErrorInvalidSize("curve Z offset");
151
2.63k
        const auto aZ = pZ->data();
152
2.63k
        if (m_hasM)
153
401
        {
154
401
            const auto pM = m_geometry->m();
155
401
            if (pM == nullptr)
156
0
            {
157
0
                CPLErrorInvalidPointer("M data");
158
0
                return OGRERR_CORRUPT_DATA;
159
0
            }
160
401
            if (offsetLen > pM->size())
161
1
                return CPLErrorInvalidSize("curve M offset");
162
400
            const auto aM = pM->data();
163
400
#if CPL_IS_LSB
164
400
            sc->setPoints(m_length, ogrXY, aZ + m_offset, aM + m_offset);
165
#else
166
            sc->setNumPoints(m_length, false);
167
            for (uint32_t i = 0; i < m_length; i++)
168
            {
169
                sc->setPoint(i, EndianScalar(ogrXY[i].x),
170
                             EndianScalar(ogrXY[i].y),
171
                             EndianScalar(aZ[m_offset + i]),
172
                             EndianScalar(aM[m_offset + i]));
173
            }
174
#endif
175
400
        }
176
2.23k
        else
177
2.23k
        {
178
2.23k
#if CPL_IS_LSB
179
2.23k
            sc->setPoints(m_length, ogrXY, aZ + m_offset);
180
#else
181
            sc->setNumPoints(m_length, false);
182
            for (uint32_t i = 0; i < m_length; i++)
183
            {
184
                sc->setPoint(i, EndianScalar(ogrXY[i].x),
185
                             EndianScalar(ogrXY[i].y),
186
                             EndianScalar(aZ[m_offset + i]));
187
            }
188
#endif
189
2.23k
        }
190
2.63k
    }
191
2.08k
    else if (m_hasM)
192
258
    {
193
258
        const auto pM = m_geometry->m();
194
258
        if (pM == nullptr)
195
0
        {
196
0
            CPLErrorInvalidPointer("M data");
197
0
            return OGRERR_CORRUPT_DATA;
198
0
        }
199
258
        if (offsetLen > pM->size())
200
1
            return CPLErrorInvalidSize("curve M offset");
201
257
        const auto aM = pM->data();
202
257
#if CPL_IS_LSB
203
257
        sc->setPointsM(m_length, ogrXY, aM + m_offset);
204
#else
205
        sc->setNumPoints(m_length, false);
206
        for (uint32_t i = 0; i < m_length; i++)
207
        {
208
            sc->setPointM(i, EndianScalar(ogrXY[i].x), EndianScalar(ogrXY[i].y),
209
                          EndianScalar(aM[m_offset + i]));
210
        }
211
#endif
212
257
    }
213
1.82k
    else
214
1.82k
    {
215
1.82k
#if CPL_IS_LSB
216
1.82k
        sc->setPoints(m_length, ogrXY);
217
#else
218
        sc->setNumPoints(m_length, false);
219
        for (uint32_t i = 0; i < m_length; i++)
220
        {
221
            sc->setPoint(i, EndianScalar(ogrXY[i].x), EndianScalar(ogrXY[i].y));
222
        }
223
#endif
224
1.82k
    }
225
4.71k
    return OGRERR_NONE;
226
4.91k
}
227
228
OGRPolygon *GeometryReader::readPolygon()
229
1.62k
{
230
1.62k
    const auto ends = m_geometry->ends();
231
1.62k
    auto p = std::make_unique<OGRPolygon>();
232
1.62k
    if (ends == nullptr || ends->size() < 2)
233
1.29k
    {
234
1.29k
        m_length = m_length / 2;
235
1.29k
        const auto lr = readSimpleCurve<OGRLinearRing>();
236
1.29k
        if (lr == nullptr)
237
1
            return nullptr;
238
1.29k
        p->addRingDirectly(lr);
239
1.29k
    }
240
329
    else
241
329
    {
242
2.81k
        for (uint32_t i = 0; i < ends->size(); i++)
243
2.53k
        {
244
2.53k
            const auto e = ends->Get(i);
245
2.53k
            if (e < m_offset)
246
47
                return CPLErrorInvalidLength("Polygon");
247
2.48k
            m_length = e - m_offset;
248
2.48k
            const auto lr = readSimpleCurve<OGRLinearRing>();
249
2.48k
            m_offset = e;
250
2.48k
            if (lr == nullptr)
251
1.73k
                continue;
252
756
            p->addRingDirectly(lr);
253
756
        }
254
282
        if (p->IsEmpty())
255
10
            return nullptr;
256
282
    }
257
1.57k
    return p.release();
258
1.62k
}
259
260
OGRMultiPolygon *GeometryReader::readMultiPolygon()
261
654
{
262
654
    auto parts = m_geometry->parts();
263
654
    if (parts == nullptr)
264
1
        return CPLErrorInvalidPointer("parts data");
265
653
    auto mp = std::make_unique<OGRMultiPolygon>();
266
1.88k
    for (uoffset_t i = 0; i < parts->size(); i++)
267
1.23k
    {
268
1.23k
        auto g = std::unique_ptr<OGRGeometry>(
269
1.23k
            readPart(parts->Get(i), GeometryType::Polygon));
270
1.23k
        if (g == nullptr)
271
3
            return nullptr;
272
1.22k
        mp->addGeometryDirectly(g.release()->toPolygon());
273
1.22k
    }
274
650
    return mp.release();
275
653
}
276
277
OGRGeometryCollection *GeometryReader::readGeometryCollection()
278
75
{
279
75
    auto parts = m_geometry->parts();
280
75
    if (parts == nullptr)
281
3
        return CPLErrorInvalidPointer("parts data");
282
72
    auto gc = std::make_unique<OGRGeometryCollection>();
283
171
    for (uoffset_t i = 0; i < parts->size(); i++)
284
104
    {
285
104
        auto g = std::unique_ptr<OGRGeometry>(readPart(parts->Get(i)));
286
104
        if (g == nullptr)
287
5
            return nullptr;
288
99
        gc->addGeometryDirectly(g.release());
289
99
    }
290
67
    return gc.release();
291
72
}
292
293
OGRCompoundCurve *GeometryReader::readCompoundCurve()
294
34
{
295
34
    auto parts = m_geometry->parts();
296
34
    if (parts == nullptr)
297
1
        return CPLErrorInvalidPointer("parts data");
298
33
    auto cc = std::make_unique<OGRCompoundCurve>();
299
43
    for (uoffset_t i = 0; i < parts->size(); i++)
300
20
    {
301
20
        auto g = std::unique_ptr<OGRGeometry>(readPart(parts->Get(i)));
302
20
        if (dynamic_cast<OGRCurve *>(g.get()) == nullptr)
303
4
            return nullptr;
304
16
        auto poCurve = g.release()->toCurve();
305
16
        if (cc->addCurveDirectly(poCurve) != OGRERR_NONE)
306
6
        {
307
6
            delete poCurve;
308
6
            return nullptr;
309
6
        }
310
16
    }
311
23
    return cc.release();
312
33
}
313
314
OGRCurvePolygon *GeometryReader::readCurvePolygon()
315
583
{
316
583
    auto parts = m_geometry->parts();
317
583
    if (parts == nullptr)
318
1
        return CPLErrorInvalidPointer("parts data");
319
582
    auto cp = std::make_unique<OGRCurvePolygon>();
320
1.56k
    for (uoffset_t i = 0; i < parts->size(); i++)
321
997
    {
322
997
        auto g = std::unique_ptr<OGRGeometry>(readPart(parts->Get(i)));
323
997
        if (dynamic_cast<OGRCurve *>(g.get()) == nullptr)
324
11
            return nullptr;
325
986
        auto poCurve = g.release()->toCurve();
326
986
        if (cp->addRingDirectly(poCurve) != OGRERR_NONE)
327
5
        {
328
5
            delete poCurve;
329
5
            return nullptr;
330
5
        }
331
986
    }
332
566
    return cp.release();
333
582
}
334
335
OGRMultiCurve *GeometryReader::readMultiCurve()
336
678
{
337
678
    auto parts = m_geometry->parts();
338
678
    if (parts == nullptr)
339
2
        return CPLErrorInvalidPointer("parts data");
340
676
    auto mc = std::make_unique<OGRMultiCurve>();
341
1.64k
    for (uoffset_t i = 0; i < parts->size(); i++)
342
977
    {
343
977
        auto g = std::unique_ptr<OGRGeometry>(readPart(parts->Get(i)));
344
977
        if (dynamic_cast<OGRCurve *>(g.get()) == nullptr)
345
12
            return nullptr;
346
965
        mc->addGeometryDirectly(g.release());
347
965
    }
348
664
    return mc.release();
349
676
}
350
351
OGRMultiSurface *GeometryReader::readMultiSurface()
352
48
{
353
48
    auto parts = m_geometry->parts();
354
48
    if (parts == nullptr)
355
3
        return CPLErrorInvalidPointer("parts data");
356
45
    auto ms = std::make_unique<OGRMultiSurface>();
357
100
    for (uoffset_t i = 0; i < parts->size(); i++)
358
65
    {
359
65
        auto g = std::unique_ptr<OGRGeometry>(readPart(parts->Get(i)));
360
65
        if (dynamic_cast<OGRSurface *>(g.get()) == nullptr)
361
8
            return nullptr;
362
57
        auto poSubGeom = g.release();
363
57
        if (ms->addGeometryDirectly(poSubGeom) != OGRERR_NONE)
364
2
        {
365
2
            delete poSubGeom;
366
2
            return nullptr;
367
2
        }
368
57
    }
369
35
    return ms.release();
370
45
}
371
372
OGRPolyhedralSurface *GeometryReader::readPolyhedralSurface()
373
37
{
374
37
    auto parts = m_geometry->parts();
375
37
    if (parts == nullptr)
376
1
        return CPLErrorInvalidPointer("parts data");
377
36
    auto ps = std::make_unique<OGRPolyhedralSurface>();
378
38
    for (uoffset_t i = 0; i < parts->size(); i++)
379
15
    {
380
15
        auto g = std::unique_ptr<OGRGeometry>(readPart(parts->Get(i)));
381
15
        if (g == nullptr)
382
3
            return nullptr;
383
12
        auto poSubGeom = g.release();
384
12
        if (ps->addGeometryDirectly(poSubGeom) != OGRERR_NONE)
385
10
        {
386
10
            delete poSubGeom;
387
10
            return nullptr;
388
10
        }
389
12
    }
390
23
    return ps.release();
391
36
}
392
393
OGRTriangulatedSurface *GeometryReader::readTIN()
394
161
{
395
161
    const auto ends = m_geometry->ends();
396
161
    auto ts = std::make_unique<OGRTriangulatedSurface>();
397
161
    if (ends == nullptr || ends->size() < 2)
398
40
    {
399
40
        m_length = m_length / 2;
400
40
        if (m_length != 4)
401
1
            return CPLErrorInvalidLength("TIN");
402
39
        const auto lr = readSimpleCurve<OGRLinearRing>();
403
39
        if (lr == nullptr)
404
0
            return nullptr;
405
39
        auto t = new OGRTriangle();
406
39
        t->addRingDirectly(lr);
407
39
        ts->addGeometryDirectly(t);
408
39
    }
409
121
    else
410
121
    {
411
333
        for (uint32_t i = 0; i < ends->size(); i++)
412
241
        {
413
241
            const auto e = ends->Get(i);
414
241
            if (e < m_offset)
415
1
                return CPLErrorInvalidLength("TIN");
416
240
            m_length = e - m_offset;
417
240
            if (m_length != 4)
418
28
                return CPLErrorInvalidLength("TIN");
419
212
            const auto lr = readSimpleCurve<OGRLinearRing>();
420
212
            m_offset = e;
421
212
            if (lr == nullptr)
422
103
                continue;
423
109
            auto t = new OGRTriangle();
424
109
            t->addRingDirectly(lr);
425
109
            ts->addGeometryDirectly(t);
426
109
        }
427
92
        if (ts->IsEmpty())
428
1
            return nullptr;
429
92
    }
430
130
    return ts.release();
431
161
}
432
433
OGRTriangle *GeometryReader::readTriangle()
434
12
{
435
12
    m_length = m_length / 2;
436
12
    if (m_length != 4)
437
1
        return CPLErrorInvalidLength("readTriangle");
438
11
    auto lr = readSimpleCurve<OGRLinearRing>();
439
11
    if (lr == nullptr)
440
1
        return nullptr;
441
10
    auto t = new OGRTriangle();
442
10
    t->addRingDirectly(lr);
443
10
    return t;
444
11
}
445
446
OGRGeometry *GeometryReader::read()
447
6.53k
{
448
    // nested types
449
6.53k
    switch (m_geometryType)
450
6.53k
    {
451
75
        case GeometryType::GeometryCollection:
452
75
            return readGeometryCollection();
453
654
        case GeometryType::MultiPolygon:
454
654
            return readMultiPolygon();
455
34
        case GeometryType::CompoundCurve:
456
34
            return readCompoundCurve();
457
583
        case GeometryType::CurvePolygon:
458
583
            return readCurvePolygon();
459
678
        case GeometryType::MultiCurve:
460
678
            return readMultiCurve();
461
48
        case GeometryType::MultiSurface:
462
48
            return readMultiSurface();
463
37
        case GeometryType::PolyhedralSurface:
464
37
            return readPolyhedralSurface();
465
4.42k
        default:
466
4.42k
            break;
467
6.53k
    }
468
469
    // if not nested must have geometry data
470
4.42k
    const auto pXy = m_geometry->xy();
471
4.42k
    if (pXy == nullptr)
472
21
        return CPLErrorInvalidPointer("XY data");
473
4.40k
    if (m_hasZ && m_geometry->z() == nullptr)
474
3
        return CPLErrorInvalidPointer("Z data");
475
4.39k
    if (m_hasM && m_geometry->m() == nullptr)
476
3
        return CPLErrorInvalidPointer("M data");
477
4.39k
    const auto xySize = pXy->size();
478
4.39k
    if (xySize >= (feature_max_buffer_size / sizeof(OGRRawPoint)))
479
0
        return CPLErrorInvalidLength("XY data");
480
4.39k
    m_length = xySize;
481
4.39k
    m_xylength = m_length;
482
4.39k
    m_xy = pXy->data();
483
484
4.39k
    switch (m_geometryType)
485
4.39k
    {
486
135
        case GeometryType::Point:
487
135
            return readPoint();
488
233
        case GeometryType::MultiPoint:
489
233
            return readMultiPoint();
490
68
        case GeometryType::LineString:
491
68
            return readSimpleCurve<OGRLineString>(true);
492
72
        case GeometryType::MultiLineString:
493
72
            return readMultiLineString();
494
1.62k
        case GeometryType::Polygon:
495
1.62k
            return readPolygon();
496
2.07k
        case GeometryType::CircularString:
497
2.07k
            return readSimpleCurve<OGRCircularString>(true);
498
12
        case GeometryType::Triangle:
499
12
            return readTriangle();
500
161
        case GeometryType::TIN:
501
161
            return readTIN();
502
16
        default:
503
16
            CPLError(CE_Failure, CPLE_AppDefined,
504
16
                     "GeometryReader::read: Unknown type %d",
505
16
                     (int)m_geometryType);
506
4.39k
    }
507
16
    return nullptr;
508
4.39k
}