LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/geojson - ogrgeojsondatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 539 623 86.5 %
Date: 2026-07-07 11:55:39 Functions: 22 22 100.0 %

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

Generated by: LCOV version 1.14