Coverage Report

Created: 2025-06-09 08:44

/src/gdal/ogr/ogrsf_frmts/geojson/ogrtopojsonreader.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  OpenGIS Simple Features Reference Implementation
4
 * Purpose:  Implementation of OGRTopoJSONReader class
5
 * Author:   Even Rouault, even dot rouault at spatialys.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2013, Even Rouault <even dot rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "ogrgeojsonreader.h"
14
#include "ogrgeojsonutils.h"
15
#include "ogrlibjsonutils.h"
16
#include "ogr_geojson.h"
17
#include "ogrgeojsongeometry.h"
18
#include <json.h>  // JSON-C
19
#include "ogr_api.h"
20
21
/************************************************************************/
22
/*                          OGRTopoJSONReader()                         */
23
/************************************************************************/
24
25
43
OGRTopoJSONReader::OGRTopoJSONReader() : poGJObject_(nullptr)
26
43
{
27
43
}
28
29
/************************************************************************/
30
/*                         ~OGRTopoJSONReader()                         */
31
/************************************************************************/
32
33
OGRTopoJSONReader::~OGRTopoJSONReader()
34
43
{
35
43
    if (nullptr != poGJObject_)
36
31
    {
37
31
        json_object_put(poGJObject_);
38
31
    }
39
40
43
    poGJObject_ = nullptr;
41
43
}
42
43
/************************************************************************/
44
/*                           Parse()                                    */
45
/************************************************************************/
46
47
OGRErr OGRTopoJSONReader::Parse(const char *pszText, bool bLooseIdentification)
48
43
{
49
43
    json_object *jsobj = nullptr;
50
43
    if (bLooseIdentification)
51
0
    {
52
0
        CPLPushErrorHandler(CPLQuietErrorHandler);
53
0
    }
54
43
    const bool bOK = nullptr != pszText && OGRJSonParse(pszText, &jsobj, true);
55
43
    if (bLooseIdentification)
56
0
    {
57
0
        CPLPopErrorHandler();
58
0
        CPLErrorReset();
59
0
    }
60
43
    if (!bOK)
61
12
    {
62
12
        return OGRERR_CORRUPT_DATA;
63
12
    }
64
65
    // JSON tree is shared for while lifetime of the reader object
66
    // and will be released in the destructor.
67
31
    poGJObject_ = jsobj;
68
31
    return OGRERR_NONE;
69
43
}
70
71
typedef struct
72
{
73
    double dfScale0;
74
    double dfScale1;
75
    double dfTranslate0;
76
    double dfTranslate1;
77
    bool bElementExists;
78
} ScalingParams;
79
80
/************************************************************************/
81
/*                            ParsePoint()                              */
82
/************************************************************************/
83
84
static bool ParsePoint(json_object *poPoint, double *pdfX, double *pdfY)
85
183
{
86
183
    if (poPoint != nullptr &&
87
183
        json_type_array == json_object_get_type(poPoint) &&
88
183
        json_object_array_length(poPoint) == 2)
89
166
    {
90
166
        json_object *poX = json_object_array_get_idx(poPoint, 0);
91
166
        json_object *poY = json_object_array_get_idx(poPoint, 1);
92
166
        if (poX != nullptr &&
93
166
            (json_type_int == json_object_get_type(poX) ||
94
165
             json_type_double == json_object_get_type(poX)) &&
95
166
            poY != nullptr &&
96
166
            (json_type_int == json_object_get_type(poY) ||
97
165
             json_type_double == json_object_get_type(poY)))
98
165
        {
99
165
            *pdfX = json_object_get_double(poX);
100
165
            *pdfY = json_object_get_double(poY);
101
165
            return true;
102
165
        }
103
166
    }
104
18
    return false;
105
183
}
106
107
/************************************************************************/
108
/*                             ParseArc()                               */
109
/************************************************************************/
110
111
static void ParseArc(OGRLineString *poLS, json_object *poArcsDB, int nArcID,
112
                     bool bReverse, ScalingParams *psParams)
113
96
{
114
96
    json_object *poArcDB = json_object_array_get_idx(poArcsDB, nArcID);
115
96
    if (poArcDB == nullptr || json_type_array != json_object_get_type(poArcDB))
116
16
        return;
117
80
    auto nPoints = json_object_array_length(poArcDB);
118
80
    double dfAccX = 0.0;
119
80
    double dfAccY = 0.0;
120
80
    int nBaseIndice = poLS->getNumPoints();
121
259
    for (auto i = decltype(nPoints){0}; i < nPoints; i++)
122
179
    {
123
179
        json_object *poPoint = json_object_array_get_idx(poArcDB, i);
124
179
        double dfX = 0.0;
125
179
        double dfY = 0.0;
126
179
        if (ParsePoint(poPoint, &dfX, &dfY))
127
165
        {
128
165
            if (psParams->bElementExists)
129
19
            {
130
19
                dfAccX += dfX;
131
19
                dfAccY += dfY;
132
19
                dfX = dfAccX * psParams->dfScale0 + psParams->dfTranslate0;
133
19
                dfY = dfAccY * psParams->dfScale1 + psParams->dfTranslate1;
134
19
            }
135
146
            else
136
146
            {
137
146
                dfX = dfX * psParams->dfScale0 + psParams->dfTranslate0;
138
146
                dfY = dfY * psParams->dfScale1 + psParams->dfTranslate1;
139
146
            }
140
165
            if (i == 0)
141
50
            {
142
50
                if (!bReverse && poLS->getNumPoints() > 0)
143
5
                {
144
5
                    poLS->setNumPoints(nBaseIndice + static_cast<int>(nPoints) -
145
5
                                       1);
146
5
                    nBaseIndice--;
147
5
                    continue;
148
5
                }
149
45
                else if (bReverse && poLS->getNumPoints() > 0)
150
3
                {
151
3
                    poLS->setNumPoints(nBaseIndice + static_cast<int>(nPoints) -
152
3
                                       1);
153
3
                    nPoints--;
154
3
                    if (nPoints == 0)
155
0
                        break;
156
3
                }
157
42
                else
158
42
                    poLS->setNumPoints(nBaseIndice + static_cast<int>(nPoints));
159
50
            }
160
161
160
            if (!bReverse)
162
121
                poLS->setPoint(nBaseIndice + static_cast<int>(i), dfX, dfY);
163
39
            else
164
39
                poLS->setPoint(nBaseIndice + static_cast<int>(nPoints) - 1 -
165
39
                                   static_cast<int>(i),
166
39
                               dfX, dfY);
167
160
        }
168
179
    }
169
80
}
170
171
/************************************************************************/
172
/*                        ParseLineString()                             */
173
/************************************************************************/
174
175
static void ParseLineString(OGRLineString *poLS, json_object *poRing,
176
                            json_object *poArcsDB, ScalingParams *psParams)
177
66
{
178
66
    const auto nArcsDB = json_object_array_length(poArcsDB);
179
180
66
    const auto nArcsRing = json_object_array_length(poRing);
181
165
    for (auto j = decltype(nArcsRing){0}; j < nArcsRing; j++)
182
99
    {
183
99
        json_object *poArcId = json_object_array_get_idx(poRing, j);
184
99
        if (poArcId != nullptr &&
185
99
            json_type_int == json_object_get_type(poArcId))
186
97
        {
187
97
            int nArcId = json_object_get_int(poArcId);
188
97
            bool bReverse = false;
189
97
            if (nArcId < 0)
190
25
            {
191
25
                nArcId = -(nArcId + 1);
192
25
                bReverse = true;
193
25
            }
194
97
            if (nArcId < static_cast<int>(nArcsDB))
195
96
            {
196
96
                ParseArc(poLS, poArcsDB, nArcId, bReverse, psParams);
197
96
            }
198
97
        }
199
99
    }
200
66
}
201
202
/************************************************************************/
203
/*                          ParsePolygon()                              */
204
/************************************************************************/
205
206
static void ParsePolygon(OGRPolygon *poPoly, json_object *poArcsObj,
207
                         json_object *poArcsDB, ScalingParams *psParams)
208
24
{
209
24
    const auto nRings = json_object_array_length(poArcsObj);
210
48
    for (auto i = decltype(nRings){0}; i < nRings; i++)
211
24
    {
212
24
        OGRLinearRing *poLR = new OGRLinearRing();
213
214
24
        json_object *poRing = json_object_array_get_idx(poArcsObj, i);
215
24
        if (poRing != nullptr &&
216
24
            json_type_array == json_object_get_type(poRing))
217
24
        {
218
24
            ParseLineString(poLR, poRing, poArcsDB, psParams);
219
24
        }
220
24
        poLR->closeRings();
221
24
        if (poLR->getNumPoints() < 4)
222
2
        {
223
2
            CPLDebug("TopoJSON", "Discarding polygon ring made of %d points",
224
2
                     poLR->getNumPoints());
225
2
            delete poLR;
226
2
        }
227
22
        else
228
22
        {
229
22
            poPoly->addRingDirectly(poLR);
230
22
        }
231
24
    }
232
24
}
233
234
/************************************************************************/
235
/*                       ParseMultiLineString()                         */
236
/************************************************************************/
237
238
static void ParseMultiLineString(OGRMultiLineString *poMLS,
239
                                 json_object *poArcsObj, json_object *poArcsDB,
240
                                 ScalingParams *psParams)
241
21
{
242
21
    const auto nRings = json_object_array_length(poArcsObj);
243
63
    for (auto i = decltype(nRings){0}; i < nRings; i++)
244
42
    {
245
42
        OGRLineString *poLS = new OGRLineString();
246
42
        poMLS->addGeometryDirectly(poLS);
247
248
42
        json_object *poRing = json_object_array_get_idx(poArcsObj, i);
249
42
        if (poRing != nullptr &&
250
42
            json_type_array == json_object_get_type(poRing))
251
42
        {
252
42
            ParseLineString(poLS, poRing, poArcsDB, psParams);
253
42
        }
254
42
    }
255
21
}
256
257
/************************************************************************/
258
/*                       ParseMultiPolygon()                            */
259
/************************************************************************/
260
261
static void ParseMultiPolygon(OGRMultiPolygon *poMultiPoly,
262
                              json_object *poArcsObj, json_object *poArcsDB,
263
                              ScalingParams *psParams)
264
32
{
265
32
    const auto nPolys = json_object_array_length(poArcsObj);
266
67
    for (auto i = decltype(nPolys){0}; i < nPolys; i++)
267
35
    {
268
35
        OGRPolygon *poPoly = new OGRPolygon();
269
270
35
        json_object *poPolyArcs = json_object_array_get_idx(poArcsObj, i);
271
35
        if (poPolyArcs != nullptr &&
272
35
            json_type_array == json_object_get_type(poPolyArcs))
273
24
        {
274
24
            ParsePolygon(poPoly, poPolyArcs, poArcsDB, psParams);
275
24
        }
276
277
35
        if (poPoly->IsEmpty())
278
13
        {
279
13
            delete poPoly;
280
13
        }
281
22
        else
282
22
        {
283
22
            poMultiPoly->addGeometryDirectly(poPoly);
284
22
        }
285
35
    }
286
32
}
287
288
/************************************************************************/
289
/*                          ParseObject()                               */
290
/************************************************************************/
291
292
static void ParseObject(const char *pszId, json_object *poObj,
293
                        OGRGeoJSONLayer *poLayer, json_object *poArcsDB,
294
                        ScalingParams *psParams)
295
111
{
296
111
    json_object *poType = OGRGeoJSONFindMemberByName(poObj, "type");
297
111
    if (poType == nullptr || json_object_get_type(poType) != json_type_string)
298
0
        return;
299
111
    const char *pszType = json_object_get_string(poType);
300
301
111
    json_object *poArcsObj = OGRGeoJSONFindMemberByName(poObj, "arcs");
302
111
    json_object *poCoordinatesObj =
303
111
        OGRGeoJSONFindMemberByName(poObj, "coordinates");
304
111
    if (strcmp(pszType, "Point") == 0 || strcmp(pszType, "MultiPoint") == 0)
305
38
    {
306
38
        if (poCoordinatesObj == nullptr ||
307
38
            json_type_array != json_object_get_type(poCoordinatesObj))
308
34
            return;
309
38
    }
310
73
    else
311
73
    {
312
73
        if (poArcsObj == nullptr ||
313
73
            json_type_array != json_object_get_type(poArcsObj))
314
20
            return;
315
73
    }
316
317
57
    if (pszId == nullptr)
318
57
    {
319
57
        json_object *poId = OGRGeoJSONFindMemberByName(poObj, "id");
320
57
        if (poId != nullptr &&
321
57
            (json_type_string == json_object_get_type(poId) ||
322
0
             json_type_int == json_object_get_type(poId)))
323
0
        {
324
0
            pszId = json_object_get_string(poId);
325
0
        }
326
57
    }
327
328
57
    OGRFeature *poFeature = new OGRFeature(poLayer->GetLayerDefn());
329
57
    if (pszId != nullptr)
330
0
        poFeature->SetField("id", pszId);
331
332
57
    json_object *poProperties = OGRGeoJSONFindMemberByName(poObj, "properties");
333
57
    if (poProperties != nullptr &&
334
57
        json_type_object == json_object_get_type(poProperties))
335
0
    {
336
0
        json_object_iter it;
337
0
        it.key = nullptr;
338
0
        it.val = nullptr;
339
0
        it.entry = nullptr;
340
0
        json_object_object_foreachC(poProperties, it)
341
0
        {
342
0
            const int nField = poFeature->GetFieldIndex(it.key);
343
0
            OGRGeoJSONReaderSetField(poLayer, poFeature, nField, it.key, it.val,
344
0
                                     false, 0);
345
0
        }
346
0
    }
347
348
57
    OGRGeometry *poGeom = nullptr;
349
57
    if (strcmp(pszType, "Point") == 0)
350
0
    {
351
0
        double dfX = 0.0;
352
0
        double dfY = 0.0;
353
0
        if (ParsePoint(poCoordinatesObj, &dfX, &dfY))
354
0
        {
355
0
            dfX = dfX * psParams->dfScale0 + psParams->dfTranslate0;
356
0
            dfY = dfY * psParams->dfScale1 + psParams->dfTranslate1;
357
0
            poGeom = new OGRPoint(dfX, dfY);
358
0
        }
359
0
        else
360
0
        {
361
0
            poGeom = new OGRPoint();
362
0
        }
363
0
    }
364
57
    else if (strcmp(pszType, "MultiPoint") == 0)
365
4
    {
366
4
        OGRMultiPoint *poMP = new OGRMultiPoint();
367
4
        poGeom = poMP;
368
4
        const auto nTuples = json_object_array_length(poCoordinatesObj);
369
8
        for (auto i = decltype(nTuples){0}; i < nTuples; i++)
370
4
        {
371
4
            json_object *poPair =
372
4
                json_object_array_get_idx(poCoordinatesObj, i);
373
4
            double dfX = 0.0;
374
4
            double dfY = 0.0;
375
4
            if (ParsePoint(poPair, &dfX, &dfY))
376
0
            {
377
0
                dfX = dfX * psParams->dfScale0 + psParams->dfTranslate0;
378
0
                dfY = dfY * psParams->dfScale1 + psParams->dfTranslate1;
379
0
                poMP->addGeometryDirectly(new OGRPoint(dfX, dfY));
380
0
            }
381
4
        }
382
4
    }
383
53
    else if (strcmp(pszType, "LineString") == 0)
384
0
    {
385
0
        OGRLineString *poLS = new OGRLineString();
386
0
        poGeom = poLS;
387
0
        ParseLineString(poLS, poArcsObj, poArcsDB, psParams);
388
0
    }
389
53
    else if (strcmp(pszType, "MultiLineString") == 0)
390
21
    {
391
21
        OGRMultiLineString *poMLS = new OGRMultiLineString();
392
21
        poGeom = poMLS;
393
21
        ParseMultiLineString(poMLS, poArcsObj, poArcsDB, psParams);
394
21
    }
395
32
    else if (strcmp(pszType, "Polygon") == 0)
396
0
    {
397
0
        OGRPolygon *poPoly = new OGRPolygon();
398
0
        poGeom = poPoly;
399
0
        ParsePolygon(poPoly, poArcsObj, poArcsDB, psParams);
400
0
    }
401
32
    else if (strcmp(pszType, "MultiPolygon") == 0)
402
32
    {
403
32
        OGRMultiPolygon *poMultiPoly = new OGRMultiPolygon();
404
32
        poGeom = poMultiPoly;
405
32
        ParseMultiPolygon(poMultiPoly, poArcsObj, poArcsDB, psParams);
406
32
    }
407
408
57
    if (poGeom != nullptr)
409
57
        poFeature->SetGeometryDirectly(poGeom);
410
57
    poLayer->AddFeature(poFeature);
411
57
    delete poFeature;
412
57
}
413
414
/************************************************************************/
415
/*                        EstablishLayerDefn()                          */
416
/************************************************************************/
417
418
static void
419
EstablishLayerDefn(int nPrevFieldIdx, std::vector<int> &anCurFieldIndices,
420
                   std::map<std::string, int> &oMapFieldNameToIdx,
421
                   std::vector<std::unique_ptr<OGRFieldDefn>> &apoFieldDefn,
422
                   gdal::DirectedAcyclicGraph<int, std::string> &dag,
423
                   json_object *poObj,
424
                   std::set<int> &aoSetUndeterminedTypeFields)
425
111
{
426
111
    json_object *poObjProps = OGRGeoJSONFindMemberByName(poObj, "properties");
427
111
    if (nullptr != poObjProps &&
428
111
        json_object_get_type(poObjProps) == json_type_object)
429
0
    {
430
0
        json_object_iter it;
431
0
        it.key = nullptr;
432
0
        it.val = nullptr;
433
0
        it.entry = nullptr;
434
435
0
        json_object_object_foreachC(poObjProps, it)
436
0
        {
437
0
            anCurFieldIndices.clear();
438
0
            OGRGeoJSONReaderAddOrUpdateField(
439
0
                anCurFieldIndices, oMapFieldNameToIdx, apoFieldDefn, it.key,
440
0
                it.val, false, 0, false, false, aoSetUndeterminedTypeFields);
441
0
            for (int idx : anCurFieldIndices)
442
0
            {
443
0
                dag.addNode(idx, apoFieldDefn[idx]->GetNameRef());
444
0
                if (nPrevFieldIdx != -1)
445
0
                {
446
0
                    dag.addEdge(nPrevFieldIdx, idx);
447
0
                }
448
0
                nPrevFieldIdx = idx;
449
0
            }
450
0
        }
451
0
    }
452
111
}
453
454
/************************************************************************/
455
/*                        ParseObjectMain()                             */
456
/************************************************************************/
457
458
static bool
459
ParseObjectMain(const char *pszId, json_object *poObj,
460
                const OGRSpatialReference *poSRS, OGRGeoJSONDataSource *poDS,
461
                OGRGeoJSONLayer **ppoMainLayer, json_object *poArcs,
462
                ScalingParams *psParams, std::vector<int> &anCurFieldIndices,
463
                std::map<std::string, int> &oMapFieldNameToIdx,
464
                std::vector<std::unique_ptr<OGRFieldDefn>> &apoFieldDefn,
465
                gdal::DirectedAcyclicGraph<int, std::string> &dag,
466
                std::set<int> &aoSetUndeterminedTypeFields)
467
380
{
468
380
    bool bNeedSecondPass = false;
469
470
380
    if (poObj != nullptr && json_type_object == json_object_get_type(poObj))
471
326
    {
472
326
        json_object *poType = OGRGeoJSONFindMemberByName(poObj, "type");
473
326
        if (poType != nullptr &&
474
326
            json_type_string == json_object_get_type(poType))
475
209
        {
476
209
            const char *pszType = json_object_get_string(poType);
477
209
            if (strcmp(pszType, "GeometryCollection") == 0)
478
1
            {
479
1
                json_object *poGeometries =
480
1
                    OGRGeoJSONFindMemberByName(poObj, "geometries");
481
1
                if (poGeometries != nullptr &&
482
1
                    json_type_array == json_object_get_type(poGeometries))
483
0
                {
484
0
                    if (pszId == nullptr)
485
0
                    {
486
0
                        json_object *poId =
487
0
                            OGRGeoJSONFindMemberByName(poObj, "id");
488
0
                        if (poId != nullptr &&
489
0
                            (json_type_string == json_object_get_type(poId) ||
490
0
                             json_type_int == json_object_get_type(poId)))
491
0
                        {
492
0
                            pszId = json_object_get_string(poId);
493
0
                        }
494
0
                    }
495
496
0
                    OGRGeoJSONLayer *poLayer =
497
0
                        new OGRGeoJSONLayer(pszId ? pszId : "TopoJSON", nullptr,
498
0
                                            wkbUnknown, poDS, nullptr);
499
0
                    poLayer->SetSupportsZGeometries(false);
500
0
                    OGRFeatureDefn *poDefn = poLayer->GetLayerDefn();
501
502
0
                    whileUnsealing(poDefn)->GetGeomFieldDefn(0)->SetSpatialRef(
503
0
                        poSRS);
504
505
0
                    const auto nGeometries =
506
0
                        json_object_array_length(poGeometries);
507
                    // First pass to establish schema.
508
509
0
                    std::vector<int> anCurFieldIndicesLocal;
510
0
                    std::map<std::string, int> oMapFieldNameToIdxLocal;
511
0
                    std::vector<std::unique_ptr<OGRFieldDefn>>
512
0
                        apoFieldDefnLocal;
513
0
                    gdal::DirectedAcyclicGraph<int, std::string> dagLocal;
514
0
                    std::set<int> aoSetUndeterminedTypeFieldsLocal;
515
516
0
                    apoFieldDefnLocal.emplace_back(
517
0
                        std::make_unique<OGRFieldDefn>("id", OFTString));
518
0
                    oMapFieldNameToIdxLocal["id"] = 0;
519
0
                    dagLocal.addNode(0, "id");
520
0
                    const int nPrevFieldIdx = 0;
521
522
0
                    for (auto i = decltype(nGeometries){0}; i < nGeometries;
523
0
                         i++)
524
0
                    {
525
0
                        json_object *poGeom =
526
0
                            json_object_array_get_idx(poGeometries, i);
527
0
                        if (poGeom != nullptr &&
528
0
                            json_type_object == json_object_get_type(poGeom))
529
0
                        {
530
0
                            EstablishLayerDefn(
531
0
                                nPrevFieldIdx, anCurFieldIndicesLocal,
532
0
                                oMapFieldNameToIdxLocal, apoFieldDefnLocal,
533
0
                                dagLocal, poGeom,
534
0
                                aoSetUndeterminedTypeFieldsLocal);
535
0
                        }
536
0
                    }
537
538
0
                    const auto sortedFields = dagLocal.getTopologicalOrdering();
539
0
                    CPLAssert(sortedFields.size() == apoFieldDefnLocal.size());
540
0
                    {
541
0
                        auto oTemporaryUnsealer(poDefn->GetTemporaryUnsealer());
542
0
                        for (int idx : sortedFields)
543
0
                        {
544
0
                            poDefn->AddFieldDefn(apoFieldDefnLocal[idx].get());
545
0
                        }
546
0
                    }
547
548
                    // Second pass to build objects.
549
0
                    for (auto i = decltype(nGeometries){0}; i < nGeometries;
550
0
                         i++)
551
0
                    {
552
0
                        json_object *poGeom =
553
0
                            json_object_array_get_idx(poGeometries, i);
554
0
                        if (poGeom != nullptr &&
555
0
                            json_type_object == json_object_get_type(poGeom))
556
0
                        {
557
0
                            ParseObject(nullptr, poGeom, poLayer, poArcs,
558
0
                                        psParams);
559
0
                        }
560
0
                    }
561
562
0
                    poLayer->DetectGeometryType();
563
0
                    poDS->AddLayer(poLayer);
564
0
                }
565
1
            }
566
208
            else if (strcmp(pszType, "Point") == 0 ||
567
208
                     strcmp(pszType, "MultiPoint") == 0 ||
568
208
                     strcmp(pszType, "LineString") == 0 ||
569
208
                     strcmp(pszType, "MultiLineString") == 0 ||
570
208
                     strcmp(pszType, "Polygon") == 0 ||
571
208
                     strcmp(pszType, "MultiPolygon") == 0)
572
111
            {
573
111
                if (*ppoMainLayer == nullptr)
574
25
                {
575
25
                    *ppoMainLayer = new OGRGeoJSONLayer(
576
25
                        "TopoJSON", nullptr, wkbUnknown, poDS, nullptr);
577
578
25
                    (*ppoMainLayer)->SetSupportsZGeometries(false);
579
580
25
                    whileUnsealing((*ppoMainLayer)->GetLayerDefn())
581
25
                        ->GetGeomFieldDefn(0)
582
25
                        ->SetSpatialRef(poSRS);
583
584
25
                    apoFieldDefn.emplace_back(
585
25
                        std::make_unique<OGRFieldDefn>("id", OFTString));
586
25
                    oMapFieldNameToIdx["id"] = 0;
587
25
                    dag.addNode(0, "id");
588
25
                }
589
590
111
                const int nPrevFieldIdx = 0;
591
111
                EstablishLayerDefn(nPrevFieldIdx, anCurFieldIndices,
592
111
                                   oMapFieldNameToIdx, apoFieldDefn, dag, poObj,
593
111
                                   aoSetUndeterminedTypeFields);
594
595
111
                bNeedSecondPass = true;
596
111
            }
597
209
        }
598
326
    }
599
380
    return bNeedSecondPass;
600
380
}
601
602
/************************************************************************/
603
/*                     ParseObjectMainSecondPass()                      */
604
/************************************************************************/
605
606
static void ParseObjectMainSecondPass(const char *pszId, json_object *poObj,
607
                                      OGRGeoJSONLayer **ppoMainLayer,
608
                                      json_object *poArcs,
609
                                      ScalingParams *psParams)
610
367
{
611
367
    if (poObj != nullptr && json_type_object == json_object_get_type(poObj))
612
317
    {
613
317
        json_object *poType = OGRGeoJSONFindMemberByName(poObj, "type");
614
317
        if (poType != nullptr &&
615
317
            json_type_string == json_object_get_type(poType))
616
203
        {
617
203
            const char *pszType = json_object_get_string(poType);
618
203
            if (strcmp(pszType, "Point") == 0 ||
619
203
                strcmp(pszType, "MultiPoint") == 0 ||
620
203
                strcmp(pszType, "LineString") == 0 ||
621
203
                strcmp(pszType, "MultiLineString") == 0 ||
622
203
                strcmp(pszType, "Polygon") == 0 ||
623
203
                strcmp(pszType, "MultiPolygon") == 0)
624
111
            {
625
111
                ParseObject(pszId, poObj, *ppoMainLayer, poArcs, psParams);
626
111
            }
627
203
        }
628
317
    }
629
367
}
630
631
/************************************************************************/
632
/*                           ReadLayers()                               */
633
/************************************************************************/
634
635
void OGRTopoJSONReader::ReadLayers(OGRGeoJSONDataSource *poDS)
636
31
{
637
31
    if (nullptr == poGJObject_)
638
0
    {
639
0
        CPLDebug("TopoJSON",
640
0
                 "Missing parsed TopoJSON data. Forgot to call Parse()?");
641
0
        return;
642
0
    }
643
644
31
    poDS->SetSupportsZGeometries(false);
645
646
31
    ScalingParams sParams;
647
31
    sParams.dfScale0 = 1.0;
648
31
    sParams.dfScale1 = 1.0;
649
31
    sParams.dfTranslate0 = 0.0;
650
31
    sParams.dfTranslate1 = 0.0;
651
31
    sParams.bElementExists = false;
652
31
    json_object *poObjTransform =
653
31
        OGRGeoJSONFindMemberByName(poGJObject_, "transform");
654
31
    if (nullptr != poObjTransform &&
655
31
        json_type_object == json_object_get_type(poObjTransform))
656
4
    {
657
4
        json_object *poObjScale =
658
4
            OGRGeoJSONFindMemberByName(poObjTransform, "scale");
659
4
        if (nullptr != poObjScale &&
660
4
            json_type_array == json_object_get_type(poObjScale) &&
661
4
            json_object_array_length(poObjScale) == 2)
662
0
        {
663
0
            json_object *poScale0 = json_object_array_get_idx(poObjScale, 0);
664
0
            json_object *poScale1 = json_object_array_get_idx(poObjScale, 1);
665
0
            if (poScale0 != nullptr &&
666
0
                (json_object_get_type(poScale0) == json_type_double ||
667
0
                 json_object_get_type(poScale0) == json_type_int) &&
668
0
                poScale1 != nullptr &&
669
0
                (json_object_get_type(poScale1) == json_type_double ||
670
0
                 json_object_get_type(poScale1) == json_type_int))
671
0
            {
672
0
                sParams.dfScale0 = json_object_get_double(poScale0);
673
0
                sParams.dfScale1 = json_object_get_double(poScale1);
674
0
                sParams.bElementExists = true;
675
0
            }
676
0
        }
677
678
4
        json_object *poObjTranslate =
679
4
            OGRGeoJSONFindMemberByName(poObjTransform, "translate");
680
4
        if (nullptr != poObjTranslate &&
681
4
            json_type_array == json_object_get_type(poObjTranslate) &&
682
4
            json_object_array_length(poObjTranslate) == 2)
683
3
        {
684
3
            json_object *poTranslate0 =
685
3
                json_object_array_get_idx(poObjTranslate, 0);
686
3
            json_object *poTranslate1 =
687
3
                json_object_array_get_idx(poObjTranslate, 1);
688
3
            if (poTranslate0 != nullptr &&
689
3
                (json_object_get_type(poTranslate0) == json_type_double ||
690
3
                 json_object_get_type(poTranslate0) == json_type_int) &&
691
3
                poTranslate1 != nullptr &&
692
3
                (json_object_get_type(poTranslate1) == json_type_double ||
693
3
                 json_object_get_type(poTranslate1) == json_type_int))
694
3
            {
695
3
                sParams.dfTranslate0 = json_object_get_double(poTranslate0);
696
3
                sParams.dfTranslate1 = json_object_get_double(poTranslate1);
697
3
                sParams.bElementExists = true;
698
3
            }
699
3
        }
700
4
    }
701
702
31
    json_object *poArcs = OGRGeoJSONFindMemberByName(poGJObject_, "arcs");
703
31
    if (poArcs == nullptr || json_type_array != json_object_get_type(poArcs))
704
0
        return;
705
706
31
    OGRGeoJSONLayer *poMainLayer = nullptr;
707
708
31
    json_object *poObjects = OGRGeoJSONFindMemberByName(poGJObject_, "objects");
709
31
    if (poObjects == nullptr)
710
1
        return;
711
712
30
    OGRSpatialReference *poSRS = OGRGeoJSONReadSpatialReference(poGJObject_);
713
714
30
    std::vector<int> anCurFieldIndices;
715
30
    std::map<std::string, int> oMapFieldNameToIdx;
716
30
    std::vector<std::unique_ptr<OGRFieldDefn>> apoFieldDefn;
717
30
    gdal::DirectedAcyclicGraph<int, std::string> dag;
718
719
30
    std::set<int> aoSetUndeterminedTypeFields;
720
30
    if (json_type_object == json_object_get_type(poObjects))
721
4
    {
722
4
        json_object_iter it;
723
4
        it.key = nullptr;
724
4
        it.val = nullptr;
725
4
        it.entry = nullptr;
726
4
        bool bNeedSecondPass = false;
727
4
        json_object_object_foreachC(poObjects, it)
728
8
        {
729
8
            json_object *poObj = it.val;
730
8
            bNeedSecondPass |= ParseObjectMain(
731
8
                it.key, poObj, poSRS, poDS, &poMainLayer, poArcs, &sParams,
732
8
                anCurFieldIndices, oMapFieldNameToIdx, apoFieldDefn, dag,
733
8
                aoSetUndeterminedTypeFields);
734
8
        }
735
4
        if (bNeedSecondPass)
736
0
        {
737
0
            OGRFeatureDefn *poDefn = poMainLayer->GetLayerDefn();
738
0
            const auto sortedFields = dag.getTopologicalOrdering();
739
0
            CPLAssert(sortedFields.size() == apoFieldDefn.size());
740
0
            auto oTemporaryUnsealer(poDefn->GetTemporaryUnsealer());
741
0
            for (int idx : sortedFields)
742
0
            {
743
0
                poDefn->AddFieldDefn(apoFieldDefn[idx].get());
744
0
            }
745
746
0
            it.key = nullptr;
747
0
            it.val = nullptr;
748
0
            it.entry = nullptr;
749
0
            json_object_object_foreachC(poObjects, it)
750
0
            {
751
0
                json_object *poObj = it.val;
752
0
                ParseObjectMainSecondPass(it.key, poObj, &poMainLayer, poArcs,
753
0
                                          &sParams);
754
0
            }
755
0
        }
756
4
    }
757
26
    else if (json_type_array == json_object_get_type(poObjects))
758
26
    {
759
26
        const auto nObjects = json_object_array_length(poObjects);
760
26
        bool bNeedSecondPass = false;
761
398
        for (auto i = decltype(nObjects){0}; i < nObjects; i++)
762
372
        {
763
372
            json_object *poObj = json_object_array_get_idx(poObjects, i);
764
372
            bNeedSecondPass |= ParseObjectMain(
765
372
                nullptr, poObj, poSRS, poDS, &poMainLayer, poArcs, &sParams,
766
372
                anCurFieldIndices, oMapFieldNameToIdx, apoFieldDefn, dag,
767
372
                aoSetUndeterminedTypeFields);
768
372
        }
769
26
        if (bNeedSecondPass)
770
25
        {
771
25
            OGRFeatureDefn *poDefn = poMainLayer->GetLayerDefn();
772
25
            const auto sortedFields = dag.getTopologicalOrdering();
773
25
            CPLAssert(sortedFields.size() == apoFieldDefn.size());
774
25
            auto oTemporaryUnsealer(poDefn->GetTemporaryUnsealer());
775
25
            for (int idx : sortedFields)
776
25
            {
777
25
                poDefn->AddFieldDefn(apoFieldDefn[idx].get());
778
25
            }
779
780
392
            for (auto i = decltype(nObjects){0}; i < nObjects; i++)
781
367
            {
782
367
                json_object *poObj = json_object_array_get_idx(poObjects, i);
783
367
                ParseObjectMainSecondPass(nullptr, poObj, &poMainLayer, poArcs,
784
367
                                          &sParams);
785
367
            }
786
25
        }
787
26
    }
788
789
30
    if (poMainLayer != nullptr)
790
25
    {
791
25
        poMainLayer->DetectGeometryType();
792
25
        poDS->AddLayer(poMainLayer);
793
25
    }
794
795
30
    if (poSRS)
796
0
        poSRS->Release();
797
30
}