LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/jsonfg - ogrjsonfgreader.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 587 654 89.8 %
Date: 2024-11-21 22:18:42 Functions: 13 13 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             : 
      15             : #include "ogrgeojsonreader.h"
      16             : #include "ogrgeojsonutils.h"
      17             : #include "ogrlibjsonutils.h"
      18             : #include "ogrgeojsongeometry.h"
      19             : #include "ogr_geojson.h"
      20             : 
      21             : #include "cpl_vsi_virtual.h"
      22             : 
      23             : #include <json.h>  // JSON-C
      24             : 
      25             : /************************************************************************/
      26             : /*                  OGRJSONFGReader::~OGRJSONFGReader()                 */
      27             : /************************************************************************/
      28             : 
      29         147 : OGRJSONFGReader::~OGRJSONFGReader()
      30             : {
      31         147 :     if (poObject_)
      32         142 :         json_object_put(poObject_);
      33         147 : }
      34             : 
      35             : /************************************************************************/
      36             : /*                  OGRJSONFGReader::Load()                             */
      37             : /************************************************************************/
      38             : 
      39          50 : bool OGRJSONFGReader::Load(OGRJSONFGDataset *poDS, const char *pszText,
      40             :                            const std::string &osDefaultLayerName)
      41             : {
      42          50 :     if (!OGRJSonParse(pszText, &poObject_))
      43           0 :         return false;
      44             : 
      45          50 :     poDS_ = poDS;
      46          50 :     osDefaultLayerName_ = osDefaultLayerName;
      47             : 
      48          50 :     if (!GenerateLayerDefns())
      49           0 :         return false;
      50             : 
      51          50 :     const GeoJSONObject::Type objType = OGRGeoJSONGetType(poObject_);
      52          50 :     if (objType == GeoJSONObject::eFeature)
      53             :     {
      54           5 :         OGRJSONFGMemLayer *poLayer = nullptr;
      55          10 :         auto poFeat = ReadFeature(poObject_, nullptr, &poLayer, nullptr);
      56           5 :         if (poFeat)
      57             :         {
      58           5 :             poLayer->AddFeature(std::move(poFeat));
      59           5 :             return true;
      60             :         }
      61           0 :         return false;
      62             :     }
      63          45 :     else if (objType == GeoJSONObject::eFeatureCollection)
      64             :     {
      65             :         json_object *poObjFeatures =
      66          45 :             OGRGeoJSONFindMemberByName(poObject_, "features");
      67          90 :         if (nullptr != poObjFeatures &&
      68          45 :             json_type_array == json_object_get_type(poObjFeatures))
      69             :         {
      70          45 :             const auto nFeatures = json_object_array_length(poObjFeatures);
      71          92 :             for (auto i = decltype(nFeatures){0}; i < nFeatures; ++i)
      72             :             {
      73             :                 json_object *poObjFeature =
      74          47 :                     json_object_array_get_idx(poObjFeatures, i);
      75          47 :                 OGRJSONFGMemLayer *poLayer = nullptr;
      76             :                 auto poFeat =
      77          47 :                     ReadFeature(poObjFeature, nullptr, &poLayer, nullptr);
      78          47 :                 if (!poFeat)
      79           0 :                     return false;
      80          47 :                 poLayer->AddFeature(std::move(poFeat));
      81             :             }
      82             :         }
      83             :     }
      84             :     else
      85             :     {
      86           0 :         return false;
      87             :     }
      88             : 
      89          45 :     return true;
      90             : }
      91             : 
      92             : /************************************************************************/
      93             : /*                    OGRJSONFGReadCoordRefSys()                        */
      94             : /************************************************************************/
      95             : 
      96             : static std::unique_ptr<OGRSpatialReference>
      97         135 : OGRJSONFGReadCoordRefSys(json_object *poCoordRefSys, bool bCanRecurse = true)
      98             : {
      99         135 :     const auto eType = json_object_get_type(poCoordRefSys);
     100         135 :     if (eType == json_type_string)
     101             :     {
     102         103 :         const char *pszStr = json_object_get_string(poCoordRefSys);
     103         103 :         if (pszStr[0] == '[' && pszStr[strlen(pszStr) - 1] == ']')
     104             :         {
     105             :             // Safe CURIE, e.g. "[EPSG:4326]"
     106          84 :             const char *pszColon = strchr(pszStr + 1, ':');
     107          84 :             if (!pszColon)
     108             :             {
     109           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
     110             :                          "Invalid coordRefSys string: %s", pszStr);
     111           2 :                 return nullptr;
     112             :             }
     113         164 :             std::string osURL("http://www.opengis.net/def/crs/");
     114          82 :             osURL.append(pszStr + 1, pszColon - (pszStr + 1));
     115          82 :             osURL += "/0/";
     116             :             osURL.append(pszColon + 1,
     117          82 :                          (pszStr + strlen(pszStr) - 1) - (pszColon + 1));
     118         164 :             auto poSRS = std::make_unique<OGRSpatialReference>();
     119          82 :             if (poSRS->importFromCRSURL(osURL.c_str()) != OGRERR_NONE)
     120             :             {
     121           3 :                 return nullptr;
     122             :             }
     123          79 :             return poSRS;
     124             :         }
     125          19 :         else if (STARTS_WITH(pszStr, "http://www.opengis.net/def/crs/") ||
     126           4 :                  STARTS_WITH(pszStr, "https://www.opengis.net/def/crs/"))
     127             :         {
     128             :             // OGC URI, e.g. "http://www.opengis.net/def/crs/EPSG/0/4326"
     129          30 :             auto poSRS = std::make_unique<OGRSpatialReference>();
     130          15 :             if (poSRS->importFromCRSURL(pszStr) != OGRERR_NONE)
     131             :             {
     132           1 :                 return nullptr;
     133             :             }
     134          14 :             return poSRS;
     135             :         }
     136             :         else
     137             :         {
     138           4 :             CPLError(CE_Failure, CPLE_AppDefined,
     139             :                      "Invalid coordRefSys string: %s", pszStr);
     140           4 :             return nullptr;
     141             :         }
     142             :     }
     143          32 :     else if (eType == json_type_object)
     144             :     {
     145             :         /* Things like
     146             :               {
     147             :                 "type": "Reference",
     148             :                 "href": "http://www.opengis.net/def/crs/EPSG/0/4258",
     149             :                 "epoch": 2016.47
     150             :               }
     151             :         */
     152             : 
     153          16 :         json_object *poType = CPL_json_object_object_get(poCoordRefSys, "type");
     154          16 :         if (!poType)
     155             :         {
     156           2 :             CPLError(CE_Failure, CPLE_AppDefined,
     157             :                      "Missing type member in coordRefSys object");
     158           2 :             return nullptr;
     159             :         }
     160          14 :         if (json_object_get_type(poType) != json_type_string)
     161             :         {
     162           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     163             :                      "Type member of coordRefSys object is not a string");
     164           1 :             return nullptr;
     165             :         }
     166          13 :         const char *pszType = json_object_get_string(poType);
     167          13 :         if (strcmp(pszType, "Reference") != 0)
     168             :         {
     169           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     170             :                      "Only type=\"Reference\" handled in coordRefSys object");
     171           1 :             return nullptr;
     172             :         }
     173             : 
     174          12 :         json_object *poHRef = CPL_json_object_object_get(poCoordRefSys, "href");
     175          12 :         if (!poHRef)
     176             :         {
     177           2 :             CPLError(CE_Failure, CPLE_AppDefined,
     178             :                      "Missing href member in coordRefSys object");
     179           2 :             return nullptr;
     180             :         }
     181             : 
     182          20 :         auto poSRS = OGRJSONFGReadCoordRefSys(poHRef);
     183          10 :         if (!poSRS)
     184           2 :             return nullptr;
     185             : 
     186             :         json_object *poEpoch =
     187           8 :             CPL_json_object_object_get(poCoordRefSys, "epoch");
     188           8 :         if (poEpoch)
     189             :         {
     190           6 :             const auto epochType = json_object_get_type(poEpoch);
     191           6 :             if (epochType != json_type_int && epochType != json_type_double)
     192             :             {
     193           1 :                 CPLError(
     194             :                     CE_Failure, CPLE_AppDefined,
     195             :                     "Wrong value type for epoch member in coordRefSys object");
     196           1 :                 return nullptr;
     197             :             }
     198             : 
     199           5 :             poSRS->SetCoordinateEpoch(json_object_get_double(poEpoch));
     200             :         }
     201             : 
     202           7 :         return poSRS;
     203             :     }
     204          16 :     else if (eType == json_type_array && bCanRecurse)
     205             :     {
     206          13 :         if (json_object_array_length(poCoordRefSys) != 2)
     207             :         {
     208           2 :             CPLError(CE_Failure, CPLE_AppDefined,
     209             :                      "Expected 2 items in coordRefSys array");
     210           2 :             return nullptr;
     211             :         }
     212             :         auto poSRS1 = OGRJSONFGReadCoordRefSys(
     213             :             json_object_array_get_idx(poCoordRefSys, 0),
     214          22 :             /* bCanRecurse = */ false);
     215          11 :         if (!poSRS1)
     216           1 :             return nullptr;
     217             :         auto poSRS2 = OGRJSONFGReadCoordRefSys(
     218             :             json_object_array_get_idx(poCoordRefSys, 1),
     219          20 :             /* bCanRecurse = */ false);
     220          10 :         if (!poSRS2)
     221           1 :             return nullptr;
     222          18 :         auto poSRS = std::make_unique<OGRSpatialReference>();
     223             : 
     224          18 :         std::string osName;
     225           9 :         const char *pszName1 = poSRS1->GetName();
     226           9 :         osName = pszName1 ? pszName1 : "unnamed";
     227           9 :         osName += " + ";
     228           9 :         const char *pszName2 = poSRS2->GetName();
     229           9 :         osName += pszName2 ? pszName2 : "unnamed";
     230             : 
     231           9 :         if (poSRS->SetCompoundCS(osName.c_str(), poSRS1.get(), poSRS2.get()) !=
     232             :             OGRERR_NONE)
     233           0 :             return nullptr;
     234           9 :         const double dfEpoch = poSRS1->GetCoordinateEpoch();
     235           9 :         if (dfEpoch > 0)
     236           2 :             poSRS->SetCoordinateEpoch(dfEpoch);
     237           9 :         return poSRS;
     238             :     }
     239             :     else
     240             :     {
     241           3 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid coordRefSys object");
     242             :     }
     243           3 :     return nullptr;
     244             : }
     245             : 
     246             : /************************************************************************/
     247             : /*              OGRJSONFGReader::AnalyzeWithStreamingParser()           */
     248             : /************************************************************************/
     249             : 
     250          92 : bool OGRJSONFGReader::AnalyzeWithStreamingParser(
     251             :     OGRJSONFGDataset *poDS, VSILFILE *fp, const std::string &osDefaultLayerName,
     252             :     bool &bCanTryWithNonStreamingParserOut)
     253             : {
     254          92 :     poDS_ = poDS;
     255          92 :     osDefaultLayerName_ = osDefaultLayerName;
     256             : 
     257          92 :     bCanTryWithNonStreamingParserOut = false;
     258         184 :     OGRJSONFGStreamingParser oParser(*this, /*bFirstPass = */ true);
     259             : 
     260         184 :     std::vector<GByte> abyBuffer;
     261          92 :     abyBuffer.resize(4096 * 10);
     262             :     while (true)
     263             :     {
     264          92 :         size_t nRead = VSIFReadL(abyBuffer.data(), 1, abyBuffer.size(), fp);
     265          92 :         const bool bFinished = nRead < abyBuffer.size();
     266          92 :         if (!oParser.Parse(reinterpret_cast<const char *>(abyBuffer.data()),
     267         184 :                            nRead, bFinished) ||
     268          92 :             oParser.ExceptionOccurred())
     269             :         {
     270           0 :             return false;
     271             :         }
     272          92 :         if (oParser.IsTypeKnown() && !oParser.IsFeatureCollection())
     273             :         {
     274           0 :             break;
     275             :         }
     276          92 :         if (bFinished)
     277          92 :             break;
     278           0 :     }
     279             : 
     280          92 :     if (!oParser.IsTypeKnown() || !oParser.IsFeatureCollection())
     281             :     {
     282           0 :         fp->Seek(0, SEEK_END);
     283           0 :         const vsi_l_offset nFileSize = fp->Tell();
     284             :         const vsi_l_offset nRAM =
     285           0 :             static_cast<vsi_l_offset>(CPLGetUsablePhysicalRAM());
     286           0 :         if (nRAM == 0 || nRAM > nFileSize * 20)
     287             :         {
     288             :             // Only try full ingestion if we have 20x more RAM than the file
     289             :             // size
     290           0 :             bCanTryWithNonStreamingParserOut = true;
     291             :         }
     292           0 :         return false;
     293             :     }
     294             : 
     295          92 :     poObject_ = oParser.StealRootObject();
     296             : 
     297          92 :     return FinalizeGenerateLayerDefns(true);
     298             : }
     299             : 
     300             : /************************************************************************/
     301             : /*                OGRJSONFGReader::GenerateLayerDefns()                 */
     302             : /************************************************************************/
     303             : 
     304          50 : bool OGRJSONFGReader::GenerateLayerDefns()
     305             : {
     306          50 :     const GeoJSONObject::Type objType = OGRGeoJSONGetType(poObject_);
     307          50 :     if (objType == GeoJSONObject::eFeature)
     308             :     {
     309           5 :         if (!GenerateLayerDefnFromFeature(poObject_))
     310           0 :             return false;
     311             :     }
     312          45 :     else if (objType == GeoJSONObject::eFeatureCollection)
     313             :     {
     314             :         json_object *poObjFeatures =
     315          45 :             OGRGeoJSONFindMemberByName(poObject_, "features");
     316          90 :         if (nullptr != poObjFeatures &&
     317          45 :             json_type_array == json_object_get_type(poObjFeatures))
     318             :         {
     319          45 :             const auto nFeatures = json_object_array_length(poObjFeatures);
     320          92 :             for (auto i = decltype(nFeatures){0}; i < nFeatures; ++i)
     321             :             {
     322             :                 json_object *poObjFeature =
     323          47 :                     json_object_array_get_idx(poObjFeatures, i);
     324          47 :                 if (!GenerateLayerDefnFromFeature(poObjFeature))
     325             :                 {
     326           0 :                     return false;
     327             :                 }
     328             :             }
     329             :         }
     330             :         else
     331             :         {
     332           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     333             :                      "Invalid FeatureCollection object. "
     334             :                      "Missing \'features\' member.");
     335           0 :             return false;
     336             :         }
     337             :     }
     338             :     else
     339             :     {
     340           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     341             :                  "Missing or unhandled root type object");
     342           0 :         return false;
     343             :     }
     344             : 
     345          50 :     return FinalizeGenerateLayerDefns(false);
     346             : }
     347             : 
     348             : /************************************************************************/
     349             : /*             OGRJSONFGReader::FinalizeGenerateLayerDefns()            */
     350             : /************************************************************************/
     351             : 
     352         142 : bool OGRJSONFGReader::FinalizeGenerateLayerDefns(bool bStreamedLayer)
     353             : {
     354         142 :     json_object *poName = CPL_json_object_object_get(poObject_, "featureType");
     355         142 :     if (poName && json_object_get_type(poName) == json_type_string)
     356             :     {
     357             :         // Remap from hard-coded default layer name to the one of featureType
     358          13 :         auto oIter = oMapBuildContext_.find(osDefaultLayerName_);
     359          13 :         osDefaultLayerName_ = json_object_get_string(poName);
     360          13 :         if (oIter != oMapBuildContext_.end())
     361             :         {
     362          24 :             auto oBuildContext = std::move(oIter->second);
     363          12 :             oMapBuildContext_.erase(oIter);
     364          12 :             oMapBuildContext_[osDefaultLayerName_] = std::move(oBuildContext);
     365             :         }
     366             :     }
     367         129 :     else if (poName && json_object_get_type(poName) == json_type_array)
     368             :     {
     369             :         static bool bWarningMsgEmitted = false;
     370           0 :         if (!bWarningMsgEmitted)
     371             :         {
     372           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     373             :                      "featureType value as an array is not supported.");
     374           0 :             bWarningMsgEmitted = true;
     375             :         }
     376             :     }
     377             : 
     378         142 :     json_object *poCoordRefSys = nullptr;
     379           0 :     std::unique_ptr<OGRSpatialReference> poSRSTopLevel;
     380         142 :     bool bInvalidCRS = false;
     381         142 :     bool bSwapPlacesXYTopLevel = false;
     382         206 :     if (json_object_object_get_ex(poObject_, "coordRefSys", &poCoordRefSys) &&
     383          64 :         eGeometryElement_ != GeometryElement::GEOMETRY)
     384             :     {
     385          61 :         poSRSTopLevel = OGRJSONFGReadCoordRefSys(poCoordRefSys);
     386          61 :         if (poSRSTopLevel)
     387             :         {
     388          39 :             poSRSTopLevel->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     389          39 :             bSwapPlacesXYTopLevel = OGRJSONFGMustSwapXY(poSRSTopLevel.get());
     390             :         }
     391             :         else
     392             :         {
     393          22 :             bInvalidCRS = true;
     394             :         }
     395             :     }
     396             : 
     397             :     // Finalize layer definition building and create OGRLayer objects
     398         288 :     for (auto &oBuildContextIter : oMapBuildContext_)
     399             :     {
     400         146 :         const char *pszLayerName = oBuildContextIter.first.c_str();
     401         146 :         auto &oBuildContext = oBuildContextIter.second;
     402             : 
     403         146 :         FinalizeBuildContext(oBuildContext, pszLayerName, bStreamedLayer,
     404             :                              bInvalidCRS, bSwapPlacesXYTopLevel,
     405             :                              poSRSTopLevel.get());
     406             :     }
     407             : 
     408         284 :     return true;
     409             : }
     410             : 
     411             : /************************************************************************/
     412             : /*                OGRJSONFGReader::FinalizeBuildContext()               */
     413             : /************************************************************************/
     414             : 
     415         146 : void OGRJSONFGReader::FinalizeBuildContext(LayerDefnBuildContext &oBuildContext,
     416             :                                            const char *pszLayerName,
     417             :                                            bool bStreamedLayer,
     418             :                                            bool bInvalidCRS,
     419             :                                            bool bSwapPlacesXYTopLevel,
     420             :                                            OGRSpatialReference *poSRSTopLevel)
     421             : {
     422             :     std::unique_ptr<OGRSpatialReference> poSRSWGS84(
     423         292 :         OGRSpatialReference::GetWGS84SRS()->Clone());
     424         146 :     poSRSWGS84->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     425             : 
     426         146 :     OGRSpatialReference *poSRSLayer = nullptr;
     427         146 :     if (oBuildContext.poCRSAtFeatureLevel)
     428             :     {
     429          37 :         poSRSLayer = oBuildContext.poCRSAtFeatureLevel.get();
     430          37 :         oBuildContext.bSwapPlacesXY = OGRJSONFGMustSwapXY(poSRSLayer);
     431             :     }
     432         109 :     else if (poSRSTopLevel)
     433             :     {
     434          31 :         poSRSLayer = poSRSTopLevel;
     435          31 :         oBuildContext.bSwapPlacesXY = bSwapPlacesXYTopLevel;
     436             :     }
     437         146 :     if (!bInvalidCRS)
     438             :     {
     439         124 :         if (!poSRSLayer && !oBuildContext.bHasCoordRefSysAtFeatureLevel)
     440             :         {
     441             :             // No coordRefSys member found anywhere ? Fallback to WGS 84
     442          55 :             poSRSLayer = poSRSWGS84.get();
     443             :         }
     444             : 
     445         124 :         if (poSRSLayer && poSRSLayer->IsSame(poSRSWGS84.get()))
     446             :         {
     447          62 :             oBuildContext.bLayerCRSIsWGS84 = true;
     448             :         }
     449          62 :         else if (poSRSLayer)
     450             :         {
     451          61 :             const char *pszAuthName = poSRSLayer->GetAuthorityName(nullptr);
     452          61 :             if (!(pszAuthName && STARTS_WITH(pszAuthName, "IAU")))
     453             :             {
     454          61 :                 oBuildContext.poCTWGS84ToLayerCRS.reset(
     455          61 :                     OGRCreateCoordinateTransformation(poSRSWGS84.get(),
     456             :                                                       poSRSLayer));
     457             :             }
     458             :         }
     459             :     }
     460             : 
     461         146 :     std::unique_ptr<OGRJSONFGMemLayer> poMemLayer;
     462         146 :     std::unique_ptr<OGRJSONFGStreamedLayer> poStreamedLayer;
     463             :     OGRLayer *poLayer;
     464         146 :     if (bStreamedLayer)
     465             :     {
     466          96 :         poStreamedLayer = std::make_unique<OGRJSONFGStreamedLayer>(
     467          96 :             poDS_, pszLayerName, poSRSLayer, oBuildContext.eLayerGeomType);
     468          96 :         poLayer = poStreamedLayer.get();
     469             :     }
     470             :     else
     471             :     {
     472          50 :         poMemLayer = std::make_unique<OGRJSONFGMemLayer>(
     473          50 :             poDS_, pszLayerName, poSRSLayer, oBuildContext.eLayerGeomType);
     474          50 :         poLayer = poMemLayer.get();
     475             :     }
     476             : 
     477             :     // Note: the current strategy will not produce stable output, depending
     478             :     // on the order of features, if there are conflicting order / cycles.
     479             :     // See https://github.com/OSGeo/gdal/pull/4552 for a number of potential
     480             :     // resolutions if that has to be solved in the future.
     481         146 :     OGRFeatureDefn *poLayerDefn = poLayer->GetLayerDefn();
     482         292 :     auto oTemporaryUnsealer(poLayerDefn->GetTemporaryUnsealer());
     483             : 
     484         146 :     if (poLayer->GetLayerDefn()->GetGeomType() != wkbNone)
     485             :     {
     486         292 :         OGRGeoJSONWriteOptions options;
     487             : 
     488         146 :         json_object *poXYRes = CPL_json_object_object_get(
     489             :             poObject_, "xy_coordinate_resolution_place");
     490         146 :         if (poXYRes && (json_object_get_type(poXYRes) == json_type_double ||
     491           0 :                         json_object_get_type(poXYRes) == json_type_int))
     492             :         {
     493           2 :             auto poGeomFieldDefn = poLayerDefn->GetGeomFieldDefn(0);
     494             :             OGRGeomCoordinatePrecision oCoordPrec(
     495           4 :                 poGeomFieldDefn->GetCoordinatePrecision());
     496           2 :             oCoordPrec.dfXYResolution = json_object_get_double(poXYRes);
     497           2 :             poGeomFieldDefn->SetCoordinatePrecision(oCoordPrec);
     498             :         }
     499             : 
     500         146 :         json_object *poZRes = CPL_json_object_object_get(
     501             :             poObject_, "z_coordinate_resolution_place");
     502         146 :         if (poZRes && (json_object_get_type(poZRes) == json_type_double ||
     503           0 :                        json_object_get_type(poZRes) == json_type_int))
     504             :         {
     505           2 :             auto poGeomFieldDefn = poLayerDefn->GetGeomFieldDefn(0);
     506             :             OGRGeomCoordinatePrecision oCoordPrec(
     507           4 :                 poGeomFieldDefn->GetCoordinatePrecision());
     508           2 :             oCoordPrec.dfZResolution = json_object_get_double(poZRes);
     509           2 :             poGeomFieldDefn->SetCoordinatePrecision(oCoordPrec);
     510             :         }
     511             :     }
     512             : 
     513         292 :     std::set<std::string> oSetFieldNames;
     514         269 :     for (const auto &poFieldDefn : oBuildContext.apoFieldDefn)
     515         123 :         oSetFieldNames.insert(poFieldDefn->GetNameRef());
     516             : 
     517             :     auto AddTimeField =
     518          88 :         [poLayerDefn, &oSetFieldNames](const char *pszName, OGRFieldType eType)
     519             :     {
     520          22 :         if (oSetFieldNames.find(pszName) == oSetFieldNames.end())
     521             :         {
     522          42 :             OGRFieldDefn oFieldDefn(pszName, eType);
     523          21 :             poLayerDefn->AddFieldDefn(&oFieldDefn);
     524             :         }
     525             :         else
     526             :         {
     527           2 :             OGRFieldDefn oFieldDefn((std::string("jsonfg_") + pszName).c_str(),
     528           2 :                                     eType);
     529           1 :             poLayerDefn->AddFieldDefn(&oFieldDefn);
     530             :         }
     531          22 :         return poLayerDefn->GetFieldCount() - 1;
     532         146 :     };
     533             : 
     534         146 :     if (oBuildContext.bHasTimeTimestamp)
     535             :     {
     536           2 :         oBuildContext.nIdxFieldTime = AddTimeField("time", OFTDateTime);
     537             :     }
     538         144 :     else if (oBuildContext.bHasTimeDate)
     539             :     {
     540           2 :         oBuildContext.nIdxFieldTime = AddTimeField("time", OFTDate);
     541             :     }
     542             : 
     543         146 :     if (oBuildContext.bHasTimeIntervalStartDate ||
     544         143 :         oBuildContext.bHasTimeIntervalStartTimestamp ||
     545         139 :         oBuildContext.bHasTimeIntervalEndDate ||
     546         138 :         oBuildContext.bHasTimeIntervalEndTimestamp)
     547             :     {
     548             :         // Mix of Date/DateTime for start/end is not supposed to happen,
     549             :         // but be tolerant to that
     550           9 :         if (oBuildContext.bHasTimeIntervalStartTimestamp)
     551             :         {
     552           5 :             oBuildContext.nIdxFieldTimeStart =
     553           5 :                 AddTimeField("time_start", OFTDateTime);
     554             :         }
     555           4 :         else if (oBuildContext.bHasTimeIntervalStartDate)
     556             :         {
     557           2 :             oBuildContext.nIdxFieldTimeStart =
     558           2 :                 AddTimeField("time_start", OFTDate);
     559             :         }
     560           2 :         else if (oBuildContext.bHasTimeIntervalEndTimestamp)
     561             :         {
     562           1 :             oBuildContext.nIdxFieldTimeStart =
     563           1 :                 AddTimeField("time_start", OFTDateTime);
     564             :         }
     565             :         else /* if( oBuildContext.bHasTimeIntervalEndDate ) */
     566             :         {
     567           1 :             oBuildContext.nIdxFieldTimeStart =
     568           1 :                 AddTimeField("time_start", OFTDate);
     569             :         }
     570             : 
     571           9 :         if (oBuildContext.bHasTimeIntervalEndTimestamp)
     572             :         {
     573           3 :             oBuildContext.nIdxFieldTimeEnd =
     574           3 :                 AddTimeField("time_end", OFTDateTime);
     575             :         }
     576           6 :         else if (oBuildContext.bHasTimeIntervalEndDate)
     577             :         {
     578           2 :             oBuildContext.nIdxFieldTimeEnd = AddTimeField("time_end", OFTDate);
     579             :         }
     580           4 :         else if (oBuildContext.bHasTimeIntervalStartTimestamp)
     581             :         {
     582           3 :             oBuildContext.nIdxFieldTimeEnd =
     583           3 :                 AddTimeField("time_end", OFTDateTime);
     584             :         }
     585             :         else /* if( oBuildContext.bHasTimeIntervalStartDate ) */
     586             :         {
     587           1 :             oBuildContext.nIdxFieldTimeEnd = AddTimeField("time_end", OFTDate);
     588             :         }
     589             :     }
     590             : 
     591         292 :     const auto sortedFields = oBuildContext.dag.getTopologicalOrdering();
     592         146 :     CPLAssert(sortedFields.size() == oBuildContext.apoFieldDefn.size());
     593         269 :     for (int idx : sortedFields)
     594             :     {
     595         123 :         poLayerDefn->AddFieldDefn(oBuildContext.apoFieldDefn[idx].get());
     596             :     }
     597             : 
     598         146 :     if (!oBuildContext.bFeatureLevelIdAsFID)
     599             :     {
     600         114 :         const int idx = poLayerDefn->GetFieldIndexCaseSensitive("id");
     601         114 :         if (idx >= 0)
     602             :         {
     603           2 :             OGRFieldDefn *poFDefn = poLayerDefn->GetFieldDefn(idx);
     604           4 :             if (poFDefn->GetType() == OFTInteger ||
     605           2 :                 poFDefn->GetType() == OFTInteger64)
     606             :             {
     607           0 :                 if (poStreamedLayer)
     608             :                 {
     609           0 :                     poStreamedLayer->SetFIDColumn(
     610           0 :                         poLayerDefn->GetFieldDefn(idx)->GetNameRef());
     611             :                 }
     612             :                 else
     613             :                 {
     614           0 :                     poMemLayer->SetFIDColumn(
     615           0 :                         poLayerDefn->GetFieldDefn(idx)->GetNameRef());
     616             :                 }
     617             :             }
     618             :         }
     619             :     }
     620             : 
     621         146 :     if (oBuildContext.bNeedFID64)
     622           0 :         poLayer->SetMetadataItem(OLMD_FID64, "YES");
     623             : 
     624         146 :     if (poStreamedLayer)
     625             :     {
     626          96 :         poStreamedLayer->SetFeatureCount(oBuildContext.nFeatureCount);
     627          96 :         oBuildContext.poStreamedLayer =
     628          96 :             poDS_->AddLayer(std::move(poStreamedLayer));
     629             :     }
     630             :     else
     631             :     {
     632          50 :         oBuildContext.poMemLayer = poDS_->AddLayer(std::move(poMemLayer));
     633             :     }
     634         146 : }
     635             : 
     636             : /************************************************************************/
     637             : /*            OGRJSONFGReader::GetLayerNameForFeature()                 */
     638             : /************************************************************************/
     639             : 
     640         812 : const char *OGRJSONFGReader::GetLayerNameForFeature(json_object *poObj) const
     641             : {
     642         812 :     const char *pszName = osDefaultLayerName_.c_str();
     643         812 :     json_object *poName = CPL_json_object_object_get(poObj, "featureType");
     644             :     // The spec allows an array of strings, but we don't support that
     645         812 :     if (poName != nullptr && json_object_get_type(poName) == json_type_string)
     646             :     {
     647         141 :         pszName = json_object_get_string(poName);
     648             :     }
     649         812 :     return pszName;
     650             : }
     651             : 
     652             : /************************************************************************/
     653             : /*                     OGRJSONFGGetOGRGeometryType()                    */
     654             : /************************************************************************/
     655             : 
     656          77 : static OGRwkbGeometryType OGRJSONFGGetOGRGeometryType(json_object *poObj)
     657             : {
     658          77 :     const auto eType = OGRGeoJSONGetOGRGeometryType(poObj);
     659          77 :     if (eType != wkbUnknown)
     660          72 :         return eType;
     661             : 
     662           5 :     json_object *poObjType = CPL_json_object_object_get(poObj, "type");
     663           5 :     const char *pszType = json_object_get_string(poObjType);
     664           5 :     if (!pszType)
     665           0 :         return wkbNone;
     666             : 
     667           5 :     if (strcmp(pszType, "Polyhedron") == 0)
     668             :     {
     669           2 :         return wkbPolyhedralSurfaceZ;
     670             :     }
     671           3 :     else if (strcmp(pszType, "Prism") == 0)
     672             :     {
     673           3 :         auto poBase = CPL_json_object_object_get(poObj, "base");
     674           3 :         if (!poBase || json_object_get_type(poBase) != json_type_object)
     675             :         {
     676           0 :             return wkbNone;
     677             :         }
     678             : 
     679           3 :         const auto eBaseGeomType = OGRGeoJSONGetOGRGeometryType(poBase);
     680           3 :         if (eBaseGeomType == wkbPoint)
     681             :         {
     682           1 :             return wkbLineString25D;
     683             :         }
     684           2 :         else if (eBaseGeomType == wkbLineString)
     685             :         {
     686           1 :             return wkbMultiPolygon25D;
     687             :         }
     688           1 :         else if (eBaseGeomType == wkbPolygon)
     689             :         {
     690           1 :             return wkbPolyhedralSurfaceZ;
     691             :         }
     692             :     }
     693           0 :     return wkbNone;
     694             : }
     695             : 
     696             : /************************************************************************/
     697             : /*                   OGRJSONFGCreateNonGeoJSONGeometry()                */
     698             : /************************************************************************/
     699             : 
     700             : static std::unique_ptr<OGRGeometry>
     701           5 : OGRJSONFGCreateNonGeoJSONGeometry(json_object *poObj, bool bWarn)
     702             : {
     703           5 :     json_object *poObjType = CPL_json_object_object_get(poObj, "type");
     704           5 :     const char *pszType = json_object_get_string(poObjType);
     705           5 :     if (!pszType)
     706           0 :         return nullptr;
     707             : 
     708           5 :     if (strcmp(pszType, "Polyhedron") == 0)
     709             :     {
     710           2 :         auto poCoordinates = CPL_json_object_object_get(poObj, "coordinates");
     711           4 :         if (!poCoordinates ||
     712           2 :             json_object_get_type(poCoordinates) != json_type_array)
     713             :         {
     714           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     715             :                      "Missing or invalid coordinates in Polyhedron");
     716           0 :             return nullptr;
     717             :         }
     718           2 :         if (json_object_array_length(poCoordinates) != 1)
     719             :         {
     720           0 :             if (bWarn)
     721             :             {
     722           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     723             :                          "Polyhedron with inner shells not supported");
     724             :             }
     725           0 :             return nullptr;
     726             :         }
     727           2 :         auto poJOuterShell = json_object_array_get_idx(poCoordinates, 0);
     728           4 :         auto poGeom = std::make_unique<OGRPolyhedralSurface>();
     729           2 :         const auto nPolys = json_object_array_length(poJOuterShell);
     730           6 :         for (auto i = decltype(nPolys){0}; i < nPolys; ++i)
     731             :         {
     732           4 :             auto poJPoly = json_object_array_get_idx(poJOuterShell, i);
     733           4 :             if (!poJPoly)
     734           0 :                 return nullptr;
     735           4 :             auto poPoly = OGRGeoJSONReadPolygon(poJPoly, /*bRaw = */ true);
     736           4 :             if (!poPoly)
     737           0 :                 return nullptr;
     738           4 :             if (poGeom->addGeometryDirectly(poPoly) != OGRERR_NONE)
     739           0 :                 return nullptr;
     740             :         }
     741           2 :         if (nPolys == 0)
     742           1 :             poGeom->set3D(true);
     743             : 
     744           2 :         return poGeom;
     745             :     }
     746           3 :     else if (strcmp(pszType, "Prism") == 0)
     747             :     {
     748           3 :         auto poBase = CPL_json_object_object_get(poObj, "base");
     749           3 :         if (!poBase || json_object_get_type(poBase) != json_type_object)
     750             :         {
     751           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     752             :                      "Missing or invalid base in Prism");
     753           0 :             return nullptr;
     754             :         }
     755             : 
     756           3 :         json_object *poLower = CPL_json_object_object_get(poObj, "lower");
     757           3 :         const double dfLower = poLower ? json_object_get_double(poLower) : 0.0;
     758           3 :         json_object *poUpper = CPL_json_object_object_get(poObj, "upper");
     759           3 :         const double dfUpper = poUpper ? json_object_get_double(poUpper) : 0.0;
     760             : 
     761             :         auto poBaseGeom =
     762           6 :             std::unique_ptr<OGRGeometry>(OGRGeoJSONReadGeometry(poBase));
     763           3 :         if (!poBaseGeom)
     764           0 :             return nullptr;
     765           3 :         if (poBaseGeom->getGeometryType() == wkbPoint)
     766             :         {
     767           1 :             const auto poPoint = poBaseGeom.get()->toPoint();
     768           2 :             auto poGeom = std::make_unique<OGRLineString>();
     769           1 :             poGeom->addPoint(poPoint->getX(), poPoint->getY(), dfLower);
     770           1 :             poGeom->addPoint(poPoint->getX(), poPoint->getY(), dfUpper);
     771           1 :             return poGeom;
     772             :         }
     773           2 :         else if (poBaseGeom->getGeometryType() == wkbLineString)
     774             :         {
     775           1 :             const auto poLS = poBaseGeom.get()->toLineString();
     776           2 :             auto poGeom = std::make_unique<OGRMultiPolygon>();
     777           2 :             for (int i = 0; i < poLS->getNumPoints() - 1; ++i)
     778             :             {
     779           1 :                 auto poPoly = new OGRPolygon();
     780           1 :                 auto poRing = new OGRLinearRing();
     781           1 :                 poRing->addPoint(poLS->getX(i), poLS->getY(i), dfLower);
     782           1 :                 poRing->addPoint(poLS->getX(i + 1), poLS->getY(i + 1), dfLower);
     783           1 :                 poRing->addPoint(poLS->getX(i + 1), poLS->getY(i + 1), dfUpper);
     784           1 :                 poRing->addPoint(poLS->getX(i), poLS->getY(i), dfUpper);
     785           1 :                 poRing->addPoint(poLS->getX(i), poLS->getY(i), dfLower);
     786           1 :                 poPoly->addRingDirectly(poRing);
     787           1 :                 poGeom->addGeometryDirectly(poPoly);
     788             :             }
     789           1 :             return poGeom;
     790             :         }
     791           1 :         else if (poBaseGeom->getGeometryType() == wkbPolygon)
     792             :         {
     793           1 :             const auto poBasePoly = poBaseGeom.get()->toPolygon();
     794           1 :             if (poBasePoly->getNumInteriorRings() > 0)
     795             :             {
     796           0 :                 if (bWarn)
     797             :                 {
     798           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
     799             :                              "Polygon with holes is not supported as the base "
     800             :                              "for Prism");
     801             :                 }
     802           0 :                 return nullptr;
     803             :             }
     804           1 :             const auto poLS = poBasePoly->getExteriorRing();
     805           1 :             if (poLS == nullptr)
     806             :             {
     807           0 :                 return nullptr;
     808             :             }
     809           2 :             auto poGeom = std::make_unique<OGRPolyhedralSurface>();
     810             :             // Build lower face
     811             :             {
     812           1 :                 auto poPoly = new OGRPolygon();
     813           1 :                 auto poRing = new OGRLinearRing();
     814           5 :                 for (int i = 0; i < poLS->getNumPoints(); ++i)
     815             :                 {
     816           4 :                     poRing->addPoint(poLS->getX(i), poLS->getY(i), dfLower);
     817             :                 }
     818           1 :                 poPoly->addRingDirectly(poRing);
     819           1 :                 poGeom->addGeometryDirectly(poPoly);
     820             :             }
     821             :             // Build side faces
     822           4 :             for (int i = 0; i < poLS->getNumPoints() - 1; ++i)
     823             :             {
     824           3 :                 auto poPoly = new OGRPolygon();
     825           3 :                 auto poRing = new OGRLinearRing();
     826           3 :                 poRing->addPoint(poLS->getX(i), poLS->getY(i), dfLower);
     827           3 :                 poRing->addPoint(poLS->getX(i + 1), poLS->getY(i + 1), dfLower);
     828           3 :                 poRing->addPoint(poLS->getX(i + 1), poLS->getY(i + 1), dfUpper);
     829           3 :                 poRing->addPoint(poLS->getX(i), poLS->getY(i), dfUpper);
     830           3 :                 poRing->addPoint(poLS->getX(i), poLS->getY(i), dfLower);
     831           3 :                 poPoly->addRingDirectly(poRing);
     832           3 :                 poGeom->addGeometryDirectly(poPoly);
     833             :             }
     834             :             // Build upper face
     835             :             {
     836           1 :                 auto poPoly = new OGRPolygon();
     837           1 :                 auto poRing = new OGRLinearRing();
     838           5 :                 for (int i = 0; i < poLS->getNumPoints(); ++i)
     839             :                 {
     840           4 :                     poRing->addPoint(poLS->getX(i), poLS->getY(i), dfUpper);
     841             :                 }
     842           1 :                 poPoly->addRingDirectly(poRing);
     843           1 :                 poGeom->addGeometryDirectly(poPoly);
     844             :             }
     845           1 :             return poGeom;
     846             :         }
     847             :         else
     848             :         {
     849           0 :             if (bWarn)
     850             :             {
     851           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     852             :                          "Unsupported base geometry type for Prism");
     853             :             }
     854           0 :             return nullptr;
     855             :         }
     856             :     }
     857             :     else
     858             :     {
     859           0 :         if (bWarn)
     860             :         {
     861           0 :             CPLError(CE_Warning, CPLE_AppDefined, "Unhandled place.type = %s",
     862             :                      pszType);
     863             :         }
     864           0 :         return nullptr;
     865             :     }
     866             : }
     867             : 
     868             : /************************************************************************/
     869             : /*            OGRJSONFGReader::GenerateLayerDefnFromFeature()           */
     870             : /************************************************************************/
     871             : 
     872         219 : bool OGRJSONFGReader::GenerateLayerDefnFromFeature(json_object *poObj)
     873             : {
     874         219 :     const GeoJSONObject::Type objType = OGRGeoJSONGetType(poObj);
     875         219 :     if (objType != GeoJSONObject::eFeature)
     876             :     {
     877           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Did not get a Feature");
     878           0 :         return false;
     879             :     }
     880             : 
     881         219 :     const char *psLayerName = GetLayerNameForFeature(poObj);
     882             : 
     883         219 :     auto oBuildContextIter = oMapBuildContext_.find(psLayerName);
     884         219 :     if (oBuildContextIter == oMapBuildContext_.end())
     885             :     {
     886         146 :         LayerDefnBuildContext oContext;
     887         146 :         oMapBuildContext_[psLayerName] = std::move(oContext);
     888         146 :         oBuildContextIter = oMapBuildContext_.find(psLayerName);
     889             :     }
     890         219 :     LayerDefnBuildContext *poContext = &(oBuildContextIter->second);
     891             : 
     892         219 :     ++poContext->nFeatureCount;
     893             : 
     894         219 :     json_object *poCoordRefSys = nullptr;
     895         219 :     json_object *poPlace = nullptr;
     896         219 :     if (eGeometryElement_ != GeometryElement::GEOMETRY)
     897             :     {
     898         216 :         poPlace = CPL_json_object_object_get(poObj, "place");
     899         216 :         if (poPlace && json_object_get_type(poPlace) == json_type_object)
     900             :         {
     901          77 :             poCoordRefSys = CPL_json_object_object_get(poPlace, "coordRefSys");
     902             :         }
     903         216 :         if (!poCoordRefSys)
     904         215 :             poCoordRefSys = CPL_json_object_object_get(poObj, "coordRefSys");
     905             : 
     906         216 :         if (poCoordRefSys)
     907             :         {
     908          86 :             std::string osVal = json_object_to_json_string(poCoordRefSys);
     909          43 :             if (!poContext->bHasCoordRefSysAtFeatureLevel)
     910             :             {
     911          39 :                 poContext->bHasCoordRefSysAtFeatureLevel = true;
     912          39 :                 poContext->osCoordRefSysAtFeatureLevel = std::move(osVal);
     913             :                 poContext->poCRSAtFeatureLevel =
     914          39 :                     OGRJSONFGReadCoordRefSys(poCoordRefSys);
     915          39 :                 if (poContext->poCRSAtFeatureLevel)
     916             :                 {
     917          39 :                     poContext->poCRSAtFeatureLevel->SetAxisMappingStrategy(
     918             :                         OAMS_TRADITIONAL_GIS_ORDER);
     919             :                 }
     920             :             }
     921           4 :             else if (poContext->osCoordRefSysAtFeatureLevel != osVal)
     922             :             {
     923           2 :                 poContext->osCoordRefSysAtFeatureLevel.clear();
     924           2 :                 poContext->poCRSAtFeatureLevel.reset();
     925             :             }
     926             :         }
     927             :     }
     928             : 
     929             :     /* -------------------------------------------------------------------- */
     930             :     /*      Deal with place / geometry                                      */
     931             :     /* -------------------------------------------------------------------- */
     932             : 
     933         219 :     if (poContext->bDetectLayerGeomType)
     934             :     {
     935         219 :         bool bFallbackToGeometry =
     936         219 :             (eGeometryElement_ != GeometryElement::PLACE);
     937         219 :         if (poPlace && json_object_get_type(poPlace) == json_type_object)
     938             :         {
     939          77 :             const auto eType = OGRJSONFGGetOGRGeometryType(poPlace);
     940          77 :             if (eType != wkbNone)
     941             :             {
     942          77 :                 bFallbackToGeometry = false;
     943          77 :                 poContext->bDetectLayerGeomType = OGRGeoJSONUpdateLayerGeomType(
     944          77 :                     poContext->bFirstGeometry, eType,
     945          77 :                     poContext->eLayerGeomType);
     946             :             }
     947             :         }
     948             : 
     949         219 :         if (bFallbackToGeometry)
     950             :         {
     951             :             json_object *poGeomObj =
     952         141 :                 CPL_json_object_object_get(poObj, "geometry");
     953         199 :             if (poGeomObj &&
     954          58 :                 json_object_get_type(poGeomObj) == json_type_object)
     955             :             {
     956          58 :                 const auto eType = OGRGeoJSONGetOGRGeometryType(poGeomObj);
     957          58 :                 poContext->bDetectLayerGeomType = OGRGeoJSONUpdateLayerGeomType(
     958          58 :                     poContext->bFirstGeometry, eType,
     959          58 :                     poContext->eLayerGeomType);
     960             :             }
     961             :         }
     962             :     }
     963             : 
     964             :     /* -------------------------------------------------------------------- */
     965             :     /*      Deal with time                                                  */
     966             :     /* -------------------------------------------------------------------- */
     967         219 :     json_object *poTime = CPL_json_object_object_get(poObj, "time");
     968         219 :     if (poTime)
     969             :     {
     970          15 :         json_object *poDate = CPL_json_object_object_get(poTime, "date");
     971          15 :         if (poDate && json_object_get_type(poDate) == json_type_string)
     972           3 :             poContext->bHasTimeDate = true;
     973             : 
     974             :         json_object *poTimestamp =
     975          15 :             CPL_json_object_object_get(poTime, "timestamp");
     976          17 :         if (poTimestamp &&
     977           2 :             json_object_get_type(poTimestamp) == json_type_string)
     978           2 :             poContext->bHasTimeTimestamp = true;
     979             : 
     980             :         json_object *poInterval =
     981          15 :             CPL_json_object_object_get(poTime, "interval");
     982          25 :         if (poInterval && json_object_get_type(poInterval) == json_type_array &&
     983          10 :             json_object_array_length(poInterval) == 2)
     984             :         {
     985          10 :             json_object *poStart = json_object_array_get_idx(poInterval, 0);
     986          10 :             if (poStart && json_object_get_type(poStart) == json_type_string)
     987             :             {
     988          10 :                 const char *pszStart = json_object_get_string(poStart);
     989          10 :                 if (strchr(pszStart, 'Z'))
     990           5 :                     poContext->bHasTimeIntervalStartTimestamp = true;
     991           5 :                 else if (strcmp(pszStart, "..") != 0)
     992           3 :                     poContext->bHasTimeIntervalStartDate = true;
     993             :             }
     994             : 
     995          10 :             json_object *poEnd = json_object_array_get_idx(poInterval, 1);
     996          10 :             if (poEnd && json_object_get_type(poEnd) == json_type_string)
     997             :             {
     998          10 :                 const char *pszEnd = json_object_get_string(poEnd);
     999          10 :                 if (strchr(pszEnd, 'Z'))
    1000           3 :                     poContext->bHasTimeIntervalEndTimestamp = true;
    1001           7 :                 else if (strcmp(pszEnd, "..") != 0)
    1002           3 :                     poContext->bHasTimeIntervalEndDate = true;
    1003             :             }
    1004             :         }
    1005             :     }
    1006             : 
    1007             :     /* -------------------------------------------------------------------- */
    1008             :     /*      Read collection of properties.                                  */
    1009             :     /* -------------------------------------------------------------------- */
    1010         219 :     json_object *poObjProps = CPL_json_object_object_get(poObj, "properties");
    1011             : 
    1012         219 :     int nPrevFieldIdx = -1;
    1013             : 
    1014             :     // First deal with id, either at top level or in properties["id"]
    1015         219 :     OGRGeoJSONGenerateFeatureDefnDealWithID(
    1016         219 :         poObj, poObjProps, nPrevFieldIdx, poContext->oMapFieldNameToIdx,
    1017         219 :         poContext->apoFieldDefn, poContext->dag,
    1018         219 :         poContext->bFeatureLevelIdAsFID, poContext->bFeatureLevelIdAsAttribute,
    1019         219 :         poContext->bNeedFID64);
    1020             : 
    1021         435 :     if (nullptr != poObjProps &&
    1022         216 :         json_object_get_type(poObjProps) == json_type_object)
    1023             :     {
    1024             :         json_object_iter it;
    1025         216 :         it.key = nullptr;
    1026         216 :         it.val = nullptr;
    1027         216 :         it.entry = nullptr;
    1028         432 :         std::vector<int> anCurFieldIndices;
    1029         554 :         json_object_object_foreachC(poObjProps, it)
    1030             :         {
    1031         338 :             anCurFieldIndices.clear();
    1032         338 :             OGRGeoJSONReaderAddOrUpdateField(
    1033         338 :                 anCurFieldIndices, poContext->oMapFieldNameToIdx,
    1034         338 :                 poContext->apoFieldDefn, it.key, it.val,
    1035         338 :                 bFlattenNestedAttributes_, chNestedAttributeSeparator_,
    1036         338 :                 bArrayAsString_, bDateAsString_,
    1037         338 :                 poContext->aoSetUndeterminedTypeFields);
    1038         676 :             for (int idx : anCurFieldIndices)
    1039             :             {
    1040         676 :                 poContext->dag.addNode(
    1041         338 :                     idx, poContext->apoFieldDefn[idx]->GetNameRef());
    1042         338 :                 if (nPrevFieldIdx != -1)
    1043             :                 {
    1044         241 :                     poContext->dag.addEdge(nPrevFieldIdx, idx);
    1045             :                 }
    1046         338 :                 nPrevFieldIdx = idx;
    1047             :             }
    1048             :         }
    1049             :     }
    1050             : 
    1051         219 :     return true;
    1052             : }
    1053             : 
    1054             : /************************************************************************/
    1055             : /*                  OGRJSONFGReader::ReadFeature()                      */
    1056             : /************************************************************************/
    1057             : 
    1058             : std::unique_ptr<OGRFeature>
    1059         593 : OGRJSONFGReader::ReadFeature(json_object *poObj, const char *pszRequestedLayer,
    1060             :                              OGRJSONFGMemLayer **pOutMemLayer,
    1061             :                              OGRJSONFGStreamedLayer **pOutStreamedLayer)
    1062             : {
    1063         593 :     const char *pszLayerName = GetLayerNameForFeature(poObj);
    1064         593 :     if (pszRequestedLayer && strcmp(pszLayerName, pszRequestedLayer) != 0)
    1065           3 :         return nullptr;
    1066             : 
    1067         590 :     auto oBuildContextIter = oMapBuildContext_.find(pszLayerName);
    1068         590 :     CPLAssert(oBuildContextIter != oMapBuildContext_.end());
    1069         590 :     auto &oBuildContext = oBuildContextIter->second;
    1070         590 :     OGRLayer *poLayer =
    1071         590 :         oBuildContext.poStreamedLayer
    1072         590 :             ? static_cast<OGRLayer *>(oBuildContext.poStreamedLayer)
    1073             :             : static_cast<OGRLayer *>(oBuildContext.poMemLayer);
    1074             : 
    1075         590 :     if (pOutMemLayer)
    1076          52 :         *pOutMemLayer = oBuildContext.poMemLayer;
    1077         538 :     else if (pOutStreamedLayer)
    1078         538 :         *pOutStreamedLayer = oBuildContext.poStreamedLayer;
    1079             : 
    1080         590 :     OGRFeatureDefn *poFDefn = poLayer->GetLayerDefn();
    1081        1180 :     auto poFeature = std::make_unique<OGRFeature>(poFDefn);
    1082             : 
    1083             :     /* -------------------------------------------------------------------- */
    1084             :     /*      Translate GeoJSON "properties" object to feature attributes.    */
    1085             :     /* -------------------------------------------------------------------- */
    1086             : 
    1087         590 :     json_object *poObjProps = CPL_json_object_object_get(poObj, "properties");
    1088        1177 :     if (nullptr != poObjProps &&
    1089         587 :         json_object_get_type(poObjProps) == json_type_object)
    1090             :     {
    1091             :         json_object_iter it;
    1092         587 :         it.key = nullptr;
    1093         587 :         it.val = nullptr;
    1094         587 :         it.entry = nullptr;
    1095        2091 :         json_object_object_foreachC(poObjProps, it)
    1096             :         {
    1097        1504 :             const int nField = poFDefn->GetFieldIndexCaseSensitive(it.key);
    1098        1504 :             if (nField < 0 &&
    1099           0 :                 !(bFlattenNestedAttributes_ && it.val != nullptr &&
    1100           0 :                   json_object_get_type(it.val) == json_type_object))
    1101             :             {
    1102           0 :                 CPLDebug("JSONFG", "Cannot find field %s", it.key);
    1103             :             }
    1104             :             else
    1105             :             {
    1106        1504 :                 OGRGeoJSONReaderSetField(
    1107        1504 :                     poLayer, poFeature.get(), nField, it.key, it.val,
    1108        1504 :                     bFlattenNestedAttributes_, chNestedAttributeSeparator_);
    1109             :             }
    1110             :         }
    1111             :     }
    1112             : 
    1113             :     /* -------------------------------------------------------------------- */
    1114             :     /*      Try to use feature-level ID if available                        */
    1115             :     /*      and of integral type. Otherwise, leave unset (-1) then index    */
    1116             :     /*      in features sequence will be used as FID.                       */
    1117             :     /* -------------------------------------------------------------------- */
    1118         590 :     json_object *poObjId = CPL_json_object_object_get(poObj, "id");
    1119         590 :     if (nullptr != poObjId && oBuildContext.bFeatureLevelIdAsFID)
    1120             :     {
    1121          32 :         poFeature->SetFID(static_cast<GIntBig>(json_object_get_int64(poObjId)));
    1122             :     }
    1123             : 
    1124             :     /* -------------------------------------------------------------------- */
    1125             :     /*      Handle the case where the special id is in a regular field.     */
    1126             :     /* -------------------------------------------------------------------- */
    1127         558 :     else if (nullptr != poObjId)
    1128             :     {
    1129           2 :         const int nIdx = poFDefn->GetFieldIndexCaseSensitive("id");
    1130           2 :         if (nIdx >= 0 && !poFeature->IsFieldSet(nIdx))
    1131             :         {
    1132           2 :             poFeature->SetField(nIdx, json_object_get_string(poObjId));
    1133             :         }
    1134             :     }
    1135             : 
    1136             :     /* -------------------------------------------------------------------- */
    1137             :     /*      Deal with time                                                  */
    1138             :     /* -------------------------------------------------------------------- */
    1139         590 :     json_object *poTime = CPL_json_object_object_get(poObj, "time");
    1140         590 :     if (poTime)
    1141             :     {
    1142          15 :         json_object *poDate = CPL_json_object_object_get(poTime, "date");
    1143          15 :         if (poDate && json_object_get_type(poDate) == json_type_string)
    1144             :         {
    1145           3 :             poFeature->SetField(oBuildContext.nIdxFieldTime,
    1146             :                                 json_object_get_string(poDate));
    1147             :         }
    1148             : 
    1149             :         json_object *poTimestamp =
    1150          15 :             CPL_json_object_object_get(poTime, "timestamp");
    1151          17 :         if (poTimestamp &&
    1152           2 :             json_object_get_type(poTimestamp) == json_type_string)
    1153             :         {
    1154           2 :             poFeature->SetField(oBuildContext.nIdxFieldTime,
    1155             :                                 json_object_get_string(poTimestamp));
    1156             :         }
    1157             : 
    1158             :         json_object *poInterval =
    1159          15 :             CPL_json_object_object_get(poTime, "interval");
    1160          25 :         if (poInterval && json_object_get_type(poInterval) == json_type_array &&
    1161          10 :             json_object_array_length(poInterval) == 2)
    1162             :         {
    1163          10 :             json_object *poStart = json_object_array_get_idx(poInterval, 0);
    1164          10 :             if (poStart && json_object_get_type(poStart) == json_type_string)
    1165             :             {
    1166          10 :                 const char *pszStart = json_object_get_string(poStart);
    1167          10 :                 if (strcmp(pszStart, "..") != 0)
    1168           8 :                     poFeature->SetField(oBuildContext.nIdxFieldTimeStart,
    1169             :                                         pszStart);
    1170             :             }
    1171             : 
    1172          10 :             json_object *poEnd = json_object_array_get_idx(poInterval, 1);
    1173          10 :             if (poEnd && json_object_get_type(poEnd) == json_type_string)
    1174             :             {
    1175          10 :                 const char *pszEnd = json_object_get_string(poEnd);
    1176          10 :                 if (strcmp(pszEnd, "..") != 0)
    1177           6 :                     poFeature->SetField(oBuildContext.nIdxFieldTimeEnd, pszEnd);
    1178             :             }
    1179             :         }
    1180             :     }
    1181             : 
    1182             :     /* -------------------------------------------------------------------- */
    1183             :     /*      Translate "place" (and fallback to "geometry") sub-object       */
    1184             :     /* -------------------------------------------------------------------- */
    1185         590 :     json_object *poPlace = nullptr;
    1186         590 :     bool bFallbackToGeometry = (eGeometryElement_ != GeometryElement::PLACE);
    1187             : 
    1188         590 :     if (eGeometryElement_ != GeometryElement::GEOMETRY)
    1189             :     {
    1190         587 :         poPlace = CPL_json_object_object_get(poObj, "place");
    1191             :     }
    1192         590 :     if (poPlace && json_object_get_type(poPlace) == json_type_object)
    1193             :     {
    1194         532 :         json_object *poCoordRefSys = nullptr;
    1195         532 :         if (!oBuildContext.poCRSAtFeatureLevel)
    1196             :         {
    1197         502 :             poCoordRefSys = CPL_json_object_object_get(poPlace, "coordRefSys");
    1198         502 :             if (!poCoordRefSys)
    1199             :             {
    1200             :                 poCoordRefSys =
    1201         502 :                     CPL_json_object_object_get(poObj, "coordRefSys");
    1202             :             }
    1203             :         }
    1204             : 
    1205         532 :         std::unique_ptr<OGRGeometry> poGeometry;
    1206         532 :         json_object *poObjType = CPL_json_object_object_get(poPlace, "type");
    1207         532 :         const char *pszType = json_object_get_string(poObjType);
    1208         532 :         if (pszType && (strcmp(pszType, "Polyhedron") == 0 ||
    1209         530 :                         strcmp(pszType, "Prism") == 0))
    1210             :         {
    1211           5 :             poGeometry =
    1212           5 :                 OGRJSONFGCreateNonGeoJSONGeometry(poPlace, /* bWarn=*/false);
    1213             :         }
    1214             :         else
    1215             :         {
    1216         527 :             poGeometry.reset(OGRGeoJSONReadGeometry(poPlace, nullptr));
    1217             :         }
    1218         532 :         if (poGeometry)
    1219         532 :             bFallbackToGeometry = false;
    1220             : 
    1221         532 :         auto poLayerSRS = poLayer->GetSpatialRef();
    1222         532 :         if (!poGeometry)
    1223             :         {
    1224             :             // nothing to do
    1225             :         }
    1226         532 :         else if (poCoordRefSys)
    1227             :         {
    1228           8 :             auto poFeatureCRS = OGRJSONFGReadCoordRefSys(poCoordRefSys);
    1229           4 :             if (poFeatureCRS)
    1230             :             {
    1231           4 :                 poFeatureCRS->SetAxisMappingStrategy(
    1232             :                     OAMS_TRADITIONAL_GIS_ORDER);
    1233             :                 const bool bFeatureCRSNeedSwapXY =
    1234           4 :                     OGRJSONFGMustSwapXY(poFeatureCRS.get());
    1235           4 :                 if (poLayerSRS)
    1236             :                 {
    1237             :                     // Both feature and layer-level CRS. Reproject if needed
    1238           2 :                     if (!poFeatureCRS->IsSame(poLayerSRS))
    1239             :                     {
    1240             :                         auto poCT =
    1241             :                             std::unique_ptr<OGRCoordinateTransformation>(
    1242             :                                 OGRCreateCoordinateTransformation(
    1243           4 :                                     poFeatureCRS.get(), poLayerSRS));
    1244           2 :                         if (poCT)
    1245             :                         {
    1246           2 :                             if (bFeatureCRSNeedSwapXY)
    1247           1 :                                 poGeometry->swapXY();
    1248           2 :                             if (poGeometry->transform(poCT.get()) ==
    1249             :                                 OGRERR_NONE)
    1250             :                             {
    1251           2 :                                 poGeometry->assignSpatialReference(poLayerSRS);
    1252           2 :                                 poFeature->SetGeometryDirectly(
    1253             :                                     poGeometry.release());
    1254             :                             }
    1255             :                         }
    1256             :                     }
    1257             :                     else
    1258             :                     {
    1259           0 :                         poGeometry->assignSpatialReference(poLayerSRS);
    1260           0 :                         if (oBuildContext.bSwapPlacesXY)
    1261           0 :                             poGeometry->swapXY();
    1262           0 :                         poFeature->SetGeometryDirectly(poGeometry.release());
    1263             :                     }
    1264             :                 }
    1265             :                 else
    1266             :                 {
    1267             :                     // No layer-level CRS
    1268           2 :                     auto poFeatureCRSBorrowed = poFeatureCRS.release();
    1269           2 :                     poGeometry->assignSpatialReference(poFeatureCRSBorrowed);
    1270           2 :                     poFeatureCRSBorrowed->Release();
    1271           2 :                     if (bFeatureCRSNeedSwapXY)
    1272           1 :                         poGeometry->swapXY();
    1273           2 :                     poFeature->SetGeometryDirectly(poGeometry.release());
    1274             :                 }
    1275             :             }
    1276             :         }
    1277             :         else
    1278             :         {
    1279         528 :             poGeometry->assignSpatialReference(poLayerSRS);
    1280         528 :             if (oBuildContext.bSwapPlacesXY)
    1281           5 :                 poGeometry->swapXY();
    1282         528 :             poFeature->SetGeometryDirectly(poGeometry.release());
    1283             :         }
    1284             :     }
    1285             : 
    1286         692 :     if (bFallbackToGeometry &&
    1287         102 :         (oBuildContext.poCTWGS84ToLayerCRS || oBuildContext.bLayerCRSIsWGS84))
    1288             :     {
    1289          35 :         json_object *poGeomObj = CPL_json_object_object_get(poObj, "geometry");
    1290          35 :         if (nullptr != poGeomObj)
    1291             :         {
    1292             :             auto poGeometry = std::unique_ptr<OGRGeometry>(
    1293          18 :                 OGRGeoJSONReadGeometry(poGeomObj, nullptr));
    1294           9 :             if (poGeometry)
    1295             :             {
    1296           9 :                 if (oBuildContext.poCTWGS84ToLayerCRS)
    1297             :                 {
    1298           2 :                     if (poGeometry->transform(
    1299           2 :                             oBuildContext.poCTWGS84ToLayerCRS.get()) ==
    1300             :                         OGRERR_NONE)
    1301             :                     {
    1302           1 :                         poGeometry->assignSpatialReference(
    1303           1 :                             poLayer->GetSpatialRef());
    1304           1 :                         poFeature->SetGeometryDirectly(poGeometry.release());
    1305             :                     }
    1306             :                 }
    1307             :                 else /* if (oBuildContext.bLayerCRSIsWGS84) */
    1308             :                 {
    1309           8 :                     poGeometry->assignSpatialReference(
    1310           8 :                         poLayer->GetSpatialRef());
    1311           8 :                     poFeature->SetGeometryDirectly(poGeometry.release());
    1312             :                 }
    1313             :             }
    1314             :         }
    1315             :     }
    1316             : 
    1317         590 :     return poFeature;
    1318             : }

Generated by: LCOV version 1.14