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

Generated by: LCOV version 1.14