Coverage Report

Created: 2025-06-13 06:18

/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
0
OGRTopoJSONReader::OGRTopoJSONReader() : poGJObject_(nullptr)
26
0
{
27
0
}
28
29
/************************************************************************/
30
/*                         ~OGRTopoJSONReader()                         */
31
/************************************************************************/
32
33
OGRTopoJSONReader::~OGRTopoJSONReader()
34
0
{
35
0
    if (nullptr != poGJObject_)
36
0
    {
37
0
        json_object_put(poGJObject_);
38
0
    }
39
40
0
    poGJObject_ = nullptr;
41
0
}
42
43
/************************************************************************/
44
/*                           Parse()                                    */
45
/************************************************************************/
46
47
OGRErr OGRTopoJSONReader::Parse(const char *pszText, bool bLooseIdentification)
48
0
{
49
0
    json_object *jsobj = nullptr;
50
0
    if (bLooseIdentification)
51
0
    {
52
0
        CPLPushErrorHandler(CPLQuietErrorHandler);
53
0
    }
54
0
    const bool bOK = nullptr != pszText && OGRJSonParse(pszText, &jsobj, true);
55
0
    if (bLooseIdentification)
56
0
    {
57
0
        CPLPopErrorHandler();
58
0
        CPLErrorReset();
59
0
    }
60
0
    if (!bOK)
61
0
    {
62
0
        return OGRERR_CORRUPT_DATA;
63
0
    }
64
65
    // JSON tree is shared for while lifetime of the reader object
66
    // and will be released in the destructor.
67
0
    poGJObject_ = jsobj;
68
0
    return OGRERR_NONE;
69
0
}
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
0
{
86
0
    if (poPoint != nullptr &&
87
0
        json_type_array == json_object_get_type(poPoint) &&
88
0
        json_object_array_length(poPoint) == 2)
89
0
    {
90
0
        json_object *poX = json_object_array_get_idx(poPoint, 0);
91
0
        json_object *poY = json_object_array_get_idx(poPoint, 1);
92
0
        if (poX != nullptr &&
93
0
            (json_type_int == json_object_get_type(poX) ||
94
0
             json_type_double == json_object_get_type(poX)) &&
95
0
            poY != nullptr &&
96
0
            (json_type_int == json_object_get_type(poY) ||
97
0
             json_type_double == json_object_get_type(poY)))
98
0
        {
99
0
            *pdfX = json_object_get_double(poX);
100
0
            *pdfY = json_object_get_double(poY);
101
0
            return true;
102
0
        }
103
0
    }
104
0
    return false;
105
0
}
106
107
/************************************************************************/
108
/*                             ParseArc()                               */
109
/************************************************************************/
110
111
static void ParseArc(OGRLineString *poLS, json_object *poArcsDB, int nArcID,
112
                     bool bReverse, ScalingParams *psParams)
113
0
{
114
0
    json_object *poArcDB = json_object_array_get_idx(poArcsDB, nArcID);
115
0
    if (poArcDB == nullptr || json_type_array != json_object_get_type(poArcDB))
116
0
        return;
117
0
    auto nPoints = json_object_array_length(poArcDB);
118
0
    double dfAccX = 0.0;
119
0
    double dfAccY = 0.0;
120
0
    int nBaseIndice = poLS->getNumPoints();
121
0
    for (auto i = decltype(nPoints){0}; i < nPoints; i++)
122
0
    {
123
0
        json_object *poPoint = json_object_array_get_idx(poArcDB, i);
124
0
        double dfX = 0.0;
125
0
        double dfY = 0.0;
126
0
        if (ParsePoint(poPoint, &dfX, &dfY))
127
0
        {
128
0
            if (psParams->bElementExists)
129
0
            {
130
0
                dfAccX += dfX;
131
0
                dfAccY += dfY;
132
0
                dfX = dfAccX * psParams->dfScale0 + psParams->dfTranslate0;
133
0
                dfY = dfAccY * psParams->dfScale1 + psParams->dfTranslate1;
134
0
            }
135
0
            else
136
0
            {
137
0
                dfX = dfX * psParams->dfScale0 + psParams->dfTranslate0;
138
0
                dfY = dfY * psParams->dfScale1 + psParams->dfTranslate1;
139
0
            }
140
0
            if (i == 0)
141
0
            {
142
0
                if (!bReverse && poLS->getNumPoints() > 0)
143
0
                {
144
0
                    poLS->setNumPoints(nBaseIndice + static_cast<int>(nPoints) -
145
0
                                       1);
146
0
                    nBaseIndice--;
147
0
                    continue;
148
0
                }
149
0
                else if (bReverse && poLS->getNumPoints() > 0)
150
0
                {
151
0
                    poLS->setNumPoints(nBaseIndice + static_cast<int>(nPoints) -
152
0
                                       1);
153
0
                    nPoints--;
154
0
                    if (nPoints == 0)
155
0
                        break;
156
0
                }
157
0
                else
158
0
                    poLS->setNumPoints(nBaseIndice + static_cast<int>(nPoints));
159
0
            }
160
161
0
            if (!bReverse)
162
0
                poLS->setPoint(nBaseIndice + static_cast<int>(i), dfX, dfY);
163
0
            else
164
0
                poLS->setPoint(nBaseIndice + static_cast<int>(nPoints) - 1 -
165
0
                                   static_cast<int>(i),
166
0
                               dfX, dfY);
167
0
        }
168
0
    }
169
0
}
170
171
/************************************************************************/
172
/*                        ParseLineString()                             */
173
/************************************************************************/
174
175
static void ParseLineString(OGRLineString *poLS, json_object *poRing,
176
                            json_object *poArcsDB, ScalingParams *psParams)
177
0
{
178
0
    const auto nArcsDB = json_object_array_length(poArcsDB);
179
180
0
    const auto nArcsRing = json_object_array_length(poRing);
181
0
    for (auto j = decltype(nArcsRing){0}; j < nArcsRing; j++)
182
0
    {
183
0
        json_object *poArcId = json_object_array_get_idx(poRing, j);
184
0
        if (poArcId != nullptr &&
185
0
            json_type_int == json_object_get_type(poArcId))
186
0
        {
187
0
            int nArcId = json_object_get_int(poArcId);
188
0
            bool bReverse = false;
189
0
            if (nArcId < 0)
190
0
            {
191
0
                nArcId = -(nArcId + 1);
192
0
                bReverse = true;
193
0
            }
194
0
            if (nArcId < static_cast<int>(nArcsDB))
195
0
            {
196
0
                ParseArc(poLS, poArcsDB, nArcId, bReverse, psParams);
197
0
            }
198
0
        }
199
0
    }
200
0
}
201
202
/************************************************************************/
203
/*                          ParsePolygon()                              */
204
/************************************************************************/
205
206
static void ParsePolygon(OGRPolygon *poPoly, json_object *poArcsObj,
207
                         json_object *poArcsDB, ScalingParams *psParams)
208
0
{
209
0
    const auto nRings = json_object_array_length(poArcsObj);
210
0
    for (auto i = decltype(nRings){0}; i < nRings; i++)
211
0
    {
212
0
        OGRLinearRing *poLR = new OGRLinearRing();
213
214
0
        json_object *poRing = json_object_array_get_idx(poArcsObj, i);
215
0
        if (poRing != nullptr &&
216
0
            json_type_array == json_object_get_type(poRing))
217
0
        {
218
0
            ParseLineString(poLR, poRing, poArcsDB, psParams);
219
0
        }
220
0
        poLR->closeRings();
221
0
        if (poLR->getNumPoints() < 4)
222
0
        {
223
0
            CPLDebug("TopoJSON", "Discarding polygon ring made of %d points",
224
0
                     poLR->getNumPoints());
225
0
            delete poLR;
226
0
        }
227
0
        else
228
0
        {
229
0
            poPoly->addRingDirectly(poLR);
230
0
        }
231
0
    }
232
0
}
233
234
/************************************************************************/
235
/*                       ParseMultiLineString()                         */
236
/************************************************************************/
237
238
static void ParseMultiLineString(OGRMultiLineString *poMLS,
239
                                 json_object *poArcsObj, json_object *poArcsDB,
240
                                 ScalingParams *psParams)
241
0
{
242
0
    const auto nRings = json_object_array_length(poArcsObj);
243
0
    for (auto i = decltype(nRings){0}; i < nRings; i++)
244
0
    {
245
0
        OGRLineString *poLS = new OGRLineString();
246
0
        poMLS->addGeometryDirectly(poLS);
247
248
0
        json_object *poRing = json_object_array_get_idx(poArcsObj, i);
249
0
        if (poRing != nullptr &&
250
0
            json_type_array == json_object_get_type(poRing))
251
0
        {
252
0
            ParseLineString(poLS, poRing, poArcsDB, psParams);
253
0
        }
254
0
    }
255
0
}
256
257
/************************************************************************/
258
/*                       ParseMultiPolygon()                            */
259
/************************************************************************/
260
261
static void ParseMultiPolygon(OGRMultiPolygon *poMultiPoly,
262
                              json_object *poArcsObj, json_object *poArcsDB,
263
                              ScalingParams *psParams)
264
0
{
265
0
    const auto nPolys = json_object_array_length(poArcsObj);
266
0
    for (auto i = decltype(nPolys){0}; i < nPolys; i++)
267
0
    {
268
0
        OGRPolygon *poPoly = new OGRPolygon();
269
270
0
        json_object *poPolyArcs = json_object_array_get_idx(poArcsObj, i);
271
0
        if (poPolyArcs != nullptr &&
272
0
            json_type_array == json_object_get_type(poPolyArcs))
273
0
        {
274
0
            ParsePolygon(poPoly, poPolyArcs, poArcsDB, psParams);
275
0
        }
276
277
0
        if (poPoly->IsEmpty())
278
0
        {
279
0
            delete poPoly;
280
0
        }
281
0
        else
282
0
        {
283
0
            poMultiPoly->addGeometryDirectly(poPoly);
284
0
        }
285
0
    }
286
0
}
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
0
{
296
0
    json_object *poType = OGRGeoJSONFindMemberByName(poObj, "type");
297
0
    if (poType == nullptr || json_object_get_type(poType) != json_type_string)
298
0
        return;
299
0
    const char *pszType = json_object_get_string(poType);
300
301
0
    json_object *poArcsObj = OGRGeoJSONFindMemberByName(poObj, "arcs");
302
0
    json_object *poCoordinatesObj =
303
0
        OGRGeoJSONFindMemberByName(poObj, "coordinates");
304
0
    if (strcmp(pszType, "Point") == 0 || strcmp(pszType, "MultiPoint") == 0)
305
0
    {
306
0
        if (poCoordinatesObj == nullptr ||
307
0
            json_type_array != json_object_get_type(poCoordinatesObj))
308
0
            return;
309
0
    }
310
0
    else
311
0
    {
312
0
        if (poArcsObj == nullptr ||
313
0
            json_type_array != json_object_get_type(poArcsObj))
314
0
            return;
315
0
    }
316
317
0
    if (pszId == nullptr)
318
0
    {
319
0
        json_object *poId = OGRGeoJSONFindMemberByName(poObj, "id");
320
0
        if (poId != nullptr &&
321
0
            (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
0
    }
327
328
0
    OGRFeature *poFeature = new OGRFeature(poLayer->GetLayerDefn());
329
0
    if (pszId != nullptr)
330
0
        poFeature->SetField("id", pszId);
331
332
0
    json_object *poProperties = OGRGeoJSONFindMemberByName(poObj, "properties");
333
0
    if (poProperties != nullptr &&
334
0
        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
0
    OGRGeometry *poGeom = nullptr;
349
0
    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
0
    else if (strcmp(pszType, "MultiPoint") == 0)
365
0
    {
366
0
        OGRMultiPoint *poMP = new OGRMultiPoint();
367
0
        poGeom = poMP;
368
0
        const auto nTuples = json_object_array_length(poCoordinatesObj);
369
0
        for (auto i = decltype(nTuples){0}; i < nTuples; i++)
370
0
        {
371
0
            json_object *poPair =
372
0
                json_object_array_get_idx(poCoordinatesObj, i);
373
0
            double dfX = 0.0;
374
0
            double dfY = 0.0;
375
0
            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
0
        }
382
0
    }
383
0
    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
0
    else if (strcmp(pszType, "MultiLineString") == 0)
390
0
    {
391
0
        OGRMultiLineString *poMLS = new OGRMultiLineString();
392
0
        poGeom = poMLS;
393
0
        ParseMultiLineString(poMLS, poArcsObj, poArcsDB, psParams);
394
0
    }
395
0
    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
0
    else if (strcmp(pszType, "MultiPolygon") == 0)
402
0
    {
403
0
        OGRMultiPolygon *poMultiPoly = new OGRMultiPolygon();
404
0
        poGeom = poMultiPoly;
405
0
        ParseMultiPolygon(poMultiPoly, poArcsObj, poArcsDB, psParams);
406
0
    }
407
408
0
    if (poGeom != nullptr)
409
0
        poFeature->SetGeometryDirectly(poGeom);
410
0
    poLayer->AddFeature(poFeature);
411
0
    delete poFeature;
412
0
}
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
0
{
426
0
    json_object *poObjProps = OGRGeoJSONFindMemberByName(poObj, "properties");
427
0
    if (nullptr != poObjProps &&
428
0
        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
0
}
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
0
{
468
0
    bool bNeedSecondPass = false;
469
470
0
    if (poObj != nullptr && json_type_object == json_object_get_type(poObj))
471
0
    {
472
0
        json_object *poType = OGRGeoJSONFindMemberByName(poObj, "type");
473
0
        if (poType != nullptr &&
474
0
            json_type_string == json_object_get_type(poType))
475
0
        {
476
0
            const char *pszType = json_object_get_string(poType);
477
0
            if (strcmp(pszType, "GeometryCollection") == 0)
478
0
            {
479
0
                json_object *poGeometries =
480
0
                    OGRGeoJSONFindMemberByName(poObj, "geometries");
481
0
                if (poGeometries != nullptr &&
482
0
                    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
0
            }
566
0
            else if (strcmp(pszType, "Point") == 0 ||
567
0
                     strcmp(pszType, "MultiPoint") == 0 ||
568
0
                     strcmp(pszType, "LineString") == 0 ||
569
0
                     strcmp(pszType, "MultiLineString") == 0 ||
570
0
                     strcmp(pszType, "Polygon") == 0 ||
571
0
                     strcmp(pszType, "MultiPolygon") == 0)
572
0
            {
573
0
                if (*ppoMainLayer == nullptr)
574
0
                {
575
0
                    *ppoMainLayer = new OGRGeoJSONLayer(
576
0
                        "TopoJSON", nullptr, wkbUnknown, poDS, nullptr);
577
578
0
                    (*ppoMainLayer)->SetSupportsZGeometries(false);
579
580
0
                    whileUnsealing((*ppoMainLayer)->GetLayerDefn())
581
0
                        ->GetGeomFieldDefn(0)
582
0
                        ->SetSpatialRef(poSRS);
583
584
0
                    apoFieldDefn.emplace_back(
585
0
                        std::make_unique<OGRFieldDefn>("id", OFTString));
586
0
                    oMapFieldNameToIdx["id"] = 0;
587
0
                    dag.addNode(0, "id");
588
0
                }
589
590
0
                const int nPrevFieldIdx = 0;
591
0
                EstablishLayerDefn(nPrevFieldIdx, anCurFieldIndices,
592
0
                                   oMapFieldNameToIdx, apoFieldDefn, dag, poObj,
593
0
                                   aoSetUndeterminedTypeFields);
594
595
0
                bNeedSecondPass = true;
596
0
            }
597
0
        }
598
0
    }
599
0
    return bNeedSecondPass;
600
0
}
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
0
{
611
0
    if (poObj != nullptr && json_type_object == json_object_get_type(poObj))
612
0
    {
613
0
        json_object *poType = OGRGeoJSONFindMemberByName(poObj, "type");
614
0
        if (poType != nullptr &&
615
0
            json_type_string == json_object_get_type(poType))
616
0
        {
617
0
            const char *pszType = json_object_get_string(poType);
618
0
            if (strcmp(pszType, "Point") == 0 ||
619
0
                strcmp(pszType, "MultiPoint") == 0 ||
620
0
                strcmp(pszType, "LineString") == 0 ||
621
0
                strcmp(pszType, "MultiLineString") == 0 ||
622
0
                strcmp(pszType, "Polygon") == 0 ||
623
0
                strcmp(pszType, "MultiPolygon") == 0)
624
0
            {
625
0
                ParseObject(pszId, poObj, *ppoMainLayer, poArcs, psParams);
626
0
            }
627
0
        }
628
0
    }
629
0
}
630
631
/************************************************************************/
632
/*                           ReadLayers()                               */
633
/************************************************************************/
634
635
void OGRTopoJSONReader::ReadLayers(OGRGeoJSONDataSource *poDS)
636
0
{
637
0
    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
0
    poDS->SetSupportsZGeometries(false);
645
646
0
    ScalingParams sParams;
647
0
    sParams.dfScale0 = 1.0;
648
0
    sParams.dfScale1 = 1.0;
649
0
    sParams.dfTranslate0 = 0.0;
650
0
    sParams.dfTranslate1 = 0.0;
651
0
    sParams.bElementExists = false;
652
0
    json_object *poObjTransform =
653
0
        OGRGeoJSONFindMemberByName(poGJObject_, "transform");
654
0
    if (nullptr != poObjTransform &&
655
0
        json_type_object == json_object_get_type(poObjTransform))
656
0
    {
657
0
        json_object *poObjScale =
658
0
            OGRGeoJSONFindMemberByName(poObjTransform, "scale");
659
0
        if (nullptr != poObjScale &&
660
0
            json_type_array == json_object_get_type(poObjScale) &&
661
0
            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
0
        json_object *poObjTranslate =
679
0
            OGRGeoJSONFindMemberByName(poObjTransform, "translate");
680
0
        if (nullptr != poObjTranslate &&
681
0
            json_type_array == json_object_get_type(poObjTranslate) &&
682
0
            json_object_array_length(poObjTranslate) == 2)
683
0
        {
684
0
            json_object *poTranslate0 =
685
0
                json_object_array_get_idx(poObjTranslate, 0);
686
0
            json_object *poTranslate1 =
687
0
                json_object_array_get_idx(poObjTranslate, 1);
688
0
            if (poTranslate0 != nullptr &&
689
0
                (json_object_get_type(poTranslate0) == json_type_double ||
690
0
                 json_object_get_type(poTranslate0) == json_type_int) &&
691
0
                poTranslate1 != nullptr &&
692
0
                (json_object_get_type(poTranslate1) == json_type_double ||
693
0
                 json_object_get_type(poTranslate1) == json_type_int))
694
0
            {
695
0
                sParams.dfTranslate0 = json_object_get_double(poTranslate0);
696
0
                sParams.dfTranslate1 = json_object_get_double(poTranslate1);
697
0
                sParams.bElementExists = true;
698
0
            }
699
0
        }
700
0
    }
701
702
0
    json_object *poArcs = OGRGeoJSONFindMemberByName(poGJObject_, "arcs");
703
0
    if (poArcs == nullptr || json_type_array != json_object_get_type(poArcs))
704
0
        return;
705
706
0
    OGRGeoJSONLayer *poMainLayer = nullptr;
707
708
0
    json_object *poObjects = OGRGeoJSONFindMemberByName(poGJObject_, "objects");
709
0
    if (poObjects == nullptr)
710
0
        return;
711
712
0
    OGRSpatialReference *poSRS = OGRGeoJSONReadSpatialReference(poGJObject_);
713
714
0
    std::vector<int> anCurFieldIndices;
715
0
    std::map<std::string, int> oMapFieldNameToIdx;
716
0
    std::vector<std::unique_ptr<OGRFieldDefn>> apoFieldDefn;
717
0
    gdal::DirectedAcyclicGraph<int, std::string> dag;
718
719
0
    std::set<int> aoSetUndeterminedTypeFields;
720
0
    if (json_type_object == json_object_get_type(poObjects))
721
0
    {
722
0
        json_object_iter it;
723
0
        it.key = nullptr;
724
0
        it.val = nullptr;
725
0
        it.entry = nullptr;
726
0
        bool bNeedSecondPass = false;
727
0
        json_object_object_foreachC(poObjects, it)
728
0
        {
729
0
            json_object *poObj = it.val;
730
0
            bNeedSecondPass |= ParseObjectMain(
731
0
                it.key, poObj, poSRS, poDS, &poMainLayer, poArcs, &sParams,
732
0
                anCurFieldIndices, oMapFieldNameToIdx, apoFieldDefn, dag,
733
0
                aoSetUndeterminedTypeFields);
734
0
        }
735
0
        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
0
    }
757
0
    else if (json_type_array == json_object_get_type(poObjects))
758
0
    {
759
0
        const auto nObjects = json_object_array_length(poObjects);
760
0
        bool bNeedSecondPass = false;
761
0
        for (auto i = decltype(nObjects){0}; i < nObjects; i++)
762
0
        {
763
0
            json_object *poObj = json_object_array_get_idx(poObjects, i);
764
0
            bNeedSecondPass |= ParseObjectMain(
765
0
                nullptr, poObj, poSRS, poDS, &poMainLayer, poArcs, &sParams,
766
0
                anCurFieldIndices, oMapFieldNameToIdx, apoFieldDefn, dag,
767
0
                aoSetUndeterminedTypeFields);
768
0
        }
769
0
        if (bNeedSecondPass)
770
0
        {
771
0
            OGRFeatureDefn *poDefn = poMainLayer->GetLayerDefn();
772
0
            const auto sortedFields = dag.getTopologicalOrdering();
773
0
            CPLAssert(sortedFields.size() == apoFieldDefn.size());
774
0
            auto oTemporaryUnsealer(poDefn->GetTemporaryUnsealer());
775
0
            for (int idx : sortedFields)
776
0
            {
777
0
                poDefn->AddFieldDefn(apoFieldDefn[idx].get());
778
0
            }
779
780
0
            for (auto i = decltype(nObjects){0}; i < nObjects; i++)
781
0
            {
782
0
                json_object *poObj = json_object_array_get_idx(poObjects, i);
783
0
                ParseObjectMainSecondPass(nullptr, poObj, &poMainLayer, poArcs,
784
0
                                          &sParams);
785
0
            }
786
0
        }
787
0
    }
788
789
0
    if (poMainLayer != nullptr)
790
0
    {
791
0
        poMainLayer->DetectGeometryType();
792
0
        poDS->AddLayer(poMainLayer);
793
0
    }
794
795
0
    if (poSRS)
796
0
        poSRS->Release();
797
0
}