Coverage Report

Created: 2025-06-13 06:29

/src/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsondriver.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  OpenGIS Simple Features Reference Implementation
4
 * Purpose:  Implementation of OGRGeoJSONDriver class (OGR GeoJSON Driver).
5
 * Author:   Mateusz Loskot, mateusz@loskot.net
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2007, Mateusz Loskot
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "cpl_port.h"
14
#include "ogr_geojson.h"
15
16
#include <stdlib.h>
17
#include <string.h>
18
#include <limits>
19
20
#include "cpl_conv.h"
21
#include "cpl_error.h"
22
#include "cpl_http.h"
23
#include "cpl_multiproc.h"
24
#include "cpl_string.h"
25
#include "cpl_vsi.h"
26
// #include "json_object.h"
27
#include "gdal.h"
28
#include "gdal_priv.h"
29
#include "ogr_core.h"
30
#include "ogr_feature.h"
31
#include "ogrgeojsonutils.h"
32
#include "ogrsf_frmts.h"
33
34
static CPLMutex *ghMutex = nullptr;
35
static char *gpszSource = nullptr;
36
static char *gpszText = nullptr;
37
38
class OGRESRIFeatureServiceDataset;
39
40
/************************************************************************/
41
/*                      OGRESRIFeatureServiceLayer                      */
42
/************************************************************************/
43
44
class OGRESRIFeatureServiceLayer final : public OGRLayer
45
{
46
    OGRESRIFeatureServiceDataset *poDS;
47
    OGRFeatureDefn *poFeatureDefn;
48
    GIntBig nFeaturesRead;
49
    GIntBig nFirstFID;
50
    GIntBig nLastFID;
51
    bool bOtherPage;
52
    bool bUseSequentialFID;
53
54
  public:
55
    explicit OGRESRIFeatureServiceLayer(OGRESRIFeatureServiceDataset *poDS);
56
    virtual ~OGRESRIFeatureServiceLayer();
57
58
    void ResetReading() override;
59
    OGRFeature *GetNextFeature() override;
60
    GIntBig GetFeatureCount(int bForce = TRUE) override;
61
    OGRErr IGetExtent(int iGeomField, OGREnvelope *psExtent,
62
                      bool bForce = true) override;
63
64
    int TestCapability(const char *pszCap) override;
65
66
    OGRFeatureDefn *GetLayerDefn() override
67
0
    {
68
0
        return poFeatureDefn;
69
0
    }
70
};
71
72
/************************************************************************/
73
/*                       OGRESRIFeatureServiceDataset                   */
74
/************************************************************************/
75
76
class OGRESRIFeatureServiceDataset final : public GDALDataset
77
{
78
    CPLString m_osURL{};
79
    GIntBig m_nFirstOffset = 0;
80
    GIntBig m_nLastOffset = 0;
81
    std::unique_ptr<OGRGeoJSONDataSource> m_poCurrent{};
82
    std::unique_ptr<OGRESRIFeatureServiceLayer> m_poLayer{};
83
    GeoJSONSourceType m_nSrcType = eGeoJSONSourceUnknown;
84
85
    bool LoadPage();
86
87
  public:
88
    OGRESRIFeatureServiceDataset(
89
        const std::string &osURL,
90
        std::unique_ptr<OGRGeoJSONDataSource> &&poFirst,
91
        GeoJSONSourceType nSrcType);
92
93
    int GetLayerCount() override
94
0
    {
95
0
        return 1;
96
0
    }
97
98
    OGRLayer *GetLayer(int nLayer) override;
99
100
    OGRLayer *GetUnderlyingLayer()
101
0
    {
102
0
        return m_poCurrent->GetLayer(0);
103
0
    }
104
105
    bool MyResetReading();
106
    bool LoadNextPage();
107
108
    const CPLString &GetURL() const
109
0
    {
110
0
        return m_osURL;
111
0
    }
112
};
113
114
OGRLayer *OGRESRIFeatureServiceDataset::GetLayer(int nLayer)
115
0
{
116
0
    return (nLayer == 0) ? m_poLayer.get() : nullptr;
117
0
}
118
119
/************************************************************************/
120
/*                       OGRESRIFeatureServiceLayer()                   */
121
/************************************************************************/
122
123
OGRESRIFeatureServiceLayer::OGRESRIFeatureServiceLayer(
124
    OGRESRIFeatureServiceDataset *poDSIn)
125
0
    : poDS(poDSIn), nFeaturesRead(0), nFirstFID(0), nLastFID(0),
126
0
      bOtherPage(false), bUseSequentialFID(false)
127
0
{
128
0
    OGRFeatureDefn *poSrcFeatDefn = poDS->GetUnderlyingLayer()->GetLayerDefn();
129
0
    poFeatureDefn = new OGRFeatureDefn(poSrcFeatDefn->GetName());
130
0
    SetDescription(poFeatureDefn->GetName());
131
0
    poFeatureDefn->Reference();
132
0
    poFeatureDefn->SetGeomType(wkbNone);
133
134
0
    for (int i = 0; i < poSrcFeatDefn->GetFieldCount(); i++)
135
0
        poFeatureDefn->AddFieldDefn(poSrcFeatDefn->GetFieldDefn(i));
136
137
0
    for (int i = 0; i < poSrcFeatDefn->GetGeomFieldCount(); i++)
138
0
        poFeatureDefn->AddGeomFieldDefn(poSrcFeatDefn->GetGeomFieldDefn(i));
139
0
}
140
141
/************************************************************************/
142
/*                      ~OGRESRIFeatureServiceLayer()                   */
143
/************************************************************************/
144
145
OGRESRIFeatureServiceLayer::~OGRESRIFeatureServiceLayer()
146
0
{
147
0
    poFeatureDefn->Release();
148
0
}
149
150
/************************************************************************/
151
/*                            ResetReading()                            */
152
/************************************************************************/
153
154
void OGRESRIFeatureServiceLayer::ResetReading()
155
0
{
156
0
    poDS->MyResetReading();
157
0
    nFeaturesRead = 0;
158
0
    nLastFID = 0;
159
0
    bOtherPage = false;
160
0
    bUseSequentialFID = false;
161
0
}
162
163
/************************************************************************/
164
/*                            GetNextFeature()                          */
165
/************************************************************************/
166
167
OGRFeature *OGRESRIFeatureServiceLayer::GetNextFeature()
168
0
{
169
0
    while (true)
170
0
    {
171
0
        const bool bWasInFirstPage = !bOtherPage;
172
0
#if defined(__GNUC__)
173
0
#pragma GCC diagnostic push
174
0
#pragma GCC diagnostic ignored "-Wnull-dereference"
175
0
#endif
176
0
        OGRFeature *poSrcFeat = poDS->GetUnderlyingLayer()->GetNextFeature();
177
0
#if defined(__GNUC__)
178
0
#pragma GCC diagnostic pop
179
0
#endif
180
0
        if (poSrcFeat == nullptr)
181
0
        {
182
0
            if (!poDS->LoadNextPage())
183
0
                return nullptr;
184
0
#if defined(__GNUC__)
185
0
#pragma GCC diagnostic push
186
0
#pragma GCC diagnostic ignored "-Wnull-dereference"
187
0
#endif
188
0
            poSrcFeat = poDS->GetUnderlyingLayer()->GetNextFeature();
189
0
#if defined(__GNUC__)
190
0
#pragma GCC diagnostic pop
191
0
#endif
192
0
            if (poSrcFeat == nullptr)
193
0
                return nullptr;
194
0
            bOtherPage = true;
195
0
            if (bWasInFirstPage && poSrcFeat->GetFID() != 0 &&
196
0
                poSrcFeat->GetFID() == nFirstFID)
197
0
            {
198
                // End-less looping
199
0
                CPLDebug("ESRIJSON", "Scrolling not working. Stopping");
200
0
                delete poSrcFeat;
201
0
                return nullptr;
202
0
            }
203
0
            if (bWasInFirstPage && poSrcFeat->GetFID() == 0 &&
204
0
                nLastFID == nFeaturesRead - 1)
205
0
            {
206
0
                bUseSequentialFID = true;
207
0
            }
208
0
        }
209
0
        if (nFeaturesRead == 0)
210
0
            nFirstFID = poSrcFeat->GetFID();
211
212
0
        OGRFeature *poFeature = new OGRFeature(poFeatureDefn);
213
0
        poFeature->SetFrom(poSrcFeat);
214
0
        if (bUseSequentialFID)
215
0
            poFeature->SetFID(nFeaturesRead);
216
0
        else
217
0
            poFeature->SetFID(poSrcFeat->GetFID());
218
0
        nLastFID = poFeature->GetFID();
219
0
        nFeaturesRead++;
220
0
        delete poSrcFeat;
221
222
0
        if ((m_poFilterGeom == nullptr ||
223
0
             FilterGeometry(poFeature->GetGeometryRef())) &&
224
0
            (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
225
0
        {
226
0
            return poFeature;
227
0
        }
228
0
        delete poFeature;
229
0
    }
230
0
}
231
232
/************************************************************************/
233
/*                          TestCapability()                            */
234
/************************************************************************/
235
236
int OGRESRIFeatureServiceLayer::TestCapability(const char *pszCap)
237
0
{
238
0
    if (EQUAL(pszCap, OLCFastFeatureCount))
239
0
        return m_poAttrQuery == nullptr && m_poFilterGeom == nullptr;
240
0
    if (EQUAL(pszCap, OLCFastGetExtent))
241
0
        return FALSE;
242
0
#if defined(__GNUC__)
243
0
#pragma GCC diagnostic push
244
0
#pragma GCC diagnostic ignored "-Wnull-dereference"
245
0
#endif
246
0
    auto poUnderlyingLayer = poDS->GetUnderlyingLayer();
247
0
    return poUnderlyingLayer->TestCapability(pszCap);
248
0
#if defined(__GNUC__)
249
0
#pragma GCC diagnostic pop
250
0
#endif
251
0
}
252
253
/************************************************************************/
254
/*                          GetFeatureCount()                           */
255
/************************************************************************/
256
257
GIntBig OGRESRIFeatureServiceLayer::GetFeatureCount(int bForce)
258
0
{
259
0
    GIntBig nFeatureCount = -1;
260
0
    if (m_poAttrQuery == nullptr && m_poFilterGeom == nullptr)
261
0
    {
262
0
        CPLString osNewURL =
263
0
            CPLURLAddKVP(poDS->GetURL(), "returnCountOnly", "true");
264
0
        osNewURL = CPLURLAddKVP(osNewURL, "resultRecordCount", nullptr);
265
0
        CPLErrorReset();
266
0
        CPLHTTPResult *pResult = CPLHTTPFetch(osNewURL, nullptr);
267
0
        if (pResult != nullptr && pResult->nDataLen != 0 &&
268
0
            CPLGetLastErrorNo() == 0 && pResult->nStatus == 0)
269
0
        {
270
0
            const char *pszCount =
271
0
                strstr((const char *)pResult->pabyData, "\"count\"");
272
0
            if (pszCount)
273
0
            {
274
0
                pszCount = strchr(pszCount, ':');
275
0
                if (pszCount)
276
0
                {
277
0
                    pszCount++;
278
0
                    nFeatureCount = CPLAtoGIntBig(pszCount);
279
0
                }
280
0
            }
281
0
        }
282
0
        CPLHTTPDestroyResult(pResult);
283
0
    }
284
0
    if (nFeatureCount < 0)
285
0
        nFeatureCount = OGRLayer::GetFeatureCount(bForce);
286
0
    return nFeatureCount;
287
0
}
288
289
/************************************************************************/
290
/*                              IGetExtent()                            */
291
/************************************************************************/
292
293
OGRErr OGRESRIFeatureServiceLayer::IGetExtent(int iGeomField,
294
                                              OGREnvelope *psExtent,
295
                                              bool bForce)
296
0
{
297
0
    OGRErr eErr = OGRERR_FAILURE;
298
0
    CPLString osNewURL =
299
0
        CPLURLAddKVP(poDS->GetURL(), "returnExtentOnly", "true");
300
0
    osNewURL = CPLURLAddKVP(osNewURL, "resultRecordCount", nullptr);
301
0
    osNewURL = CPLURLAddKVP(osNewURL, "f", "geojson");
302
0
    CPLErrorReset();
303
0
    CPLHTTPResult *pResult = CPLHTTPFetch(osNewURL, nullptr);
304
0
    if (pResult != nullptr && pResult->nDataLen != 0 &&
305
0
        CPLGetLastErrorNo() == 0 && pResult->nStatus == 0)
306
0
    {
307
0
        const char *pszBBox =
308
0
            strstr((const char *)pResult->pabyData, "\"bbox\"");
309
0
        if (pszBBox)
310
0
        {
311
0
            pszBBox = strstr(pszBBox, ":[");
312
0
            if (pszBBox)
313
0
            {
314
0
                pszBBox += 2;
315
0
                char **papszTokens = CSLTokenizeString2(pszBBox, ",", 0);
316
0
                if (CSLCount(papszTokens) >= 4)
317
0
                {
318
0
                    psExtent->MinX = CPLAtof(papszTokens[0]);
319
0
                    psExtent->MinY = CPLAtof(papszTokens[1]);
320
0
                    psExtent->MaxX = CPLAtof(papszTokens[2]);
321
0
                    psExtent->MaxY = CPLAtof(papszTokens[3]);
322
0
                    eErr = OGRERR_NONE;
323
0
                }
324
0
                CSLDestroy(papszTokens);
325
0
            }
326
0
        }
327
0
    }
328
0
    CPLHTTPDestroyResult(pResult);
329
0
    if (eErr == OGRERR_FAILURE)
330
0
        eErr = OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
331
0
    return eErr;
332
0
}
333
334
/************************************************************************/
335
/*                      OGRESRIFeatureServiceDataset()                  */
336
/************************************************************************/
337
338
OGRESRIFeatureServiceDataset::OGRESRIFeatureServiceDataset(
339
    const std::string &osURL, std::unique_ptr<OGRGeoJSONDataSource> &&poFirst,
340
    GeoJSONSourceType nSrcType)
341
0
    : m_osURL(osURL), m_poCurrent(std::move(poFirst)), m_nSrcType(nSrcType)
342
0
{
343
0
    m_poLayer = std::make_unique<OGRESRIFeatureServiceLayer>(this);
344
0
    if (CPLURLGetValue(m_osURL, "resultRecordCount").empty())
345
0
    {
346
        // We assume that if the server sets the exceededTransferLimit, the
347
        // and resultRecordCount is not set, the number of features returned
348
        // in our first request is the maximum allowed by the server
349
        // So set it for following requests.
350
0
        m_osURL = CPLURLAddKVP(
351
0
            m_osURL, "resultRecordCount",
352
0
            CPLSPrintf("%d", static_cast<int>(
353
0
                                 m_poCurrent->GetLayer(0)->GetFeatureCount())));
354
0
    }
355
0
    else
356
0
    {
357
0
        const int nUserSetRecordCount =
358
0
            atoi(CPLURLGetValue(m_osURL, "resultRecordCount"));
359
0
        if (nUserSetRecordCount > m_poCurrent->GetLayer(0)->GetFeatureCount())
360
0
        {
361
0
            CPLError(
362
0
                CE_Warning, CPLE_AppDefined,
363
0
                "Specified resultRecordCount=%d is greater than "
364
0
                "the maximum %d supported by the server",
365
0
                nUserSetRecordCount,
366
0
                static_cast<int>(m_poCurrent->GetLayer(0)->GetFeatureCount()));
367
0
        }
368
0
    }
369
0
    m_nFirstOffset = CPLAtoGIntBig(CPLURLGetValue(m_osURL, "resultOffset"));
370
0
    m_nLastOffset = m_nFirstOffset;
371
0
}
372
373
/************************************************************************/
374
/*                           MyResetReading()                           */
375
/************************************************************************/
376
377
bool OGRESRIFeatureServiceDataset::MyResetReading()
378
0
{
379
0
    if (m_nLastOffset > m_nFirstOffset)
380
0
    {
381
0
        m_nLastOffset = m_nFirstOffset;
382
0
        return LoadPage();
383
0
    }
384
385
0
#if defined(__GNUC__)
386
0
#pragma GCC diagnostic push
387
0
#pragma GCC diagnostic ignored "-Wnull-dereference"
388
0
#endif
389
0
    m_poCurrent->GetLayer(0)->ResetReading();
390
0
#if defined(__GNUC__)
391
0
#pragma GCC diagnostic pop
392
0
#endif
393
0
    return true;
394
0
}
395
396
/************************************************************************/
397
/*                             LoadNextPage()                           */
398
/************************************************************************/
399
400
bool OGRESRIFeatureServiceDataset::LoadNextPage()
401
0
{
402
0
    if (!m_poCurrent->HasOtherPages())
403
0
        return false;
404
0
#if defined(__GNUC__)
405
0
#pragma GCC diagnostic push
406
0
#pragma GCC diagnostic ignored "-Wnull-dereference"
407
0
#endif
408
0
    const auto nCurPageFC = m_poCurrent->GetLayer(0)->GetFeatureCount();
409
0
#if defined(__GNUC__)
410
0
#pragma GCC diagnostic pop
411
0
#endif
412
0
    if (m_nLastOffset > std::numeric_limits<GIntBig>::max() - nCurPageFC)
413
0
        return false;
414
0
    m_nLastOffset += nCurPageFC;
415
0
    return LoadPage();
416
0
}
417
418
/************************************************************************/
419
/*                                 LoadPage()                           */
420
/************************************************************************/
421
422
bool OGRESRIFeatureServiceDataset::LoadPage()
423
0
{
424
0
    CPLString osNewURL = CPLURLAddKVP(m_osURL, "resultOffset",
425
0
                                      CPLSPrintf(CPL_FRMT_GIB, m_nLastOffset));
426
0
    auto poDS = std::make_unique<OGRGeoJSONDataSource>();
427
0
    GDALOpenInfo oOpenInfo(osNewURL, GA_ReadOnly);
428
0
    if (!poDS->Open(&oOpenInfo, m_nSrcType, m_poCurrent->GetJSonFlavor()) ||
429
0
        poDS->GetLayerCount() == 0)
430
0
    {
431
0
        return false;
432
0
    }
433
0
    m_poCurrent = std::move(poDS);
434
0
    return true;
435
0
}
436
437
/************************************************************************/
438
/*                        OGRGeoJSONDriverIdentify()                    */
439
/************************************************************************/
440
441
static int OGRGeoJSONDriverIdentifyInternal(GDALOpenInfo *poOpenInfo,
442
                                            GeoJSONSourceType &nSrcType)
443
0
{
444
    /* -------------------------------------------------------------------- */
445
    /*      Determine type of data source: text file (.geojson, .json),     */
446
    /*      Web Service or text passed directly and load data.              */
447
    /* -------------------------------------------------------------------- */
448
449
0
    nSrcType = GeoJSONGetSourceType(poOpenInfo);
450
0
    if (nSrcType == eGeoJSONSourceUnknown)
451
0
    {
452
0
        const char *pszHeader =
453
0
            reinterpret_cast<const char *>(poOpenInfo->pabyHeader);
454
0
        if (pszHeader && STARTS_WITH(pszHeader, "{\"properties\":{"))
455
0
            return GDAL_IDENTIFY_UNKNOWN;
456
457
0
        return FALSE;
458
0
    }
459
460
0
    if (nSrcType == eGeoJSONSourceService)
461
0
    {
462
0
        if (poOpenInfo->IsSingleAllowedDriver("GeoJSON"))
463
0
            return TRUE;
464
0
        if (!STARTS_WITH_CI(poOpenInfo->pszFilename, "GeoJSON:"))
465
0
        {
466
0
            return -1;
467
0
        }
468
0
    }
469
470
    // If this looks like a file that can be handled by the STACTA driver,
471
    // and that one is available, then don't identify the file.
472
0
    const char *pszHeader =
473
0
        reinterpret_cast<const char *>(poOpenInfo->pabyHeader);
474
0
    if (pszHeader != nullptr &&
475
0
        strstr(pszHeader, "\"stac_extensions\"") != nullptr &&
476
0
        strstr(pszHeader, "\"tiled-assets\"") != nullptr &&
477
0
        GDALGetDriverByName("STACTA") != nullptr)
478
0
    {
479
0
        if (poOpenInfo->IsSingleAllowedDriver("GeoJSON"))
480
0
            return TRUE;
481
0
        return FALSE;
482
0
    }
483
484
0
    return TRUE;
485
0
}
486
487
/************************************************************************/
488
/*                        OGRGeoJSONDriverIdentify()                    */
489
/************************************************************************/
490
491
static int OGRGeoJSONDriverIdentify(GDALOpenInfo *poOpenInfo)
492
0
{
493
0
    GeoJSONSourceType nSrcType;
494
0
    return OGRGeoJSONDriverIdentifyInternal(poOpenInfo, nSrcType);
495
0
}
496
497
/************************************************************************/
498
/*                           Open()                                     */
499
/************************************************************************/
500
501
static GDALDataset *OGRGeoJSONDriverOpen(GDALOpenInfo *poOpenInfo)
502
0
{
503
0
    GeoJSONSourceType nSrcType;
504
0
    if (OGRGeoJSONDriverIdentifyInternal(poOpenInfo, nSrcType) == FALSE)
505
0
    {
506
0
        return nullptr;
507
0
    }
508
0
    return OGRGeoJSONDriverOpenInternal(poOpenInfo, nSrcType, "GeoJSON");
509
0
}
510
511
/************************************************************************/
512
/*                     OGRGeoJSONDriverOpenInternal()                   */
513
/************************************************************************/
514
515
GDALDataset *OGRGeoJSONDriverOpenInternal(GDALOpenInfo *poOpenInfo,
516
                                          GeoJSONSourceType nSrcType,
517
                                          const char *pszJSonFlavor)
518
0
{
519
0
    auto poDS = std::make_unique<OGRGeoJSONDataSource>();
520
521
    /* -------------------------------------------------------------------- */
522
    /*      Processing configuration options.                               */
523
    /* -------------------------------------------------------------------- */
524
525
    // TODO: Currently, options are based on environment variables.
526
    //       This is workaround for not yet implemented Andrey's concept
527
    //       described in document 'RFC 10: OGR Open Parameters'.
528
529
0
    poDS->SetGeometryTranslation(OGRGeoJSONDataSource::eGeometryPreserve);
530
0
    const char *pszOpt = CPLGetConfigOption("GEOMETRY_AS_COLLECTION", nullptr);
531
0
    if (nullptr != pszOpt && STARTS_WITH_CI(pszOpt, "YES"))
532
0
    {
533
0
        poDS->SetGeometryTranslation(
534
0
            OGRGeoJSONDataSource::eGeometryAsCollection);
535
0
    }
536
537
0
    poDS->SetAttributesTranslation(OGRGeoJSONDataSource::eAttributesPreserve);
538
0
    pszOpt = CPLGetConfigOption("ATTRIBUTES_SKIP", nullptr);
539
0
    if (nullptr != pszOpt && STARTS_WITH_CI(pszOpt, "YES"))
540
0
    {
541
0
        poDS->SetAttributesTranslation(OGRGeoJSONDataSource::eAttributesSkip);
542
0
    }
543
544
    /* -------------------------------------------------------------------- */
545
    /*      Open and start processing GeoJSON datasource to OGR objects.    */
546
    /* -------------------------------------------------------------------- */
547
0
    if (!poDS->Open(poOpenInfo, nSrcType, pszJSonFlavor))
548
0
    {
549
0
        poDS.reset();
550
0
    }
551
552
0
    if (poDS != nullptr && poDS->HasOtherPages())
553
0
    {
554
0
        const char *pszFilename = poOpenInfo->pszFilename;
555
0
        if (STARTS_WITH_CI(pszFilename, "ESRIJSON:"))
556
0
            pszFilename += strlen("ESRIJSON:");
557
0
        if (STARTS_WITH(pszFilename, "http") ||
558
0
            STARTS_WITH(pszFilename, "/vsimem/"))
559
0
        {
560
0
            const char *pszFSP = CSLFetchNameValue(poOpenInfo->papszOpenOptions,
561
0
                                                   "FEATURE_SERVER_PAGING");
562
0
            const bool bHasResultOffset =
563
0
                !CPLURLGetValue(pszFilename, "resultOffset").empty();
564
0
            if ((!bHasResultOffset &&
565
0
                 (pszFSP == nullptr || CPLTestBool(pszFSP))) ||
566
0
                (bHasResultOffset && pszFSP != nullptr && CPLTestBool(pszFSP)))
567
0
            {
568
0
                return new OGRESRIFeatureServiceDataset(
569
0
                    pszFilename, std::move(poDS), nSrcType);
570
0
            }
571
0
        }
572
0
    }
573
574
0
    return poDS.release();
575
0
}
576
577
/************************************************************************/
578
/*                               Create()                               */
579
/************************************************************************/
580
581
static GDALDataset *OGRGeoJSONDriverCreate(const char *pszName,
582
                                           int /* nBands */, int /* nXSize */,
583
                                           int /* nYSize */,
584
                                           GDALDataType /* eDT */,
585
                                           char **papszOptions)
586
0
{
587
0
    OGRGeoJSONDataSource *poDS = new OGRGeoJSONDataSource();
588
589
0
    if (!poDS->Create(pszName, papszOptions))
590
0
    {
591
0
        delete poDS;
592
0
        poDS = nullptr;
593
0
    }
594
595
0
    return poDS;
596
0
}
597
598
/************************************************************************/
599
/*                               Delete()                               */
600
/************************************************************************/
601
602
static CPLErr OGRGeoJSONDriverDelete(const char *pszFilename)
603
0
{
604
0
    if (VSIUnlink(pszFilename) == 0)
605
0
    {
606
0
        return CE_None;
607
0
    }
608
609
0
    CPLDebug("GeoJSON", "Failed to delete \'%s\'", pszFilename);
610
611
0
    return CE_Failure;
612
0
}
613
614
/************************************************************************/
615
/*                      OGRGeoJSONDriverStoreContent()                  */
616
/************************************************************************/
617
618
void OGRGeoJSONDriverStoreContent(const char *pszSource, char *pszText)
619
0
{
620
0
    CPLMutexHolderD(&ghMutex);
621
0
    CPLAssert(pszSource);
622
0
    CPLAssert(pszText);
623
624
0
    CPLFree(gpszSource);
625
0
    CPLFree(gpszText);
626
0
    gpszSource = CPLStrdup(pszSource);
627
0
    gpszText = pszText;
628
0
}
629
630
/************************************************************************/
631
/*                    OGRGeoJSONDriverStealStoredContent()              */
632
/************************************************************************/
633
634
char *OGRGeoJSONDriverStealStoredContent(const char *pszSource)
635
0
{
636
0
    CPLMutexHolderD(&ghMutex);
637
0
    if (gpszSource && EQUAL(pszSource, gpszSource))
638
0
    {
639
0
        char *pszRet = gpszText;
640
0
        CPLFree(gpszSource);
641
0
        gpszSource = nullptr;
642
0
        gpszText = nullptr;
643
0
        return pszRet;
644
0
    }
645
0
    return nullptr;
646
0
}
647
648
/************************************************************************/
649
/*                        OGRGeoJSONDriverUnload()                      */
650
/************************************************************************/
651
652
static void OGRGeoJSONDriverUnload(GDALDriver *)
653
0
{
654
0
    if (ghMutex)
655
0
        CPLDestroyMutex(ghMutex);
656
0
    ghMutex = nullptr;
657
0
    CPLFree(gpszSource);
658
0
    CPLFree(gpszText);
659
0
    gpszSource = nullptr;
660
0
    gpszText = nullptr;
661
0
}
662
663
/************************************************************************/
664
/*                           RegisterOGRGeoJSON()                       */
665
/************************************************************************/
666
667
void RegisterOGRGeoJSON()
668
0
{
669
0
    if (!GDAL_CHECK_VERSION("OGR/GeoJSON driver"))
670
0
        return;
671
672
0
    if (GDALGetDriverByName("GeoJSON") != nullptr)
673
0
        return;
674
675
0
    GDALDriver *poDriver = new GDALDriver();
676
677
0
    poDriver->SetDescription("GeoJSON");
678
0
    poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
679
0
    poDriver->SetMetadataItem(GDAL_DCAP_CREATE_LAYER, "YES");
680
0
    poDriver->SetMetadataItem(GDAL_DCAP_CREATE_FIELD, "YES");
681
0
    poDriver->SetMetadataItem(GDAL_DCAP_DELETE_FIELD, "YES");
682
0
    poDriver->SetMetadataItem(GDAL_DCAP_REORDER_FIELDS, "YES");
683
0
    poDriver->SetMetadataItem(GDAL_DMD_ALTER_FIELD_DEFN_FLAGS, "Name Type");
684
0
    poDriver->SetMetadataItem(GDAL_DCAP_Z_GEOMETRIES, "YES");
685
0
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "GeoJSON");
686
0
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "json geojson");
687
0
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
688
0
                              "drivers/vector/geojson.html");
689
690
0
    poDriver->SetMetadataItem(
691
0
        GDAL_DMD_OPENOPTIONLIST,
692
0
        "<OpenOptionList>"
693
0
        "  <Option name='FLATTEN_NESTED_ATTRIBUTES' type='boolean' "
694
0
        "description='Whether to recursively explore nested objects and "
695
0
        "produce flatten OGR attributes' default='NO'/>"
696
0
        "  <Option name='NESTED_ATTRIBUTE_SEPARATOR' type='string' "
697
0
        "description='Separator between components of nested attributes' "
698
0
        "default='_'/>"
699
0
        "  <Option name='FEATURE_SERVER_PAGING' type='boolean' "
700
0
        "description='Whether to automatically scroll through results with a "
701
0
        "ArcGIS Feature Service endpoint'/>"
702
0
        "  <Option name='NATIVE_DATA' type='boolean' description='Whether to "
703
0
        "store the native JSon representation at FeatureCollection and Feature "
704
0
        "level' default='NO'/>"
705
0
        "  <Option name='ARRAY_AS_STRING' type='boolean' description='Whether "
706
0
        "to expose JSon arrays of strings, integers or reals as a OGR String' "
707
0
        "default='NO'/>"
708
0
        "  <Option name='DATE_AS_STRING' type='boolean' description='Whether "
709
0
        "to expose date/time/date-time content using dedicated OGR "
710
0
        "date/time/date-time types or as a OGR String' default='NO'/>"
711
0
        "  <Option name='FOREIGN_MEMBERS' type='string-select' "
712
0
        "description='Whether and how foreign members at the feature level "
713
0
        "should be processed as OGR fields' default='AUTO'>"
714
0
        "    <Value>AUTO</Value>"
715
0
        "    <Value>ALL</Value>"
716
0
        "    <Value>NONE</Value>"
717
0
        "    <Value>STAC</Value>"
718
0
        "  </Option>"
719
0
        "  <Option name='OGR_SCHEMA' type='string' description='"
720
0
        "Partially or totally overrides the auto-detected schema to use for "
721
0
        "creating the layer. "
722
0
        "The overrides are defined as a JSON list of field definitions. "
723
0
        "This can be a filename or a JSON string or a URL.'/>"
724
0
        "</OpenOptionList>");
725
726
0
    poDriver->SetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST,
727
0
                              "<CreationOptionList/>");
728
729
0
    poDriver->SetMetadataItem(
730
0
        GDAL_DS_LAYER_CREATIONOPTIONLIST,
731
0
        "<LayerCreationOptionList>"
732
0
        "  <Option name='WRITE_BBOX' type='boolean' description='whether to "
733
0
        "write a bbox property with the bounding box of the geometries at the "
734
0
        "feature and feature collection level' default='NO'/>"
735
0
        "  <Option name='COORDINATE_PRECISION' type='int' description='Number "
736
0
        "of decimal for coordinates. Default is 15 for GJ2008 and 7 for "
737
0
        "RFC7946'/>"
738
0
        "  <Option name='SIGNIFICANT_FIGURES' type='int' description='Number "
739
0
        "of significant figures for floating-point values' default='17'/>"
740
0
        "  <Option name='NATIVE_DATA' type='string' "
741
0
        "description='FeatureCollection level elements.'/>"
742
0
        "  <Option name='NATIVE_MEDIA_TYPE' type='string' description='Format "
743
0
        "of NATIVE_DATA. Must be \"application/vnd.geo+json\", otherwise "
744
0
        "NATIVE_DATA will be ignored.'/>"
745
0
        "  <Option name='RFC7946' type='boolean' description='Whether to use "
746
0
        "RFC 7946 standard. Otherwise GeoJSON 2008 initial version will be "
747
0
        "used' default='NO'/>"
748
0
        "  <Option name='WRAPDATELINE' type='boolean' description='Whether to "
749
0
        "apply heuristics to split geometries that cross dateline.' "
750
0
        "default='YES'/>"
751
0
        "  <Option name='WRITE_NAME' type='boolean' description='Whether to "
752
0
        "write a &quot;name&quot; property at feature collection level with "
753
0
        "layer name' default='YES'/>"
754
0
        "  <Option name='DESCRIPTION' type='string' description='(Long) "
755
0
        "description to write in a &quot;description&quot; property at feature "
756
0
        "collection level'/>"
757
0
        "  <Option name='ID_FIELD' type='string' description='Name of the "
758
0
        "source field that must be used as the id member of Feature features'/>"
759
0
        "  <Option name='ID_TYPE' type='string-select' description='Type of "
760
0
        "the id member of Feature features'>"
761
0
        "    <Value>AUTO</Value>"
762
0
        "    <Value>String</Value>"
763
0
        "    <Value>Integer</Value>"
764
0
        "  </Option>"
765
0
        "  <Option name='ID_GENERATE' type='boolean' "
766
0
        "description='Auto-generate feature ids' />"
767
0
        "  <Option name='WRITE_NON_FINITE_VALUES' type='boolean' "
768
0
        "description='Whether to write NaN / Infinity values' default='NO'/>"
769
0
        "  <Option name='AUTODETECT_JSON_STRINGS' type='boolean' "
770
0
        "description='Whether to try to interpret string fields as JSON "
771
0
        "arrays or objects' default='YES'/>"
772
0
        "  <Option name='FOREIGN_MEMBERS_FEATURE' type='string' "
773
0
        "description='Extra JSON content to add in each feature as a foreign "
774
0
        "members'/>"
775
0
        "  <Option name='FOREIGN_MEMBERS_COLLECTION' type='string' "
776
0
        "description='Extra JSON content to add to the feature collection as "
777
0
        "a foreign members'/>"
778
0
        "</LayerCreationOptionList>");
779
780
0
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
781
0
    poDriver->SetMetadataItem(
782
0
        GDAL_DMD_CREATIONFIELDDATATYPES,
783
0
        "Integer Integer64 Real String IntegerList "
784
0
        "Integer64List RealList StringList Date DateTime");
785
0
    poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATASUBTYPES, "Boolean");
786
0
    poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "OGRSQL SQLITE");
787
788
0
    poDriver->SetMetadataItem(GDAL_DCAP_FLUSHCACHE_CONSISTENT_STATE, "YES");
789
0
    poDriver->SetMetadataItem(GDAL_DCAP_HONOR_GEOM_COORDINATE_PRECISION, "YES");
790
791
0
    poDriver->SetMetadataItem(GDAL_DCAP_UPDATE, "YES");
792
0
    poDriver->SetMetadataItem(GDAL_DMD_UPDATE_ITEMS, "Features");
793
794
0
    poDriver->pfnOpen = OGRGeoJSONDriverOpen;
795
0
    poDriver->pfnIdentify = OGRGeoJSONDriverIdentify;
796
0
    poDriver->pfnCreate = OGRGeoJSONDriverCreate;
797
0
    poDriver->pfnDelete = OGRGeoJSONDriverDelete;
798
0
    poDriver->pfnUnloadDriver = OGRGeoJSONDriverUnload;
799
800
0
    GetGDALDriverManager()->RegisterDriver(poDriver);
801
802
#ifdef BUILT_AS_PLUGIN
803
    RegisterOGRTopoJSON();
804
    RegisterOGRESRIJSON();
805
    RegisterOGRGeoJSONSeq();
806
#endif
807
0
}