LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/geojson - ogrgeojsondatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 516 599 86.1 %
Date: 2024-05-03 15:49:35 Functions: 21 22 95.5 %

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

Generated by: LCOV version 1.14