Coverage Report

Created: 2025-11-16 06:25

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()
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
                                 char ** /* 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(pszSource);
865
0
    if (!pResult)
866
0
    {
867
0
        return FALSE;
868
0
    }
869
870
    /* -------------------------------------------------------------------- */
871
    /*      Copy returned GeoJSON data to text buffer.                      */
872
    /* -------------------------------------------------------------------- */
873
0
    char *pszData = reinterpret_cast<char *>(pResult->pabyData);
874
875
    // Directly assign CPLHTTPResult::pabyData to pszGeoData_.
876
0
    pszGeoData_ = pszData;
877
0
    nGeoDataLen_ = pResult->nDataLen;
878
0
    pResult->pabyData = nullptr;
879
0
    pResult->nDataLen = 0;
880
881
0
    pszName_ = CPLStrdup(pszSource);
882
883
    /* -------------------------------------------------------------------- */
884
    /*      Cleanup HTTP resources.                                         */
885
    /* -------------------------------------------------------------------- */
886
0
    CPLHTTPDestroyResult(pResult);
887
888
0
    CPLAssert(nullptr != pszGeoData_);
889
890
    /* -------------------------------------------------------------------- */
891
    /*      Cache the content if it is not handled by this driver, but      */
892
    /*      another related one.                                            */
893
    /* -------------------------------------------------------------------- */
894
0
    if (EQUAL(pszSource, poOpenInfo->pszFilename) && osJSonFlavor_ == "GeoJSON")
895
0
    {
896
0
        if (!GeoJSONIsObject(pszGeoData_, poOpenInfo))
897
0
        {
898
0
            if (ESRIJSONIsObject(pszGeoData_, poOpenInfo) ||
899
0
                TopoJSONIsObject(pszGeoData_, poOpenInfo) ||
900
0
                GeoJSONSeqIsObject(pszGeoData_, poOpenInfo) ||
901
0
                JSONFGIsObject(pszGeoData_, poOpenInfo))
902
0
            {
903
0
                OGRGeoJSONDriverStoreContent(pszSource, pszGeoData_);
904
0
                pszGeoData_ = nullptr;
905
0
                nGeoDataLen_ = 0;
906
0
            }
907
0
            else
908
0
            {
909
0
                OGRGeoJSONDriverStoreContent(
910
0
                    pszSource, CPLStrdup(INVALID_CONTENT_FOR_JSON_LIKE));
911
0
            }
912
0
            return false;
913
0
        }
914
0
    }
915
916
0
    return TRUE;
917
0
}
918
919
/************************************************************************/
920
/*                       RemoveJSonPStuff()                             */
921
/************************************************************************/
922
923
void OGRGeoJSONDataSource::RemoveJSonPStuff()
924
0
{
925
0
    const char *const apszPrefix[] = {"loadGeoJSON(", "jsonp("};
926
0
    for (size_t iP = 0; iP < CPL_ARRAYSIZE(apszPrefix); iP++)
927
0
    {
928
0
        if (strncmp(pszGeoData_, apszPrefix[iP], strlen(apszPrefix[iP])) == 0)
929
0
        {
930
0
            const size_t nDataLen = strlen(pszGeoData_);
931
0
            memmove(pszGeoData_, pszGeoData_ + strlen(apszPrefix[iP]),
932
0
                    nDataLen - strlen(apszPrefix[iP]));
933
0
            size_t i = nDataLen - strlen(apszPrefix[iP]);
934
0
            pszGeoData_[i] = '\0';
935
0
            while (i > 0 && pszGeoData_[i] != ')')
936
0
            {
937
0
                i--;
938
0
            }
939
0
            pszGeoData_[i] = '\0';
940
0
        }
941
0
    }
942
0
}
943
944
/************************************************************************/
945
/*                           LoadLayers()                               */
946
/************************************************************************/
947
948
void OGRGeoJSONDataSource::LoadLayers(GDALOpenInfo *poOpenInfo,
949
                                      GeoJSONSourceType nSrcType,
950
                                      const char *pszUnprefixed,
951
                                      const char *pszJSonFlavor)
952
0
{
953
0
    if (nullptr == pszGeoData_)
954
0
    {
955
0
        CPLError(CE_Failure, CPLE_ObjectNull, "%s data buffer empty",
956
0
                 pszJSonFlavor);
957
0
        return;
958
0
    }
959
960
0
    if (nSrcType != eGeoJSONSourceFile)
961
0
    {
962
0
        RemoveJSonPStuff();
963
0
    }
964
965
    /* -------------------------------------------------------------------- */
966
    /*      Is it ESRI Feature Service data ?                               */
967
    /* -------------------------------------------------------------------- */
968
0
    if (EQUAL(pszJSonFlavor, "ESRIJSON"))
969
0
    {
970
0
        OGRESRIJSONReader reader;
971
0
        if (nSrcType == eGeoJSONSourceFile)
972
0
        {
973
0
            if (!ReadFromFile(poOpenInfo, pszUnprefixed))
974
0
                return;
975
0
        }
976
0
        OGRErr err = reader.Parse(pszGeoData_);
977
0
        if (OGRERR_NONE == err)
978
0
        {
979
0
            json_object *poObj = reader.GetJSonObject();
980
0
            CheckExceededTransferLimit(poObj);
981
0
            reader.ReadLayers(this, nSrcType);
982
0
        }
983
0
        return;
984
0
    }
985
986
    /* -------------------------------------------------------------------- */
987
    /*      Is it TopoJSON data ?                                           */
988
    /* -------------------------------------------------------------------- */
989
0
    if (EQUAL(pszJSonFlavor, "TOPOJSON"))
990
0
    {
991
0
        OGRTopoJSONReader reader;
992
0
        if (nSrcType == eGeoJSONSourceFile)
993
0
        {
994
0
            if (!ReadFromFile(poOpenInfo, pszUnprefixed))
995
0
                return;
996
0
        }
997
0
        OGRErr err = reader.Parse(
998
0
            pszGeoData_,
999
0
            nSrcType == eGeoJSONSourceService &&
1000
0
                !STARTS_WITH_CI(poOpenInfo->pszFilename, "TopoJSON:"));
1001
0
        if (OGRERR_NONE == err)
1002
0
        {
1003
0
            reader.ReadLayers(this);
1004
0
        }
1005
0
        return;
1006
0
    }
1007
1008
0
    VSILFILE *fp = nullptr;
1009
0
    if (nSrcType == eGeoJSONSourceFile &&
1010
0
        !EQUAL(poOpenInfo->pszFilename, pszUnprefixed))
1011
0
    {
1012
0
        GDALOpenInfo oOpenInfo(pszUnprefixed, GA_ReadOnly);
1013
0
        if (oOpenInfo.fpL == nullptr || oOpenInfo.pabyHeader == nullptr)
1014
0
            return;
1015
0
        CPL_IGNORE_RET_VAL(oOpenInfo.TryToIngest(6000));
1016
0
        CPLFree(pszGeoData_);
1017
0
        pszGeoData_ =
1018
0
            CPLStrdup(reinterpret_cast<const char *>(oOpenInfo.pabyHeader));
1019
0
        fp = oOpenInfo.fpL;
1020
0
        oOpenInfo.fpL = nullptr;
1021
0
    }
1022
1023
0
    if (!GeoJSONIsObject(pszGeoData_, poOpenInfo))
1024
0
    {
1025
0
        CPLDebug(pszJSonFlavor, "No valid %s data found in source '%s'",
1026
0
                 pszJSonFlavor, pszName_);
1027
0
        if (fp)
1028
0
            VSIFCloseL(fp);
1029
0
        return;
1030
0
    }
1031
1032
    /* -------------------------------------------------------------------- */
1033
    /*      Configure GeoJSON format translator.                            */
1034
    /* -------------------------------------------------------------------- */
1035
0
    OGRGeoJSONReader *poReader = new OGRGeoJSONReader();
1036
0
    SetOptionsOnReader(poOpenInfo, poReader);
1037
1038
    /* -------------------------------------------------------------------- */
1039
    /*      Parse GeoJSON and build valid OGRLayer instance.                */
1040
    /* -------------------------------------------------------------------- */
1041
0
    bool bUseStreamingInterface = false;
1042
0
    const GIntBig nMaxBytesFirstPass = CPLAtoGIntBig(
1043
0
        CPLGetConfigOption("OGR_GEOJSON_MAX_BYTES_FIRST_PASS", "0"));
1044
0
    if ((fp != nullptr || poOpenInfo->fpL != nullptr) &&
1045
0
        (!STARTS_WITH(pszUnprefixed, "/vsistdin/") ||
1046
0
         (nMaxBytesFirstPass > 0 && nMaxBytesFirstPass <= 1000000)))
1047
0
    {
1048
0
        const char *pszStr = strstr(pszGeoData_, "\"features\"");
1049
0
        if (pszStr)
1050
0
        {
1051
0
            pszStr += strlen("\"features\"");
1052
0
            while (*pszStr && isspace(static_cast<unsigned char>(*pszStr)))
1053
0
                pszStr++;
1054
0
            if (*pszStr == ':')
1055
0
            {
1056
0
                pszStr++;
1057
0
                while (*pszStr && isspace(static_cast<unsigned char>(*pszStr)))
1058
0
                    pszStr++;
1059
0
                if (*pszStr == '[')
1060
0
                {
1061
0
                    bUseStreamingInterface = true;
1062
0
                }
1063
0
            }
1064
0
        }
1065
0
    }
1066
1067
0
    if (bUseStreamingInterface)
1068
0
    {
1069
0
        bool bTryStandardReading = false;
1070
0
        if (poReader->FirstPassReadLayer(this, fp ? fp : poOpenInfo->fpL,
1071
0
                                         bTryStandardReading))
1072
0
        {
1073
0
            if (fp)
1074
0
                fp = nullptr;
1075
0
            else
1076
0
                poOpenInfo->fpL = nullptr;
1077
0
            CheckExceededTransferLimit(poReader->GetJSonObject());
1078
0
        }
1079
0
        else
1080
0
        {
1081
0
            delete poReader;
1082
0
        }
1083
0
        if (!bTryStandardReading)
1084
0
        {
1085
0
            if (fp)
1086
0
                VSIFCloseL(fp);
1087
0
            return;
1088
0
        }
1089
1090
0
        poReader = new OGRGeoJSONReader();
1091
0
        SetOptionsOnReader(poOpenInfo, poReader);
1092
0
    }
1093
1094
0
    if (fp)
1095
0
        VSIFCloseL(fp);
1096
0
    if (nSrcType == eGeoJSONSourceFile)
1097
0
    {
1098
0
        if (!ReadFromFile(poOpenInfo, pszUnprefixed))
1099
0
        {
1100
0
            delete poReader;
1101
0
            return;
1102
0
        }
1103
0
        RemoveJSonPStuff();
1104
0
    }
1105
0
    const OGRErr err = poReader->Parse(pszGeoData_);
1106
0
    if (OGRERR_NONE == err)
1107
0
    {
1108
0
        CheckExceededTransferLimit(poReader->GetJSonObject());
1109
0
    }
1110
1111
0
    poReader->ReadLayers(this);
1112
0
    delete poReader;
1113
0
}
1114
1115
/************************************************************************/
1116
/*                          SetOptionsOnReader()                        */
1117
/************************************************************************/
1118
1119
void OGRGeoJSONDataSource::SetOptionsOnReader(GDALOpenInfo *poOpenInfo,
1120
                                              OGRGeoJSONReader *poReader)
1121
0
{
1122
0
    if (eGeometryAsCollection == flTransGeom_)
1123
0
    {
1124
0
        poReader->SetPreserveGeometryType(false);
1125
0
        CPLDebug("GeoJSON", "Geometry as OGRGeometryCollection type.");
1126
0
    }
1127
1128
0
    if (eAttributesSkip == flTransAttrs_)
1129
0
    {
1130
0
        poReader->SetSkipAttributes(true);
1131
0
        CPLDebug("GeoJSON", "Skip all attributes.");
1132
0
    }
1133
1134
0
    poReader->SetFlattenNestedAttributes(
1135
0
        CPLFetchBool(poOpenInfo->papszOpenOptions, "FLATTEN_NESTED_ATTRIBUTES",
1136
0
                     false),
1137
0
        CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
1138
0
                             "NESTED_ATTRIBUTE_SEPARATOR", "_")[0]);
1139
1140
0
    const bool bDefaultNativeData = bUpdatable_;
1141
0
    poReader->SetStoreNativeData(CPLFetchBool(
1142
0
        poOpenInfo->papszOpenOptions, "NATIVE_DATA", bDefaultNativeData));
1143
1144
0
    poReader->SetArrayAsString(CPLTestBool(CSLFetchNameValueDef(
1145
0
        poOpenInfo->papszOpenOptions, "ARRAY_AS_STRING",
1146
0
        CPLGetConfigOption("OGR_GEOJSON_ARRAY_AS_STRING", "NO"))));
1147
1148
0
    poReader->SetDateAsString(CPLTestBool(CSLFetchNameValueDef(
1149
0
        poOpenInfo->papszOpenOptions, "DATE_AS_STRING",
1150
0
        CPLGetConfigOption("OGR_GEOJSON_DATE_AS_STRING", "NO"))));
1151
1152
0
    const char *pszForeignMembers = CSLFetchNameValueDef(
1153
0
        poOpenInfo->papszOpenOptions, "FOREIGN_MEMBERS", "AUTO");
1154
0
    if (EQUAL(pszForeignMembers, "AUTO"))
1155
0
    {
1156
0
        poReader->SetForeignMemberProcessing(
1157
0
            OGRGeoJSONBaseReader::ForeignMemberProcessing::AUTO);
1158
0
    }
1159
0
    else if (EQUAL(pszForeignMembers, "ALL"))
1160
0
    {
1161
0
        poReader->SetForeignMemberProcessing(
1162
0
            OGRGeoJSONBaseReader::ForeignMemberProcessing::ALL);
1163
0
    }
1164
0
    else if (EQUAL(pszForeignMembers, "NONE"))
1165
0
    {
1166
0
        poReader->SetForeignMemberProcessing(
1167
0
            OGRGeoJSONBaseReader::ForeignMemberProcessing::NONE);
1168
0
    }
1169
0
    else if (EQUAL(pszForeignMembers, "STAC"))
1170
0
    {
1171
0
        poReader->SetForeignMemberProcessing(
1172
0
            OGRGeoJSONBaseReader::ForeignMemberProcessing::STAC);
1173
0
    }
1174
0
}
1175
1176
/************************************************************************/
1177
/*                     CheckExceededTransferLimit()                     */
1178
/************************************************************************/
1179
1180
void OGRGeoJSONDataSource::CheckExceededTransferLimit(json_object *poObj)
1181
0
{
1182
0
    for (int i = 0; i < 2; i++)
1183
0
    {
1184
0
        if (i == 1)
1185
0
        {
1186
0
            if (poObj && json_object_get_type(poObj) == json_type_object)
1187
0
            {
1188
0
                poObj = CPL_json_object_object_get(poObj, "properties");
1189
0
            }
1190
0
        }
1191
0
        if (poObj && json_object_get_type(poObj) == json_type_object)
1192
0
        {
1193
0
            json_object *poExceededTransferLimit =
1194
0
                CPL_json_object_object_get(poObj, "exceededTransferLimit");
1195
0
            if (poExceededTransferLimit &&
1196
0
                json_object_get_type(poExceededTransferLimit) ==
1197
0
                    json_type_boolean)
1198
0
            {
1199
0
                bOtherPages_ = CPL_TO_BOOL(
1200
0
                    json_object_get_boolean(poExceededTransferLimit));
1201
0
                return;
1202
0
            }
1203
0
        }
1204
0
    }
1205
0
}
1206
1207
/************************************************************************/
1208
/*                            AddLayer()                                */
1209
/************************************************************************/
1210
1211
void OGRGeoJSONDataSource::AddLayer(OGRGeoJSONLayer *poLayer)
1212
0
{
1213
0
    CPLAssert(papoLayersWriter_ == nullptr);
1214
1215
    // Return layer in readable state.
1216
0
    poLayer->ResetReading();
1217
1218
0
    papoLayers_ = static_cast<OGRGeoJSONLayer **>(
1219
0
        CPLRealloc(papoLayers_, sizeof(OGRGeoJSONLayer *) * (nLayers_ + 1)));
1220
0
    papoLayers_[nLayers_] = poLayer;
1221
0
    nLayers_++;
1222
0
}
1223
1224
/************************************************************************/
1225
/*                            FlushCache()                              */
1226
/************************************************************************/
1227
1228
CPLErr OGRGeoJSONDataSource::FlushCache(bool /*bAtClosing*/)
1229
0
{
1230
0
    if (papoLayersWriter_ != nullptr)
1231
0
    {
1232
0
        return papoLayersWriter_[0]->SyncToDisk() == OGRERR_NONE ? CE_None
1233
0
                                                                 : CE_Failure;
1234
0
    }
1235
1236
0
    CPLErr eErr = CE_None;
1237
0
    for (int i = 0; i < nLayers_; i++)
1238
0
    {
1239
0
        if (papoLayers_[i]->HasBeenUpdated())
1240
0
        {
1241
0
            papoLayers_[i]->SetUpdated(false);
1242
1243
0
            bool bOK = false;
1244
1245
            // Disable all filters.
1246
0
            OGRFeatureQuery *poAttrQueryBak = papoLayers_[i]->m_poAttrQuery;
1247
0
            papoLayers_[i]->m_poAttrQuery = nullptr;
1248
0
            OGRGeometry *poFilterGeomBak = papoLayers_[i]->m_poFilterGeom;
1249
0
            papoLayers_[i]->m_poFilterGeom = nullptr;
1250
1251
            // If the source data only contained one single feature and
1252
            // that's still the case, then do not use a FeatureCollection
1253
            // on writing.
1254
0
            bool bAlreadyDone = false;
1255
0
            if (papoLayers_[i]->GetFeatureCount(TRUE) == 1 &&
1256
0
                papoLayers_[i]->GetMetadata("NATIVE_DATA") == nullptr)
1257
0
            {
1258
0
                papoLayers_[i]->ResetReading();
1259
0
                OGRFeature *poFeature = papoLayers_[i]->GetNextFeature();
1260
0
                if (poFeature != nullptr)
1261
0
                {
1262
0
                    if (poFeature->GetNativeData() != nullptr)
1263
0
                    {
1264
0
                        bAlreadyDone = true;
1265
0
                        OGRGeoJSONWriteOptions oOptions;
1266
0
                        json_object *poObj =
1267
0
                            OGRGeoJSONWriteFeature(poFeature, oOptions);
1268
0
                        VSILFILE *fp = VSIFOpenL(pszName_, "wb");
1269
0
                        if (fp != nullptr)
1270
0
                        {
1271
0
                            bOK = VSIFPrintfL(
1272
0
                                      fp, "%s",
1273
0
                                      json_object_to_json_string(poObj)) > 0;
1274
0
                            VSIFCloseL(fp);
1275
0
                        }
1276
0
                        json_object_put(poObj);
1277
0
                    }
1278
0
                    delete poFeature;
1279
0
                }
1280
0
            }
1281
1282
            // Otherwise do layer translation.
1283
0
            if (!bAlreadyDone)
1284
0
            {
1285
0
                char **papszOptions = CSLAddString(nullptr, "-f");
1286
0
                papszOptions = CSLAddString(papszOptions, "GeoJSON");
1287
0
                GDALVectorTranslateOptions *psOptions =
1288
0
                    GDALVectorTranslateOptionsNew(papszOptions, nullptr);
1289
0
                CSLDestroy(papszOptions);
1290
0
                GDALDatasetH hSrcDS = this;
1291
0
                CPLString osNewFilename(pszName_);
1292
0
                osNewFilename += ".tmp";
1293
0
                GDALDatasetH hOutDS = GDALVectorTranslate(
1294
0
                    osNewFilename, nullptr, 1, &hSrcDS, psOptions, nullptr);
1295
0
                GDALVectorTranslateOptionsFree(psOptions);
1296
1297
0
                if (hOutDS != nullptr)
1298
0
                {
1299
0
                    CPLErrorReset();
1300
0
                    GDALClose(hOutDS);
1301
0
                    bOK = (CPLGetLastErrorType() == CE_None);
1302
0
                }
1303
0
                if (bOK)
1304
0
                {
1305
0
                    const bool bOverwrite = CPLTestBool(
1306
0
                        CPLGetConfigOption("OGR_GEOJSON_REWRITE_IN_PLACE",
1307
#ifdef _WIN32
1308
                                           "YES"
1309
#else
1310
0
                                           "NO"
1311
0
#endif
1312
0
                                           ));
1313
0
                    if (bOverwrite)
1314
0
                    {
1315
0
                        VSILFILE *fpTarget = nullptr;
1316
0
                        for (int attempt = 0; attempt < 10; attempt++)
1317
0
                        {
1318
0
                            fpTarget = VSIFOpenL(pszName_, "rb+");
1319
0
                            if (fpTarget)
1320
0
                                break;
1321
0
                            CPLSleep(0.1);
1322
0
                        }
1323
0
                        if (!fpTarget)
1324
0
                        {
1325
0
                            CPLError(CE_Failure, CPLE_AppDefined,
1326
0
                                     "Cannot rewrite %s", pszName_);
1327
0
                        }
1328
0
                        else
1329
0
                        {
1330
0
                            bool bCopyOK = CPL_TO_BOOL(
1331
0
                                VSIOverwriteFile(fpTarget, osNewFilename));
1332
0
                            if (VSIFCloseL(fpTarget) != 0)
1333
0
                                bCopyOK = false;
1334
0
                            if (bCopyOK)
1335
0
                            {
1336
0
                                VSIUnlink(osNewFilename);
1337
0
                            }
1338
0
                            else
1339
0
                            {
1340
0
                                CPLError(CE_Failure, CPLE_AppDefined,
1341
0
                                         "Cannot rewrite %s with content of %s",
1342
0
                                         pszName_, osNewFilename.c_str());
1343
0
                            }
1344
0
                        }
1345
0
                    }
1346
0
                    else
1347
0
                    {
1348
0
                        CPLString osBackup(pszName_);
1349
0
                        osBackup += ".bak";
1350
0
                        if (VSIRename(pszName_, osBackup) < 0)
1351
0
                        {
1352
0
                            CPLError(CE_Failure, CPLE_AppDefined,
1353
0
                                     "Cannot create backup copy");
1354
0
                        }
1355
0
                        else if (VSIRename(osNewFilename, pszName_) < 0)
1356
0
                        {
1357
0
                            CPLError(CE_Failure, CPLE_AppDefined,
1358
0
                                     "Cannot rename %s to %s",
1359
0
                                     osNewFilename.c_str(), pszName_);
1360
0
                        }
1361
0
                        else
1362
0
                        {
1363
0
                            VSIUnlink(osBackup);
1364
0
                        }
1365
0
                    }
1366
0
                }
1367
0
            }
1368
0
            if (!bOK)
1369
0
                eErr = CE_Failure;
1370
1371
            // Restore filters.
1372
0
            papoLayers_[i]->m_poAttrQuery = poAttrQueryBak;
1373
0
            papoLayers_[i]->m_poFilterGeom = poFilterGeomBak;
1374
0
        }
1375
0
    }
1376
0
    return eErr;
1377
0
}