Coverage Report

Created: 2026-07-25 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsondatasource.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  OpenGIS Simple Features Reference Implementation
4
 * Purpose:  Implementation of OGRGeoJSONDataSource class (OGR GeoJSON Driver).
5
 * Author:   Mateusz Loskot, mateusz@loskot.net
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2007, Mateusz Loskot
9
 * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "cpl_port.h"
15
#include "ogr_geojson.h"
16
17
#include <cmath>
18
#include <cstddef>
19
#include <cstdlib>
20
#include <cstring>
21
#include <string>
22
23
#include "cpl_conv.h"
24
#include "cpl_error.h"
25
#include "cpl_http.h"
26
#include "cpl_multiproc.h"  // CPLSleep()
27
#include "cpl_string.h"
28
#include "cpl_vsi.h"
29
#include "cpl_vsi_error.h"
30
#include "json.h"
31
// #include "json_object.h"
32
#include "gdal_utils.h"
33
#include "gdal.h"
34
#include "gdal_priv.h"
35
#include "ogr_core.h"
36
#include "ogr_feature.h"
37
#include "ogr_geometry.h"
38
#include "ogr_spatialref.h"
39
#include "ogrlibjsonutils.h"
40
#include "ogrgeojsonreader.h"
41
#include "ogrgeojsonutils.h"
42
#include "ogrgeojsonwriter.h"
43
#include "ogrsf_frmts.h"
44
#include "ogr_schema_override.h"
45
#include "ogr_p.h"
46
47
// #include "symbol_renames.h"
48
49
/************************************************************************/
50
/*                        OGRGeoJSONDataSource()                        */
51
/************************************************************************/
52
53
OGRGeoJSONDataSource::OGRGeoJSONDataSource()
54
0
    : pszName_(nullptr), pszGeoData_(nullptr), nGeoDataLen_(0),
55
0
      papoLayers_(nullptr), papoLayersWriter_(nullptr), nLayers_(0),
56
0
      fpOut_(nullptr), flTransGeom_(OGRGeoJSONDataSource::eGeometryPreserve),
57
0
      flTransAttrs_(OGRGeoJSONDataSource::eAttributesPreserve),
58
0
      bOtherPages_(false), bFpOutputIsSeekable_(false), nBBOXInsertLocation_(0),
59
0
      bUpdatable_(false)
60
0
{
61
0
}
62
63
/************************************************************************/
64
/*                       ~OGRGeoJSONDataSource()                        */
65
/************************************************************************/
66
67
OGRGeoJSONDataSource::~OGRGeoJSONDataSource()
68
0
{
69
0
    OGRGeoJSONDataSource::Close();
70
0
}
71
72
/************************************************************************/
73
/*                               Close()                                */
74
/************************************************************************/
75
76
CPLErr OGRGeoJSONDataSource::Close(GDALProgressFunc, void *)
77
0
{
78
0
    CPLErr eErr = CE_None;
79
0
    if (nOpenFlags != OPEN_FLAGS_CLOSED)
80
0
    {
81
0
        if (OGRGeoJSONDataSource::FlushCache(true) != CE_None)
82
0
            eErr = CE_Failure;
83
84
0
        if (!OGRGeoJSONDataSource::Clear())
85
0
            eErr = CE_Failure;
86
87
0
        if (GDALDataset::Close() != CE_None)
88
0
            eErr = CE_Failure;
89
0
    }
90
0
    return eErr;
91
0
}
92
93
/************************************************************************/
94
/*                    DealWithOgrSchemaOpenOption()                     */
95
/************************************************************************/
96
97
bool OGRGeoJSONDataSource::DealWithOgrSchemaOpenOption(
98
    const GDALOpenInfo *poOpenInfo)
99
0
{
100
0
    const std::string osFieldsSchemaOverrideParam =
101
0
        CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "OGR_SCHEMA", "");
102
103
0
    if (!osFieldsSchemaOverrideParam.empty())
104
0
    {
105
106
0
        if (poOpenInfo->eAccess == GA_Update)
107
0
        {
108
0
            CPLError(CE_Failure, CPLE_NotSupported,
109
0
                     "OGR_SCHEMA open option is not supported in update mode.");
110
0
            return false;
111
0
        }
112
113
0
        OGRSchemaOverride oSchemaOverride;
114
0
        const auto nErrorCount = CPLGetErrorCounter();
115
0
        if (!oSchemaOverride.LoadFromJSON(osFieldsSchemaOverrideParam) ||
116
0
            !oSchemaOverride.IsValid())
117
0
        {
118
0
            if (nErrorCount == CPLGetErrorCounter())
119
0
            {
120
0
                CPLError(CE_Failure, CPLE_AppDefined,
121
0
                         "Content of OGR_SCHEMA in %s is not valid",
122
0
                         osFieldsSchemaOverrideParam.c_str());
123
0
            }
124
0
            return false;
125
0
        }
126
127
0
        if (!oSchemaOverride.DefaultApply(this, "GeoJSON"))
128
0
            return false;
129
0
    }
130
0
    return true;
131
0
}
132
133
/************************************************************************/
134
/*                                Open()                                */
135
/************************************************************************/
136
137
int OGRGeoJSONDataSource::Open(GDALOpenInfo *poOpenInfo,
138
                               GeoJSONSourceType nSrcType,
139
                               const char *pszJSonFlavor)
140
0
{
141
0
    osJSonFlavor_ = pszJSonFlavor;
142
143
0
    const char *pszUnprefixed = poOpenInfo->pszFilename;
144
0
    if (STARTS_WITH_CI(pszUnprefixed, pszJSonFlavor) &&
145
0
        pszUnprefixed[strlen(pszJSonFlavor)] == ':')
146
0
    {
147
0
        pszUnprefixed += strlen(pszJSonFlavor) + 1;
148
0
    }
149
150
0
    if (eGeoJSONSourceService == nSrcType)
151
0
    {
152
0
        if (!ReadFromService(poOpenInfo, pszUnprefixed))
153
0
            return FALSE;
154
0
        if (poOpenInfo->eAccess == GA_Update)
155
0
        {
156
0
            CPLError(CE_Failure, CPLE_NotSupported,
157
0
                     "Update from remote service not supported");
158
0
            return FALSE;
159
0
        }
160
0
    }
161
0
    else if (eGeoJSONSourceText == nSrcType)
162
0
    {
163
0
        if (poOpenInfo->eAccess == GA_Update)
164
0
        {
165
0
            CPLError(CE_Failure, CPLE_NotSupported,
166
0
                     "Update from inline definition not supported");
167
0
            return FALSE;
168
0
        }
169
0
        pszGeoData_ = CPLStrdup(pszUnprefixed);
170
0
    }
171
0
    else if (eGeoJSONSourceFile == nSrcType)
172
0
    {
173
0
        if (poOpenInfo->eAccess == GA_Update &&
174
0
            !EQUAL(pszJSonFlavor, "GeoJSON"))
175
0
        {
176
0
            CPLError(CE_Failure, CPLE_NotSupported,
177
0
                     "Update of %s not supported", pszJSonFlavor);
178
0
            return FALSE;
179
0
        }
180
0
        pszName_ = CPLStrdup(pszUnprefixed);
181
0
        bUpdatable_ = (poOpenInfo->eAccess == GA_Update);
182
183
0
        if (!EQUAL(pszUnprefixed, poOpenInfo->pszFilename))
184
0
        {
185
0
            GDALOpenInfo oOpenInfo(pszUnprefixed, GA_ReadOnly);
186
0
            if (oOpenInfo.fpL == nullptr || oOpenInfo.pabyHeader == nullptr)
187
0
                return FALSE;
188
0
            pszGeoData_ =
189
0
                CPLStrdup(reinterpret_cast<const char *>(oOpenInfo.pabyHeader));
190
0
        }
191
0
        else if (poOpenInfo->fpL == nullptr)
192
0
            return FALSE;
193
0
        else
194
0
        {
195
0
            pszGeoData_ = CPLStrdup(
196
0
                reinterpret_cast<const char *>(poOpenInfo->pabyHeader));
197
0
        }
198
0
    }
199
0
    else
200
0
    {
201
0
        Clear();
202
0
        return FALSE;
203
0
    }
204
205
    /* -------------------------------------------------------------------- */
206
    /*      Construct OGR layer and feature objects from                    */
207
    /*      GeoJSON text tree.                                              */
208
    /* -------------------------------------------------------------------- */
209
0
    if (nullptr == pszGeoData_ ||
210
0
        STARTS_WITH(pszGeoData_, "{\"couchdb\":\"Welcome\"") ||
211
0
        STARTS_WITH(pszGeoData_, "{\"db_name\":\"") ||
212
0
        STARTS_WITH(pszGeoData_, "{\"total_rows\":") ||
213
0
        STARTS_WITH(pszGeoData_, "{\"rows\":["))
214
0
    {
215
0
        Clear();
216
0
        return FALSE;
217
0
    }
218
219
0
    SetDescription(poOpenInfo->pszFilename);
220
0
    LoadLayers(poOpenInfo, nSrcType, pszUnprefixed, pszJSonFlavor);
221
222
0
    if (!DealWithOgrSchemaOpenOption(poOpenInfo))
223
0
    {
224
0
        Clear();
225
0
        return FALSE;
226
0
    }
227
228
0
    if (nLayers_ == 0)
229
0
    {
230
0
        bool bEmitError = true;
231
0
        if (eGeoJSONSourceService == nSrcType)
232
0
        {
233
0
            const CPLString osTmpFilename =
234
0
                VSIMemGenerateHiddenFilename(CPLSPrintf(
235
0
                    "geojson_%s", CPLGetFilename(poOpenInfo->pszFilename)));
236
0
            VSIFCloseL(VSIFileFromMemBuffer(osTmpFilename, (GByte *)pszGeoData_,
237
0
                                            nGeoDataLen_, TRUE));
238
0
            pszGeoData_ = nullptr;
239
0
            if (GDALIdentifyDriver(osTmpFilename, nullptr))
240
0
                bEmitError = false;
241
0
            VSIUnlink(osTmpFilename);
242
0
        }
243
0
        Clear();
244
245
0
        if (bEmitError)
246
0
        {
247
0
            CPLError(CE_Failure, CPLE_OpenFailed, "Failed to read %s data",
248
0
                     pszJSonFlavor);
249
0
        }
250
0
        return FALSE;
251
0
    }
252
253
0
    return TRUE;
254
0
}
255
256
/************************************************************************/
257
/*                           GetLayerCount()                            */
258
/************************************************************************/
259
260
int OGRGeoJSONDataSource::GetLayerCount() const
261
0
{
262
0
    return nLayers_;
263
0
}
264
265
/************************************************************************/
266
/*                              GetLayer()                              */
267
/************************************************************************/
268
269
const OGRLayer *OGRGeoJSONDataSource::GetLayer(int nLayer) const
270
0
{
271
0
    if (0 <= nLayer && nLayer < nLayers_)
272
0
    {
273
0
        if (papoLayers_)
274
0
            return papoLayers_[nLayer];
275
0
        else
276
0
            return papoLayersWriter_[nLayer];
277
0
    }
278
279
0
    return nullptr;
280
0
}
281
282
/************************************************************************/
283
/*                            ICreateLayer()                            */
284
/************************************************************************/
285
286
OGRLayer *
287
OGRGeoJSONDataSource::ICreateLayer(const char *pszNameIn,
288
                                   const OGRGeomFieldDefn *poSrcGeomFieldDefn,
289
                                   CSLConstList papszOptions)
290
0
{
291
0
    if (nullptr == fpOut_)
292
0
    {
293
0
        CPLError(CE_Failure, CPLE_NotSupported,
294
0
                 "GeoJSON driver doesn't support creating a layer "
295
0
                 "on a read-only datasource");
296
0
        return nullptr;
297
0
    }
298
299
0
    if (nLayers_ != 0)
300
0
    {
301
0
        CPLError(CE_Failure, CPLE_NotSupported,
302
0
                 "GeoJSON driver doesn't support creating more than one layer");
303
0
        return nullptr;
304
0
    }
305
306
0
    const auto eGType =
307
0
        poSrcGeomFieldDefn ? poSrcGeomFieldDefn->GetType() : wkbNone;
308
0
    const auto poSRS =
309
0
        poSrcGeomFieldDefn ? poSrcGeomFieldDefn->GetSpatialRef() : nullptr;
310
311
0
    const char *pszForeignMembersCollection =
312
0
        CSLFetchNameValue(papszOptions, "FOREIGN_MEMBERS_COLLECTION");
313
0
    if (pszForeignMembersCollection)
314
0
    {
315
0
        if (pszForeignMembersCollection[0] != '{' ||
316
0
            pszForeignMembersCollection[strlen(pszForeignMembersCollection) -
317
0
                                        1] != '}')
318
0
        {
319
0
            CPLError(CE_Failure, CPLE_AppDefined,
320
0
                     "Value of FOREIGN_MEMBERS_COLLECTION should start with { "
321
0
                     "and end with }");
322
0
            return nullptr;
323
0
        }
324
0
        json_object *poTmp = nullptr;
325
0
        if (!OGRJSonParse(pszForeignMembersCollection, &poTmp, false))
326
0
        {
327
0
            pszForeignMembersCollection = nullptr;
328
0
        }
329
0
        json_object_put(poTmp);
330
0
        if (!pszForeignMembersCollection)
331
0
        {
332
0
            CPLError(CE_Failure, CPLE_AppDefined,
333
0
                     "Value of FOREIGN_MEMBERS_COLLECTION is invalid JSON");
334
0
            return nullptr;
335
0
        }
336
0
    }
337
338
0
    std::string osForeignMembersFeature =
339
0
        CSLFetchNameValueDef(papszOptions, "FOREIGN_MEMBERS_FEATURE", "");
340
0
    if (!osForeignMembersFeature.empty())
341
0
    {
342
0
        if (osForeignMembersFeature.front() != '{' ||
343
0
            osForeignMembersFeature.back() != '}')
344
0
        {
345
0
            CPLError(CE_Failure, CPLE_AppDefined,
346
0
                     "Value of FOREIGN_MEMBERS_FEATURE should start with { and "
347
0
                     "end with }");
348
0
            return nullptr;
349
0
        }
350
0
        json_object *poTmp = nullptr;
351
0
        if (!OGRJSonParse(osForeignMembersFeature.c_str(), &poTmp, false))
352
0
        {
353
0
            osForeignMembersFeature.clear();
354
0
        }
355
0
        json_object_put(poTmp);
356
0
        if (osForeignMembersFeature.empty())
357
0
        {
358
0
            CPLError(CE_Failure, CPLE_AppDefined,
359
0
                     "Value of FOREIGN_MEMBERS_FEATURE is invalid JSON");
360
0
            return nullptr;
361
0
        }
362
0
    }
363
364
0
    VSIFPrintfL(fpOut_, "{\n\"type\": \"FeatureCollection\",\n");
365
366
0
    if (pszForeignMembersCollection)
367
0
    {
368
0
        VSIFWriteL(pszForeignMembersCollection + 1, 1,
369
0
                   strlen(pszForeignMembersCollection) - 2, fpOut_);
370
0
        VSIFWriteL(",\n", 2, 1, fpOut_);
371
0
    }
372
373
0
    bool bWriteFC_BBOX =
374
0
        CPLTestBool(CSLFetchNameValueDef(papszOptions, "WRITE_BBOX", "FALSE"));
375
376
0
    const bool bRFC7946 =
377
0
        CPLTestBool(CSLFetchNameValueDef(papszOptions, "RFC7946", "FALSE"));
378
379
0
    const char *pszNativeData = CSLFetchNameValue(papszOptions, "NATIVE_DATA");
380
0
    const char *pszNativeMediaType =
381
0
        CSLFetchNameValue(papszOptions, "NATIVE_MEDIA_TYPE");
382
0
    bool bWriteCRSIfWGS84 = true;
383
0
    bool bFoundNameInNativeData = false;
384
0
    if (pszNativeData && pszNativeMediaType &&
385
0
        OGRIsGeoJSONMediaType(pszNativeMediaType))
386
0
    {
387
0
        json_object *poObj = nullptr;
388
0
        if (OGRJSonParse(pszNativeData, &poObj) &&
389
0
            json_object_get_type(poObj) == json_type_object)
390
0
        {
391
0
            json_object_iter it;
392
0
            it.key = nullptr;
393
0
            it.val = nullptr;
394
0
            it.entry = nullptr;
395
0
            CPLString osNativeData;
396
0
            bWriteCRSIfWGS84 = false;
397
0
            json_object_object_foreachC(poObj, it)
398
0
            {
399
0
                if (strcmp(it.key, "type") == 0 ||
400
0
                    strcmp(it.key, "features") == 0)
401
0
                {
402
0
                    continue;
403
0
                }
404
0
                if (strcmp(it.key, "bbox") == 0)
405
0
                {
406
0
                    if (CSLFetchNameValue(papszOptions, "WRITE_BBOX") ==
407
0
                        nullptr)
408
0
                        bWriteFC_BBOX = true;
409
0
                    continue;
410
0
                }
411
0
                if (strcmp(it.key, "crs") == 0)
412
0
                {
413
0
                    if (!bRFC7946)
414
0
                        bWriteCRSIfWGS84 = true;
415
0
                    continue;
416
0
                }
417
                // See https://tools.ietf.org/html/rfc7946#section-7.1
418
0
                if (bRFC7946 && (strcmp(it.key, "coordinates") == 0 ||
419
0
                                 strcmp(it.key, "geometries") == 0 ||
420
0
                                 strcmp(it.key, "geometry") == 0 ||
421
0
                                 strcmp(it.key, "properties") == 0))
422
0
                {
423
0
                    continue;
424
0
                }
425
426
0
                if (strcmp(it.key, "name") == 0)
427
0
                {
428
0
                    bFoundNameInNativeData = true;
429
0
                    if (!CPLFetchBool(papszOptions, "WRITE_NAME", true) ||
430
0
                        CSLFetchNameValue(papszOptions, "@NAME") != nullptr)
431
0
                    {
432
0
                        continue;
433
0
                    }
434
0
                }
435
436
                // If a native description exists, ignore it if an explicit
437
                // DESCRIPTION option has been provided.
438
0
                if (strcmp(it.key, "description") == 0 &&
439
0
                    CSLFetchNameValue(papszOptions, "DESCRIPTION"))
440
0
                {
441
0
                    continue;
442
0
                }
443
444
0
                if (strcmp(it.key, "xy_coordinate_resolution") == 0 ||
445
0
                    strcmp(it.key, "z_coordinate_resolution") == 0)
446
0
                {
447
0
                    continue;
448
0
                }
449
450
0
                json_object *poKey = json_object_new_string(it.key);
451
0
                VSIFPrintfL(fpOut_, "%s: ", json_object_to_json_string(poKey));
452
0
                json_object_put(poKey);
453
0
                VSIFPrintfL(fpOut_, "%s,\n",
454
0
                            json_object_to_json_string(it.val));
455
0
            }
456
0
            json_object_put(poObj);
457
0
        }
458
0
    }
459
460
    // Used by ogr2ogr in -nln mode
461
0
    const char *pszAtName = CSLFetchNameValue(papszOptions, "@NAME");
462
0
    if (pszAtName && CPLFetchBool(papszOptions, "WRITE_NAME", true))
463
0
    {
464
0
        json_object *poName = json_object_new_string(pszAtName);
465
0
        VSIFPrintfL(fpOut_, "\"name\": %s,\n",
466
0
                    json_object_to_json_string(poName));
467
0
        json_object_put(poName);
468
0
    }
469
0
    else if (!bFoundNameInNativeData &&
470
0
             CPLFetchBool(papszOptions, "WRITE_NAME", true) &&
471
0
             !EQUAL(pszNameIn, OGRGeoJSONLayer::DefaultName) &&
472
0
             !EQUAL(pszNameIn, ""))
473
0
    {
474
0
        json_object *poName = json_object_new_string(pszNameIn);
475
0
        VSIFPrintfL(fpOut_, "\"name\": %s,\n",
476
0
                    json_object_to_json_string(poName));
477
0
        json_object_put(poName);
478
0
    }
479
480
0
    const char *pszDescription = CSLFetchNameValue(papszOptions, "DESCRIPTION");
481
0
    if (pszDescription)
482
0
    {
483
0
        json_object *poDesc = json_object_new_string(pszDescription);
484
0
        VSIFPrintfL(fpOut_, "\"description\": %s,\n",
485
0
                    json_object_to_json_string(poDesc));
486
0
        json_object_put(poDesc);
487
0
    }
488
489
0
    OGRCoordinateTransformation *poCT = nullptr;
490
0
    if (bRFC7946)
491
0
    {
492
0
        if (poSRS == nullptr)
493
0
        {
494
0
            CPLError(CE_Warning, CPLE_AppDefined,
495
0
                     "No SRS set on layer. Assuming it is long/lat on WGS84 "
496
0
                     "ellipsoid");
497
0
        }
498
0
        else if (poSRS->GetAxesCount() == 3)
499
0
        {
500
0
            OGRSpatialReference oSRS_EPSG_4979;
501
0
            oSRS_EPSG_4979.importFromEPSG(4979);
502
0
            oSRS_EPSG_4979.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
503
0
            if (!poSRS->IsSame(&oSRS_EPSG_4979))
504
0
            {
505
0
                poCT =
506
0
                    OGRCreateCoordinateTransformation(poSRS, &oSRS_EPSG_4979);
507
0
                if (poCT == nullptr)
508
0
                {
509
0
                    CPLError(CE_Warning, CPLE_AppDefined,
510
0
                             "Failed to create coordinate transformation "
511
0
                             "between the "
512
0
                             "input coordinate system and WGS84.");
513
514
0
                    return nullptr;
515
0
                }
516
0
            }
517
0
        }
518
0
        else
519
0
        {
520
0
            OGRSpatialReference oSRSWGS84;
521
0
            oSRSWGS84.SetWellKnownGeogCS("WGS84");
522
0
            oSRSWGS84.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
523
0
            if (!poSRS->IsSame(&oSRSWGS84))
524
0
            {
525
0
                poCT = OGRCreateCoordinateTransformation(poSRS, &oSRSWGS84);
526
0
                if (poCT == nullptr)
527
0
                {
528
0
                    CPLError(CE_Warning, CPLE_AppDefined,
529
0
                             "Failed to create coordinate transformation "
530
0
                             "between the "
531
0
                             "input coordinate system and WGS84.");
532
533
0
                    return nullptr;
534
0
                }
535
0
            }
536
0
        }
537
0
    }
538
0
    else if (poSRS)
539
0
    {
540
0
        char *pszOGCURN = poSRS->GetOGCURN();
541
0
        if (pszOGCURN != nullptr &&
542
0
            (bWriteCRSIfWGS84 ||
543
0
             !EQUAL(pszOGCURN, "urn:ogc:def:crs:EPSG::4326")))
544
0
        {
545
0
            json_object *poObjCRS = json_object_new_object();
546
0
            json_object_object_add(poObjCRS, "type",
547
0
                                   json_object_new_string("name"));
548
0
            json_object *poObjProperties = json_object_new_object();
549
0
            json_object_object_add(poObjCRS, "properties", poObjProperties);
550
551
0
            if (EQUAL(pszOGCURN, "urn:ogc:def:crs:EPSG::4326"))
552
0
            {
553
0
                json_object_object_add(
554
0
                    poObjProperties, "name",
555
0
                    json_object_new_string("urn:ogc:def:crs:OGC:1.3:CRS84"));
556
0
            }
557
0
            else
558
0
            {
559
0
                json_object_object_add(poObjProperties, "name",
560
0
                                       json_object_new_string(pszOGCURN));
561
0
            }
562
563
0
            const char *pszCRS = json_object_to_json_string(poObjCRS);
564
0
            VSIFPrintfL(fpOut_, "\"crs\": %s,\n", pszCRS);
565
566
0
            json_object_put(poObjCRS);
567
0
        }
568
0
        CPLFree(pszOGCURN);
569
0
    }
570
571
0
    CPLStringList aosOptions(papszOptions);
572
573
0
    double dfXYResolution = OGRGeomCoordinatePrecision::UNKNOWN;
574
0
    double dfZResolution = OGRGeomCoordinatePrecision::UNKNOWN;
575
576
0
    if (const char *pszCoordPrecision =
577
0
            CSLFetchNameValue(papszOptions, "COORDINATE_PRECISION"))
578
0
    {
579
0
        dfXYResolution = std::pow(10.0, -CPLAtof(pszCoordPrecision));
580
0
        dfZResolution = dfXYResolution;
581
0
        VSIFPrintfL(fpOut_, "\"xy_coordinate_resolution\": %g,\n",
582
0
                    dfXYResolution);
583
0
        if (poSRS && poSRS->GetAxesCount() == 3)
584
0
        {
585
0
            VSIFPrintfL(fpOut_, "\"z_coordinate_resolution\": %g,\n",
586
0
                        dfZResolution);
587
0
        }
588
0
    }
589
0
    else if (poSrcGeomFieldDefn)
590
0
    {
591
0
        const auto &oCoordPrec = poSrcGeomFieldDefn->GetCoordinatePrecision();
592
0
        OGRSpatialReference oSRSWGS84;
593
0
        oSRSWGS84.SetWellKnownGeogCS("WGS84");
594
0
        const auto oCoordPrecWGS84 =
595
0
            oCoordPrec.ConvertToOtherSRS(poSRS, &oSRSWGS84);
596
597
0
        if (oCoordPrec.dfXYResolution != OGRGeomCoordinatePrecision::UNKNOWN)
598
0
        {
599
0
            dfXYResolution = poSRS && bRFC7946 ? oCoordPrecWGS84.dfXYResolution
600
0
                                               : oCoordPrec.dfXYResolution;
601
602
0
            aosOptions.SetNameValue(
603
0
                "XY_COORD_PRECISION",
604
0
                CPLSPrintf("%d",
605
0
                           OGRGeomCoordinatePrecision::ResolutionToPrecision(
606
0
                               dfXYResolution)));
607
0
            VSIFPrintfL(fpOut_, "\"xy_coordinate_resolution\": %g,\n",
608
0
                        dfXYResolution);
609
0
        }
610
0
        if (oCoordPrec.dfZResolution != OGRGeomCoordinatePrecision::UNKNOWN)
611
0
        {
612
0
            dfZResolution = poSRS && bRFC7946 ? oCoordPrecWGS84.dfZResolution
613
0
                                              : oCoordPrec.dfZResolution;
614
615
0
            aosOptions.SetNameValue(
616
0
                "Z_COORD_PRECISION",
617
0
                CPLSPrintf("%d",
618
0
                           OGRGeomCoordinatePrecision::ResolutionToPrecision(
619
0
                               dfZResolution)));
620
0
            VSIFPrintfL(fpOut_, "\"z_coordinate_resolution\": %g,\n",
621
0
                        dfZResolution);
622
0
        }
623
0
    }
624
625
0
    if (bFpOutputIsSeekable_ && bWriteFC_BBOX)
626
0
    {
627
0
        nBBOXInsertLocation_ = static_cast<int>(VSIFTellL(fpOut_));
628
629
0
        const std::string osSpaceForBBOX(SPACE_FOR_BBOX + 1, ' ');
630
0
        VSIFPrintfL(fpOut_, "%s\n", osSpaceForBBOX.c_str());
631
0
    }
632
633
0
    VSIFPrintfL(fpOut_, "\"features\": [\n");
634
635
0
    OGRGeoJSONWriteLayer *poLayer = new OGRGeoJSONWriteLayer(
636
0
        pszNameIn, eGType, aosOptions.List(), bWriteFC_BBOX, poCT, this);
637
638
0
    if (eGType != wkbNone &&
639
0
        dfXYResolution != OGRGeomCoordinatePrecision::UNKNOWN)
640
0
    {
641
0
        auto poGeomFieldDefn = poLayer->GetLayerDefn()->GetGeomFieldDefn(0);
642
0
        OGRGeomCoordinatePrecision oCoordPrec(
643
0
            poGeomFieldDefn->GetCoordinatePrecision());
644
0
        oCoordPrec.dfXYResolution = dfXYResolution;
645
0
        poGeomFieldDefn->SetCoordinatePrecision(oCoordPrec);
646
0
    }
647
648
0
    if (eGType != wkbNone &&
649
0
        dfZResolution != OGRGeomCoordinatePrecision::UNKNOWN)
650
0
    {
651
0
        auto poGeomFieldDefn = poLayer->GetLayerDefn()->GetGeomFieldDefn(0);
652
0
        OGRGeomCoordinatePrecision oCoordPrec(
653
0
            poGeomFieldDefn->GetCoordinatePrecision());
654
0
        oCoordPrec.dfZResolution = dfZResolution;
655
0
        poGeomFieldDefn->SetCoordinatePrecision(oCoordPrec);
656
0
    }
657
658
    /* -------------------------------------------------------------------- */
659
    /*      Add layer to data source layer list.                            */
660
    /* -------------------------------------------------------------------- */
661
0
    CPLAssert(papoLayers_ == nullptr);
662
0
    papoLayersWriter_ = static_cast<OGRGeoJSONWriteLayer **>(CPLRealloc(
663
0
        papoLayers_, sizeof(OGRGeoJSONWriteLayer *) * (nLayers_ + 1)));
664
665
0
    papoLayersWriter_[nLayers_++] = poLayer;
666
667
0
    return poLayer;
668
0
}
669
670
/************************************************************************/
671
/*                           TestCapability()                           */
672
/************************************************************************/
673
674
int OGRGeoJSONDataSource::TestCapability(const char *pszCap) const
675
0
{
676
0
    if (EQUAL(pszCap, ODsCCreateLayer))
677
0
        return fpOut_ != nullptr && nLayers_ == 0;
678
0
    else if (EQUAL(pszCap, ODsCMeasuredGeometries))
679
0
        return m_bSupportsMGeometries;
680
0
    else if (EQUAL(pszCap, ODsCZGeometries))
681
0
        return m_bSupportsZGeometries;
682
683
0
    return FALSE;
684
0
}
685
686
/************************************************************************/
687
/*                               Create()                               */
688
/************************************************************************/
689
690
int OGRGeoJSONDataSource::Create(const char *pszName,
691
                                 CSLConstList /* papszOptions */)
692
0
{
693
0
    CPLAssert(nullptr == fpOut_);
694
695
0
    if (strcmp(pszName, "/dev/stdout") == 0)
696
0
        pszName = "/vsistdout/";
697
698
0
    bFpOutputIsSeekable_ = !(strcmp(pszName, "/vsistdout/") == 0 ||
699
0
                             STARTS_WITH(pszName, "/vsigzip/") ||
700
0
                             STARTS_WITH(pszName, "/vsizip/"));
701
702
    /* -------------------------------------------------------------------- */
703
    /*     File overwrite not supported.                                    */
704
    /* -------------------------------------------------------------------- */
705
0
    VSIStatBufL sStatBuf;
706
0
    if (0 == VSIStatL(pszName, &sStatBuf))
707
0
    {
708
0
        CPLError(CE_Failure, CPLE_NotSupported,
709
0
                 "The GeoJSON driver does not overwrite existing files.");
710
0
        return FALSE;
711
0
    }
712
713
    /* -------------------------------------------------------------------- */
714
    /*      Create the output file.                                         */
715
    /* -------------------------------------------------------------------- */
716
0
    fpOut_ = VSIFOpenExL(pszName, "w", true);
717
0
    if (nullptr == fpOut_)
718
0
    {
719
0
        CPLError(CE_Failure, CPLE_OpenFailed,
720
0
                 "Failed to create GeoJSON datasource: %s: %s", pszName,
721
0
                 VSIGetLastErrorMsg());
722
0
        return FALSE;
723
0
    }
724
725
0
    pszName_ = CPLStrdup(pszName);
726
727
0
    return TRUE;
728
0
}
729
730
/************************************************************************/
731
/*                       SetGeometryTranslation()                       */
732
/************************************************************************/
733
734
void OGRGeoJSONDataSource::SetGeometryTranslation(GeometryTranslation type)
735
0
{
736
0
    flTransGeom_ = type;
737
0
}
738
739
/************************************************************************/
740
/*                      SetAttributesTranslation()                      */
741
/************************************************************************/
742
743
void OGRGeoJSONDataSource::SetAttributesTranslation(AttributesTranslation type)
744
0
{
745
0
    flTransAttrs_ = type;
746
0
}
747
748
/************************************************************************/
749
/*                   PRIVATE FUNCTIONS IMPLEMENTATION                   */
750
/************************************************************************/
751
752
bool OGRGeoJSONDataSource::Clear()
753
0
{
754
0
    for (int i = 0; i < nLayers_; i++)
755
0
    {
756
0
        if (papoLayers_ != nullptr)
757
0
            delete papoLayers_[i];
758
0
        else
759
0
            delete papoLayersWriter_[i];
760
0
    }
761
762
0
    CPLFree(papoLayers_);
763
0
    papoLayers_ = nullptr;
764
0
    CPLFree(papoLayersWriter_);
765
0
    papoLayersWriter_ = nullptr;
766
0
    nLayers_ = 0;
767
768
0
    CPLFree(pszName_);
769
0
    pszName_ = nullptr;
770
771
0
    CPLFree(pszGeoData_);
772
0
    pszGeoData_ = nullptr;
773
0
    nGeoDataLen_ = 0;
774
775
0
    bool bRet = true;
776
0
    if (fpOut_)
777
0
    {
778
0
        if (VSIFCloseL(fpOut_) != 0)
779
0
            bRet = false;
780
0
        fpOut_ = nullptr;
781
0
    }
782
0
    return bRet;
783
0
}
784
785
/************************************************************************/
786
/*                            ReadFromFile()                            */
787
/************************************************************************/
788
789
int OGRGeoJSONDataSource::ReadFromFile(GDALOpenInfo *poOpenInfo,
790
                                       const char *pszUnprefixed)
791
0
{
792
0
    GByte *pabyOut = nullptr;
793
0
    if (!EQUAL(poOpenInfo->pszFilename, pszUnprefixed))
794
0
    {
795
0
        GDALOpenInfo oOpenInfo(pszUnprefixed, GA_ReadOnly);
796
0
        if (oOpenInfo.fpL == nullptr || oOpenInfo.pabyHeader == nullptr)
797
0
            return FALSE;
798
0
        VSIFSeekL(oOpenInfo.fpL, 0, SEEK_SET);
799
0
        if (!VSIIngestFile(oOpenInfo.fpL, pszUnprefixed, &pabyOut, nullptr, -1))
800
0
        {
801
0
            return FALSE;
802
0
        }
803
0
    }
804
0
    else
805
0
    {
806
0
        if (poOpenInfo->fpL == nullptr)
807
0
            return FALSE;
808
0
        VSIFSeekL(poOpenInfo->fpL, 0, SEEK_SET);
809
0
        if (!VSIIngestFile(poOpenInfo->fpL, poOpenInfo->pszFilename, &pabyOut,
810
0
                           nullptr, -1))
811
0
        {
812
0
            return FALSE;
813
0
        }
814
815
0
        VSIFCloseL(poOpenInfo->fpL);
816
0
        poOpenInfo->fpL = nullptr;
817
0
    }
818
819
0
    CPLFree(pszGeoData_);
820
0
    pszGeoData_ = reinterpret_cast<char *>(pabyOut);
821
822
0
    CPLAssert(nullptr != pszGeoData_);
823
824
0
    return TRUE;
825
0
}
826
827
/************************************************************************/
828
/*                          ReadFromService()                           */
829
/************************************************************************/
830
831
int OGRGeoJSONDataSource::ReadFromService(GDALOpenInfo *poOpenInfo,
832
                                          const char *pszSource)
833
0
{
834
0
    CPLAssert(nullptr == pszGeoData_);
835
0
    CPLAssert(nullptr != pszSource);
836
837
0
    CPLErrorReset();
838
839
    /* -------------------------------------------------------------------- */
840
    /*      Look if we already cached the content.                          */
841
    /* -------------------------------------------------------------------- */
842
0
    char *pszStoredContent = OGRGeoJSONDriverStealStoredContent(pszSource);
843
0
    if (pszStoredContent != nullptr)
844
0
    {
845
0
        if (!EQUAL(pszStoredContent, INVALID_CONTENT_FOR_JSON_LIKE) &&
846
0
            ((osJSonFlavor_ == "ESRIJSON" &&
847
0
              ESRIJSONIsObject(pszStoredContent, poOpenInfo)) ||
848
0
             (osJSonFlavor_ == "TopoJSON" &&
849
0
              TopoJSONIsObject(pszStoredContent, poOpenInfo))))
850
0
        {
851
0
            pszGeoData_ = pszStoredContent;
852
0
            nGeoDataLen_ = strlen(pszGeoData_);
853
854
0
            pszName_ = CPLStrdup(pszSource);
855
0
            return true;
856
0
        }
857
858
0
        OGRGeoJSONDriverStoreContent(pszSource, pszStoredContent);
859
0
        return false;
860
0
    }
861
862
    /* -------------------------------------------------------------------- */
863
    /*      Fetch the GeoJSON result.                                        */
864
    /* -------------------------------------------------------------------- */
865
0
    CPLHTTPResult *pResult = GeoJSONHTTPFetchWithContentTypeHeader(
866
0
        pszSource, /* bCanUsePOST = */ osJSonFlavor_ == "ESRIJSON", poOpenInfo);
867
0
    if (!pResult)
868
0
    {
869
0
        return FALSE;
870
0
    }
871
872
    /* -------------------------------------------------------------------- */
873
    /*      Copy returned GeoJSON data to text buffer.                      */
874
    /* -------------------------------------------------------------------- */
875
0
    char *pszData = reinterpret_cast<char *>(pResult->pabyData);
876
877
    // Directly assign CPLHTTPResult::pabyData to pszGeoData_.
878
0
    pszGeoData_ = pszData;
879
0
    nGeoDataLen_ = pResult->nDataLen;
880
0
    pResult->pabyData = nullptr;
881
0
    pResult->nDataLen = 0;
882
883
0
    pszName_ = CPLStrdup(pszSource);
884
885
    /* -------------------------------------------------------------------- */
886
    /*      Cleanup HTTP resources.                                         */
887
    /* -------------------------------------------------------------------- */
888
0
    CPLHTTPDestroyResult(pResult);
889
890
0
    CPLAssert(nullptr != pszGeoData_);
891
892
    /* -------------------------------------------------------------------- */
893
    /*      Cache the content if it is not handled by this driver, but      */
894
    /*      another related one.                                            */
895
    /* -------------------------------------------------------------------- */
896
0
    if (EQUAL(pszSource, poOpenInfo->pszFilename) && osJSonFlavor_ == "GeoJSON")
897
0
    {
898
0
        if (!GeoJSONIsObject(pszGeoData_, poOpenInfo))
899
0
        {
900
0
            if (ESRIJSONIsObject(pszGeoData_, poOpenInfo) ||
901
0
                TopoJSONIsObject(pszGeoData_, poOpenInfo) ||
902
0
                GeoJSONSeqIsObject(pszGeoData_, poOpenInfo) ||
903
0
                JSONFGIsObject(pszGeoData_, poOpenInfo))
904
0
            {
905
0
                OGRGeoJSONDriverStoreContent(pszSource, pszGeoData_);
906
0
                pszGeoData_ = nullptr;
907
0
                nGeoDataLen_ = 0;
908
0
            }
909
0
            else
910
0
            {
911
0
                OGRGeoJSONDriverStoreContent(
912
0
                    pszSource, CPLStrdup(INVALID_CONTENT_FOR_JSON_LIKE));
913
0
            }
914
0
            return false;
915
0
        }
916
0
    }
917
918
0
    return TRUE;
919
0
}
920
921
/************************************************************************/
922
/*                          RemoveJSonPStuff()                          */
923
/************************************************************************/
924
925
void OGRGeoJSONDataSource::RemoveJSonPStuff()
926
0
{
927
0
    const char *const apszPrefix[] = {"loadGeoJSON(", "jsonp("};
928
0
    for (size_t iP = 0; iP < CPL_ARRAYSIZE(apszPrefix); iP++)
929
0
    {
930
0
        if (strncmp(pszGeoData_, apszPrefix[iP], strlen(apszPrefix[iP])) == 0)
931
0
        {
932
0
            const size_t nDataLen = strlen(pszGeoData_);
933
0
            memmove(pszGeoData_, pszGeoData_ + strlen(apszPrefix[iP]),
934
0
                    nDataLen - strlen(apszPrefix[iP]));
935
0
            size_t i = nDataLen - strlen(apszPrefix[iP]);
936
0
            pszGeoData_[i] = '\0';
937
0
            while (i > 0 && pszGeoData_[i] != ')')
938
0
            {
939
0
                i--;
940
0
            }
941
0
            pszGeoData_[i] = '\0';
942
0
        }
943
0
    }
944
0
}
945
946
/************************************************************************/
947
/*                             LoadLayers()                             */
948
/************************************************************************/
949
950
void OGRGeoJSONDataSource::LoadLayers(GDALOpenInfo *poOpenInfo,
951
                                      GeoJSONSourceType nSrcType,
952
                                      const char *pszUnprefixed,
953
                                      const char *pszJSonFlavor)
954
0
{
955
0
    if (nullptr == pszGeoData_)
956
0
    {
957
0
        CPLError(CE_Failure, CPLE_ObjectNull, "%s data buffer empty",
958
0
                 pszJSonFlavor);
959
0
        return;
960
0
    }
961
962
0
    if (nSrcType != eGeoJSONSourceFile)
963
0
    {
964
0
        RemoveJSonPStuff();
965
0
    }
966
967
    /* -------------------------------------------------------------------- */
968
    /*      Is it ESRI Feature Service data ?                               */
969
    /* -------------------------------------------------------------------- */
970
0
    if (EQUAL(pszJSonFlavor, "ESRIJSON"))
971
0
    {
972
0
        OGRESRIJSONReader reader;
973
0
        if (nSrcType == eGeoJSONSourceFile)
974
0
        {
975
0
            if (!ReadFromFile(poOpenInfo, pszUnprefixed))
976
0
                return;
977
0
        }
978
0
        OGRErr err = reader.Parse(pszGeoData_);
979
0
        if (OGRERR_NONE == err)
980
0
        {
981
0
            json_object *poObj = reader.GetJSonObject();
982
0
            CheckExceededTransferLimit(poObj);
983
0
            reader.ReadLayers(this, nSrcType);
984
0
        }
985
0
        return;
986
0
    }
987
988
    /* -------------------------------------------------------------------- */
989
    /*      Is it TopoJSON data ?                                           */
990
    /* -------------------------------------------------------------------- */
991
0
    if (EQUAL(pszJSonFlavor, "TOPOJSON"))
992
0
    {
993
0
        OGRTopoJSONReader reader;
994
0
        if (nSrcType == eGeoJSONSourceFile)
995
0
        {
996
0
            if (!ReadFromFile(poOpenInfo, pszUnprefixed))
997
0
                return;
998
0
        }
999
0
        OGRErr err = reader.Parse(
1000
0
            pszGeoData_,
1001
0
            nSrcType == eGeoJSONSourceService &&
1002
0
                !STARTS_WITH_CI(poOpenInfo->pszFilename, "TopoJSON:"));
1003
0
        if (OGRERR_NONE == err)
1004
0
        {
1005
0
            reader.ReadLayers(this);
1006
0
        }
1007
0
        return;
1008
0
    }
1009
1010
0
    VSILFILE *fp = nullptr;
1011
0
    if (nSrcType == eGeoJSONSourceFile &&
1012
0
        !EQUAL(poOpenInfo->pszFilename, pszUnprefixed))
1013
0
    {
1014
0
        GDALOpenInfo oOpenInfo(pszUnprefixed, GA_ReadOnly);
1015
0
        if (oOpenInfo.fpL == nullptr || oOpenInfo.pabyHeader == nullptr)
1016
0
            return;
1017
0
        CPL_IGNORE_RET_VAL(oOpenInfo.TryToIngest(6000));
1018
0
        CPLFree(pszGeoData_);
1019
0
        pszGeoData_ =
1020
0
            CPLStrdup(reinterpret_cast<const char *>(oOpenInfo.pabyHeader));
1021
0
        fp = oOpenInfo.fpL;
1022
0
        oOpenInfo.fpL = nullptr;
1023
0
    }
1024
1025
0
    if (!GeoJSONIsObject(pszGeoData_, poOpenInfo))
1026
0
    {
1027
0
        CPLDebug(pszJSonFlavor, "No valid %s data found in source '%s'",
1028
0
                 pszJSonFlavor, pszName_);
1029
0
        if (fp)
1030
0
            VSIFCloseL(fp);
1031
0
        return;
1032
0
    }
1033
1034
    /* -------------------------------------------------------------------- */
1035
    /*      Configure GeoJSON format translator.                            */
1036
    /* -------------------------------------------------------------------- */
1037
0
    OGRGeoJSONReader *poReader = new OGRGeoJSONReader();
1038
0
    SetOptionsOnReader(poOpenInfo, poReader);
1039
1040
    /* -------------------------------------------------------------------- */
1041
    /*      Parse GeoJSON and build valid OGRLayer instance.                */
1042
    /* -------------------------------------------------------------------- */
1043
0
    bool bUseStreamingInterface = false;
1044
0
    const GIntBig nMaxBytesFirstPass = CPLAtoGIntBig(
1045
0
        CPLGetConfigOption("OGR_GEOJSON_MAX_BYTES_FIRST_PASS", "0"));
1046
0
    if ((fp != nullptr || poOpenInfo->fpL != nullptr) &&
1047
0
        (!STARTS_WITH(pszUnprefixed, "/vsistdin/") ||
1048
0
         (nMaxBytesFirstPass > 0 && nMaxBytesFirstPass <= 1000000)))
1049
0
    {
1050
0
        const char *pszStr = strstr(pszGeoData_, "\"features\"");
1051
0
        if (pszStr)
1052
0
        {
1053
0
            pszStr += strlen("\"features\"");
1054
0
            while (*pszStr && isspace(static_cast<unsigned char>(*pszStr)))
1055
0
                pszStr++;
1056
0
            if (*pszStr == ':')
1057
0
            {
1058
0
                pszStr++;
1059
0
                while (*pszStr && isspace(static_cast<unsigned char>(*pszStr)))
1060
0
                    pszStr++;
1061
0
                if (*pszStr == '[')
1062
0
                {
1063
0
                    bUseStreamingInterface = true;
1064
0
                }
1065
0
            }
1066
0
        }
1067
0
    }
1068
1069
0
    if (bUseStreamingInterface)
1070
0
    {
1071
0
        bool bTryStandardReading = false;
1072
0
        if (poReader->FirstPassReadLayer(this, fp ? fp : poOpenInfo->fpL,
1073
0
                                         bTryStandardReading))
1074
0
        {
1075
0
            if (fp)
1076
0
                fp = nullptr;
1077
0
            else
1078
0
                poOpenInfo->fpL = nullptr;
1079
0
            CheckExceededTransferLimit(poReader->GetJSonObject());
1080
0
        }
1081
0
        else
1082
0
        {
1083
0
            delete poReader;
1084
0
        }
1085
0
        if (!bTryStandardReading)
1086
0
        {
1087
0
            if (fp)
1088
0
                VSIFCloseL(fp);
1089
0
            return;
1090
0
        }
1091
1092
0
        poReader = new OGRGeoJSONReader();
1093
0
        SetOptionsOnReader(poOpenInfo, poReader);
1094
0
    }
1095
1096
0
    if (fp)
1097
0
        VSIFCloseL(fp);
1098
0
    if (nSrcType == eGeoJSONSourceFile)
1099
0
    {
1100
0
        if (!ReadFromFile(poOpenInfo, pszUnprefixed))
1101
0
        {
1102
0
            delete poReader;
1103
0
            return;
1104
0
        }
1105
0
        RemoveJSonPStuff();
1106
0
    }
1107
0
    const OGRErr err = poReader->Parse(pszGeoData_);
1108
0
    if (OGRERR_NONE == err)
1109
0
    {
1110
0
        CheckExceededTransferLimit(poReader->GetJSonObject());
1111
0
    }
1112
1113
0
    poReader->ReadLayers(this);
1114
0
    delete poReader;
1115
0
}
1116
1117
/************************************************************************/
1118
/*                         SetOptionsOnReader()                         */
1119
/************************************************************************/
1120
1121
void OGRGeoJSONDataSource::SetOptionsOnReader(GDALOpenInfo *poOpenInfo,
1122
                                              OGRGeoJSONReader *poReader)
1123
0
{
1124
0
    if (eGeometryAsCollection == flTransGeom_)
1125
0
    {
1126
0
        poReader->SetPreserveGeometryType(false);
1127
0
        CPLDebug("GeoJSON", "Geometry as OGRGeometryCollection type.");
1128
0
    }
1129
1130
0
    if (eAttributesSkip == flTransAttrs_)
1131
0
    {
1132
0
        poReader->SetSkipAttributes(true);
1133
0
        CPLDebug("GeoJSON", "Skip all attributes.");
1134
0
    }
1135
1136
0
    poReader->SetFlattenNestedAttributes(
1137
0
        CPLFetchBool(poOpenInfo->papszOpenOptions, "FLATTEN_NESTED_ATTRIBUTES",
1138
0
                     false),
1139
0
        CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
1140
0
                             "NESTED_ATTRIBUTE_SEPARATOR", "_")[0]);
1141
1142
0
    const bool bDefaultNativeData = bUpdatable_;
1143
0
    poReader->SetStoreNativeData(CPLFetchBool(
1144
0
        poOpenInfo->papszOpenOptions, "NATIVE_DATA", bDefaultNativeData));
1145
1146
0
    poReader->SetArrayAsString(CPLTestBool(CSLFetchNameValueDef(
1147
0
        poOpenInfo->papszOpenOptions, "ARRAY_AS_STRING",
1148
0
        CPLGetConfigOption("OGR_GEOJSON_ARRAY_AS_STRING", "NO"))));
1149
1150
0
    poReader->SetDateAsString(CPLTestBool(CSLFetchNameValueDef(
1151
0
        poOpenInfo->papszOpenOptions, "DATE_AS_STRING",
1152
0
        CPLGetConfigOption("OGR_GEOJSON_DATE_AS_STRING", "NO"))));
1153
1154
0
    const char *pszForeignMembers = CSLFetchNameValueDef(
1155
0
        poOpenInfo->papszOpenOptions, "FOREIGN_MEMBERS", "AUTO");
1156
0
    if (EQUAL(pszForeignMembers, "AUTO"))
1157
0
    {
1158
0
        poReader->SetForeignMemberProcessing(
1159
0
            OGRGeoJSONBaseReader::ForeignMemberProcessing::AUTO);
1160
0
    }
1161
0
    else if (EQUAL(pszForeignMembers, "ALL"))
1162
0
    {
1163
0
        poReader->SetForeignMemberProcessing(
1164
0
            OGRGeoJSONBaseReader::ForeignMemberProcessing::ALL);
1165
0
    }
1166
0
    else if (EQUAL(pszForeignMembers, "NONE"))
1167
0
    {
1168
0
        poReader->SetForeignMemberProcessing(
1169
0
            OGRGeoJSONBaseReader::ForeignMemberProcessing::NONE);
1170
0
    }
1171
0
    else if (EQUAL(pszForeignMembers, "STAC"))
1172
0
    {
1173
0
        poReader->SetForeignMemberProcessing(
1174
0
            OGRGeoJSONBaseReader::ForeignMemberProcessing::STAC);
1175
0
    }
1176
0
}
1177
1178
/************************************************************************/
1179
/*                     CheckExceededTransferLimit()                     */
1180
/************************************************************************/
1181
1182
void OGRGeoJSONDataSource::CheckExceededTransferLimit(json_object *poObj)
1183
0
{
1184
0
    for (int i = 0; i < 2; i++)
1185
0
    {
1186
0
        if (i == 1)
1187
0
        {
1188
0
            if (poObj && json_object_get_type(poObj) == json_type_object)
1189
0
            {
1190
0
                poObj = CPL_json_object_object_get(poObj, "properties");
1191
0
            }
1192
0
        }
1193
0
        if (poObj && json_object_get_type(poObj) == json_type_object)
1194
0
        {
1195
0
            json_object *poExceededTransferLimit =
1196
0
                CPL_json_object_object_get(poObj, "exceededTransferLimit");
1197
0
            if (poExceededTransferLimit &&
1198
0
                json_object_get_type(poExceededTransferLimit) ==
1199
0
                    json_type_boolean)
1200
0
            {
1201
0
                bOtherPages_ = CPL_TO_BOOL(
1202
0
                    json_object_get_boolean(poExceededTransferLimit));
1203
0
                return;
1204
0
            }
1205
0
        }
1206
0
    }
1207
0
}
1208
1209
/************************************************************************/
1210
/*                              AddLayer()                              */
1211
/************************************************************************/
1212
1213
void OGRGeoJSONDataSource::AddLayer(OGRGeoJSONLayer *poLayer)
1214
0
{
1215
0
    CPLAssert(papoLayersWriter_ == nullptr);
1216
1217
    // Return layer in readable state.
1218
0
    poLayer->ResetReading();
1219
1220
0
    papoLayers_ = static_cast<OGRGeoJSONLayer **>(
1221
0
        CPLRealloc(papoLayers_, sizeof(OGRGeoJSONLayer *) * (nLayers_ + 1)));
1222
0
    papoLayers_[nLayers_] = poLayer;
1223
0
    nLayers_++;
1224
0
}
1225
1226
/************************************************************************/
1227
/*                             FlushCache()                             */
1228
/************************************************************************/
1229
1230
CPLErr OGRGeoJSONDataSource::FlushCache(bool /*bAtClosing*/)
1231
0
{
1232
0
    if (papoLayersWriter_ != nullptr)
1233
0
    {
1234
0
        return papoLayersWriter_[0]->SyncToDisk() == OGRERR_NONE ? CE_None
1235
0
                                                                 : CE_Failure;
1236
0
    }
1237
1238
0
    CPLErr eErr = CE_None;
1239
0
    for (int i = 0; i < nLayers_; i++)
1240
0
    {
1241
0
        if (papoLayers_[i]->HasBeenUpdated())
1242
0
        {
1243
0
            papoLayers_[i]->SetUpdated(false);
1244
1245
0
            bool bOK = false;
1246
1247
            // Disable all filters.
1248
0
            OGRFeatureQuery *poAttrQueryBak = papoLayers_[i]->m_poAttrQuery;
1249
0
            papoLayers_[i]->m_poAttrQuery = nullptr;
1250
0
            OGRGeometry *poFilterGeomBak = papoLayers_[i]->m_poFilterGeom;
1251
0
            papoLayers_[i]->m_poFilterGeom = nullptr;
1252
1253
            // If the source data only contained one single feature and
1254
            // that's still the case, then do not use a FeatureCollection
1255
            // on writing.
1256
0
            bool bAlreadyDone = false;
1257
0
            if (papoLayers_[i]->GetFeatureCount(TRUE) == 1 &&
1258
0
                papoLayers_[i]->GetMetadata("NATIVE_DATA") == nullptr)
1259
0
            {
1260
0
                papoLayers_[i]->ResetReading();
1261
0
                OGRFeature *poFeature = papoLayers_[i]->GetNextFeature();
1262
0
                if (poFeature != nullptr)
1263
0
                {
1264
0
                    if (poFeature->GetNativeData() != nullptr)
1265
0
                    {
1266
0
                        bAlreadyDone = true;
1267
0
                        OGRGeoJSONWriteOptions oOptions;
1268
0
                        json_object *poObj =
1269
0
                            OGRGeoJSONWriteFeature(poFeature, oOptions);
1270
0
                        VSILFILE *fp = VSIFOpenL(pszName_, "wb");
1271
0
                        if (fp != nullptr)
1272
0
                        {
1273
0
                            bOK = VSIFPrintfL(
1274
0
                                      fp, "%s",
1275
0
                                      json_object_to_json_string(poObj)) > 0;
1276
0
                            VSIFCloseL(fp);
1277
0
                        }
1278
0
                        json_object_put(poObj);
1279
0
                    }
1280
0
                    delete poFeature;
1281
0
                }
1282
0
            }
1283
1284
            // Otherwise do layer translation.
1285
0
            if (!bAlreadyDone)
1286
0
            {
1287
0
                const char *const apszOpenOptions[] = {"-f", "GeoJSON",
1288
0
                                                       nullptr};
1289
0
                GDALVectorTranslateOptions *psOptions =
1290
0
                    GDALVectorTranslateOptionsNew(
1291
0
                        const_cast<char **>(apszOpenOptions), nullptr);
1292
0
                GDALDatasetH hSrcDS = this;
1293
0
                CPLString osNewFilename(pszName_);
1294
0
                osNewFilename += ".tmp";
1295
0
                GDALDatasetH hOutDS = GDALVectorTranslate(
1296
0
                    osNewFilename, nullptr, 1, &hSrcDS, psOptions, nullptr);
1297
0
                GDALVectorTranslateOptionsFree(psOptions);
1298
1299
0
                if (hOutDS != nullptr)
1300
0
                {
1301
0
                    CPLErrorReset();
1302
0
                    GDALClose(hOutDS);
1303
0
                    bOK = (CPLGetLastErrorType() == CE_None);
1304
0
                }
1305
0
                if (bOK)
1306
0
                {
1307
0
                    const bool bOverwrite = CPLTestBool(
1308
0
                        CPLGetConfigOption("OGR_GEOJSON_REWRITE_IN_PLACE",
1309
#ifdef _WIN32
1310
                                           "YES"
1311
#else
1312
0
                                           "NO"
1313
0
#endif
1314
0
                                           ));
1315
0
                    if (bOverwrite)
1316
0
                    {
1317
0
                        VSILFILE *fpTarget = nullptr;
1318
0
                        for (int attempt = 0; attempt < 10; attempt++)
1319
0
                        {
1320
0
                            fpTarget = VSIFOpenL(pszName_, "rb+");
1321
0
                            if (fpTarget)
1322
0
                                break;
1323
0
                            CPLSleep(0.1);
1324
0
                        }
1325
0
                        if (!fpTarget)
1326
0
                        {
1327
0
                            CPLError(CE_Failure, CPLE_AppDefined,
1328
0
                                     "Cannot rewrite %s", pszName_);
1329
0
                        }
1330
0
                        else
1331
0
                        {
1332
0
                            bool bCopyOK = CPL_TO_BOOL(
1333
0
                                VSIOverwriteFile(fpTarget, osNewFilename));
1334
0
                            if (VSIFCloseL(fpTarget) != 0)
1335
0
                                bCopyOK = false;
1336
0
                            if (bCopyOK)
1337
0
                            {
1338
0
                                VSIUnlink(osNewFilename);
1339
0
                            }
1340
0
                            else
1341
0
                            {
1342
0
                                CPLError(CE_Failure, CPLE_AppDefined,
1343
0
                                         "Cannot rewrite %s with content of %s",
1344
0
                                         pszName_, osNewFilename.c_str());
1345
0
                            }
1346
0
                        }
1347
0
                    }
1348
0
                    else
1349
0
                    {
1350
0
                        CPLString osBackup(pszName_);
1351
0
                        osBackup += ".bak";
1352
0
                        if (VSIRename(pszName_, osBackup) < 0)
1353
0
                        {
1354
0
                            CPLError(CE_Failure, CPLE_AppDefined,
1355
0
                                     "Cannot create backup copy");
1356
0
                        }
1357
0
                        else if (VSIRename(osNewFilename, pszName_) < 0)
1358
0
                        {
1359
0
                            CPLError(CE_Failure, CPLE_AppDefined,
1360
0
                                     "Cannot rename %s to %s",
1361
0
                                     osNewFilename.c_str(), pszName_);
1362
0
                        }
1363
0
                        else
1364
0
                        {
1365
0
                            VSIUnlink(osBackup);
1366
0
                        }
1367
0
                    }
1368
0
                }
1369
0
            }
1370
0
            if (!bOK)
1371
0
                eErr = CE_Failure;
1372
1373
            // Restore filters.
1374
0
            papoLayers_[i]->m_poAttrQuery = poAttrQueryBak;
1375
0
            papoLayers_[i]->m_poFilterGeom = poFilterGeomBak;
1376
0
        }
1377
0
    }
1378
0
    return eErr;
1379
0
}