Coverage Report

Created: 2026-04-01 06:20

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