LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/geojson - ogrgeojsondriver.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 262 288 91.0 %
Date: 2024-05-03 15:49:35 Functions: 29 30 96.7 %

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

Generated by: LCOV version 1.14