LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/geojson - ogrgeojsondatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 541 625 86.6 %
Date: 2026-03-20 01:44:29 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         763 : 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         763 :       bUpdatable_(false)
      59             : {
      60         763 : }
      61             : 
      62             : /************************************************************************/
      63             : /*                       ~OGRGeoJSONDataSource()                        */
      64             : /************************************************************************/
      65             : 
      66        1524 : OGRGeoJSONDataSource::~OGRGeoJSONDataSource()
      67             : {
      68         762 :     OGRGeoJSONDataSource::Close();
      69        1524 : }
      70             : 
      71             : /************************************************************************/
      72             : /*                               Close()                                */
      73             : /************************************************************************/
      74             : 
      75        1334 : CPLErr OGRGeoJSONDataSource::Close(GDALProgressFunc, void *)
      76             : {
      77        1334 :     CPLErr eErr = CE_None;
      78        1334 :     if (nOpenFlags != OPEN_FLAGS_CLOSED)
      79             :     {
      80         762 :         if (OGRGeoJSONDataSource::FlushCache(true) != CE_None)
      81           0 :             eErr = CE_Failure;
      82             : 
      83         762 :         if (!OGRGeoJSONDataSource::Clear())
      84           0 :             eErr = CE_Failure;
      85             : 
      86         762 :         if (GDALDataset::Close() != CE_None)
      87           0 :             eErr = CE_Failure;
      88             :     }
      89        1334 :     return eErr;
      90             : }
      91             : 
      92             : /************************************************************************/
      93             : /*                    DealWithOgrSchemaOpenOption()                     */
      94             : /************************************************************************/
      95             : 
      96         566 : bool OGRGeoJSONDataSource::DealWithOgrSchemaOpenOption(
      97             :     const GDALOpenInfo *poOpenInfo)
      98             : {
      99             :     const std::string osFieldsSchemaOverrideParam =
     100        1132 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "OGR_SCHEMA", "");
     101             : 
     102         566 :     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         563 :     return true;
     130             : }
     131             : 
     132             : /************************************************************************/
     133             : /*                                Open()                                */
     134             : /************************************************************************/
     135             : 
     136         593 : int OGRGeoJSONDataSource::Open(GDALOpenInfo *poOpenInfo,
     137             :                                GeoJSONSourceType nSrcType,
     138             :                                const char *pszJSonFlavor)
     139             : {
     140         593 :     osJSonFlavor_ = pszJSonFlavor;
     141             : 
     142         593 :     const char *pszUnprefixed = poOpenInfo->pszFilename;
     143         593 :     if (STARTS_WITH_CI(pszUnprefixed, pszJSonFlavor) &&
     144           2 :         pszUnprefixed[strlen(pszJSonFlavor)] == ':')
     145             :     {
     146           2 :         pszUnprefixed += strlen(pszJSonFlavor) + 1;
     147             :     }
     148             : 
     149         593 :     if (eGeoJSONSourceService == nSrcType)
     150             :     {
     151          24 :         if (!ReadFromService(poOpenInfo, pszUnprefixed))
     152          19 :             return FALSE;
     153           5 :         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         569 :     else if (eGeoJSONSourceText == nSrcType)
     161             :     {
     162         207 :         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         206 :         pszGeoData_ = CPLStrdup(pszUnprefixed);
     169             :     }
     170         362 :     else if (eGeoJSONSourceFile == nSrcType)
     171             :     {
     172         361 :         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         361 :         pszName_ = CPLStrdup(pszUnprefixed);
     180         361 :         bUpdatable_ = (poOpenInfo->eAccess == GA_Update);
     181             : 
     182         361 :         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         360 :         else if (poOpenInfo->fpL == nullptr)
     191           6 :             return FALSE;
     192             :         else
     193             :         {
     194         354 :             pszGeoData_ = CPLStrdup(
     195         354 :                 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         566 :     if (nullptr == pszGeoData_ ||
     209         566 :         STARTS_WITH(pszGeoData_, "{\"couchdb\":\"Welcome\"") ||
     210         566 :         STARTS_WITH(pszGeoData_, "{\"db_name\":\"") ||
     211         566 :         STARTS_WITH(pszGeoData_, "{\"total_rows\":") ||
     212         566 :         STARTS_WITH(pszGeoData_, "{\"rows\":["))
     213             :     {
     214           0 :         Clear();
     215           0 :         return FALSE;
     216             :     }
     217             : 
     218         566 :     SetDescription(poOpenInfo->pszFilename);
     219         566 :     LoadLayers(poOpenInfo, nSrcType, pszUnprefixed, pszJSonFlavor);
     220             : 
     221         566 :     if (!DealWithOgrSchemaOpenOption(poOpenInfo))
     222             :     {
     223           3 :         Clear();
     224           3 :         return FALSE;
     225             :     }
     226             : 
     227         563 :     if (nLayers_ == 0)
     228             :     {
     229          36 :         bool bEmitError = true;
     230          36 :         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          36 :         Clear();
     243             : 
     244          36 :         if (bEmitError)
     245             :         {
     246          36 :             CPLError(CE_Failure, CPLE_OpenFailed, "Failed to read %s data",
     247             :                      pszJSonFlavor);
     248             :         }
     249          36 :         return FALSE;
     250             :     }
     251             : 
     252         527 :     return TRUE;
     253             : }
     254             : 
     255             : /************************************************************************/
     256             : /*                           GetLayerCount()                            */
     257             : /************************************************************************/
     258             : 
     259         572 : int OGRGeoJSONDataSource::GetLayerCount() const
     260             : {
     261         572 :     return nLayers_;
     262             : }
     263             : 
     264             : /************************************************************************/
     265             : /*                              GetLayer()                              */
     266             : /************************************************************************/
     267             : 
     268         621 : const OGRLayer *OGRGeoJSONDataSource::GetLayer(int nLayer) const
     269             : {
     270         621 :     if (0 <= nLayer && nLayer < nLayers_)
     271             :     {
     272         617 :         if (papoLayers_)
     273         616 :             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         189 : OGRGeoJSONDataSource::ICreateLayer(const char *pszNameIn,
     287             :                                    const OGRGeomFieldDefn *poSrcGeomFieldDefn,
     288             :                                    CSLConstList papszOptions)
     289             : {
     290         189 :     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         189 :     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         173 :         poSrcGeomFieldDefn ? poSrcGeomFieldDefn->GetType() : wkbNone;
     307             :     const auto poSRS =
     308         173 :         poSrcGeomFieldDefn ? poSrcGeomFieldDefn->GetSpatialRef() : nullptr;
     309             : 
     310             :     const char *pszForeignMembersCollection =
     311         173 :         CSLFetchNameValue(papszOptions, "FOREIGN_MEMBERS_COLLECTION");
     312         173 :     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         340 :         CSLFetchNameValueDef(papszOptions, "FOREIGN_MEMBERS_FEATURE", "");
     339         170 :     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         167 :     VSIFPrintfL(fpOut_, "{\n\"type\": \"FeatureCollection\",\n");
     364             : 
     365         167 :     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         167 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "WRITE_BBOX", "FALSE"));
     374             : 
     375             :     const bool bRFC7946 =
     376         167 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "RFC7946", "FALSE"));
     377             : 
     378         167 :     const char *pszNativeData = CSLFetchNameValue(papszOptions, "NATIVE_DATA");
     379             :     const char *pszNativeMediaType =
     380         167 :         CSLFetchNameValue(papszOptions, "NATIVE_MEDIA_TYPE");
     381         167 :     bool bWriteCRSIfWGS84 = true;
     382         167 :     bool bFoundNameInNativeData = false;
     383         167 :     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         167 :     const char *pszAtName = CSLFetchNameValue(papszOptions, "@NAME");
     461         167 :     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         481 :     else if (!bFoundNameInNativeData &&
     469         149 :              CPLFetchBool(papszOptions, "WRITE_NAME", true) &&
     470         436 :              !EQUAL(pszNameIn, OGRGeoJSONLayer::DefaultName) &&
     471         121 :              !EQUAL(pszNameIn, ""))
     472             :     {
     473         121 :         json_object *poName = json_object_new_string(pszNameIn);
     474         121 :         VSIFPrintfL(fpOut_, "\"name\": %s,\n",
     475             :                     json_object_to_json_string(poName));
     476         121 :         json_object_put(poName);
     477             :     }
     478             : 
     479         167 :     const char *pszDescription = CSLFetchNameValue(papszOptions, "DESCRIPTION");
     480         167 :     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         167 :     OGRCoordinateTransformation *poCT = nullptr;
     489         167 :     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         144 :     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         167 :     CPLStringList aosOptions(papszOptions);
     571             : 
     572         167 :     double dfXYResolution = OGRGeomCoordinatePrecision::UNKNOWN;
     573         167 :     double dfZResolution = OGRGeomCoordinatePrecision::UNKNOWN;
     574             : 
     575         167 :     if (const char *pszCoordPrecision =
     576         167 :             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         164 :     else if (poSrcGeomFieldDefn)
     589             :     {
     590         160 :         const auto &oCoordPrec = poSrcGeomFieldDefn->GetCoordinatePrecision();
     591         320 :         OGRSpatialReference oSRSWGS84;
     592         160 :         oSRSWGS84.SetWellKnownGeogCS("WGS84");
     593             :         const auto oCoordPrecWGS84 =
     594         320 :             oCoordPrec.ConvertToOtherSRS(poSRS, &oSRSWGS84);
     595             : 
     596         160 :         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         160 :         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         167 :     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         167 :     VSIFPrintfL(fpOut_, "\"features\": [\n");
     633             : 
     634             :     OGRGeoJSONWriteLayer *poLayer = new OGRGeoJSONWriteLayer(
     635         167 :         pszNameIn, eGType, aosOptions.List(), bWriteFC_BBOX, poCT, this);
     636             : 
     637         167 :     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         167 :     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         167 :     CPLAssert(papoLayers_ == nullptr);
     661         334 :     papoLayersWriter_ = static_cast<OGRGeoJSONWriteLayer **>(CPLRealloc(
     662         167 :         papoLayers_, sizeof(OGRGeoJSONWriteLayer *) * (nLayers_ + 1)));
     663             : 
     664         167 :     papoLayersWriter_[nLayers_++] = poLayer;
     665             : 
     666         167 :     return poLayer;
     667             : }
     668             : 
     669             : /************************************************************************/
     670             : /*                           TestCapability()                           */
     671             : /************************************************************************/
     672             : 
     673         377 : int OGRGeoJSONDataSource::TestCapability(const char *pszCap) const
     674             : {
     675         377 :     if (EQUAL(pszCap, ODsCCreateLayer))
     676         110 :         return fpOut_ != nullptr && nLayers_ == 0;
     677         267 :     else if (EQUAL(pszCap, ODsCMeasuredGeometries))
     678           2 :         return m_bSupportsMGeometries;
     679         265 :     else if (EQUAL(pszCap, ODsCZGeometries))
     680           4 :         return m_bSupportsZGeometries;
     681             : 
     682         261 :     return FALSE;
     683             : }
     684             : 
     685             : /************************************************************************/
     686             : /*                               Create()                               */
     687             : /************************************************************************/
     688             : 
     689         170 : int OGRGeoJSONDataSource::Create(const char *pszName,
     690             :                                  CSLConstList /* papszOptions */)
     691             : {
     692         170 :     CPLAssert(nullptr == fpOut_);
     693             : 
     694         170 :     if (strcmp(pszName, "/dev/stdout") == 0)
     695           0 :         pszName = "/vsistdout/";
     696             : 
     697         338 :     bFpOutputIsSeekable_ = !(strcmp(pszName, "/vsistdout/") == 0 ||
     698         168 :                              STARTS_WITH(pszName, "/vsigzip/") ||
     699         162 :                              STARTS_WITH(pszName, "/vsizip/"));
     700             : 
     701             :     /* -------------------------------------------------------------------- */
     702             :     /*     File overwrite not supported.                                    */
     703             :     /* -------------------------------------------------------------------- */
     704             :     VSIStatBufL sStatBuf;
     705         170 :     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         170 :     fpOut_ = VSIFOpenExL(pszName, "w", true);
     716         170 :     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         168 :     pszName_ = CPLStrdup(pszName);
     725             : 
     726         168 :     return TRUE;
     727             : }
     728             : 
     729             : /************************************************************************/
     730             : /*                       SetGeometryTranslation()                       */
     731             : /************************************************************************/
     732             : 
     733         571 : void OGRGeoJSONDataSource::SetGeometryTranslation(GeometryTranslation type)
     734             : {
     735         571 :     flTransGeom_ = type;
     736         571 : }
     737             : 
     738             : /************************************************************************/
     739             : /*                      SetAttributesTranslation()                      */
     740             : /************************************************************************/
     741             : 
     742         571 : void OGRGeoJSONDataSource::SetAttributesTranslation(AttributesTranslation type)
     743             : {
     744         571 :     flTransAttrs_ = type;
     745         571 : }
     746             : 
     747             : /************************************************************************/
     748             : /*                   PRIVATE FUNCTIONS IMPLEMENTATION                   */
     749             : /************************************************************************/
     750             : 
     751         802 : bool OGRGeoJSONDataSource::Clear()
     752             : {
     753        1506 :     for (int i = 0; i < nLayers_; i++)
     754             :     {
     755         704 :         if (papoLayers_ != nullptr)
     756         537 :             delete papoLayers_[i];
     757             :         else
     758         167 :             delete papoLayersWriter_[i];
     759             :     }
     760             : 
     761         802 :     CPLFree(papoLayers_);
     762         802 :     papoLayers_ = nullptr;
     763         802 :     CPLFree(papoLayersWriter_);
     764         802 :     papoLayersWriter_ = nullptr;
     765         802 :     nLayers_ = 0;
     766             : 
     767         802 :     CPLFree(pszName_);
     768         802 :     pszName_ = nullptr;
     769             : 
     770         802 :     CPLFree(pszGeoData_);
     771         802 :     pszGeoData_ = nullptr;
     772         802 :     nGeoDataLen_ = 0;
     773             : 
     774         802 :     bool bRet = true;
     775         802 :     if (fpOut_)
     776             :     {
     777         167 :         if (VSIFCloseL(fpOut_) != 0)
     778           0 :             bRet = false;
     779         167 :         fpOut_ = nullptr;
     780             :     }
     781         802 :     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          24 : int OGRGeoJSONDataSource::ReadFromService(GDALOpenInfo *poOpenInfo,
     831             :                                           const char *pszSource)
     832             : {
     833          24 :     CPLAssert(nullptr == pszGeoData_);
     834          24 :     CPLAssert(nullptr != pszSource);
     835             : 
     836          24 :     CPLErrorReset();
     837             : 
     838             :     /* -------------------------------------------------------------------- */
     839             :     /*      Look if we already cached the content.                          */
     840             :     /* -------------------------------------------------------------------- */
     841          24 :     char *pszStoredContent = OGRGeoJSONDriverStealStoredContent(pszSource);
     842          24 :     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           8 :     CPLHTTPResult *pResult = GeoJSONHTTPFetchWithContentTypeHeader(
     865           8 :         pszSource, /* bCanUsePOST = */ osJSonFlavor_ == "ESRIJSON", poOpenInfo);
     866           8 :     if (!pResult)
     867             :     {
     868           0 :         return FALSE;
     869             :     }
     870             : 
     871             :     /* -------------------------------------------------------------------- */
     872             :     /*      Copy returned GeoJSON data to text buffer.                      */
     873             :     /* -------------------------------------------------------------------- */
     874           8 :     char *pszData = reinterpret_cast<char *>(pResult->pabyData);
     875             : 
     876             :     // Directly assign CPLHTTPResult::pabyData to pszGeoData_.
     877           8 :     pszGeoData_ = pszData;
     878           8 :     nGeoDataLen_ = pResult->nDataLen;
     879           8 :     pResult->pabyData = nullptr;
     880           8 :     pResult->nDataLen = 0;
     881             : 
     882           8 :     pszName_ = CPLStrdup(pszSource);
     883             : 
     884             :     /* -------------------------------------------------------------------- */
     885             :     /*      Cleanup HTTP resources.                                         */
     886             :     /* -------------------------------------------------------------------- */
     887           8 :     CPLHTTPDestroyResult(pResult);
     888             : 
     889           8 :     CPLAssert(nullptr != pszGeoData_);
     890             : 
     891             :     /* -------------------------------------------------------------------- */
     892             :     /*      Cache the content if it is not handled by this driver, but      */
     893             :     /*      another related one.                                            */
     894             :     /* -------------------------------------------------------------------- */
     895           8 :     if (EQUAL(pszSource, poOpenInfo->pszFilename) && osJSonFlavor_ == "GeoJSON")
     896             :     {
     897           7 :         if (!GeoJSONIsObject(pszGeoData_, poOpenInfo))
     898             :         {
     899           3 :             if (ESRIJSONIsObject(pszGeoData_, poOpenInfo) ||
     900           3 :                 TopoJSONIsObject(pszGeoData_, poOpenInfo) ||
     901           9 :                 GeoJSONSeqIsObject(pszGeoData_, poOpenInfo) ||
     902           3 :                 JSONFGIsObject(pszGeoData_, poOpenInfo))
     903             :             {
     904           0 :                 OGRGeoJSONDriverStoreContent(pszSource, pszGeoData_);
     905           0 :                 pszGeoData_ = nullptr;
     906           0 :                 nGeoDataLen_ = 0;
     907             :             }
     908             :             else
     909             :             {
     910           3 :                 OGRGeoJSONDriverStoreContent(
     911             :                     pszSource, CPLStrdup(INVALID_CONTENT_FOR_JSON_LIKE));
     912             :             }
     913           3 :             return false;
     914             :         }
     915             :     }
     916             : 
     917           5 :     return TRUE;
     918             : }
     919             : 
     920             : /************************************************************************/
     921             : /*                          RemoveJSonPStuff()                          */
     922             : /************************************************************************/
     923             : 
     924         253 : void OGRGeoJSONDataSource::RemoveJSonPStuff()
     925             : {
     926         253 :     const char *const apszPrefix[] = {"loadGeoJSON(", "jsonp("};
     927         759 :     for (size_t iP = 0; iP < CPL_ARRAYSIZE(apszPrefix); iP++)
     928             :     {
     929         506 :         if (strncmp(pszGeoData_, apszPrefix[iP], strlen(apszPrefix[iP])) == 0)
     930             :         {
     931           2 :             const size_t nDataLen = strlen(pszGeoData_);
     932           2 :             memmove(pszGeoData_, pszGeoData_ + strlen(apszPrefix[iP]),
     933           2 :                     nDataLen - strlen(apszPrefix[iP]));
     934           2 :             size_t i = nDataLen - strlen(apszPrefix[iP]);
     935           2 :             pszGeoData_[i] = '\0';
     936           4 :             while (i > 0 && pszGeoData_[i] != ')')
     937             :             {
     938           2 :                 i--;
     939             :             }
     940           2 :             pszGeoData_[i] = '\0';
     941             :         }
     942             :     }
     943         253 : }
     944             : 
     945             : /************************************************************************/
     946             : /*                             LoadLayers()                             */
     947             : /************************************************************************/
     948             : 
     949         566 : void OGRGeoJSONDataSource::LoadLayers(GDALOpenInfo *poOpenInfo,
     950             :                                       GeoJSONSourceType nSrcType,
     951             :                                       const char *pszUnprefixed,
     952             :                                       const char *pszJSonFlavor)
     953             : {
     954         566 :     if (nullptr == pszGeoData_)
     955             :     {
     956           0 :         CPLError(CE_Failure, CPLE_ObjectNull, "%s data buffer empty",
     957             :                  pszJSonFlavor);
     958           0 :         return;
     959             :     }
     960             : 
     961         566 :     if (nSrcType != eGeoJSONSourceFile)
     962             :     {
     963         211 :         RemoveJSonPStuff();
     964             :     }
     965             : 
     966             :     /* -------------------------------------------------------------------- */
     967             :     /*      Is it ESRI Feature Service data ?                               */
     968             :     /* -------------------------------------------------------------------- */
     969         566 :     if (EQUAL(pszJSonFlavor, "ESRIJSON"))
     970             :     {
     971          44 :         OGRESRIJSONReader reader;
     972          22 :         if (nSrcType == eGeoJSONSourceFile)
     973             :         {
     974          19 :             if (!ReadFromFile(poOpenInfo, pszUnprefixed))
     975           0 :                 return;
     976             :         }
     977          22 :         OGRErr err = reader.Parse(pszGeoData_);
     978          22 :         if (OGRERR_NONE == err)
     979             :         {
     980          22 :             json_object *poObj = reader.GetJSonObject();
     981          22 :             CheckExceededTransferLimit(poObj);
     982          22 :             reader.ReadLayers(this, nSrcType);
     983             :         }
     984          22 :         return;
     985             :     }
     986             : 
     987             :     /* -------------------------------------------------------------------- */
     988             :     /*      Is it TopoJSON data ?                                           */
     989             :     /* -------------------------------------------------------------------- */
     990         544 :     if (EQUAL(pszJSonFlavor, "TOPOJSON"))
     991             :     {
     992          10 :         OGRTopoJSONReader reader;
     993           5 :         if (nSrcType == eGeoJSONSourceFile)
     994             :         {
     995           5 :             if (!ReadFromFile(poOpenInfo, pszUnprefixed))
     996           0 :                 return;
     997             :         }
     998          10 :         OGRErr err = reader.Parse(
     999           5 :             pszGeoData_,
    1000           5 :             nSrcType == eGeoJSONSourceService &&
    1001           0 :                 !STARTS_WITH_CI(poOpenInfo->pszFilename, "TopoJSON:"));
    1002           5 :         if (OGRERR_NONE == err)
    1003             :         {
    1004           5 :             reader.ReadLayers(this);
    1005             :         }
    1006           5 :         return;
    1007             :     }
    1008             : 
    1009         539 :     VSILFILE *fp = nullptr;
    1010         539 :     if (nSrcType == eGeoJSONSourceFile &&
    1011         331 :         !EQUAL(poOpenInfo->pszFilename, pszUnprefixed))
    1012             :     {
    1013           0 :         GDALOpenInfo oOpenInfo(pszUnprefixed, GA_ReadOnly);
    1014           0 :         if (oOpenInfo.fpL == nullptr || oOpenInfo.pabyHeader == nullptr)
    1015           0 :             return;
    1016           0 :         CPL_IGNORE_RET_VAL(oOpenInfo.TryToIngest(6000));
    1017           0 :         CPLFree(pszGeoData_);
    1018           0 :         pszGeoData_ =
    1019           0 :             CPLStrdup(reinterpret_cast<const char *>(oOpenInfo.pabyHeader));
    1020           0 :         fp = oOpenInfo.fpL;
    1021           0 :         oOpenInfo.fpL = nullptr;
    1022             :     }
    1023             : 
    1024         539 :     if (!GeoJSONIsObject(pszGeoData_, poOpenInfo))
    1025             :     {
    1026           0 :         CPLDebug(pszJSonFlavor, "No valid %s data found in source '%s'",
    1027             :                  pszJSonFlavor, pszName_);
    1028           0 :         if (fp)
    1029           0 :             VSIFCloseL(fp);
    1030           0 :         return;
    1031             :     }
    1032             : 
    1033             :     /* -------------------------------------------------------------------- */
    1034             :     /*      Configure GeoJSON format translator.                            */
    1035             :     /* -------------------------------------------------------------------- */
    1036         539 :     OGRGeoJSONReader *poReader = new OGRGeoJSONReader();
    1037         539 :     SetOptionsOnReader(poOpenInfo, poReader);
    1038             : 
    1039             :     /* -------------------------------------------------------------------- */
    1040             :     /*      Parse GeoJSON and build valid OGRLayer instance.                */
    1041             :     /* -------------------------------------------------------------------- */
    1042         539 :     bool bUseStreamingInterface = false;
    1043         539 :     const GIntBig nMaxBytesFirstPass = CPLAtoGIntBig(
    1044             :         CPLGetConfigOption("OGR_GEOJSON_MAX_BYTES_FIRST_PASS", "0"));
    1045         539 :     if ((fp != nullptr || poOpenInfo->fpL != nullptr) &&
    1046         331 :         (!STARTS_WITH(pszUnprefixed, "/vsistdin/") ||
    1047           0 :          (nMaxBytesFirstPass > 0 && nMaxBytesFirstPass <= 1000000)))
    1048             :     {
    1049         331 :         const char *pszStr = strstr(pszGeoData_, "\"features\"");
    1050         331 :         if (pszStr)
    1051             :         {
    1052         290 :             pszStr += strlen("\"features\"");
    1053         313 :             while (*pszStr && isspace(static_cast<unsigned char>(*pszStr)))
    1054          23 :                 pszStr++;
    1055         290 :             if (*pszStr == ':')
    1056             :             {
    1057         290 :                 pszStr++;
    1058         652 :                 while (*pszStr && isspace(static_cast<unsigned char>(*pszStr)))
    1059         362 :                     pszStr++;
    1060         290 :                 if (*pszStr == '[')
    1061             :                 {
    1062         290 :                     bUseStreamingInterface = true;
    1063             :                 }
    1064             :             }
    1065             :         }
    1066             :     }
    1067             : 
    1068         539 :     if (bUseStreamingInterface)
    1069             :     {
    1070         290 :         bool bTryStandardReading = false;
    1071         290 :         if (poReader->FirstPassReadLayer(this, fp ? fp : poOpenInfo->fpL,
    1072             :                                          bTryStandardReading))
    1073             :         {
    1074         284 :             if (fp)
    1075           0 :                 fp = nullptr;
    1076             :             else
    1077         284 :                 poOpenInfo->fpL = nullptr;
    1078         284 :             CheckExceededTransferLimit(poReader->GetJSonObject());
    1079             :         }
    1080             :         else
    1081             :         {
    1082           6 :             delete poReader;
    1083             :         }
    1084         290 :         if (!bTryStandardReading)
    1085             :         {
    1086         289 :             if (fp)
    1087           0 :                 VSIFCloseL(fp);
    1088         289 :             return;
    1089             :         }
    1090             : 
    1091           1 :         poReader = new OGRGeoJSONReader();
    1092           1 :         SetOptionsOnReader(poOpenInfo, poReader);
    1093             :     }
    1094             : 
    1095         250 :     if (fp)
    1096           0 :         VSIFCloseL(fp);
    1097         250 :     if (nSrcType == eGeoJSONSourceFile)
    1098             :     {
    1099          42 :         if (!ReadFromFile(poOpenInfo, pszUnprefixed))
    1100             :         {
    1101           0 :             delete poReader;
    1102           0 :             return;
    1103             :         }
    1104          42 :         RemoveJSonPStuff();
    1105             :     }
    1106         250 :     const OGRErr err = poReader->Parse(pszGeoData_);
    1107         250 :     if (OGRERR_NONE == err)
    1108             :     {
    1109         250 :         CheckExceededTransferLimit(poReader->GetJSonObject());
    1110             :     }
    1111             : 
    1112         250 :     poReader->ReadLayers(this);
    1113         250 :     delete poReader;
    1114             : }
    1115             : 
    1116             : /************************************************************************/
    1117             : /*                         SetOptionsOnReader()                         */
    1118             : /************************************************************************/
    1119             : 
    1120         540 : void OGRGeoJSONDataSource::SetOptionsOnReader(GDALOpenInfo *poOpenInfo,
    1121             :                                               OGRGeoJSONReader *poReader)
    1122             : {
    1123         540 :     if (eGeometryAsCollection == flTransGeom_)
    1124             :     {
    1125           0 :         poReader->SetPreserveGeometryType(false);
    1126           0 :         CPLDebug("GeoJSON", "Geometry as OGRGeometryCollection type.");
    1127             :     }
    1128             : 
    1129         540 :     if (eAttributesSkip == flTransAttrs_)
    1130             :     {
    1131           0 :         poReader->SetSkipAttributes(true);
    1132           0 :         CPLDebug("GeoJSON", "Skip all attributes.");
    1133             :     }
    1134             : 
    1135        1620 :     poReader->SetFlattenNestedAttributes(
    1136         540 :         CPLFetchBool(poOpenInfo->papszOpenOptions, "FLATTEN_NESTED_ATTRIBUTES",
    1137             :                      false),
    1138         540 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
    1139         540 :                              "NESTED_ATTRIBUTE_SEPARATOR", "_")[0]);
    1140             : 
    1141         540 :     const bool bDefaultNativeData = bUpdatable_;
    1142         540 :     poReader->SetStoreNativeData(CPLFetchBool(
    1143         540 :         poOpenInfo->papszOpenOptions, "NATIVE_DATA", bDefaultNativeData));
    1144             : 
    1145        1080 :     poReader->SetArrayAsString(CPLTestBool(CSLFetchNameValueDef(
    1146         540 :         poOpenInfo->papszOpenOptions, "ARRAY_AS_STRING",
    1147             :         CPLGetConfigOption("OGR_GEOJSON_ARRAY_AS_STRING", "NO"))));
    1148             : 
    1149        1080 :     poReader->SetDateAsString(CPLTestBool(CSLFetchNameValueDef(
    1150         540 :         poOpenInfo->papszOpenOptions, "DATE_AS_STRING",
    1151             :         CPLGetConfigOption("OGR_GEOJSON_DATE_AS_STRING", "NO"))));
    1152             : 
    1153        1080 :     const char *pszForeignMembers = CSLFetchNameValueDef(
    1154         540 :         poOpenInfo->papszOpenOptions, "FOREIGN_MEMBERS", "AUTO");
    1155         540 :     if (EQUAL(pszForeignMembers, "AUTO"))
    1156             :     {
    1157         536 :         poReader->SetForeignMemberProcessing(
    1158             :             OGRGeoJSONBaseReader::ForeignMemberProcessing::AUTO);
    1159             :     }
    1160           4 :     else if (EQUAL(pszForeignMembers, "ALL"))
    1161             :     {
    1162           1 :         poReader->SetForeignMemberProcessing(
    1163             :             OGRGeoJSONBaseReader::ForeignMemberProcessing::ALL);
    1164             :     }
    1165           3 :     else if (EQUAL(pszForeignMembers, "NONE"))
    1166             :     {
    1167           2 :         poReader->SetForeignMemberProcessing(
    1168             :             OGRGeoJSONBaseReader::ForeignMemberProcessing::NONE);
    1169             :     }
    1170           1 :     else if (EQUAL(pszForeignMembers, "STAC"))
    1171             :     {
    1172           1 :         poReader->SetForeignMemberProcessing(
    1173             :             OGRGeoJSONBaseReader::ForeignMemberProcessing::STAC);
    1174             :     }
    1175         540 : }
    1176             : 
    1177             : /************************************************************************/
    1178             : /*                     CheckExceededTransferLimit()                     */
    1179             : /************************************************************************/
    1180             : 
    1181         556 : void OGRGeoJSONDataSource::CheckExceededTransferLimit(json_object *poObj)
    1182             : {
    1183        1650 :     for (int i = 0; i < 2; i++)
    1184             :     {
    1185        1110 :         if (i == 1)
    1186             :         {
    1187         554 :             if (poObj && json_object_get_type(poObj) == json_type_object)
    1188             :             {
    1189         554 :                 poObj = CPL_json_object_object_get(poObj, "properties");
    1190             :             }
    1191             :         }
    1192        1110 :         if (poObj && json_object_get_type(poObj) == json_type_object)
    1193             :         {
    1194             :             json_object *poExceededTransferLimit =
    1195         596 :                 CPL_json_object_object_get(poObj, "exceededTransferLimit");
    1196         612 :             if (poExceededTransferLimit &&
    1197          16 :                 json_object_get_type(poExceededTransferLimit) ==
    1198             :                     json_type_boolean)
    1199             :             {
    1200          16 :                 bOtherPages_ = CPL_TO_BOOL(
    1201             :                     json_object_get_boolean(poExceededTransferLimit));
    1202          16 :                 return;
    1203             :             }
    1204             :         }
    1205             :     }
    1206             : }
    1207             : 
    1208             : /************************************************************************/
    1209             : /*                              AddLayer()                              */
    1210             : /************************************************************************/
    1211             : 
    1212         537 : void OGRGeoJSONDataSource::AddLayer(OGRGeoJSONLayer *poLayer)
    1213             : {
    1214         537 :     CPLAssert(papoLayersWriter_ == nullptr);
    1215             : 
    1216             :     // Return layer in readable state.
    1217         537 :     poLayer->ResetReading();
    1218             : 
    1219         537 :     papoLayers_ = static_cast<OGRGeoJSONLayer **>(
    1220         537 :         CPLRealloc(papoLayers_, sizeof(OGRGeoJSONLayer *) * (nLayers_ + 1)));
    1221         537 :     papoLayers_[nLayers_] = poLayer;
    1222         537 :     nLayers_++;
    1223         537 : }
    1224             : 
    1225             : /************************************************************************/
    1226             : /*                             FlushCache()                             */
    1227             : /************************************************************************/
    1228             : 
    1229         853 : CPLErr OGRGeoJSONDataSource::FlushCache(bool /*bAtClosing*/)
    1230             : {
    1231         853 :     if (papoLayersWriter_ != nullptr)
    1232             :     {
    1233         255 :         return papoLayersWriter_[0]->SyncToDisk() == OGRERR_NONE ? CE_None
    1234         255 :                                                                  : CE_Failure;
    1235             :     }
    1236             : 
    1237         598 :     CPLErr eErr = CE_None;
    1238        1135 :     for (int i = 0; i < nLayers_; i++)
    1239             :     {
    1240         537 :         if (papoLayers_[i]->HasBeenUpdated())
    1241             :         {
    1242          15 :             papoLayers_[i]->SetUpdated(false);
    1243             : 
    1244          15 :             bool bOK = false;
    1245             : 
    1246             :             // Disable all filters.
    1247          15 :             OGRFeatureQuery *poAttrQueryBak = papoLayers_[i]->m_poAttrQuery;
    1248          15 :             papoLayers_[i]->m_poAttrQuery = nullptr;
    1249          15 :             OGRGeometry *poFilterGeomBak = papoLayers_[i]->m_poFilterGeom;
    1250          15 :             papoLayers_[i]->m_poFilterGeom = nullptr;
    1251             : 
    1252             :             // If the source data only contained one single feature and
    1253             :             // that's still the case, then do not use a FeatureCollection
    1254             :             // on writing.
    1255          15 :             bool bAlreadyDone = false;
    1256          23 :             if (papoLayers_[i]->GetFeatureCount(TRUE) == 1 &&
    1257           8 :                 papoLayers_[i]->GetMetadata("NATIVE_DATA") == nullptr)
    1258             :             {
    1259           1 :                 papoLayers_[i]->ResetReading();
    1260           1 :                 OGRFeature *poFeature = papoLayers_[i]->GetNextFeature();
    1261           1 :                 if (poFeature != nullptr)
    1262             :                 {
    1263           1 :                     if (poFeature->GetNativeData() != nullptr)
    1264             :                     {
    1265           1 :                         bAlreadyDone = true;
    1266           2 :                         OGRGeoJSONWriteOptions oOptions;
    1267             :                         json_object *poObj =
    1268           1 :                             OGRGeoJSONWriteFeature(poFeature, oOptions);
    1269           1 :                         VSILFILE *fp = VSIFOpenL(pszName_, "wb");
    1270           1 :                         if (fp != nullptr)
    1271             :                         {
    1272           1 :                             bOK = VSIFPrintfL(
    1273             :                                       fp, "%s",
    1274             :                                       json_object_to_json_string(poObj)) > 0;
    1275           1 :                             VSIFCloseL(fp);
    1276             :                         }
    1277           1 :                         json_object_put(poObj);
    1278             :                     }
    1279           1 :                     delete poFeature;
    1280             :                 }
    1281             :             }
    1282             : 
    1283             :             // Otherwise do layer translation.
    1284          15 :             if (!bAlreadyDone)
    1285             :             {
    1286          14 :                 char **papszOptions = CSLAddString(nullptr, "-f");
    1287          14 :                 papszOptions = CSLAddString(papszOptions, "GeoJSON");
    1288             :                 GDALVectorTranslateOptions *psOptions =
    1289          14 :                     GDALVectorTranslateOptionsNew(papszOptions, nullptr);
    1290          14 :                 CSLDestroy(papszOptions);
    1291          14 :                 GDALDatasetH hSrcDS = this;
    1292          28 :                 CPLString osNewFilename(pszName_);
    1293          14 :                 osNewFilename += ".tmp";
    1294          14 :                 GDALDatasetH hOutDS = GDALVectorTranslate(
    1295             :                     osNewFilename, nullptr, 1, &hSrcDS, psOptions, nullptr);
    1296          14 :                 GDALVectorTranslateOptionsFree(psOptions);
    1297             : 
    1298          14 :                 if (hOutDS != nullptr)
    1299             :                 {
    1300          14 :                     CPLErrorReset();
    1301          14 :                     GDALClose(hOutDS);
    1302          14 :                     bOK = (CPLGetLastErrorType() == CE_None);
    1303             :                 }
    1304          14 :                 if (bOK)
    1305             :                 {
    1306          14 :                     const bool bOverwrite = CPLTestBool(
    1307             :                         CPLGetConfigOption("OGR_GEOJSON_REWRITE_IN_PLACE",
    1308             : #ifdef _WIN32
    1309             :                                            "YES"
    1310             : #else
    1311             :                                            "NO"
    1312             : #endif
    1313             :                                            ));
    1314          14 :                     if (bOverwrite)
    1315             :                     {
    1316           1 :                         VSILFILE *fpTarget = nullptr;
    1317           1 :                         for (int attempt = 0; attempt < 10; attempt++)
    1318             :                         {
    1319           1 :                             fpTarget = VSIFOpenL(pszName_, "rb+");
    1320           1 :                             if (fpTarget)
    1321           1 :                                 break;
    1322           0 :                             CPLSleep(0.1);
    1323             :                         }
    1324           1 :                         if (!fpTarget)
    1325             :                         {
    1326           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
    1327             :                                      "Cannot rewrite %s", pszName_);
    1328             :                         }
    1329             :                         else
    1330             :                         {
    1331           1 :                             bool bCopyOK = CPL_TO_BOOL(
    1332             :                                 VSIOverwriteFile(fpTarget, osNewFilename));
    1333           1 :                             if (VSIFCloseL(fpTarget) != 0)
    1334           0 :                                 bCopyOK = false;
    1335           1 :                             if (bCopyOK)
    1336             :                             {
    1337           1 :                                 VSIUnlink(osNewFilename);
    1338             :                             }
    1339             :                             else
    1340             :                             {
    1341           0 :                                 CPLError(CE_Failure, CPLE_AppDefined,
    1342             :                                          "Cannot rewrite %s with content of %s",
    1343             :                                          pszName_, osNewFilename.c_str());
    1344             :                             }
    1345             :                         }
    1346             :                     }
    1347             :                     else
    1348             :                     {
    1349          26 :                         CPLString osBackup(pszName_);
    1350          13 :                         osBackup += ".bak";
    1351          13 :                         if (VSIRename(pszName_, osBackup) < 0)
    1352             :                         {
    1353           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
    1354             :                                      "Cannot create backup copy");
    1355             :                         }
    1356          13 :                         else if (VSIRename(osNewFilename, pszName_) < 0)
    1357             :                         {
    1358           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
    1359             :                                      "Cannot rename %s to %s",
    1360             :                                      osNewFilename.c_str(), pszName_);
    1361             :                         }
    1362             :                         else
    1363             :                         {
    1364          13 :                             VSIUnlink(osBackup);
    1365             :                         }
    1366             :                     }
    1367             :                 }
    1368             :             }
    1369          15 :             if (!bOK)
    1370           0 :                 eErr = CE_Failure;
    1371             : 
    1372             :             // Restore filters.
    1373          15 :             papoLayers_[i]->m_poAttrQuery = poAttrQueryBak;
    1374          15 :             papoLayers_[i]->m_poFilterGeom = poFilterGeomBak;
    1375             :         }
    1376             :     }
    1377         598 :     return eErr;
    1378             : }

Generated by: LCOV version 1.14