LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/jsonfg - ogrjsonfgreader.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 573 637 90.0 %
Date: 2024-05-15 17:37:59 Functions: 12 12 100.0 %

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

Generated by: LCOV version 1.14