LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/geojson - ogrgeojsonreader.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1151 1238 93.0 %
Date: 2025-01-18 12:42:00 Functions: 44 46 95.7 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implementation of OGRGeoJSONReader class (OGR GeoJSON Driver).
       5             :  * Author:   Mateusz Loskot, mateusz@loskot.net
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2007, Mateusz Loskot
       9             :  * Copyright (c) 2008-2017, Even Rouault <even dot rouault at spatialys dot com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "ogrgeojsonreader.h"
      15             : #include "ogrgeojsonutils.h"
      16             : #include "ogrgeojsongeometry.h"
      17             : #include "ogr_geojson.h"
      18             : #include "ogrlibjsonutils.h"
      19             : #include "ogrjsoncollectionstreamingparser.h"
      20             : #include "ogr_api.h"
      21             : 
      22             : #include <cmath>
      23             : #include <limits>
      24             : #include <set>
      25             : #include <functional>
      26             : 
      27             : /************************************************************************/
      28             : /*                      OGRGeoJSONReaderStreamingParser                 */
      29             : /************************************************************************/
      30             : 
      31             : class OGRGeoJSONReaderStreamingParser final
      32             :     : public OGRJSONCollectionStreamingParser
      33             : {
      34             :     OGRGeoJSONReader &m_oReader;
      35             :     OGRGeoJSONLayer *m_poLayer = nullptr;
      36             : 
      37             :     std::vector<OGRFeature *> m_apoFeatures{};
      38             :     size_t m_nCurFeatureIdx = 0;
      39             :     bool m_bOriginalIdModifiedEmitted = false;
      40             :     std::set<GIntBig> m_oSetUsedFIDs{};
      41             : 
      42             :     std::map<std::string, int> m_oMapFieldNameToIdx{};
      43             :     std::vector<std::unique_ptr<OGRFieldDefn>> m_apoFieldDefn{};
      44             :     gdal::DirectedAcyclicGraph<int, std::string> m_dag{};
      45             : 
      46             :     void AnalyzeFeature();
      47             : 
      48             :     CPL_DISALLOW_COPY_ASSIGN(OGRGeoJSONReaderStreamingParser)
      49             : 
      50             :   protected:
      51             :     void GotFeature(json_object *poObj, bool bFirstPass,
      52             :                     const std::string &osJson) override;
      53             :     void TooComplex() override;
      54             : 
      55             :   public:
      56             :     OGRGeoJSONReaderStreamingParser(OGRGeoJSONReader &oReader,
      57             :                                     OGRGeoJSONLayer *poLayer, bool bFirstPass,
      58             :                                     bool bStoreNativeData);
      59             :     ~OGRGeoJSONReaderStreamingParser();
      60             : 
      61             :     void FinalizeLayerDefn();
      62             : 
      63             :     OGRFeature *GetNextFeature();
      64             : 
      65         127 :     inline bool GetOriginalIdModifiedEmitted() const
      66             :     {
      67         127 :         return m_bOriginalIdModifiedEmitted;
      68             :     }
      69             : 
      70         304 :     inline void SetOriginalIdModifiedEmitted(bool b)
      71             :     {
      72         304 :         m_bOriginalIdModifiedEmitted = b;
      73         304 :     }
      74             : };
      75             : 
      76             : /************************************************************************/
      77             : /*                        OGRGeoJSONBaseReader()                        */
      78             : /************************************************************************/
      79             : 
      80             : OGRGeoJSONBaseReader::OGRGeoJSONBaseReader() = default;
      81             : 
      82             : /************************************************************************/
      83             : /*                           SetPreserveGeometryType                    */
      84             : /************************************************************************/
      85             : 
      86           0 : void OGRGeoJSONBaseReader::SetPreserveGeometryType(bool bPreserve)
      87             : {
      88           0 :     bGeometryPreserve_ = bPreserve;
      89           0 : }
      90             : 
      91             : /************************************************************************/
      92             : /*                           SetSkipAttributes                          */
      93             : /************************************************************************/
      94             : 
      95           0 : void OGRGeoJSONBaseReader::SetSkipAttributes(bool bSkip)
      96             : {
      97           0 :     bAttributesSkip_ = bSkip;
      98           0 : }
      99             : 
     100             : /************************************************************************/
     101             : /*                         SetFlattenNestedAttributes                   */
     102             : /************************************************************************/
     103             : 
     104         456 : void OGRGeoJSONBaseReader::SetFlattenNestedAttributes(bool bFlatten,
     105             :                                                       char chSeparator)
     106             : {
     107         456 :     bFlattenNestedAttributes_ = bFlatten;
     108         456 :     chNestedAttributeSeparator_ = chSeparator;
     109         456 : }
     110             : 
     111             : /************************************************************************/
     112             : /*                           SetStoreNativeData                         */
     113             : /************************************************************************/
     114             : 
     115         456 : void OGRGeoJSONBaseReader::SetStoreNativeData(bool bStoreNativeData)
     116             : {
     117         456 :     bStoreNativeData_ = bStoreNativeData;
     118         456 : }
     119             : 
     120             : /************************************************************************/
     121             : /*                           SetArrayAsString                           */
     122             : /************************************************************************/
     123             : 
     124         456 : void OGRGeoJSONBaseReader::SetArrayAsString(bool bArrayAsString)
     125             : {
     126         456 :     bArrayAsString_ = bArrayAsString;
     127         456 : }
     128             : 
     129             : /************************************************************************/
     130             : /*                           SetDateAsString                           */
     131             : /************************************************************************/
     132             : 
     133         456 : void OGRGeoJSONBaseReader::SetDateAsString(bool bDateAsString)
     134             : {
     135         456 :     bDateAsString_ = bDateAsString;
     136         456 : }
     137             : 
     138             : /************************************************************************/
     139             : /*                           OGRGeoJSONReader                           */
     140             : /************************************************************************/
     141             : 
     142         456 : OGRGeoJSONReader::OGRGeoJSONReader()
     143             :     : poGJObject_(nullptr), poStreamingParser_(nullptr), bFirstSeg_(false),
     144             :       bJSonPLikeWrapper_(false), fp_(nullptr), bCanEasilyAppend_(false),
     145             :       bFCHasBBOX_(false), nBufferSize_(0), pabyBuffer_(nullptr),
     146         456 :       nTotalFeatureCount_(0), nTotalOGRFeatureMemEstimate_(0)
     147             : {
     148         456 : }
     149             : 
     150             : /************************************************************************/
     151             : /*                          ~OGRGeoJSONReader                           */
     152             : /************************************************************************/
     153             : 
     154         456 : OGRGeoJSONReader::~OGRGeoJSONReader()
     155             : {
     156         456 :     if (nullptr != poGJObject_)
     157             :     {
     158         453 :         json_object_put(poGJObject_);
     159             :     }
     160         456 :     if (fp_ != nullptr)
     161             :     {
     162         278 :         VSIFCloseL(fp_);
     163             :     }
     164         456 :     delete poStreamingParser_;
     165         456 :     CPLFree(pabyBuffer_);
     166             : 
     167         456 :     poGJObject_ = nullptr;
     168         456 : }
     169             : 
     170             : /************************************************************************/
     171             : /*                           Parse                                      */
     172             : /************************************************************************/
     173             : 
     174         175 : OGRErr OGRGeoJSONReader::Parse(const char *pszText)
     175             : {
     176         175 :     if (nullptr != pszText)
     177             :     {
     178             :         // Skip UTF-8 BOM (#5630).
     179         175 :         const GByte *pabyData = (const GByte *)pszText;
     180         175 :         if (pabyData[0] == 0xEF && pabyData[1] == 0xBB && pabyData[2] == 0xBF)
     181             :         {
     182           1 :             CPLDebug("GeoJSON", "Skip UTF-8 BOM");
     183           1 :             pszText += 3;
     184             :         }
     185             : 
     186         175 :         if (poGJObject_ != nullptr)
     187             :         {
     188           0 :             json_object_put(poGJObject_);
     189           0 :             poGJObject_ = nullptr;
     190             :         }
     191             : 
     192             :         // JSON tree is shared for while lifetime of the reader object
     193             :         // and will be released in the destructor.
     194         175 :         if (!OGRJSonParse(pszText, &poGJObject_))
     195           0 :             return OGRERR_CORRUPT_DATA;
     196             :     }
     197             : 
     198         175 :     return OGRERR_NONE;
     199             : }
     200             : 
     201             : /************************************************************************/
     202             : /*                           ReadLayers                                 */
     203             : /************************************************************************/
     204             : 
     205         175 : void OGRGeoJSONReader::ReadLayers(OGRGeoJSONDataSource *poDS)
     206             : {
     207         175 :     if (nullptr == poGJObject_)
     208             :     {
     209           0 :         CPLDebug("GeoJSON",
     210             :                  "Missing parsed GeoJSON data. Forgot to call Parse()?");
     211           0 :         return;
     212             :     }
     213             : 
     214         175 :     ReadLayer(poDS, nullptr, poGJObject_);
     215             : }
     216             : 
     217             : /************************************************************************/
     218             : /*           OGRGeoJSONReaderStreamingParserGetMaxObjectSize()          */
     219             : /************************************************************************/
     220             : 
     221         585 : static size_t OGRGeoJSONReaderStreamingParserGetMaxObjectSize()
     222             : {
     223             :     const double dfTmp =
     224         585 :         CPLAtof(CPLGetConfigOption("OGR_GEOJSON_MAX_OBJ_SIZE", "200"));
     225         585 :     return dfTmp > 0 ? static_cast<size_t>(dfTmp * 1024 * 1024) : 0;
     226             : }
     227             : 
     228             : /************************************************************************/
     229             : /*                     OGRGeoJSONReaderStreamingParser()                */
     230             : /************************************************************************/
     231             : 
     232         585 : OGRGeoJSONReaderStreamingParser::OGRGeoJSONReaderStreamingParser(
     233             :     OGRGeoJSONReader &oReader, OGRGeoJSONLayer *poLayer, bool bFirstPass,
     234         585 :     bool bStoreNativeData)
     235             :     : OGRJSONCollectionStreamingParser(
     236             :           bFirstPass, bStoreNativeData,
     237             :           OGRGeoJSONReaderStreamingParserGetMaxObjectSize()),
     238         585 :       m_oReader(oReader), m_poLayer(poLayer)
     239             : {
     240         585 : }
     241             : 
     242             : /************************************************************************/
     243             : /*                   ~OGRGeoJSONReaderStreamingParser()                 */
     244             : /************************************************************************/
     245             : 
     246         884 : OGRGeoJSONReaderStreamingParser::~OGRGeoJSONReaderStreamingParser()
     247             : {
     248         952 :     for (size_t i = 0; i < m_apoFeatures.size(); i++)
     249         367 :         delete m_apoFeatures[i];
     250         884 : }
     251             : 
     252             : /************************************************************************/
     253             : /*                          GetNextFeature()                            */
     254             : /************************************************************************/
     255             : 
     256        1849 : OGRFeature *OGRGeoJSONReaderStreamingParser::GetNextFeature()
     257             : {
     258        1849 :     if (m_nCurFeatureIdx < m_apoFeatures.size())
     259             :     {
     260        1106 :         OGRFeature *poFeat = m_apoFeatures[m_nCurFeatureIdx];
     261        1106 :         m_apoFeatures[m_nCurFeatureIdx] = nullptr;
     262        1106 :         m_nCurFeatureIdx++;
     263        1106 :         return poFeat;
     264             :     }
     265         743 :     m_nCurFeatureIdx = 0;
     266         743 :     m_apoFeatures.clear();
     267         743 :     return nullptr;
     268             : }
     269             : 
     270             : /************************************************************************/
     271             : /*                          GotFeature()                                */
     272             : /************************************************************************/
     273             : 
     274        5714 : void OGRGeoJSONReaderStreamingParser::GotFeature(json_object *poObj,
     275             :                                                  bool bFirstPass,
     276             :                                                  const std::string &osJson)
     277             : {
     278        5714 :     if (bFirstPass)
     279             :     {
     280        4425 :         if (!m_oReader.GenerateFeatureDefn(m_oMapFieldNameToIdx, m_apoFieldDefn,
     281        4425 :                                            m_dag, m_poLayer, poObj))
     282             :         {
     283             :         }
     284        4425 :         m_poLayer->IncFeatureCount();
     285             :     }
     286             :     else
     287             :     {
     288             :         OGRFeature *poFeat =
     289        1289 :             m_oReader.ReadFeature(m_poLayer, poObj, osJson.c_str());
     290        1289 :         if (poFeat)
     291             :         {
     292        1289 :             GIntBig nFID = poFeat->GetFID();
     293        1289 :             if (nFID == OGRNullFID)
     294             :             {
     295        1115 :                 nFID = static_cast<GIntBig>(m_oSetUsedFIDs.size());
     296        1115 :                 while (cpl::contains(m_oSetUsedFIDs, nFID))
     297             :                 {
     298           0 :                     ++nFID;
     299             :                 }
     300             :             }
     301         174 :             else if (cpl::contains(m_oSetUsedFIDs, nFID))
     302             :             {
     303          33 :                 if (!m_bOriginalIdModifiedEmitted)
     304             :                 {
     305           2 :                     CPLError(CE_Warning, CPLE_AppDefined,
     306             :                              "Several features with id = " CPL_FRMT_GIB " have "
     307             :                              "been found. Altering it to be unique. "
     308             :                              "This warning will not be emitted anymore for "
     309             :                              "this layer",
     310             :                              nFID);
     311           2 :                     m_bOriginalIdModifiedEmitted = true;
     312             :                 }
     313          33 :                 nFID = static_cast<GIntBig>(m_oSetUsedFIDs.size());
     314          33 :                 while (cpl::contains(m_oSetUsedFIDs, nFID))
     315             :                 {
     316           0 :                     ++nFID;
     317             :                 }
     318             :             }
     319        1289 :             m_oSetUsedFIDs.insert(nFID);
     320        1289 :             poFeat->SetFID(nFID);
     321             : 
     322        1289 :             m_apoFeatures.push_back(poFeat);
     323             :         }
     324             :     }
     325        5714 : }
     326             : 
     327             : /************************************************************************/
     328             : /*                         FinalizeLayerDefn()                          */
     329             : /************************************************************************/
     330             : 
     331         278 : void OGRGeoJSONReaderStreamingParser::FinalizeLayerDefn()
     332             : {
     333         278 :     OGRFeatureDefn *poDefn = m_poLayer->GetLayerDefn();
     334         556 :     auto oTemporaryUnsealer(poDefn->GetTemporaryUnsealer());
     335         556 :     const auto sortedFields = m_dag.getTopologicalOrdering();
     336         278 :     CPLAssert(sortedFields.size() == m_apoFieldDefn.size());
     337        1102 :     for (int idx : sortedFields)
     338             :     {
     339         824 :         poDefn->AddFieldDefn(m_apoFieldDefn[idx].get());
     340             :     }
     341         278 :     m_dag = gdal::DirectedAcyclicGraph<int, std::string>();
     342         278 :     m_oMapFieldNameToIdx.clear();
     343         278 :     m_apoFieldDefn.clear();
     344         278 : }
     345             : 
     346             : /************************************************************************/
     347             : /*                            TooComplex()                              */
     348             : /************************************************************************/
     349             : 
     350           1 : void OGRGeoJSONReaderStreamingParser::TooComplex()
     351             : {
     352           1 :     if (!ExceptionOccurred())
     353           1 :         EmitException("GeoJSON object too complex/large. You may define the "
     354             :                       "OGR_GEOJSON_MAX_OBJ_SIZE configuration option to "
     355             :                       "a value in megabytes to allow "
     356             :                       "for larger features, or 0 to remove any size limit.");
     357           1 : }
     358             : 
     359             : /************************************************************************/
     360             : /*                       SetCoordinatePrecision()                       */
     361             : /************************************************************************/
     362             : 
     363         374 : static void SetCoordinatePrecision(json_object *poRootObj,
     364             :                                    OGRGeoJSONLayer *poLayer)
     365             : {
     366         374 :     if (poLayer->GetLayerDefn()->GetGeomType() != wkbNone)
     367             :     {
     368         748 :         OGRGeoJSONWriteOptions options;
     369             : 
     370             :         json_object *poXYRes =
     371         374 :             CPL_json_object_object_get(poRootObj, "xy_coordinate_resolution");
     372         374 :         if (poXYRes && (json_object_get_type(poXYRes) == json_type_double ||
     373           0 :                         json_object_get_type(poXYRes) == json_type_int))
     374             :         {
     375           8 :             auto poGeomFieldDefn = poLayer->GetLayerDefn()->GetGeomFieldDefn(0);
     376             :             OGRGeomCoordinatePrecision oCoordPrec(
     377           8 :                 poGeomFieldDefn->GetCoordinatePrecision());
     378           8 :             oCoordPrec.dfXYResolution = json_object_get_double(poXYRes);
     379           8 :             whileUnsealing(poGeomFieldDefn)->SetCoordinatePrecision(oCoordPrec);
     380             : 
     381           8 :             options.nXYCoordPrecision =
     382           8 :                 OGRGeomCoordinatePrecision::ResolutionToPrecision(
     383             :                     oCoordPrec.dfXYResolution);
     384             :         }
     385             : 
     386             :         json_object *poZRes =
     387         374 :             CPL_json_object_object_get(poRootObj, "z_coordinate_resolution");
     388         374 :         if (poZRes && (json_object_get_type(poZRes) == json_type_double ||
     389           0 :                        json_object_get_type(poZRes) == json_type_int))
     390             :         {
     391           6 :             auto poGeomFieldDefn = poLayer->GetLayerDefn()->GetGeomFieldDefn(0);
     392             :             OGRGeomCoordinatePrecision oCoordPrec(
     393           6 :                 poGeomFieldDefn->GetCoordinatePrecision());
     394           6 :             oCoordPrec.dfZResolution = json_object_get_double(poZRes);
     395           6 :             whileUnsealing(poGeomFieldDefn)->SetCoordinatePrecision(oCoordPrec);
     396             : 
     397           6 :             options.nZCoordPrecision =
     398           6 :                 OGRGeomCoordinatePrecision::ResolutionToPrecision(
     399             :                     oCoordPrec.dfZResolution);
     400             :         }
     401             : 
     402         374 :         poLayer->SetWriteOptions(options);
     403             :     }
     404         374 : }
     405             : 
     406             : /************************************************************************/
     407             : /*                       FirstPassReadLayer()                           */
     408             : /************************************************************************/
     409             : 
     410         281 : bool OGRGeoJSONReader::FirstPassReadLayer(OGRGeoJSONDataSource *poDS,
     411             :                                           VSILFILE *fp,
     412             :                                           bool &bTryStandardReading)
     413             : {
     414         281 :     bTryStandardReading = false;
     415         281 :     VSIFSeekL(fp, 0, SEEK_SET);
     416         281 :     bFirstSeg_ = true;
     417             : 
     418         562 :     std::string osName = poDS->GetDescription();
     419         281 :     if (STARTS_WITH_CI(osName.c_str(), "GeoJSON:"))
     420           0 :         osName = osName.substr(strlen("GeoJSON:"));
     421         281 :     osName = CPLGetBasenameSafe(osName.c_str());
     422         281 :     osName = OGRGeoJSONLayer::GetValidLayerName(osName.c_str());
     423             : 
     424             :     OGRGeoJSONLayer *poLayer =
     425         281 :         new OGRGeoJSONLayer(osName.c_str(), nullptr,
     426         281 :                             OGRGeoJSONLayer::DefaultGeometryType, poDS, this);
     427             :     OGRGeoJSONReaderStreamingParser oParser(*this, poLayer, true,
     428         562 :                                             bStoreNativeData_);
     429             : 
     430         281 :     vsi_l_offset nFileSize = 0;
     431         369 :     if (STARTS_WITH(poDS->GetDescription(), "/vsimem/") ||
     432          88 :         !STARTS_WITH(poDS->GetDescription(), "/vsi"))
     433             :     {
     434             :         VSIStatBufL sStatBuf;
     435         275 :         if (VSIStatL(poDS->GetDescription(), &sStatBuf) == 0)
     436             :         {
     437         275 :             nFileSize = sStatBuf.st_size;
     438             :         }
     439             :     }
     440             : 
     441         281 :     nBufferSize_ = 4096 * 10;
     442         281 :     pabyBuffer_ = static_cast<GByte *>(CPLMalloc(nBufferSize_));
     443         281 :     int nIter = 0;
     444         281 :     bool bThresholdReached = false;
     445         281 :     const GIntBig nMaxBytesFirstPass = CPLAtoGIntBig(
     446             :         CPLGetConfigOption("OGR_GEOJSON_MAX_BYTES_FIRST_PASS", "0"));
     447         281 :     const GIntBig nLimitFeaturesFirstPass = CPLAtoGIntBig(
     448             :         CPLGetConfigOption("OGR_GEOJSON_MAX_FEATURES_FIRST_PASS", "0"));
     449             :     while (true)
     450             :     {
     451         426 :         nIter++;
     452             : 
     453         426 :         if (nMaxBytesFirstPass > 0 &&
     454           0 :             static_cast<GIntBig>(nIter) * static_cast<GIntBig>(nBufferSize_) >=
     455             :                 nMaxBytesFirstPass)
     456             :         {
     457           0 :             CPLDebug("GeoJSON", "First pass: early exit since above "
     458             :                                 "OGR_GEOJSON_MAX_BYTES_FIRST_PASS");
     459           0 :             bThresholdReached = true;
     460           0 :             break;
     461             :         }
     462             : 
     463         426 :         size_t nRead = VSIFReadL(pabyBuffer_, 1, nBufferSize_, fp);
     464         426 :         const bool bFinished = nRead < nBufferSize_;
     465         426 :         size_t nSkip = 0;
     466         426 :         if (bFirstSeg_)
     467             :         {
     468         281 :             bFirstSeg_ = false;
     469         281 :             nSkip = SkipPrologEpilogAndUpdateJSonPLikeWrapper(nRead);
     470             :         }
     471         426 :         if (bFinished && bJSonPLikeWrapper_ && nRead > nSkip)
     472           1 :             nRead--;
     473         426 :         if (!oParser.Parse(reinterpret_cast<const char *>(pabyBuffer_ + nSkip),
     474         850 :                            nRead - nSkip, bFinished) ||
     475         424 :             oParser.ExceptionOccurred())
     476             :         {
     477             :             // to avoid killing ourselves during layer deletion
     478           2 :             poLayer->UnsetReader();
     479           2 :             delete poLayer;
     480           2 :             return false;
     481             :         }
     482         424 :         if (bFinished || (nIter % 100) == 0)
     483             :         {
     484         279 :             if (nFileSize == 0)
     485             :             {
     486           6 :                 if (bFinished)
     487             :                 {
     488           6 :                     CPLDebug("GeoJSON", "First pass: 100.00 %%");
     489             :                 }
     490             :                 else
     491             :                 {
     492           0 :                     CPLDebug("GeoJSON",
     493             :                              "First pass: " CPL_FRMT_GUIB " bytes read",
     494           0 :                              static_cast<GUIntBig>(nIter) *
     495           0 :                                      static_cast<GUIntBig>(nBufferSize_) +
     496             :                                  nRead);
     497             :                 }
     498             :             }
     499             :             else
     500             :             {
     501         273 :                 CPLDebug("GeoJSON", "First pass: %.2f %%",
     502         273 :                          100.0 * VSIFTellL(fp) / nFileSize);
     503             :             }
     504             :         }
     505         424 :         if (nLimitFeaturesFirstPass > 0 &&
     506           0 :             poLayer->GetFeatureCount(FALSE) >= nLimitFeaturesFirstPass)
     507             :         {
     508           0 :             CPLDebug("GeoJSON", "First pass: early exit since above "
     509             :                                 "OGR_GEOJSON_MAX_FEATURES_FIRST_PASS");
     510           0 :             bThresholdReached = true;
     511           0 :             break;
     512             :         }
     513         424 :         if (oParser.IsTypeKnown() && !oParser.IsFeatureCollection())
     514           0 :             break;
     515         424 :         if (bFinished)
     516         279 :             break;
     517         145 :     }
     518             : 
     519         279 :     if (bThresholdReached)
     520             :     {
     521           0 :         poLayer->InvalidateFeatureCount();
     522             :     }
     523         279 :     else if (!oParser.IsTypeKnown() || !oParser.IsFeatureCollection())
     524             :     {
     525             :         // to avoid killing ourselves during layer deletion
     526           1 :         poLayer->UnsetReader();
     527           1 :         delete poLayer;
     528             :         const vsi_l_offset nRAM =
     529           1 :             static_cast<vsi_l_offset>(CPLGetUsablePhysicalRAM());
     530           1 :         if (nFileSize == 0 || nRAM == 0 || nRAM > nFileSize * 20)
     531             :         {
     532             :             // Only try full ingestion if we have 20x more RAM than the file
     533             :             // size
     534           1 :             bTryStandardReading = true;
     535             :         }
     536           1 :         return false;
     537             :     }
     538             : 
     539         278 :     oParser.FinalizeLayerDefn();
     540             : 
     541         278 :     CPLString osFIDColumn;
     542         278 :     FinalizeLayerDefn(poLayer, osFIDColumn);
     543         278 :     if (!osFIDColumn.empty())
     544          26 :         poLayer->SetFIDColumn(osFIDColumn);
     545             : 
     546         278 :     bCanEasilyAppend_ = oParser.CanEasilyAppend();
     547         278 :     nTotalFeatureCount_ = poLayer->GetFeatureCount(FALSE);
     548         278 :     nTotalOGRFeatureMemEstimate_ = oParser.GetTotalOGRFeatureMemEstimate();
     549             : 
     550         278 :     json_object *poRootObj = oParser.StealRootObject();
     551         278 :     if (poRootObj)
     552             :     {
     553         278 :         bFCHasBBOX_ = CPL_json_object_object_get(poRootObj, "bbox") != nullptr;
     554             : 
     555             :         // CPLDebug("GeoJSON", "%s", json_object_get_string(poRootObj));
     556             : 
     557         278 :         json_object *poName = CPL_json_object_object_get(poRootObj, "name");
     558         278 :         if (poName && json_object_get_type(poName) == json_type_string)
     559             :         {
     560         120 :             const char *pszValue = json_object_get_string(poName);
     561         120 :             whileUnsealing(poLayer->GetLayerDefn())->SetName(pszValue);
     562         120 :             poLayer->SetDescription(pszValue);
     563             :         }
     564             : 
     565             :         json_object *poDescription =
     566         278 :             CPL_json_object_object_get(poRootObj, "description");
     567         279 :         if (poDescription &&
     568           1 :             json_object_get_type(poDescription) == json_type_string)
     569             :         {
     570           1 :             const char *pszValue = json_object_get_string(poDescription);
     571           1 :             poLayer->SetMetadataItem("DESCRIPTION", pszValue);
     572             :         }
     573             : 
     574         278 :         OGRSpatialReference *poSRS = OGRGeoJSONReadSpatialReference(poRootObj);
     575         278 :         const auto eGeomType = poLayer->GetLayerDefn()->GetGeomType();
     576         278 :         if (eGeomType != wkbNone && poSRS == nullptr)
     577             :         {
     578             :             // If there is none defined, we use 4326 / 4979.
     579         243 :             poSRS = new OGRSpatialReference();
     580         243 :             if (OGR_GT_HasZ(eGeomType))
     581          17 :                 poSRS->importFromEPSG(4979);
     582             :             else
     583         226 :                 poSRS->SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
     584         243 :             poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     585             :         }
     586         278 :         CPLErrorReset();
     587             : 
     588         278 :         if (eGeomType != wkbNone && poSRS != nullptr)
     589             :         {
     590         278 :             auto poGeomFieldDefn = poLayer->GetLayerDefn()->GetGeomFieldDefn(0);
     591         278 :             whileUnsealing(poGeomFieldDefn)->SetSpatialRef(poSRS);
     592             :         }
     593         278 :         if (poSRS)
     594         278 :             poSRS->Release();
     595             : 
     596         278 :         SetCoordinatePrecision(poRootObj, poLayer);
     597             : 
     598         278 :         if (bStoreNativeData_)
     599             :         {
     600          58 :             CPLString osNativeData("NATIVE_DATA=");
     601          29 :             osNativeData += json_object_get_string(poRootObj);
     602             : 
     603          29 :             char *apszMetadata[3] = {
     604          29 :                 const_cast<char *>(osNativeData.c_str()),
     605             :                 const_cast<char *>(
     606             :                     "NATIVE_MEDIA_TYPE=application/vnd.geo+json"),
     607          29 :                 nullptr};
     608             : 
     609          29 :             poLayer->SetMetadata(apszMetadata, "NATIVE_DATA");
     610             :         }
     611             : 
     612         278 :         poGJObject_ = poRootObj;
     613             :     }
     614             : 
     615         278 :     fp_ = fp;
     616         278 :     poDS->AddLayer(poLayer);
     617         278 :     return true;
     618             : }
     619             : 
     620             : /************************************************************************/
     621             : /*               SkipPrologEpilogAndUpdateJSonPLikeWrapper()            */
     622             : /************************************************************************/
     623             : 
     624         585 : size_t OGRGeoJSONReader::SkipPrologEpilogAndUpdateJSonPLikeWrapper(size_t nRead)
     625             : {
     626         585 :     size_t nSkip = 0;
     627         585 :     if (nRead >= 3 && pabyBuffer_[0] == 0xEF && pabyBuffer_[1] == 0xBB &&
     628           0 :         pabyBuffer_[2] == 0xBF)
     629             :     {
     630           0 :         CPLDebug("GeoJSON", "Skip UTF-8 BOM");
     631           0 :         nSkip += 3;
     632             :     }
     633             : 
     634         585 :     const char *const apszPrefix[] = {"loadGeoJSON(", "jsonp("};
     635        1753 :     for (size_t i = 0; i < CPL_ARRAYSIZE(apszPrefix); i++)
     636             :     {
     637        1169 :         if (nRead >= nSkip + strlen(apszPrefix[i]) &&
     638        1169 :             memcmp(pabyBuffer_ + nSkip, apszPrefix[i], strlen(apszPrefix[i])) ==
     639             :                 0)
     640             :         {
     641           1 :             nSkip += strlen(apszPrefix[i]);
     642           1 :             bJSonPLikeWrapper_ = true;
     643           1 :             break;
     644             :         }
     645             :     }
     646             : 
     647         585 :     return nSkip;
     648             : }
     649             : 
     650             : /************************************************************************/
     651             : /*                            ResetReading()                            */
     652             : /************************************************************************/
     653             : 
     654         555 : void OGRGeoJSONReader::ResetReading()
     655             : {
     656         555 :     CPLAssert(fp_);
     657         555 :     if (poStreamingParser_)
     658         118 :         bOriginalIdModifiedEmitted_ =
     659         118 :             poStreamingParser_->GetOriginalIdModifiedEmitted();
     660         555 :     delete poStreamingParser_;
     661         555 :     poStreamingParser_ = nullptr;
     662         555 : }
     663             : 
     664             : /************************************************************************/
     665             : /*                           GetNextFeature()                           */
     666             : /************************************************************************/
     667             : 
     668        1275 : OGRFeature *OGRGeoJSONReader::GetNextFeature(OGRGeoJSONLayer *poLayer)
     669             : {
     670        1275 :     CPLAssert(fp_);
     671        1275 :     if (poStreamingParser_ == nullptr)
     672             :     {
     673         299 :         poStreamingParser_ = new OGRGeoJSONReaderStreamingParser(
     674         299 :             *this, poLayer, false, bStoreNativeData_);
     675         299 :         poStreamingParser_->SetOriginalIdModifiedEmitted(
     676         299 :             bOriginalIdModifiedEmitted_);
     677         299 :         VSIFSeekL(fp_, 0, SEEK_SET);
     678         299 :         bFirstSeg_ = true;
     679         299 :         bJSonPLikeWrapper_ = false;
     680             :     }
     681             : 
     682        1275 :     OGRFeature *poFeat = poStreamingParser_->GetNextFeature();
     683        1275 :     if (poFeat)
     684         719 :         return poFeat;
     685             : 
     686             :     while (true)
     687             :     {
     688         556 :         size_t nRead = VSIFReadL(pabyBuffer_, 1, nBufferSize_, fp_);
     689         556 :         const bool bFinished = nRead < nBufferSize_;
     690         556 :         size_t nSkip = 0;
     691         556 :         if (bFirstSeg_)
     692             :         {
     693         299 :             bFirstSeg_ = false;
     694         299 :             nSkip = SkipPrologEpilogAndUpdateJSonPLikeWrapper(nRead);
     695             :         }
     696         556 :         if (bFinished && bJSonPLikeWrapper_ && nRead > nSkip)
     697           0 :             nRead--;
     698        1668 :         if (!poStreamingParser_->Parse(
     699         556 :                 reinterpret_cast<const char *>(pabyBuffer_ + nSkip),
     700        1112 :                 nRead - nSkip, bFinished) ||
     701         556 :             poStreamingParser_->ExceptionOccurred())
     702             :         {
     703           0 :             break;
     704             :         }
     705             : 
     706         556 :         poFeat = poStreamingParser_->GetNextFeature();
     707         556 :         if (poFeat)
     708         369 :             return poFeat;
     709             : 
     710         187 :         if (bFinished)
     711         187 :             break;
     712           0 :     }
     713             : 
     714         187 :     return nullptr;
     715             : }
     716             : 
     717             : /************************************************************************/
     718             : /*                             GetFeature()                             */
     719             : /************************************************************************/
     720             : 
     721          22 : OGRFeature *OGRGeoJSONReader::GetFeature(OGRGeoJSONLayer *poLayer, GIntBig nFID)
     722             : {
     723          22 :     CPLAssert(fp_);
     724             : 
     725          22 :     if (oMapFIDToOffsetSize_.empty())
     726             :     {
     727           5 :         CPLDebug("GeoJSON",
     728             :                  "Establishing index to features for first GetFeature() call");
     729             : 
     730           5 :         if (poStreamingParser_)
     731           4 :             bOriginalIdModifiedEmitted_ =
     732           4 :                 poStreamingParser_->GetOriginalIdModifiedEmitted();
     733           5 :         delete poStreamingParser_;
     734           5 :         poStreamingParser_ = nullptr;
     735             : 
     736             :         OGRGeoJSONReaderStreamingParser oParser(*this, poLayer, false,
     737           5 :                                                 bStoreNativeData_);
     738           5 :         oParser.SetOriginalIdModifiedEmitted(bOriginalIdModifiedEmitted_);
     739           5 :         VSIFSeekL(fp_, 0, SEEK_SET);
     740           5 :         bFirstSeg_ = true;
     741           5 :         bJSonPLikeWrapper_ = false;
     742           5 :         vsi_l_offset nCurOffset = 0;
     743           5 :         vsi_l_offset nFeatureOffset = 0;
     744             :         while (true)
     745             :         {
     746           5 :             size_t nRead = VSIFReadL(pabyBuffer_, 1, nBufferSize_, fp_);
     747           5 :             const bool bFinished = nRead < nBufferSize_;
     748           5 :             size_t nSkip = 0;
     749           5 :             if (bFirstSeg_)
     750             :             {
     751           5 :                 bFirstSeg_ = false;
     752           5 :                 nSkip = SkipPrologEpilogAndUpdateJSonPLikeWrapper(nRead);
     753             :             }
     754           5 :             if (bFinished && bJSonPLikeWrapper_ && nRead > nSkip)
     755           0 :                 nRead--;
     756           5 :             auto pszPtr = reinterpret_cast<const char *>(pabyBuffer_ + nSkip);
     757        1673 :             for (size_t i = 0; i < nRead - nSkip; i++)
     758             :             {
     759        1668 :                 oParser.ResetFeatureDetectionState();
     760        1668 :                 if (!oParser.Parse(pszPtr + i, 1,
     761        5004 :                                    bFinished && (i + 1 == nRead - nSkip)) ||
     762        1668 :                     oParser.ExceptionOccurred())
     763             :                 {
     764           0 :                     return nullptr;
     765             :                 }
     766        1668 :                 if (oParser.IsStartFeature())
     767             :                 {
     768          18 :                     nFeatureOffset = nCurOffset + i;
     769             :                 }
     770        1650 :                 else if (oParser.IsEndFeature())
     771             :                 {
     772          18 :                     vsi_l_offset nFeatureSize =
     773          18 :                         (nCurOffset + i) - nFeatureOffset + 1;
     774          18 :                     auto poFeat = oParser.GetNextFeature();
     775          18 :                     if (poFeat)
     776             :                     {
     777          18 :                         const GIntBig nThisFID = poFeat->GetFID();
     778          18 :                         if (!cpl::contains(oMapFIDToOffsetSize_, nThisFID))
     779             :                         {
     780          18 :                             oMapFIDToOffsetSize_[nThisFID] =
     781          36 :                                 std::pair<vsi_l_offset, vsi_l_offset>(
     782          18 :                                     nFeatureOffset, nFeatureSize);
     783             :                         }
     784          18 :                         delete poFeat;
     785             :                     }
     786             :                 }
     787             :             }
     788             : 
     789           5 :             if (bFinished)
     790           5 :                 break;
     791           0 :             nCurOffset += nRead;
     792           0 :         }
     793             : 
     794           5 :         bOriginalIdModifiedEmitted_ = oParser.GetOriginalIdModifiedEmitted();
     795             :     }
     796             : 
     797          22 :     const auto oIter = oMapFIDToOffsetSize_.find(nFID);
     798          22 :     if (oIter == oMapFIDToOffsetSize_.end())
     799             :     {
     800           5 :         return nullptr;
     801             :     }
     802             : 
     803          17 :     VSIFSeekL(fp_, oIter->second.first, SEEK_SET);
     804          17 :     if (oIter->second.second > 1000 * 1000 * 1000)
     805             :     {
     806           0 :         return nullptr;
     807             :     }
     808          17 :     size_t nSize = static_cast<size_t>(oIter->second.second);
     809          17 :     char *pszBuffer = static_cast<char *>(VSIMalloc(nSize + 1));
     810          17 :     if (!pszBuffer)
     811             :     {
     812           0 :         return nullptr;
     813             :     }
     814          17 :     if (VSIFReadL(pszBuffer, 1, nSize, fp_) != nSize)
     815             :     {
     816           0 :         VSIFree(pszBuffer);
     817           0 :         return nullptr;
     818             :     }
     819          17 :     pszBuffer[nSize] = 0;
     820          17 :     json_object *poObj = nullptr;
     821          17 :     if (!OGRJSonParse(pszBuffer, &poObj))
     822             :     {
     823           0 :         VSIFree(pszBuffer);
     824           0 :         return nullptr;
     825             :     }
     826             : 
     827          17 :     OGRFeature *poFeat = ReadFeature(poLayer, poObj, pszBuffer);
     828          17 :     json_object_put(poObj);
     829          17 :     VSIFree(pszBuffer);
     830          17 :     if (!poFeat)
     831             :     {
     832           0 :         return nullptr;
     833             :     }
     834          17 :     poFeat->SetFID(nFID);
     835          17 :     return poFeat;
     836             : }
     837             : 
     838             : /************************************************************************/
     839             : /*                           IngestAll()                                */
     840             : /************************************************************************/
     841             : 
     842          14 : bool OGRGeoJSONReader::IngestAll(OGRGeoJSONLayer *poLayer)
     843             : {
     844             :     const vsi_l_offset nRAM =
     845          14 :         static_cast<vsi_l_offset>(CPLGetUsablePhysicalRAM()) / 3 * 4;
     846          14 :     if (nRAM && nTotalOGRFeatureMemEstimate_ > nRAM)
     847             :     {
     848           0 :         CPLError(CE_Failure, CPLE_OutOfMemory,
     849             :                  "Not enough memory to ingest all the layer: " CPL_FRMT_GUIB
     850             :                  " available, " CPL_FRMT_GUIB " needed",
     851             :                  nRAM, nTotalOGRFeatureMemEstimate_);
     852           0 :         return false;
     853             :     }
     854             : 
     855          14 :     CPLDebug("GeoJSON",
     856             :              "Total memory estimated for ingestion: " CPL_FRMT_GUIB " bytes",
     857             :              nTotalOGRFeatureMemEstimate_);
     858             : 
     859          14 :     ResetReading();
     860          14 :     GIntBig nCounter = 0;
     861             :     while (true)
     862             :     {
     863          42 :         OGRFeature *poFeature = GetNextFeature(poLayer);
     864          42 :         if (poFeature == nullptr)
     865          14 :             break;
     866          28 :         poLayer->AddFeature(poFeature);
     867          28 :         delete poFeature;
     868          28 :         nCounter++;
     869          28 :         if (((nCounter % 10000) == 0 || nCounter == nTotalFeatureCount_) &&
     870          12 :             nTotalFeatureCount_ > 0)
     871             :         {
     872          12 :             CPLDebug("GeoJSON", "Ingestion at %.02f %%",
     873          12 :                      100.0 * nCounter / nTotalFeatureCount_);
     874             :         }
     875          28 :     }
     876          14 :     return true;
     877             : }
     878             : 
     879             : /************************************************************************/
     880             : /*                           ReadLayer                                  */
     881             : /************************************************************************/
     882             : 
     883         179 : void OGRGeoJSONReader::ReadLayer(OGRGeoJSONDataSource *poDS,
     884             :                                  const char *pszName, json_object *poObj)
     885             : {
     886         179 :     GeoJSONObject::Type objType = OGRGeoJSONGetType(poObj);
     887         179 :     if (objType == GeoJSONObject::eUnknown)
     888             :     {
     889             :         // Check if the object contains key:value pairs where value
     890             :         // is a standard GeoJSON object. In which case, use key as the layer
     891             :         // name.
     892           2 :         if (json_type_object == json_object_get_type(poObj))
     893             :         {
     894             :             json_object_iter it;
     895           2 :             it.key = nullptr;
     896           2 :             it.val = nullptr;
     897           2 :             it.entry = nullptr;
     898           6 :             json_object_object_foreachC(poObj, it)
     899             :             {
     900           4 :                 objType = OGRGeoJSONGetType(it.val);
     901           4 :                 if (objType != GeoJSONObject::eUnknown)
     902           4 :                     ReadLayer(poDS, it.key, it.val);
     903             :             }
     904             :         }
     905             : 
     906             :         // CPLError(CE_Failure, CPLE_AppDefined,
     907             :         //          "Unrecognized GeoJSON structure.");
     908             : 
     909           4 :         return;
     910             :     }
     911             : 
     912         177 :     CPLErrorReset();
     913             : 
     914             :     // Figure out layer name
     915         177 :     std::string osName;
     916         177 :     if (pszName)
     917             :     {
     918           4 :         osName = pszName;
     919             :     }
     920             :     else
     921             :     {
     922         173 :         if (GeoJSONObject::eFeatureCollection == objType)
     923             :         {
     924          95 :             json_object *poName = CPL_json_object_object_get(poObj, "name");
     925          99 :             if (poName != nullptr &&
     926           4 :                 json_object_get_type(poName) == json_type_string)
     927             :             {
     928           4 :                 pszName = json_object_get_string(poName);
     929             :             }
     930             :         }
     931         173 :         if (pszName)
     932             :         {
     933           4 :             osName = pszName;
     934             :         }
     935             :         else
     936             :         {
     937         169 :             const char *pszDesc = poDS->GetDescription();
     938         169 :             if (strchr(pszDesc, '?') == nullptr &&
     939         169 :                 strchr(pszDesc, '{') == nullptr)
     940             :             {
     941          38 :                 osName = CPLGetBasenameSafe(pszDesc);
     942             :             }
     943             :         }
     944             :     }
     945         177 :     osName = OGRGeoJSONLayer::GetValidLayerName(osName.c_str());
     946             : 
     947             :     OGRGeoJSONLayer *poLayer = new OGRGeoJSONLayer(
     948         177 :         osName.c_str(), nullptr, OGRGeoJSONLayer::DefaultGeometryType, poDS,
     949         177 :         nullptr);
     950             : 
     951         177 :     OGRSpatialReference *poSRS = OGRGeoJSONReadSpatialReference(poObj);
     952         177 :     bool bDefaultSRS = false;
     953         177 :     if (poSRS == nullptr)
     954             :     {
     955             :         // If there is none defined, we use 4326 / 4979.
     956         170 :         poSRS = new OGRSpatialReference();
     957         170 :         bDefaultSRS = true;
     958             :     }
     959             :     {
     960         177 :         auto poGeomFieldDefn = poLayer->GetLayerDefn()->GetGeomFieldDefn(0);
     961         177 :         whileUnsealing(poGeomFieldDefn)->SetSpatialRef(poSRS);
     962             :     }
     963             : 
     964         177 :     if (!GenerateLayerDefn(poLayer, poObj))
     965             :     {
     966           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     967             :                  "Layer schema generation failed.");
     968             : 
     969           1 :         delete poLayer;
     970           1 :         poSRS->Release();
     971           1 :         return;
     972             :     }
     973             : 
     974         176 :     if (GeoJSONObject::eFeatureCollection == objType)
     975             :     {
     976             :         json_object *poDescription =
     977          96 :             CPL_json_object_object_get(poObj, "description");
     978          97 :         if (poDescription != nullptr &&
     979           1 :             json_object_get_type(poDescription) == json_type_string)
     980             :         {
     981           1 :             poLayer->SetMetadataItem("DESCRIPTION",
     982           1 :                                      json_object_get_string(poDescription));
     983             :         }
     984             : 
     985          96 :         SetCoordinatePrecision(poObj, poLayer);
     986             :     }
     987             : 
     988             :     /* -------------------------------------------------------------------- */
     989             :     /*      Translate single geometry-only Feature object.                  */
     990             :     /* -------------------------------------------------------------------- */
     991             : 
     992         176 :     if (GeoJSONObject::ePoint == objType ||
     993         130 :         GeoJSONObject::eMultiPoint == objType ||
     994         128 :         GeoJSONObject::eLineString == objType ||
     995         127 :         GeoJSONObject::eMultiLineString == objType ||
     996         125 :         GeoJSONObject::ePolygon == objType ||
     997         120 :         GeoJSONObject::eMultiPolygon == objType ||
     998             :         GeoJSONObject::eGeometryCollection == objType)
     999             :     {
    1000          58 :         OGRGeometry *poGeometry = ReadGeometry(poObj, poLayer->GetSpatialRef());
    1001          58 :         if (!AddFeature(poLayer, poGeometry))
    1002             :         {
    1003           1 :             CPLDebug("GeoJSON", "Translation of single geometry failed.");
    1004           1 :             delete poLayer;
    1005           1 :             poSRS->Release();
    1006           1 :             return;
    1007          57 :         }
    1008             :     }
    1009             :     /* -------------------------------------------------------------------- */
    1010             :     /*      Translate single but complete Feature object.                   */
    1011             :     /* -------------------------------------------------------------------- */
    1012         118 :     else if (GeoJSONObject::eFeature == objType)
    1013             :     {
    1014          22 :         OGRFeature *poFeature = ReadFeature(poLayer, poObj, nullptr);
    1015          22 :         AddFeature(poLayer, poFeature);
    1016             :     }
    1017             :     /* -------------------------------------------------------------------- */
    1018             :     /*      Translate multi-feature FeatureCollection object.               */
    1019             :     /* -------------------------------------------------------------------- */
    1020          96 :     else if (GeoJSONObject::eFeatureCollection == objType)
    1021             :     {
    1022          96 :         ReadFeatureCollection(poLayer, poObj);
    1023             :     }
    1024             : 
    1025         175 :     if (CPLGetLastErrorType() != CE_Warning)
    1026         172 :         CPLErrorReset();
    1027             : 
    1028         175 :     poLayer->DetectGeometryType();
    1029             : 
    1030         175 :     if (bDefaultSRS && poLayer->GetGeomType() != wkbNone)
    1031             :     {
    1032         168 :         if (OGR_GT_HasZ(poLayer->GetGeomType()))
    1033          46 :             poSRS->importFromEPSG(4979);
    1034             :         else
    1035         122 :             poSRS->SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
    1036         168 :         poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1037             :     }
    1038         175 :     poSRS->Release();
    1039             : 
    1040         175 :     poDS->AddLayer(poLayer);
    1041             : }
    1042             : 
    1043             : /************************************************************************/
    1044             : /*                         GenerateLayerDefn()                          */
    1045             : /************************************************************************/
    1046             : 
    1047         177 : bool OGRGeoJSONReader::GenerateLayerDefn(OGRGeoJSONLayer *poLayer,
    1048             :                                          json_object *poGJObject)
    1049             : {
    1050         177 :     CPLAssert(nullptr != poGJObject);
    1051         177 :     CPLAssert(nullptr != poLayer->GetLayerDefn());
    1052         177 :     CPLAssert(0 == poLayer->GetLayerDefn()->GetFieldCount());
    1053             : 
    1054         177 :     if (bAttributesSkip_)
    1055           0 :         return true;
    1056             : 
    1057             :     /* -------------------------------------------------------------------- */
    1058             :     /*      Scan all features and generate layer definition.                */
    1059             :     /* -------------------------------------------------------------------- */
    1060         177 :     bool bSuccess = true;
    1061             : 
    1062         354 :     std::map<std::string, int> oMapFieldNameToIdx;
    1063         354 :     std::vector<std::unique_ptr<OGRFieldDefn>> apoFieldDefn;
    1064         354 :     gdal::DirectedAcyclicGraph<int, std::string> dag;
    1065             : 
    1066         177 :     GeoJSONObject::Type objType = OGRGeoJSONGetType(poGJObject);
    1067         177 :     if (GeoJSONObject::eFeature == objType)
    1068             :     {
    1069          22 :         bSuccess = GenerateFeatureDefn(oMapFieldNameToIdx, apoFieldDefn, dag,
    1070             :                                        poLayer, poGJObject);
    1071             :     }
    1072         155 :     else if (GeoJSONObject::eFeatureCollection == objType)
    1073             :     {
    1074             :         json_object *poObjFeatures =
    1075          97 :             OGRGeoJSONFindMemberByName(poGJObject, "features");
    1076         194 :         if (nullptr != poObjFeatures &&
    1077          97 :             json_type_array == json_object_get_type(poObjFeatures))
    1078             :         {
    1079          97 :             const auto nFeatures = json_object_array_length(poObjFeatures);
    1080         215 :             for (auto i = decltype(nFeatures){0}; i < nFeatures; ++i)
    1081             :             {
    1082             :                 json_object *poObjFeature =
    1083         118 :                     json_object_array_get_idx(poObjFeatures, i);
    1084         118 :                 if (!GenerateFeatureDefn(oMapFieldNameToIdx, apoFieldDefn, dag,
    1085             :                                          poLayer, poObjFeature))
    1086             :                 {
    1087           1 :                     CPLDebug("GeoJSON", "Create feature schema failure.");
    1088           1 :                     bSuccess = false;
    1089             :                 }
    1090             :             }
    1091             :         }
    1092             :         else
    1093             :         {
    1094           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1095             :                      "Invalid FeatureCollection object. "
    1096             :                      "Missing \'features\' member.");
    1097           0 :             bSuccess = false;
    1098             :         }
    1099             :     }
    1100             : 
    1101             :     // Note: the current strategy will not produce stable output, depending
    1102             :     // on the order of features, if there are conflicting order / cycles.
    1103             :     // See https://github.com/OSGeo/gdal/pull/4552 for a number of potential
    1104             :     // resolutions if that has to be solved in the future.
    1105         177 :     OGRFeatureDefn *poDefn = poLayer->GetLayerDefn();
    1106         354 :     const auto sortedFields = dag.getTopologicalOrdering();
    1107         177 :     CPLAssert(sortedFields.size() == apoFieldDefn.size());
    1108             :     {
    1109         354 :         auto oTemporaryUnsealer(poDefn->GetTemporaryUnsealer());
    1110         480 :         for (int idx : sortedFields)
    1111             :         {
    1112         303 :             poDefn->AddFieldDefn(apoFieldDefn[idx].get());
    1113             :         }
    1114             :     }
    1115             : 
    1116         177 :     CPLString osFIDColumn;
    1117         177 :     FinalizeLayerDefn(poLayer, osFIDColumn);
    1118         177 :     if (!osFIDColumn.empty())
    1119           8 :         poLayer->SetFIDColumn(osFIDColumn);
    1120             : 
    1121         177 :     return bSuccess;
    1122             : }
    1123             : 
    1124             : /************************************************************************/
    1125             : /*                          FinalizeLayerDefn()                         */
    1126             : /************************************************************************/
    1127             : 
    1128         498 : void OGRGeoJSONBaseReader::FinalizeLayerDefn(OGRLayer *poLayer,
    1129             :                                              CPLString &osFIDColumn)
    1130             : {
    1131             :     /* -------------------------------------------------------------------- */
    1132             :     /*      Validate and add FID column if necessary.                       */
    1133             :     /* -------------------------------------------------------------------- */
    1134         498 :     osFIDColumn.clear();
    1135         498 :     OGRFeatureDefn *poLayerDefn = poLayer->GetLayerDefn();
    1136         498 :     CPLAssert(nullptr != poLayerDefn);
    1137             : 
    1138         498 :     whileUnsealing(poLayerDefn)->SetGeomType(m_eLayerGeomType);
    1139             : 
    1140         498 :     if (m_bNeedFID64)
    1141             :     {
    1142           4 :         poLayer->SetMetadataItem(OLMD_FID64, "YES");
    1143             :     }
    1144             : 
    1145         498 :     if (!bFeatureLevelIdAsFID_)
    1146             :     {
    1147         475 :         const int idx = poLayerDefn->GetFieldIndexCaseSensitive("id");
    1148         475 :         if (idx >= 0)
    1149             :         {
    1150          72 :             OGRFieldDefn *poFDefn = poLayerDefn->GetFieldDefn(idx);
    1151         112 :             if (poFDefn->GetType() == OFTInteger ||
    1152          40 :                 poFDefn->GetType() == OFTInteger64)
    1153             :             {
    1154          34 :                 osFIDColumn = poLayerDefn->GetFieldDefn(idx)->GetNameRef();
    1155             :             }
    1156             :         }
    1157             :     }
    1158         498 : }
    1159             : 
    1160             : /************************************************************************/
    1161             : /*                     OGRGeoJSONReaderAddOrUpdateField()               */
    1162             : /************************************************************************/
    1163             : 
    1164       20380 : void OGRGeoJSONReaderAddOrUpdateField(
    1165             :     std::vector<int> &retIndices,
    1166             :     std::map<std::string, int> &oMapFieldNameToIdx,
    1167             :     std::vector<std::unique_ptr<OGRFieldDefn>> &apoFieldDefn,
    1168             :     const char *pszKey, json_object *poVal, bool bFlattenNestedAttributes,
    1169             :     char chNestedAttributeSeparator, bool bArrayAsString, bool bDateAsString,
    1170             :     std::set<int> &aoSetUndeterminedTypeFields)
    1171             : {
    1172       20380 :     const auto jType = json_object_get_type(poVal);
    1173       20380 :     if (bFlattenNestedAttributes && poVal != nullptr &&
    1174             :         jType == json_type_object)
    1175             :     {
    1176             :         json_object_iter it;
    1177           2 :         it.key = nullptr;
    1178           2 :         it.val = nullptr;
    1179           2 :         it.entry = nullptr;
    1180           6 :         json_object_object_foreachC(poVal, it)
    1181             :         {
    1182           4 :             char szSeparator[2] = {chNestedAttributeSeparator, '\0'};
    1183             : 
    1184             :             CPLString osAttrName(
    1185           8 :                 CPLSPrintf("%s%s%s", pszKey, szSeparator, it.key));
    1186           8 :             if (it.val != nullptr &&
    1187           4 :                 json_object_get_type(it.val) == json_type_object)
    1188             :             {
    1189           0 :                 OGRGeoJSONReaderAddOrUpdateField(
    1190             :                     retIndices, oMapFieldNameToIdx, apoFieldDefn, osAttrName,
    1191             :                     it.val, true, chNestedAttributeSeparator, bArrayAsString,
    1192             :                     bDateAsString, aoSetUndeterminedTypeFields);
    1193             :             }
    1194             :             else
    1195             :             {
    1196           4 :                 OGRGeoJSONReaderAddOrUpdateField(
    1197             :                     retIndices, oMapFieldNameToIdx, apoFieldDefn, osAttrName,
    1198             :                     it.val, false, 0, bArrayAsString, bDateAsString,
    1199             :                     aoSetUndeterminedTypeFields);
    1200             :             }
    1201             :         }
    1202           2 :         return;
    1203             :     }
    1204             : 
    1205       20378 :     const auto oMapFieldNameToIdxIter = oMapFieldNameToIdx.find(pszKey);
    1206       20378 :     if (oMapFieldNameToIdxIter == oMapFieldNameToIdx.end())
    1207             :     {
    1208             :         OGRFieldSubType eSubType;
    1209             :         const OGRFieldType eType =
    1210        1328 :             GeoJSONPropertyToFieldType(poVal, eSubType, bArrayAsString);
    1211        2656 :         auto poFieldDefn = std::make_unique<OGRFieldDefn>(pszKey, eType);
    1212        1328 :         poFieldDefn->SetSubType(eSubType);
    1213        1328 :         if (eSubType == OFSTBoolean)
    1214          34 :             poFieldDefn->SetWidth(1);
    1215        1328 :         if (poFieldDefn->GetType() == OFTString && !bDateAsString)
    1216             :         {
    1217         686 :             int nTZFlag = 0;
    1218         686 :             poFieldDefn->SetType(
    1219             :                 GeoJSONStringPropertyToFieldType(poVal, nTZFlag));
    1220         686 :             poFieldDefn->SetTZFlag(nTZFlag);
    1221             :         }
    1222        1328 :         apoFieldDefn.emplace_back(std::move(poFieldDefn));
    1223        1328 :         const int nIndex = static_cast<int>(apoFieldDefn.size()) - 1;
    1224        1328 :         retIndices.emplace_back(nIndex);
    1225        1328 :         oMapFieldNameToIdx[pszKey] = nIndex;
    1226        1328 :         if (poVal == nullptr)
    1227          13 :             aoSetUndeterminedTypeFields.insert(nIndex);
    1228             :     }
    1229       19050 :     else if (poVal)
    1230             :     {
    1231       19035 :         const int nIndex = oMapFieldNameToIdxIter->second;
    1232       19035 :         retIndices.emplace_back(nIndex);
    1233             :         // If there is a null value: do not update field definition.
    1234       19035 :         OGRFieldDefn *poFDefn = apoFieldDefn[nIndex].get();
    1235       19035 :         const OGRFieldType eType = poFDefn->GetType();
    1236       19035 :         const OGRFieldSubType eSubType = poFDefn->GetSubType();
    1237             :         OGRFieldSubType eNewSubType;
    1238             :         OGRFieldType eNewType =
    1239       19035 :             GeoJSONPropertyToFieldType(poVal, eNewSubType, bArrayAsString);
    1240             :         const bool bNewIsEmptyArray =
    1241       19035 :             (jType == json_type_array && json_object_array_length(poVal) == 0);
    1242       19035 :         if (cpl::contains(aoSetUndeterminedTypeFields, nIndex))
    1243             :         {
    1244           7 :             poFDefn->SetSubType(OFSTNone);
    1245           7 :             poFDefn->SetType(eNewType);
    1246           7 :             if (poFDefn->GetType() == OFTString && !bDateAsString)
    1247             :             {
    1248           2 :                 int nTZFlag = 0;
    1249           2 :                 poFDefn->SetType(
    1250             :                     GeoJSONStringPropertyToFieldType(poVal, nTZFlag));
    1251           2 :                 poFDefn->SetTZFlag(nTZFlag);
    1252             :             }
    1253           7 :             poFDefn->SetSubType(eNewSubType);
    1254           7 :             aoSetUndeterminedTypeFields.erase(nIndex);
    1255             :         }
    1256       19028 :         else if (eType == OFTInteger)
    1257             :         {
    1258       12971 :             if (eNewType == OFTInteger && eSubType == OFSTBoolean &&
    1259           5 :                 eNewSubType != OFSTBoolean)
    1260             :             {
    1261           2 :                 poFDefn->SetSubType(OFSTNone);
    1262             :             }
    1263       12969 :             else if (eNewType == OFTInteger64 || eNewType == OFTReal ||
    1264       12955 :                      eNewType == OFTInteger64List || eNewType == OFTRealList ||
    1265             :                      eNewType == OFTStringList)
    1266             :             {
    1267          18 :                 poFDefn->SetSubType(OFSTNone);
    1268          18 :                 poFDefn->SetType(eNewType);
    1269             :             }
    1270       12951 :             else if (eNewType == OFTIntegerList)
    1271             :             {
    1272           4 :                 if (eSubType == OFSTBoolean && eNewSubType != OFSTBoolean)
    1273             :                 {
    1274           1 :                     poFDefn->SetSubType(OFSTNone);
    1275             :                 }
    1276           4 :                 poFDefn->SetType(eNewType);
    1277             :             }
    1278       12947 :             else if (eNewType != OFTInteger)
    1279             :             {
    1280          15 :                 poFDefn->SetSubType(OFSTNone);
    1281          15 :                 poFDefn->SetType(OFTString);
    1282          15 :                 poFDefn->SetSubType(OFSTJSON);
    1283             :             }
    1284             :         }
    1285        6057 :         else if (eType == OFTInteger64)
    1286             :         {
    1287          15 :             if (eNewType == OFTReal)
    1288             :             {
    1289           2 :                 poFDefn->SetSubType(OFSTNone);
    1290           2 :                 poFDefn->SetType(eNewType);
    1291             :             }
    1292          13 :             else if (eNewType == OFTIntegerList || eNewType == OFTInteger64List)
    1293             :             {
    1294           3 :                 poFDefn->SetSubType(OFSTNone);
    1295           3 :                 poFDefn->SetType(OFTInteger64List);
    1296             :             }
    1297          10 :             else if (eNewType == OFTRealList || eNewType == OFTStringList)
    1298             :             {
    1299           2 :                 poFDefn->SetSubType(OFSTNone);
    1300           2 :                 poFDefn->SetType(eNewType);
    1301             :             }
    1302           8 :             else if (eNewType != OFTInteger && eNewType != OFTInteger64)
    1303             :             {
    1304           2 :                 poFDefn->SetSubType(OFSTNone);
    1305           2 :                 poFDefn->SetType(OFTString);
    1306           2 :                 poFDefn->SetSubType(OFSTJSON);
    1307             :             }
    1308             :         }
    1309        6042 :         else if (eType == OFTReal)
    1310             :         {
    1311         413 :             if (eNewType == OFTIntegerList || eNewType == OFTInteger64List ||
    1312             :                 eNewType == OFTRealList)
    1313             :             {
    1314           4 :                 poFDefn->SetSubType(OFSTNone);
    1315           4 :                 poFDefn->SetType(OFTRealList);
    1316             :             }
    1317         409 :             else if (eNewType == OFTStringList)
    1318             :             {
    1319           1 :                 poFDefn->SetSubType(OFSTNone);
    1320           1 :                 poFDefn->SetType(OFTStringList);
    1321             :             }
    1322         408 :             else if (eNewType != OFTInteger && eNewType != OFTInteger64 &&
    1323             :                      eNewType != OFTReal)
    1324             :             {
    1325           2 :                 poFDefn->SetSubType(OFSTNone);
    1326           2 :                 poFDefn->SetType(OFTString);
    1327           2 :                 poFDefn->SetSubType(OFSTJSON);
    1328             :             }
    1329             :         }
    1330        5629 :         else if (eType == OFTString)
    1331             :         {
    1332        5353 :             if (eSubType == OFSTNone)
    1333             :             {
    1334        5317 :                 if (eNewType == OFTStringList)
    1335             :                 {
    1336           1 :                     poFDefn->SetType(OFTStringList);
    1337             :                 }
    1338        5316 :                 else if (eNewType != OFTString)
    1339             :                 {
    1340          15 :                     poFDefn->SetSubType(OFSTJSON);
    1341             :                 }
    1342             :             }
    1343             :         }
    1344         276 :         else if (eType == OFTIntegerList)
    1345             :         {
    1346          50 :             if (eNewType == OFTString)
    1347             :             {
    1348           6 :                 if (!bNewIsEmptyArray)
    1349             :                 {
    1350           6 :                     poFDefn->SetSubType(OFSTNone);
    1351           6 :                     poFDefn->SetType(eNewType);
    1352           6 :                     poFDefn->SetSubType(OFSTJSON);
    1353             :                 }
    1354             :             }
    1355          44 :             else if (eNewType == OFTInteger64List || eNewType == OFTRealList ||
    1356             :                      eNewType == OFTStringList)
    1357             :             {
    1358          19 :                 poFDefn->SetSubType(OFSTNone);
    1359          19 :                 poFDefn->SetType(eNewType);
    1360             :             }
    1361          25 :             else if (eNewType == OFTInteger64)
    1362             :             {
    1363           2 :                 poFDefn->SetSubType(OFSTNone);
    1364           2 :                 poFDefn->SetType(OFTInteger64List);
    1365             :             }
    1366          23 :             else if (eNewType == OFTReal)
    1367             :             {
    1368           2 :                 poFDefn->SetSubType(OFSTNone);
    1369           2 :                 poFDefn->SetType(OFTRealList);
    1370             :             }
    1371          21 :             else if (eNewType == OFTInteger || eNewType == OFTIntegerList)
    1372             :             {
    1373          21 :                 if (eSubType == OFSTBoolean && eNewSubType != OFSTBoolean)
    1374             :                 {
    1375           2 :                     poFDefn->SetSubType(OFSTNone);
    1376             :                 }
    1377             :             }
    1378             :             else
    1379             :             {
    1380           0 :                 poFDefn->SetSubType(OFSTNone);
    1381           0 :                 poFDefn->SetType(OFTString);
    1382           0 :                 poFDefn->SetSubType(OFSTJSON);
    1383             :             }
    1384             :         }
    1385         226 :         else if (eType == OFTInteger64List)
    1386             :         {
    1387          24 :             if (eNewType == OFTString)
    1388             :             {
    1389           3 :                 if (!bNewIsEmptyArray)
    1390             :                 {
    1391           3 :                     poFDefn->SetSubType(OFSTNone);
    1392           3 :                     poFDefn->SetType(eNewType);
    1393           3 :                     poFDefn->SetSubType(OFSTJSON);
    1394             :                 }
    1395             :             }
    1396          21 :             else if (eNewType == OFTInteger64List || eNewType == OFTRealList ||
    1397             :                      eNewType == OFTStringList)
    1398             :             {
    1399           9 :                 poFDefn->SetSubType(OFSTNone);
    1400           9 :                 poFDefn->SetType(eNewType);
    1401             :             }
    1402          12 :             else if (eNewType == OFTReal)
    1403             :             {
    1404           1 :                 poFDefn->SetSubType(OFSTNone);
    1405           1 :                 poFDefn->SetType(OFTRealList);
    1406             :             }
    1407          11 :             else if (eNewType != OFTInteger && eNewType != OFTInteger64 &&
    1408             :                      eNewType != OFTIntegerList)
    1409             :             {
    1410           0 :                 poFDefn->SetSubType(OFSTNone);
    1411           0 :                 poFDefn->SetType(OFTString);
    1412           0 :                 poFDefn->SetSubType(OFSTJSON);
    1413             :             }
    1414             :         }
    1415         202 :         else if (eType == OFTRealList)
    1416             :         {
    1417          24 :             if (eNewType == OFTString)
    1418             :             {
    1419           3 :                 if (!bNewIsEmptyArray)
    1420             :                 {
    1421           3 :                     poFDefn->SetSubType(OFSTNone);
    1422           3 :                     poFDefn->SetType(eNewType);
    1423           3 :                     poFDefn->SetSubType(OFSTJSON);
    1424             :                 }
    1425             :             }
    1426          21 :             else if (eNewType == OFTStringList)
    1427             :             {
    1428           1 :                 poFDefn->SetSubType(OFSTNone);
    1429           1 :                 poFDefn->SetType(eNewType);
    1430             :             }
    1431          20 :             else if (eNewType != OFTInteger && eNewType != OFTInteger64 &&
    1432          16 :                      eNewType != OFTReal && eNewType != OFTIntegerList &&
    1433           4 :                      eNewType != OFTInteger64List && eNewType != OFTRealList)
    1434             :             {
    1435           0 :                 poFDefn->SetSubType(OFSTNone);
    1436           0 :                 poFDefn->SetType(OFTString);
    1437           0 :                 poFDefn->SetSubType(OFSTJSON);
    1438             :             }
    1439             :         }
    1440         178 :         else if (eType == OFTStringList)
    1441             :         {
    1442          30 :             if (eNewType == OFTString && eNewSubType == OFSTJSON)
    1443             :             {
    1444           1 :                 if (!bNewIsEmptyArray)
    1445             :                 {
    1446           1 :                     poFDefn->SetSubType(OFSTNone);
    1447           1 :                     poFDefn->SetType(eNewType);
    1448           1 :                     poFDefn->SetSubType(OFSTJSON);
    1449             :                 }
    1450             :             }
    1451             :         }
    1452         148 :         else if (eType == OFTDate || eType == OFTTime || eType == OFTDateTime)
    1453             :         {
    1454         148 :             if (eNewType == OFTString && !bDateAsString &&
    1455         147 :                 eNewSubType == OFSTNone)
    1456             :             {
    1457         146 :                 int nTZFlag = 0;
    1458         146 :                 eNewType = GeoJSONStringPropertyToFieldType(poVal, nTZFlag);
    1459         156 :                 if (poFDefn->GetTZFlag() > OGR_TZFLAG_UNKNOWN &&
    1460          10 :                     nTZFlag != poFDefn->GetTZFlag())
    1461             :                 {
    1462           6 :                     if (nTZFlag == OGR_TZFLAG_UNKNOWN)
    1463           4 :                         poFDefn->SetTZFlag(OGR_TZFLAG_UNKNOWN);
    1464             :                     else
    1465           2 :                         poFDefn->SetTZFlag(OGR_TZFLAG_MIXED_TZ);
    1466             :                 }
    1467             :             }
    1468         148 :             if (eType != eNewType)
    1469             :             {
    1470           8 :                 poFDefn->SetSubType(OFSTNone);
    1471           8 :                 if (eNewType == OFTString)
    1472             :                 {
    1473           5 :                     poFDefn->SetType(eNewType);
    1474           5 :                     poFDefn->SetSubType(eNewSubType);
    1475             :                 }
    1476           3 :                 else if (eType == OFTDate && eNewType == OFTDateTime)
    1477             :                 {
    1478           1 :                     poFDefn->SetType(OFTDateTime);
    1479             :                 }
    1480           2 :                 else if (!(eType == OFTDateTime && eNewType == OFTDate))
    1481             :                 {
    1482           1 :                     poFDefn->SetType(OFTString);
    1483           1 :                     poFDefn->SetSubType(OFSTJSON);
    1484             :                 }
    1485             :             }
    1486             :         }
    1487             : 
    1488       19035 :         poFDefn->SetWidth(poFDefn->GetSubType() == OFSTBoolean ? 1 : 0);
    1489             :     }
    1490             :     else
    1491             :     {
    1492          15 :         const int nIndex = oMapFieldNameToIdxIter->second;
    1493          15 :         retIndices.emplace_back(nIndex);
    1494             :     }
    1495             : }
    1496             : 
    1497             : /************************************************************************/
    1498             : /*             OGRGeoJSONGenerateFeatureDefnDealWithID()                */
    1499             : /************************************************************************/
    1500             : 
    1501        4937 : void OGRGeoJSONGenerateFeatureDefnDealWithID(
    1502             :     json_object *poObj, json_object *poObjProps, int &nPrevFieldIdx,
    1503             :     std::map<std::string, int> &oMapFieldNameToIdx,
    1504             :     std::vector<std::unique_ptr<OGRFieldDefn>> &apoFieldDefn,
    1505             :     gdal::DirectedAcyclicGraph<int, std::string> &dag,
    1506             :     bool &bFeatureLevelIdAsFID, bool &bFeatureLevelIdAsAttribute,
    1507             :     bool &bNeedFID64)
    1508             : {
    1509        4937 :     json_object *poObjId = OGRGeoJSONFindMemberByName(poObj, "id");
    1510        4937 :     if (poObjId)
    1511             :     {
    1512        1702 :         const auto iterIdxId = oMapFieldNameToIdx.find("id");
    1513        1702 :         if (iterIdxId == oMapFieldNameToIdx.end())
    1514             :         {
    1515         112 :             if (json_object_get_type(poObjId) == json_type_int)
    1516             :             {
    1517             :                 // If the value is negative, we cannot use it as the FID
    1518             :                 // as OGRMemLayer doesn't support negative FID. And we would
    1519             :                 // have an ambiguity with -1 that can mean OGRNullFID
    1520             :                 // so in that case create a regular attribute and let OGR
    1521             :                 // attribute sequential OGR FIDs.
    1522          71 :                 if (json_object_get_int64(poObjId) < 0)
    1523             :                 {
    1524           5 :                     bFeatureLevelIdAsFID = false;
    1525             :                 }
    1526             :                 else
    1527             :                 {
    1528          66 :                     bFeatureLevelIdAsFID = true;
    1529             :                 }
    1530             :             }
    1531         112 :             if (!bFeatureLevelIdAsFID)
    1532             :             {
    1533             :                 // If there's a top-level id of type string or negative int,
    1534             :                 // and no properties.id, then declare a id field.
    1535          46 :                 bool bHasRegularIdProp = false;
    1536          90 :                 if (nullptr != poObjProps &&
    1537          44 :                     json_object_get_type(poObjProps) == json_type_object)
    1538             :                 {
    1539          44 :                     bHasRegularIdProp =
    1540          44 :                         CPL_json_object_object_get(poObjProps, "id") != nullptr;
    1541             :                 }
    1542          46 :                 if (!bHasRegularIdProp)
    1543             :                 {
    1544          43 :                     OGRFieldType eType = OFTString;
    1545          43 :                     if (json_object_get_type(poObjId) == json_type_int)
    1546             :                     {
    1547           5 :                         if (CPL_INT64_FITS_ON_INT32(
    1548             :                                 json_object_get_int64(poObjId)))
    1549           4 :                             eType = OFTInteger;
    1550             :                         else
    1551           1 :                             eType = OFTInteger64;
    1552             :                     }
    1553             :                     apoFieldDefn.emplace_back(
    1554          43 :                         std::make_unique<OGRFieldDefn>("id", eType));
    1555          43 :                     const int nIdx = static_cast<int>(apoFieldDefn.size()) - 1;
    1556          43 :                     oMapFieldNameToIdx["id"] = nIdx;
    1557          43 :                     nPrevFieldIdx = nIdx;
    1558          43 :                     dag.addNode(nIdx, "id");
    1559          43 :                     bFeatureLevelIdAsAttribute = true;
    1560             :                 }
    1561             :             }
    1562             :         }
    1563             :         else
    1564             :         {
    1565        1590 :             const int nIdx = iterIdxId->second;
    1566        1590 :             nPrevFieldIdx = nIdx;
    1567        1607 :             if (bFeatureLevelIdAsAttribute &&
    1568          17 :                 json_object_get_type(poObjId) == json_type_int)
    1569             :             {
    1570           3 :                 if (apoFieldDefn[nIdx]->GetType() == OFTInteger)
    1571             :                 {
    1572           1 :                     if (!CPL_INT64_FITS_ON_INT32(
    1573             :                             json_object_get_int64(poObjId)))
    1574           1 :                         apoFieldDefn[nIdx]->SetType(OFTInteger64);
    1575             :                 }
    1576             :             }
    1577        1587 :             else if (bFeatureLevelIdAsAttribute)
    1578             :             {
    1579          14 :                 apoFieldDefn[nIdx]->SetType(OFTString);
    1580             :             }
    1581             :         }
    1582             :     }
    1583             : 
    1584        4937 :     if (!bNeedFID64)
    1585             :     {
    1586        4936 :         json_object *poId = CPL_json_object_object_get(poObj, "id");
    1587        4936 :         if (poId == nullptr)
    1588             :         {
    1589        6421 :             if (poObjProps &&
    1590        3186 :                 json_object_get_type(poObjProps) == json_type_object)
    1591             :             {
    1592        3185 :                 poId = CPL_json_object_object_get(poObjProps, "id");
    1593             :             }
    1594             :         }
    1595        4936 :         if (poId != nullptr && json_object_get_type(poId) == json_type_int)
    1596             :         {
    1597        1689 :             GIntBig nFID = json_object_get_int64(poId);
    1598        1689 :             if (!CPL_INT64_FITS_ON_INT32(nFID))
    1599             :             {
    1600           4 :                 bNeedFID64 = true;
    1601             :             }
    1602             :         }
    1603             :     }
    1604        4937 : }
    1605             : 
    1606             : /************************************************************************/
    1607             : /*                        GenerateFeatureDefn()                         */
    1608             : /************************************************************************/
    1609        4718 : bool OGRGeoJSONBaseReader::GenerateFeatureDefn(
    1610             :     std::map<std::string, int> &oMapFieldNameToIdx,
    1611             :     std::vector<std::unique_ptr<OGRFieldDefn>> &apoFieldDefn,
    1612             :     gdal::DirectedAcyclicGraph<int, std::string> &dag, OGRLayer *poLayer,
    1613             :     json_object *poObj)
    1614             : {
    1615             :     /* -------------------------------------------------------------------- */
    1616             :     /*      Read collection of properties.                                  */
    1617             :     /* -------------------------------------------------------------------- */
    1618             :     lh_entry *poObjPropsEntry =
    1619        4718 :         OGRGeoJSONFindMemberEntryByName(poObj, "properties");
    1620        4718 :     json_object *poObjProps =
    1621        4718 :         const_cast<json_object *>(static_cast<const json_object *>(
    1622             :             poObjPropsEntry ? poObjPropsEntry->v : nullptr));
    1623             : 
    1624        9436 :     std::vector<int> anCurFieldIndices;
    1625        4718 :     int nPrevFieldIdx = -1;
    1626             : 
    1627        4718 :     OGRGeoJSONGenerateFeatureDefnDealWithID(
    1628             :         poObj, poObjProps, nPrevFieldIdx, oMapFieldNameToIdx, apoFieldDefn, dag,
    1629        4718 :         bFeatureLevelIdAsFID_, bFeatureLevelIdAsAttribute_, m_bNeedFID64);
    1630             : 
    1631        4718 :     json_object *poGeomObj = CPL_json_object_object_get(poObj, "geometry");
    1632        4718 :     if (poGeomObj && json_object_get_type(poGeomObj) == json_type_object)
    1633             :     {
    1634        4472 :         const auto eType = OGRGeoJSONGetOGRGeometryType(poGeomObj);
    1635             : 
    1636        4472 :         OGRGeoJSONUpdateLayerGeomType(m_bFirstGeometry, eType,
    1637        4472 :                                       m_eLayerGeomType);
    1638             : 
    1639        4472 :         if (eType != wkbNone && eType != wkbUnknown)
    1640             :         {
    1641             :             // This is maybe too optimistic: it assumes that the geometry
    1642             :             // coordinates array is in the correct format
    1643        4472 :             m_bExtentRead |= OGRGeoJSONGetExtent3D(poGeomObj, &m_oEnvelope3D);
    1644             :         }
    1645             :     }
    1646             : 
    1647        4718 :     bool bSuccess = false;
    1648             : 
    1649        9388 :     if (nullptr != poObjProps &&
    1650        4670 :         json_object_get_type(poObjProps) == json_type_object)
    1651             :     {
    1652        4669 :         if (bIsGeocouchSpatiallistFormat)
    1653             :         {
    1654           3 :             poObjProps = CPL_json_object_object_get(poObjProps, "properties");
    1655           6 :             if (nullptr == poObjProps ||
    1656           3 :                 json_object_get_type(poObjProps) != json_type_object)
    1657             :             {
    1658           2 :                 return true;
    1659             :             }
    1660             :         }
    1661             : 
    1662             :         json_object_iter it;
    1663        4669 :         it.key = nullptr;
    1664        4669 :         it.val = nullptr;
    1665        4669 :         it.entry = nullptr;
    1666       24653 :         json_object_object_foreachC(poObjProps, it)
    1667             :         {
    1668       39963 :             if (!bIsGeocouchSpatiallistFormat &&
    1669       19977 :                 !cpl::contains(oMapFieldNameToIdx, it.key))
    1670             :             {
    1671             :                 // Detect the special kind of GeoJSON output by a spatiallist of
    1672             :                 // GeoCouch such as:
    1673             :                 // http://gd.iriscouch.com/cphosm/_design/geo/_rewrite/data?bbox=12.53%2C55.73%2C12.54%2C55.73
    1674        1154 :                 if (strcmp(it.key, "_id") == 0)
    1675             :                 {
    1676           2 :                     bFoundGeocouchId = true;
    1677             :                 }
    1678        1152 :                 else if (bFoundGeocouchId && strcmp(it.key, "_rev") == 0)
    1679             :                 {
    1680           2 :                     bFoundRev = true;
    1681             :                 }
    1682           4 :                 else if (bFoundRev && strcmp(it.key, "type") == 0 &&
    1683           2 :                          it.val != nullptr &&
    1684        1156 :                          json_object_get_type(it.val) == json_type_string &&
    1685           2 :                          strcmp(json_object_get_string(it.val), "Feature") == 0)
    1686             :                 {
    1687           2 :                     bFoundTypeFeature = true;
    1688             :                 }
    1689        2298 :                 else if (bFoundTypeFeature &&
    1690           2 :                          strcmp(it.key, "properties") == 0 &&
    1691        1152 :                          it.val != nullptr &&
    1692           2 :                          json_object_get_type(it.val) == json_type_object)
    1693             :                 {
    1694           2 :                     if (bFlattenGeocouchSpatiallistFormat < 0)
    1695           2 :                         bFlattenGeocouchSpatiallistFormat =
    1696           2 :                             CPLTestBool(CPLGetConfigOption(
    1697             :                                 "GEOJSON_FLATTEN_GEOCOUCH", "TRUE"));
    1698           2 :                     if (bFlattenGeocouchSpatiallistFormat)
    1699             :                     {
    1700           2 :                         const auto typeIter = oMapFieldNameToIdx.find("type");
    1701           2 :                         if (typeIter != oMapFieldNameToIdx.end())
    1702             :                         {
    1703           2 :                             const int nIdx = typeIter->second;
    1704           2 :                             apoFieldDefn.erase(apoFieldDefn.begin() + nIdx);
    1705           2 :                             oMapFieldNameToIdx.erase(typeIter);
    1706           2 :                             dag.removeNode(nIdx);
    1707             :                         }
    1708             : 
    1709           2 :                         bIsGeocouchSpatiallistFormat = true;
    1710           2 :                         return GenerateFeatureDefn(oMapFieldNameToIdx,
    1711             :                                                    apoFieldDefn, dag, poLayer,
    1712           2 :                                                    poObj);
    1713             :                     }
    1714             :                 }
    1715             :             }
    1716             : 
    1717       19984 :             anCurFieldIndices.clear();
    1718       19984 :             OGRGeoJSONReaderAddOrUpdateField(
    1719       19984 :                 anCurFieldIndices, oMapFieldNameToIdx, apoFieldDefn, it.key,
    1720       19984 :                 it.val, bFlattenNestedAttributes_, chNestedAttributeSeparator_,
    1721       19984 :                 bArrayAsString_, bDateAsString_, aoSetUndeterminedTypeFields_);
    1722       39970 :             for (int idx : anCurFieldIndices)
    1723             :             {
    1724       19986 :                 dag.addNode(idx, apoFieldDefn[idx]->GetNameRef());
    1725       19986 :                 if (nPrevFieldIdx != -1)
    1726             :                 {
    1727       17647 :                     dag.addEdge(nPrevFieldIdx, idx);
    1728             :                 }
    1729       19986 :                 nPrevFieldIdx = idx;
    1730             :             }
    1731             :         }
    1732             : 
    1733             :         // Whether/how we should deal with foreign members
    1734        4667 :         if (eForeignMemberProcessing_ == ForeignMemberProcessing::AUTO)
    1735             :         {
    1736         359 :             if (CPL_json_object_object_get(poObj, "stac_version"))
    1737           3 :                 eForeignMemberProcessing_ = ForeignMemberProcessing::STAC;
    1738             :             else
    1739         356 :                 eForeignMemberProcessing_ = ForeignMemberProcessing::NONE;
    1740             :         }
    1741        4667 :         if (eForeignMemberProcessing_ != ForeignMemberProcessing::NONE)
    1742             :         {
    1743           5 :             it.key = nullptr;
    1744           5 :             it.val = nullptr;
    1745           5 :             it.entry = nullptr;
    1746          57 :             json_object_object_foreachC(poObj, it)
    1747             :             {
    1748         104 :                 if (eForeignMemberProcessing_ ==
    1749          41 :                         ForeignMemberProcessing::STAC &&
    1750          55 :                     strcmp(it.key, "assets") == 0 &&
    1751           3 :                     json_object_get_type(it.val) == json_type_object)
    1752             :                 {
    1753             :                     json_object_iter it2;
    1754           3 :                     it2.key = nullptr;
    1755           3 :                     it2.val = nullptr;
    1756           3 :                     it2.entry = nullptr;
    1757           6 :                     json_object_object_foreachC(it.val, it2)
    1758             :                     {
    1759           3 :                         if (json_object_get_type(it2.val) == json_type_object)
    1760             :                         {
    1761             :                             json_object_iter it3;
    1762           3 :                             it3.key = nullptr;
    1763           3 :                             it3.val = nullptr;
    1764           3 :                             it3.entry = nullptr;
    1765          18 :                             json_object_object_foreachC(it2.val, it3)
    1766             :                             {
    1767          15 :                                 anCurFieldIndices.clear();
    1768          15 :                                 OGRGeoJSONReaderAddOrUpdateField(
    1769             :                                     anCurFieldIndices, oMapFieldNameToIdx,
    1770             :                                     apoFieldDefn,
    1771          30 :                                     std::string("assets.")
    1772          15 :                                         .append(it2.key)
    1773          15 :                                         .append(".")
    1774          15 :                                         .append(it3.key)
    1775             :                                         .c_str(),
    1776          15 :                                     it3.val, bFlattenNestedAttributes_,
    1777          15 :                                     chNestedAttributeSeparator_,
    1778          15 :                                     bArrayAsString_, bDateAsString_,
    1779          15 :                                     aoSetUndeterminedTypeFields_);
    1780          30 :                                 for (int idx : anCurFieldIndices)
    1781             :                                 {
    1782          30 :                                     dag.addNode(
    1783          15 :                                         idx, apoFieldDefn[idx]->GetNameRef());
    1784          15 :                                     if (nPrevFieldIdx != -1)
    1785             :                                     {
    1786          15 :                                         dag.addEdge(nPrevFieldIdx, idx);
    1787             :                                     }
    1788          15 :                                     nPrevFieldIdx = idx;
    1789             :                                 }
    1790             :                             }
    1791             :                         }
    1792             :                     }
    1793             :                 }
    1794          49 :                 else if (strcmp(it.key, "type") != 0 &&
    1795          44 :                          strcmp(it.key, "id") != 0 &&
    1796          39 :                          strcmp(it.key, "geometry") != 0 &&
    1797          34 :                          strcmp(it.key, "bbox") != 0 &&
    1798          29 :                          strcmp(it.key, "properties") != 0)
    1799             :                 {
    1800          24 :                     anCurFieldIndices.clear();
    1801          24 :                     OGRGeoJSONReaderAddOrUpdateField(
    1802             :                         anCurFieldIndices, oMapFieldNameToIdx, apoFieldDefn,
    1803          24 :                         it.key, it.val, bFlattenNestedAttributes_,
    1804          24 :                         chNestedAttributeSeparator_, bArrayAsString_,
    1805          24 :                         bDateAsString_, aoSetUndeterminedTypeFields_);
    1806          48 :                     for (int idx : anCurFieldIndices)
    1807             :                     {
    1808          24 :                         dag.addNode(idx, apoFieldDefn[idx]->GetNameRef());
    1809          24 :                         if (nPrevFieldIdx != -1)
    1810             :                         {
    1811          24 :                             dag.addEdge(nPrevFieldIdx, idx);
    1812             :                         }
    1813          24 :                         nPrevFieldIdx = idx;
    1814             :                     }
    1815             :                 }
    1816             :             }
    1817             :         }
    1818             : 
    1819        4667 :         bSuccess = true;  // SUCCESS
    1820             :     }
    1821          50 :     else if (nullptr != poObjPropsEntry &&
    1822           1 :              (poObjProps == nullptr ||
    1823           1 :               (json_object_get_type(poObjProps) == json_type_array &&
    1824           1 :                json_object_array_length(poObjProps) == 0)))
    1825             :     {
    1826             :         // Ignore "properties": null and "properties": []
    1827          16 :         bSuccess = true;
    1828             :     }
    1829          65 :     else if (poObj != nullptr &&
    1830          32 :              json_object_get_type(poObj) == json_type_object)
    1831             :     {
    1832             :         json_object_iter it;
    1833          32 :         it.key = nullptr;
    1834          32 :         it.val = nullptr;
    1835          32 :         it.entry = nullptr;
    1836         100 :         json_object_object_foreachC(poObj, it)
    1837             :         {
    1838          68 :             if (strcmp(it.key, "type") != 0 &&
    1839          36 :                 strcmp(it.key, "geometry") != 0 &&
    1840           4 :                 strcmp(it.key, "centroid") != 0 &&
    1841           4 :                 strcmp(it.key, "bbox") != 0 && strcmp(it.key, "center") != 0)
    1842             :             {
    1843           4 :                 if (!cpl::contains(oMapFieldNameToIdx, it.key))
    1844             :                 {
    1845           4 :                     anCurFieldIndices.clear();
    1846           4 :                     OGRGeoJSONReaderAddOrUpdateField(
    1847             :                         anCurFieldIndices, oMapFieldNameToIdx, apoFieldDefn,
    1848           4 :                         it.key, it.val, bFlattenNestedAttributes_,
    1849           4 :                         chNestedAttributeSeparator_, bArrayAsString_,
    1850           4 :                         bDateAsString_, aoSetUndeterminedTypeFields_);
    1851           8 :                     for (int idx : anCurFieldIndices)
    1852             :                     {
    1853           4 :                         dag.addNode(idx, apoFieldDefn[idx]->GetNameRef());
    1854           4 :                         if (nPrevFieldIdx != -1)
    1855             :                         {
    1856           0 :                             dag.addEdge(nPrevFieldIdx, idx);
    1857             :                         }
    1858           4 :                         nPrevFieldIdx = idx;
    1859             :                     }
    1860             :                 }
    1861             :             }
    1862             :         }
    1863             : 
    1864          32 :         bSuccess = true;  // SUCCESS
    1865             :         // CPLError(CE_Failure, CPLE_AppDefined,
    1866             :         //          "Invalid Feature object. "
    1867             :         //          "Missing \'properties\' member." );
    1868             :     }
    1869             : 
    1870        4716 :     return bSuccess;
    1871             : }
    1872             : 
    1873             : /************************************************************************/
    1874             : /*                   OGRGeoJSONUpdateLayerGeomType()                    */
    1875             : /************************************************************************/
    1876             : 
    1877        4687 : bool OGRGeoJSONUpdateLayerGeomType(bool &bFirstGeom,
    1878             :                                    OGRwkbGeometryType eGeomType,
    1879             :                                    OGRwkbGeometryType &eLayerGeomType)
    1880             : {
    1881        4687 :     if (bFirstGeom)
    1882             :     {
    1883         436 :         eLayerGeomType = eGeomType;
    1884         436 :         bFirstGeom = false;
    1885             :     }
    1886        4262 :     else if (OGR_GT_HasZ(eGeomType) && !OGR_GT_HasZ(eLayerGeomType) &&
    1887          11 :              wkbFlatten(eGeomType) == wkbFlatten(eLayerGeomType))
    1888             :     {
    1889           2 :         eLayerGeomType = eGeomType;
    1890             :     }
    1891        4252 :     else if (!OGR_GT_HasZ(eGeomType) && OGR_GT_HasZ(eLayerGeomType) &&
    1892           3 :              wkbFlatten(eGeomType) == wkbFlatten(eLayerGeomType))
    1893             :     {
    1894             :         // ok
    1895             :     }
    1896        4247 :     else if (eGeomType != eLayerGeomType && eLayerGeomType != wkbUnknown)
    1897             :     {
    1898          45 :         CPLDebug("GeoJSON", "Detected layer of mixed-geometry type features.");
    1899          45 :         eLayerGeomType = wkbUnknown;
    1900          45 :         return false;
    1901             :     }
    1902        4642 :     return true;
    1903             : }
    1904             : 
    1905             : /************************************************************************/
    1906             : /*                           AddFeature                                 */
    1907             : /************************************************************************/
    1908             : 
    1909          58 : bool OGRGeoJSONReader::AddFeature(OGRGeoJSONLayer *poLayer,
    1910             :                                   OGRGeometry *poGeometry)
    1911             : {
    1912          58 :     bool bAdded = false;
    1913             : 
    1914             :     // TODO: Should we check if geometry is of type of wkbGeometryCollection?
    1915             : 
    1916          58 :     if (nullptr != poGeometry)
    1917             :     {
    1918          57 :         OGRFeature *poFeature = new OGRFeature(poLayer->GetLayerDefn());
    1919          57 :         poFeature->SetGeometryDirectly(poGeometry);
    1920             : 
    1921          57 :         bAdded = AddFeature(poLayer, poFeature);
    1922             :     }
    1923             : 
    1924          58 :     return bAdded;
    1925             : }
    1926             : 
    1927             : /************************************************************************/
    1928             : /*                           AddFeature                                 */
    1929             : /************************************************************************/
    1930             : 
    1931         196 : bool OGRGeoJSONReader::AddFeature(OGRGeoJSONLayer *poLayer,
    1932             :                                   OGRFeature *poFeature)
    1933             : {
    1934         196 :     if (poFeature == nullptr)
    1935           0 :         return false;
    1936             : 
    1937         196 :     poLayer->AddFeature(poFeature);
    1938         196 :     delete poFeature;
    1939             : 
    1940         196 :     return true;
    1941             : }
    1942             : 
    1943             : /************************************************************************/
    1944             : /*                           ReadGeometry                               */
    1945             : /************************************************************************/
    1946             : 
    1947        1272 : OGRGeometry *OGRGeoJSONBaseReader::ReadGeometry(json_object *poObj,
    1948             :                                                 OGRSpatialReference *poLayerSRS)
    1949             : {
    1950        1272 :     OGRGeometry *poGeometry = OGRGeoJSONReadGeometry(poObj, poLayerSRS);
    1951             : 
    1952             :     /* -------------------------------------------------------------------- */
    1953             :     /*      Wrap geometry with GeometryCollection as a common denominator.  */
    1954             :     /*      Sometimes a GeoJSON text may consist of objects of different    */
    1955             :     /*      geometry types. Users may request wrapping all geometries with  */
    1956             :     /*      OGRGeometryCollection type by using option                      */
    1957             :     /*      GEOMETRY_AS_COLLECTION=NO|YES (NO is default).                  */
    1958             :     /* -------------------------------------------------------------------- */
    1959        1272 :     if (nullptr != poGeometry)
    1960             :     {
    1961        1257 :         if (!bGeometryPreserve_ &&
    1962           0 :             wkbGeometryCollection != poGeometry->getGeometryType())
    1963             :         {
    1964           0 :             OGRGeometryCollection *poMetaGeometry = new OGRGeometryCollection();
    1965           0 :             poMetaGeometry->addGeometryDirectly(poGeometry);
    1966           0 :             return poMetaGeometry;
    1967             :         }
    1968             :     }
    1969             : 
    1970        1272 :     return poGeometry;
    1971             : }
    1972             : 
    1973             : /************************************************************************/
    1974             : /*                OGRGeoJSONReaderSetFieldNestedAttribute()             */
    1975             : /************************************************************************/
    1976             : 
    1977           2 : static void OGRGeoJSONReaderSetFieldNestedAttribute(OGRLayer *poLayer,
    1978             :                                                     OGRFeature *poFeature,
    1979             :                                                     const char *pszAttrPrefix,
    1980             :                                                     char chSeparator,
    1981             :                                                     json_object *poVal)
    1982             : {
    1983             :     json_object_iter it;
    1984           2 :     it.key = nullptr;
    1985           2 :     it.val = nullptr;
    1986           2 :     it.entry = nullptr;
    1987           6 :     json_object_object_foreachC(poVal, it)
    1988             :     {
    1989           4 :         const char szSeparator[2] = {chSeparator, '\0'};
    1990             :         const CPLString osAttrName(
    1991           8 :             CPLSPrintf("%s%s%s", pszAttrPrefix, szSeparator, it.key));
    1992           8 :         if (it.val != nullptr &&
    1993           4 :             json_object_get_type(it.val) == json_type_object)
    1994             :         {
    1995           0 :             OGRGeoJSONReaderSetFieldNestedAttribute(
    1996             :                 poLayer, poFeature, osAttrName, chSeparator, it.val);
    1997             :         }
    1998             :         else
    1999             :         {
    2000             :             const int nField =
    2001           4 :                 poFeature->GetDefnRef()->GetFieldIndexCaseSensitive(osAttrName);
    2002           4 :             OGRGeoJSONReaderSetField(poLayer, poFeature, nField, osAttrName,
    2003             :                                      it.val, false, 0);
    2004             :         }
    2005             :     }
    2006           2 : }
    2007             : 
    2008             : /************************************************************************/
    2009             : /*                   OGRGeoJSONReaderSetField()                         */
    2010             : /************************************************************************/
    2011             : 
    2012        6324 : void OGRGeoJSONReaderSetField(OGRLayer *poLayer, OGRFeature *poFeature,
    2013             :                               int nField, const char *pszAttrPrefix,
    2014             :                               json_object *poVal, bool bFlattenNestedAttributes,
    2015             :                               char chNestedAttributeSeparator)
    2016             : {
    2017        6328 :     if (bFlattenNestedAttributes && poVal != nullptr &&
    2018           4 :         json_object_get_type(poVal) == json_type_object)
    2019             :     {
    2020           2 :         OGRGeoJSONReaderSetFieldNestedAttribute(
    2021             :             poLayer, poFeature, pszAttrPrefix, chNestedAttributeSeparator,
    2022             :             poVal);
    2023           2 :         return;
    2024             :     }
    2025        6322 :     if (nField < 0)
    2026           0 :         return;
    2027             : 
    2028        6322 :     OGRFieldDefn *poFieldDefn = poFeature->GetFieldDefnRef(nField);
    2029        6322 :     CPLAssert(nullptr != poFieldDefn);
    2030        6322 :     OGRFieldType eType = poFieldDefn->GetType();
    2031             : 
    2032        6322 :     if (poVal == nullptr)
    2033             :     {
    2034          91 :         poFeature->SetFieldNull(nField);
    2035             :     }
    2036        6231 :     else if (OFTInteger == eType)
    2037             :     {
    2038        1739 :         poFeature->SetField(nField, json_object_get_int(poVal));
    2039             : 
    2040             :         // Check if FID available and set correct value.
    2041        1739 :         if (EQUAL(poFieldDefn->GetNameRef(), poLayer->GetFIDColumn()))
    2042         145 :             poFeature->SetFID(json_object_get_int(poVal));
    2043             :     }
    2044        4492 :     else if (OFTInteger64 == eType)
    2045             :     {
    2046          26 :         poFeature->SetField(nField, (GIntBig)json_object_get_int64(poVal));
    2047             : 
    2048             :         // Check if FID available and set correct value.
    2049          26 :         if (EQUAL(poFieldDefn->GetNameRef(), poLayer->GetFIDColumn()))
    2050           0 :             poFeature->SetFID(
    2051           0 :                 static_cast<GIntBig>(json_object_get_int64(poVal)));
    2052             :     }
    2053        4466 :     else if (OFTReal == eType)
    2054             :     {
    2055        1569 :         poFeature->SetField(nField, json_object_get_double(poVal));
    2056             :     }
    2057        2897 :     else if (OFTIntegerList == eType)
    2058             :     {
    2059          85 :         const enum json_type eJSonType(json_object_get_type(poVal));
    2060          85 :         if (eJSonType == json_type_array)
    2061             :         {
    2062          77 :             const auto nLength = json_object_array_length(poVal);
    2063          77 :             int *panVal = static_cast<int *>(CPLMalloc(sizeof(int) * nLength));
    2064         238 :             for (auto i = decltype(nLength){0}; i < nLength; i++)
    2065             :             {
    2066         161 :                 json_object *poRow = json_object_array_get_idx(poVal, i);
    2067         161 :                 panVal[i] = json_object_get_int(poRow);
    2068             :             }
    2069          77 :             poFeature->SetField(nField, static_cast<int>(nLength), panVal);
    2070          77 :             CPLFree(panVal);
    2071             :         }
    2072           8 :         else if (eJSonType == json_type_boolean || eJSonType == json_type_int)
    2073             :         {
    2074           8 :             poFeature->SetField(nField, json_object_get_int(poVal));
    2075             :         }
    2076             :     }
    2077        2812 :     else if (OFTInteger64List == eType)
    2078             :     {
    2079          77 :         const enum json_type eJSonType(json_object_get_type(poVal));
    2080          77 :         if (eJSonType == json_type_array)
    2081             :         {
    2082          67 :             const auto nLength = json_object_array_length(poVal);
    2083             :             GIntBig *panVal =
    2084          67 :                 static_cast<GIntBig *>(CPLMalloc(sizeof(GIntBig) * nLength));
    2085         149 :             for (auto i = decltype(nLength){0}; i < nLength; i++)
    2086             :             {
    2087          82 :                 json_object *poRow = json_object_array_get_idx(poVal, i);
    2088          82 :                 panVal[i] = static_cast<GIntBig>(json_object_get_int64(poRow));
    2089             :             }
    2090          67 :             poFeature->SetField(nField, static_cast<int>(nLength), panVal);
    2091          67 :             CPLFree(panVal);
    2092             :         }
    2093          10 :         else if (eJSonType == json_type_boolean || eJSonType == json_type_int)
    2094             :         {
    2095          10 :             poFeature->SetField(
    2096          10 :                 nField, static_cast<GIntBig>(json_object_get_int64(poVal)));
    2097             :         }
    2098             :     }
    2099        2735 :     else if (OFTRealList == eType)
    2100             :     {
    2101          96 :         const enum json_type eJSonType(json_object_get_type(poVal));
    2102          96 :         if (eJSonType == json_type_array)
    2103             :         {
    2104          82 :             const auto nLength = json_object_array_length(poVal);
    2105             :             double *padfVal =
    2106          82 :                 static_cast<double *>(CPLMalloc(sizeof(double) * nLength));
    2107         179 :             for (auto i = decltype(nLength){0}; i < nLength; i++)
    2108             :             {
    2109          97 :                 json_object *poRow = json_object_array_get_idx(poVal, i);
    2110          97 :                 padfVal[i] = json_object_get_double(poRow);
    2111             :             }
    2112          82 :             poFeature->SetField(nField, static_cast<int>(nLength), padfVal);
    2113          82 :             CPLFree(padfVal);
    2114             :         }
    2115          14 :         else if (eJSonType == json_type_boolean || eJSonType == json_type_int ||
    2116             :                  eJSonType == json_type_double)
    2117             :         {
    2118          14 :             poFeature->SetField(nField, json_object_get_double(poVal));
    2119             :         }
    2120             :     }
    2121        2639 :     else if (OFTStringList == eType)
    2122             :     {
    2123         101 :         const enum json_type eJSonType(json_object_get_type(poVal));
    2124         101 :         if (eJSonType == json_type_array)
    2125             :         {
    2126          91 :             auto nLength = json_object_array_length(poVal);
    2127             :             char **papszVal =
    2128          91 :                 (char **)CPLMalloc(sizeof(char *) * (nLength + 1));
    2129          91 :             decltype(nLength) i = 0;  // Used after for.
    2130         199 :             for (; i < nLength; i++)
    2131             :             {
    2132         108 :                 json_object *poRow = json_object_array_get_idx(poVal, i);
    2133         108 :                 const char *pszVal = json_object_get_string(poRow);
    2134         108 :                 if (pszVal == nullptr)
    2135           0 :                     break;
    2136         108 :                 papszVal[i] = CPLStrdup(pszVal);
    2137             :             }
    2138          91 :             papszVal[i] = nullptr;
    2139          91 :             poFeature->SetField(nField, papszVal);
    2140          91 :             CSLDestroy(papszVal);
    2141             :         }
    2142             :         else
    2143             :         {
    2144          10 :             poFeature->SetField(nField, json_object_get_string(poVal));
    2145             :         }
    2146             :     }
    2147             :     else
    2148             :     {
    2149        2538 :         poFeature->SetField(nField, json_object_get_string(poVal));
    2150             :     }
    2151             : }
    2152             : 
    2153             : /************************************************************************/
    2154             : /*                           ReadFeature()                              */
    2155             : /************************************************************************/
    2156             : 
    2157        1580 : OGRFeature *OGRGeoJSONBaseReader::ReadFeature(OGRLayer *poLayer,
    2158             :                                               json_object *poObj,
    2159             :                                               const char *pszSerializedObj)
    2160             : {
    2161        1580 :     CPLAssert(nullptr != poObj);
    2162             : 
    2163        1580 :     OGRFeatureDefn *poFDefn = poLayer->GetLayerDefn();
    2164        1580 :     OGRFeature *poFeature = new OGRFeature(poFDefn);
    2165             : 
    2166        1580 :     if (bStoreNativeData_)
    2167             :     {
    2168         430 :         poFeature->SetNativeData(pszSerializedObj
    2169             :                                      ? pszSerializedObj
    2170          10 :                                      : json_object_to_json_string(poObj));
    2171         420 :         poFeature->SetNativeMediaType("application/vnd.geo+json");
    2172             :     }
    2173             : 
    2174             :     /* -------------------------------------------------------------------- */
    2175             :     /*      Translate GeoJSON "properties" object to feature attributes.    */
    2176             :     /* -------------------------------------------------------------------- */
    2177        1580 :     CPLAssert(nullptr != poFeature);
    2178             : 
    2179        1580 :     json_object *poObjProps = OGRGeoJSONFindMemberByName(poObj, "properties");
    2180        3116 :     if (!bAttributesSkip_ && nullptr != poObjProps &&
    2181        1536 :         json_object_get_type(poObjProps) == json_type_object)
    2182             :     {
    2183        1535 :         if (bIsGeocouchSpatiallistFormat)
    2184             :         {
    2185           3 :             json_object *poId = CPL_json_object_object_get(poObjProps, "_id");
    2186           6 :             if (poId != nullptr &&
    2187           3 :                 json_object_get_type(poId) == json_type_string)
    2188           3 :                 poFeature->SetField("_id", json_object_get_string(poId));
    2189             : 
    2190           3 :             json_object *poRev = CPL_json_object_object_get(poObjProps, "_rev");
    2191           6 :             if (poRev != nullptr &&
    2192           3 :                 json_object_get_type(poRev) == json_type_string)
    2193             :             {
    2194           3 :                 poFeature->SetField("_rev", json_object_get_string(poRev));
    2195             :             }
    2196             : 
    2197           3 :             poObjProps = CPL_json_object_object_get(poObjProps, "properties");
    2198           6 :             if (nullptr == poObjProps ||
    2199           3 :                 json_object_get_type(poObjProps) != json_type_object)
    2200             :             {
    2201           0 :                 return poFeature;
    2202             :             }
    2203             :         }
    2204             : 
    2205             :         json_object_iter it;
    2206        1535 :         it.key = nullptr;
    2207        1535 :         it.val = nullptr;
    2208        1535 :         it.entry = nullptr;
    2209        6304 :         json_object_object_foreachC(poObjProps, it)
    2210             :         {
    2211        4769 :             const int nField = poFDefn->GetFieldIndexCaseSensitive(it.key);
    2212        4777 :             if (nField < 0 &&
    2213           8 :                 !(bFlattenNestedAttributes_ && it.val != nullptr &&
    2214           2 :                   json_object_get_type(it.val) == json_type_object))
    2215             :             {
    2216           6 :                 CPLDebug("GeoJSON", "Cannot find field %s", it.key);
    2217             :             }
    2218             :             else
    2219             :             {
    2220        4763 :                 OGRGeoJSONReaderSetField(poLayer, poFeature, nField, it.key,
    2221        4763 :                                          it.val, bFlattenNestedAttributes_,
    2222        4763 :                                          chNestedAttributeSeparator_);
    2223             :             }
    2224             :         }
    2225             :     }
    2226             : 
    2227             :     // Whether/how we should deal with foreign members
    2228        1580 :     if (!bAttributesSkip_ &&
    2229        1580 :         eForeignMemberProcessing_ != ForeignMemberProcessing::NONE)
    2230             :     {
    2231             :         json_object_iter it;
    2232          66 :         it.key = nullptr;
    2233          66 :         it.val = nullptr;
    2234          66 :         it.entry = nullptr;
    2235         274 :         json_object_object_foreachC(poObj, it)
    2236             :         {
    2237         457 :             if (eForeignMemberProcessing_ == ForeignMemberProcessing::STAC &&
    2238         211 :                 strcmp(it.key, "assets") == 0 &&
    2239           3 :                 json_object_get_type(it.val) == json_type_object)
    2240             :             {
    2241             :                 json_object_iter it2;
    2242           3 :                 it2.key = nullptr;
    2243           3 :                 it2.val = nullptr;
    2244           3 :                 it2.entry = nullptr;
    2245           6 :                 json_object_object_foreachC(it.val, it2)
    2246             :                 {
    2247           3 :                     if (json_object_get_type(it2.val) == json_type_object)
    2248             :                     {
    2249             :                         json_object_iter it3;
    2250           3 :                         it3.key = nullptr;
    2251           3 :                         it3.val = nullptr;
    2252           3 :                         it3.entry = nullptr;
    2253          18 :                         json_object_object_foreachC(it2.val, it3)
    2254             :                         {
    2255             :                             const std::string osFieldName =
    2256          30 :                                 std::string("assets.")
    2257          15 :                                     .append(it2.key)
    2258          15 :                                     .append(".")
    2259          15 :                                     .append(it3.key)
    2260          45 :                                     .c_str();
    2261             :                             const int nField =
    2262          15 :                                 poFDefn->GetFieldIndexCaseSensitive(
    2263             :                                     osFieldName.c_str());
    2264          15 :                             if (nField < 0 && !(bFlattenNestedAttributes_ &&
    2265           0 :                                                 it3.val != nullptr &&
    2266           0 :                                                 json_object_get_type(it3.val) ==
    2267             :                                                     json_type_object))
    2268             :                             {
    2269           0 :                                 CPLDebug("GeoJSON", "Cannot find field %s",
    2270             :                                          osFieldName.c_str());
    2271             :                             }
    2272             :                             else
    2273             :                             {
    2274          15 :                                 OGRGeoJSONReaderSetField(
    2275             :                                     poLayer, poFeature, nField,
    2276             :                                     osFieldName.c_str(), it3.val,
    2277          15 :                                     bFlattenNestedAttributes_,
    2278          15 :                                     chNestedAttributeSeparator_);
    2279             :                             }
    2280             :                         }
    2281             :                     }
    2282             :                 }
    2283             :             }
    2284         205 :             else if (strcmp(it.key, "type") != 0 && strcmp(it.key, "id") != 0 &&
    2285         134 :                      strcmp(it.key, "geometry") != 0 &&
    2286          68 :                      strcmp(it.key, "bbox") != 0 &&
    2287          63 :                      strcmp(it.key, "properties") != 0)
    2288             :             {
    2289          28 :                 const int nField = poFDefn->GetFieldIndexCaseSensitive(it.key);
    2290          28 :                 if (nField < 0 &&
    2291           0 :                     !(bFlattenNestedAttributes_ && it.val != nullptr &&
    2292           0 :                       json_object_get_type(it.val) == json_type_object))
    2293             :                 {
    2294           0 :                     CPLDebug("GeoJSON", "Cannot find field %s", it.key);
    2295             :                 }
    2296             :                 else
    2297             :                 {
    2298          28 :                     OGRGeoJSONReaderSetField(poLayer, poFeature, nField, it.key,
    2299          28 :                                              it.val, bFlattenNestedAttributes_,
    2300          28 :                                              chNestedAttributeSeparator_);
    2301             :                 }
    2302             :             }
    2303             :         }
    2304             :     }
    2305             : 
    2306        1580 :     if (!bAttributesSkip_ && nullptr == poObjProps)
    2307             :     {
    2308             :         json_object_iter it;
    2309          44 :         it.key = nullptr;
    2310          44 :         it.val = nullptr;
    2311          44 :         it.entry = nullptr;
    2312         149 :         json_object_object_foreachC(poObj, it)
    2313             :         {
    2314         105 :             const int nFldIndex = poFDefn->GetFieldIndexCaseSensitive(it.key);
    2315         105 :             if (nFldIndex >= 0)
    2316             :             {
    2317           4 :                 if (it.val)
    2318           4 :                     poFeature->SetField(nFldIndex,
    2319             :                                         json_object_get_string(it.val));
    2320             :                 else
    2321           0 :                     poFeature->SetFieldNull(nFldIndex);
    2322             :             }
    2323             :         }
    2324             :     }
    2325             : 
    2326             :     /* -------------------------------------------------------------------- */
    2327             :     /*      Try to use feature-level ID if available                        */
    2328             :     /*      and of integral type. Otherwise, leave unset (-1) then index    */
    2329             :     /*      in features sequence will be used as FID.                       */
    2330             :     /* -------------------------------------------------------------------- */
    2331        1580 :     json_object *poObjId = OGRGeoJSONFindMemberByName(poObj, "id");
    2332        1580 :     if (nullptr != poObjId && bFeatureLevelIdAsFID_)
    2333             :     {
    2334          57 :         poFeature->SetFID(static_cast<GIntBig>(json_object_get_int64(poObjId)));
    2335             :     }
    2336             : 
    2337             :     /* -------------------------------------------------------------------- */
    2338             :     /*      Handle the case where the special id is in a regular field.     */
    2339             :     /* -------------------------------------------------------------------- */
    2340        1523 :     else if (nullptr != poObjId)
    2341             :     {
    2342          50 :         const int nIdx = poFDefn->GetFieldIndexCaseSensitive("id");
    2343          50 :         if (nIdx >= 0 && !poFeature->IsFieldSet(nIdx))
    2344             :         {
    2345          46 :             poFeature->SetField(nIdx, json_object_get_string(poObjId));
    2346             :         }
    2347             :     }
    2348             : 
    2349             :     /* -------------------------------------------------------------------- */
    2350             :     /*      Translate geometry sub-object of GeoJSON Feature.               */
    2351             :     /* -------------------------------------------------------------------- */
    2352        1580 :     json_object *poObjGeom = nullptr;
    2353        1580 :     json_object *poTmp = poObj;
    2354             :     json_object_iter it;
    2355        1580 :     it.key = nullptr;
    2356        1580 :     it.val = nullptr;
    2357        1580 :     it.entry = nullptr;
    2358        6084 :     json_object_object_foreachC(poTmp, it)
    2359             :     {
    2360        4847 :         if (EQUAL(it.key, "geometry"))
    2361             :         {
    2362        1553 :             if (it.val != nullptr)
    2363        1210 :                 poObjGeom = it.val;
    2364             :             // Done.  They had 'geometry':null.
    2365             :             else
    2366         343 :                 return poFeature;
    2367             :         }
    2368             :     }
    2369             : 
    2370        1237 :     if (nullptr != poObjGeom)
    2371             :     {
    2372             :         // NOTE: If geometry can not be parsed or read correctly
    2373             :         //       then NULL geometry is assigned to a feature and
    2374             :         //       geometry type for layer is classified as wkbUnknown.
    2375             :         OGRGeometry *poGeometry =
    2376        1210 :             ReadGeometry(poObjGeom, poLayer->GetSpatialRef());
    2377        1210 :         if (nullptr != poGeometry)
    2378             :         {
    2379        1196 :             poFeature->SetGeometryDirectly(poGeometry);
    2380             :         }
    2381             :     }
    2382             :     else
    2383             :     {
    2384             :         static bool bWarned = false;
    2385          27 :         if (!bWarned)
    2386             :         {
    2387           1 :             bWarned = true;
    2388           1 :             CPLDebug(
    2389             :                 "GeoJSON",
    2390             :                 "Non conformant Feature object. Missing \'geometry\' member.");
    2391             :         }
    2392             :     }
    2393             : 
    2394        1237 :     return poFeature;
    2395             : }
    2396             : 
    2397             : /************************************************************************/
    2398             : /*                           Extent getters                             */
    2399             : /************************************************************************/
    2400             : 
    2401          38 : bool OGRGeoJSONBaseReader::ExtentRead() const
    2402             : {
    2403          38 :     return m_bExtentRead;
    2404             : }
    2405             : 
    2406          32 : OGREnvelope3D OGRGeoJSONBaseReader::GetExtent3D() const
    2407             : {
    2408          32 :     return m_oEnvelope3D;
    2409             : }
    2410             : 
    2411             : /************************************************************************/
    2412             : /*                           ReadFeatureCollection()                    */
    2413             : /************************************************************************/
    2414             : 
    2415          96 : void OGRGeoJSONReader::ReadFeatureCollection(OGRGeoJSONLayer *poLayer,
    2416             :                                              json_object *poObj)
    2417             : {
    2418          96 :     json_object *poObjFeatures = OGRGeoJSONFindMemberByName(poObj, "features");
    2419          96 :     if (nullptr == poObjFeatures)
    2420             :     {
    2421           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2422             :                  "Invalid FeatureCollection object. "
    2423             :                  "Missing \'features\' member.");
    2424           0 :         return;
    2425             :     }
    2426             : 
    2427          96 :     if (json_type_array == json_object_get_type(poObjFeatures))
    2428             :     {
    2429          96 :         const auto nFeatures = json_object_array_length(poObjFeatures);
    2430         213 :         for (auto i = decltype(nFeatures){0}; i < nFeatures; ++i)
    2431             :         {
    2432             :             json_object *poObjFeature =
    2433         117 :                 json_object_array_get_idx(poObjFeatures, i);
    2434         117 :             OGRFeature *poFeature = ReadFeature(poLayer, poObjFeature, nullptr);
    2435         117 :             AddFeature(poLayer, poFeature);
    2436             :         }
    2437             :     }
    2438             : 
    2439             :     // Collect top objects except 'type' and the 'features' array.
    2440          96 :     if (bStoreNativeData_)
    2441             :     {
    2442             :         json_object_iter it;
    2443           5 :         it.key = nullptr;
    2444           5 :         it.val = nullptr;
    2445           5 :         it.entry = nullptr;
    2446          10 :         CPLString osNativeData;
    2447          25 :         json_object_object_foreachC(poObj, it)
    2448             :         {
    2449          20 :             if (strcmp(it.key, "type") == 0 || strcmp(it.key, "features") == 0)
    2450             :             {
    2451          10 :                 continue;
    2452             :             }
    2453          10 :             if (osNativeData.empty())
    2454           3 :                 osNativeData = "{ ";
    2455             :             else
    2456           7 :                 osNativeData += ", ";
    2457          10 :             json_object *poKey = json_object_new_string(it.key);
    2458          10 :             osNativeData += json_object_to_json_string(poKey);
    2459          10 :             json_object_put(poKey);
    2460          10 :             osNativeData += ": ";
    2461          10 :             osNativeData += json_object_to_json_string(it.val);
    2462             :         }
    2463           5 :         if (osNativeData.empty())
    2464             :         {
    2465           2 :             osNativeData = "{ ";
    2466             :         }
    2467           5 :         osNativeData += " }";
    2468             : 
    2469           5 :         osNativeData = "NATIVE_DATA=" + osNativeData;
    2470             : 
    2471           5 :         char *apszMetadata[3] = {
    2472           5 :             const_cast<char *>(osNativeData.c_str()),
    2473             :             const_cast<char *>("NATIVE_MEDIA_TYPE=application/vnd.geo+json"),
    2474           5 :             nullptr};
    2475             : 
    2476           5 :         poLayer->SetMetadata(apszMetadata, "NATIVE_DATA");
    2477             :     }
    2478             : }
    2479             : 
    2480             : /************************************************************************/
    2481             : /*                          OGRGeoJSONGetExtent3D()                     */
    2482             : /************************************************************************/
    2483             : 
    2484        4472 : bool OGRGeoJSONGetExtent3D(json_object *poObj, OGREnvelope3D *poEnvelope)
    2485             : {
    2486        4472 :     if (!poEnvelope || !poObj)
    2487             :     {
    2488           0 :         return false;
    2489             :     }
    2490             : 
    2491             :     // poObjCoords can be an array of arrays, this lambda function will
    2492             :     // recursively parse the array
    2493        8944 :     std::function<bool(json_object *, OGREnvelope3D *)> fParseCoords;
    2494      194573 :     fParseCoords = [&fParseCoords](json_object *poObjCoordsIn,
    2495      190097 :                                    OGREnvelope3D *poEnvelopeIn) -> bool
    2496             :     {
    2497      194573 :         if (json_type_array == json_object_get_type(poObjCoordsIn))
    2498             :         {
    2499      194573 :             const auto nItems = json_object_array_length(poObjCoordsIn);
    2500             : 
    2501      194573 :             double dXVal = std::numeric_limits<double>::quiet_NaN();
    2502      194573 :             double dYVal = std::numeric_limits<double>::quiet_NaN();
    2503      194573 :             double dZVal = std::numeric_limits<double>::quiet_NaN();
    2504             : 
    2505      756331 :             for (auto i = decltype(nItems){0}; i < nItems; ++i)
    2506             :             {
    2507             : 
    2508             :                 // Get the i element
    2509             :                 json_object *poObjCoordsElement =
    2510      561775 :                     json_object_array_get_idx(poObjCoordsIn, i);
    2511             : 
    2512      561775 :                 const json_type eType{json_object_get_type(poObjCoordsElement)};
    2513             : 
    2514             :                 // if it is an array, recurse
    2515      561775 :                 if (json_type_array == eType)
    2516             :                 {
    2517      190097 :                     if (!fParseCoords(poObjCoordsElement, poEnvelopeIn))
    2518             :                     {
    2519           1 :                         return false;
    2520             :                     }
    2521             :                 }
    2522      371678 :                 else if (json_type_double == eType || json_type_int == eType)
    2523             :                 {
    2524      371662 :                     switch (i)
    2525             :                     {
    2526      185711 :                         case 0:
    2527             :                         {
    2528      185711 :                             dXVal = json_object_get_double(poObjCoordsElement);
    2529      185711 :                             break;
    2530             :                         }
    2531      185710 :                         case 1:
    2532             :                         {
    2533      185710 :                             dYVal = json_object_get_double(poObjCoordsElement);
    2534      185710 :                             break;
    2535             :                         }
    2536         241 :                         case 2:
    2537             :                         {
    2538         241 :                             dZVal = json_object_get_double(poObjCoordsElement);
    2539         241 :                             break;
    2540             :                         }
    2541           0 :                         default:
    2542           0 :                             return false;
    2543             :                     }
    2544             :                 }
    2545             :                 else
    2546             :                 {
    2547          16 :                     return false;
    2548             :                 }
    2549             :             }
    2550             : 
    2551      194556 :             if (!std::isnan(dXVal) && !std::isnan(dYVal))
    2552             :             {
    2553      185709 :                 if (std::isnan(dZVal))
    2554             :                 {
    2555             :                     static_cast<OGREnvelope *>(poEnvelopeIn)
    2556      185468 :                         ->Merge(dXVal, dYVal);
    2557             :                 }
    2558             :                 else
    2559             :                 {
    2560         241 :                     poEnvelopeIn->Merge(dXVal, dYVal, dZVal);
    2561             :                 }
    2562             :             }
    2563             : 
    2564      194556 :             return true;
    2565             :         }
    2566             :         else
    2567             :         {
    2568           0 :             return false;
    2569             :         }
    2570        4472 :     };
    2571             : 
    2572             :     // This function looks for "coordinates" and for "geometries" to handle
    2573             :     // geometry collections.  It will recurse on itself to handle nested geometry.
    2574        8944 :     std::function<bool(json_object *, OGREnvelope3D *)> fParseGeometry;
    2575        4494 :     fParseGeometry = [&fParseGeometry,
    2576             :                       &fParseCoords](json_object *poObjIn,
    2577        4498 :                                      OGREnvelope3D *poEnvelopeIn) -> bool
    2578             :     {
    2579             :         // Get the "coordinates" array from the JSON object
    2580             :         json_object *poObjCoords =
    2581        4494 :             OGRGeoJSONFindMemberByName(poObjIn, "coordinates");
    2582             : 
    2583             :         // Return if found and not an array
    2584        4494 :         if (poObjCoords && json_object_get_type(poObjCoords) != json_type_array)
    2585             :         {
    2586           0 :             return false;
    2587             :         }
    2588        4494 :         else if (poObjCoords)
    2589             :         {
    2590        4476 :             return fParseCoords(poObjCoords, poEnvelopeIn);
    2591             :         }
    2592             : 
    2593             :         // Try "geometries"
    2594          18 :         if (!poObjCoords)
    2595             :         {
    2596          18 :             poObjCoords = OGRGeoJSONFindMemberByName(poObjIn, "geometries");
    2597             :         }
    2598             : 
    2599             :         // Return if not found or not an array
    2600          28 :         if (!poObjCoords ||
    2601          10 :             json_object_get_type(poObjCoords) != json_type_array)
    2602             :         {
    2603           8 :             return false;
    2604             :         }
    2605             :         else
    2606             :         {
    2607             :             // Loop thgrough the geometries
    2608          10 :             const auto nItems = json_object_array_length(poObjCoords);
    2609          30 :             for (auto i = decltype(nItems){0}; i < nItems; ++i)
    2610             :             {
    2611             :                 json_object *poObjGeometry =
    2612          22 :                     json_object_array_get_idx(poObjCoords, i);
    2613             : 
    2614             :                 // Recurse
    2615          22 :                 if (!fParseGeometry(poObjGeometry, poEnvelopeIn))
    2616             :                 {
    2617           2 :                     return false;
    2618             :                 }
    2619             :             }
    2620           8 :             return true;
    2621             :         }
    2622        4472 :     };
    2623             : 
    2624        4472 :     return fParseGeometry(poObj, poEnvelope);
    2625             : }

Generated by: LCOV version 1.14