LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/jsonfg - ogrjsonfgwritelayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 188 199 94.5 %
Date: 2024-05-15 17:37:59 Functions: 10 10 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             :  * 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 "cpl_time.h"
      31             : #include "ogrgeojsonreader.h"  // OGRJSonParse()
      32             : 
      33             : #include <algorithm>
      34             : 
      35             : /************************************************************************/
      36             : /*                         OGRJSONFGWriteLayer()                        */
      37             : /************************************************************************/
      38             : 
      39          90 : OGRJSONFGWriteLayer::OGRJSONFGWriteLayer(
      40             :     const char *pszName, const OGRSpatialReference *poSRS,
      41             :     std::unique_ptr<OGRCoordinateTransformation> &&poCTToWGS84,
      42             :     const std::string &osCoordRefSys, OGRwkbGeometryType eGType,
      43          90 :     CSLConstList papszOptions, OGRJSONFGDataset *poDS)
      44          90 :     : poDS_(poDS), poFeatureDefn_(new OGRFeatureDefn(pszName)),
      45         180 :       poCTToWGS84_(std::move(poCTToWGS84)), osCoordRefSys_(osCoordRefSys)
      46             : {
      47          90 :     poFeatureDefn_->Reference();
      48          90 :     poFeatureDefn_->SetGeomType(eGType);
      49          90 :     if (eGType != wkbNone && poSRS)
      50             :     {
      51          33 :         auto poSRSClone = poSRS->Clone();
      52          33 :         poFeatureDefn_->GetGeomFieldDefn(0)->SetSpatialRef(poSRSClone);
      53          33 :         poSRSClone->Release();
      54          33 :         m_bMustSwapForPlace = OGRJSONFGMustSwapXY(poSRS);
      55             :     }
      56          90 :     SetDescription(poFeatureDefn_->GetName());
      57             : 
      58         156 :     bIsWGS84CRS_ = osCoordRefSys_.find("[OGC:CRS84]") != std::string::npos ||
      59         111 :                    osCoordRefSys_.find("[OGC:CRS84h]") != std::string::npos ||
      60         201 :                    osCoordRefSys_.find("[EPSG:4326]") != std::string::npos ||
      61          43 :                    osCoordRefSys_.find("[EPSG:4979]") != std::string::npos;
      62             : 
      63          90 :     oWriteOptions_.nXYCoordPrecision = atoi(CSLFetchNameValueDef(
      64             :         papszOptions, "XY_COORD_PRECISION_GEOMETRY", "-1"));
      65          90 :     oWriteOptions_.nZCoordPrecision = atoi(
      66             :         CSLFetchNameValueDef(papszOptions, "Z_COORD_PRECISION_GEOMETRY", "-1"));
      67          90 :     oWriteOptions_.nSignificantFigures =
      68          90 :         atoi(CSLFetchNameValueDef(papszOptions, "SIGNIFICANT_FIGURES", "-1"));
      69          90 :     oWriteOptions_.SetRFC7946Settings();
      70          90 :     oWriteOptions_.SetIDOptions(papszOptions);
      71             : 
      72          90 :     oWriteOptionsPlace_.nXYCoordPrecision = atoi(
      73             :         CSLFetchNameValueDef(papszOptions, "XY_COORD_PRECISION_PLACE", "-1"));
      74          90 :     oWriteOptionsPlace_.nZCoordPrecision = atoi(
      75             :         CSLFetchNameValueDef(papszOptions, "Z_COORD_PRECISION_PLACE", "-1"));
      76          90 :     oWriteOptionsPlace_.nSignificantFigures =
      77          90 :         atoi(CSLFetchNameValueDef(papszOptions, "SIGNIFICANT_FIGURES", "-1"));
      78             : 
      79          90 :     bWriteFallbackGeometry_ = CPLTestBool(
      80             :         CSLFetchNameValueDef(papszOptions, "WRITE_GEOMETRY", "TRUE"));
      81             : 
      82          90 :     VSILFILE *fp = poDS_->GetOutputFile();
      83          90 :     if (poDS_->IsSingleOutputLayer())
      84             :     {
      85           9 :         auto poFeatureType = json_object_new_string(pszName);
      86           9 :         VSIFPrintfL(fp, "\"featureType\" : %s,\n",
      87             :                     json_object_to_json_string_ext(poFeatureType,
      88             :                                                    JSON_C_TO_STRING_SPACED));
      89           9 :         json_object_put(poFeatureType);
      90           9 :         if (!osCoordRefSys.empty())
      91           9 :             VSIFPrintfL(fp, "\"coordRefSys\" : %s,\n", osCoordRefSys.c_str());
      92             :     }
      93          90 : }
      94             : 
      95             : /************************************************************************/
      96             : /*                        ~OGRJSONFGWriteLayer()                        */
      97             : /************************************************************************/
      98             : 
      99         180 : OGRJSONFGWriteLayer::~OGRJSONFGWriteLayer()
     100             : {
     101          90 :     poFeatureDefn_->Release();
     102         180 : }
     103             : 
     104             : /************************************************************************/
     105             : /*                           SyncToDisk()                               */
     106             : /************************************************************************/
     107             : 
     108           3 : OGRErr OGRJSONFGWriteLayer::SyncToDisk()
     109             : {
     110           3 :     return poDS_->SyncToDiskInternal();
     111             : }
     112             : 
     113             : /************************************************************************/
     114             : /*                       GetValueAsDateOrDateTime()                     */
     115             : /************************************************************************/
     116             : 
     117          16 : static const char *GetValueAsDateOrDateTime(const OGRField *psRawValue,
     118             :                                             OGRFieldType eType)
     119             : {
     120          16 :     if (eType == OFTDate)
     121             :     {
     122          22 :         return CPLSPrintf("%04d-%02d-%02d", psRawValue->Date.Year,
     123          11 :                           psRawValue->Date.Month, psRawValue->Date.Day);
     124             :     }
     125             :     else
     126             :     {
     127             :         struct tm brokenDown;
     128           5 :         memset(&brokenDown, 0, sizeof(brokenDown));
     129           5 :         brokenDown.tm_year = psRawValue->Date.Year - 1900;
     130           5 :         brokenDown.tm_mon = psRawValue->Date.Month - 1;
     131           5 :         brokenDown.tm_mday = psRawValue->Date.Day;
     132           5 :         brokenDown.tm_hour = psRawValue->Date.Hour;
     133           5 :         brokenDown.tm_min = psRawValue->Date.Minute;
     134           5 :         brokenDown.tm_sec = 0;
     135           5 :         if (psRawValue->Date.TZFlag > 0)
     136             :         {
     137             :             // Force to UTC
     138           5 :             GIntBig nVal = CPLYMDHMSToUnixTime(&brokenDown);
     139           5 :             nVal -= (psRawValue->Date.TZFlag - 100) * 15 * 60;
     140           5 :             CPLUnixTimeToYMDHMS(nVal, &brokenDown);
     141             :         }
     142           5 :         if (std::fabs(std::round(psRawValue->Date.Second) -
     143           5 :                       psRawValue->Date.Second) < 1e-3)
     144             :         {
     145           8 :             return CPLSPrintf(
     146           4 :                 "%04d-%02d-%02dT%02d:%02d:%02dZ", brokenDown.tm_year + 1900,
     147           4 :                 brokenDown.tm_mon + 1, brokenDown.tm_mday, brokenDown.tm_hour,
     148             :                 brokenDown.tm_min,
     149           8 :                 static_cast<int>(std::round(psRawValue->Date.Second)));
     150             :         }
     151             :         else
     152             :         {
     153           2 :             return CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%06.3fZ",
     154           1 :                               brokenDown.tm_year + 1900, brokenDown.tm_mon + 1,
     155             :                               brokenDown.tm_mday, brokenDown.tm_hour,
     156           1 :                               brokenDown.tm_min, psRawValue->Date.Second);
     157             :         }
     158             :     }
     159             : }
     160             : 
     161             : /************************************************************************/
     162             : /*                     OGRJSONFGWriteGeometry()                         */
     163             : /************************************************************************/
     164             : 
     165             : static json_object *
     166           2 : OGRJSONFGWriteGeometry(const OGRGeometry *poGeometry,
     167             :                        const OGRGeoJSONWriteOptions &oOptions)
     168             : {
     169           2 :     if (wkbFlatten(poGeometry->getGeometryType()) == wkbPolyhedralSurface)
     170             :     {
     171           2 :         const auto poPS = poGeometry->toPolyhedralSurface();
     172           2 :         json_object *poObj = json_object_new_object();
     173           2 :         json_object_object_add(poObj, "type",
     174             :                                json_object_new_string("Polyhedron"));
     175           2 :         json_object *poCoordinates = json_object_new_array();
     176           2 :         json_object_object_add(poObj, "coordinates", poCoordinates);
     177           2 :         json_object *poOuterShell = json_object_new_array();
     178           2 :         json_object_array_add(poCoordinates, poOuterShell);
     179           6 :         for (const auto *poPoly : *poPS)
     180             :         {
     181           4 :             json_object_array_add(poOuterShell,
     182             :                                   OGRGeoJSONWritePolygon(poPoly, oOptions));
     183             :         }
     184           2 :         return poObj;
     185             :     }
     186             :     else
     187             :     {
     188           0 :         return nullptr;
     189             :     }
     190             : }
     191             : 
     192             : /************************************************************************/
     193             : /*                           ICreateFeature()                           */
     194             : /************************************************************************/
     195             : 
     196         140 : OGRErr OGRJSONFGWriteLayer::ICreateFeature(OGRFeature *poFeature)
     197             : {
     198         140 :     VSILFILE *fp = poDS_->GetOutputFile();
     199         140 :     poDS_->BeforeCreateFeature();
     200             : 
     201         140 :     if (oWriteOptions_.bGenerateID && poFeature->GetFID() == OGRNullFID)
     202             :     {
     203           0 :         poFeature->SetFID(nOutCounter_);
     204             :     }
     205             : 
     206         140 :     json_object *poObj = json_object_new_object();
     207             : 
     208         140 :     json_object_object_add(poObj, "type", json_object_new_string("Feature"));
     209             : 
     210             :     /* -------------------------------------------------------------------- */
     211             :     /*      Write FID if available                                          */
     212             :     /* -------------------------------------------------------------------- */
     213         140 :     OGRGeoJSONWriteId(poFeature, poObj, /* bIdAlreadyWritten = */ false,
     214         140 :                       oWriteOptions_);
     215             : 
     216         140 :     if (!poDS_->IsSingleOutputLayer())
     217             :     {
     218         122 :         json_object_object_add(poObj, "featureType",
     219         122 :                                json_object_new_string(GetDescription()));
     220         122 :         if (!osCoordRefSys_.empty() && !bIsWGS84CRS_)
     221             :         {
     222          26 :             json_object *poCoordRefSys = nullptr;
     223          26 :             CPL_IGNORE_RET_VAL(
     224          26 :                 OGRJSonParse(osCoordRefSys_.c_str(), &poCoordRefSys));
     225          26 :             json_object_object_add(poObj, "coordRefSys", poCoordRefSys);
     226             :         }
     227             :     }
     228             : 
     229             :     /* -------------------------------------------------------------------- */
     230             :     /*      Write feature attributes to "properties" object.                */
     231             :     /* -------------------------------------------------------------------- */
     232         280 :     json_object *poObjProps = OGRGeoJSONWriteAttributes(
     233         140 :         poFeature, /* bWriteIdIfFoundInAttributes = */ true, oWriteOptions_);
     234             : 
     235             :     /* -------------------------------------------------------------------- */
     236             :     /*      Deal with time properties.                                      */
     237             :     /* -------------------------------------------------------------------- */
     238         140 :     json_object *poTime = nullptr;
     239         140 :     int nFieldTimeIdx = poFeatureDefn_->GetFieldIndex("jsonfg_time");
     240         140 :     if (nFieldTimeIdx < 0)
     241         138 :         nFieldTimeIdx = poFeatureDefn_->GetFieldIndex("time");
     242         140 :     if (nFieldTimeIdx >= 0 && poFeature->IsFieldSetAndNotNull(nFieldTimeIdx))
     243             :     {
     244           6 :         const auto poFieldDefn = poFeatureDefn_->GetFieldDefn(nFieldTimeIdx);
     245           6 :         const auto eType = poFieldDefn->GetType();
     246           6 :         if (eType == OFTDate || eType == OFTDateTime)
     247             :         {
     248           6 :             json_object_object_del(poObjProps, poFieldDefn->GetNameRef());
     249           6 :             poTime = json_object_new_object();
     250           6 :             json_object_object_add(
     251             :                 poTime, eType == OFTDate ? "date" : "timestamp",
     252             :                 json_object_new_string(GetValueAsDateOrDateTime(
     253           6 :                     poFeature->GetRawFieldRef(nFieldTimeIdx), eType)));
     254             :         }
     255             :     }
     256             :     else
     257             :     {
     258         134 :         bool bHasStartOrStop = false;
     259         134 :         json_object *poTimeStart = nullptr;
     260             :         int nFieldTimeStartIdx =
     261         134 :             poFeatureDefn_->GetFieldIndex("jsonfg_time_start");
     262         134 :         if (nFieldTimeStartIdx < 0)
     263         133 :             nFieldTimeStartIdx = poFeatureDefn_->GetFieldIndex("time_start");
     264         141 :         if (nFieldTimeStartIdx >= 0 &&
     265           7 :             poFeature->IsFieldSetAndNotNull(nFieldTimeStartIdx))
     266             :         {
     267             :             const auto poFieldDefnStart =
     268           5 :                 poFeatureDefn_->GetFieldDefn(nFieldTimeStartIdx);
     269           5 :             const auto eType = poFieldDefnStart->GetType();
     270           5 :             if (eType == OFTDate || eType == OFTDateTime)
     271             :             {
     272           5 :                 json_object_object_del(poObjProps,
     273             :                                        poFieldDefnStart->GetNameRef());
     274           5 :                 poTimeStart = json_object_new_string(GetValueAsDateOrDateTime(
     275           5 :                     poFeature->GetRawFieldRef(nFieldTimeStartIdx), eType));
     276           5 :                 bHasStartOrStop = true;
     277             :             }
     278             :         }
     279             : 
     280         134 :         json_object *poTimeEnd = nullptr;
     281         134 :         int nFieldTimeEndIdx = poFeatureDefn_->GetFieldIndex("jsonfg_time_end");
     282         134 :         if (nFieldTimeEndIdx < 0)
     283         133 :             nFieldTimeEndIdx = poFeatureDefn_->GetFieldIndex("time_end");
     284         141 :         if (nFieldTimeEndIdx >= 0 &&
     285           7 :             poFeature->IsFieldSetAndNotNull(nFieldTimeEndIdx))
     286             :         {
     287             :             const auto poFieldDefnEnd =
     288           5 :                 poFeatureDefn_->GetFieldDefn(nFieldTimeEndIdx);
     289           5 :             const auto eType = poFieldDefnEnd->GetType();
     290           5 :             if (eType == OFTDate || eType == OFTDateTime)
     291             :             {
     292           5 :                 json_object_object_del(poObjProps,
     293             :                                        poFieldDefnEnd->GetNameRef());
     294           5 :                 poTimeEnd = json_object_new_string(GetValueAsDateOrDateTime(
     295           5 :                     poFeature->GetRawFieldRef(nFieldTimeEndIdx), eType));
     296           5 :                 bHasStartOrStop = true;
     297             :             }
     298             :         }
     299             : 
     300         134 :         if (bHasStartOrStop)
     301             :         {
     302           7 :             poTime = json_object_new_object();
     303           7 :             json_object *poInterval = json_object_new_array();
     304           7 :             json_object_object_add(poTime, "interval", poInterval);
     305           9 :             json_object_array_add(poInterval,
     306             :                                   poTimeStart ? poTimeStart
     307           2 :                                               : json_object_new_string(".."));
     308           9 :             json_object_array_add(poInterval,
     309             :                                   poTimeEnd ? poTimeEnd
     310           2 :                                             : json_object_new_string(".."));
     311             :         }
     312             :     }
     313             : 
     314         140 :     json_object_object_add(poObj, "properties", poObjProps);
     315             : 
     316             :     /* -------------------------------------------------------------------- */
     317             :     /*      Write place and/or geometry                                     */
     318             :     /* -------------------------------------------------------------------- */
     319         140 :     const OGRGeometry *poGeom = poFeature->GetGeometryRef();
     320         140 :     if (!poGeom)
     321             :     {
     322          51 :         json_object_object_add(poObj, "geometry", nullptr);
     323          51 :         json_object_object_add(poObj, "place", nullptr);
     324             :     }
     325             :     else
     326             :     {
     327          89 :         if (wkbFlatten(poGeom->getGeometryType()) == wkbPolyhedralSurface)
     328             :         {
     329           2 :             json_object_object_add(poObj, "geometry", nullptr);
     330           2 :             if (m_bMustSwapForPlace)
     331             :             {
     332             :                 auto poGeomClone =
     333           0 :                     std::unique_ptr<OGRGeometry>(poGeom->clone());
     334           0 :                 poGeomClone->swapXY();
     335           0 :                 json_object_object_add(
     336             :                     poObj, "place",
     337           0 :                     OGRJSONFGWriteGeometry(poGeomClone.get(),
     338           0 :                                            oWriteOptionsPlace_));
     339             :             }
     340             :             else
     341             :             {
     342           2 :                 json_object_object_add(
     343             :                     poObj, "place",
     344           2 :                     OGRJSONFGWriteGeometry(poGeom, oWriteOptionsPlace_));
     345             :             }
     346             :         }
     347          87 :         else if (bIsWGS84CRS_)
     348             :         {
     349          47 :             json_object_object_add(
     350             :                 poObj, "geometry",
     351          47 :                 OGRGeoJSONWriteGeometry(poGeom, oWriteOptions_));
     352          47 :             json_object_object_add(poObj, "place", nullptr);
     353             :         }
     354             :         else
     355             :         {
     356          40 :             if (bWriteFallbackGeometry_ && poCTToWGS84_)
     357             :             {
     358             :                 auto poGeomClone =
     359          78 :                     std::unique_ptr<OGRGeometry>(poGeom->clone());
     360          39 :                 if (poGeomClone->transform(poCTToWGS84_.get()) == OGRERR_NONE)
     361             :                 {
     362          39 :                     json_object_object_add(
     363             :                         poObj, "geometry",
     364          39 :                         OGRGeoJSONWriteGeometry(poGeomClone.get(),
     365          39 :                                                 oWriteOptions_));
     366             :                 }
     367             :                 else
     368             :                 {
     369           0 :                     json_object_object_add(poObj, "geometry", nullptr);
     370             :                 }
     371             :             }
     372             :             else
     373             :             {
     374           1 :                 json_object_object_add(poObj, "geometry", nullptr);
     375             :             }
     376             : 
     377          40 :             if (m_bMustSwapForPlace)
     378             :             {
     379             :                 auto poGeomClone =
     380           6 :                     std::unique_ptr<OGRGeometry>(poGeom->clone());
     381           3 :                 poGeomClone->swapXY();
     382           3 :                 json_object_object_add(
     383             :                     poObj, "place",
     384           3 :                     OGRGeoJSONWriteGeometry(poGeomClone.get(),
     385           3 :                                             oWriteOptionsPlace_));
     386             :             }
     387             :             else
     388             :             {
     389          37 :                 json_object_object_add(
     390             :                     poObj, "place",
     391          37 :                     OGRGeoJSONWriteGeometry(poGeom, oWriteOptionsPlace_));
     392             :             }
     393             :         }
     394             :     }
     395             : 
     396         140 :     json_object_object_add(poObj, "time", poTime);
     397             : 
     398         140 :     VSIFPrintfL(fp, "%s",
     399             :                 json_object_to_json_string_ext(
     400             :                     poObj, JSON_C_TO_STRING_SPACED
     401             : #ifdef JSON_C_TO_STRING_NOSLASHESCAPE
     402             :                                | JSON_C_TO_STRING_NOSLASHESCAPE
     403             : #endif
     404             :                     ));
     405             : 
     406         140 :     json_object_put(poObj);
     407             : 
     408         140 :     ++nOutCounter_;
     409             : 
     410         140 :     return OGRERR_NONE;
     411             : }
     412             : 
     413             : /************************************************************************/
     414             : /*                           CreateField()                              */
     415             : /************************************************************************/
     416             : 
     417         122 : OGRErr OGRJSONFGWriteLayer::CreateField(const OGRFieldDefn *poField,
     418             :                                         int /* bApproxOK */)
     419             : {
     420         122 :     if (poFeatureDefn_->GetFieldIndexCaseSensitive(poField->GetNameRef()) >= 0)
     421             :     {
     422           0 :         CPLDebug("JSONFG", "Field '%s' already present in schema",
     423             :                  poField->GetNameRef());
     424             : 
     425           0 :         return OGRERR_NONE;
     426             :     }
     427             : 
     428         122 :     poFeatureDefn_->AddFieldDefn(poField);
     429             : 
     430         122 :     return OGRERR_NONE;
     431             : }
     432             : 
     433             : /************************************************************************/
     434             : /*                           TestCapability()                           */
     435             : /************************************************************************/
     436             : 
     437         181 : int OGRJSONFGWriteLayer::TestCapability(const char *pszCap)
     438             : {
     439         181 :     if (EQUAL(pszCap, OLCCreateField))
     440          16 :         return TRUE;
     441         165 :     else if (EQUAL(pszCap, OLCSequentialWrite))
     442          16 :         return TRUE;
     443         149 :     else if (EQUAL(pszCap, OLCStringsAsUTF8))
     444           0 :         return TRUE;
     445         149 :     return FALSE;
     446             : }
     447             : 
     448             : /************************************************************************/
     449             : /*                             GetDataset()                             */
     450             : /************************************************************************/
     451             : 
     452          22 : GDALDataset *OGRJSONFGWriteLayer::GetDataset()
     453             : {
     454          22 :     return poDS_;
     455             : }

Generated by: LCOV version 1.14