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