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

Generated by: LCOV version 1.14