Coverage Report

Created: 2025-06-09 08:44

/src/gdal/ogr/ogrsf_frmts/jsonfg/ogrjsonfgreader.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  OpenGIS Simple Features Reference Implementation
4
 * Purpose:  Implementation of OGC Features and Geometries JSON (JSON-FG)
5
 * Author:   Even Rouault <even.rouault at spatialys.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2023, Even Rouault <even.rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "ogr_jsonfg.h"
14
15
#include "ogrgeojsonreader.h"
16
#include "ogrgeojsonutils.h"
17
#include "ogrlibjsonutils.h"
18
#include "ogrgeojsongeometry.h"
19
#include "ogr_geojson.h"
20
21
#include "cpl_vsi_virtual.h"
22
23
#include <json.h>  // JSON-C
24
25
/************************************************************************/
26
/*                  OGRJSONFGReader::~OGRJSONFGReader()                 */
27
/************************************************************************/
28
29
OGRJSONFGReader::~OGRJSONFGReader()
30
746
{
31
746
    if (poObject_)
32
27
        json_object_put(poObject_);
33
746
}
34
35
/************************************************************************/
36
/*                  OGRJSONFGReader::Load()                             */
37
/************************************************************************/
38
39
bool OGRJSONFGReader::Load(OGRJSONFGDataset *poDS, const char *pszText,
40
                           const std::string &osDefaultLayerName)
41
337
{
42
337
    if (!OGRJSonParse(pszText, &poObject_))
43
313
        return false;
44
45
24
    poDS_ = poDS;
46
24
    osDefaultLayerName_ = osDefaultLayerName;
47
48
24
    if (!GenerateLayerDefns())
49
19
        return false;
50
51
5
    const GeoJSONObject::Type objType = OGRGeoJSONGetType(poObject_);
52
5
    if (objType == GeoJSONObject::eFeature)
53
5
    {
54
5
        OGRJSONFGMemLayer *poLayer = nullptr;
55
5
        auto poFeat = ReadFeature(poObject_, nullptr, &poLayer, nullptr);
56
5
        if (poFeat)
57
5
        {
58
5
            poLayer->AddFeature(std::move(poFeat));
59
5
            return true;
60
5
        }
61
0
        return false;
62
5
    }
63
0
    else if (objType == GeoJSONObject::eFeatureCollection)
64
0
    {
65
0
        json_object *poObjFeatures =
66
0
            OGRGeoJSONFindMemberByName(poObject_, "features");
67
0
        if (nullptr != poObjFeatures &&
68
0
            json_type_array == json_object_get_type(poObjFeatures))
69
0
        {
70
0
            const auto nFeatures = json_object_array_length(poObjFeatures);
71
0
            for (auto i = decltype(nFeatures){0}; i < nFeatures; ++i)
72
0
            {
73
0
                json_object *poObjFeature =
74
0
                    json_object_array_get_idx(poObjFeatures, i);
75
0
                OGRJSONFGMemLayer *poLayer = nullptr;
76
0
                auto poFeat =
77
0
                    ReadFeature(poObjFeature, nullptr, &poLayer, nullptr);
78
0
                if (!poFeat)
79
0
                    return false;
80
0
                poLayer->AddFeature(std::move(poFeat));
81
0
            }
82
0
        }
83
0
    }
84
0
    else
85
0
    {
86
0
        return false;
87
0
    }
88
89
0
    return true;
90
5
}
91
92
/************************************************************************/
93
/*                    OGRJSONFGReadCoordRefSys()                        */
94
/************************************************************************/
95
96
static std::unique_ptr<OGRSpatialReference>
97
OGRJSONFGReadCoordRefSys(json_object *poCoordRefSys, bool bCanRecurse = true)
98
0
{
99
0
    const auto eType = json_object_get_type(poCoordRefSys);
100
0
    if (eType == json_type_string)
101
0
    {
102
0
        const char *pszStr = json_object_get_string(poCoordRefSys);
103
0
        if (pszStr[0] == '[' && pszStr[strlen(pszStr) - 1] == ']')
104
0
        {
105
            // Safe CURIE, e.g. "[EPSG:4326]"
106
0
            const char *pszColon = strchr(pszStr + 1, ':');
107
0
            if (!pszColon)
108
0
            {
109
0
                CPLError(CE_Failure, CPLE_AppDefined,
110
0
                         "Invalid coordRefSys string: %s", pszStr);
111
0
                return nullptr;
112
0
            }
113
0
            std::string osURL("http://www.opengis.net/def/crs/");
114
0
            osURL.append(pszStr + 1, pszColon - (pszStr + 1));
115
0
            osURL += "/0/";
116
0
            osURL.append(pszColon + 1,
117
0
                         (pszStr + strlen(pszStr) - 1) - (pszColon + 1));
118
0
            auto poSRS = std::make_unique<OGRSpatialReference>();
119
0
            if (poSRS->importFromCRSURL(osURL.c_str()) != OGRERR_NONE)
120
0
            {
121
0
                return nullptr;
122
0
            }
123
0
            return poSRS;
124
0
        }
125
0
        else if (STARTS_WITH(pszStr, "http://www.opengis.net/def/crs/") ||
126
0
                 STARTS_WITH(pszStr, "https://www.opengis.net/def/crs/"))
127
0
        {
128
            // OGC URI, e.g. "http://www.opengis.net/def/crs/EPSG/0/4326"
129
0
            auto poSRS = std::make_unique<OGRSpatialReference>();
130
0
            if (poSRS->importFromCRSURL(pszStr) != OGRERR_NONE)
131
0
            {
132
0
                return nullptr;
133
0
            }
134
0
            return poSRS;
135
0
        }
136
0
        else
137
0
        {
138
0
            CPLError(CE_Failure, CPLE_AppDefined,
139
0
                     "Invalid coordRefSys string: %s", pszStr);
140
0
            return nullptr;
141
0
        }
142
0
    }
143
0
    else if (eType == json_type_object)
144
0
    {
145
        /* Things like
146
              {
147
                "type": "Reference",
148
                "href": "http://www.opengis.net/def/crs/EPSG/0/4258",
149
                "epoch": 2016.47
150
              }
151
        */
152
153
0
        json_object *poType = CPL_json_object_object_get(poCoordRefSys, "type");
154
0
        if (!poType)
155
0
        {
156
0
            CPLError(CE_Failure, CPLE_AppDefined,
157
0
                     "Missing type member in coordRefSys object");
158
0
            return nullptr;
159
0
        }
160
0
        if (json_object_get_type(poType) != json_type_string)
161
0
        {
162
0
            CPLError(CE_Failure, CPLE_AppDefined,
163
0
                     "Type member of coordRefSys object is not a string");
164
0
            return nullptr;
165
0
        }
166
0
        const char *pszType = json_object_get_string(poType);
167
0
        if (strcmp(pszType, "Reference") != 0)
168
0
        {
169
0
            CPLError(CE_Failure, CPLE_AppDefined,
170
0
                     "Only type=\"Reference\" handled in coordRefSys object");
171
0
            return nullptr;
172
0
        }
173
174
0
        json_object *poHRef = CPL_json_object_object_get(poCoordRefSys, "href");
175
0
        if (!poHRef)
176
0
        {
177
0
            CPLError(CE_Failure, CPLE_AppDefined,
178
0
                     "Missing href member in coordRefSys object");
179
0
            return nullptr;
180
0
        }
181
182
0
        auto poSRS = OGRJSONFGReadCoordRefSys(poHRef);
183
0
        if (!poSRS)
184
0
            return nullptr;
185
186
0
        json_object *poEpoch =
187
0
            CPL_json_object_object_get(poCoordRefSys, "epoch");
188
0
        if (poEpoch)
189
0
        {
190
0
            const auto epochType = json_object_get_type(poEpoch);
191
0
            if (epochType != json_type_int && epochType != json_type_double)
192
0
            {
193
0
                CPLError(
194
0
                    CE_Failure, CPLE_AppDefined,
195
0
                    "Wrong value type for epoch member in coordRefSys object");
196
0
                return nullptr;
197
0
            }
198
199
0
            poSRS->SetCoordinateEpoch(json_object_get_double(poEpoch));
200
0
        }
201
202
0
        return poSRS;
203
0
    }
204
0
    else if (eType == json_type_array && bCanRecurse)
205
0
    {
206
0
        if (json_object_array_length(poCoordRefSys) != 2)
207
0
        {
208
0
            CPLError(CE_Failure, CPLE_AppDefined,
209
0
                     "Expected 2 items in coordRefSys array");
210
0
            return nullptr;
211
0
        }
212
0
        auto poSRS1 = OGRJSONFGReadCoordRefSys(
213
0
            json_object_array_get_idx(poCoordRefSys, 0),
214
0
            /* bCanRecurse = */ false);
215
0
        if (!poSRS1)
216
0
            return nullptr;
217
0
        auto poSRS2 = OGRJSONFGReadCoordRefSys(
218
0
            json_object_array_get_idx(poCoordRefSys, 1),
219
0
            /* bCanRecurse = */ false);
220
0
        if (!poSRS2)
221
0
            return nullptr;
222
0
        auto poSRS = std::make_unique<OGRSpatialReference>();
223
224
0
        std::string osName;
225
0
        const char *pszName1 = poSRS1->GetName();
226
0
        osName = pszName1 ? pszName1 : "unnamed";
227
0
        osName += " + ";
228
0
        const char *pszName2 = poSRS2->GetName();
229
0
        osName += pszName2 ? pszName2 : "unnamed";
230
231
0
        if (poSRS->SetCompoundCS(osName.c_str(), poSRS1.get(), poSRS2.get()) !=
232
0
            OGRERR_NONE)
233
0
            return nullptr;
234
0
        const double dfEpoch = poSRS1->GetCoordinateEpoch();
235
0
        if (dfEpoch > 0)
236
0
            poSRS->SetCoordinateEpoch(dfEpoch);
237
0
        return poSRS;
238
0
    }
239
0
    else
240
0
    {
241
0
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid coordRefSys object");
242
0
    }
243
0
    return nullptr;
244
0
}
245
246
/************************************************************************/
247
/*              OGRJSONFGReader::AnalyzeWithStreamingParser()           */
248
/************************************************************************/
249
250
bool OGRJSONFGReader::AnalyzeWithStreamingParser(
251
    OGRJSONFGDataset *poDS, VSILFILE *fp, const std::string &osDefaultLayerName,
252
    bool &bCanTryWithNonStreamingParserOut)
253
178
{
254
178
    poDS_ = poDS;
255
178
    osDefaultLayerName_ = osDefaultLayerName;
256
257
178
    bCanTryWithNonStreamingParserOut = false;
258
178
    OGRJSONFGStreamingParser oParser(*this, /*bFirstPass = */ true);
259
260
178
    std::vector<GByte> abyBuffer;
261
178
    abyBuffer.resize(4096 * 10);
262
383
    while (true)
263
383
    {
264
383
        size_t nRead = VSIFReadL(abyBuffer.data(), 1, abyBuffer.size(), fp);
265
383
        const bool bFinished = nRead < abyBuffer.size();
266
383
        if (!oParser.Parse(reinterpret_cast<const char *>(abyBuffer.data()),
267
383
                           nRead, bFinished) ||
268
383
            oParser.ExceptionOccurred())
269
167
        {
270
167
            return false;
271
167
        }
272
216
        if (oParser.IsTypeKnown() && !oParser.IsFeatureCollection())
273
1
        {
274
1
            break;
275
1
        }
276
215
        if (bFinished)
277
10
            break;
278
215
    }
279
280
11
    if (!oParser.IsTypeKnown() || !oParser.IsFeatureCollection())
281
8
    {
282
8
        fp->Seek(0, SEEK_END);
283
8
        const vsi_l_offset nFileSize = fp->Tell();
284
8
        const vsi_l_offset nRAM =
285
8
            static_cast<vsi_l_offset>(CPLGetUsablePhysicalRAM());
286
8
        if (nRAM == 0 || nRAM > nFileSize * 20)
287
8
        {
288
            // Only try full ingestion if we have 20x more RAM than the file
289
            // size
290
8
            bCanTryWithNonStreamingParserOut = true;
291
8
        }
292
8
        return false;
293
8
    }
294
295
3
    poObject_ = oParser.StealRootObject();
296
297
3
    return FinalizeGenerateLayerDefns(true);
298
11
}
299
300
/************************************************************************/
301
/*                OGRJSONFGReader::GenerateLayerDefns()                 */
302
/************************************************************************/
303
304
bool OGRJSONFGReader::GenerateLayerDefns()
305
24
{
306
24
    const GeoJSONObject::Type objType = OGRGeoJSONGetType(poObject_);
307
24
    if (objType == GeoJSONObject::eFeature)
308
5
    {
309
5
        if (!GenerateLayerDefnFromFeature(poObject_))
310
0
            return false;
311
5
    }
312
19
    else if (objType == GeoJSONObject::eFeatureCollection)
313
1
    {
314
1
        json_object *poObjFeatures =
315
1
            OGRGeoJSONFindMemberByName(poObject_, "features");
316
1
        if (nullptr != poObjFeatures &&
317
1
            json_type_array == json_object_get_type(poObjFeatures))
318
0
        {
319
0
            const auto nFeatures = json_object_array_length(poObjFeatures);
320
0
            for (auto i = decltype(nFeatures){0}; i < nFeatures; ++i)
321
0
            {
322
0
                json_object *poObjFeature =
323
0
                    json_object_array_get_idx(poObjFeatures, i);
324
0
                if (!GenerateLayerDefnFromFeature(poObjFeature))
325
0
                {
326
0
                    return false;
327
0
                }
328
0
            }
329
0
        }
330
1
        else
331
1
        {
332
1
            CPLError(CE_Failure, CPLE_AppDefined,
333
1
                     "Invalid FeatureCollection object. "
334
1
                     "Missing \'features\' member.");
335
1
            return false;
336
1
        }
337
1
    }
338
18
    else
339
18
    {
340
18
        CPLError(CE_Failure, CPLE_AppDefined,
341
18
                 "Missing or unhandled root type object");
342
18
        return false;
343
18
    }
344
345
5
    return FinalizeGenerateLayerDefns(false);
346
24
}
347
348
/************************************************************************/
349
/*             OGRJSONFGReader::FinalizeGenerateLayerDefns()            */
350
/************************************************************************/
351
352
bool OGRJSONFGReader::FinalizeGenerateLayerDefns(bool bStreamedLayer)
353
8
{
354
8
    json_object *poName = CPL_json_object_object_get(poObject_, "featureType");
355
8
    if (poName && json_object_get_type(poName) == json_type_string)
356
0
    {
357
        // Remap from hard-coded default layer name to the one of featureType
358
0
        auto oIter = oMapBuildContext_.find(osDefaultLayerName_);
359
0
        osDefaultLayerName_ = json_object_get_string(poName);
360
0
        if (oIter != oMapBuildContext_.end())
361
0
        {
362
0
            auto oBuildContext = std::move(oIter->second);
363
0
            oMapBuildContext_.erase(oIter);
364
0
            oMapBuildContext_[osDefaultLayerName_] = std::move(oBuildContext);
365
0
        }
366
0
    }
367
8
    else if (poName && json_object_get_type(poName) == json_type_array)
368
0
    {
369
0
        static bool bWarningMsgEmitted = false;
370
0
        if (!bWarningMsgEmitted)
371
0
        {
372
0
            CPLError(CE_Warning, CPLE_AppDefined,
373
0
                     "featureType value as an array is not supported.");
374
0
            bWarningMsgEmitted = true;
375
0
        }
376
0
    }
377
378
8
    json_object *poCoordRefSys = nullptr;
379
8
    std::unique_ptr<OGRSpatialReference> poSRSTopLevel;
380
8
    bool bInvalidCRS = false;
381
8
    bool bSwapPlacesXYTopLevel = false;
382
8
    if (json_object_object_get_ex(poObject_, "coordRefSys", &poCoordRefSys) &&
383
8
        eGeometryElement_ != GeometryElement::GEOMETRY)
384
0
    {
385
0
        poSRSTopLevel = OGRJSONFGReadCoordRefSys(poCoordRefSys);
386
0
        if (poSRSTopLevel)
387
0
        {
388
0
            poSRSTopLevel->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
389
0
            bSwapPlacesXYTopLevel = OGRJSONFGMustSwapXY(poSRSTopLevel.get());
390
0
        }
391
0
        else
392
0
        {
393
0
            bInvalidCRS = true;
394
0
        }
395
0
    }
396
397
    // Finalize layer definition building and create OGRLayer objects
398
8
    for (auto &oBuildContextIter : oMapBuildContext_)
399
8
    {
400
8
        const char *pszLayerName = oBuildContextIter.first.c_str();
401
8
        auto &oBuildContext = oBuildContextIter.second;
402
403
8
        FinalizeBuildContext(oBuildContext, pszLayerName, bStreamedLayer,
404
8
                             bInvalidCRS, bSwapPlacesXYTopLevel,
405
8
                             poSRSTopLevel.get());
406
8
    }
407
408
8
    return true;
409
8
}
410
411
/************************************************************************/
412
/*                OGRJSONFGReader::FinalizeBuildContext()               */
413
/************************************************************************/
414
415
void OGRJSONFGReader::FinalizeBuildContext(LayerDefnBuildContext &oBuildContext,
416
                                           const char *pszLayerName,
417
                                           bool bStreamedLayer,
418
                                           bool bInvalidCRS,
419
                                           bool bSwapPlacesXYTopLevel,
420
                                           OGRSpatialReference *poSRSTopLevel)
421
8
{
422
8
    std::unique_ptr<OGRSpatialReference> poSRSWGS84(
423
8
        OGRSpatialReference::GetWGS84SRS()->Clone());
424
8
    poSRSWGS84->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
425
426
8
    OGRSpatialReference *poSRSLayer = nullptr;
427
8
    if (oBuildContext.poCRSAtFeatureLevel)
428
0
    {
429
0
        poSRSLayer = oBuildContext.poCRSAtFeatureLevel.get();
430
0
        oBuildContext.bSwapPlacesXY = OGRJSONFGMustSwapXY(poSRSLayer);
431
0
    }
432
8
    else if (poSRSTopLevel)
433
0
    {
434
0
        poSRSLayer = poSRSTopLevel;
435
0
        oBuildContext.bSwapPlacesXY = bSwapPlacesXYTopLevel;
436
0
    }
437
8
    if (!bInvalidCRS)
438
8
    {
439
8
        if (!poSRSLayer && !oBuildContext.bHasCoordRefSysAtFeatureLevel)
440
8
        {
441
            // No coordRefSys member found anywhere ? Fallback to WGS 84
442
8
            poSRSLayer = poSRSWGS84.get();
443
8
        }
444
445
8
        if (poSRSLayer && poSRSLayer->IsSame(poSRSWGS84.get()))
446
8
        {
447
8
            oBuildContext.bLayerCRSIsWGS84 = true;
448
8
        }
449
0
        else if (poSRSLayer)
450
0
        {
451
0
            const char *pszAuthName = poSRSLayer->GetAuthorityName(nullptr);
452
0
            if (!(pszAuthName && STARTS_WITH(pszAuthName, "IAU")))
453
0
            {
454
0
                oBuildContext.poCTWGS84ToLayerCRS.reset(
455
0
                    OGRCreateCoordinateTransformation(poSRSWGS84.get(),
456
0
                                                      poSRSLayer));
457
0
            }
458
0
        }
459
8
    }
460
461
8
    std::unique_ptr<OGRJSONFGMemLayer> poMemLayer;
462
8
    std::unique_ptr<OGRJSONFGStreamedLayer> poStreamedLayer;
463
8
    OGRLayer *poLayer;
464
8
    if (bStreamedLayer)
465
3
    {
466
3
        poStreamedLayer = std::make_unique<OGRJSONFGStreamedLayer>(
467
3
            poDS_, pszLayerName, poSRSLayer, oBuildContext.eLayerGeomType);
468
3
        poLayer = poStreamedLayer.get();
469
3
    }
470
5
    else
471
5
    {
472
5
        poMemLayer = std::make_unique<OGRJSONFGMemLayer>(
473
5
            poDS_, pszLayerName, poSRSLayer, oBuildContext.eLayerGeomType);
474
5
        poLayer = poMemLayer.get();
475
5
    }
476
477
    // Note: the current strategy will not produce stable output, depending
478
    // on the order of features, if there are conflicting order / cycles.
479
    // See https://github.com/OSGeo/gdal/pull/4552 for a number of potential
480
    // resolutions if that has to be solved in the future.
481
8
    OGRFeatureDefn *poLayerDefn = poLayer->GetLayerDefn();
482
8
    auto oTemporaryUnsealer(poLayerDefn->GetTemporaryUnsealer());
483
484
8
    if (poLayer->GetLayerDefn()->GetGeomType() != wkbNone)
485
8
    {
486
8
        OGRGeoJSONWriteOptions options;
487
488
8
        json_object *poXYRes = CPL_json_object_object_get(
489
8
            poObject_, "xy_coordinate_resolution_place");
490
8
        if (poXYRes && (json_object_get_type(poXYRes) == json_type_double ||
491
0
                        json_object_get_type(poXYRes) == json_type_int))
492
0
        {
493
0
            auto poGeomFieldDefn = poLayerDefn->GetGeomFieldDefn(0);
494
0
            OGRGeomCoordinatePrecision oCoordPrec(
495
0
                poGeomFieldDefn->GetCoordinatePrecision());
496
0
            oCoordPrec.dfXYResolution = json_object_get_double(poXYRes);
497
0
            poGeomFieldDefn->SetCoordinatePrecision(oCoordPrec);
498
0
        }
499
500
8
        json_object *poZRes = CPL_json_object_object_get(
501
8
            poObject_, "z_coordinate_resolution_place");
502
8
        if (poZRes && (json_object_get_type(poZRes) == json_type_double ||
503
0
                       json_object_get_type(poZRes) == json_type_int))
504
0
        {
505
0
            auto poGeomFieldDefn = poLayerDefn->GetGeomFieldDefn(0);
506
0
            OGRGeomCoordinatePrecision oCoordPrec(
507
0
                poGeomFieldDefn->GetCoordinatePrecision());
508
0
            oCoordPrec.dfZResolution = json_object_get_double(poZRes);
509
0
            poGeomFieldDefn->SetCoordinatePrecision(oCoordPrec);
510
0
        }
511
8
    }
512
513
8
    std::set<std::string> oSetFieldNames;
514
8
    for (const auto &poFieldDefn : oBuildContext.apoFieldDefn)
515
0
        oSetFieldNames.insert(poFieldDefn->GetNameRef());
516
517
8
    auto AddTimeField =
518
8
        [poLayerDefn, &oSetFieldNames](const char *pszName, OGRFieldType eType)
519
8
    {
520
0
        if (oSetFieldNames.find(pszName) == oSetFieldNames.end())
521
0
        {
522
0
            OGRFieldDefn oFieldDefn(pszName, eType);
523
0
            poLayerDefn->AddFieldDefn(&oFieldDefn);
524
0
        }
525
0
        else
526
0
        {
527
0
            OGRFieldDefn oFieldDefn((std::string("jsonfg_") + pszName).c_str(),
528
0
                                    eType);
529
0
            poLayerDefn->AddFieldDefn(&oFieldDefn);
530
0
        }
531
0
        return poLayerDefn->GetFieldCount() - 1;
532
0
    };
533
534
8
    if (oBuildContext.bHasTimeTimestamp)
535
0
    {
536
0
        oBuildContext.nIdxFieldTime = AddTimeField("time", OFTDateTime);
537
0
    }
538
8
    else if (oBuildContext.bHasTimeDate)
539
0
    {
540
0
        oBuildContext.nIdxFieldTime = AddTimeField("time", OFTDate);
541
0
    }
542
543
8
    if (oBuildContext.bHasTimeIntervalStartDate ||
544
8
        oBuildContext.bHasTimeIntervalStartTimestamp ||
545
8
        oBuildContext.bHasTimeIntervalEndDate ||
546
8
        oBuildContext.bHasTimeIntervalEndTimestamp)
547
0
    {
548
        // Mix of Date/DateTime for start/end is not supposed to happen,
549
        // but be tolerant to that
550
0
        if (oBuildContext.bHasTimeIntervalStartTimestamp)
551
0
        {
552
0
            oBuildContext.nIdxFieldTimeStart =
553
0
                AddTimeField("time_start", OFTDateTime);
554
0
        }
555
0
        else if (oBuildContext.bHasTimeIntervalStartDate)
556
0
        {
557
0
            oBuildContext.nIdxFieldTimeStart =
558
0
                AddTimeField("time_start", OFTDate);
559
0
        }
560
0
        else if (oBuildContext.bHasTimeIntervalEndTimestamp)
561
0
        {
562
0
            oBuildContext.nIdxFieldTimeStart =
563
0
                AddTimeField("time_start", OFTDateTime);
564
0
        }
565
0
        else /* if( oBuildContext.bHasTimeIntervalEndDate ) */
566
0
        {
567
0
            oBuildContext.nIdxFieldTimeStart =
568
0
                AddTimeField("time_start", OFTDate);
569
0
        }
570
571
0
        if (oBuildContext.bHasTimeIntervalEndTimestamp)
572
0
        {
573
0
            oBuildContext.nIdxFieldTimeEnd =
574
0
                AddTimeField("time_end", OFTDateTime);
575
0
        }
576
0
        else if (oBuildContext.bHasTimeIntervalEndDate)
577
0
        {
578
0
            oBuildContext.nIdxFieldTimeEnd = AddTimeField("time_end", OFTDate);
579
0
        }
580
0
        else if (oBuildContext.bHasTimeIntervalStartTimestamp)
581
0
        {
582
0
            oBuildContext.nIdxFieldTimeEnd =
583
0
                AddTimeField("time_end", OFTDateTime);
584
0
        }
585
0
        else /* if( oBuildContext.bHasTimeIntervalStartDate ) */
586
0
        {
587
0
            oBuildContext.nIdxFieldTimeEnd = AddTimeField("time_end", OFTDate);
588
0
        }
589
0
    }
590
591
8
    const auto sortedFields = oBuildContext.dag.getTopologicalOrdering();
592
8
    CPLAssert(sortedFields.size() == oBuildContext.apoFieldDefn.size());
593
8
    for (int idx : sortedFields)
594
0
    {
595
0
        poLayerDefn->AddFieldDefn(oBuildContext.apoFieldDefn[idx].get());
596
0
    }
597
598
8
    if (!oBuildContext.bFeatureLevelIdAsFID)
599
6
    {
600
6
        const int idx = poLayerDefn->GetFieldIndexCaseSensitive("id");
601
6
        if (idx >= 0)
602
0
        {
603
0
            OGRFieldDefn *poFDefn = poLayerDefn->GetFieldDefn(idx);
604
0
            if (poFDefn->GetType() == OFTInteger ||
605
0
                poFDefn->GetType() == OFTInteger64)
606
0
            {
607
0
                if (poStreamedLayer)
608
0
                {
609
0
                    poStreamedLayer->SetFIDColumn(
610
0
                        poLayerDefn->GetFieldDefn(idx)->GetNameRef());
611
0
                }
612
0
                else
613
0
                {
614
0
                    poMemLayer->SetFIDColumn(
615
0
                        poLayerDefn->GetFieldDefn(idx)->GetNameRef());
616
0
                }
617
0
            }
618
0
        }
619
6
    }
620
621
8
    if (oBuildContext.bNeedFID64)
622
0
        poLayer->SetMetadataItem(OLMD_FID64, "YES");
623
624
8
    if (poStreamedLayer)
625
3
    {
626
3
        poStreamedLayer->SetFeatureCount(oBuildContext.nFeatureCount);
627
3
        oBuildContext.poStreamedLayer =
628
3
            poDS_->AddLayer(std::move(poStreamedLayer));
629
3
    }
630
5
    else
631
5
    {
632
5
        oBuildContext.poMemLayer = poDS_->AddLayer(std::move(poMemLayer));
633
5
    }
634
8
}
635
636
/************************************************************************/
637
/*            OGRJSONFGReader::GetLayerNameForFeature()                 */
638
/************************************************************************/
639
640
const char *OGRJSONFGReader::GetLayerNameForFeature(json_object *poObj) const
641
265
{
642
265
    const char *pszName = osDefaultLayerName_.c_str();
643
265
    json_object *poName = CPL_json_object_object_get(poObj, "featureType");
644
    // The spec allows an array of strings, but we don't support that
645
265
    if (poName != nullptr && json_object_get_type(poName) == json_type_string)
646
109
    {
647
109
        pszName = json_object_get_string(poName);
648
109
    }
649
265
    return pszName;
650
265
}
651
652
/************************************************************************/
653
/*                     OGRJSONFGGetOGRGeometryType()                    */
654
/************************************************************************/
655
656
static OGRwkbGeometryType OGRJSONFGGetOGRGeometryType(json_object *poObj)
657
0
{
658
0
    const auto eType = OGRGeoJSONGetOGRGeometryType(poObj);
659
0
    if (eType != wkbUnknown)
660
0
        return eType;
661
662
0
    json_object *poObjType = CPL_json_object_object_get(poObj, "type");
663
0
    const char *pszType = json_object_get_string(poObjType);
664
0
    if (!pszType)
665
0
        return wkbNone;
666
667
0
    if (strcmp(pszType, "Polyhedron") == 0)
668
0
    {
669
0
        return wkbPolyhedralSurfaceZ;
670
0
    }
671
0
    else if (strcmp(pszType, "Prism") == 0)
672
0
    {
673
0
        auto poBase = CPL_json_object_object_get(poObj, "base");
674
0
        if (!poBase || json_object_get_type(poBase) != json_type_object)
675
0
        {
676
0
            return wkbNone;
677
0
        }
678
679
0
        const auto eBaseGeomType = OGRGeoJSONGetOGRGeometryType(poBase);
680
0
        if (eBaseGeomType == wkbPoint)
681
0
        {
682
0
            return wkbLineString25D;
683
0
        }
684
0
        else if (eBaseGeomType == wkbLineString)
685
0
        {
686
0
            return wkbMultiPolygon25D;
687
0
        }
688
0
        else if (eBaseGeomType == wkbPolygon)
689
0
        {
690
0
            return wkbPolyhedralSurfaceZ;
691
0
        }
692
0
    }
693
0
    return wkbNone;
694
0
}
695
696
/************************************************************************/
697
/*                   OGRJSONFGCreateNonGeoJSONGeometry()                */
698
/************************************************************************/
699
700
static std::unique_ptr<OGRGeometry>
701
OGRJSONFGCreateNonGeoJSONGeometry(json_object *poObj, bool bWarn)
702
0
{
703
0
    json_object *poObjType = CPL_json_object_object_get(poObj, "type");
704
0
    const char *pszType = json_object_get_string(poObjType);
705
0
    if (!pszType)
706
0
        return nullptr;
707
708
0
    if (strcmp(pszType, "Polyhedron") == 0)
709
0
    {
710
0
        auto poCoordinates = CPL_json_object_object_get(poObj, "coordinates");
711
0
        if (!poCoordinates ||
712
0
            json_object_get_type(poCoordinates) != json_type_array)
713
0
        {
714
0
            CPLError(CE_Failure, CPLE_AppDefined,
715
0
                     "Missing or invalid coordinates in Polyhedron");
716
0
            return nullptr;
717
0
        }
718
0
        if (json_object_array_length(poCoordinates) != 1)
719
0
        {
720
0
            if (bWarn)
721
0
            {
722
0
                CPLError(CE_Warning, CPLE_AppDefined,
723
0
                         "Polyhedron with inner shells not supported");
724
0
            }
725
0
            return nullptr;
726
0
        }
727
0
        auto poJOuterShell = json_object_array_get_idx(poCoordinates, 0);
728
0
        auto poGeom = std::make_unique<OGRPolyhedralSurface>();
729
0
        const auto nPolys = json_object_array_length(poJOuterShell);
730
0
        for (auto i = decltype(nPolys){0}; i < nPolys; ++i)
731
0
        {
732
0
            auto poJPoly = json_object_array_get_idx(poJOuterShell, i);
733
0
            if (!poJPoly)
734
0
                return nullptr;
735
0
            auto poPoly = OGRGeoJSONReadPolygon(poJPoly, /*bRaw = */ true);
736
0
            if (!poPoly)
737
0
                return nullptr;
738
0
            if (poGeom->addGeometryDirectly(poPoly) != OGRERR_NONE)
739
0
                return nullptr;
740
0
        }
741
0
        if (nPolys == 0)
742
0
            poGeom->set3D(true);
743
744
0
        return poGeom;
745
0
    }
746
0
    else if (strcmp(pszType, "Prism") == 0)
747
0
    {
748
0
        auto poBase = CPL_json_object_object_get(poObj, "base");
749
0
        if (!poBase || json_object_get_type(poBase) != json_type_object)
750
0
        {
751
0
            CPLError(CE_Failure, CPLE_AppDefined,
752
0
                     "Missing or invalid base in Prism");
753
0
            return nullptr;
754
0
        }
755
756
0
        json_object *poLower = CPL_json_object_object_get(poObj, "lower");
757
0
        const double dfLower = poLower ? json_object_get_double(poLower) : 0.0;
758
0
        json_object *poUpper = CPL_json_object_object_get(poObj, "upper");
759
0
        const double dfUpper = poUpper ? json_object_get_double(poUpper) : 0.0;
760
761
0
        auto poBaseGeom =
762
0
            std::unique_ptr<OGRGeometry>(OGRGeoJSONReadGeometry(poBase));
763
0
        if (!poBaseGeom)
764
0
            return nullptr;
765
0
        if (poBaseGeom->getGeometryType() == wkbPoint)
766
0
        {
767
0
            const auto poPoint = poBaseGeom.get()->toPoint();
768
0
            auto poGeom = std::make_unique<OGRLineString>();
769
0
            poGeom->addPoint(poPoint->getX(), poPoint->getY(), dfLower);
770
0
            poGeom->addPoint(poPoint->getX(), poPoint->getY(), dfUpper);
771
0
            return poGeom;
772
0
        }
773
0
        else if (poBaseGeom->getGeometryType() == wkbLineString)
774
0
        {
775
0
            const auto poLS = poBaseGeom.get()->toLineString();
776
0
            auto poGeom = std::make_unique<OGRMultiPolygon>();
777
0
            for (int i = 0; i < poLS->getNumPoints() - 1; ++i)
778
0
            {
779
0
                auto poPoly = new OGRPolygon();
780
0
                auto poRing = new OGRLinearRing();
781
0
                poRing->addPoint(poLS->getX(i), poLS->getY(i), dfLower);
782
0
                poRing->addPoint(poLS->getX(i + 1), poLS->getY(i + 1), dfLower);
783
0
                poRing->addPoint(poLS->getX(i + 1), poLS->getY(i + 1), dfUpper);
784
0
                poRing->addPoint(poLS->getX(i), poLS->getY(i), dfUpper);
785
0
                poRing->addPoint(poLS->getX(i), poLS->getY(i), dfLower);
786
0
                poPoly->addRingDirectly(poRing);
787
0
                poGeom->addGeometryDirectly(poPoly);
788
0
            }
789
0
            return poGeom;
790
0
        }
791
0
        else if (poBaseGeom->getGeometryType() == wkbPolygon)
792
0
        {
793
0
            const auto poBasePoly = poBaseGeom.get()->toPolygon();
794
0
            if (poBasePoly->getNumInteriorRings() > 0)
795
0
            {
796
0
                if (bWarn)
797
0
                {
798
0
                    CPLError(CE_Warning, CPLE_AppDefined,
799
0
                             "Polygon with holes is not supported as the base "
800
0
                             "for Prism");
801
0
                }
802
0
                return nullptr;
803
0
            }
804
0
            const auto poLS = poBasePoly->getExteriorRing();
805
0
            if (poLS == nullptr)
806
0
            {
807
0
                return nullptr;
808
0
            }
809
0
            auto poGeom = std::make_unique<OGRPolyhedralSurface>();
810
            // Build lower face
811
0
            {
812
0
                auto poPoly = new OGRPolygon();
813
0
                auto poRing = new OGRLinearRing();
814
0
                for (int i = 0; i < poLS->getNumPoints(); ++i)
815
0
                {
816
0
                    poRing->addPoint(poLS->getX(i), poLS->getY(i), dfLower);
817
0
                }
818
0
                poPoly->addRingDirectly(poRing);
819
0
                poGeom->addGeometryDirectly(poPoly);
820
0
            }
821
            // Build side faces
822
0
            for (int i = 0; i < poLS->getNumPoints() - 1; ++i)
823
0
            {
824
0
                auto poPoly = new OGRPolygon();
825
0
                auto poRing = new OGRLinearRing();
826
0
                poRing->addPoint(poLS->getX(i), poLS->getY(i), dfLower);
827
0
                poRing->addPoint(poLS->getX(i + 1), poLS->getY(i + 1), dfLower);
828
0
                poRing->addPoint(poLS->getX(i + 1), poLS->getY(i + 1), dfUpper);
829
0
                poRing->addPoint(poLS->getX(i), poLS->getY(i), dfUpper);
830
0
                poRing->addPoint(poLS->getX(i), poLS->getY(i), dfLower);
831
0
                poPoly->addRingDirectly(poRing);
832
0
                poGeom->addGeometryDirectly(poPoly);
833
0
            }
834
            // Build upper face
835
0
            {
836
0
                auto poPoly = new OGRPolygon();
837
0
                auto poRing = new OGRLinearRing();
838
0
                for (int i = 0; i < poLS->getNumPoints(); ++i)
839
0
                {
840
0
                    poRing->addPoint(poLS->getX(i), poLS->getY(i), dfUpper);
841
0
                }
842
0
                poPoly->addRingDirectly(poRing);
843
0
                poGeom->addGeometryDirectly(poPoly);
844
0
            }
845
0
            return poGeom;
846
0
        }
847
0
        else
848
0
        {
849
0
            if (bWarn)
850
0
            {
851
0
                CPLError(CE_Warning, CPLE_AppDefined,
852
0
                         "Unsupported base geometry type for Prism");
853
0
            }
854
0
            return nullptr;
855
0
        }
856
0
    }
857
0
    else
858
0
    {
859
0
        if (bWarn)
860
0
        {
861
0
            CPLError(CE_Warning, CPLE_AppDefined, "Unhandled place.type = %s",
862
0
                     pszType);
863
0
        }
864
0
        return nullptr;
865
0
    }
866
0
}
867
868
/************************************************************************/
869
/*            OGRJSONFGReader::GenerateLayerDefnFromFeature()           */
870
/************************************************************************/
871
872
bool OGRJSONFGReader::GenerateLayerDefnFromFeature(json_object *poObj)
873
255
{
874
255
    const GeoJSONObject::Type objType = OGRGeoJSONGetType(poObj);
875
255
    if (objType != GeoJSONObject::eFeature)
876
0
    {
877
0
        CPLError(CE_Failure, CPLE_AppDefined, "Did not get a Feature");
878
0
        return false;
879
0
    }
880
881
255
    const char *psLayerName = GetLayerNameForFeature(poObj);
882
883
255
    auto oBuildContextIter = oMapBuildContext_.find(psLayerName);
884
255
    if (oBuildContextIter == oMapBuildContext_.end())
885
120
    {
886
120
        LayerDefnBuildContext oContext;
887
120
        oMapBuildContext_[psLayerName] = std::move(oContext);
888
120
        oBuildContextIter = oMapBuildContext_.find(psLayerName);
889
120
    }
890
255
    LayerDefnBuildContext *poContext = &(oBuildContextIter->second);
891
892
255
    ++poContext->nFeatureCount;
893
894
255
    json_object *poCoordRefSys = nullptr;
895
255
    json_object *poPlace = nullptr;
896
255
    if (eGeometryElement_ != GeometryElement::GEOMETRY)
897
255
    {
898
255
        poPlace = CPL_json_object_object_get(poObj, "place");
899
255
        if (poPlace && json_object_get_type(poPlace) == json_type_object)
900
0
        {
901
0
            poCoordRefSys = CPL_json_object_object_get(poPlace, "coordRefSys");
902
0
        }
903
255
        if (!poCoordRefSys)
904
255
            poCoordRefSys = CPL_json_object_object_get(poObj, "coordRefSys");
905
906
255
        if (poCoordRefSys)
907
0
        {
908
0
            std::string osVal = json_object_to_json_string(poCoordRefSys);
909
0
            if (!poContext->bHasCoordRefSysAtFeatureLevel)
910
0
            {
911
0
                poContext->bHasCoordRefSysAtFeatureLevel = true;
912
0
                poContext->osCoordRefSysAtFeatureLevel = std::move(osVal);
913
0
                poContext->poCRSAtFeatureLevel =
914
0
                    OGRJSONFGReadCoordRefSys(poCoordRefSys);
915
0
                if (poContext->poCRSAtFeatureLevel)
916
0
                {
917
0
                    poContext->poCRSAtFeatureLevel->SetAxisMappingStrategy(
918
0
                        OAMS_TRADITIONAL_GIS_ORDER);
919
0
                }
920
0
            }
921
0
            else if (poContext->osCoordRefSysAtFeatureLevel != osVal)
922
0
            {
923
0
                poContext->osCoordRefSysAtFeatureLevel.clear();
924
0
                poContext->poCRSAtFeatureLevel.reset();
925
0
            }
926
0
        }
927
255
    }
928
929
    /* -------------------------------------------------------------------- */
930
    /*      Deal with place / geometry                                      */
931
    /* -------------------------------------------------------------------- */
932
933
255
    if (poContext->bDetectLayerGeomType)
934
237
    {
935
237
        bool bFallbackToGeometry =
936
237
            (eGeometryElement_ != GeometryElement::PLACE);
937
237
        if (poPlace && json_object_get_type(poPlace) == json_type_object)
938
0
        {
939
0
            const auto eType = OGRJSONFGGetOGRGeometryType(poPlace);
940
0
            if (eType != wkbNone)
941
0
            {
942
0
                bFallbackToGeometry = false;
943
0
                poContext->bDetectLayerGeomType = OGRGeoJSONUpdateLayerGeomType(
944
0
                    poContext->bFirstGeometry, eType,
945
0
                    poContext->eLayerGeomType);
946
0
            }
947
0
        }
948
949
237
        if (bFallbackToGeometry)
950
237
        {
951
237
            json_object *poGeomObj =
952
237
                CPL_json_object_object_get(poObj, "geometry");
953
237
            if (poGeomObj &&
954
237
                json_object_get_type(poGeomObj) == json_type_object)
955
46
            {
956
46
                const auto eType = OGRGeoJSONGetOGRGeometryType(poGeomObj);
957
46
                poContext->bDetectLayerGeomType = OGRGeoJSONUpdateLayerGeomType(
958
46
                    poContext->bFirstGeometry, eType,
959
46
                    poContext->eLayerGeomType);
960
46
            }
961
237
        }
962
237
    }
963
964
    /* -------------------------------------------------------------------- */
965
    /*      Deal with time                                                  */
966
    /* -------------------------------------------------------------------- */
967
255
    json_object *poTime = CPL_json_object_object_get(poObj, "time");
968
255
    if (poTime)
969
2
    {
970
2
        json_object *poDate = CPL_json_object_object_get(poTime, "date");
971
2
        if (poDate && json_object_get_type(poDate) == json_type_string)
972
0
            poContext->bHasTimeDate = true;
973
974
2
        json_object *poTimestamp =
975
2
            CPL_json_object_object_get(poTime, "timestamp");
976
2
        if (poTimestamp &&
977
2
            json_object_get_type(poTimestamp) == json_type_string)
978
0
            poContext->bHasTimeTimestamp = true;
979
980
2
        json_object *poInterval =
981
2
            CPL_json_object_object_get(poTime, "interval");
982
2
        if (poInterval && json_object_get_type(poInterval) == json_type_array &&
983
2
            json_object_array_length(poInterval) == 2)
984
0
        {
985
0
            json_object *poStart = json_object_array_get_idx(poInterval, 0);
986
0
            if (poStart && json_object_get_type(poStart) == json_type_string)
987
0
            {
988
0
                const char *pszStart = json_object_get_string(poStart);
989
0
                if (strchr(pszStart, 'Z'))
990
0
                    poContext->bHasTimeIntervalStartTimestamp = true;
991
0
                else if (strcmp(pszStart, "..") != 0)
992
0
                    poContext->bHasTimeIntervalStartDate = true;
993
0
            }
994
995
0
            json_object *poEnd = json_object_array_get_idx(poInterval, 1);
996
0
            if (poEnd && json_object_get_type(poEnd) == json_type_string)
997
0
            {
998
0
                const char *pszEnd = json_object_get_string(poEnd);
999
0
                if (strchr(pszEnd, 'Z'))
1000
0
                    poContext->bHasTimeIntervalEndTimestamp = true;
1001
0
                else if (strcmp(pszEnd, "..") != 0)
1002
0
                    poContext->bHasTimeIntervalEndDate = true;
1003
0
            }
1004
0
        }
1005
2
    }
1006
1007
    /* -------------------------------------------------------------------- */
1008
    /*      Read collection of properties.                                  */
1009
    /* -------------------------------------------------------------------- */
1010
255
    json_object *poObjProps = CPL_json_object_object_get(poObj, "properties");
1011
1012
255
    int nPrevFieldIdx = -1;
1013
1014
    // First deal with id, either at top level or in properties["id"]
1015
255
    OGRGeoJSONGenerateFeatureDefnDealWithID(
1016
255
        poObj, poObjProps, nPrevFieldIdx, poContext->oMapFieldNameToIdx,
1017
255
        poContext->apoFieldDefn, poContext->dag,
1018
255
        poContext->bFeatureLevelIdAsFID, poContext->bFeatureLevelIdAsAttribute,
1019
255
        poContext->bNeedFID64);
1020
1021
255
    if (nullptr != poObjProps &&
1022
255
        json_object_get_type(poObjProps) == json_type_object)
1023
134
    {
1024
134
        json_object_iter it;
1025
134
        it.key = nullptr;
1026
134
        it.val = nullptr;
1027
134
        it.entry = nullptr;
1028
134
        std::vector<int> anCurFieldIndices;
1029
134
        json_object_object_foreachC(poObjProps, it)
1030
291
        {
1031
291
            anCurFieldIndices.clear();
1032
291
            OGRGeoJSONReaderAddOrUpdateField(
1033
291
                anCurFieldIndices, poContext->oMapFieldNameToIdx,
1034
291
                poContext->apoFieldDefn, it.key, it.val,
1035
291
                bFlattenNestedAttributes_, chNestedAttributeSeparator_,
1036
291
                bArrayAsString_, bDateAsString_,
1037
291
                poContext->aoSetUndeterminedTypeFields);
1038
291
            for (int idx : anCurFieldIndices)
1039
291
            {
1040
291
                poContext->dag.addNode(
1041
291
                    idx, poContext->apoFieldDefn[idx]->GetNameRef());
1042
291
                if (nPrevFieldIdx != -1)
1043
200
                {
1044
200
                    poContext->dag.addEdge(nPrevFieldIdx, idx);
1045
200
                }
1046
291
                nPrevFieldIdx = idx;
1047
291
            }
1048
291
        }
1049
134
    }
1050
1051
255
    return true;
1052
255
}
1053
1054
/************************************************************************/
1055
/*                  OGRJSONFGReader::ReadFeature()                      */
1056
/************************************************************************/
1057
1058
std::unique_ptr<OGRFeature>
1059
OGRJSONFGReader::ReadFeature(json_object *poObj, const char *pszRequestedLayer,
1060
                             OGRJSONFGMemLayer **pOutMemLayer,
1061
                             OGRJSONFGStreamedLayer **pOutStreamedLayer)
1062
10
{
1063
10
    const char *pszLayerName = GetLayerNameForFeature(poObj);
1064
10
    if (pszRequestedLayer && strcmp(pszLayerName, pszRequestedLayer) != 0)
1065
0
        return nullptr;
1066
1067
10
    auto oBuildContextIter = oMapBuildContext_.find(pszLayerName);
1068
10
    CPLAssert(oBuildContextIter != oMapBuildContext_.end());
1069
10
    auto &oBuildContext = oBuildContextIter->second;
1070
10
    OGRLayer *poLayer =
1071
10
        oBuildContext.poStreamedLayer
1072
10
            ? static_cast<OGRLayer *>(oBuildContext.poStreamedLayer)
1073
10
            : static_cast<OGRLayer *>(oBuildContext.poMemLayer);
1074
1075
10
    if (pOutMemLayer)
1076
5
        *pOutMemLayer = oBuildContext.poMemLayer;
1077
5
    else if (pOutStreamedLayer)
1078
5
        *pOutStreamedLayer = oBuildContext.poStreamedLayer;
1079
1080
10
    OGRFeatureDefn *poFDefn = poLayer->GetLayerDefn();
1081
10
    auto poFeature = std::make_unique<OGRFeature>(poFDefn);
1082
1083
    /* -------------------------------------------------------------------- */
1084
    /*      Translate GeoJSON "properties" object to feature attributes.    */
1085
    /* -------------------------------------------------------------------- */
1086
1087
10
    json_object *poObjProps = CPL_json_object_object_get(poObj, "properties");
1088
10
    if (nullptr != poObjProps &&
1089
10
        json_object_get_type(poObjProps) == json_type_object)
1090
0
    {
1091
0
        json_object_iter it;
1092
0
        it.key = nullptr;
1093
0
        it.val = nullptr;
1094
0
        it.entry = nullptr;
1095
0
        json_object_object_foreachC(poObjProps, it)
1096
0
        {
1097
0
            const int nField = poFDefn->GetFieldIndexCaseSensitive(it.key);
1098
0
            if (nField < 0 &&
1099
0
                !(bFlattenNestedAttributes_ && it.val != nullptr &&
1100
0
                  json_object_get_type(it.val) == json_type_object))
1101
0
            {
1102
0
                CPLDebug("JSONFG", "Cannot find field %s", it.key);
1103
0
            }
1104
0
            else
1105
0
            {
1106
0
                OGRGeoJSONReaderSetField(
1107
0
                    poLayer, poFeature.get(), nField, it.key, it.val,
1108
0
                    bFlattenNestedAttributes_, chNestedAttributeSeparator_);
1109
0
            }
1110
0
        }
1111
0
    }
1112
1113
    /* -------------------------------------------------------------------- */
1114
    /*      Try to use feature-level ID if available                        */
1115
    /*      and of integral type. Otherwise, leave unset (-1) then index    */
1116
    /*      in features sequence will be used as FID.                       */
1117
    /* -------------------------------------------------------------------- */
1118
10
    json_object *poObjId = CPL_json_object_object_get(poObj, "id");
1119
10
    if (nullptr != poObjId && oBuildContext.bFeatureLevelIdAsFID)
1120
3
    {
1121
3
        poFeature->SetFID(static_cast<GIntBig>(json_object_get_int64(poObjId)));
1122
3
    }
1123
1124
    /* -------------------------------------------------------------------- */
1125
    /*      Handle the case where the special id is in a regular field.     */
1126
    /* -------------------------------------------------------------------- */
1127
7
    else if (nullptr != poObjId)
1128
0
    {
1129
0
        const int nIdx = poFDefn->GetFieldIndexCaseSensitive("id");
1130
0
        if (nIdx >= 0 && !poFeature->IsFieldSet(nIdx))
1131
0
        {
1132
0
            poFeature->SetField(nIdx, json_object_get_string(poObjId));
1133
0
        }
1134
0
    }
1135
1136
    /* -------------------------------------------------------------------- */
1137
    /*      Deal with time                                                  */
1138
    /* -------------------------------------------------------------------- */
1139
10
    json_object *poTime = CPL_json_object_object_get(poObj, "time");
1140
10
    if (poTime)
1141
0
    {
1142
0
        json_object *poDate = CPL_json_object_object_get(poTime, "date");
1143
0
        if (poDate && json_object_get_type(poDate) == json_type_string)
1144
0
        {
1145
0
            poFeature->SetField(oBuildContext.nIdxFieldTime,
1146
0
                                json_object_get_string(poDate));
1147
0
        }
1148
1149
0
        json_object *poTimestamp =
1150
0
            CPL_json_object_object_get(poTime, "timestamp");
1151
0
        if (poTimestamp &&
1152
0
            json_object_get_type(poTimestamp) == json_type_string)
1153
0
        {
1154
0
            poFeature->SetField(oBuildContext.nIdxFieldTime,
1155
0
                                json_object_get_string(poTimestamp));
1156
0
        }
1157
1158
0
        json_object *poInterval =
1159
0
            CPL_json_object_object_get(poTime, "interval");
1160
0
        if (poInterval && json_object_get_type(poInterval) == json_type_array &&
1161
0
            json_object_array_length(poInterval) == 2)
1162
0
        {
1163
0
            json_object *poStart = json_object_array_get_idx(poInterval, 0);
1164
0
            if (poStart && json_object_get_type(poStart) == json_type_string)
1165
0
            {
1166
0
                const char *pszStart = json_object_get_string(poStart);
1167
0
                if (strcmp(pszStart, "..") != 0)
1168
0
                    poFeature->SetField(oBuildContext.nIdxFieldTimeStart,
1169
0
                                        pszStart);
1170
0
            }
1171
1172
0
            json_object *poEnd = json_object_array_get_idx(poInterval, 1);
1173
0
            if (poEnd && json_object_get_type(poEnd) == json_type_string)
1174
0
            {
1175
0
                const char *pszEnd = json_object_get_string(poEnd);
1176
0
                if (strcmp(pszEnd, "..") != 0)
1177
0
                    poFeature->SetField(oBuildContext.nIdxFieldTimeEnd, pszEnd);
1178
0
            }
1179
0
        }
1180
0
    }
1181
1182
    /* -------------------------------------------------------------------- */
1183
    /*      Translate "place" (and fallback to "geometry") sub-object       */
1184
    /* -------------------------------------------------------------------- */
1185
10
    json_object *poPlace = nullptr;
1186
10
    bool bFallbackToGeometry = (eGeometryElement_ != GeometryElement::PLACE);
1187
1188
10
    if (eGeometryElement_ != GeometryElement::GEOMETRY)
1189
10
    {
1190
10
        poPlace = CPL_json_object_object_get(poObj, "place");
1191
10
    }
1192
10
    if (poPlace && json_object_get_type(poPlace) == json_type_object)
1193
0
    {
1194
0
        json_object *poCoordRefSys = nullptr;
1195
0
        if (!oBuildContext.poCRSAtFeatureLevel)
1196
0
        {
1197
0
            poCoordRefSys = CPL_json_object_object_get(poPlace, "coordRefSys");
1198
0
            if (!poCoordRefSys)
1199
0
            {
1200
0
                poCoordRefSys =
1201
0
                    CPL_json_object_object_get(poObj, "coordRefSys");
1202
0
            }
1203
0
        }
1204
1205
0
        std::unique_ptr<OGRGeometry> poGeometry;
1206
0
        json_object *poObjType = CPL_json_object_object_get(poPlace, "type");
1207
0
        const char *pszType = json_object_get_string(poObjType);
1208
0
        if (pszType && (strcmp(pszType, "Polyhedron") == 0 ||
1209
0
                        strcmp(pszType, "Prism") == 0))
1210
0
        {
1211
0
            poGeometry =
1212
0
                OGRJSONFGCreateNonGeoJSONGeometry(poPlace, /* bWarn=*/false);
1213
0
        }
1214
0
        else
1215
0
        {
1216
0
            poGeometry.reset(OGRGeoJSONReadGeometry(poPlace, nullptr));
1217
0
        }
1218
0
        if (poGeometry)
1219
0
            bFallbackToGeometry = false;
1220
1221
0
        auto poLayerSRS = poLayer->GetSpatialRef();
1222
0
        if (!poGeometry)
1223
0
        {
1224
            // nothing to do
1225
0
        }
1226
0
        else if (poCoordRefSys)
1227
0
        {
1228
0
            auto poFeatureCRS = OGRJSONFGReadCoordRefSys(poCoordRefSys);
1229
0
            if (poFeatureCRS)
1230
0
            {
1231
0
                poFeatureCRS->SetAxisMappingStrategy(
1232
0
                    OAMS_TRADITIONAL_GIS_ORDER);
1233
0
                const bool bFeatureCRSNeedSwapXY =
1234
0
                    OGRJSONFGMustSwapXY(poFeatureCRS.get());
1235
0
                if (poLayerSRS)
1236
0
                {
1237
                    // Both feature and layer-level CRS. Reproject if needed
1238
0
                    if (!poFeatureCRS->IsSame(poLayerSRS))
1239
0
                    {
1240
0
                        auto poCT =
1241
0
                            std::unique_ptr<OGRCoordinateTransformation>(
1242
0
                                OGRCreateCoordinateTransformation(
1243
0
                                    poFeatureCRS.get(), poLayerSRS));
1244
0
                        if (poCT)
1245
0
                        {
1246
0
                            if (bFeatureCRSNeedSwapXY)
1247
0
                                poGeometry->swapXY();
1248
0
                            if (poGeometry->transform(poCT.get()) ==
1249
0
                                OGRERR_NONE)
1250
0
                            {
1251
0
                                poGeometry->assignSpatialReference(poLayerSRS);
1252
0
                                poFeature->SetGeometryDirectly(
1253
0
                                    poGeometry.release());
1254
0
                            }
1255
0
                        }
1256
0
                    }
1257
0
                    else
1258
0
                    {
1259
0
                        poGeometry->assignSpatialReference(poLayerSRS);
1260
0
                        if (oBuildContext.bSwapPlacesXY)
1261
0
                            poGeometry->swapXY();
1262
0
                        poFeature->SetGeometryDirectly(poGeometry.release());
1263
0
                    }
1264
0
                }
1265
0
                else
1266
0
                {
1267
                    // No layer-level CRS
1268
0
                    auto poFeatureCRSBorrowed = poFeatureCRS.release();
1269
0
                    poGeometry->assignSpatialReference(poFeatureCRSBorrowed);
1270
0
                    poFeatureCRSBorrowed->Release();
1271
0
                    if (bFeatureCRSNeedSwapXY)
1272
0
                        poGeometry->swapXY();
1273
0
                    poFeature->SetGeometryDirectly(poGeometry.release());
1274
0
                }
1275
0
            }
1276
0
        }
1277
0
        else
1278
0
        {
1279
0
            poGeometry->assignSpatialReference(poLayerSRS);
1280
0
            if (oBuildContext.bSwapPlacesXY)
1281
0
                poGeometry->swapXY();
1282
0
            poFeature->SetGeometryDirectly(poGeometry.release());
1283
0
        }
1284
0
    }
1285
1286
10
    if (bFallbackToGeometry &&
1287
10
        (oBuildContext.poCTWGS84ToLayerCRS || oBuildContext.bLayerCRSIsWGS84))
1288
10
    {
1289
10
        json_object *poGeomObj = CPL_json_object_object_get(poObj, "geometry");
1290
10
        if (nullptr != poGeomObj)
1291
0
        {
1292
0
            auto poGeometry = std::unique_ptr<OGRGeometry>(
1293
0
                OGRGeoJSONReadGeometry(poGeomObj, nullptr));
1294
0
            if (poGeometry)
1295
0
            {
1296
0
                if (oBuildContext.poCTWGS84ToLayerCRS)
1297
0
                {
1298
0
                    if (poGeometry->transform(
1299
0
                            oBuildContext.poCTWGS84ToLayerCRS.get()) ==
1300
0
                        OGRERR_NONE)
1301
0
                    {
1302
0
                        poGeometry->assignSpatialReference(
1303
0
                            poLayer->GetSpatialRef());
1304
0
                        poFeature->SetGeometryDirectly(poGeometry.release());
1305
0
                    }
1306
0
                }
1307
0
                else /* if (oBuildContext.bLayerCRSIsWGS84) */
1308
0
                {
1309
0
                    poGeometry->assignSpatialReference(
1310
0
                        poLayer->GetSpatialRef());
1311
0
                    poFeature->SetGeometryDirectly(poGeometry.release());
1312
0
                }
1313
0
            }
1314
0
        }
1315
10
    }
1316
1317
10
    return poFeature;
1318
10
}