Coverage Report

Created: 2025-12-31 06:48

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
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
    auto poFeature = std::make_unique<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.get(), nField, it.key,
344
0
                                     it.val, 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(std::move(poFeature));
411
0
}
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
0
{
425
0
    json_object *poObjProps = OGRGeoJSONFindMemberByName(poObj, "properties");
426
0
    if (nullptr != poObjProps &&
427
0
        json_object_get_type(poObjProps) == json_type_object)
428
0
    {
429
0
        json_object_iter it;
430
0
        it.key = nullptr;
431
0
        it.val = nullptr;
432
0
        it.entry = nullptr;
433
434
0
        json_object_object_foreachC(poObjProps, it)
435
0
        {
436
0
            anCurFieldIndices.clear();
437
0
            OGRGeoJSONReaderAddOrUpdateField(
438
0
                anCurFieldIndices, oMapFieldNameToIdx, apoFieldDefn, it.key,
439
0
                it.val, false, 0, false, false, aoSetUndeterminedTypeFields);
440
0
            for (int idx : anCurFieldIndices)
441
0
            {
442
0
                dag.addNode(idx, apoFieldDefn[idx]->GetNameRef());
443
0
                if (nPrevFieldIdx != -1)
444
0
                {
445
0
                    dag.addEdge(nPrevFieldIdx, idx);
446
0
                }
447
0
                nPrevFieldIdx = idx;
448
0
            }
449
0
        }
450
0
    }
451
0
}
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
0
{
467
0
    bool bNeedSecondPass = false;
468
469
0
    if (poObj != nullptr && json_type_object == json_object_get_type(poObj))
470
0
    {
471
0
        json_object *poType = OGRGeoJSONFindMemberByName(poObj, "type");
472
0
        if (poType != nullptr &&
473
0
            json_type_string == json_object_get_type(poType))
474
0
        {
475
0
            const char *pszType = json_object_get_string(poType);
476
0
            if (strcmp(pszType, "GeometryCollection") == 0)
477
0
            {
478
0
                json_object *poGeometries =
479
0
                    OGRGeoJSONFindMemberByName(poObj, "geometries");
480
0
                if (poGeometries != nullptr &&
481
0
                    json_type_array == json_object_get_type(poGeometries))
482
0
                {
483
0
                    if (pszId == nullptr)
484
0
                    {
485
0
                        json_object *poId =
486
0
                            OGRGeoJSONFindMemberByName(poObj, "id");
487
0
                        if (poId != nullptr &&
488
0
                            (json_type_string == json_object_get_type(poId) ||
489
0
                             json_type_int == json_object_get_type(poId)))
490
0
                        {
491
0
                            pszId = json_object_get_string(poId);
492
0
                        }
493
0
                    }
494
495
0
                    OGRGeoJSONLayer *poLayer =
496
0
                        new OGRGeoJSONLayer(pszId ? pszId : "TopoJSON", nullptr,
497
0
                                            wkbUnknown, poDS, nullptr);
498
0
                    poLayer->SetSupportsZGeometries(false);
499
0
                    OGRFeatureDefn *poDefn = poLayer->GetLayerDefn();
500
501
0
                    whileUnsealing(poDefn)->GetGeomFieldDefn(0)->SetSpatialRef(
502
0
                        poSRS);
503
504
0
                    const auto nGeometries =
505
0
                        json_object_array_length(poGeometries);
506
                    // First pass to establish schema.
507
508
0
                    std::vector<int> anCurFieldIndicesLocal;
509
0
                    std::map<std::string, int> oMapFieldNameToIdxLocal;
510
0
                    std::vector<std::unique_ptr<OGRFieldDefn>>
511
0
                        apoFieldDefnLocal;
512
0
                    gdal::DirectedAcyclicGraph<int, std::string> dagLocal;
513
0
                    std::set<int> aoSetUndeterminedTypeFieldsLocal;
514
515
0
                    apoFieldDefnLocal.emplace_back(
516
0
                        std::make_unique<OGRFieldDefn>("id", OFTString));
517
0
                    oMapFieldNameToIdxLocal["id"] = 0;
518
0
                    dagLocal.addNode(0, "id");
519
0
                    const int nPrevFieldIdx = 0;
520
521
0
                    for (auto i = decltype(nGeometries){0}; i < nGeometries;
522
0
                         i++)
523
0
                    {
524
0
                        json_object *poGeom =
525
0
                            json_object_array_get_idx(poGeometries, i);
526
0
                        if (poGeom != nullptr &&
527
0
                            json_type_object == json_object_get_type(poGeom))
528
0
                        {
529
0
                            EstablishLayerDefn(
530
0
                                nPrevFieldIdx, anCurFieldIndicesLocal,
531
0
                                oMapFieldNameToIdxLocal, apoFieldDefnLocal,
532
0
                                dagLocal, poGeom,
533
0
                                aoSetUndeterminedTypeFieldsLocal);
534
0
                        }
535
0
                    }
536
537
0
                    const auto sortedFields = dagLocal.getTopologicalOrdering();
538
0
                    CPLAssert(sortedFields.size() == apoFieldDefnLocal.size());
539
0
                    {
540
0
                        auto oTemporaryUnsealer(poDefn->GetTemporaryUnsealer());
541
0
                        for (int idx : sortedFields)
542
0
                        {
543
0
                            poDefn->AddFieldDefn(apoFieldDefnLocal[idx].get());
544
0
                        }
545
0
                    }
546
547
                    // Second pass to build objects.
548
0
                    for (auto i = decltype(nGeometries){0}; i < nGeometries;
549
0
                         i++)
550
0
                    {
551
0
                        json_object *poGeom =
552
0
                            json_object_array_get_idx(poGeometries, i);
553
0
                        if (poGeom != nullptr &&
554
0
                            json_type_object == json_object_get_type(poGeom))
555
0
                        {
556
0
                            ParseObject(nullptr, poGeom, poLayer, poArcs,
557
0
                                        psParams);
558
0
                        }
559
0
                    }
560
561
0
                    poLayer->DetectGeometryType();
562
0
                    poDS->AddLayer(poLayer);
563
0
                }
564
0
            }
565
0
            else if (strcmp(pszType, "Point") == 0 ||
566
0
                     strcmp(pszType, "MultiPoint") == 0 ||
567
0
                     strcmp(pszType, "LineString") == 0 ||
568
0
                     strcmp(pszType, "MultiLineString") == 0 ||
569
0
                     strcmp(pszType, "Polygon") == 0 ||
570
0
                     strcmp(pszType, "MultiPolygon") == 0)
571
0
            {
572
0
                if (*ppoMainLayer == nullptr)
573
0
                {
574
0
                    *ppoMainLayer = new OGRGeoJSONLayer(
575
0
                        "TopoJSON", nullptr, wkbUnknown, poDS, nullptr);
576
577
0
                    (*ppoMainLayer)->SetSupportsZGeometries(false);
578
579
0
                    whileUnsealing((*ppoMainLayer)->GetLayerDefn())
580
0
                        ->GetGeomFieldDefn(0)
581
0
                        ->SetSpatialRef(poSRS);
582
583
0
                    apoFieldDefn.emplace_back(
584
0
                        std::make_unique<OGRFieldDefn>("id", OFTString));
585
0
                    oMapFieldNameToIdx["id"] = 0;
586
0
                    dag.addNode(0, "id");
587
0
                }
588
589
0
                const int nPrevFieldIdx = 0;
590
0
                EstablishLayerDefn(nPrevFieldIdx, anCurFieldIndices,
591
0
                                   oMapFieldNameToIdx, apoFieldDefn, dag, poObj,
592
0
                                   aoSetUndeterminedTypeFields);
593
594
0
                bNeedSecondPass = true;
595
0
            }
596
0
        }
597
0
    }
598
0
    return bNeedSecondPass;
599
0
}
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
0
{
610
0
    if (poObj != nullptr && json_type_object == json_object_get_type(poObj))
611
0
    {
612
0
        json_object *poType = OGRGeoJSONFindMemberByName(poObj, "type");
613
0
        if (poType != nullptr &&
614
0
            json_type_string == json_object_get_type(poType))
615
0
        {
616
0
            const char *pszType = json_object_get_string(poType);
617
0
            if (strcmp(pszType, "Point") == 0 ||
618
0
                strcmp(pszType, "MultiPoint") == 0 ||
619
0
                strcmp(pszType, "LineString") == 0 ||
620
0
                strcmp(pszType, "MultiLineString") == 0 ||
621
0
                strcmp(pszType, "Polygon") == 0 ||
622
0
                strcmp(pszType, "MultiPolygon") == 0)
623
0
            {
624
0
                ParseObject(pszId, poObj, *ppoMainLayer, poArcs, psParams);
625
0
            }
626
0
        }
627
0
    }
628
0
}
629
630
/************************************************************************/
631
/*                           ReadLayers()                               */
632
/************************************************************************/
633
634
void OGRTopoJSONReader::ReadLayers(OGRGeoJSONDataSource *poDS)
635
0
{
636
0
    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
0
    poDS->SetSupportsZGeometries(false);
644
645
0
    ScalingParams sParams;
646
0
    sParams.dfScale0 = 1.0;
647
0
    sParams.dfScale1 = 1.0;
648
0
    sParams.dfTranslate0 = 0.0;
649
0
    sParams.dfTranslate1 = 0.0;
650
0
    sParams.bElementExists = false;
651
0
    json_object *poObjTransform =
652
0
        OGRGeoJSONFindMemberByName(poGJObject_, "transform");
653
0
    if (nullptr != poObjTransform &&
654
0
        json_type_object == json_object_get_type(poObjTransform))
655
0
    {
656
0
        json_object *poObjScale =
657
0
            OGRGeoJSONFindMemberByName(poObjTransform, "scale");
658
0
        if (nullptr != poObjScale &&
659
0
            json_type_array == json_object_get_type(poObjScale) &&
660
0
            json_object_array_length(poObjScale) == 2)
661
0
        {
662
0
            json_object *poScale0 = json_object_array_get_idx(poObjScale, 0);
663
0
            json_object *poScale1 = json_object_array_get_idx(poObjScale, 1);
664
0
            if (poScale0 != nullptr &&
665
0
                (json_object_get_type(poScale0) == json_type_double ||
666
0
                 json_object_get_type(poScale0) == json_type_int) &&
667
0
                poScale1 != nullptr &&
668
0
                (json_object_get_type(poScale1) == json_type_double ||
669
0
                 json_object_get_type(poScale1) == json_type_int))
670
0
            {
671
0
                sParams.dfScale0 = json_object_get_double(poScale0);
672
0
                sParams.dfScale1 = json_object_get_double(poScale1);
673
0
                sParams.bElementExists = true;
674
0
            }
675
0
        }
676
677
0
        json_object *poObjTranslate =
678
0
            OGRGeoJSONFindMemberByName(poObjTransform, "translate");
679
0
        if (nullptr != poObjTranslate &&
680
0
            json_type_array == json_object_get_type(poObjTranslate) &&
681
0
            json_object_array_length(poObjTranslate) == 2)
682
0
        {
683
0
            json_object *poTranslate0 =
684
0
                json_object_array_get_idx(poObjTranslate, 0);
685
0
            json_object *poTranslate1 =
686
0
                json_object_array_get_idx(poObjTranslate, 1);
687
0
            if (poTranslate0 != nullptr &&
688
0
                (json_object_get_type(poTranslate0) == json_type_double ||
689
0
                 json_object_get_type(poTranslate0) == json_type_int) &&
690
0
                poTranslate1 != nullptr &&
691
0
                (json_object_get_type(poTranslate1) == json_type_double ||
692
0
                 json_object_get_type(poTranslate1) == json_type_int))
693
0
            {
694
0
                sParams.dfTranslate0 = json_object_get_double(poTranslate0);
695
0
                sParams.dfTranslate1 = json_object_get_double(poTranslate1);
696
0
                sParams.bElementExists = true;
697
0
            }
698
0
        }
699
0
    }
700
701
0
    json_object *poArcs = OGRGeoJSONFindMemberByName(poGJObject_, "arcs");
702
0
    if (poArcs == nullptr || json_type_array != json_object_get_type(poArcs))
703
0
        return;
704
705
0
    OGRGeoJSONLayer *poMainLayer = nullptr;
706
707
0
    json_object *poObjects = OGRGeoJSONFindMemberByName(poGJObject_, "objects");
708
0
    if (poObjects == nullptr)
709
0
        return;
710
711
0
    OGRSpatialReference *poSRS = OGRGeoJSONReadSpatialReference(poGJObject_);
712
713
0
    std::vector<int> anCurFieldIndices;
714
0
    std::map<std::string, int> oMapFieldNameToIdx;
715
0
    std::vector<std::unique_ptr<OGRFieldDefn>> apoFieldDefn;
716
0
    gdal::DirectedAcyclicGraph<int, std::string> dag;
717
718
0
    std::set<int> aoSetUndeterminedTypeFields;
719
0
    if (json_type_object == json_object_get_type(poObjects))
720
0
    {
721
0
        json_object_iter it;
722
0
        it.key = nullptr;
723
0
        it.val = nullptr;
724
0
        it.entry = nullptr;
725
0
        bool bNeedSecondPass = false;
726
0
        json_object_object_foreachC(poObjects, it)
727
0
        {
728
0
            json_object *poObj = it.val;
729
0
            bNeedSecondPass |= ParseObjectMain(
730
0
                it.key, poObj, poSRS, poDS, &poMainLayer, poArcs, &sParams,
731
0
                anCurFieldIndices, oMapFieldNameToIdx, apoFieldDefn, dag,
732
0
                aoSetUndeterminedTypeFields);
733
0
        }
734
0
        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
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
            OGRLayer *poMainLayerAsLayer = poMainLayer;
772
0
            OGRFeatureDefn *poDefn = poMainLayerAsLayer->GetLayerDefn();
773
0
            const auto sortedFields = dag.getTopologicalOrdering();
774
0
            CPLAssert(sortedFields.size() == apoFieldDefn.size());
775
0
            auto oTemporaryUnsealer(poDefn->GetTemporaryUnsealer());
776
0
            for (int idx : sortedFields)
777
0
            {
778
0
                poDefn->AddFieldDefn(apoFieldDefn[idx].get());
779
0
            }
780
781
0
            for (auto i = decltype(nObjects){0}; i < nObjects; i++)
782
0
            {
783
0
                json_object *poObj = json_object_array_get_idx(poObjects, i);
784
0
                ParseObjectMainSecondPass(nullptr, poObj, &poMainLayer, poArcs,
785
0
                                          &sParams);
786
0
            }
787
0
        }
788
0
    }
789
790
0
    if (poMainLayer != nullptr)
791
0
    {
792
0
        poMainLayer->DetectGeometryType();
793
0
        poDS->AddLayer(poMainLayer);
794
0
    }
795
796
0
    if (poSRS)
797
0
        poSRS->Release();
798
0
}