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

Generated by: LCOV version 1.14