LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/geojson - ogrgeojsondriver.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 264 285 92.6 %
Date: 2025-07-05 13:22:42 Functions: 26 27 96.3 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implementation of OGRGeoJSONDriver class (OGR GeoJSON Driver).
       5             :  * Author:   Mateusz Loskot, mateusz@loskot.net
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2007, Mateusz Loskot
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_port.h"
      14             : #include "ogr_geojson.h"
      15             : 
      16             : #include <stdlib.h>
      17             : #include <string.h>
      18             : #include <limits>
      19             : 
      20             : #include "cpl_conv.h"
      21             : #include "cpl_error.h"
      22             : #include "cpl_http.h"
      23             : #include "cpl_multiproc.h"
      24             : #include "cpl_string.h"
      25             : #include "cpl_vsi.h"
      26             : // #include "json_object.h"
      27             : #include "gdal.h"
      28             : #include "gdal_priv.h"
      29             : #include "ogr_core.h"
      30             : #include "ogr_feature.h"
      31             : #include "ogrgeojsonutils.h"
      32             : #include "ogrsf_frmts.h"
      33             : 
      34             : static CPLMutex *ghMutex = nullptr;
      35             : static char *gpszSource = nullptr;
      36             : static char *gpszText = nullptr;
      37             : 
      38             : class OGRESRIFeatureServiceDataset;
      39             : 
      40             : /************************************************************************/
      41             : /*                      OGRESRIFeatureServiceLayer                      */
      42             : /************************************************************************/
      43             : 
      44             : class OGRESRIFeatureServiceLayer final : public OGRLayer
      45             : {
      46             :     OGRESRIFeatureServiceDataset *poDS;
      47             :     OGRFeatureDefn *poFeatureDefn;
      48             :     GIntBig nFeaturesRead;
      49             :     GIntBig nFirstFID;
      50             :     GIntBig nLastFID;
      51             :     bool bOtherPage;
      52             :     bool bUseSequentialFID;
      53             : 
      54             :   public:
      55             :     explicit OGRESRIFeatureServiceLayer(OGRESRIFeatureServiceDataset *poDS);
      56             :     virtual ~OGRESRIFeatureServiceLayer();
      57             : 
      58             :     void ResetReading() override;
      59             :     OGRFeature *GetNextFeature() override;
      60             :     GIntBig GetFeatureCount(int bForce = TRUE) override;
      61             :     OGRErr IGetExtent(int iGeomField, OGREnvelope *psExtent,
      62             :                       bool bForce = true) override;
      63             : 
      64             :     int TestCapability(const char *pszCap) override;
      65             : 
      66           8 :     OGRFeatureDefn *GetLayerDefn() override
      67             :     {
      68           8 :         return poFeatureDefn;
      69             :     }
      70             : };
      71             : 
      72             : /************************************************************************/
      73             : /*                       OGRESRIFeatureServiceDataset                   */
      74             : /************************************************************************/
      75             : 
      76             : class OGRESRIFeatureServiceDataset final : public GDALDataset
      77             : {
      78             :     CPLString m_osURL{};
      79             :     GIntBig m_nFirstOffset = 0;
      80             :     GIntBig m_nLastOffset = 0;
      81             :     std::unique_ptr<OGRGeoJSONDataSource> m_poCurrent{};
      82             :     std::unique_ptr<OGRESRIFeatureServiceLayer> m_poLayer{};
      83             :     GeoJSONSourceType m_nSrcType = eGeoJSONSourceUnknown;
      84             : 
      85             :     bool LoadPage();
      86             : 
      87             :   public:
      88             :     OGRESRIFeatureServiceDataset(
      89             :         const std::string &osURL,
      90             :         std::unique_ptr<OGRGeoJSONDataSource> &&poFirst,
      91             :         GeoJSONSourceType nSrcType);
      92             : 
      93           0 :     int GetLayerCount() override
      94             :     {
      95           0 :         return 1;
      96             :     }
      97             : 
      98             :     OGRLayer *GetLayer(int nLayer) override;
      99             : 
     100          52 :     OGRLayer *GetUnderlyingLayer()
     101             :     {
     102          52 :         return m_poCurrent->GetLayer(0);
     103             :     }
     104             : 
     105             :     bool MyResetReading();
     106             :     bool LoadNextPage();
     107             : 
     108           8 :     const CPLString &GetURL() const
     109             :     {
     110           8 :         return m_osURL;
     111             :     }
     112             : };
     113             : 
     114           8 : OGRLayer *OGRESRIFeatureServiceDataset::GetLayer(int nLayer)
     115             : {
     116           8 :     return (nLayer == 0) ? m_poLayer.get() : nullptr;
     117             : }
     118             : 
     119             : /************************************************************************/
     120             : /*                       OGRESRIFeatureServiceLayer()                   */
     121             : /************************************************************************/
     122             : 
     123           8 : OGRESRIFeatureServiceLayer::OGRESRIFeatureServiceLayer(
     124           8 :     OGRESRIFeatureServiceDataset *poDSIn)
     125             :     : poDS(poDSIn), nFeaturesRead(0), nFirstFID(0), nLastFID(0),
     126           8 :       bOtherPage(false), bUseSequentialFID(false)
     127             : {
     128           8 :     OGRFeatureDefn *poSrcFeatDefn = poDS->GetUnderlyingLayer()->GetLayerDefn();
     129           8 :     poFeatureDefn = new OGRFeatureDefn(poSrcFeatDefn->GetName());
     130           8 :     SetDescription(poFeatureDefn->GetName());
     131           8 :     poFeatureDefn->Reference();
     132           8 :     poFeatureDefn->SetGeomType(wkbNone);
     133             : 
     134          22 :     for (int i = 0; i < poSrcFeatDefn->GetFieldCount(); i++)
     135          14 :         poFeatureDefn->AddFieldDefn(poSrcFeatDefn->GetFieldDefn(i));
     136             : 
     137          16 :     for (int i = 0; i < poSrcFeatDefn->GetGeomFieldCount(); i++)
     138           8 :         poFeatureDefn->AddGeomFieldDefn(poSrcFeatDefn->GetGeomFieldDefn(i));
     139           8 : }
     140             : 
     141             : /************************************************************************/
     142             : /*                      ~OGRESRIFeatureServiceLayer()                   */
     143             : /************************************************************************/
     144             : 
     145          16 : OGRESRIFeatureServiceLayer::~OGRESRIFeatureServiceLayer()
     146             : {
     147           8 :     poFeatureDefn->Release();
     148          16 : }
     149             : 
     150             : /************************************************************************/
     151             : /*                            ResetReading()                            */
     152             : /************************************************************************/
     153             : 
     154          10 : void OGRESRIFeatureServiceLayer::ResetReading()
     155             : {
     156          10 :     poDS->MyResetReading();
     157          10 :     nFeaturesRead = 0;
     158          10 :     nLastFID = 0;
     159          10 :     bOtherPage = false;
     160          10 :     bUseSequentialFID = false;
     161          10 : }
     162             : 
     163             : /************************************************************************/
     164             : /*                            GetNextFeature()                          */
     165             : /************************************************************************/
     166             : 
     167          34 : OGRFeature *OGRESRIFeatureServiceLayer::GetNextFeature()
     168             : {
     169             :     while (true)
     170             :     {
     171          34 :         const bool bWasInFirstPage = !bOtherPage;
     172             : #if defined(__GNUC__)
     173             : #pragma GCC diagnostic push
     174             : #pragma GCC diagnostic ignored "-Wnull-dereference"
     175             : #endif
     176          34 :         OGRFeature *poSrcFeat = poDS->GetUnderlyingLayer()->GetNextFeature();
     177             : #if defined(__GNUC__)
     178             : #pragma GCC diagnostic pop
     179             : #endif
     180          34 :         if (poSrcFeat == nullptr)
     181             :         {
     182          20 :             if (!poDS->LoadNextPage())
     183          12 :                 return nullptr;
     184             : #if defined(__GNUC__)
     185             : #pragma GCC diagnostic push
     186             : #pragma GCC diagnostic ignored "-Wnull-dereference"
     187             : #endif
     188           8 :             poSrcFeat = poDS->GetUnderlyingLayer()->GetNextFeature();
     189             : #if defined(__GNUC__)
     190             : #pragma GCC diagnostic pop
     191             : #endif
     192           8 :             if (poSrcFeat == nullptr)
     193           0 :                 return nullptr;
     194           8 :             bOtherPage = true;
     195          16 :             if (bWasInFirstPage && poSrcFeat->GetFID() != 0 &&
     196           8 :                 poSrcFeat->GetFID() == nFirstFID)
     197             :             {
     198             :                 // End-less looping
     199           0 :                 CPLDebug("ESRIJSON", "Scrolling not working. Stopping");
     200           0 :                 delete poSrcFeat;
     201           0 :                 return nullptr;
     202             :             }
     203           8 :             if (bWasInFirstPage && poSrcFeat->GetFID() == 0 &&
     204           0 :                 nLastFID == nFeaturesRead - 1)
     205             :             {
     206           0 :                 bUseSequentialFID = true;
     207             :             }
     208             :         }
     209          22 :         if (nFeaturesRead == 0)
     210          14 :             nFirstFID = poSrcFeat->GetFID();
     211             : 
     212          22 :         OGRFeature *poFeature = new OGRFeature(poFeatureDefn);
     213          22 :         poFeature->SetFrom(poSrcFeat);
     214          22 :         if (bUseSequentialFID)
     215           0 :             poFeature->SetFID(nFeaturesRead);
     216             :         else
     217          22 :             poFeature->SetFID(poSrcFeat->GetFID());
     218          22 :         nLastFID = poFeature->GetFID();
     219          22 :         nFeaturesRead++;
     220          22 :         delete poSrcFeat;
     221             : 
     222          44 :         if ((m_poFilterGeom == nullptr ||
     223          44 :              FilterGeometry(poFeature->GetGeometryRef())) &&
     224          22 :             (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
     225             :         {
     226          22 :             return poFeature;
     227             :         }
     228           0 :         delete poFeature;
     229           0 :     }
     230             : }
     231             : 
     232             : /************************************************************************/
     233             : /*                          TestCapability()                            */
     234             : /************************************************************************/
     235             : 
     236           6 : int OGRESRIFeatureServiceLayer::TestCapability(const char *pszCap)
     237             : {
     238           6 :     if (EQUAL(pszCap, OLCFastFeatureCount))
     239           2 :         return m_poAttrQuery == nullptr && m_poFilterGeom == nullptr;
     240           4 :     if (EQUAL(pszCap, OLCFastGetExtent))
     241           2 :         return FALSE;
     242             : #if defined(__GNUC__)
     243             : #pragma GCC diagnostic push
     244             : #pragma GCC diagnostic ignored "-Wnull-dereference"
     245             : #endif
     246           2 :     auto poUnderlyingLayer = poDS->GetUnderlyingLayer();
     247           2 :     return poUnderlyingLayer->TestCapability(pszCap);
     248             : #if defined(__GNUC__)
     249             : #pragma GCC diagnostic pop
     250             : #endif
     251             : }
     252             : 
     253             : /************************************************************************/
     254             : /*                          GetFeatureCount()                           */
     255             : /************************************************************************/
     256             : 
     257           4 : GIntBig OGRESRIFeatureServiceLayer::GetFeatureCount(int bForce)
     258             : {
     259           4 :     GIntBig nFeatureCount = -1;
     260           4 :     if (m_poAttrQuery == nullptr && m_poFilterGeom == nullptr)
     261             :     {
     262             :         CPLString osNewURL =
     263           8 :             CPLURLAddKVP(poDS->GetURL(), "returnCountOnly", "true");
     264           4 :         osNewURL = CPLURLAddKVP(osNewURL, "resultRecordCount", nullptr);
     265           4 :         CPLErrorReset();
     266           4 :         CPLHTTPResult *pResult = CPLHTTPFetch(osNewURL, nullptr);
     267           4 :         if (pResult != nullptr && pResult->nDataLen != 0 &&
     268           8 :             CPLGetLastErrorNo() == 0 && pResult->nStatus == 0)
     269             :         {
     270             :             const char *pszCount =
     271           2 :                 strstr((const char *)pResult->pabyData, "\"count\"");
     272           2 :             if (pszCount)
     273             :             {
     274           2 :                 pszCount = strchr(pszCount, ':');
     275           2 :                 if (pszCount)
     276             :                 {
     277           2 :                     pszCount++;
     278           2 :                     nFeatureCount = CPLAtoGIntBig(pszCount);
     279             :                 }
     280             :             }
     281             :         }
     282           4 :         CPLHTTPDestroyResult(pResult);
     283             :     }
     284           4 :     if (nFeatureCount < 0)
     285           2 :         nFeatureCount = OGRLayer::GetFeatureCount(bForce);
     286           4 :     return nFeatureCount;
     287             : }
     288             : 
     289             : /************************************************************************/
     290             : /*                              IGetExtent()                            */
     291             : /************************************************************************/
     292             : 
     293           4 : OGRErr OGRESRIFeatureServiceLayer::IGetExtent(int iGeomField,
     294             :                                               OGREnvelope *psExtent,
     295             :                                               bool bForce)
     296             : {
     297           4 :     OGRErr eErr = OGRERR_FAILURE;
     298             :     CPLString osNewURL =
     299           4 :         CPLURLAddKVP(poDS->GetURL(), "returnExtentOnly", "true");
     300           4 :     osNewURL = CPLURLAddKVP(osNewURL, "resultRecordCount", nullptr);
     301           4 :     osNewURL = CPLURLAddKVP(osNewURL, "f", "geojson");
     302           4 :     CPLErrorReset();
     303           4 :     CPLHTTPResult *pResult = CPLHTTPFetch(osNewURL, nullptr);
     304           4 :     if (pResult != nullptr && pResult->nDataLen != 0 &&
     305           8 :         CPLGetLastErrorNo() == 0 && pResult->nStatus == 0)
     306             :     {
     307             :         const char *pszBBox =
     308           2 :             strstr((const char *)pResult->pabyData, "\"bbox\"");
     309           2 :         if (pszBBox)
     310             :         {
     311           2 :             pszBBox = strstr(pszBBox, ":[");
     312           2 :             if (pszBBox)
     313             :             {
     314           2 :                 pszBBox += 2;
     315           2 :                 char **papszTokens = CSLTokenizeString2(pszBBox, ",", 0);
     316           2 :                 if (CSLCount(papszTokens) >= 4)
     317             :                 {
     318           2 :                     psExtent->MinX = CPLAtof(papszTokens[0]);
     319           2 :                     psExtent->MinY = CPLAtof(papszTokens[1]);
     320           2 :                     psExtent->MaxX = CPLAtof(papszTokens[2]);
     321           2 :                     psExtent->MaxY = CPLAtof(papszTokens[3]);
     322           2 :                     eErr = OGRERR_NONE;
     323             :                 }
     324           2 :                 CSLDestroy(papszTokens);
     325             :             }
     326             :         }
     327             :     }
     328           4 :     CPLHTTPDestroyResult(pResult);
     329           4 :     if (eErr == OGRERR_FAILURE)
     330           2 :         eErr = OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
     331           8 :     return eErr;
     332             : }
     333             : 
     334             : /************************************************************************/
     335             : /*                      OGRESRIFeatureServiceDataset()                  */
     336             : /************************************************************************/
     337             : 
     338           8 : OGRESRIFeatureServiceDataset::OGRESRIFeatureServiceDataset(
     339             :     const std::string &osURL, std::unique_ptr<OGRGeoJSONDataSource> &&poFirst,
     340           8 :     GeoJSONSourceType nSrcType)
     341           8 :     : m_osURL(osURL), m_poCurrent(std::move(poFirst)), m_nSrcType(nSrcType)
     342             : {
     343           8 :     m_poLayer = std::make_unique<OGRESRIFeatureServiceLayer>(this);
     344           8 :     if (CPLURLGetValue(m_osURL, "resultRecordCount").empty())
     345             :     {
     346             :         // We assume that if the server sets the exceededTransferLimit, the
     347             :         // and resultRecordCount is not set, the number of features returned
     348             :         // in our first request is the maximum allowed by the server
     349             :         // So set it for following requests.
     350           4 :         m_osURL = CPLURLAddKVP(
     351             :             m_osURL, "resultRecordCount",
     352             :             CPLSPrintf("%d", static_cast<int>(
     353           4 :                                  m_poCurrent->GetLayer(0)->GetFeatureCount())));
     354             :     }
     355             :     else
     356             :     {
     357             :         const int nUserSetRecordCount =
     358           6 :             atoi(CPLURLGetValue(m_osURL, "resultRecordCount"));
     359           6 :         if (nUserSetRecordCount > m_poCurrent->GetLayer(0)->GetFeatureCount())
     360             :         {
     361           2 :             CPLError(
     362             :                 CE_Warning, CPLE_AppDefined,
     363             :                 "Specified resultRecordCount=%d is greater than "
     364             :                 "the maximum %d supported by the server",
     365             :                 nUserSetRecordCount,
     366           2 :                 static_cast<int>(m_poCurrent->GetLayer(0)->GetFeatureCount()));
     367             :         }
     368             :     }
     369           8 :     m_nFirstOffset = CPLAtoGIntBig(CPLURLGetValue(m_osURL, "resultOffset"));
     370           8 :     m_nLastOffset = m_nFirstOffset;
     371           8 : }
     372             : 
     373             : /************************************************************************/
     374             : /*                           MyResetReading()                           */
     375             : /************************************************************************/
     376             : 
     377          10 : bool OGRESRIFeatureServiceDataset::MyResetReading()
     378             : {
     379          10 :     if (m_nLastOffset > m_nFirstOffset)
     380             :     {
     381           8 :         m_nLastOffset = m_nFirstOffset;
     382           8 :         return LoadPage();
     383             :     }
     384             : 
     385             : #if defined(__GNUC__)
     386             : #pragma GCC diagnostic push
     387             : #pragma GCC diagnostic ignored "-Wnull-dereference"
     388             : #endif
     389           2 :     m_poCurrent->GetLayer(0)->ResetReading();
     390             : #if defined(__GNUC__)
     391             : #pragma GCC diagnostic pop
     392             : #endif
     393           2 :     return true;
     394             : }
     395             : 
     396             : /************************************************************************/
     397             : /*                             LoadNextPage()                           */
     398             : /************************************************************************/
     399             : 
     400          20 : bool OGRESRIFeatureServiceDataset::LoadNextPage()
     401             : {
     402          20 :     if (!m_poCurrent->HasOtherPages())
     403           6 :         return false;
     404             : #if defined(__GNUC__)
     405             : #pragma GCC diagnostic push
     406             : #pragma GCC diagnostic ignored "-Wnull-dereference"
     407             : #endif
     408          14 :     const auto nCurPageFC = m_poCurrent->GetLayer(0)->GetFeatureCount();
     409             : #if defined(__GNUC__)
     410             : #pragma GCC diagnostic pop
     411             : #endif
     412          14 :     if (m_nLastOffset > std::numeric_limits<GIntBig>::max() - nCurPageFC)
     413           0 :         return false;
     414          14 :     m_nLastOffset += nCurPageFC;
     415          14 :     return LoadPage();
     416             : }
     417             : 
     418             : /************************************************************************/
     419             : /*                                 LoadPage()                           */
     420             : /************************************************************************/
     421             : 
     422          22 : bool OGRESRIFeatureServiceDataset::LoadPage()
     423             : {
     424             :     CPLString osNewURL = CPLURLAddKVP(m_osURL, "resultOffset",
     425          44 :                                       CPLSPrintf(CPL_FRMT_GIB, m_nLastOffset));
     426          44 :     auto poDS = std::make_unique<OGRGeoJSONDataSource>();
     427          44 :     GDALOpenInfo oOpenInfo(osNewURL, GA_ReadOnly);
     428          38 :     if (!poDS->Open(&oOpenInfo, m_nSrcType, m_poCurrent->GetJSonFlavor()) ||
     429          16 :         poDS->GetLayerCount() == 0)
     430             :     {
     431           6 :         return false;
     432             :     }
     433          16 :     m_poCurrent = std::move(poDS);
     434          16 :     return true;
     435             : }
     436             : 
     437             : /************************************************************************/
     438             : /*                        OGRGeoJSONDriverIdentify()                    */
     439             : /************************************************************************/
     440             : 
     441       55175 : static int OGRGeoJSONDriverIdentifyInternal(GDALOpenInfo *poOpenInfo,
     442             :                                             GeoJSONSourceType &nSrcType)
     443             : {
     444             :     /* -------------------------------------------------------------------- */
     445             :     /*      Determine type of data source: text file (.geojson, .json),     */
     446             :     /*      Web Service or text passed directly and load data.              */
     447             :     /* -------------------------------------------------------------------- */
     448             : 
     449       55175 :     nSrcType = GeoJSONGetSourceType(poOpenInfo);
     450       55175 :     if (nSrcType == eGeoJSONSourceUnknown)
     451             :     {
     452       54143 :         const char *pszHeader =
     453             :             reinterpret_cast<const char *>(poOpenInfo->pabyHeader);
     454       54143 :         if (pszHeader && STARTS_WITH(pszHeader, "{\"properties\":{"))
     455           3 :             return GDAL_IDENTIFY_UNKNOWN;
     456             : 
     457       54140 :         return FALSE;
     458             :     }
     459             : 
     460        1032 :     if (nSrcType == eGeoJSONSourceService)
     461             :     {
     462          37 :         if (poOpenInfo->IsSingleAllowedDriver("GeoJSON"))
     463           1 :             return TRUE;
     464          36 :         if (!STARTS_WITH_CI(poOpenInfo->pszFilename, "GeoJSON:"))
     465             :         {
     466          36 :             return -1;
     467             :         }
     468             :     }
     469             : 
     470             :     // If this looks like a file that can be handled by the STACTA driver,
     471             :     // and that one is available, then don't identify the file.
     472         995 :     const char *pszHeader =
     473             :         reinterpret_cast<const char *>(poOpenInfo->pabyHeader);
     474        1630 :     if (pszHeader != nullptr &&
     475         635 :         strstr(pszHeader, "\"stac_extensions\"") != nullptr &&
     476        1632 :         strstr(pszHeader, "\"tiled-assets\"") != nullptr &&
     477           2 :         GDALGetDriverByName("STACTA") != nullptr)
     478             :     {
     479           2 :         if (poOpenInfo->IsSingleAllowedDriver("GeoJSON"))
     480           2 :             return TRUE;
     481           0 :         return FALSE;
     482             :     }
     483             : 
     484         993 :     return TRUE;
     485             : }
     486             : 
     487             : /************************************************************************/
     488             : /*                        OGRGeoJSONDriverIdentify()                    */
     489             : /************************************************************************/
     490             : 
     491       54670 : static int OGRGeoJSONDriverIdentify(GDALOpenInfo *poOpenInfo)
     492             : {
     493             :     GeoJSONSourceType nSrcType;
     494      109340 :     return OGRGeoJSONDriverIdentifyInternal(poOpenInfo, nSrcType);
     495             : }
     496             : 
     497             : /************************************************************************/
     498             : /*                           Open()                                     */
     499             : /************************************************************************/
     500             : 
     501         505 : static GDALDataset *OGRGeoJSONDriverOpen(GDALOpenInfo *poOpenInfo)
     502             : {
     503             :     GeoJSONSourceType nSrcType;
     504         505 :     if (OGRGeoJSONDriverIdentifyInternal(poOpenInfo, nSrcType) == FALSE)
     505             :     {
     506           0 :         return nullptr;
     507             :     }
     508         505 :     return OGRGeoJSONDriverOpenInternal(poOpenInfo, nSrcType, "GeoJSON");
     509             : }
     510             : 
     511             : /************************************************************************/
     512             : /*                     OGRGeoJSONDriverOpenInternal()                   */
     513             : /************************************************************************/
     514             : 
     515         538 : GDALDataset *OGRGeoJSONDriverOpenInternal(GDALOpenInfo *poOpenInfo,
     516             :                                           GeoJSONSourceType nSrcType,
     517             :                                           const char *pszJSonFlavor)
     518             : {
     519        1076 :     auto poDS = std::make_unique<OGRGeoJSONDataSource>();
     520             : 
     521             :     /* -------------------------------------------------------------------- */
     522             :     /*      Processing configuration options.                               */
     523             :     /* -------------------------------------------------------------------- */
     524             : 
     525             :     // TODO: Currently, options are based on environment variables.
     526             :     //       This is workaround for not yet implemented Andrey's concept
     527             :     //       described in document 'RFC 10: OGR Open Parameters'.
     528             : 
     529         538 :     poDS->SetGeometryTranslation(OGRGeoJSONDataSource::eGeometryPreserve);
     530         538 :     const char *pszOpt = CPLGetConfigOption("GEOMETRY_AS_COLLECTION", nullptr);
     531         538 :     if (nullptr != pszOpt && STARTS_WITH_CI(pszOpt, "YES"))
     532             :     {
     533           0 :         poDS->SetGeometryTranslation(
     534             :             OGRGeoJSONDataSource::eGeometryAsCollection);
     535             :     }
     536             : 
     537         538 :     poDS->SetAttributesTranslation(OGRGeoJSONDataSource::eAttributesPreserve);
     538         538 :     pszOpt = CPLGetConfigOption("ATTRIBUTES_SKIP", nullptr);
     539         538 :     if (nullptr != pszOpt && STARTS_WITH_CI(pszOpt, "YES"))
     540             :     {
     541           0 :         poDS->SetAttributesTranslation(OGRGeoJSONDataSource::eAttributesSkip);
     542             :     }
     543             : 
     544             :     /* -------------------------------------------------------------------- */
     545             :     /*      Open and start processing GeoJSON datasource to OGR objects.    */
     546             :     /* -------------------------------------------------------------------- */
     547         538 :     if (!poDS->Open(poOpenInfo, nSrcType, pszJSonFlavor))
     548             :     {
     549          57 :         poDS.reset();
     550             :     }
     551             : 
     552         538 :     if (poDS != nullptr && poDS->HasOtherPages())
     553             :     {
     554           8 :         const char *pszFilename = poOpenInfo->pszFilename;
     555           8 :         if (STARTS_WITH_CI(pszFilename, "ESRIJSON:"))
     556           1 :             pszFilename += strlen("ESRIJSON:");
     557           8 :         if (STARTS_WITH(pszFilename, "http") ||
     558           8 :             STARTS_WITH(pszFilename, "/vsimem/"))
     559             :         {
     560           8 :             const char *pszFSP = CSLFetchNameValue(poOpenInfo->papszOpenOptions,
     561             :                                                    "FEATURE_SERVER_PAGING");
     562             :             const bool bHasResultOffset =
     563           8 :                 !CPLURLGetValue(pszFilename, "resultOffset").empty();
     564           8 :             if ((!bHasResultOffset &&
     565          16 :                  (pszFSP == nullptr || CPLTestBool(pszFSP))) ||
     566           0 :                 (bHasResultOffset && pszFSP != nullptr && CPLTestBool(pszFSP)))
     567             :             {
     568             :                 return new OGRESRIFeatureServiceDataset(
     569           8 :                     pszFilename, std::move(poDS), nSrcType);
     570             :             }
     571             :         }
     572             :     }
     573             : 
     574         530 :     return poDS.release();
     575             : }
     576             : 
     577             : /************************************************************************/
     578             : /*                               Create()                               */
     579             : /************************************************************************/
     580             : 
     581         159 : static GDALDataset *OGRGeoJSONDriverCreate(const char *pszName,
     582             :                                            int /* nBands */, int /* nXSize */,
     583             :                                            int /* nYSize */,
     584             :                                            GDALDataType /* eDT */,
     585             :                                            char **papszOptions)
     586             : {
     587         159 :     OGRGeoJSONDataSource *poDS = new OGRGeoJSONDataSource();
     588             : 
     589         159 :     if (!poDS->Create(pszName, papszOptions))
     590             :     {
     591           2 :         delete poDS;
     592           2 :         poDS = nullptr;
     593             :     }
     594             : 
     595         159 :     return poDS;
     596             : }
     597             : 
     598             : /************************************************************************/
     599             : /*                               Delete()                               */
     600             : /************************************************************************/
     601             : 
     602          38 : static CPLErr OGRGeoJSONDriverDelete(const char *pszFilename)
     603             : {
     604          38 :     if (VSIUnlink(pszFilename) == 0)
     605             :     {
     606          38 :         return CE_None;
     607             :     }
     608             : 
     609           0 :     CPLDebug("GeoJSON", "Failed to delete \'%s\'", pszFilename);
     610             : 
     611           0 :     return CE_Failure;
     612             : }
     613             : 
     614             : /************************************************************************/
     615             : /*                      OGRGeoJSONDriverStoreContent()                  */
     616             : /************************************************************************/
     617             : 
     618          37 : void OGRGeoJSONDriverStoreContent(const char *pszSource, char *pszText)
     619             : {
     620          37 :     CPLMutexHolderD(&ghMutex);
     621          37 :     CPLAssert(pszSource);
     622          37 :     CPLAssert(pszText);
     623             : 
     624          37 :     CPLFree(gpszSource);
     625          37 :     CPLFree(gpszText);
     626          37 :     gpszSource = CPLStrdup(pszSource);
     627          37 :     gpszText = pszText;
     628          37 : }
     629             : 
     630             : /************************************************************************/
     631             : /*                    OGRGeoJSONDriverStealStoredContent()              */
     632             : /************************************************************************/
     633             : 
     634          41 : char *OGRGeoJSONDriverStealStoredContent(const char *pszSource)
     635             : {
     636          82 :     CPLMutexHolderD(&ghMutex);
     637          41 :     if (gpszSource && EQUAL(pszSource, gpszSource))
     638             :     {
     639          34 :         char *pszRet = gpszText;
     640          34 :         CPLFree(gpszSource);
     641          34 :         gpszSource = nullptr;
     642          34 :         gpszText = nullptr;
     643          34 :         return pszRet;
     644             :     }
     645           7 :     return nullptr;
     646             : }
     647             : 
     648             : /************************************************************************/
     649             : /*                        OGRGeoJSONDriverUnload()                      */
     650             : /************************************************************************/
     651             : 
     652        1114 : static void OGRGeoJSONDriverUnload(GDALDriver *)
     653             : {
     654        1114 :     if (ghMutex)
     655           0 :         CPLDestroyMutex(ghMutex);
     656        1114 :     ghMutex = nullptr;
     657        1114 :     CPLFree(gpszSource);
     658        1114 :     CPLFree(gpszText);
     659        1114 :     gpszSource = nullptr;
     660        1114 :     gpszText = nullptr;
     661        1114 : }
     662             : 
     663             : /************************************************************************/
     664             : /*                           RegisterOGRGeoJSON()                       */
     665             : /************************************************************************/
     666             : 
     667        1928 : void RegisterOGRGeoJSON()
     668             : {
     669        1928 :     if (!GDAL_CHECK_VERSION("OGR/GeoJSON driver"))
     670           0 :         return;
     671             : 
     672        1928 :     if (GDALGetDriverByName("GeoJSON") != nullptr)
     673         282 :         return;
     674             : 
     675        1646 :     GDALDriver *poDriver = new GDALDriver();
     676             : 
     677        1646 :     poDriver->SetDescription("GeoJSON");
     678        1646 :     poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
     679        1646 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATE_LAYER, "YES");
     680        1646 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATE_FIELD, "YES");
     681        1646 :     poDriver->SetMetadataItem(GDAL_DCAP_DELETE_FIELD, "YES");
     682        1646 :     poDriver->SetMetadataItem(GDAL_DCAP_REORDER_FIELDS, "YES");
     683        1646 :     poDriver->SetMetadataItem(GDAL_DMD_ALTER_FIELD_DEFN_FLAGS, "Name Type");
     684        1646 :     poDriver->SetMetadataItem(GDAL_DCAP_Z_GEOMETRIES, "YES");
     685        1646 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "GeoJSON");
     686        1646 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "json geojson");
     687        1646 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
     688        1646 :                               "drivers/vector/geojson.html");
     689             : 
     690        1646 :     poDriver->SetMetadataItem(
     691             :         GDAL_DMD_OPENOPTIONLIST,
     692             :         "<OpenOptionList>"
     693             :         "  <Option name='FLATTEN_NESTED_ATTRIBUTES' type='boolean' "
     694             :         "description='Whether to recursively explore nested objects and "
     695             :         "produce flatten OGR attributes' default='NO'/>"
     696             :         "  <Option name='NESTED_ATTRIBUTE_SEPARATOR' type='string' "
     697             :         "description='Separator between components of nested attributes' "
     698             :         "default='_'/>"
     699             :         "  <Option name='FEATURE_SERVER_PAGING' type='boolean' "
     700             :         "description='Whether to automatically scroll through results with a "
     701             :         "ArcGIS Feature Service endpoint'/>"
     702             :         "  <Option name='NATIVE_DATA' type='boolean' description='Whether to "
     703             :         "store the native JSon representation at FeatureCollection and Feature "
     704             :         "level' default='NO'/>"
     705             :         "  <Option name='ARRAY_AS_STRING' type='boolean' description='Whether "
     706             :         "to expose JSon arrays of strings, integers or reals as a OGR String' "
     707             :         "default='NO'/>"
     708             :         "  <Option name='DATE_AS_STRING' type='boolean' description='Whether "
     709             :         "to expose date/time/date-time content using dedicated OGR "
     710             :         "date/time/date-time types or as a OGR String' default='NO'/>"
     711             :         "  <Option name='FOREIGN_MEMBERS' type='string-select' "
     712             :         "description='Whether and how foreign members at the feature level "
     713             :         "should be processed as OGR fields' default='AUTO'>"
     714             :         "    <Value>AUTO</Value>"
     715             :         "    <Value>ALL</Value>"
     716             :         "    <Value>NONE</Value>"
     717             :         "    <Value>STAC</Value>"
     718             :         "  </Option>"
     719             :         "  <Option name='OGR_SCHEMA' type='string' description='"
     720             :         "Partially or totally overrides the auto-detected schema to use for "
     721             :         "creating the layer. "
     722             :         "The overrides are defined as a JSON list of field definitions. "
     723             :         "This can be a filename or a JSON string or a URL.'/>"
     724        1646 :         "</OpenOptionList>");
     725             : 
     726        1646 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST,
     727        1646 :                               "<CreationOptionList/>");
     728             : 
     729        1646 :     poDriver->SetMetadataItem(
     730             :         GDAL_DS_LAYER_CREATIONOPTIONLIST,
     731             :         "<LayerCreationOptionList>"
     732             :         "  <Option name='WRITE_BBOX' type='boolean' description='whether to "
     733             :         "write a bbox property with the bounding box of the geometries at the "
     734             :         "feature and feature collection level' default='NO'/>"
     735             :         "  <Option name='COORDINATE_PRECISION' type='int' description='Number "
     736             :         "of decimal for coordinates. Default is 15 for GJ2008 and 7 for "
     737             :         "RFC7946'/>"
     738             :         "  <Option name='SIGNIFICANT_FIGURES' type='int' description='Number "
     739             :         "of significant figures for floating-point values' default='17'/>"
     740             :         "  <Option name='NATIVE_DATA' type='string' "
     741             :         "description='FeatureCollection level elements.'/>"
     742             :         "  <Option name='NATIVE_MEDIA_TYPE' type='string' description='Format "
     743             :         "of NATIVE_DATA. Must be \"application/vnd.geo+json\", otherwise "
     744             :         "NATIVE_DATA will be ignored.'/>"
     745             :         "  <Option name='RFC7946' type='boolean' description='Whether to use "
     746             :         "RFC 7946 standard. Otherwise GeoJSON 2008 initial version will be "
     747             :         "used' default='NO'/>"
     748             :         "  <Option name='WRAPDATELINE' type='boolean' description='Whether to "
     749             :         "apply heuristics to split geometries that cross dateline.' "
     750             :         "default='YES'/>"
     751             :         "  <Option name='WRITE_NAME' type='boolean' description='Whether to "
     752             :         "write a &quot;name&quot; property at feature collection level with "
     753             :         "layer name' default='YES'/>"
     754             :         "  <Option name='DESCRIPTION' type='string' description='(Long) "
     755             :         "description to write in a &quot;description&quot; property at feature "
     756             :         "collection level'/>"
     757             :         "  <Option name='ID_FIELD' type='string' description='Name of the "
     758             :         "source field that must be used as the id member of Feature features'/>"
     759             :         "  <Option name='ID_TYPE' type='string-select' description='Type of "
     760             :         "the id member of Feature features'>"
     761             :         "    <Value>AUTO</Value>"
     762             :         "    <Value>String</Value>"
     763             :         "    <Value>Integer</Value>"
     764             :         "  </Option>"
     765             :         "  <Option name='ID_GENERATE' type='boolean' "
     766             :         "description='Auto-generate feature ids' />"
     767             :         "  <Option name='WRITE_NON_FINITE_VALUES' type='boolean' "
     768             :         "description='Whether to write NaN / Infinity values' default='NO'/>"
     769             :         "  <Option name='AUTODETECT_JSON_STRINGS' type='boolean' "
     770             :         "description='Whether to try to interpret string fields as JSON "
     771             :         "arrays or objects' default='YES'/>"
     772             :         "  <Option name='FOREIGN_MEMBERS_FEATURE' type='string' "
     773             :         "description='Extra JSON content to add in each feature as a foreign "
     774             :         "members'/>"
     775             :         "  <Option name='FOREIGN_MEMBERS_COLLECTION' type='string' "
     776             :         "description='Extra JSON content to add to the feature collection as "
     777             :         "a foreign members'/>"
     778        1646 :         "</LayerCreationOptionList>");
     779             : 
     780        1646 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
     781        1646 :     poDriver->SetMetadataItem(
     782             :         GDAL_DMD_CREATIONFIELDDATATYPES,
     783             :         "Integer Integer64 Real String IntegerList "
     784        1646 :         "Integer64List RealList StringList Date DateTime");
     785        1646 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATASUBTYPES, "Boolean");
     786        1646 :     poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "OGRSQL SQLITE");
     787             : 
     788        1646 :     poDriver->SetMetadataItem(GDAL_DCAP_FLUSHCACHE_CONSISTENT_STATE, "YES");
     789        1646 :     poDriver->SetMetadataItem(GDAL_DCAP_HONOR_GEOM_COORDINATE_PRECISION, "YES");
     790             : 
     791        1646 :     poDriver->SetMetadataItem(GDAL_DCAP_UPDATE, "YES");
     792        1646 :     poDriver->SetMetadataItem(GDAL_DMD_UPDATE_ITEMS, "Features");
     793             : 
     794        1646 :     poDriver->pfnOpen = OGRGeoJSONDriverOpen;
     795        1646 :     poDriver->pfnIdentify = OGRGeoJSONDriverIdentify;
     796        1646 :     poDriver->pfnCreate = OGRGeoJSONDriverCreate;
     797        1646 :     poDriver->pfnDelete = OGRGeoJSONDriverDelete;
     798        1646 :     poDriver->pfnUnloadDriver = OGRGeoJSONDriverUnload;
     799             : 
     800        1646 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     801             : 
     802             : #ifdef BUILT_AS_PLUGIN
     803             :     RegisterOGRTopoJSON();
     804             :     RegisterOGRESRIJSON();
     805             :     RegisterOGRGeoJSONSeq();
     806             : #endif
     807             : }

Generated by: LCOV version 1.14