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

Generated by: LCOV version 1.14