LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/jsonfg - ogrjsonfgdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 328 399 82.2 %
Date: 2024-11-21 22:18:42 Functions: 19 19 100.0 %

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

Generated by: LCOV version 1.14