Coverage Report

Created: 2026-05-16 08:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/jsonfg/ogr_jsonfg.h
Line
Count
Source
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
#ifndef OGR_JSONFG_H_INCLUDED
14
#define OGR_JSONFG_H_INCLUDED
15
16
#include "cpl_vsi_virtual.h"
17
18
#include "gdal_priv.h"
19
#include "ogrsf_frmts.h"
20
#include "ogrgeojsonutils.h"
21
#include "ogrgeojsonwriter.h"
22
#include "ogrjsoncollectionstreamingparser.h"
23
#include "memdataset.h"
24
#include "directedacyclicgraph.hpp"
25
26
#include <map>
27
#include <set>
28
#include <utility>
29
30
/************************************************************************/
31
/*                          OGRJSONFGMemLayer                           */
32
/************************************************************************/
33
34
/** Layer with all features ingested into memory. */
35
class OGRJSONFGMemLayer final : public OGRMemLayer
36
{
37
  public:
38
    OGRJSONFGMemLayer(GDALDataset *poDS, const char *pszName,
39
                      OGRSpatialReference *poSRS, OGRwkbGeometryType eGType);
40
    ~OGRJSONFGMemLayer() override;
41
42
    const char *GetFIDColumn() const override
43
4
    {
44
4
        return osFIDColumn_.c_str();
45
4
    }
46
47
    void SetFIDColumn(const char *pszName)
48
0
    {
49
0
        osFIDColumn_ = pszName;
50
0
    }
51
52
    void AddFeature(std::unique_ptr<OGRFeature> poFeature);
53
54
    GDALDataset *GetDataset() override
55
0
    {
56
0
        return m_poDS;
57
0
    }
58
59
  private:
60
    GDALDataset *m_poDS = nullptr;
61
    std::string osFIDColumn_{};
62
    bool bOriginalIdModified_ = false;
63
64
    CPL_DISALLOW_COPY_ASSIGN(OGRJSONFGMemLayer)
65
};
66
67
/************************************************************************/
68
/*                        OGRJSONFGStreamedLayer                        */
69
/************************************************************************/
70
71
class OGRJSONFGStreamingParser;
72
73
/** Layer with features being acquired progressively through a streaming
74
 parser.
75
76
 Only applies for FeatureCollection read through a file
77
*/
78
class OGRJSONFGStreamedLayer final
79
    : public OGRLayer,
80
      public OGRGetNextFeatureThroughRaw<OGRJSONFGStreamedLayer>
81
{
82
  public:
83
    OGRJSONFGStreamedLayer(GDALDataset *poDS, const char *pszName,
84
                           OGRSpatialReference *poSRS,
85
                           OGRwkbGeometryType eGType);
86
    ~OGRJSONFGStreamedLayer() override;
87
88
    // BEGIN specific public API
89
90
    //! Set the FID column name
91
    void SetFIDColumn(const char *pszName)
92
0
    {
93
0
        osFIDColumn_ = pszName;
94
0
    }
95
96
    //! Set the total feature count
97
    void SetFeatureCount(GIntBig nCount)
98
12
    {
99
12
        nFeatureCount_ = nCount;
100
12
    }
101
102
    /** Set the file handle.
103
104
     Must be called before GetNextFeature() is called
105
     */
106
    void SetFile(VSIVirtualHandleUniquePtr &&poFile);
107
108
    /** Set the streaming parser
109
110
     Must be called before GetNextFeature() is called
111
     */
112
    void SetStreamingParser(
113
        std::unique_ptr<OGRJSONFGStreamingParser> &&poStreamingParser);
114
115
    // END specific public API
116
117
    const char *GetFIDColumn() const override
118
12
    {
119
12
        return osFIDColumn_.c_str();
120
12
    }
121
122
    const OGRFeatureDefn *GetLayerDefn() const override
123
120
    {
124
120
        return poFeatureDefn_;
125
120
    }
126
127
    int TestCapability(const char *pszCap) const override;
128
129
    GIntBig GetFeatureCount(int bForce) override;
130
131
    void ResetReading() override;
132
133
    DEFINE_GET_NEXT_FEATURE_THROUGH_RAW(OGRJSONFGStreamedLayer)
134
135
    GDALDataset *GetDataset() override
136
0
    {
137
0
        return m_poDS;
138
0
    }
139
140
  private:
141
    GDALDataset *m_poDS = nullptr;
142
    OGRFeatureDefn *poFeatureDefn_ = nullptr;
143
    std::string osFIDColumn_{};
144
145
    /** Total number of features. */
146
    GIntBig nFeatureCount_ = -1;
147
148
    VSIVirtualHandleUniquePtr poFile_{};
149
150
    std::unique_ptr<OGRJSONFGStreamingParser> poStreamingParser_{};
151
152
    /** Whether a warning has been emitted about feature IDs having been
153
     * modified */
154
    bool bOriginalIdModified_ = false;
155
    /** Set of feature IDs read/allocated up to that point */
156
    std::set<GIntBig> oSetUsedFIDs_{};
157
158
    /** Ensure the FID of the feature is unique */
159
    OGRFeature *EnsureUniqueFID(OGRFeature *poFeat);
160
161
    /** Return next feature (without filter) */
162
    OGRFeature *GetNextRawFeature();
163
164
    CPL_DISALLOW_COPY_ASSIGN(OGRJSONFGStreamedLayer)
165
};
166
167
/************************************************************************/
168
/*                         OGRJSONFGWriteLayer                          */
169
/************************************************************************/
170
171
class OGRJSONFGDataset;
172
173
class OGRJSONFGWriteLayer final : public OGRLayer
174
{
175
  public:
176
    OGRJSONFGWriteLayer(
177
        const char *pszName, const OGRSpatialReference *poSRS,
178
        std::unique_ptr<OGRCoordinateTransformation> &&poCTToWGS84,
179
        const std::string &osCoordRefSys, OGRwkbGeometryType eGType,
180
        CSLConstList papszOptions, OGRJSONFGDataset *poDS);
181
    ~OGRJSONFGWriteLayer() override;
182
183
    //
184
    // OGRLayer Interface
185
    //
186
    const OGRFeatureDefn *GetLayerDefn() const override
187
0
    {
188
0
        return poFeatureDefn_;
189
0
    }
190
191
    const OGRSpatialReference *GetSpatialRef() const override
192
0
    {
193
0
        return nullptr;
194
0
    }
195
196
    void ResetReading() override
197
0
    {
198
0
    }
199
200
    OGRFeature *GetNextFeature() override
201
0
    {
202
0
        return nullptr;
203
0
    }
204
205
    OGRErr ICreateFeature(OGRFeature *poFeature) override;
206
    OGRErr CreateField(const OGRFieldDefn *poField, int bApproxOK) override;
207
    int TestCapability(const char *pszCap) const override;
208
209
    OGRErr SyncToDisk() override;
210
211
    GDALDataset *GetDataset() override;
212
213
    bool HasPolyhedra() const
214
0
    {
215
0
        return m_bPolyhedraWritten;
216
0
    }
217
218
    bool HasCurve() const
219
0
    {
220
0
        return m_bCurveWritten;
221
0
    }
222
223
    bool HasMeasure() const
224
0
    {
225
0
        return m_bMeasureWritten;
226
0
    }
227
228
  private:
229
    OGRJSONFGDataset *poDS_{};
230
    OGRFeatureDefn *poFeatureDefn_ = nullptr;
231
    std::unique_ptr<OGRCoordinateTransformation> poCTToWGS84_;
232
    bool bIsWGS84CRS_ = false;
233
    bool m_bMustSwapForPlace = false;
234
    int nOutCounter_ = 0;
235
    std::string osCoordRefSys_{};
236
    bool m_bPolyhedraWritten = false;
237
    bool m_bCurveWritten = false;
238
    bool m_bMeasureWritten = false;
239
    bool bLayerLevelMeasuresWritten_ = false;
240
    std::string osMeasureUnit_{};
241
    std::string osMeasureDescription_{};
242
243
    OGRGeoJSONWriteOptions oWriteOptions_{};
244
    OGRGeoJSONWriteOptions oWriteOptionsPlace_{};
245
    bool bWriteFallbackGeometry_ = true;
246
247
    CPL_DISALLOW_COPY_ASSIGN(OGRJSONFGWriteLayer)
248
};
249
250
/************************************************************************/
251
/*                           OGRJSONFGDataset                           */
252
/************************************************************************/
253
254
class OGRJSONFGReader;
255
256
class OGRJSONFGDataset final : public GDALDataset
257
{
258
  public:
259
1.64k
    OGRJSONFGDataset() = default;
260
    ~OGRJSONFGDataset() override;
261
262
    CPLErr Close(GDALProgressFunc = nullptr, void * = nullptr) override;
263
264
    bool Open(GDALOpenInfo *poOpenInfo, GeoJSONSourceType nSrcType);
265
    bool Create(const char *pszName, CSLConstList papszOptions);
266
267
    int GetLayerCount() const override
268
16
    {
269
16
        return static_cast<int>(apoLayers_.size());
270
16
    }
271
272
    const OGRLayer *GetLayer(int i) const override;
273
274
    //! Return the output file handle. Used by OGRJSONFGWriteLayer
275
    VSILFILE *GetOutputFile() const
276
0
    {
277
0
        return fpOut_;
278
0
    }
279
280
    /** Return whether there is a single output layer.
281
     * Used by OGRJSONFGWriteLayer
282
     */
283
    bool IsSingleOutputLayer() const
284
0
    {
285
0
        return bSingleOutputLayer_;
286
0
    }
287
288
    //! Return whether the output file is seekable
289
    bool GetFpOutputIsSeekable() const
290
0
    {
291
0
        return bFpOutputIsSeekable_;
292
0
    }
293
294
    void BeforeCreateFeature();
295
296
    OGRLayer *ICreateLayer(const char *pszName,
297
                           const OGRGeomFieldDefn *poGeomFieldDefn,
298
                           CSLConstList papszOptions) override;
299
300
    int TestCapability(const char *pszCap) const override;
301
302
    OGRErr SyncToDiskInternal();
303
304
  protected:
305
    friend class OGRJSONFGReader;
306
    OGRJSONFGMemLayer *AddLayer(std::unique_ptr<OGRJSONFGMemLayer> &&poLayer);
307
    OGRJSONFGStreamedLayer *
308
    AddLayer(std::unique_ptr<OGRJSONFGStreamedLayer> &&poLayer);
309
310
  private:
311
    char *pszGeoData_ = nullptr;
312
    size_t nGeoDataLen_ = 0;
313
    std::vector<std::unique_ptr<OGRLayer>> apoLayers_{};
314
    std::unique_ptr<OGRJSONFGReader> poReader_{};
315
316
    // Write side
317
    VSILFILE *fpOut_ = nullptr;
318
    vsi_l_offset m_nPositionBeforeConformsTo = 0;
319
    vsi_l_offset m_nPositionAfterConformsTo = 0;
320
    bool bSingleOutputLayer_ = false;
321
    bool bHasEmittedFeatures_ = false;
322
    bool bFpOutputIsSeekable_ = false;
323
324
    /** Offset at which the '] }' terminating sequence has already been
325
     * written by SyncToDisk(). 0 if it has not been written.
326
     */
327
    vsi_l_offset m_nPositionBeforeFCClosed = 0;
328
329
    bool ReadFromFile(GDALOpenInfo *poOpenInfo, const char *pszUnprefixed);
330
    bool ReadFromService(GDALOpenInfo *poOpenInfo, const char *pszSource);
331
332
    bool FinishWriting();
333
334
    bool EmitStartFeaturesIfNeededAndReturnIfFirstFeature();
335
336
    CPL_DISALLOW_COPY_ASSIGN(OGRJSONFGDataset)
337
};
338
339
/************************************************************************/
340
/*                           OGRJSONFGReader                            */
341
/************************************************************************/
342
343
class OGRJSONFGReader
344
{
345
  public:
346
938
    OGRJSONFGReader() = default;
347
    ~OGRJSONFGReader();
348
349
    /** Load all features from the passed in JSON text in OGRJSONFGMemLayer(s)
350
     *
351
     * This method should only be called once, and is exclusive with
352
     * AnalyzeWithStreamingParser()
353
     */
354
    bool Load(OGRJSONFGDataset *poDS, const char *pszText,
355
              const std::string &osDefaultLayerName);
356
357
    /** Do a first pass analysis of the content of the passed file to create
358
     * OGRJSONFGStreamedLayer's
359
     *
360
     * It is the responsibility of the caller to call
361
     * SetFile() and SetStreamingParser() on the created layers afterwards
362
     *
363
     * This method should only be called once, and is exclusive with
364
     * Load()
365
     */
366
    bool AnalyzeWithStreamingParser(OGRJSONFGDataset *poDS, VSILFILE *fp,
367
                                    const std::string &osDefaultLayerName,
368
                                    bool &bCanTryWithNonStreamingParserOut,
369
                                    bool &bHasTopLevelMeasures);
370
371
    /** Geometry element we are interested in. */
372
    enum class GeometryElement
373
    {
374
        /** Use "place" when possible, fallback to "geometry" otherwise. */
375
        AUTO,
376
        /** Only use "place" */
377
        PLACE,
378
        /** Only use "geometry" */
379
        GEOMETRY,
380
    };
381
382
    /** Sets the geometry element we are interested in. */
383
    void SetGeometryElement(GeometryElement elt)
384
0
    {
385
0
        eGeometryElement_ = elt;
386
0
    }
387
388
    /** Returns a OGRFeature built from the passed in JSON object.
389
     *
390
     * @param poObj JSON feature
391
     * @param pszRequestedLayer name of the layer of interest, or nullptr if
392
     * no filtering needed on the layer name. If the feature does not belong
393
     * to the requested layer, nullptr is returned.
394
     * @param bHasM Whether the upper level of this object has measures
395
     * @param pOutMemLayer Pointer to the OGRJSONFGMemLayer* layer to which
396
     * the returned feature belongs to. May be nullptr. Only applies when
397
     * the Load() method has been used.
398
     * @param pOutStreamedLayer Pointer to the OGRJSONFGStreamedLayer* layer to
399
     * which the returned feature belongs to. May be nullptr. Only applies when
400
     * the AnalyzeWithStreamingParser() method has been used.
401
     */
402
    std::unique_ptr<OGRFeature>
403
    ReadFeature(json_object *poObj, const char *pszRequestedLayer, bool bHasM,
404
                OGRJSONFGMemLayer **pOutMemLayer,
405
                OGRJSONFGStreamedLayer **pOutStreamedLayer);
406
407
  protected:
408
    friend class OGRJSONFGStreamingParser;
409
410
    bool GenerateLayerDefnFromFeature(json_object *poObj);
411
412
  private:
413
    GeometryElement eGeometryElement_ = GeometryElement::AUTO;
414
415
    OGRJSONFGDataset *poDS_ = nullptr;
416
    std::string osDefaultLayerName_{};
417
    json_object *poObject_ = nullptr;
418
419
    bool bFlattenNestedAttributes_ = false;
420
    char chNestedAttributeSeparator_ = 0;
421
    bool bArrayAsString_ = false;
422
    bool bDateAsString_ = false;
423
    std::string osMeasureUnit_{};
424
    std::string osMeasureDescription_{};
425
426
    /** Layer building context, specific to one layer. */
427
    struct LayerDefnBuildContext
428
    {
429
        //! Maps a field name to its index in apoFieldDefn[]
430
        std::map<std::string, int> oMapFieldNameToIdx{};
431
432
        //! Vector of OGRFieldDefn
433
        std::vector<std::unique_ptr<OGRFieldDefn>> apoFieldDefn{};
434
435
        //! Directed acyclic graph used to build the order of fields.
436
        gdal::DirectedAcyclicGraph<int, std::string> dag{};
437
438
        /** Set of indices of apoFieldDefn[] for which no type information is
439
         * known yet. */
440
        std::set<int> aoSetUndeterminedTypeFields{};
441
442
        //! Whether at least one feature has a "coordRefSys" member.
443
        bool bHasCoordRefSysAtFeatureLevel = false;
444
445
        /** CRS object corresponding to "coordRefsys" member at feature level.
446
         * Only set if homogeneous among features.
447
         */
448
        std::unique_ptr<OGRSpatialReference> poCRSAtFeatureLevel{};
449
450
        /** Serialized JSON value of "coordRefsys" member at feature level.
451
         * Only set if homogeneous among features.
452
         */
453
        std::string osCoordRefSysAtFeatureLevel{};
454
455
        /** Whether to switch X/Y ordinates in geometries appearing in "place"
456
         * element. Only applies to CRS at layer level.
457
         */
458
        bool bSwapPlacesXY = false;
459
460
        //! Whether the layer CRS is WGS 84.
461
        bool bLayerCRSIsWGS84 = false;
462
463
        //! Coordinate transformation from WGS 84 to layer CRS (might be null)
464
        std::unique_ptr<OGRCoordinateTransformation> poCTWGS84ToLayerCRS{};
465
466
        /** Feature count */
467
        GIntBig nFeatureCount = 0;
468
469
        //! Whether the Feature.id should be mapped to a OGR field.
470
        bool bFeatureLevelIdAsAttribute = false;
471
472
        //! Whether the Feature.id should be mapped to a OGR FID.
473
        bool bFeatureLevelIdAsFID = false;
474
475
        //! Whether 64-bit integers are needed for OGR FID.
476
        bool bNeedFID64 = false;
477
478
        //! Whether detection of layer geometry type is still needed.
479
        bool bDetectLayerGeomType = true;
480
481
        //! Whether no geometry has been analyzed yet.
482
        bool bFirstGeometry = true;
483
484
        //! Layer geometry type.
485
        OGRwkbGeometryType eLayerGeomType = wkbUnknown;
486
487
        //! Whether a Feature.time.date element has been found.
488
        bool bHasTimeDate = false;
489
490
        //! Whether a Feature.time.timestamp element has been found.
491
        bool bHasTimeTimestamp = false;
492
493
        /** Whether a Feature.time.interval[0] element of type timestamp has
494
         * been found */
495
        bool bHasTimeIntervalStartTimestamp = false;
496
497
        /** Whether a Feature.time.interval[0] element of type date has
498
         * been found */
499
        bool bHasTimeIntervalStartDate = false;
500
501
        /** Whether a Feature.time.interval[1] element of type timestamp has
502
         * been found */
503
        bool bHasTimeIntervalEndTimestamp = false;
504
505
        /** Whether a Feature.time.interval[1] element of type date has
506
         * been found */
507
        bool bHasTimeIntervalEndDate = false;
508
509
        //! Index of OGR field "time" / "jsonfg_time"
510
        int nIdxFieldTime = -1;
511
512
        //! Index of OGR field "time_start" / "jsonfg_time_start"
513
        int nIdxFieldTimeStart = -1;
514
515
        //! Index of OGR field "time_end" / "jsonfg_time_end"
516
        int nIdxFieldTimeEnd = -1;
517
518
        //! Corresponding OGRJSONFGMemLayer (only for Load() ingestion mode)
519
        OGRJSONFGMemLayer *poMemLayer = nullptr;
520
521
        /** Corresponding OGRJSONFGStreamedLayer(only for
522
         * AnalyzeWithStreamingParser() mode) */
523
        OGRJSONFGStreamedLayer *poStreamedLayer = nullptr;
524
525
        bool bSameMeasureMetadata = true;
526
527
        //! Measure unit
528
        std::string osMeasureUnit{};
529
530
        //! Measure description
531
        std::string osMeasureDescription{};
532
533
238
        LayerDefnBuildContext() = default;
534
0
        LayerDefnBuildContext(LayerDefnBuildContext &&) = default;
535
119
        LayerDefnBuildContext &operator=(LayerDefnBuildContext &&) = default;
536
537
      private:
538
        CPL_DISALLOW_COPY_ASSIGN(LayerDefnBuildContext)
539
    };
540
541
    //! Maps a layer name to its build context
542
    std::map<std::string, LayerDefnBuildContext> oMapBuildContext_{};
543
544
    //
545
    // Copy operations not supported.
546
    //
547
    CPL_DISALLOW_COPY_ASSIGN(OGRJSONFGReader)
548
549
    const char *GetLayerNameForFeature(json_object *poObj) const;
550
    bool GenerateLayerDefns();
551
    bool FinalizeGenerateLayerDefns(bool bStreamedLayer);
552
    void FinalizeBuildContext(LayerDefnBuildContext &oBuildContext,
553
                              const char *pszLayerName, bool bStreamedLayer,
554
                              bool bInvalidCRS, bool bSwapPlacesXYTopLevel,
555
                              OGRSpatialReference *poSRSTopLevel);
556
};
557
558
/************************************************************************/
559
/*                       OGRJSONFGStreamingParser                       */
560
/************************************************************************/
561
562
/** FeatureCollection streaming parser. */
563
class OGRJSONFGStreamingParser final : public OGRJSONCollectionStreamingParser
564
{
565
    OGRJSONFGReader &m_oReader;
566
    std::string m_osRequestedLayer{};
567
568
    std::vector<std::pair<std::unique_ptr<OGRFeature>, OGRLayer *>>
569
        m_apoFeatures{};
570
    size_t m_nCurFeatureIdx = 0;
571
572
    CPL_DISALLOW_COPY_ASSIGN(OGRJSONFGStreamingParser)
573
574
  protected:
575
    void GotFeature(json_object *poObj, bool bFirstPass,
576
                    const std::string &osJson) override;
577
    void TooComplex() override;
578
579
  public:
580
    OGRJSONFGStreamingParser(OGRJSONFGReader &oReader, bool bFirstPass,
581
                             bool bHasTopLevelMeasures);
582
    ~OGRJSONFGStreamingParser() override;
583
584
    void SetRequestedLayer(const char *pszRequestedLayer)
585
12
    {
586
12
        m_osRequestedLayer = pszRequestedLayer;
587
12
    }
588
589
    std::unique_ptr<OGRJSONFGStreamingParser> Clone();
590
591
    std::pair<std::unique_ptr<OGRFeature>, OGRLayer *> GetNextFeature();
592
};
593
594
bool OGRJSONFGMustSwapXY(const OGRSpatialReference *poSRS);
595
596
#endif  // OGR_JSONFG_H_INCLUDED