LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/jsonfg - ogrjsonfgdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 329 399 82.5 %
Date: 2024-05-14 23:54:21 Functions: 18 19 94.7 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implementation of OGC Features and Geometries JSON (JSON-FG)
       5             :  * Author:   Even Rouault <even.rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2023, Even Rouault <even.rouault at spatialys.com>
       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 "ogr_jsonfg.h"
      30             : #include "ogr_geojson.h"
      31             : 
      32             : #include "cpl_http.h"
      33             : #include "cpl_vsi_error.h"
      34             : #include "cpl_vsi_virtual.h"
      35             : 
      36             : #include <cmath>
      37             : 
      38             : /************************************************************************/
      39             : /*                  OGRJSONFGDataset::~OGRJSONFGDataset()               */
      40             : /************************************************************************/
      41             : 
      42         422 : OGRJSONFGDataset::~OGRJSONFGDataset()
      43             : {
      44         211 :     CPLFree(pszGeoData_);
      45         211 :     if (fpOut_)
      46             :     {
      47          72 :         FinishWriting();
      48             : 
      49          72 :         VSIFCloseL(fpOut_);
      50             :     }
      51         422 : }
      52             : 
      53             : /************************************************************************/
      54             : /*                           FinishWriting()                            */
      55             : /************************************************************************/
      56             : 
      57          74 : void OGRJSONFGDataset::FinishWriting()
      58             : {
      59          74 :     if (m_nPositionBeforeFCClosed == 0)
      60             :     {
      61          73 :         m_nPositionBeforeFCClosed = fpOut_->Tell();
      62             : 
      63          73 :         if (!EmitStartFeaturesIfNeededAndReturnIfFirstFeature())
      64          57 :             VSIFPrintfL(fpOut_, "\n");
      65          73 :         VSIFPrintfL(fpOut_, "]");
      66             : 
      67             :         // When we didn't know if there was a single layer, we omitted writing
      68             :         // the coordinate precision at ICreateLayer() time.
      69             :         // Now we can check if there was a single layer, or several layers with
      70             :         // same precision setting, and write it when possible.
      71         137 :         if (!bSingleOutputLayer_ && !apoLayers_.empty() &&
      72          64 :             apoLayers_.front()->GetLayerDefn()->GetGeomFieldCount() > 0)
      73             :         {
      74          53 :             const auto &oCoordPrec = apoLayers_.front()
      75          53 :                                          ->GetLayerDefn()
      76          53 :                                          ->GetGeomFieldDefn(0)
      77          53 :                                          ->GetCoordinatePrecision();
      78          53 :             bool bSameGeomCoordPrec =
      79          53 :                 (oCoordPrec.dfXYResolution !=
      80         105 :                      OGRGeomCoordinatePrecision::UNKNOWN ||
      81          52 :                  oCoordPrec.dfZResolution !=
      82             :                      OGRGeomCoordinatePrecision::UNKNOWN);
      83          71 :             for (size_t i = 1; i < apoLayers_.size(); ++i)
      84             :             {
      85          18 :                 if (apoLayers_[i]->GetLayerDefn()->GetGeomFieldCount() > 0)
      86             :                 {
      87             :                     const auto &oOtherCoordPrec =
      88          18 :                         apoLayers_[i]
      89          18 :                             ->GetLayerDefn()
      90          18 :                             ->GetGeomFieldDefn(0)
      91          18 :                             ->GetCoordinatePrecision();
      92          36 :                     bSameGeomCoordPrec &= (oOtherCoordPrec.dfXYResolution ==
      93          36 :                                                oCoordPrec.dfXYResolution &&
      94          18 :                                            oOtherCoordPrec.dfZResolution ==
      95          18 :                                                oCoordPrec.dfZResolution);
      96             :                 }
      97             :             }
      98          53 :             if (bSameGeomCoordPrec)
      99             :             {
     100           1 :                 if (oCoordPrec.dfXYResolution !=
     101             :                     OGRGeomCoordinatePrecision::UNKNOWN)
     102             :                 {
     103           1 :                     VSIFPrintfL(fpOut_,
     104             :                                 ",\n\"xy_coordinate_resolution_place\":%g",
     105           1 :                                 oCoordPrec.dfXYResolution);
     106             :                 }
     107           1 :                 if (oCoordPrec.dfZResolution !=
     108             :                     OGRGeomCoordinatePrecision::UNKNOWN)
     109             :                 {
     110           1 :                     VSIFPrintfL(fpOut_,
     111             :                                 ",\n\"z_coordinate_resolution_place\":%g",
     112           1 :                                 oCoordPrec.dfZResolution);
     113             :                 }
     114             : 
     115           2 :                 OGRSpatialReference oSRSWGS84;
     116           1 :                 oSRSWGS84.SetWellKnownGeogCS("WGS84");
     117             :                 const auto oCoordPrecWGS84 = oCoordPrec.ConvertToOtherSRS(
     118           2 :                     apoLayers_.front()->GetSpatialRef(), &oSRSWGS84);
     119             : 
     120           1 :                 if (oCoordPrecWGS84.dfXYResolution !=
     121             :                     OGRGeomCoordinatePrecision::UNKNOWN)
     122             :                 {
     123           1 :                     VSIFPrintfL(fpOut_, ",\n\"xy_coordinate_resolution\":%g",
     124           1 :                                 oCoordPrecWGS84.dfXYResolution);
     125             :                 }
     126           1 :                 if (oCoordPrecWGS84.dfZResolution !=
     127             :                     OGRGeomCoordinatePrecision::UNKNOWN)
     128             :                 {
     129           1 :                     VSIFPrintfL(fpOut_, ",\n\"z_coordinate_resolution\":%g",
     130           1 :                                 oCoordPrecWGS84.dfZResolution);
     131             :                 }
     132             :             }
     133             :         }
     134             : 
     135          73 :         VSIFPrintfL(fpOut_, "\n}\n");
     136          73 :         fpOut_->Flush();
     137             :     }
     138          74 : }
     139             : 
     140             : /************************************************************************/
     141             : /*                         SyncToDiskInternal()                         */
     142             : /************************************************************************/
     143             : 
     144           3 : OGRErr OGRJSONFGDataset::SyncToDiskInternal()
     145             : {
     146           3 :     if (m_nPositionBeforeFCClosed == 0 && GetFpOutputIsSeekable())
     147             :     {
     148           2 :         FinishWriting();
     149             :     }
     150             : 
     151           3 :     return OGRERR_NONE;
     152             : }
     153             : 
     154             : /************************************************************************/
     155             : /*                         BeforeCreateFeature()                        */
     156             : /************************************************************************/
     157             : 
     158         140 : void OGRJSONFGDataset::BeforeCreateFeature()
     159             : {
     160         140 :     if (m_nPositionBeforeFCClosed)
     161             :     {
     162             :         // If we had called SyncToDisk() previously, undo its effects
     163           1 :         fpOut_->Seek(m_nPositionBeforeFCClosed, SEEK_SET);
     164           1 :         m_nPositionBeforeFCClosed = 0;
     165             :     }
     166             : 
     167         140 :     if (!EmitStartFeaturesIfNeededAndReturnIfFirstFeature())
     168             :     {
     169          84 :         VSIFPrintfL(fpOut_, ",\n");
     170             :     }
     171         140 : }
     172             : 
     173             : /************************************************************************/
     174             : /*                           Open()                                     */
     175             : /************************************************************************/
     176             : 
     177         138 : bool OGRJSONFGDataset::Open(GDALOpenInfo *poOpenInfo,
     178             :                             GeoJSONSourceType nSrcType)
     179             : {
     180         138 :     const char *pszUnprefixed = poOpenInfo->pszFilename;
     181         138 :     if (STARTS_WITH_CI(pszUnprefixed, "JSONFG:"))
     182             :     {
     183           0 :         pszUnprefixed += strlen("JSONFG:");
     184             :     }
     185             : 
     186         276 :     std::string osDefaultLayerName;
     187             : 
     188         138 :     VSIVirtualHandleUniquePtr fp;
     189         138 :     if (nSrcType == eGeoJSONSourceService)
     190             :     {
     191           1 :         if (!ReadFromService(poOpenInfo, pszUnprefixed))
     192           1 :             return false;
     193           0 :         if (poOpenInfo->eAccess == GA_Update)
     194             :         {
     195           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     196             :                      "Update from remote service not supported");
     197           0 :             return false;
     198             :         }
     199             :     }
     200         137 :     else if (nSrcType == eGeoJSONSourceText)
     201             :     {
     202          45 :         if (poOpenInfo->eAccess == GA_Update)
     203             :         {
     204           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     205             :                      "Update from inline definition not supported");
     206           0 :             return false;
     207             :         }
     208          45 :         pszGeoData_ = CPLStrdup(pszUnprefixed);
     209             :     }
     210          92 :     else if (nSrcType == eGeoJSONSourceFile)
     211             :     {
     212          92 :         if (poOpenInfo->eAccess == GA_Update)
     213             :         {
     214           0 :             CPLError(CE_Failure, CPLE_NotSupported, "Update not supported");
     215           0 :             return false;
     216             :         }
     217          92 :         SetDescription(pszUnprefixed);
     218          92 :         osDefaultLayerName = CPLGetBasename(pszUnprefixed);
     219          92 :         eAccess = poOpenInfo->eAccess;
     220             : 
     221             :         // Ingests the first bytes of the file in pszGeoData_
     222          92 :         if (!EQUAL(pszUnprefixed, poOpenInfo->pszFilename))
     223             :         {
     224           0 :             GDALOpenInfo oOpenInfo(pszUnprefixed, GA_ReadOnly);
     225           0 :             if (oOpenInfo.fpL == nullptr || oOpenInfo.pabyHeader == nullptr)
     226           0 :                 return false;
     227           0 :             pszGeoData_ =
     228           0 :                 CPLStrdup(reinterpret_cast<const char *>(oOpenInfo.pabyHeader));
     229           0 :             fp.reset(oOpenInfo.fpL);
     230           0 :             oOpenInfo.fpL = nullptr;
     231             :         }
     232          92 :         else if (poOpenInfo->fpL == nullptr)
     233           0 :             return false;
     234             :         else
     235             :         {
     236          92 :             fp.reset(poOpenInfo->fpL);
     237          92 :             poOpenInfo->fpL = nullptr;
     238          92 :             pszGeoData_ = CPLStrdup(
     239          92 :                 reinterpret_cast<const char *>(poOpenInfo->pabyHeader));
     240             :         }
     241             :     }
     242             :     else
     243             :     {
     244           0 :         return false;
     245             :     }
     246             : 
     247         137 :     if (osDefaultLayerName.empty())
     248          45 :         osDefaultLayerName = "features";
     249             : 
     250         141 :     const auto SetReaderOptions = [poOpenInfo](OGRJSONFGReader &oReader)
     251             :     {
     252         282 :         const char *pszGeometryElement = CSLFetchNameValueDef(
     253         141 :             poOpenInfo->papszOpenOptions, "GEOMETRY_ELEMENT", "AUTO");
     254         141 :         if (EQUAL(pszGeometryElement, "PLACE"))
     255           3 :             oReader.SetGeometryElement(OGRJSONFGReader::GeometryElement::PLACE);
     256         138 :         else if (EQUAL(pszGeometryElement, "GEOMETRY"))
     257           3 :             oReader.SetGeometryElement(
     258             :                 OGRJSONFGReader::GeometryElement::GEOMETRY);
     259         278 :     };
     260             : 
     261         137 :     if (nSrcType == eGeoJSONSourceFile)
     262             :     {
     263          92 :         auto poReader = std::make_unique<OGRJSONFGReader>();
     264          92 :         SetReaderOptions(*(poReader.get()));
     265             : 
     266             :         // Try to use a streaming parser if the content of the file seems
     267             :         // to be FeatureCollection
     268          92 :         bool bUseStreamingInterface = false;
     269          92 :         const char *pszStr = strstr(pszGeoData_, "\"features\"");
     270          92 :         if (pszStr)
     271             :         {
     272          88 :             pszStr += strlen("\"features\"");
     273         151 :             while (*pszStr && isspace(static_cast<unsigned char>(*pszStr)))
     274          63 :                 pszStr++;
     275          88 :             if (*pszStr == ':')
     276             :             {
     277          88 :                 pszStr++;
     278         176 :                 while (*pszStr && isspace(static_cast<unsigned char>(*pszStr)))
     279          88 :                     pszStr++;
     280          88 :                 if (*pszStr == '[')
     281             :                 {
     282          88 :                     bUseStreamingInterface = true;
     283             :                 }
     284             :             }
     285             :         }
     286          92 :         if (bUseStreamingInterface)
     287             :         {
     288          88 :             bool bCanTryWithNonStreamingParserOut = true;
     289          88 :             if (poReader->AnalyzeWithStreamingParser(
     290             :                     this, fp.get(), osDefaultLayerName,
     291             :                     bCanTryWithNonStreamingParserOut))
     292             :             {
     293          88 :                 if (!apoLayers_.empty())
     294             :                 {
     295          72 :                     auto poLayer = cpl::down_cast<OGRJSONFGStreamedLayer *>(
     296          72 :                         apoLayers_[0].get());
     297          72 :                     poLayer->SetFile(std::move(fp));
     298             :                     auto poParser = std::make_unique<OGRJSONFGStreamingParser>(
     299         144 :                         *(poReader.get()), false);
     300          72 :                     poLayer->SetStreamingParser(std::move(poParser));
     301             :                 }
     302             : 
     303         108 :                 for (size_t i = 1; i < apoLayers_.size(); ++i)
     304             :                 {
     305          20 :                     auto poLayer = cpl::down_cast<OGRJSONFGStreamedLayer *>(
     306          20 :                         apoLayers_[i].get());
     307             : 
     308             :                     auto fpNew = VSIVirtualHandleUniquePtr(
     309          20 :                         VSIFOpenL(pszUnprefixed, "rb"));
     310          20 :                     if (!fpNew)
     311             :                     {
     312           0 :                         CPLError(CE_Failure, CPLE_FileIO,
     313             :                                  "Cannot open %s again", pszUnprefixed);
     314           0 :                         return false;
     315             :                     }
     316          20 :                     poLayer->SetFile(std::move(fpNew));
     317             : 
     318             :                     auto poParser = std::make_unique<OGRJSONFGStreamingParser>(
     319          40 :                         *(poReader.get()), false);
     320          20 :                     poLayer->SetStreamingParser(std::move(poParser));
     321             :                 }
     322          88 :                 poReader_ = std::move(poReader);
     323          88 :                 return true;
     324             :             }
     325           0 :             if (!bCanTryWithNonStreamingParserOut)
     326           0 :                 return false;
     327             : 
     328             :             // Fallback to in-memory ingestion
     329           0 :             CPLAssert(poOpenInfo->fpL == nullptr);
     330           0 :             poOpenInfo->fpL = fp.release();
     331           0 :             if (!ReadFromFile(poOpenInfo, pszUnprefixed))
     332           0 :                 return false;
     333             :         }
     334             :     }
     335             : 
     336             :     // In-memory ingestion of the file
     337          49 :     OGRJSONFGReader oReader;
     338          49 :     SetReaderOptions(oReader);
     339          49 :     const bool bRet = oReader.Load(this, pszGeoData_, osDefaultLayerName);
     340          49 :     CPLFree(pszGeoData_);
     341          49 :     pszGeoData_ = nullptr;
     342          49 :     return bRet;
     343             : }
     344             : 
     345             : /************************************************************************/
     346             : /*                  OGRJSONFGDataset::GetLayer()                        */
     347             : /************************************************************************/
     348             : 
     349         137 : OGRLayer *OGRJSONFGDataset::GetLayer(int i)
     350             : {
     351         137 :     if (i < 0 || i >= static_cast<int>(apoLayers_.size()))
     352           2 :         return nullptr;
     353         135 :     return apoLayers_[i].get();
     354             : }
     355             : 
     356             : /************************************************************************/
     357             : /*                  OGRJSONFGDataset::AddLayer()                        */
     358             : /************************************************************************/
     359             : 
     360             : OGRJSONFGMemLayer *
     361          49 : OGRJSONFGDataset::AddLayer(std::unique_ptr<OGRJSONFGMemLayer> &&poLayer)
     362             : {
     363          49 :     apoLayers_.emplace_back(std::move(poLayer));
     364          49 :     return static_cast<OGRJSONFGMemLayer *>(apoLayers_.back().get());
     365             : }
     366             : 
     367             : /************************************************************************/
     368             : /*                  OGRJSONFGDataset::AddLayer()                        */
     369             : /************************************************************************/
     370             : 
     371             : OGRJSONFGStreamedLayer *
     372          92 : OGRJSONFGDataset::AddLayer(std::unique_ptr<OGRJSONFGStreamedLayer> &&poLayer)
     373             : {
     374          92 :     apoLayers_.emplace_back(std::move(poLayer));
     375          92 :     return static_cast<OGRJSONFGStreamedLayer *>(apoLayers_.back().get());
     376             : }
     377             : 
     378             : /************************************************************************/
     379             : /*                           ReadFromFile()                             */
     380             : /************************************************************************/
     381             : 
     382           0 : bool OGRJSONFGDataset::ReadFromFile(GDALOpenInfo *poOpenInfo,
     383             :                                     const char *pszUnprefixed)
     384             : {
     385           0 :     GByte *pabyOut = nullptr;
     386           0 :     if (!EQUAL(poOpenInfo->pszFilename, pszUnprefixed))
     387             :     {
     388           0 :         GDALOpenInfo oOpenInfo(pszUnprefixed, GA_ReadOnly);
     389           0 :         if (oOpenInfo.fpL == nullptr || oOpenInfo.pabyHeader == nullptr)
     390           0 :             return false;
     391           0 :         VSIFSeekL(oOpenInfo.fpL, 0, SEEK_SET);
     392           0 :         if (!VSIIngestFile(oOpenInfo.fpL, pszUnprefixed, &pabyOut, nullptr, -1))
     393             :         {
     394           0 :             return false;
     395             :         }
     396             :     }
     397             :     else
     398             :     {
     399           0 :         if (poOpenInfo->fpL == nullptr)
     400           0 :             return false;
     401           0 :         VSIFSeekL(poOpenInfo->fpL, 0, SEEK_SET);
     402           0 :         if (!VSIIngestFile(poOpenInfo->fpL, poOpenInfo->pszFilename, &pabyOut,
     403             :                            nullptr, -1))
     404             :         {
     405           0 :             return false;
     406             :         }
     407             : 
     408           0 :         VSIFCloseL(poOpenInfo->fpL);
     409           0 :         poOpenInfo->fpL = nullptr;
     410             :     }
     411             : 
     412           0 :     CPLFree(pszGeoData_);
     413           0 :     pszGeoData_ = reinterpret_cast<char *>(pabyOut);
     414             : 
     415           0 :     CPLAssert(nullptr != pszGeoData_);
     416             : 
     417           0 :     return true;
     418             : }
     419             : 
     420             : /************************************************************************/
     421             : /*                           ReadFromService()                          */
     422             : /************************************************************************/
     423             : 
     424           1 : bool OGRJSONFGDataset::ReadFromService(GDALOpenInfo *poOpenInfo,
     425             :                                        const char *pszSource)
     426             : {
     427           1 :     CPLAssert(nullptr == pszGeoData_);
     428           1 :     CPLAssert(nullptr != pszSource);
     429             : 
     430           1 :     CPLErrorReset();
     431             : 
     432             :     /* -------------------------------------------------------------------- */
     433             :     /*      Look if we already cached the content.                          */
     434             :     /* -------------------------------------------------------------------- */
     435           1 :     char *pszStoredContent = OGRGeoJSONDriverStealStoredContent(pszSource);
     436           1 :     if (pszStoredContent != nullptr)
     437             :     {
     438           0 :         if (JSONFGIsObject(pszStoredContent))
     439             :         {
     440           0 :             pszGeoData_ = pszStoredContent;
     441           0 :             nGeoDataLen_ = strlen(pszGeoData_);
     442             : 
     443           0 :             SetDescription(pszSource);
     444           0 :             return true;
     445             :         }
     446             : 
     447           0 :         OGRGeoJSONDriverStoreContent(pszSource, pszStoredContent);
     448           0 :         return false;
     449             :     }
     450             : 
     451             :     /* -------------------------------------------------------------------- */
     452             :     /*      Fetch the result.                                               */
     453             :     /* -------------------------------------------------------------------- */
     454           1 :     char *papsOptions[] = {
     455             :         const_cast<char *>("HEADERS=Accept: text/plain, application/json"),
     456             :         nullptr};
     457             : 
     458           1 :     CPLHTTPResult *pResult = CPLHTTPFetch(pszSource, papsOptions);
     459             : 
     460             :     /* -------------------------------------------------------------------- */
     461             :     /*      Try to handle CURL/HTTP errors.                                 */
     462             :     /* -------------------------------------------------------------------- */
     463           2 :     if (nullptr == pResult || 0 == pResult->nDataLen ||
     464           1 :         0 != CPLGetLastErrorNo())
     465             :     {
     466           0 :         CPLHTTPDestroyResult(pResult);
     467           0 :         return false;
     468             :     }
     469             : 
     470           1 :     if (0 != pResult->nStatus)
     471             :     {
     472           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Curl reports error: %d: %s",
     473             :                  pResult->nStatus, pResult->pszErrBuf);
     474           0 :         CPLHTTPDestroyResult(pResult);
     475           0 :         return false;
     476             :     }
     477             : 
     478             :     /* -------------------------------------------------------------------- */
     479             :     /*      Copy returned GeoJSON data to text buffer.                      */
     480             :     /* -------------------------------------------------------------------- */
     481           1 :     char *pszData = reinterpret_cast<char *>(pResult->pabyData);
     482             : 
     483             :     // Directly assign CPLHTTPResult::pabyData to pszGeoData_.
     484           1 :     pszGeoData_ = pszData;
     485           1 :     nGeoDataLen_ = pResult->nDataLen;
     486           1 :     pResult->pabyData = nullptr;
     487           1 :     pResult->nDataLen = 0;
     488             : 
     489           1 :     SetDescription(pszSource);
     490             : 
     491             :     /* -------------------------------------------------------------------- */
     492             :     /*      Cleanup HTTP resources.                                         */
     493             :     /* -------------------------------------------------------------------- */
     494           1 :     CPLHTTPDestroyResult(pResult);
     495             : 
     496           1 :     CPLAssert(nullptr != pszGeoData_);
     497             : 
     498             :     /* -------------------------------------------------------------------- */
     499             :     /*      Cache the content if it is not handled by this driver, but      */
     500             :     /*      another related one.                                            */
     501             :     /* -------------------------------------------------------------------- */
     502           1 :     if (EQUAL(pszSource, poOpenInfo->pszFilename))
     503             :     {
     504           1 :         if (!JSONFGIsObject(pszGeoData_))
     505             :         {
     506           1 :             OGRGeoJSONDriverStoreContent(pszSource, pszGeoData_);
     507           1 :             pszGeoData_ = nullptr;
     508           1 :             nGeoDataLen_ = 0;
     509           1 :             return false;
     510             :         }
     511             :     }
     512             : 
     513           0 :     return true;
     514             : }
     515             : 
     516             : /************************************************************************/
     517             : /*                              Create()                                */
     518             : /************************************************************************/
     519             : 
     520          73 : bool OGRJSONFGDataset::Create(const char *pszName, CSLConstList papszOptions)
     521             : {
     522          73 :     CPLAssert(nullptr == fpOut_);
     523          73 :     bSingleOutputLayer_ =
     524          73 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "SINGLE_LAYER", "NO"));
     525             : 
     526         146 :     bFpOutputIsSeekable_ = !(strcmp(pszName, "/vsistdout/") == 0 ||
     527          73 :                              STARTS_WITH(pszName, "/vsigzip/") ||
     528          73 :                              STARTS_WITH(pszName, "/vsizip/"));
     529             : 
     530          73 :     if (strcmp(pszName, "/dev/stdout") == 0)
     531           0 :         pszName = "/vsistdout/";
     532             : 
     533             :     /* -------------------------------------------------------------------- */
     534             :     /*     File overwrite not supported.                                    */
     535             :     /* -------------------------------------------------------------------- */
     536             :     VSIStatBufL sStatBuf;
     537          73 :     if (0 == VSIStatL(pszName, &sStatBuf))
     538             :     {
     539           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     540             :                  "The JSONFG driver does not overwrite existing files.");
     541           0 :         return false;
     542             :     }
     543             : 
     544             :     /* -------------------------------------------------------------------- */
     545             :     /*      Create the output file.                                         */
     546             :     /* -------------------------------------------------------------------- */
     547          73 :     fpOut_ = VSIFOpenExL(pszName, "w", true);
     548          73 :     if (nullptr == fpOut_)
     549             :     {
     550           1 :         CPLError(CE_Failure, CPLE_OpenFailed,
     551             :                  "Failed to create JSONFG dataset: %s: %s", pszName,
     552             :                  VSIGetLastErrorMsg());
     553           1 :         return false;
     554             :     }
     555             : 
     556          72 :     SetDescription(pszName);
     557             : 
     558          72 :     VSIFPrintfL(fpOut_, "{\n\"type\": \"FeatureCollection\",\n");
     559          72 :     VSIFPrintfL(fpOut_, "\"conformsTo\" : [\"[ogc-json-fg-1-0.1:core]\"],\n");
     560             : 
     561          72 :     return true;
     562             : }
     563             : 
     564             : /************************************************************************/
     565             : /*                        EmitStartFeaturesIfNeeded()                   */
     566             : /************************************************************************/
     567             : 
     568         213 : bool OGRJSONFGDataset::EmitStartFeaturesIfNeededAndReturnIfFirstFeature()
     569             : {
     570         213 :     if (!bHasEmittedFeatures_)
     571             :     {
     572          72 :         bHasEmittedFeatures_ = true;
     573          72 :         VSIFPrintfL(fpOut_, "\"features\" : [\n");
     574          72 :         return true;
     575             :     }
     576         141 :     return false;
     577             : }
     578             : 
     579             : /************************************************************************/
     580             : /*                           ICreateLayer()                             */
     581             : /************************************************************************/
     582             : 
     583             : OGRLayer *
     584          90 : OGRJSONFGDataset::ICreateLayer(const char *pszNameIn,
     585             :                                const OGRGeomFieldDefn *poSrcGeomFieldDefn,
     586             :                                CSLConstList papszOptions)
     587             : {
     588          90 :     if (nullptr == fpOut_)
     589             :     {
     590           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     591             :                  "JSONFG driver doesn't support creating a layer "
     592             :                  "on a read-only datasource");
     593           0 :         return nullptr;
     594             :     }
     595             : 
     596          90 :     if (bSingleOutputLayer_ && !apoLayers_.empty())
     597             :     {
     598           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     599             :                  "Only one layer can be created since SINGLE_LAYER=YES "
     600             :                  "creation option has been used");
     601           0 :         return nullptr;
     602             :     }
     603             : 
     604             :     const auto eGType =
     605          90 :         poSrcGeomFieldDefn ? poSrcGeomFieldDefn->GetType() : wkbNone;
     606             :     const auto poSRS =
     607          90 :         poSrcGeomFieldDefn ? poSrcGeomFieldDefn->GetSpatialRef() : nullptr;
     608             : 
     609         180 :     std::string osCoordRefSys;
     610          90 :     std::unique_ptr<OGRCoordinateTransformation> poCTToWGS84;
     611          90 :     if (poSRS)
     612             :     {
     613             :         const auto GetCURIE =
     614          37 :             [](const char *pszAuthName, const char *pszAuthCode)
     615             :         {
     616          37 :             std::string osRet = "[";
     617          37 :             if (STARTS_WITH(pszAuthName, "IAU_"))
     618           0 :                 osRet += "IAU";
     619             :             else
     620          37 :                 osRet += pszAuthName;
     621          37 :             osRet += ':';
     622          37 :             osRet += pszAuthCode;
     623          37 :             osRet += ']';
     624          37 :             return osRet;
     625             :         };
     626             : 
     627          37 :         const auto GetCoordRefSys = [GetCURIE](const char *pszAuthName,
     628             :                                                const char *pszAuthCode,
     629             :                                                double dfCoordEpoch = 0)
     630             :         {
     631          37 :             if (dfCoordEpoch > 0)
     632             :             {
     633           2 :                 json_object *poObj = json_object_new_object();
     634           2 :                 json_object_object_add(poObj, "type",
     635             :                                        json_object_new_string("Reference"));
     636           2 :                 json_object_object_add(
     637             :                     poObj, "href",
     638             :                     json_object_new_string(
     639           4 :                         GetCURIE(pszAuthName, pszAuthCode).c_str()));
     640           2 :                 json_object_object_add(poObj, "epoch",
     641             :                                        json_object_new_double(dfCoordEpoch));
     642           2 :                 return poObj;
     643             :             }
     644             :             else
     645             :             {
     646          35 :                 return json_object_new_string(
     647          70 :                     GetCURIE(pszAuthName, pszAuthCode).c_str());
     648             :             }
     649             :         };
     650             : 
     651          33 :         const char *pszAuthName = poSRS->GetAuthorityName(nullptr);
     652          33 :         const char *pszAuthCode = poSRS->GetAuthorityCode(nullptr);
     653          33 :         const double dfCoordEpoch = poSRS->GetCoordinateEpoch();
     654          33 :         json_object *poObj = nullptr;
     655          33 :         if (pszAuthName && pszAuthCode)
     656             :         {
     657          29 :             poObj = GetCoordRefSys(pszAuthName, pszAuthCode, dfCoordEpoch);
     658             :         }
     659           4 :         else if (poSRS->IsCompound())
     660             :         {
     661           4 :             const char *pszAuthNameHoriz = poSRS->GetAuthorityName("HORIZCRS");
     662           4 :             const char *pszAuthCodeHoriz = poSRS->GetAuthorityCode("HORIZCRS");
     663           4 :             const char *pszAuthNameVert = poSRS->GetAuthorityName("VERTCRS");
     664           4 :             const char *pszAuthCodeVert = poSRS->GetAuthorityCode("VERTCRS");
     665           4 :             if (pszAuthNameHoriz && pszAuthCodeHoriz && pszAuthNameVert &&
     666             :                 pszAuthCodeVert)
     667             :             {
     668           4 :                 poObj = json_object_new_array();
     669           4 :                 json_object_array_add(poObj, GetCoordRefSys(pszAuthNameHoriz,
     670             :                                                             pszAuthCodeHoriz,
     671             :                                                             dfCoordEpoch));
     672           4 :                 json_object_array_add(
     673             :                     poObj, GetCoordRefSys(pszAuthNameVert, pszAuthCodeVert));
     674             :             }
     675             :         }
     676             : 
     677          33 :         if (poObj)
     678             :         {
     679             :             osCoordRefSys =
     680          33 :                 json_object_to_json_string_ext(poObj, JSON_C_TO_STRING_SPACED);
     681          33 :             json_object_put(poObj);
     682             :         }
     683             :         else
     684             :         {
     685           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     686             :                      "Input CRS %s cannot be expressed as a reference (ie "
     687             :                      "well-known CRS by code). "
     688             :                      "Retry be reprojecting to a known CRS first",
     689             :                      poSRS->GetName());
     690           0 :             return nullptr;
     691             :         }
     692             : 
     693          33 :         if (!strstr(osCoordRefSys.c_str(), "[IAU:"))
     694             :         {
     695          66 :             OGRSpatialReference oSRSWGS84;
     696          33 :             oSRSWGS84.SetWellKnownGeogCS("WGS84");
     697          33 :             oSRSWGS84.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     698          33 :             poCTToWGS84.reset(
     699             :                 OGRCreateCoordinateTransformation(poSRS, &oSRSWGS84));
     700             :         }
     701             :     }
     702          57 :     else if (eGType != wkbNone)
     703             :     {
     704          45 :         if (OGR_GT_HasZ(eGType))
     705          21 :             osCoordRefSys = "[OGC:CRS84h]";
     706             :         else
     707          24 :             osCoordRefSys = "[OGC:CRS84]";
     708          45 :         CPLError(CE_Warning, CPLE_AppDefined,
     709             :                  "No SRS set on layer. Assuming it is long/lat on WGS84 "
     710             :                  "ellipsoid");
     711             :     }
     712             : 
     713         180 :     CPLStringList aosOptions(papszOptions);
     714             : 
     715          90 :     if (const char *pszCoordPrecisionGeom =
     716          90 :             CSLFetchNameValue(papszOptions, "COORDINATE_PRECISION_GEOMETRY"))
     717             :     {
     718             :         double dfXYResolutionGeometry =
     719           1 :             std::pow(10.0, -CPLAtof(pszCoordPrecisionGeom));
     720           1 :         double dfZResolutionGeometry = dfXYResolutionGeometry;
     721             :         aosOptions.SetNameValue("XY_COORD_PRECISION_GEOMETRY",
     722           1 :                                 pszCoordPrecisionGeom);
     723             :         aosOptions.SetNameValue("Z_COORD_PRECISION_GEOMETRY",
     724           1 :                                 pszCoordPrecisionGeom);
     725           1 :         if (IsSingleOutputLayer())
     726             :         {
     727           1 :             VSIFPrintfL(fpOut_, "\"xy_coordinate_resolution\": %g,\n",
     728             :                         dfXYResolutionGeometry);
     729           1 :             if (poSRS && poSRS->GetAxesCount() == 3)
     730             :             {
     731           0 :                 VSIFPrintfL(fpOut_, "\"z_coordinate_resolution\": %g,\n",
     732             :                             dfZResolutionGeometry);
     733             :             }
     734             :         }
     735             :     }
     736         166 :     else if (poSrcGeomFieldDefn &&
     737          77 :              poSrcGeomFieldDefn->GetCoordinatePrecision().dfXYResolution ==
     738         166 :                  OGRGeomCoordinatePrecision::UNKNOWN &&
     739          75 :              CSLFetchNameValue(papszOptions, "SIGNIFICANT_FIGURES") == nullptr)
     740             :     {
     741          75 :         const int nXYPrecisionGeometry = 7;
     742          75 :         const int nZPrecisionGeometry = 3;
     743             :         aosOptions.SetNameValue("XY_COORD_PRECISION_GEOMETRY",
     744          75 :                                 CPLSPrintf("%d", nXYPrecisionGeometry));
     745             :         aosOptions.SetNameValue("Z_COORD_PRECISION_GEOMETRY",
     746          75 :                                 CPLSPrintf("%d", nZPrecisionGeometry));
     747             :     }
     748             : 
     749          90 :     double dfXYResolution = OGRGeomCoordinatePrecision::UNKNOWN;
     750          90 :     double dfZResolution = OGRGeomCoordinatePrecision::UNKNOWN;
     751             : 
     752          90 :     if (const char *pszCoordPrecisionPlace =
     753          90 :             CSLFetchNameValue(papszOptions, "COORDINATE_PRECISION_PLACE"))
     754             :     {
     755           1 :         dfXYResolution = std::pow(10.0, -CPLAtof(pszCoordPrecisionPlace));
     756           1 :         dfZResolution = dfXYResolution;
     757           1 :         if (IsSingleOutputLayer())
     758             :         {
     759           1 :             VSIFPrintfL(fpOut_, "\"xy_coordinate_resolution_place\": %g,\n",
     760             :                         dfXYResolution);
     761           1 :             if (poSRS && poSRS->GetAxesCount() == 3)
     762             :             {
     763           0 :                 VSIFPrintfL(fpOut_, "\"z_coordinate_resolution_place\": %g,\n",
     764             :                             dfZResolution);
     765             :             }
     766             :         }
     767             :     }
     768         166 :     else if (poSrcGeomFieldDefn &&
     769          77 :              CSLFetchNameValue(papszOptions, "COORDINATE_PRECISION_PLACE") ==
     770         166 :                  nullptr &&
     771          77 :              CSLFetchNameValue(papszOptions, "SIGNIFICANT_FIGURES") == nullptr)
     772             :     {
     773          77 :         const auto &oCoordPrec = poSrcGeomFieldDefn->GetCoordinatePrecision();
     774         154 :         OGRSpatialReference oSRSWGS84;
     775          77 :         oSRSWGS84.SetWellKnownGeogCS("WGS84");
     776             :         const auto oCoordPrecWGS84 =
     777         154 :             oCoordPrec.ConvertToOtherSRS(poSRS, &oSRSWGS84);
     778             : 
     779          77 :         if (oCoordPrec.dfXYResolution != OGRGeomCoordinatePrecision::UNKNOWN)
     780             :         {
     781           2 :             dfXYResolution = oCoordPrec.dfXYResolution;
     782             :             aosOptions.SetNameValue(
     783             :                 "XY_COORD_PRECISION_PLACE",
     784             :                 CPLSPrintf("%d",
     785             :                            OGRGeomCoordinatePrecision::ResolutionToPrecision(
     786           2 :                                oCoordPrec.dfXYResolution)));
     787           2 :             if (IsSingleOutputLayer())
     788             :             {
     789           1 :                 VSIFPrintfL(fpOut_, "\"xy_coordinate_resolution_place\": %g,\n",
     790           1 :                             oCoordPrec.dfXYResolution);
     791             :             }
     792             : 
     793           2 :             if (CSLFetchNameValue(papszOptions,
     794           2 :                                   "COORDINATE_PRECISION_GEOMETRY") == nullptr)
     795             :             {
     796           2 :                 const double dfXYResolutionGeometry =
     797             :                     oCoordPrecWGS84.dfXYResolution;
     798             : 
     799             :                 aosOptions.SetNameValue(
     800             :                     "XY_COORD_PRECISION_GEOMETRY",
     801             :                     CPLSPrintf(
     802             :                         "%d", OGRGeomCoordinatePrecision::ResolutionToPrecision(
     803           2 :                                   dfXYResolutionGeometry)));
     804           2 :                 if (IsSingleOutputLayer())
     805             :                 {
     806           1 :                     VSIFPrintfL(fpOut_, "\"xy_coordinate_resolution\": %g,\n",
     807             :                                 dfXYResolutionGeometry);
     808             :                 }
     809             :             }
     810             :         }
     811             : 
     812          77 :         if (oCoordPrec.dfZResolution != OGRGeomCoordinatePrecision::UNKNOWN)
     813             :         {
     814           2 :             dfZResolution = oCoordPrec.dfZResolution;
     815             :             aosOptions.SetNameValue(
     816             :                 "Z_COORD_PRECISION_PLACE",
     817             :                 CPLSPrintf("%d",
     818             :                            OGRGeomCoordinatePrecision::ResolutionToPrecision(
     819           2 :                                dfZResolution)));
     820           2 :             if (IsSingleOutputLayer())
     821             :             {
     822           1 :                 VSIFPrintfL(fpOut_, "\"z_coordinate_resolution_place\": %g,\n",
     823             :                             dfZResolution);
     824             :             }
     825             : 
     826           2 :             if (CSLFetchNameValue(papszOptions,
     827           2 :                                   "COORDINATE_PRECISION_GEOMETRY") == nullptr)
     828             :             {
     829           2 :                 const double dfZResolutionGeometry =
     830             :                     oCoordPrecWGS84.dfZResolution;
     831             : 
     832             :                 aosOptions.SetNameValue(
     833             :                     "Z_COORD_PRECISION_GEOMETRY",
     834             :                     CPLSPrintf(
     835             :                         "%d", OGRGeomCoordinatePrecision::ResolutionToPrecision(
     836           2 :                                   dfZResolutionGeometry)));
     837           2 :                 if (IsSingleOutputLayer())
     838             :                 {
     839           1 :                     VSIFPrintfL(fpOut_, "\"z_coordinate_resolution\": %g,\n",
     840             :                                 dfZResolutionGeometry);
     841             :                 }
     842             :             }
     843             :         }
     844             :     }
     845             : 
     846             :     auto poLayer = std::make_unique<OGRJSONFGWriteLayer>(
     847          90 :         pszNameIn, poSRS, std::move(poCTToWGS84), osCoordRefSys, eGType,
     848          90 :         aosOptions.List(), this);
     849          90 :     apoLayers_.emplace_back(std::move(poLayer));
     850             : 
     851          90 :     auto poLayerAdded = apoLayers_.back().get();
     852          90 :     if (eGType != wkbNone &&
     853             :         dfXYResolution != OGRGeomCoordinatePrecision::UNKNOWN)
     854             :     {
     855             :         auto poGeomFieldDefn =
     856           3 :             poLayerAdded->GetLayerDefn()->GetGeomFieldDefn(0);
     857             :         OGRGeomCoordinatePrecision oCoordPrec(
     858           6 :             poGeomFieldDefn->GetCoordinatePrecision());
     859           3 :         oCoordPrec.dfXYResolution = dfXYResolution;
     860           3 :         poGeomFieldDefn->SetCoordinatePrecision(oCoordPrec);
     861             :     }
     862             : 
     863          90 :     if (eGType != wkbNone &&
     864             :         dfZResolution != OGRGeomCoordinatePrecision::UNKNOWN)
     865             :     {
     866             :         auto poGeomFieldDefn =
     867           3 :             poLayerAdded->GetLayerDefn()->GetGeomFieldDefn(0);
     868             :         OGRGeomCoordinatePrecision oCoordPrec(
     869           6 :             poGeomFieldDefn->GetCoordinatePrecision());
     870           3 :         oCoordPrec.dfZResolution = dfZResolution;
     871           3 :         poGeomFieldDefn->SetCoordinatePrecision(oCoordPrec);
     872             :     }
     873             : 
     874          90 :     return poLayerAdded;
     875             : }
     876             : 
     877             : /************************************************************************/
     878             : /*                           TestCapability()                           */
     879             : /************************************************************************/
     880             : 
     881          58 : int OGRJSONFGDataset::TestCapability(const char *pszCap)
     882             : {
     883          58 :     if (EQUAL(pszCap, ODsCCreateLayer))
     884          67 :         return fpOut_ != nullptr &&
     885          67 :                (!bSingleOutputLayer_ || apoLayers_.empty());
     886          24 :     else if (EQUAL(pszCap, ODsCZGeometries))
     887           2 :         return TRUE;
     888             : 
     889          22 :     return FALSE;
     890             : }
     891             : 
     892             : /************************************************************************/
     893             : /*                      OGRJSONFGMustSwapXY()                           */
     894             : /************************************************************************/
     895             : 
     896         112 : bool OGRJSONFGMustSwapXY(const OGRSpatialReference *poSRS)
     897             : {
     898         319 :     return poSRS->GetDataAxisToSRSAxisMapping() == std::vector<int>{2, 1} ||
     899         319 :            poSRS->GetDataAxisToSRSAxisMapping() == std::vector<int>{2, 1, 3};
     900             : }

Generated by: LCOV version 1.14