LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/geojson - ogrgeojsonreader.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1465 1572 93.2 %
Date: 2024-05-03 15:49:35 Functions: 64 66 97.0 %

          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             :  * Permission is hereby granted, free of charge, to any person obtaining a
      12             :  * copy of this software and associated documentation files (the "Software"),
      13             :  * to deal in the Software without restriction, including without limitation
      14             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      15             :  * and/or sell copies of the Software, and to permit persons to whom the
      16             :  * Software is furnished to do so, subject to the following conditions:
      17             :  *
      18             :  * The above copyright notice and this permission notice shall be included
      19             :  * in all copies or substantial portions of the Software.
      20             :  *
      21             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      22             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      23             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      24             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      25             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      26             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      27             :  * DEALINGS IN THE SOFTWARE.
      28             :  ****************************************************************************/
      29             : 
      30             : #include "ogrgeojsonreader.h"
      31             : #include "ogrgeojsonutils.h"
      32             : #include "ogr_geojson.h"
      33             : #include "ogrjsoncollectionstreamingparser.h"
      34             : #include "ogr_api.h"
      35             : 
      36             : #include <limits>
      37             : #include <set>
      38             : #include <functional>
      39             : 
      40             : /************************************************************************/
      41             : /*                      OGRGeoJSONReaderStreamingParser                 */
      42             : /************************************************************************/
      43             : 
      44             : class OGRGeoJSONReaderStreamingParser final
      45             :     : public OGRJSONCollectionStreamingParser
      46             : {
      47             :     OGRGeoJSONReader &m_oReader;
      48             :     OGRGeoJSONLayer *m_poLayer = nullptr;
      49             : 
      50             :     std::vector<OGRFeature *> m_apoFeatures{};
      51             :     size_t m_nCurFeatureIdx = 0;
      52             :     bool m_bOriginalIdModifiedEmitted = false;
      53             :     std::set<GIntBig> m_oSetUsedFIDs{};
      54             : 
      55             :     std::map<std::string, int> m_oMapFieldNameToIdx{};
      56             :     std::vector<std::unique_ptr<OGRFieldDefn>> m_apoFieldDefn{};
      57             :     gdal::DirectedAcyclicGraph<int, std::string> m_dag{};
      58             : 
      59             :     void AnalyzeFeature();
      60             : 
      61             :     CPL_DISALLOW_COPY_ASSIGN(OGRGeoJSONReaderStreamingParser)
      62             : 
      63             :   protected:
      64             :     void GotFeature(json_object *poObj, bool bFirstPass,
      65             :                     const std::string &osJson) override;
      66             :     void TooComplex() override;
      67             : 
      68             :   public:
      69             :     OGRGeoJSONReaderStreamingParser(OGRGeoJSONReader &oReader,
      70             :                                     OGRGeoJSONLayer *poLayer, bool bFirstPass,
      71             :                                     bool bStoreNativeData);
      72             :     ~OGRGeoJSONReaderStreamingParser();
      73             : 
      74             :     void FinalizeLayerDefn();
      75             : 
      76             :     OGRFeature *GetNextFeature();
      77             : 
      78          84 :     inline bool GetOriginalIdModifiedEmitted() const
      79             :     {
      80          84 :         return m_bOriginalIdModifiedEmitted;
      81             :     }
      82             : 
      83         236 :     inline void SetOriginalIdModifiedEmitted(bool b)
      84             :     {
      85         236 :         m_bOriginalIdModifiedEmitted = b;
      86         236 :     }
      87             : };
      88             : 
      89             : /************************************************************************/
      90             : /*                        OGRGeoJSONBaseReader()                        */
      91             : /************************************************************************/
      92             : 
      93             : OGRGeoJSONBaseReader::OGRGeoJSONBaseReader() = default;
      94             : 
      95             : /************************************************************************/
      96             : /*                           SetPreserveGeometryType                    */
      97             : /************************************************************************/
      98             : 
      99           0 : void OGRGeoJSONBaseReader::SetPreserveGeometryType(bool bPreserve)
     100             : {
     101           0 :     bGeometryPreserve_ = bPreserve;
     102           0 : }
     103             : 
     104             : /************************************************************************/
     105             : /*                           SetSkipAttributes                          */
     106             : /************************************************************************/
     107             : 
     108           0 : void OGRGeoJSONBaseReader::SetSkipAttributes(bool bSkip)
     109             : {
     110           0 :     bAttributesSkip_ = bSkip;
     111           0 : }
     112             : 
     113             : /************************************************************************/
     114             : /*                         SetFlattenNestedAttributes                   */
     115             : /************************************************************************/
     116             : 
     117         407 : void OGRGeoJSONBaseReader::SetFlattenNestedAttributes(bool bFlatten,
     118             :                                                       char chSeparator)
     119             : {
     120         407 :     bFlattenNestedAttributes_ = bFlatten;
     121         407 :     chNestedAttributeSeparator_ = chSeparator;
     122         407 : }
     123             : 
     124             : /************************************************************************/
     125             : /*                           SetStoreNativeData                         */
     126             : /************************************************************************/
     127             : 
     128         407 : void OGRGeoJSONBaseReader::SetStoreNativeData(bool bStoreNativeData)
     129             : {
     130         407 :     bStoreNativeData_ = bStoreNativeData;
     131         407 : }
     132             : 
     133             : /************************************************************************/
     134             : /*                           SetArrayAsString                           */
     135             : /************************************************************************/
     136             : 
     137         407 : void OGRGeoJSONBaseReader::SetArrayAsString(bool bArrayAsString)
     138             : {
     139         407 :     bArrayAsString_ = bArrayAsString;
     140         407 : }
     141             : 
     142             : /************************************************************************/
     143             : /*                           SetDateAsString                           */
     144             : /************************************************************************/
     145             : 
     146         407 : void OGRGeoJSONBaseReader::SetDateAsString(bool bDateAsString)
     147             : {
     148         407 :     bDateAsString_ = bDateAsString;
     149         407 : }
     150             : 
     151             : /************************************************************************/
     152             : /*                           OGRGeoJSONReader                           */
     153             : /************************************************************************/
     154             : 
     155         407 : OGRGeoJSONReader::OGRGeoJSONReader()
     156             :     : poGJObject_(nullptr), poStreamingParser_(nullptr), bFirstSeg_(false),
     157             :       bJSonPLikeWrapper_(false), fp_(nullptr), bCanEasilyAppend_(false),
     158             :       bFCHasBBOX_(false), nBufferSize_(0), pabyBuffer_(nullptr),
     159         407 :       nTotalFeatureCount_(0), nTotalOGRFeatureMemEstimate_(0)
     160             : {
     161         407 : }
     162             : 
     163             : /************************************************************************/
     164             : /*                          ~OGRGeoJSONReader                           */
     165             : /************************************************************************/
     166             : 
     167         407 : OGRGeoJSONReader::~OGRGeoJSONReader()
     168             : {
     169         407 :     if (nullptr != poGJObject_)
     170             :     {
     171         404 :         json_object_put(poGJObject_);
     172             :     }
     173         407 :     if (fp_ != nullptr)
     174             :     {
     175         241 :         VSIFCloseL(fp_);
     176             :     }
     177         407 :     delete poStreamingParser_;
     178         407 :     CPLFree(pabyBuffer_);
     179             : 
     180         407 :     poGJObject_ = nullptr;
     181         407 : }
     182             : 
     183             : /************************************************************************/
     184             : /*                           Parse                                      */
     185             : /************************************************************************/
     186             : 
     187         163 : OGRErr OGRGeoJSONReader::Parse(const char *pszText)
     188             : {
     189         163 :     if (nullptr != pszText)
     190             :     {
     191             :         // Skip UTF-8 BOM (#5630).
     192         163 :         const GByte *pabyData = (const GByte *)pszText;
     193         163 :         if (pabyData[0] == 0xEF && pabyData[1] == 0xBB && pabyData[2] == 0xBF)
     194             :         {
     195           1 :             CPLDebug("GeoJSON", "Skip UTF-8 BOM");
     196           1 :             pszText += 3;
     197             :         }
     198             : 
     199         163 :         if (poGJObject_ != nullptr)
     200             :         {
     201           0 :             json_object_put(poGJObject_);
     202           0 :             poGJObject_ = nullptr;
     203             :         }
     204             : 
     205             :         // JSON tree is shared for while lifetime of the reader object
     206             :         // and will be released in the destructor.
     207         163 :         if (!OGRJSonParse(pszText, &poGJObject_))
     208           0 :             return OGRERR_CORRUPT_DATA;
     209             :     }
     210             : 
     211         163 :     return OGRERR_NONE;
     212             : }
     213             : 
     214             : /************************************************************************/
     215             : /*                           ReadLayers                                 */
     216             : /************************************************************************/
     217             : 
     218         163 : void OGRGeoJSONReader::ReadLayers(OGRGeoJSONDataSource *poDS)
     219             : {
     220         163 :     if (nullptr == poGJObject_)
     221             :     {
     222           0 :         CPLDebug("GeoJSON",
     223             :                  "Missing parsed GeoJSON data. Forgot to call Parse()?");
     224           0 :         return;
     225             :     }
     226             : 
     227         163 :     ReadLayer(poDS, nullptr, poGJObject_);
     228             : }
     229             : 
     230             : /************************************************************************/
     231             : /*           OGRGeoJSONReaderStreamingParserGetMaxObjectSize()          */
     232             : /************************************************************************/
     233             : 
     234         480 : static size_t OGRGeoJSONReaderStreamingParserGetMaxObjectSize()
     235             : {
     236             :     const double dfTmp =
     237         480 :         CPLAtof(CPLGetConfigOption("OGR_GEOJSON_MAX_OBJ_SIZE", "200"));
     238         480 :     return dfTmp > 0 ? static_cast<size_t>(dfTmp * 1024 * 1024) : 0;
     239             : }
     240             : 
     241             : /************************************************************************/
     242             : /*                     OGRGeoJSONReaderStreamingParser()                */
     243             : /************************************************************************/
     244             : 
     245         480 : OGRGeoJSONReaderStreamingParser::OGRGeoJSONReaderStreamingParser(
     246             :     OGRGeoJSONReader &oReader, OGRGeoJSONLayer *poLayer, bool bFirstPass,
     247         480 :     bool bStoreNativeData)
     248             :     : OGRJSONCollectionStreamingParser(
     249             :           bFirstPass, bStoreNativeData,
     250             :           OGRGeoJSONReaderStreamingParserGetMaxObjectSize()),
     251         480 :       m_oReader(oReader), m_poLayer(poLayer)
     252             : {
     253         480 : }
     254             : 
     255             : /************************************************************************/
     256             : /*                   ~OGRGeoJSONReaderStreamingParser()                 */
     257             : /************************************************************************/
     258             : 
     259         711 : OGRGeoJSONReaderStreamingParser::~OGRGeoJSONReaderStreamingParser()
     260             : {
     261         672 :     for (size_t i = 0; i < m_apoFeatures.size(); i++)
     262         192 :         delete m_apoFeatures[i];
     263         711 : }
     264             : 
     265             : /************************************************************************/
     266             : /*                          GetNextFeature()                            */
     267             : /************************************************************************/
     268             : 
     269        1411 : OGRFeature *OGRGeoJSONReaderStreamingParser::GetNextFeature()
     270             : {
     271        1411 :     if (m_nCurFeatureIdx < m_apoFeatures.size())
     272             :     {
     273         806 :         OGRFeature *poFeat = m_apoFeatures[m_nCurFeatureIdx];
     274         806 :         m_apoFeatures[m_nCurFeatureIdx] = nullptr;
     275         806 :         m_nCurFeatureIdx++;
     276         806 :         return poFeat;
     277             :     }
     278         605 :     m_nCurFeatureIdx = 0;
     279         605 :     m_apoFeatures.clear();
     280         605 :     return nullptr;
     281             : }
     282             : 
     283             : /************************************************************************/
     284             : /*                          GotFeature()                                */
     285             : /************************************************************************/
     286             : 
     287        3708 : void OGRGeoJSONReaderStreamingParser::GotFeature(json_object *poObj,
     288             :                                                  bool bFirstPass,
     289             :                                                  const std::string &osJson)
     290             : {
     291        3708 :     if (bFirstPass)
     292             :     {
     293        2830 :         if (!m_oReader.GenerateFeatureDefn(m_oMapFieldNameToIdx, m_apoFieldDefn,
     294        2830 :                                            m_dag, m_poLayer, poObj))
     295             :         {
     296             :         }
     297        2830 :         m_poLayer->IncFeatureCount();
     298             :     }
     299             :     else
     300             :     {
     301             :         OGRFeature *poFeat =
     302         878 :             m_oReader.ReadFeature(m_poLayer, poObj, osJson.c_str());
     303         878 :         if (poFeat)
     304             :         {
     305         878 :             GIntBig nFID = poFeat->GetFID();
     306         878 :             if (nFID == OGRNullFID)
     307             :             {
     308         711 :                 nFID = static_cast<GIntBig>(m_oSetUsedFIDs.size());
     309         711 :                 while (m_oSetUsedFIDs.find(nFID) != m_oSetUsedFIDs.end())
     310             :                 {
     311           0 :                     ++nFID;
     312             :                 }
     313             :             }
     314         167 :             else if (m_oSetUsedFIDs.find(nFID) != m_oSetUsedFIDs.end())
     315             :             {
     316          33 :                 if (!m_bOriginalIdModifiedEmitted)
     317             :                 {
     318           2 :                     CPLError(CE_Warning, CPLE_AppDefined,
     319             :                              "Several features with id = " CPL_FRMT_GIB " have "
     320             :                              "been found. Altering it to be unique. "
     321             :                              "This warning will not be emitted anymore for "
     322             :                              "this layer",
     323             :                              nFID);
     324           2 :                     m_bOriginalIdModifiedEmitted = true;
     325             :                 }
     326          33 :                 nFID = static_cast<GIntBig>(m_oSetUsedFIDs.size());
     327          33 :                 while (m_oSetUsedFIDs.find(nFID) != m_oSetUsedFIDs.end())
     328             :                 {
     329           0 :                     ++nFID;
     330             :                 }
     331             :             }
     332         878 :             m_oSetUsedFIDs.insert(nFID);
     333         878 :             poFeat->SetFID(nFID);
     334             : 
     335         878 :             m_apoFeatures.push_back(poFeat);
     336             :         }
     337             :     }
     338        3708 : }
     339             : 
     340             : /************************************************************************/
     341             : /*                         FinalizeLayerDefn()                          */
     342             : /************************************************************************/
     343             : 
     344         241 : void OGRGeoJSONReaderStreamingParser::FinalizeLayerDefn()
     345             : {
     346         241 :     OGRFeatureDefn *poDefn = m_poLayer->GetLayerDefn();
     347         482 :     auto oTemporaryUnsealer(poDefn->GetTemporaryUnsealer());
     348         482 :     const auto sortedFields = m_dag.getTopologicalOrdering();
     349         241 :     CPLAssert(sortedFields.size() == m_apoFieldDefn.size());
     350         908 :     for (int idx : sortedFields)
     351             :     {
     352         667 :         poDefn->AddFieldDefn(m_apoFieldDefn[idx].get());
     353             :     }
     354         241 :     m_dag = gdal::DirectedAcyclicGraph<int, std::string>();
     355         241 :     m_oMapFieldNameToIdx.clear();
     356         241 :     m_apoFieldDefn.clear();
     357         241 : }
     358             : 
     359             : /************************************************************************/
     360             : /*                            TooComplex()                              */
     361             : /************************************************************************/
     362             : 
     363       24885 : void OGRGeoJSONReaderStreamingParser::TooComplex()
     364             : {
     365       24885 :     if (!ExceptionOccurred())
     366           1 :         EmitException("GeoJSON object too complex/large. You may define the "
     367             :                       "OGR_GEOJSON_MAX_OBJ_SIZE configuration option to "
     368             :                       "a value in megabytes to allow "
     369             :                       "for larger features, or 0 to remove any size limit.");
     370       24885 : }
     371             : 
     372             : /************************************************************************/
     373             : /*                       SetCoordinatePrecision()                       */
     374             : /************************************************************************/
     375             : 
     376         332 : static void SetCoordinatePrecision(json_object *poRootObj,
     377             :                                    OGRGeoJSONLayer *poLayer)
     378             : {
     379         332 :     if (poLayer->GetLayerDefn()->GetGeomType() != wkbNone)
     380             :     {
     381         664 :         OGRGeoJSONWriteOptions options;
     382             : 
     383             :         json_object *poXYRes =
     384         332 :             CPL_json_object_object_get(poRootObj, "xy_coordinate_resolution");
     385         332 :         if (poXYRes && (json_object_get_type(poXYRes) == json_type_double ||
     386           0 :                         json_object_get_type(poXYRes) == json_type_int))
     387             :         {
     388           8 :             auto poGeomFieldDefn = poLayer->GetLayerDefn()->GetGeomFieldDefn(0);
     389             :             OGRGeomCoordinatePrecision oCoordPrec(
     390           8 :                 poGeomFieldDefn->GetCoordinatePrecision());
     391           8 :             oCoordPrec.dfXYResolution = json_object_get_double(poXYRes);
     392           8 :             whileUnsealing(poGeomFieldDefn)->SetCoordinatePrecision(oCoordPrec);
     393             : 
     394           8 :             options.nXYCoordPrecision =
     395           8 :                 OGRGeomCoordinatePrecision::ResolutionToPrecision(
     396             :                     oCoordPrec.dfXYResolution);
     397             :         }
     398             : 
     399             :         json_object *poZRes =
     400         332 :             CPL_json_object_object_get(poRootObj, "z_coordinate_resolution");
     401         332 :         if (poZRes && (json_object_get_type(poZRes) == json_type_double ||
     402           0 :                        json_object_get_type(poZRes) == json_type_int))
     403             :         {
     404           6 :             auto poGeomFieldDefn = poLayer->GetLayerDefn()->GetGeomFieldDefn(0);
     405             :             OGRGeomCoordinatePrecision oCoordPrec(
     406           6 :                 poGeomFieldDefn->GetCoordinatePrecision());
     407           6 :             oCoordPrec.dfZResolution = json_object_get_double(poZRes);
     408           6 :             whileUnsealing(poGeomFieldDefn)->SetCoordinatePrecision(oCoordPrec);
     409             : 
     410           6 :             options.nZCoordPrecision =
     411           6 :                 OGRGeomCoordinatePrecision::ResolutionToPrecision(
     412             :                     oCoordPrec.dfZResolution);
     413             :         }
     414             : 
     415         332 :         poLayer->SetWriteOptions(options);
     416             :     }
     417         332 : }
     418             : 
     419             : /************************************************************************/
     420             : /*                       FirstPassReadLayer()                           */
     421             : /************************************************************************/
     422             : 
     423         244 : bool OGRGeoJSONReader::FirstPassReadLayer(OGRGeoJSONDataSource *poDS,
     424             :                                           VSILFILE *fp,
     425             :                                           bool &bTryStandardReading)
     426             : {
     427         244 :     bTryStandardReading = false;
     428         244 :     VSIFSeekL(fp, 0, SEEK_SET);
     429         244 :     bFirstSeg_ = true;
     430             : 
     431         244 :     const char *pszName = poDS->GetDescription();
     432         244 :     if (STARTS_WITH_CI(pszName, "GeoJSON:"))
     433           0 :         pszName += strlen("GeoJSON:");
     434         244 :     pszName = CPLGetBasename(pszName);
     435             : 
     436             :     OGRGeoJSONLayer *poLayer = new OGRGeoJSONLayer(
     437         244 :         pszName, nullptr, OGRGeoJSONLayer::DefaultGeometryType, poDS, this);
     438             :     OGRGeoJSONReaderStreamingParser oParser(*this, poLayer, true,
     439         488 :                                             bStoreNativeData_);
     440             : 
     441         244 :     vsi_l_offset nFileSize = 0;
     442         314 :     if (STARTS_WITH(poDS->GetDescription(), "/vsimem/") ||
     443          70 :         !STARTS_WITH(poDS->GetDescription(), "/vsi"))
     444             :     {
     445             :         VSIStatBufL sStatBuf;
     446         238 :         if (VSIStatL(poDS->GetDescription(), &sStatBuf) == 0)
     447             :         {
     448         238 :             nFileSize = sStatBuf.st_size;
     449             :         }
     450             :     }
     451             : 
     452         244 :     nBufferSize_ = 4096 * 10;
     453         244 :     pabyBuffer_ = static_cast<GByte *>(CPLMalloc(nBufferSize_));
     454         244 :     int nIter = 0;
     455         244 :     bool bThresholdReached = false;
     456         244 :     const GIntBig nMaxBytesFirstPass = CPLAtoGIntBig(
     457             :         CPLGetConfigOption("OGR_GEOJSON_MAX_BYTES_FIRST_PASS", "0"));
     458         244 :     const GIntBig nLimitFeaturesFirstPass = CPLAtoGIntBig(
     459             :         CPLGetConfigOption("OGR_GEOJSON_MAX_FEATURES_FIRST_PASS", "0"));
     460             :     while (true)
     461             :     {
     462         293 :         nIter++;
     463             : 
     464         293 :         if (nMaxBytesFirstPass > 0 &&
     465           0 :             static_cast<GIntBig>(nIter) * static_cast<GIntBig>(nBufferSize_) >=
     466             :                 nMaxBytesFirstPass)
     467             :         {
     468           0 :             CPLDebug("GeoJSON", "First pass: early exit since above "
     469             :                                 "OGR_GEOJSON_MAX_BYTES_FIRST_PASS");
     470           0 :             bThresholdReached = true;
     471           0 :             break;
     472             :         }
     473             : 
     474         293 :         size_t nRead = VSIFReadL(pabyBuffer_, 1, nBufferSize_, fp);
     475         293 :         const bool bFinished = nRead < nBufferSize_;
     476         293 :         size_t nSkip = 0;
     477         293 :         if (bFirstSeg_)
     478             :         {
     479         244 :             bFirstSeg_ = false;
     480         244 :             nSkip = SkipPrologEpilogAndUpdateJSonPLikeWrapper(nRead);
     481             :         }
     482         293 :         if (bFinished && bJSonPLikeWrapper_ && nRead - nSkip > 0)
     483           1 :             nRead--;
     484         293 :         if (!oParser.Parse(reinterpret_cast<const char *>(pabyBuffer_ + nSkip),
     485         585 :                            nRead - nSkip, bFinished) ||
     486         292 :             oParser.ExceptionOccurred())
     487             :         {
     488             :             // to avoid killing ourselves during layer deletion
     489           2 :             poLayer->UnsetReader();
     490           2 :             delete poLayer;
     491           2 :             return false;
     492             :         }
     493         291 :         if (bFinished || (nIter % 100) == 0)
     494             :         {
     495         242 :             if (nFileSize == 0)
     496             :             {
     497           6 :                 if (bFinished)
     498             :                 {
     499           6 :                     CPLDebug("GeoJSON", "First pass: 100.00 %%");
     500             :                 }
     501             :                 else
     502             :                 {
     503           0 :                     CPLDebug("GeoJSON",
     504             :                              "First pass: " CPL_FRMT_GUIB " bytes read",
     505           0 :                              static_cast<GUIntBig>(nIter) *
     506           0 :                                      static_cast<GUIntBig>(nBufferSize_) +
     507             :                                  nRead);
     508             :                 }
     509             :             }
     510             :             else
     511             :             {
     512         236 :                 CPLDebug("GeoJSON", "First pass: %.2f %%",
     513         236 :                          100.0 * VSIFTellL(fp) / nFileSize);
     514             :             }
     515             :         }
     516         291 :         if (nLimitFeaturesFirstPass > 0 &&
     517           0 :             poLayer->GetFeatureCount(FALSE) >= nLimitFeaturesFirstPass)
     518             :         {
     519           0 :             CPLDebug("GeoJSON", "First pass: early exit since above "
     520             :                                 "OGR_GEOJSON_MAX_FEATURES_FIRST_PASS");
     521           0 :             bThresholdReached = true;
     522           0 :             break;
     523             :         }
     524         291 :         if (oParser.IsTypeKnown() && !oParser.IsFeatureCollection())
     525           0 :             break;
     526         291 :         if (bFinished)
     527         242 :             break;
     528          49 :     }
     529             : 
     530         242 :     if (bThresholdReached)
     531             :     {
     532           0 :         poLayer->InvalidateFeatureCount();
     533             :     }
     534         242 :     else if (!oParser.IsTypeKnown() || !oParser.IsFeatureCollection())
     535             :     {
     536             :         // to avoid killing ourselves during layer deletion
     537           1 :         poLayer->UnsetReader();
     538           1 :         delete poLayer;
     539             :         const vsi_l_offset nRAM =
     540           1 :             static_cast<vsi_l_offset>(CPLGetUsablePhysicalRAM());
     541           1 :         if (nFileSize == 0 || nRAM == 0 || nRAM > nFileSize * 20)
     542             :         {
     543             :             // Only try full ingestion if we have 20x more RAM than the file
     544             :             // size
     545           1 :             bTryStandardReading = true;
     546             :         }
     547           1 :         return false;
     548             :     }
     549             : 
     550         241 :     oParser.FinalizeLayerDefn();
     551             : 
     552         241 :     CPLString osFIDColumn;
     553         241 :     FinalizeLayerDefn(poLayer, osFIDColumn);
     554         241 :     if (!osFIDColumn.empty())
     555          25 :         poLayer->SetFIDColumn(osFIDColumn);
     556             : 
     557         241 :     bCanEasilyAppend_ = oParser.CanEasilyAppend();
     558         241 :     nTotalFeatureCount_ = poLayer->GetFeatureCount(FALSE);
     559         241 :     nTotalOGRFeatureMemEstimate_ = oParser.GetTotalOGRFeatureMemEstimate();
     560             : 
     561         241 :     json_object *poRootObj = oParser.StealRootObject();
     562         241 :     if (poRootObj)
     563             :     {
     564         241 :         bFCHasBBOX_ = CPL_json_object_object_get(poRootObj, "bbox") != nullptr;
     565             : 
     566             :         // CPLDebug("GeoJSON", "%s", json_object_get_string(poRootObj));
     567             : 
     568         241 :         json_object *poName = CPL_json_object_object_get(poRootObj, "name");
     569         241 :         if (poName && json_object_get_type(poName) == json_type_string)
     570             :         {
     571         102 :             const char *pszValue = json_object_get_string(poName);
     572         102 :             whileUnsealing(poLayer->GetLayerDefn())->SetName(pszValue);
     573         102 :             poLayer->SetDescription(pszValue);
     574             :         }
     575             : 
     576             :         json_object *poDescription =
     577         241 :             CPL_json_object_object_get(poRootObj, "description");
     578         242 :         if (poDescription &&
     579           1 :             json_object_get_type(poDescription) == json_type_string)
     580             :         {
     581           1 :             const char *pszValue = json_object_get_string(poDescription);
     582           1 :             poLayer->SetMetadataItem("DESCRIPTION", pszValue);
     583             :         }
     584             : 
     585         241 :         OGRSpatialReference *poSRS = OGRGeoJSONReadSpatialReference(poRootObj);
     586         241 :         const auto eGeomType = poLayer->GetLayerDefn()->GetGeomType();
     587         241 :         if (eGeomType != wkbNone && poSRS == nullptr)
     588             :         {
     589             :             // If there is none defined, we use 4326 / 4979.
     590         212 :             poSRS = new OGRSpatialReference();
     591         212 :             if (OGR_GT_HasZ(eGeomType))
     592          17 :                 poSRS->importFromEPSG(4979);
     593             :             else
     594         195 :                 poSRS->SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
     595         212 :             poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     596             :         }
     597         241 :         CPLErrorReset();
     598             : 
     599         241 :         if (eGeomType != wkbNone && poSRS != nullptr)
     600             :         {
     601         241 :             auto poGeomFieldDefn = poLayer->GetLayerDefn()->GetGeomFieldDefn(0);
     602         241 :             whileUnsealing(poGeomFieldDefn)->SetSpatialRef(poSRS);
     603             :         }
     604         241 :         if (poSRS)
     605         241 :             poSRS->Release();
     606             : 
     607         241 :         SetCoordinatePrecision(poRootObj, poLayer);
     608             : 
     609         241 :         if (bStoreNativeData_)
     610             :         {
     611          48 :             CPLString osNativeData("NATIVE_DATA=");
     612          24 :             osNativeData += json_object_get_string(poRootObj);
     613             : 
     614          24 :             char *apszMetadata[3] = {
     615          24 :                 const_cast<char *>(osNativeData.c_str()),
     616             :                 const_cast<char *>(
     617             :                     "NATIVE_MEDIA_TYPE=application/vnd.geo+json"),
     618          24 :                 nullptr};
     619             : 
     620          24 :             poLayer->SetMetadata(apszMetadata, "NATIVE_DATA");
     621             :         }
     622             : 
     623         241 :         poGJObject_ = poRootObj;
     624             :     }
     625             : 
     626         241 :     fp_ = fp;
     627         241 :     poDS->AddLayer(poLayer);
     628         241 :     return true;
     629             : }
     630             : 
     631             : /************************************************************************/
     632             : /*               SkipPrologEpilogAndUpdateJSonPLikeWrapper()            */
     633             : /************************************************************************/
     634             : 
     635         480 : size_t OGRGeoJSONReader::SkipPrologEpilogAndUpdateJSonPLikeWrapper(size_t nRead)
     636             : {
     637         480 :     size_t nSkip = 0;
     638         480 :     if (nRead >= 3 && pabyBuffer_[0] == 0xEF && pabyBuffer_[1] == 0xBB &&
     639           0 :         pabyBuffer_[2] == 0xBF)
     640             :     {
     641           0 :         CPLDebug("GeoJSON", "Skip UTF-8 BOM");
     642           0 :         nSkip += 3;
     643             :     }
     644             : 
     645         480 :     const char *const apszPrefix[] = {"loadGeoJSON(", "jsonp("};
     646        1438 :     for (size_t i = 0; i < CPL_ARRAYSIZE(apszPrefix); i++)
     647             :     {
     648         959 :         if (nRead >= nSkip + strlen(apszPrefix[i]) &&
     649         959 :             memcmp(pabyBuffer_ + nSkip, apszPrefix[i], strlen(apszPrefix[i])) ==
     650             :                 0)
     651             :         {
     652           1 :             nSkip += strlen(apszPrefix[i]);
     653           1 :             bJSonPLikeWrapper_ = true;
     654           1 :             break;
     655             :         }
     656             :     }
     657             : 
     658         480 :     return nSkip;
     659             : }
     660             : 
     661             : /************************************************************************/
     662             : /*                            ResetReading()                            */
     663             : /************************************************************************/
     664             : 
     665         431 : void OGRGeoJSONReader::ResetReading()
     666             : {
     667         431 :     CPLAssert(fp_);
     668         431 :     if (poStreamingParser_)
     669          75 :         bOriginalIdModifiedEmitted_ =
     670          75 :             poStreamingParser_->GetOriginalIdModifiedEmitted();
     671         431 :     delete poStreamingParser_;
     672         431 :     poStreamingParser_ = nullptr;
     673         431 : }
     674             : 
     675             : /************************************************************************/
     676             : /*                           GetNextFeature()                           */
     677             : /************************************************************************/
     678             : 
     679         940 : OGRFeature *OGRGeoJSONReader::GetNextFeature(OGRGeoJSONLayer *poLayer)
     680             : {
     681         940 :     CPLAssert(fp_);
     682         940 :     if (poStreamingParser_ == nullptr)
     683             :     {
     684         231 :         poStreamingParser_ = new OGRGeoJSONReaderStreamingParser(
     685         231 :             *this, poLayer, false, bStoreNativeData_);
     686         231 :         poStreamingParser_->SetOriginalIdModifiedEmitted(
     687         231 :             bOriginalIdModifiedEmitted_);
     688         231 :         VSIFSeekL(fp_, 0, SEEK_SET);
     689         231 :         bFirstSeg_ = true;
     690         231 :         bJSonPLikeWrapper_ = false;
     691             :     }
     692             : 
     693         940 :     OGRFeature *poFeat = poStreamingParser_->GetNextFeature();
     694         940 :     if (poFeat)
     695         487 :         return poFeat;
     696             : 
     697             :     while (true)
     698             :     {
     699         453 :         size_t nRead = VSIFReadL(pabyBuffer_, 1, nBufferSize_, fp_);
     700         453 :         const bool bFinished = nRead < nBufferSize_;
     701         453 :         size_t nSkip = 0;
     702         453 :         if (bFirstSeg_)
     703             :         {
     704         231 :             bFirstSeg_ = false;
     705         231 :             nSkip = SkipPrologEpilogAndUpdateJSonPLikeWrapper(nRead);
     706             :         }
     707         453 :         if (bFinished && bJSonPLikeWrapper_ && nRead - nSkip > 0)
     708           0 :             nRead--;
     709        1359 :         if (!poStreamingParser_->Parse(
     710         453 :                 reinterpret_cast<const char *>(pabyBuffer_ + nSkip),
     711         906 :                 nRead - nSkip, bFinished) ||
     712         453 :             poStreamingParser_->ExceptionOccurred())
     713             :         {
     714           0 :             break;
     715             :         }
     716             : 
     717         453 :         poFeat = poStreamingParser_->GetNextFeature();
     718         453 :         if (poFeat)
     719         301 :             return poFeat;
     720             : 
     721         152 :         if (bFinished)
     722         152 :             break;
     723           0 :     }
     724             : 
     725         152 :     return nullptr;
     726             : }
     727             : 
     728             : /************************************************************************/
     729             : /*                             GetFeature()                             */
     730             : /************************************************************************/
     731             : 
     732          22 : OGRFeature *OGRGeoJSONReader::GetFeature(OGRGeoJSONLayer *poLayer, GIntBig nFID)
     733             : {
     734          22 :     CPLAssert(fp_);
     735             : 
     736          22 :     if (oMapFIDToOffsetSize_.empty())
     737             :     {
     738           5 :         CPLDebug("GeoJSON",
     739             :                  "Establishing index to features for first GetFeature() call");
     740             : 
     741           5 :         if (poStreamingParser_)
     742           4 :             bOriginalIdModifiedEmitted_ =
     743           4 :                 poStreamingParser_->GetOriginalIdModifiedEmitted();
     744           5 :         delete poStreamingParser_;
     745           5 :         poStreamingParser_ = nullptr;
     746             : 
     747             :         OGRGeoJSONReaderStreamingParser oParser(*this, poLayer, false,
     748           5 :                                                 bStoreNativeData_);
     749           5 :         oParser.SetOriginalIdModifiedEmitted(bOriginalIdModifiedEmitted_);
     750           5 :         VSIFSeekL(fp_, 0, SEEK_SET);
     751           5 :         bFirstSeg_ = true;
     752           5 :         bJSonPLikeWrapper_ = false;
     753           5 :         vsi_l_offset nCurOffset = 0;
     754           5 :         vsi_l_offset nFeatureOffset = 0;
     755             :         while (true)
     756             :         {
     757           5 :             size_t nRead = VSIFReadL(pabyBuffer_, 1, nBufferSize_, fp_);
     758           5 :             const bool bFinished = nRead < nBufferSize_;
     759           5 :             size_t nSkip = 0;
     760           5 :             if (bFirstSeg_)
     761             :             {
     762           5 :                 bFirstSeg_ = false;
     763           5 :                 nSkip = SkipPrologEpilogAndUpdateJSonPLikeWrapper(nRead);
     764             :             }
     765           5 :             if (bFinished && bJSonPLikeWrapper_ && nRead - nSkip > 0)
     766           0 :                 nRead--;
     767           5 :             auto pszPtr = reinterpret_cast<const char *>(pabyBuffer_ + nSkip);
     768        1673 :             for (size_t i = 0; i < nRead - nSkip; i++)
     769             :             {
     770        1668 :                 oParser.ResetFeatureDetectionState();
     771        1668 :                 if (!oParser.Parse(pszPtr + i, 1,
     772        5004 :                                    bFinished && (i + 1 == nRead - nSkip)) ||
     773        1668 :                     oParser.ExceptionOccurred())
     774             :                 {
     775           0 :                     return nullptr;
     776             :                 }
     777        1668 :                 if (oParser.IsStartFeature())
     778             :                 {
     779          18 :                     nFeatureOffset = nCurOffset + i;
     780             :                 }
     781        1650 :                 else if (oParser.IsEndFeature())
     782             :                 {
     783          18 :                     vsi_l_offset nFeatureSize =
     784          18 :                         (nCurOffset + i) - nFeatureOffset + 1;
     785          18 :                     auto poFeat = oParser.GetNextFeature();
     786          18 :                     if (poFeat)
     787             :                     {
     788          18 :                         const GIntBig nThisFID = poFeat->GetFID();
     789          18 :                         if (oMapFIDToOffsetSize_.find(nThisFID) ==
     790          36 :                             oMapFIDToOffsetSize_.end())
     791             :                         {
     792          18 :                             oMapFIDToOffsetSize_[nThisFID] =
     793          36 :                                 std::pair<vsi_l_offset, vsi_l_offset>(
     794          18 :                                     nFeatureOffset, nFeatureSize);
     795             :                         }
     796          18 :                         delete poFeat;
     797             :                     }
     798             :                 }
     799             :             }
     800             : 
     801           5 :             if (bFinished)
     802           5 :                 break;
     803           0 :             nCurOffset += nRead;
     804           0 :         }
     805             : 
     806           5 :         bOriginalIdModifiedEmitted_ = oParser.GetOriginalIdModifiedEmitted();
     807             :     }
     808             : 
     809          22 :     auto oIter = oMapFIDToOffsetSize_.find(nFID);
     810          22 :     if (oIter == oMapFIDToOffsetSize_.end())
     811             :     {
     812           5 :         return nullptr;
     813             :     }
     814             : 
     815          17 :     VSIFSeekL(fp_, oIter->second.first, SEEK_SET);
     816          17 :     if (oIter->second.second > 1000 * 1000 * 1000)
     817             :     {
     818           0 :         return nullptr;
     819             :     }
     820          17 :     size_t nSize = static_cast<size_t>(oIter->second.second);
     821          17 :     char *pszBuffer = static_cast<char *>(VSIMalloc(nSize + 1));
     822          17 :     if (!pszBuffer)
     823             :     {
     824           0 :         return nullptr;
     825             :     }
     826          17 :     if (VSIFReadL(pszBuffer, 1, nSize, fp_) != nSize)
     827             :     {
     828           0 :         VSIFree(pszBuffer);
     829           0 :         return nullptr;
     830             :     }
     831          17 :     pszBuffer[nSize] = 0;
     832          17 :     json_object *poObj = nullptr;
     833          17 :     if (!OGRJSonParse(pszBuffer, &poObj))
     834             :     {
     835           0 :         VSIFree(pszBuffer);
     836           0 :         return nullptr;
     837             :     }
     838             : 
     839          17 :     OGRFeature *poFeat = ReadFeature(poLayer, poObj, pszBuffer);
     840          17 :     json_object_put(poObj);
     841          17 :     VSIFree(pszBuffer);
     842          17 :     if (!poFeat)
     843             :     {
     844           0 :         return nullptr;
     845             :     }
     846          17 :     poFeat->SetFID(nFID);
     847          17 :     return poFeat;
     848             : }
     849             : 
     850             : /************************************************************************/
     851             : /*                           IngestAll()                                */
     852             : /************************************************************************/
     853             : 
     854           9 : bool OGRGeoJSONReader::IngestAll(OGRGeoJSONLayer *poLayer)
     855             : {
     856             :     const vsi_l_offset nRAM =
     857           9 :         static_cast<vsi_l_offset>(CPLGetUsablePhysicalRAM()) / 3 * 4;
     858           9 :     if (nRAM && nTotalOGRFeatureMemEstimate_ > nRAM)
     859             :     {
     860           0 :         CPLError(CE_Failure, CPLE_OutOfMemory,
     861             :                  "Not enough memory to ingest all the layer: " CPL_FRMT_GUIB
     862             :                  " available, " CPL_FRMT_GUIB " needed",
     863             :                  nRAM, nTotalOGRFeatureMemEstimate_);
     864           0 :         return false;
     865             :     }
     866             : 
     867           9 :     CPLDebug("GeoJSON",
     868             :              "Total memory estimated for ingestion: " CPL_FRMT_GUIB " bytes",
     869             :              nTotalOGRFeatureMemEstimate_);
     870             : 
     871           9 :     ResetReading();
     872           9 :     GIntBig nCounter = 0;
     873             :     while (true)
     874             :     {
     875          23 :         OGRFeature *poFeature = GetNextFeature(poLayer);
     876          23 :         if (poFeature == nullptr)
     877           9 :             break;
     878          14 :         poLayer->AddFeature(poFeature);
     879          14 :         delete poFeature;
     880          14 :         nCounter++;
     881          14 :         if (((nCounter % 10000) == 0 || nCounter == nTotalFeatureCount_) &&
     882           7 :             nTotalFeatureCount_ > 0)
     883             :         {
     884           7 :             CPLDebug("GeoJSON", "Ingestion at %.02f %%",
     885           7 :                      100.0 * nCounter / nTotalFeatureCount_);
     886             :         }
     887          14 :     }
     888           9 :     return true;
     889             : }
     890             : 
     891             : /************************************************************************/
     892             : /*                           ReadLayer                                  */
     893             : /************************************************************************/
     894             : 
     895         168 : void OGRGeoJSONReader::ReadLayer(OGRGeoJSONDataSource *poDS,
     896             :                                  const char *pszName, json_object *poObj)
     897             : {
     898         168 :     GeoJSONObject::Type objType = OGRGeoJSONGetType(poObj);
     899         168 :     if (objType == GeoJSONObject::eUnknown)
     900             :     {
     901             :         // Check if the object contains key:value pairs where value
     902             :         // is a standard GeoJSON object. In which case, use key as the layer
     903             :         // name.
     904           3 :         if (json_type_object == json_object_get_type(poObj))
     905             :         {
     906             :             json_object_iter it;
     907           3 :             it.key = nullptr;
     908           3 :             it.val = nullptr;
     909           3 :             it.entry = nullptr;
     910           8 :             json_object_object_foreachC(poObj, it)
     911             :             {
     912           5 :                 objType = OGRGeoJSONGetType(it.val);
     913           5 :                 if (objType != GeoJSONObject::eUnknown)
     914           5 :                     ReadLayer(poDS, it.key, it.val);
     915             :             }
     916             :         }
     917             : 
     918             :         // CPLError(CE_Failure, CPLE_AppDefined,
     919             :         //          "Unrecognized GeoJSON structure.");
     920             : 
     921           3 :         return;
     922             :     }
     923             : 
     924         165 :     CPLErrorReset();
     925             : 
     926             :     // Figure out layer name
     927         165 :     if (pszName == nullptr)
     928             :     {
     929         160 :         if (GeoJSONObject::eFeatureCollection == objType)
     930             :         {
     931          90 :             json_object *poName = CPL_json_object_object_get(poObj, "name");
     932          93 :             if (poName != nullptr &&
     933           3 :                 json_object_get_type(poName) == json_type_string)
     934             :             {
     935           3 :                 pszName = json_object_get_string(poName);
     936             :             }
     937             :         }
     938         160 :         if (pszName == nullptr)
     939             :         {
     940         157 :             const char *pszDesc = poDS->GetDescription();
     941         157 :             if (strchr(pszDesc, '?') == nullptr &&
     942         157 :                 strchr(pszDesc, '{') == nullptr)
     943             :             {
     944          26 :                 pszName = CPLGetBasename(pszDesc);
     945             :             }
     946             :         }
     947         160 :         if (pszName == nullptr)
     948         131 :             pszName = OGRGeoJSONLayer::DefaultName;
     949             :     }
     950             : 
     951             :     OGRGeoJSONLayer *poLayer = new OGRGeoJSONLayer(
     952         165 :         pszName, nullptr, OGRGeoJSONLayer::DefaultGeometryType, poDS, nullptr);
     953             : 
     954         165 :     OGRSpatialReference *poSRS = OGRGeoJSONReadSpatialReference(poObj);
     955         165 :     bool bDefaultSRS = false;
     956         165 :     if (poSRS == nullptr)
     957             :     {
     958             :         // If there is none defined, we use 4326 / 4979.
     959         158 :         poSRS = new OGRSpatialReference();
     960         158 :         bDefaultSRS = true;
     961             :     }
     962             :     {
     963         165 :         auto poGeomFieldDefn = poLayer->GetLayerDefn()->GetGeomFieldDefn(0);
     964         165 :         whileUnsealing(poGeomFieldDefn)->SetSpatialRef(poSRS);
     965             :     }
     966             : 
     967         165 :     if (!GenerateLayerDefn(poLayer, poObj))
     968             :     {
     969           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     970             :                  "Layer schema generation failed.");
     971             : 
     972           1 :         delete poLayer;
     973           1 :         poSRS->Release();
     974           1 :         return;
     975             :     }
     976             : 
     977         164 :     if (GeoJSONObject::eFeatureCollection == objType)
     978             :     {
     979             :         json_object *poDescription =
     980          91 :             CPL_json_object_object_get(poObj, "description");
     981          92 :         if (poDescription != nullptr &&
     982           1 :             json_object_get_type(poDescription) == json_type_string)
     983             :         {
     984           1 :             poLayer->SetMetadataItem("DESCRIPTION",
     985           1 :                                      json_object_get_string(poDescription));
     986             :         }
     987             : 
     988          91 :         SetCoordinatePrecision(poObj, poLayer);
     989             :     }
     990             : 
     991             :     /* -------------------------------------------------------------------- */
     992             :     /*      Translate single geometry-only Feature object.                  */
     993             :     /* -------------------------------------------------------------------- */
     994             : 
     995         164 :     if (GeoJSONObject::ePoint == objType ||
     996         117 :         GeoJSONObject::eMultiPoint == objType ||
     997         115 :         GeoJSONObject::eLineString == objType ||
     998         114 :         GeoJSONObject::eMultiLineString == objType ||
     999         112 :         GeoJSONObject::ePolygon == objType ||
    1000         107 :         GeoJSONObject::eMultiPolygon == objType ||
    1001             :         GeoJSONObject::eGeometryCollection == objType)
    1002             :     {
    1003          59 :         OGRGeometry *poGeometry = ReadGeometry(poObj, poLayer->GetSpatialRef());
    1004          59 :         if (!AddFeature(poLayer, poGeometry))
    1005             :         {
    1006           1 :             CPLDebug("GeoJSON", "Translation of single geometry failed.");
    1007           1 :             delete poLayer;
    1008           1 :             poSRS->Release();
    1009           1 :             return;
    1010          58 :         }
    1011             :     }
    1012             :     /* -------------------------------------------------------------------- */
    1013             :     /*      Translate single but complete Feature object.                   */
    1014             :     /* -------------------------------------------------------------------- */
    1015         105 :     else if (GeoJSONObject::eFeature == objType)
    1016             :     {
    1017          14 :         OGRFeature *poFeature = ReadFeature(poLayer, poObj, nullptr);
    1018          14 :         AddFeature(poLayer, poFeature);
    1019             :     }
    1020             :     /* -------------------------------------------------------------------- */
    1021             :     /*      Translate multi-feature FeatureCollection object.               */
    1022             :     /* -------------------------------------------------------------------- */
    1023          91 :     else if (GeoJSONObject::eFeatureCollection == objType)
    1024             :     {
    1025          91 :         ReadFeatureCollection(poLayer, poObj);
    1026             :     }
    1027             : 
    1028         163 :     if (CPLGetLastErrorType() != CE_Warning)
    1029         160 :         CPLErrorReset();
    1030             : 
    1031         163 :     poLayer->DetectGeometryType();
    1032             : 
    1033         163 :     if (bDefaultSRS && poLayer->GetGeomType() != wkbNone)
    1034             :     {
    1035         156 :         if (OGR_GT_HasZ(poLayer->GetGeomType()))
    1036          46 :             poSRS->importFromEPSG(4979);
    1037             :         else
    1038         110 :             poSRS->SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
    1039         156 :         poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1040             :     }
    1041         163 :     poSRS->Release();
    1042             : 
    1043         163 :     poDS->AddLayer(poLayer);
    1044             : }
    1045             : 
    1046             : /************************************************************************/
    1047             : /*                    OGRGeoJSONReadSpatialReference                    */
    1048             : /************************************************************************/
    1049             : 
    1050         409 : OGRSpatialReference *OGRGeoJSONReadSpatialReference(json_object *poObj)
    1051             : {
    1052             : 
    1053             :     /* -------------------------------------------------------------------- */
    1054             :     /*      Read spatial reference definition.                              */
    1055             :     /* -------------------------------------------------------------------- */
    1056         409 :     OGRSpatialReference *poSRS = nullptr;
    1057             : 
    1058         409 :     json_object *poObjSrs = OGRGeoJSONFindMemberByName(poObj, "crs");
    1059         409 :     if (nullptr != poObjSrs)
    1060             :     {
    1061             :         json_object *poObjSrsType =
    1062          64 :             OGRGeoJSONFindMemberByName(poObjSrs, "type");
    1063          64 :         if (poObjSrsType == nullptr)
    1064           1 :             return nullptr;
    1065             : 
    1066          63 :         const char *pszSrsType = json_object_get_string(poObjSrsType);
    1067             : 
    1068             :         // TODO: Add URL and URN types support.
    1069          63 :         if (STARTS_WITH_CI(pszSrsType, "NAME"))
    1070             :         {
    1071             :             json_object *poObjSrsProps =
    1072          43 :                 OGRGeoJSONFindMemberByName(poObjSrs, "properties");
    1073          43 :             if (poObjSrsProps == nullptr)
    1074           2 :                 return nullptr;
    1075             : 
    1076             :             json_object *poNameURL =
    1077          41 :                 OGRGeoJSONFindMemberByName(poObjSrsProps, "name");
    1078          41 :             if (poNameURL == nullptr)
    1079           2 :                 return nullptr;
    1080             : 
    1081          39 :             const char *pszName = json_object_get_string(poNameURL);
    1082             : 
    1083             :             // Mostly to emulate GDAL 2.x behavior
    1084             :             // See https://github.com/OSGeo/gdal/issues/2035
    1085          39 :             if (EQUAL(pszName, "urn:ogc:def:crs:OGC:1.3:CRS84"))
    1086           9 :                 pszName = "EPSG:4326";
    1087             : 
    1088          39 :             poSRS = new OGRSpatialReference();
    1089          39 :             poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1090          39 :             if (OGRERR_NONE !=
    1091          39 :                 poSRS->SetFromUserInput(
    1092             :                     pszName,
    1093             :                     OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()))
    1094             :             {
    1095           2 :                 delete poSRS;
    1096           2 :                 poSRS = nullptr;
    1097             :             }
    1098             :         }
    1099             : 
    1100          20 :         else if (STARTS_WITH_CI(pszSrsType, "EPSG"))
    1101             :         {
    1102             :             json_object *poObjSrsProps =
    1103           7 :                 OGRGeoJSONFindMemberByName(poObjSrs, "properties");
    1104           7 :             if (poObjSrsProps == nullptr)
    1105           2 :                 return nullptr;
    1106             : 
    1107             :             json_object *poObjCode =
    1108           5 :                 OGRGeoJSONFindMemberByName(poObjSrsProps, "code");
    1109           5 :             if (poObjCode == nullptr)
    1110           2 :                 return nullptr;
    1111             : 
    1112           3 :             int nEPSG = json_object_get_int(poObjCode);
    1113             : 
    1114           3 :             poSRS = new OGRSpatialReference();
    1115           3 :             poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1116           3 :             if (OGRERR_NONE != poSRS->importFromEPSG(nEPSG))
    1117             :             {
    1118           2 :                 delete poSRS;
    1119           2 :                 poSRS = nullptr;
    1120             :             }
    1121             :         }
    1122             : 
    1123          13 :         else if (STARTS_WITH_CI(pszSrsType, "URL") ||
    1124          13 :                  STARTS_WITH_CI(pszSrsType, "LINK"))
    1125             :         {
    1126             :             json_object *poObjSrsProps =
    1127           6 :                 OGRGeoJSONFindMemberByName(poObjSrs, "properties");
    1128           6 :             if (poObjSrsProps == nullptr)
    1129           2 :                 return nullptr;
    1130             : 
    1131             :             json_object *poObjURL =
    1132           4 :                 OGRGeoJSONFindMemberByName(poObjSrsProps, "url");
    1133             : 
    1134           4 :             if (nullptr == poObjURL)
    1135             :             {
    1136           4 :                 poObjURL = OGRGeoJSONFindMemberByName(poObjSrsProps, "href");
    1137             :             }
    1138           4 :             if (poObjURL == nullptr)
    1139           2 :                 return nullptr;
    1140             : 
    1141           2 :             const char *pszURL = json_object_get_string(poObjURL);
    1142             : 
    1143           2 :             poSRS = new OGRSpatialReference();
    1144           2 :             poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1145           2 :             if (OGRERR_NONE != poSRS->importFromUrl(pszURL))
    1146             :             {
    1147           2 :                 delete poSRS;
    1148           2 :                 poSRS = nullptr;
    1149           2 :             }
    1150             :         }
    1151             : 
    1152           7 :         else if (EQUAL(pszSrsType, "OGC"))
    1153             :         {
    1154             :             json_object *poObjSrsProps =
    1155           7 :                 OGRGeoJSONFindMemberByName(poObjSrs, "properties");
    1156           7 :             if (poObjSrsProps == nullptr)
    1157           2 :                 return nullptr;
    1158             : 
    1159             :             json_object *poObjURN =
    1160           5 :                 OGRGeoJSONFindMemberByName(poObjSrsProps, "urn");
    1161           5 :             if (poObjURN == nullptr)
    1162           2 :                 return nullptr;
    1163             : 
    1164           3 :             poSRS = new OGRSpatialReference();
    1165           3 :             poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1166           3 :             if (OGRERR_NONE !=
    1167           3 :                 poSRS->importFromURN(json_object_get_string(poObjURN)))
    1168             :             {
    1169           2 :                 delete poSRS;
    1170           2 :                 poSRS = nullptr;
    1171             :             }
    1172             :         }
    1173             :     }
    1174             : 
    1175             :     // Strip AXIS, since geojson has (easting, northing) / (longitude, latitude)
    1176             :     // order.  According to http://www.geojson.org/geojson-spec.html#id2 :
    1177             :     // "Point coordinates are in x, y order (easting, northing for projected
    1178             :     // coordinates, longitude, latitude for geographic coordinates)".
    1179         392 :     if (poSRS != nullptr)
    1180             :     {
    1181          39 :         OGR_SRSNode *poGEOGCS = poSRS->GetAttrNode("GEOGCS");
    1182          39 :         if (poGEOGCS != nullptr)
    1183          39 :             poGEOGCS->StripNodes("AXIS");
    1184             :     }
    1185             : 
    1186         392 :     return poSRS;
    1187             : }
    1188             : 
    1189             : /************************************************************************/
    1190             : /*                         GenerateLayerDefn()                          */
    1191             : /************************************************************************/
    1192             : 
    1193         165 : bool OGRGeoJSONReader::GenerateLayerDefn(OGRGeoJSONLayer *poLayer,
    1194             :                                          json_object *poGJObject)
    1195             : {
    1196         165 :     CPLAssert(nullptr != poGJObject);
    1197         165 :     CPLAssert(nullptr != poLayer->GetLayerDefn());
    1198         165 :     CPLAssert(0 == poLayer->GetLayerDefn()->GetFieldCount());
    1199             : 
    1200         165 :     if (bAttributesSkip_)
    1201           0 :         return true;
    1202             : 
    1203             :     /* -------------------------------------------------------------------- */
    1204             :     /*      Scan all features and generate layer definition.                */
    1205             :     /* -------------------------------------------------------------------- */
    1206         165 :     bool bSuccess = true;
    1207             : 
    1208         330 :     std::map<std::string, int> oMapFieldNameToIdx;
    1209         330 :     std::vector<std::unique_ptr<OGRFieldDefn>> apoFieldDefn;
    1210         330 :     gdal::DirectedAcyclicGraph<int, std::string> dag;
    1211             : 
    1212         165 :     GeoJSONObject::Type objType = OGRGeoJSONGetType(poGJObject);
    1213         165 :     if (GeoJSONObject::eFeature == objType)
    1214             :     {
    1215          14 :         bSuccess = GenerateFeatureDefn(oMapFieldNameToIdx, apoFieldDefn, dag,
    1216             :                                        poLayer, poGJObject);
    1217             :     }
    1218         151 :     else if (GeoJSONObject::eFeatureCollection == objType)
    1219             :     {
    1220             :         json_object *poObjFeatures =
    1221          92 :             OGRGeoJSONFindMemberByName(poGJObject, "features");
    1222         184 :         if (nullptr != poObjFeatures &&
    1223          92 :             json_type_array == json_object_get_type(poObjFeatures))
    1224             :         {
    1225          92 :             const auto nFeatures = json_object_array_length(poObjFeatures);
    1226         207 :             for (auto i = decltype(nFeatures){0}; i < nFeatures; ++i)
    1227             :             {
    1228             :                 json_object *poObjFeature =
    1229         115 :                     json_object_array_get_idx(poObjFeatures, i);
    1230         115 :                 if (!GenerateFeatureDefn(oMapFieldNameToIdx, apoFieldDefn, dag,
    1231             :                                          poLayer, poObjFeature))
    1232             :                 {
    1233           1 :                     CPLDebug("GeoJSON", "Create feature schema failure.");
    1234           1 :                     bSuccess = false;
    1235             :                 }
    1236             :             }
    1237             :         }
    1238             :         else
    1239             :         {
    1240           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1241             :                      "Invalid FeatureCollection object. "
    1242             :                      "Missing \'features\' member.");
    1243           0 :             bSuccess = false;
    1244             :         }
    1245             :     }
    1246             : 
    1247             :     // Note: the current strategy will not produce stable output, depending
    1248             :     // on the order of features, if there are conflicting order / cycles.
    1249             :     // See https://github.com/OSGeo/gdal/pull/4552 for a number of potential
    1250             :     // resolutions if that has to be solved in the future.
    1251         165 :     OGRFeatureDefn *poDefn = poLayer->GetLayerDefn();
    1252         330 :     const auto sortedFields = dag.getTopologicalOrdering();
    1253         165 :     CPLAssert(sortedFields.size() == apoFieldDefn.size());
    1254             :     {
    1255         330 :         auto oTemporaryUnsealer(poDefn->GetTemporaryUnsealer());
    1256         266 :         for (int idx : sortedFields)
    1257             :         {
    1258         101 :             poDefn->AddFieldDefn(apoFieldDefn[idx].get());
    1259             :         }
    1260             :     }
    1261             : 
    1262         165 :     CPLString osFIDColumn;
    1263         165 :     FinalizeLayerDefn(poLayer, osFIDColumn);
    1264         165 :     if (!osFIDColumn.empty())
    1265           8 :         poLayer->SetFIDColumn(osFIDColumn);
    1266             : 
    1267         165 :     return bSuccess;
    1268             : }
    1269             : 
    1270             : /************************************************************************/
    1271             : /*                          FinalizeLayerDefn()                         */
    1272             : /************************************************************************/
    1273             : 
    1274         465 : void OGRGeoJSONBaseReader::FinalizeLayerDefn(OGRLayer *poLayer,
    1275             :                                              CPLString &osFIDColumn)
    1276             : {
    1277             :     /* -------------------------------------------------------------------- */
    1278             :     /*      Validate and add FID column if necessary.                       */
    1279             :     /* -------------------------------------------------------------------- */
    1280         465 :     osFIDColumn.clear();
    1281         465 :     OGRFeatureDefn *poLayerDefn = poLayer->GetLayerDefn();
    1282         465 :     CPLAssert(nullptr != poLayerDefn);
    1283             : 
    1284         465 :     whileUnsealing(poLayerDefn)->SetGeomType(m_eLayerGeomType);
    1285             : 
    1286         465 :     if (m_bNeedFID64)
    1287             :     {
    1288           4 :         poLayer->SetMetadataItem(OLMD_FID64, "YES");
    1289             :     }
    1290             : 
    1291         465 :     if (!bFeatureLevelIdAsFID_)
    1292             :     {
    1293         443 :         const int idx = poLayerDefn->GetFieldIndexCaseSensitive("id");
    1294         443 :         if (idx >= 0)
    1295             :         {
    1296          64 :             OGRFieldDefn *poFDefn = poLayerDefn->GetFieldDefn(idx);
    1297          97 :             if (poFDefn->GetType() == OFTInteger ||
    1298          33 :                 poFDefn->GetType() == OFTInteger64)
    1299             :             {
    1300          33 :                 osFIDColumn = poLayerDefn->GetFieldDefn(idx)->GetNameRef();
    1301             :             }
    1302             :         }
    1303             :     }
    1304         465 : }
    1305             : 
    1306             : /************************************************************************/
    1307             : /*                     OGRGeoJSONReaderAddOrUpdateField()               */
    1308             : /************************************************************************/
    1309             : 
    1310        4630 : void OGRGeoJSONReaderAddOrUpdateField(
    1311             :     std::vector<int> &retIndices,
    1312             :     std::map<std::string, int> &oMapFieldNameToIdx,
    1313             :     std::vector<std::unique_ptr<OGRFieldDefn>> &apoFieldDefn,
    1314             :     const char *pszKey, json_object *poVal, bool bFlattenNestedAttributes,
    1315             :     char chNestedAttributeSeparator, bool bArrayAsString, bool bDateAsString,
    1316             :     std::set<int> &aoSetUndeterminedTypeFields)
    1317             : {
    1318        4630 :     const auto jType = json_object_get_type(poVal);
    1319        4630 :     if (bFlattenNestedAttributes && poVal != nullptr &&
    1320             :         jType == json_type_object)
    1321             :     {
    1322             :         json_object_iter it;
    1323           2 :         it.key = nullptr;
    1324           2 :         it.val = nullptr;
    1325           2 :         it.entry = nullptr;
    1326           6 :         json_object_object_foreachC(poVal, it)
    1327             :         {
    1328           4 :             char szSeparator[2] = {chNestedAttributeSeparator, '\0'};
    1329             : 
    1330             :             CPLString osAttrName(
    1331           8 :                 CPLSPrintf("%s%s%s", pszKey, szSeparator, it.key));
    1332           8 :             if (it.val != nullptr &&
    1333           4 :                 json_object_get_type(it.val) == json_type_object)
    1334             :             {
    1335           0 :                 OGRGeoJSONReaderAddOrUpdateField(
    1336             :                     retIndices, oMapFieldNameToIdx, apoFieldDefn, osAttrName,
    1337             :                     it.val, true, chNestedAttributeSeparator, bArrayAsString,
    1338             :                     bDateAsString, aoSetUndeterminedTypeFields);
    1339             :             }
    1340             :             else
    1341             :             {
    1342           4 :                 OGRGeoJSONReaderAddOrUpdateField(
    1343             :                     retIndices, oMapFieldNameToIdx, apoFieldDefn, osAttrName,
    1344             :                     it.val, false, 0, bArrayAsString, bDateAsString,
    1345             :                     aoSetUndeterminedTypeFields);
    1346             :             }
    1347             :         }
    1348           2 :         return;
    1349             :     }
    1350             : 
    1351        4628 :     auto oMapFieldNameToIdxIter = oMapFieldNameToIdx.find(pszKey);
    1352        4628 :     if (oMapFieldNameToIdxIter == oMapFieldNameToIdx.end())
    1353             :     {
    1354             :         OGRFieldSubType eSubType;
    1355             :         const OGRFieldType eType =
    1356         970 :             GeoJSONPropertyToFieldType(poVal, eSubType, bArrayAsString);
    1357        1940 :         auto poFieldDefn = std::make_unique<OGRFieldDefn>(pszKey, eType);
    1358         970 :         poFieldDefn->SetSubType(eSubType);
    1359         970 :         if (eSubType == OFSTBoolean)
    1360          34 :             poFieldDefn->SetWidth(1);
    1361         970 :         if (poFieldDefn->GetType() == OFTString && !bDateAsString)
    1362             :         {
    1363         470 :             int nTZFlag = 0;
    1364         470 :             poFieldDefn->SetType(
    1365             :                 GeoJSONStringPropertyToFieldType(poVal, nTZFlag));
    1366         470 :             poFieldDefn->SetTZFlag(nTZFlag);
    1367             :         }
    1368         970 :         apoFieldDefn.emplace_back(std::move(poFieldDefn));
    1369         970 :         const int nIndex = static_cast<int>(apoFieldDefn.size()) - 1;
    1370         970 :         retIndices.emplace_back(nIndex);
    1371         970 :         oMapFieldNameToIdx[pszKey] = nIndex;
    1372         970 :         if (poVal == nullptr)
    1373           7 :             aoSetUndeterminedTypeFields.insert(nIndex);
    1374             :     }
    1375        3658 :     else if (poVal)
    1376             :     {
    1377        3643 :         const int nIndex = oMapFieldNameToIdxIter->second;
    1378        3643 :         retIndices.emplace_back(nIndex);
    1379             :         // If there is a null value: do not update field definition.
    1380        3643 :         OGRFieldDefn *poFDefn = apoFieldDefn[nIndex].get();
    1381        3643 :         const OGRFieldType eType = poFDefn->GetType();
    1382        3643 :         const OGRFieldSubType eSubType = poFDefn->GetSubType();
    1383             :         OGRFieldSubType eNewSubType;
    1384             :         OGRFieldType eNewType =
    1385        3643 :             GeoJSONPropertyToFieldType(poVal, eNewSubType, bArrayAsString);
    1386             :         const bool bNewIsEmptyArray =
    1387        3643 :             (jType == json_type_array && json_object_array_length(poVal) == 0);
    1388        3643 :         if (aoSetUndeterminedTypeFields.find(nIndex) !=
    1389        7286 :             aoSetUndeterminedTypeFields.end())
    1390             :         {
    1391           6 :             poFDefn->SetSubType(OFSTNone);
    1392           6 :             poFDefn->SetType(eNewType);
    1393           6 :             if (poFDefn->GetType() == OFTString && !bDateAsString)
    1394             :             {
    1395           2 :                 int nTZFlag = 0;
    1396           2 :                 poFDefn->SetType(
    1397             :                     GeoJSONStringPropertyToFieldType(poVal, nTZFlag));
    1398           2 :                 poFDefn->SetTZFlag(nTZFlag);
    1399             :             }
    1400           6 :             poFDefn->SetSubType(eNewSubType);
    1401           6 :             aoSetUndeterminedTypeFields.erase(nIndex);
    1402             :         }
    1403        3637 :         else if (eType == OFTInteger)
    1404             :         {
    1405        2200 :             if (eNewType == OFTInteger && eSubType == OFSTBoolean &&
    1406           5 :                 eNewSubType != OFSTBoolean)
    1407             :             {
    1408           2 :                 poFDefn->SetSubType(OFSTNone);
    1409             :             }
    1410        2198 :             else if (eNewType == OFTInteger64 || eNewType == OFTReal ||
    1411        2184 :                      eNewType == OFTInteger64List || eNewType == OFTRealList ||
    1412             :                      eNewType == OFTStringList)
    1413             :             {
    1414          18 :                 poFDefn->SetSubType(OFSTNone);
    1415          18 :                 poFDefn->SetType(eNewType);
    1416             :             }
    1417        2180 :             else if (eNewType == OFTIntegerList)
    1418             :             {
    1419           4 :                 if (eSubType == OFSTBoolean && eNewSubType != OFSTBoolean)
    1420             :                 {
    1421           1 :                     poFDefn->SetSubType(OFSTNone);
    1422             :                 }
    1423           4 :                 poFDefn->SetType(eNewType);
    1424             :             }
    1425        2176 :             else if (eNewType != OFTInteger)
    1426             :             {
    1427          14 :                 poFDefn->SetSubType(OFSTNone);
    1428          14 :                 poFDefn->SetType(OFTString);
    1429          14 :                 poFDefn->SetSubType(OFSTJSON);
    1430             :             }
    1431             :         }
    1432        1437 :         else if (eType == OFTInteger64)
    1433             :         {
    1434          15 :             if (eNewType == OFTReal)
    1435             :             {
    1436           2 :                 poFDefn->SetSubType(OFSTNone);
    1437           2 :                 poFDefn->SetType(eNewType);
    1438             :             }
    1439          13 :             else if (eNewType == OFTIntegerList || eNewType == OFTInteger64List)
    1440             :             {
    1441           3 :                 poFDefn->SetSubType(OFSTNone);
    1442           3 :                 poFDefn->SetType(OFTInteger64List);
    1443             :             }
    1444          10 :             else if (eNewType == OFTRealList || eNewType == OFTStringList)
    1445             :             {
    1446           2 :                 poFDefn->SetSubType(OFSTNone);
    1447           2 :                 poFDefn->SetType(eNewType);
    1448             :             }
    1449           8 :             else if (eNewType != OFTInteger && eNewType != OFTInteger64)
    1450             :             {
    1451           2 :                 poFDefn->SetSubType(OFSTNone);
    1452           2 :                 poFDefn->SetType(OFTString);
    1453           2 :                 poFDefn->SetSubType(OFSTJSON);
    1454             :             }
    1455             :         }
    1456        1422 :         else if (eType == OFTReal)
    1457             :         {
    1458         395 :             if (eNewType == OFTIntegerList || eNewType == OFTInteger64List ||
    1459             :                 eNewType == OFTRealList)
    1460             :             {
    1461           4 :                 poFDefn->SetSubType(OFSTNone);
    1462           4 :                 poFDefn->SetType(OFTRealList);
    1463             :             }
    1464         391 :             else if (eNewType == OFTStringList)
    1465             :             {
    1466           1 :                 poFDefn->SetSubType(OFSTNone);
    1467           1 :                 poFDefn->SetType(OFTStringList);
    1468             :             }
    1469         390 :             else if (eNewType != OFTInteger && eNewType != OFTInteger64 &&
    1470             :                      eNewType != OFTReal)
    1471             :             {
    1472           2 :                 poFDefn->SetSubType(OFSTNone);
    1473           2 :                 poFDefn->SetType(OFTString);
    1474           2 :                 poFDefn->SetSubType(OFSTJSON);
    1475             :             }
    1476             :         }
    1477        1027 :         else if (eType == OFTString)
    1478             :         {
    1479         754 :             if (eSubType == OFSTNone)
    1480             :             {
    1481         740 :                 if (eNewType == OFTStringList)
    1482             :                 {
    1483           1 :                     poFDefn->SetType(OFTStringList);
    1484             :                 }
    1485         739 :                 else if (eNewType != OFTString)
    1486             :                 {
    1487          15 :                     poFDefn->SetSubType(OFSTJSON);
    1488             :                 }
    1489             :             }
    1490             :         }
    1491         273 :         else if (eType == OFTIntegerList)
    1492             :         {
    1493          50 :             if (eNewType == OFTString)
    1494             :             {
    1495           6 :                 if (!bNewIsEmptyArray)
    1496             :                 {
    1497           6 :                     poFDefn->SetSubType(OFSTNone);
    1498           6 :                     poFDefn->SetType(eNewType);
    1499           6 :                     poFDefn->SetSubType(OFSTJSON);
    1500             :                 }
    1501             :             }
    1502          44 :             else if (eNewType == OFTInteger64List || eNewType == OFTRealList ||
    1503             :                      eNewType == OFTStringList)
    1504             :             {
    1505          19 :                 poFDefn->SetSubType(OFSTNone);
    1506          19 :                 poFDefn->SetType(eNewType);
    1507             :             }
    1508          25 :             else if (eNewType == OFTInteger64)
    1509             :             {
    1510           2 :                 poFDefn->SetSubType(OFSTNone);
    1511           2 :                 poFDefn->SetType(OFTInteger64List);
    1512             :             }
    1513          23 :             else if (eNewType == OFTReal)
    1514             :             {
    1515           2 :                 poFDefn->SetSubType(OFSTNone);
    1516           2 :                 poFDefn->SetType(OFTRealList);
    1517             :             }
    1518          21 :             else if (eNewType == OFTInteger || eNewType == OFTIntegerList)
    1519             :             {
    1520          21 :                 if (eSubType == OFSTBoolean && eNewSubType != OFSTBoolean)
    1521             :                 {
    1522           2 :                     poFDefn->SetSubType(OFSTNone);
    1523             :                 }
    1524             :             }
    1525             :             else
    1526             :             {
    1527           0 :                 poFDefn->SetSubType(OFSTNone);
    1528           0 :                 poFDefn->SetType(OFTString);
    1529           0 :                 poFDefn->SetSubType(OFSTJSON);
    1530             :             }
    1531             :         }
    1532         223 :         else if (eType == OFTInteger64List)
    1533             :         {
    1534          24 :             if (eNewType == OFTString)
    1535             :             {
    1536           3 :                 if (!bNewIsEmptyArray)
    1537             :                 {
    1538           3 :                     poFDefn->SetSubType(OFSTNone);
    1539           3 :                     poFDefn->SetType(eNewType);
    1540           3 :                     poFDefn->SetSubType(OFSTJSON);
    1541             :                 }
    1542             :             }
    1543          21 :             else if (eNewType == OFTInteger64List || eNewType == OFTRealList ||
    1544             :                      eNewType == OFTStringList)
    1545             :             {
    1546           9 :                 poFDefn->SetSubType(OFSTNone);
    1547           9 :                 poFDefn->SetType(eNewType);
    1548             :             }
    1549          12 :             else if (eNewType == OFTReal)
    1550             :             {
    1551           1 :                 poFDefn->SetSubType(OFSTNone);
    1552           1 :                 poFDefn->SetType(OFTRealList);
    1553             :             }
    1554          11 :             else if (eNewType != OFTInteger && eNewType != OFTInteger64 &&
    1555             :                      eNewType != OFTIntegerList)
    1556             :             {
    1557           0 :                 poFDefn->SetSubType(OFSTNone);
    1558           0 :                 poFDefn->SetType(OFTString);
    1559           0 :                 poFDefn->SetSubType(OFSTJSON);
    1560             :             }
    1561             :         }
    1562         199 :         else if (eType == OFTRealList)
    1563             :         {
    1564          24 :             if (eNewType == OFTString)
    1565             :             {
    1566           3 :                 if (!bNewIsEmptyArray)
    1567             :                 {
    1568           3 :                     poFDefn->SetSubType(OFSTNone);
    1569           3 :                     poFDefn->SetType(eNewType);
    1570           3 :                     poFDefn->SetSubType(OFSTJSON);
    1571             :                 }
    1572             :             }
    1573          21 :             else if (eNewType == OFTStringList)
    1574             :             {
    1575           1 :                 poFDefn->SetSubType(OFSTNone);
    1576           1 :                 poFDefn->SetType(eNewType);
    1577             :             }
    1578          20 :             else if (eNewType != OFTInteger && eNewType != OFTInteger64 &&
    1579          16 :                      eNewType != OFTReal && eNewType != OFTIntegerList &&
    1580           4 :                      eNewType != OFTInteger64List && eNewType != OFTRealList)
    1581             :             {
    1582           0 :                 poFDefn->SetSubType(OFSTNone);
    1583           0 :                 poFDefn->SetType(OFTString);
    1584           0 :                 poFDefn->SetSubType(OFSTJSON);
    1585             :             }
    1586             :         }
    1587         175 :         else if (eType == OFTStringList)
    1588             :         {
    1589          30 :             if (eNewType == OFTString && eNewSubType == OFSTJSON)
    1590             :             {
    1591           1 :                 if (!bNewIsEmptyArray)
    1592             :                 {
    1593           1 :                     poFDefn->SetSubType(OFSTNone);
    1594           1 :                     poFDefn->SetType(eNewType);
    1595           1 :                     poFDefn->SetSubType(OFSTJSON);
    1596             :                 }
    1597             :             }
    1598             :         }
    1599         145 :         else if (eType == OFTDate || eType == OFTTime || eType == OFTDateTime)
    1600             :         {
    1601         145 :             if (eNewType == OFTString && !bDateAsString &&
    1602         144 :                 eNewSubType == OFSTNone)
    1603             :             {
    1604         143 :                 int nTZFlag = 0;
    1605         143 :                 eNewType = GeoJSONStringPropertyToFieldType(poVal, nTZFlag);
    1606         150 :                 if (poFDefn->GetTZFlag() > OGR_TZFLAG_UNKNOWN &&
    1607           7 :                     nTZFlag != poFDefn->GetTZFlag())
    1608             :                 {
    1609           5 :                     if (nTZFlag == OGR_TZFLAG_UNKNOWN)
    1610           3 :                         poFDefn->SetTZFlag(OGR_TZFLAG_UNKNOWN);
    1611             :                     else
    1612           2 :                         poFDefn->SetTZFlag(OGR_TZFLAG_MIXED_TZ);
    1613             :                 }
    1614             :             }
    1615         145 :             if (eType != eNewType)
    1616             :             {
    1617           8 :                 poFDefn->SetSubType(OFSTNone);
    1618           8 :                 if (eNewType == OFTString)
    1619             :                 {
    1620           5 :                     poFDefn->SetType(eNewType);
    1621           5 :                     poFDefn->SetSubType(eNewSubType);
    1622             :                 }
    1623           3 :                 else if (eType == OFTDate && eNewType == OFTDateTime)
    1624             :                 {
    1625           1 :                     poFDefn->SetType(OFTDateTime);
    1626             :                 }
    1627           2 :                 else if (!(eType == OFTDateTime && eNewType == OFTDate))
    1628             :                 {
    1629           1 :                     poFDefn->SetType(OFTString);
    1630           1 :                     poFDefn->SetSubType(OFSTJSON);
    1631             :                 }
    1632             :             }
    1633             :         }
    1634             : 
    1635        3643 :         poFDefn->SetWidth(poFDefn->GetSubType() == OFSTBoolean ? 1 : 0);
    1636             :     }
    1637             :     else
    1638             :     {
    1639          15 :         const int nIndex = oMapFieldNameToIdxIter->second;
    1640          15 :         retIndices.emplace_back(nIndex);
    1641             :     }
    1642             : }
    1643             : 
    1644             : /************************************************************************/
    1645             : /*             OGRGeoJSONGenerateFeatureDefnDealWithID()                */
    1646             : /************************************************************************/
    1647             : 
    1648        3325 : void OGRGeoJSONGenerateFeatureDefnDealWithID(
    1649             :     json_object *poObj, json_object *poObjProps, int &nPrevFieldIdx,
    1650             :     std::map<std::string, int> &oMapFieldNameToIdx,
    1651             :     std::vector<std::unique_ptr<OGRFieldDefn>> &apoFieldDefn,
    1652             :     gdal::DirectedAcyclicGraph<int, std::string> &dag,
    1653             :     bool &bFeatureLevelIdAsFID, bool &bFeatureLevelIdAsAttribute,
    1654             :     bool &bNeedFID64)
    1655             : {
    1656        3325 :     json_object *poObjId = OGRGeoJSONFindMemberByName(poObj, "id");
    1657        3325 :     if (poObjId)
    1658             :     {
    1659         154 :         auto iterIdxId = oMapFieldNameToIdx.find("id");
    1660         154 :         if (iterIdxId == oMapFieldNameToIdx.end())
    1661             :         {
    1662         100 :             if (json_object_get_type(poObjId) == json_type_int)
    1663             :             {
    1664             :                 // If the value is negative, we cannot use it as the FID
    1665             :                 // as OGRMemLayer doesn't support negative FID. And we would
    1666             :                 // have an ambiguity with -1 that can mean OGRNullFID
    1667             :                 // so in that case create a regular attribute and let OGR
    1668             :                 // attribute sequential OGR FIDs.
    1669          66 :                 if (json_object_get_int64(poObjId) < 0)
    1670             :                 {
    1671           5 :                     bFeatureLevelIdAsFID = false;
    1672             :                 }
    1673             :                 else
    1674             :                 {
    1675          61 :                     bFeatureLevelIdAsFID = true;
    1676             :                 }
    1677             :             }
    1678         100 :             if (!bFeatureLevelIdAsFID)
    1679             :             {
    1680             :                 // If there's a top-level id of type string or negative int,
    1681             :                 // and no properties.id, then declare a id field.
    1682          39 :                 bool bHasRegularIdProp = false;
    1683          76 :                 if (nullptr != poObjProps &&
    1684          37 :                     json_object_get_type(poObjProps) == json_type_object)
    1685             :                 {
    1686          37 :                     bHasRegularIdProp =
    1687          37 :                         CPL_json_object_object_get(poObjProps, "id") != nullptr;
    1688             :                 }
    1689          39 :                 if (!bHasRegularIdProp)
    1690             :                 {
    1691          36 :                     OGRFieldType eType = OFTString;
    1692          36 :                     if (json_object_get_type(poObjId) == json_type_int)
    1693             :                     {
    1694           5 :                         if (CPL_INT64_FITS_ON_INT32(
    1695             :                                 json_object_get_int64(poObjId)))
    1696           4 :                             eType = OFTInteger;
    1697             :                         else
    1698           1 :                             eType = OFTInteger64;
    1699             :                     }
    1700             :                     apoFieldDefn.emplace_back(
    1701          36 :                         std::make_unique<OGRFieldDefn>("id", eType));
    1702          36 :                     const int nIdx = static_cast<int>(apoFieldDefn.size()) - 1;
    1703          36 :                     oMapFieldNameToIdx["id"] = nIdx;
    1704          36 :                     nPrevFieldIdx = nIdx;
    1705          36 :                     dag.addNode(nIdx, "id");
    1706          36 :                     bFeatureLevelIdAsAttribute = true;
    1707             :                 }
    1708             :             }
    1709             :         }
    1710             :         else
    1711             :         {
    1712          54 :             const int nIdx = iterIdxId->second;
    1713          54 :             nPrevFieldIdx = nIdx;
    1714          71 :             if (bFeatureLevelIdAsAttribute &&
    1715          17 :                 json_object_get_type(poObjId) == json_type_int)
    1716             :             {
    1717           3 :                 if (apoFieldDefn[nIdx]->GetType() == OFTInteger)
    1718             :                 {
    1719           1 :                     if (!CPL_INT64_FITS_ON_INT32(
    1720             :                             json_object_get_int64(poObjId)))
    1721           1 :                         apoFieldDefn[nIdx]->SetType(OFTInteger64);
    1722             :                 }
    1723             :             }
    1724          51 :             else if (bFeatureLevelIdAsAttribute)
    1725             :             {
    1726          14 :                 apoFieldDefn[nIdx]->SetType(OFTString);
    1727             :             }
    1728             :         }
    1729             :     }
    1730             : 
    1731        3325 :     if (!bNeedFID64)
    1732             :     {
    1733        3324 :         json_object *poId = CPL_json_object_object_get(poObj, "id");
    1734        3324 :         if (poId == nullptr)
    1735             :         {
    1736        6297 :             if (poObjProps &&
    1737        3126 :                 json_object_get_type(poObjProps) == json_type_object)
    1738             :             {
    1739        3125 :                 poId = CPL_json_object_object_get(poObjProps, "id");
    1740             :             }
    1741             :         }
    1742        3324 :         if (poId != nullptr && json_object_get_type(poId) == json_type_int)
    1743             :         {
    1744         147 :             GIntBig nFID = json_object_get_int64(poId);
    1745         147 :             if (!CPL_INT64_FITS_ON_INT32(nFID))
    1746             :             {
    1747           4 :                 bNeedFID64 = true;
    1748             :             }
    1749             :         }
    1750             :     }
    1751        3325 : }
    1752             : 
    1753             : /************************************************************************/
    1754             : /*                        GenerateFeatureDefn()                         */
    1755             : /************************************************************************/
    1756        3111 : bool OGRGeoJSONBaseReader::GenerateFeatureDefn(
    1757             :     std::map<std::string, int> &oMapFieldNameToIdx,
    1758             :     std::vector<std::unique_ptr<OGRFieldDefn>> &apoFieldDefn,
    1759             :     gdal::DirectedAcyclicGraph<int, std::string> &dag, OGRLayer *poLayer,
    1760             :     json_object *poObj)
    1761             : {
    1762             :     /* -------------------------------------------------------------------- */
    1763             :     /*      Read collection of properties.                                  */
    1764             :     /* -------------------------------------------------------------------- */
    1765             :     lh_entry *poObjPropsEntry =
    1766        3111 :         OGRGeoJSONFindMemberEntryByName(poObj, "properties");
    1767        3111 :     json_object *poObjProps =
    1768        3111 :         const_cast<json_object *>(static_cast<const json_object *>(
    1769             :             poObjPropsEntry ? poObjPropsEntry->v : nullptr));
    1770             : 
    1771        6222 :     std::vector<int> anCurFieldIndices;
    1772        3111 :     int nPrevFieldIdx = -1;
    1773             : 
    1774        3111 :     OGRGeoJSONGenerateFeatureDefnDealWithID(
    1775             :         poObj, poObjProps, nPrevFieldIdx, oMapFieldNameToIdx, apoFieldDefn, dag,
    1776        3111 :         bFeatureLevelIdAsFID_, bFeatureLevelIdAsAttribute_, m_bNeedFID64);
    1777             : 
    1778        3111 :     json_object *poGeomObj = CPL_json_object_object_get(poObj, "geometry");
    1779        3111 :     if (poGeomObj && json_object_get_type(poGeomObj) == json_type_object)
    1780             :     {
    1781        2888 :         const auto eType = OGRGeoJSONGetOGRGeometryType(poGeomObj);
    1782             : 
    1783        2888 :         OGRGeoJSONUpdateLayerGeomType(m_bFirstGeometry, eType,
    1784        2888 :                                       m_eLayerGeomType);
    1785             : 
    1786        2888 :         if (eType != wkbNone && eType != wkbUnknown)
    1787             :         {
    1788             :             // This is maybe too optimistic: it assumes that the geometry
    1789             :             // coordinates array is in the correct format
    1790        2888 :             m_bExtentRead |= OGRGeoJSONGetExtent3D(poGeomObj, &m_oEnvelope3D);
    1791             :         }
    1792             :     }
    1793             : 
    1794        3111 :     bool bSuccess = false;
    1795             : 
    1796        6178 :     if (nullptr != poObjProps &&
    1797        3067 :         json_object_get_type(poObjProps) == json_type_object)
    1798             :     {
    1799        3066 :         if (bIsGeocouchSpatiallistFormat)
    1800             :         {
    1801           3 :             poObjProps = CPL_json_object_object_get(poObjProps, "properties");
    1802           6 :             if (nullptr == poObjProps ||
    1803           3 :                 json_object_get_type(poObjProps) != json_type_object)
    1804             :             {
    1805           2 :                 return true;
    1806             :             }
    1807             :         }
    1808             : 
    1809             :         json_object_iter it;
    1810        3066 :         it.key = nullptr;
    1811        3066 :         it.val = nullptr;
    1812        3066 :         it.entry = nullptr;
    1813        7348 :         json_object_object_foreachC(poObjProps, it)
    1814             :         {
    1815        8559 :             if (!bIsGeocouchSpatiallistFormat &&
    1816        8559 :                 oMapFieldNameToIdx.find(it.key) == oMapFieldNameToIdx.end())
    1817             :             {
    1818             :                 // Detect the special kind of GeoJSON output by a spatiallist of
    1819             :                 // GeoCouch such as:
    1820             :                 // http://gd.iriscouch.com/cphosm/_design/geo/_rewrite/data?bbox=12.53%2C55.73%2C12.54%2C55.73
    1821         841 :                 if (strcmp(it.key, "_id") == 0)
    1822             :                 {
    1823           2 :                     bFoundGeocouchId = true;
    1824             :                 }
    1825         839 :                 else if (bFoundGeocouchId && strcmp(it.key, "_rev") == 0)
    1826             :                 {
    1827           2 :                     bFoundRev = true;
    1828             :                 }
    1829           4 :                 else if (bFoundRev && strcmp(it.key, "type") == 0 &&
    1830           2 :                          it.val != nullptr &&
    1831         843 :                          json_object_get_type(it.val) == json_type_string &&
    1832           2 :                          strcmp(json_object_get_string(it.val), "Feature") == 0)
    1833             :                 {
    1834           2 :                     bFoundTypeFeature = true;
    1835             :                 }
    1836        1672 :                 else if (bFoundTypeFeature &&
    1837           2 :                          strcmp(it.key, "properties") == 0 &&
    1838         839 :                          it.val != nullptr &&
    1839           2 :                          json_object_get_type(it.val) == json_type_object)
    1840             :                 {
    1841           2 :                     if (bFlattenGeocouchSpatiallistFormat < 0)
    1842           2 :                         bFlattenGeocouchSpatiallistFormat =
    1843           2 :                             CPLTestBool(CPLGetConfigOption(
    1844             :                                 "GEOJSON_FLATTEN_GEOCOUCH", "TRUE"));
    1845           2 :                     if (bFlattenGeocouchSpatiallistFormat)
    1846             :                     {
    1847           2 :                         auto typeIter = oMapFieldNameToIdx.find("type");
    1848           2 :                         if (typeIter != oMapFieldNameToIdx.end())
    1849             :                         {
    1850           2 :                             const int nIdx = typeIter->second;
    1851           2 :                             apoFieldDefn.erase(apoFieldDefn.begin() + nIdx);
    1852           2 :                             oMapFieldNameToIdx.erase(typeIter);
    1853           2 :                             dag.removeNode(nIdx);
    1854             :                         }
    1855             : 
    1856           2 :                         bIsGeocouchSpatiallistFormat = true;
    1857           2 :                         return GenerateFeatureDefn(oMapFieldNameToIdx,
    1858             :                                                    apoFieldDefn, dag, poLayer,
    1859           2 :                                                    poObj);
    1860             :                     }
    1861             :                 }
    1862             :             }
    1863             : 
    1864        4282 :             anCurFieldIndices.clear();
    1865        4282 :             OGRGeoJSONReaderAddOrUpdateField(
    1866        4282 :                 anCurFieldIndices, oMapFieldNameToIdx, apoFieldDefn, it.key,
    1867        4282 :                 it.val, bFlattenNestedAttributes_, chNestedAttributeSeparator_,
    1868        4282 :                 bArrayAsString_, bDateAsString_, aoSetUndeterminedTypeFields_);
    1869        8566 :             for (int idx : anCurFieldIndices)
    1870             :             {
    1871        4284 :                 dag.addNode(idx, apoFieldDefn[idx]->GetNameRef());
    1872        4284 :                 if (nPrevFieldIdx != -1)
    1873             :                 {
    1874        2004 :                     dag.addEdge(nPrevFieldIdx, idx);
    1875             :                 }
    1876        4284 :                 nPrevFieldIdx = idx;
    1877             :             }
    1878             :         }
    1879             : 
    1880        3064 :         bSuccess = true;  // SUCCESS
    1881             :     }
    1882          46 :     else if (nullptr != poObjPropsEntry &&
    1883           1 :              (poObjProps == nullptr ||
    1884           1 :               (json_object_get_type(poObjProps) == json_type_array &&
    1885           1 :                json_object_array_length(poObjProps) == 0)))
    1886             :     {
    1887             :         // Ignore "properties": null and "properties": []
    1888          13 :         bSuccess = true;
    1889             :     }
    1890          63 :     else if (poObj != nullptr &&
    1891          31 :              json_object_get_type(poObj) == json_type_object)
    1892             :     {
    1893             :         json_object_iter it;
    1894          31 :         it.key = nullptr;
    1895          31 :         it.val = nullptr;
    1896          31 :         it.entry = nullptr;
    1897          97 :         json_object_object_foreachC(poObj, it)
    1898             :         {
    1899          66 :             if (strcmp(it.key, "type") != 0 &&
    1900          35 :                 strcmp(it.key, "geometry") != 0 &&
    1901           4 :                 strcmp(it.key, "centroid") != 0 &&
    1902           4 :                 strcmp(it.key, "bbox") != 0 && strcmp(it.key, "center") != 0)
    1903             :             {
    1904           4 :                 if (oMapFieldNameToIdx.find(it.key) == oMapFieldNameToIdx.end())
    1905             :                 {
    1906           4 :                     anCurFieldIndices.clear();
    1907           4 :                     OGRGeoJSONReaderAddOrUpdateField(
    1908             :                         anCurFieldIndices, oMapFieldNameToIdx, apoFieldDefn,
    1909           4 :                         it.key, it.val, bFlattenNestedAttributes_,
    1910           4 :                         chNestedAttributeSeparator_, bArrayAsString_,
    1911           4 :                         bDateAsString_, aoSetUndeterminedTypeFields_);
    1912           8 :                     for (int idx : anCurFieldIndices)
    1913             :                     {
    1914           4 :                         dag.addNode(idx, apoFieldDefn[idx]->GetNameRef());
    1915           4 :                         if (nPrevFieldIdx != -1)
    1916             :                         {
    1917           0 :                             dag.addEdge(nPrevFieldIdx, idx);
    1918             :                         }
    1919           4 :                         nPrevFieldIdx = idx;
    1920             :                     }
    1921             :                 }
    1922             :             }
    1923             :         }
    1924             : 
    1925          31 :         bSuccess = true;  // SUCCESS
    1926             :         // CPLError(CE_Failure, CPLE_AppDefined,
    1927             :         //          "Invalid Feature object. "
    1928             :         //          "Missing \'properties\' member." );
    1929             :     }
    1930             : 
    1931        3109 :     return bSuccess;
    1932             : }
    1933             : 
    1934             : /************************************************************************/
    1935             : /*                   OGRGeoJSONUpdateLayerGeomType()                    */
    1936             : /************************************************************************/
    1937             : 
    1938        3092 : bool OGRGeoJSONUpdateLayerGeomType(bool &bFirstGeom,
    1939             :                                    OGRwkbGeometryType eGeomType,
    1940             :                                    OGRwkbGeometryType &eLayerGeomType)
    1941             : {
    1942        3092 :     if (bFirstGeom)
    1943             :     {
    1944         399 :         eLayerGeomType = eGeomType;
    1945         399 :         bFirstGeom = false;
    1946             :     }
    1947        2704 :     else if (OGR_GT_HasZ(eGeomType) && !OGR_GT_HasZ(eLayerGeomType) &&
    1948          11 :              wkbFlatten(eGeomType) == wkbFlatten(eLayerGeomType))
    1949             :     {
    1950           2 :         eLayerGeomType = eGeomType;
    1951             :     }
    1952        2694 :     else if (!OGR_GT_HasZ(eGeomType) && OGR_GT_HasZ(eLayerGeomType) &&
    1953           3 :              wkbFlatten(eGeomType) == wkbFlatten(eLayerGeomType))
    1954             :     {
    1955             :         // ok
    1956             :     }
    1957        2689 :     else if (eGeomType != eLayerGeomType)
    1958             :     {
    1959          92 :         CPLDebug("GeoJSON", "Detected layer of mixed-geometry type features.");
    1960          92 :         eLayerGeomType = wkbUnknown;
    1961          92 :         return false;
    1962             :     }
    1963        3000 :     return true;
    1964             : }
    1965             : 
    1966             : /************************************************************************/
    1967             : /*                           AddFeature                                 */
    1968             : /************************************************************************/
    1969             : 
    1970          59 : bool OGRGeoJSONReader::AddFeature(OGRGeoJSONLayer *poLayer,
    1971             :                                   OGRGeometry *poGeometry)
    1972             : {
    1973          59 :     bool bAdded = false;
    1974             : 
    1975             :     // TODO: Should we check if geometry is of type of wkbGeometryCollection?
    1976             : 
    1977          59 :     if (nullptr != poGeometry)
    1978             :     {
    1979          58 :         OGRFeature *poFeature = new OGRFeature(poLayer->GetLayerDefn());
    1980          58 :         poFeature->SetGeometryDirectly(poGeometry);
    1981             : 
    1982          58 :         bAdded = AddFeature(poLayer, poFeature);
    1983             :     }
    1984             : 
    1985          59 :     return bAdded;
    1986             : }
    1987             : 
    1988             : /************************************************************************/
    1989             : /*                           AddFeature                                 */
    1990             : /************************************************************************/
    1991             : 
    1992         186 : bool OGRGeoJSONReader::AddFeature(OGRGeoJSONLayer *poLayer,
    1993             :                                   OGRFeature *poFeature)
    1994             : {
    1995         186 :     if (poFeature == nullptr)
    1996           0 :         return false;
    1997             : 
    1998         186 :     poLayer->AddFeature(poFeature);
    1999         186 :     delete poFeature;
    2000             : 
    2001         186 :     return true;
    2002             : }
    2003             : 
    2004             : /************************************************************************/
    2005             : /*                           ReadGeometry                               */
    2006             : /************************************************************************/
    2007             : 
    2008         871 : OGRGeometry *OGRGeoJSONBaseReader::ReadGeometry(json_object *poObj,
    2009             :                                                 OGRSpatialReference *poLayerSRS)
    2010             : {
    2011         871 :     OGRGeometry *poGeometry = OGRGeoJSONReadGeometry(poObj, poLayerSRS);
    2012             : 
    2013             :     /* -------------------------------------------------------------------- */
    2014             :     /*      Wrap geometry with GeometryCollection as a common denominator.  */
    2015             :     /*      Sometimes a GeoJSON text may consist of objects of different    */
    2016             :     /*      geometry types. Users may request wrapping all geometries with  */
    2017             :     /*      OGRGeometryCollection type by using option                      */
    2018             :     /*      GEOMETRY_AS_COLLECTION=NO|YES (NO is default).                  */
    2019             :     /* -------------------------------------------------------------------- */
    2020         871 :     if (nullptr != poGeometry)
    2021             :     {
    2022         856 :         if (!bGeometryPreserve_ &&
    2023           0 :             wkbGeometryCollection != poGeometry->getGeometryType())
    2024             :         {
    2025           0 :             OGRGeometryCollection *poMetaGeometry = new OGRGeometryCollection();
    2026           0 :             poMetaGeometry->addGeometryDirectly(poGeometry);
    2027           0 :             return poMetaGeometry;
    2028             :         }
    2029             :     }
    2030             : 
    2031         871 :     return poGeometry;
    2032             : }
    2033             : 
    2034             : /************************************************************************/
    2035             : /*                OGRGeoJSONReaderSetFieldNestedAttribute()             */
    2036             : /************************************************************************/
    2037             : 
    2038           2 : static void OGRGeoJSONReaderSetFieldNestedAttribute(OGRLayer *poLayer,
    2039             :                                                     OGRFeature *poFeature,
    2040             :                                                     const char *pszAttrPrefix,
    2041             :                                                     char chSeparator,
    2042             :                                                     json_object *poVal)
    2043             : {
    2044             :     json_object_iter it;
    2045           2 :     it.key = nullptr;
    2046           2 :     it.val = nullptr;
    2047           2 :     it.entry = nullptr;
    2048           6 :     json_object_object_foreachC(poVal, it)
    2049             :     {
    2050           4 :         const char szSeparator[2] = {chSeparator, '\0'};
    2051             :         const CPLString osAttrName(
    2052           8 :             CPLSPrintf("%s%s%s", pszAttrPrefix, szSeparator, it.key));
    2053           8 :         if (it.val != nullptr &&
    2054           4 :             json_object_get_type(it.val) == json_type_object)
    2055             :         {
    2056           0 :             OGRGeoJSONReaderSetFieldNestedAttribute(
    2057             :                 poLayer, poFeature, osAttrName, chSeparator, it.val);
    2058             :         }
    2059             :         else
    2060             :         {
    2061             :             const int nField =
    2062           4 :                 poFeature->GetDefnRef()->GetFieldIndexCaseSensitive(osAttrName);
    2063           4 :             OGRGeoJSONReaderSetField(poLayer, poFeature, nField, osAttrName,
    2064             :                                      it.val, false, 0);
    2065             :         }
    2066             :     }
    2067           2 : }
    2068             : 
    2069             : /************************************************************************/
    2070             : /*                   OGRGeoJSONReaderSetField()                         */
    2071             : /************************************************************************/
    2072             : 
    2073        4769 : void OGRGeoJSONReaderSetField(OGRLayer *poLayer, OGRFeature *poFeature,
    2074             :                               int nField, const char *pszAttrPrefix,
    2075             :                               json_object *poVal, bool bFlattenNestedAttributes,
    2076             :                               char chNestedAttributeSeparator)
    2077             : {
    2078        4773 :     if (bFlattenNestedAttributes && poVal != nullptr &&
    2079           4 :         json_object_get_type(poVal) == json_type_object)
    2080             :     {
    2081           2 :         OGRGeoJSONReaderSetFieldNestedAttribute(
    2082             :             poLayer, poFeature, pszAttrPrefix, chNestedAttributeSeparator,
    2083             :             poVal);
    2084           2 :         return;
    2085             :     }
    2086        4767 :     if (nField < 0)
    2087           0 :         return;
    2088             : 
    2089        4767 :     OGRFieldDefn *poFieldDefn = poFeature->GetFieldDefnRef(nField);
    2090        4767 :     CPLAssert(nullptr != poFieldDefn);
    2091        4767 :     OGRFieldType eType = poFieldDefn->GetType();
    2092             : 
    2093        4767 :     if (poVal == nullptr)
    2094             :     {
    2095          85 :         poFeature->SetFieldNull(nField);
    2096             :     }
    2097        4682 :     else if (OFTInteger == eType)
    2098             :     {
    2099        1286 :         poFeature->SetField(nField, json_object_get_int(poVal));
    2100             : 
    2101             :         // Check if FID available and set correct value.
    2102        1286 :         if (EQUAL(poFieldDefn->GetNameRef(), poLayer->GetFIDColumn()))
    2103         144 :             poFeature->SetFID(json_object_get_int(poVal));
    2104             :     }
    2105        3396 :     else if (OFTInteger64 == eType)
    2106             :     {
    2107          26 :         poFeature->SetField(nField, (GIntBig)json_object_get_int64(poVal));
    2108             : 
    2109             :         // Check if FID available and set correct value.
    2110          26 :         if (EQUAL(poFieldDefn->GetNameRef(), poLayer->GetFIDColumn()))
    2111           0 :             poFeature->SetFID(
    2112           0 :                 static_cast<GIntBig>(json_object_get_int64(poVal)));
    2113             :     }
    2114        3370 :     else if (OFTReal == eType)
    2115             :     {
    2116        1148 :         poFeature->SetField(nField, json_object_get_double(poVal));
    2117             :     }
    2118        2222 :     else if (OFTIntegerList == eType)
    2119             :     {
    2120          58 :         const enum json_type eJSonType(json_object_get_type(poVal));
    2121          58 :         if (eJSonType == json_type_array)
    2122             :         {
    2123          50 :             const auto nLength = json_object_array_length(poVal);
    2124          50 :             int *panVal = static_cast<int *>(CPLMalloc(sizeof(int) * nLength));
    2125         105 :             for (auto i = decltype(nLength){0}; i < nLength; i++)
    2126             :             {
    2127          55 :                 json_object *poRow = json_object_array_get_idx(poVal, i);
    2128          55 :                 panVal[i] = json_object_get_int(poRow);
    2129             :             }
    2130          50 :             poFeature->SetField(nField, static_cast<int>(nLength), panVal);
    2131          50 :             CPLFree(panVal);
    2132             :         }
    2133           8 :         else if (eJSonType == json_type_boolean || eJSonType == json_type_int)
    2134             :         {
    2135           8 :             poFeature->SetField(nField, json_object_get_int(poVal));
    2136             :         }
    2137             :     }
    2138        2164 :     else if (OFTInteger64List == eType)
    2139             :     {
    2140          69 :         const enum json_type eJSonType(json_object_get_type(poVal));
    2141          69 :         if (eJSonType == json_type_array)
    2142             :         {
    2143          59 :             const auto nLength = json_object_array_length(poVal);
    2144             :             GIntBig *panVal =
    2145          59 :                 static_cast<GIntBig *>(CPLMalloc(sizeof(GIntBig) * nLength));
    2146         125 :             for (auto i = decltype(nLength){0}; i < nLength; i++)
    2147             :             {
    2148          66 :                 json_object *poRow = json_object_array_get_idx(poVal, i);
    2149          66 :                 panVal[i] = static_cast<GIntBig>(json_object_get_int64(poRow));
    2150             :             }
    2151          59 :             poFeature->SetField(nField, static_cast<int>(nLength), panVal);
    2152          59 :             CPLFree(panVal);
    2153             :         }
    2154          10 :         else if (eJSonType == json_type_boolean || eJSonType == json_type_int)
    2155             :         {
    2156          10 :             poFeature->SetField(
    2157          10 :                 nField, static_cast<GIntBig>(json_object_get_int64(poVal)));
    2158             :         }
    2159             :     }
    2160        2095 :     else if (OFTRealList == eType)
    2161             :     {
    2162          95 :         const enum json_type eJSonType(json_object_get_type(poVal));
    2163          95 :         if (eJSonType == json_type_array)
    2164             :         {
    2165          81 :             const auto nLength = json_object_array_length(poVal);
    2166             :             double *padfVal =
    2167          81 :                 static_cast<double *>(CPLMalloc(sizeof(double) * nLength));
    2168         172 :             for (auto i = decltype(nLength){0}; i < nLength; i++)
    2169             :             {
    2170          91 :                 json_object *poRow = json_object_array_get_idx(poVal, i);
    2171          91 :                 padfVal[i] = json_object_get_double(poRow);
    2172             :             }
    2173          81 :             poFeature->SetField(nField, static_cast<int>(nLength), padfVal);
    2174          81 :             CPLFree(padfVal);
    2175             :         }
    2176          14 :         else if (eJSonType == json_type_boolean || eJSonType == json_type_int ||
    2177             :                  eJSonType == json_type_double)
    2178             :         {
    2179          14 :             poFeature->SetField(nField, json_object_get_double(poVal));
    2180             :         }
    2181             :     }
    2182        2000 :     else if (OFTStringList == eType)
    2183             :     {
    2184          79 :         const enum json_type eJSonType(json_object_get_type(poVal));
    2185          79 :         if (eJSonType == json_type_array)
    2186             :         {
    2187          69 :             auto nLength = json_object_array_length(poVal);
    2188             :             char **papszVal =
    2189          69 :                 (char **)CPLMalloc(sizeof(char *) * (nLength + 1));
    2190          69 :             decltype(nLength) i = 0;  // Used after for.
    2191         141 :             for (; i < nLength; i++)
    2192             :             {
    2193          72 :                 json_object *poRow = json_object_array_get_idx(poVal, i);
    2194          72 :                 const char *pszVal = json_object_get_string(poRow);
    2195          72 :                 if (pszVal == nullptr)
    2196           0 :                     break;
    2197          72 :                 papszVal[i] = CPLStrdup(pszVal);
    2198             :             }
    2199          69 :             papszVal[i] = nullptr;
    2200          69 :             poFeature->SetField(nField, papszVal);
    2201          69 :             CSLDestroy(papszVal);
    2202             :         }
    2203             :         else
    2204             :         {
    2205          10 :             poFeature->SetField(nField, json_object_get_string(poVal));
    2206             :         }
    2207             :     }
    2208             :     else
    2209             :     {
    2210        1921 :         poFeature->SetField(nField, json_object_get_string(poVal));
    2211             :     }
    2212             : }
    2213             : 
    2214             : /************************************************************************/
    2215             : /*                           ReadFeature()                              */
    2216             : /************************************************************************/
    2217             : 
    2218        1158 : OGRFeature *OGRGeoJSONBaseReader::ReadFeature(OGRLayer *poLayer,
    2219             :                                               json_object *poObj,
    2220             :                                               const char *pszSerializedObj)
    2221             : {
    2222        1158 :     CPLAssert(nullptr != poObj);
    2223             : 
    2224        1158 :     OGRFeatureDefn *poFDefn = poLayer->GetLayerDefn();
    2225        1158 :     OGRFeature *poFeature = new OGRFeature(poFDefn);
    2226             : 
    2227        1158 :     if (bStoreNativeData_)
    2228             :     {
    2229          56 :         poFeature->SetNativeData(pszSerializedObj
    2230             :                                      ? pszSerializedObj
    2231          10 :                                      : json_object_to_json_string(poObj));
    2232          46 :         poFeature->SetNativeMediaType("application/vnd.geo+json");
    2233             :     }
    2234             : 
    2235             :     /* -------------------------------------------------------------------- */
    2236             :     /*      Translate GeoJSON "properties" object to feature attributes.    */
    2237             :     /* -------------------------------------------------------------------- */
    2238        1158 :     CPLAssert(nullptr != poFeature);
    2239             : 
    2240        1158 :     json_object *poObjProps = OGRGeoJSONFindMemberByName(poObj, "properties");
    2241        2276 :     if (!bAttributesSkip_ && nullptr != poObjProps &&
    2242        1118 :         json_object_get_type(poObjProps) == json_type_object)
    2243             :     {
    2244        1117 :         if (bIsGeocouchSpatiallistFormat)
    2245             :         {
    2246           3 :             json_object *poId = CPL_json_object_object_get(poObjProps, "_id");
    2247           6 :             if (poId != nullptr &&
    2248           3 :                 json_object_get_type(poId) == json_type_string)
    2249           3 :                 poFeature->SetField("_id", json_object_get_string(poId));
    2250             : 
    2251           3 :             json_object *poRev = CPL_json_object_object_get(poObjProps, "_rev");
    2252           6 :             if (poRev != nullptr &&
    2253           3 :                 json_object_get_type(poRev) == json_type_string)
    2254             :             {
    2255           3 :                 poFeature->SetField("_rev", json_object_get_string(poRev));
    2256             :             }
    2257             : 
    2258           3 :             poObjProps = CPL_json_object_object_get(poObjProps, "properties");
    2259           6 :             if (nullptr == poObjProps ||
    2260           3 :                 json_object_get_type(poObjProps) != json_type_object)
    2261             :             {
    2262           0 :                 return poFeature;
    2263             :             }
    2264             :         }
    2265             : 
    2266             :         json_object_iter it;
    2267        1117 :         it.key = nullptr;
    2268        1117 :         it.val = nullptr;
    2269        1117 :         it.entry = nullptr;
    2270        4373 :         json_object_object_foreachC(poObjProps, it)
    2271             :         {
    2272        3256 :             const int nField = poFDefn->GetFieldIndexCaseSensitive(it.key);
    2273        3258 :             if (nField < 0 &&
    2274           2 :                 !(bFlattenNestedAttributes_ && it.val != nullptr &&
    2275           2 :                   json_object_get_type(it.val) == json_type_object))
    2276             :             {
    2277           0 :                 CPLDebug("GeoJSON", "Cannot find field %s", it.key);
    2278             :             }
    2279             :             else
    2280             :             {
    2281        3256 :                 OGRGeoJSONReaderSetField(poLayer, poFeature, nField, it.key,
    2282        3256 :                                          it.val, bFlattenNestedAttributes_,
    2283        3256 :                                          chNestedAttributeSeparator_);
    2284             :             }
    2285             :         }
    2286             :     }
    2287             : 
    2288        1158 :     if (!bAttributesSkip_ && nullptr == poObjProps)
    2289             :     {
    2290             :         json_object_iter it;
    2291          40 :         it.key = nullptr;
    2292          40 :         it.val = nullptr;
    2293          40 :         it.entry = nullptr;
    2294         134 :         json_object_object_foreachC(poObj, it)
    2295             :         {
    2296          94 :             const int nFldIndex = poFDefn->GetFieldIndexCaseSensitive(it.key);
    2297          94 :             if (nFldIndex >= 0)
    2298             :             {
    2299           4 :                 if (it.val)
    2300           4 :                     poFeature->SetField(nFldIndex,
    2301             :                                         json_object_get_string(it.val));
    2302             :                 else
    2303           0 :                     poFeature->SetFieldNull(nFldIndex);
    2304             :             }
    2305             :         }
    2306             :     }
    2307             : 
    2308             :     /* -------------------------------------------------------------------- */
    2309             :     /*      Try to use feature-level ID if available                        */
    2310             :     /*      and of integral type. Otherwise, leave unset (-1) then index    */
    2311             :     /*      in features sequence will be used as FID.                       */
    2312             :     /* -------------------------------------------------------------------- */
    2313        1158 :     json_object *poObjId = OGRGeoJSONFindMemberByName(poObj, "id");
    2314        1158 :     if (nullptr != poObjId && bFeatureLevelIdAsFID_)
    2315             :     {
    2316          51 :         poFeature->SetFID(static_cast<GIntBig>(json_object_get_int64(poObjId)));
    2317             :     }
    2318             : 
    2319             :     /* -------------------------------------------------------------------- */
    2320             :     /*      Handle the case where the special id is in a regular field.     */
    2321             :     /* -------------------------------------------------------------------- */
    2322        1107 :     else if (nullptr != poObjId)
    2323             :     {
    2324          43 :         const int nIdx = poFDefn->GetFieldIndexCaseSensitive("id");
    2325          43 :         if (nIdx >= 0 && !poFeature->IsFieldSet(nIdx))
    2326             :         {
    2327          39 :             poFeature->SetField(nIdx, json_object_get_string(poObjId));
    2328             :         }
    2329             :     }
    2330             : 
    2331             :     /* -------------------------------------------------------------------- */
    2332             :     /*      Translate geometry sub-object of GeoJSON Feature.               */
    2333             :     /* -------------------------------------------------------------------- */
    2334        1158 :     json_object *poObjGeom = nullptr;
    2335        1158 :     json_object *poTmp = poObj;
    2336             :     json_object_iter it;
    2337        1158 :     it.key = nullptr;
    2338        1158 :     it.val = nullptr;
    2339        1158 :     it.entry = nullptr;
    2340        4361 :     json_object_object_foreachC(poTmp, it)
    2341             :     {
    2342        3532 :         if (EQUAL(it.key, "geometry"))
    2343             :         {
    2344        1137 :             if (it.val != nullptr)
    2345         808 :                 poObjGeom = it.val;
    2346             :             // Done.  They had 'geometry':null.
    2347             :             else
    2348         329 :                 return poFeature;
    2349             :         }
    2350             :     }
    2351             : 
    2352         829 :     if (nullptr != poObjGeom)
    2353             :     {
    2354             :         // NOTE: If geometry can not be parsed or read correctly
    2355             :         //       then NULL geometry is assigned to a feature and
    2356             :         //       geometry type for layer is classified as wkbUnknown.
    2357             :         OGRGeometry *poGeometry =
    2358         808 :             ReadGeometry(poObjGeom, poLayer->GetSpatialRef());
    2359         808 :         if (nullptr != poGeometry)
    2360             :         {
    2361         794 :             poFeature->SetGeometryDirectly(poGeometry);
    2362             :         }
    2363             :     }
    2364             :     else
    2365             :     {
    2366             :         static bool bWarned = false;
    2367          21 :         if (!bWarned)
    2368             :         {
    2369           1 :             bWarned = true;
    2370           1 :             CPLDebug(
    2371             :                 "GeoJSON",
    2372             :                 "Non conformant Feature object. Missing \'geometry\' member.");
    2373             :         }
    2374             :     }
    2375             : 
    2376         829 :     return poFeature;
    2377             : }
    2378             : 
    2379             : /************************************************************************/
    2380             : /*                           Extent getters                             */
    2381             : /************************************************************************/
    2382             : 
    2383          26 : bool OGRGeoJSONBaseReader::ExtentRead() const
    2384             : {
    2385          26 :     return m_bExtentRead;
    2386             : }
    2387             : 
    2388          20 : OGREnvelope3D OGRGeoJSONBaseReader::GetExtent3D() const
    2389             : {
    2390          20 :     return m_oEnvelope3D;
    2391             : }
    2392             : 
    2393             : /************************************************************************/
    2394             : /*                           ReadFeatureCollection()                    */
    2395             : /************************************************************************/
    2396             : 
    2397          91 : void OGRGeoJSONReader::ReadFeatureCollection(OGRGeoJSONLayer *poLayer,
    2398             :                                              json_object *poObj)
    2399             : {
    2400          91 :     json_object *poObjFeatures = OGRGeoJSONFindMemberByName(poObj, "features");
    2401          91 :     if (nullptr == poObjFeatures)
    2402             :     {
    2403           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2404             :                  "Invalid FeatureCollection object. "
    2405             :                  "Missing \'features\' member.");
    2406           0 :         return;
    2407             :     }
    2408             : 
    2409          91 :     if (json_type_array == json_object_get_type(poObjFeatures))
    2410             :     {
    2411          91 :         const auto nFeatures = json_object_array_length(poObjFeatures);
    2412         205 :         for (auto i = decltype(nFeatures){0}; i < nFeatures; ++i)
    2413             :         {
    2414             :             json_object *poObjFeature =
    2415         114 :                 json_object_array_get_idx(poObjFeatures, i);
    2416         114 :             OGRFeature *poFeature = ReadFeature(poLayer, poObjFeature, nullptr);
    2417         114 :             AddFeature(poLayer, poFeature);
    2418             :         }
    2419             :     }
    2420             : 
    2421             :     // Collect top objects except 'type' and the 'features' array.
    2422          91 :     if (bStoreNativeData_)
    2423             :     {
    2424             :         json_object_iter it;
    2425           5 :         it.key = nullptr;
    2426           5 :         it.val = nullptr;
    2427           5 :         it.entry = nullptr;
    2428          10 :         CPLString osNativeData;
    2429          25 :         json_object_object_foreachC(poObj, it)
    2430             :         {
    2431          20 :             if (strcmp(it.key, "type") == 0 || strcmp(it.key, "features") == 0)
    2432             :             {
    2433          10 :                 continue;
    2434             :             }
    2435          10 :             if (osNativeData.empty())
    2436           3 :                 osNativeData = "{ ";
    2437             :             else
    2438           7 :                 osNativeData += ", ";
    2439          10 :             json_object *poKey = json_object_new_string(it.key);
    2440          10 :             osNativeData += json_object_to_json_string(poKey);
    2441          10 :             json_object_put(poKey);
    2442          10 :             osNativeData += ": ";
    2443          10 :             osNativeData += json_object_to_json_string(it.val);
    2444             :         }
    2445           5 :         if (osNativeData.empty())
    2446             :         {
    2447           2 :             osNativeData = "{ ";
    2448             :         }
    2449           5 :         osNativeData += " }";
    2450             : 
    2451           5 :         osNativeData = "NATIVE_DATA=" + osNativeData;
    2452             : 
    2453           5 :         char *apszMetadata[3] = {
    2454           5 :             const_cast<char *>(osNativeData.c_str()),
    2455             :             const_cast<char *>("NATIVE_MEDIA_TYPE=application/vnd.geo+json"),
    2456           5 :             nullptr};
    2457             : 
    2458           5 :         poLayer->SetMetadata(apszMetadata, "NATIVE_DATA");
    2459             :     }
    2460             : }
    2461             : 
    2462             : /************************************************************************/
    2463             : /*                           OGRGeoJSONFindMemberByName                 */
    2464             : /************************************************************************/
    2465             : 
    2466       18789 : lh_entry *OGRGeoJSONFindMemberEntryByName(json_object *poObj,
    2467             :                                           const char *pszName)
    2468             : {
    2469       18789 :     if (nullptr == pszName || nullptr == poObj)
    2470           4 :         return nullptr;
    2471             : 
    2472       18785 :     if (nullptr != json_object_get_object(poObj))
    2473             :     {
    2474       18779 :         lh_entry *entry = json_object_get_object(poObj)->head;
    2475       46528 :         while (entry != nullptr)
    2476             :         {
    2477       40074 :             if (EQUAL(static_cast<const char *>(entry->k), pszName))
    2478       12325 :                 return entry;
    2479       27749 :             entry = entry->next;
    2480             :         }
    2481             :     }
    2482             : 
    2483        6460 :     return nullptr;
    2484             : }
    2485             : 
    2486       14178 : json_object *OGRGeoJSONFindMemberByName(json_object *poObj, const char *pszName)
    2487             : {
    2488       14178 :     lh_entry *entry = OGRGeoJSONFindMemberEntryByName(poObj, pszName);
    2489       14178 :     if (nullptr == entry)
    2490        4936 :         return nullptr;
    2491        9242 :     return (json_object *)entry->v;
    2492             : }
    2493             : 
    2494             : /************************************************************************/
    2495             : /*                           OGRGeoJSONGetType                          */
    2496             : /************************************************************************/
    2497             : 
    2498        2466 : GeoJSONObject::Type OGRGeoJSONGetType(json_object *poObj)
    2499             : {
    2500        2466 :     if (nullptr == poObj)
    2501           0 :         return GeoJSONObject::eUnknown;
    2502             : 
    2503        2466 :     json_object *poObjType = OGRGeoJSONFindMemberByName(poObj, "type");
    2504        2466 :     if (nullptr == poObjType)
    2505          19 :         return GeoJSONObject::eUnknown;
    2506             : 
    2507        2447 :     const char *name = json_object_get_string(poObjType);
    2508        2447 :     if (EQUAL(name, "Point"))
    2509         360 :         return GeoJSONObject::ePoint;
    2510        2087 :     else if (EQUAL(name, "LineString"))
    2511          53 :         return GeoJSONObject::eLineString;
    2512        2034 :     else if (EQUAL(name, "Polygon"))
    2513         614 :         return GeoJSONObject::ePolygon;
    2514        1420 :     else if (EQUAL(name, "MultiPoint"))
    2515         138 :         return GeoJSONObject::eMultiPoint;
    2516        1282 :     else if (EQUAL(name, "MultiLineString"))
    2517          34 :         return GeoJSONObject::eMultiLineString;
    2518        1248 :     else if (EQUAL(name, "MultiPolygon"))
    2519         415 :         return GeoJSONObject::eMultiPolygon;
    2520         833 :     else if (EQUAL(name, "GeometryCollection"))
    2521          14 :         return GeoJSONObject::eGeometryCollection;
    2522         819 :     else if (EQUAL(name, "Feature"))
    2523         543 :         return GeoJSONObject::eFeature;
    2524         276 :     else if (EQUAL(name, "FeatureCollection"))
    2525         276 :         return GeoJSONObject::eFeatureCollection;
    2526             :     else
    2527           0 :         return GeoJSONObject::eUnknown;
    2528             : }
    2529             : 
    2530             : /************************************************************************/
    2531             : /*                   OGRGeoJSONGetOGRGeometryType()                     */
    2532             : /************************************************************************/
    2533             : 
    2534        3035 : OGRwkbGeometryType OGRGeoJSONGetOGRGeometryType(json_object *poObj)
    2535             : {
    2536        3035 :     if (nullptr == poObj)
    2537           1 :         return wkbUnknown;
    2538             : 
    2539        3034 :     json_object *poObjType = CPL_json_object_object_get(poObj, "type");
    2540        3034 :     if (nullptr == poObjType)
    2541           0 :         return wkbUnknown;
    2542             : 
    2543        3034 :     OGRwkbGeometryType eType = wkbUnknown;
    2544        3034 :     const char *name = json_object_get_string(poObjType);
    2545        3034 :     if (EQUAL(name, "Point"))
    2546         318 :         eType = wkbPoint;
    2547        2716 :     else if (EQUAL(name, "LineString"))
    2548          49 :         eType = wkbLineString;
    2549        2667 :     else if (EQUAL(name, "Polygon"))
    2550        2367 :         eType = wkbPolygon;
    2551         300 :     else if (EQUAL(name, "MultiPoint"))
    2552          25 :         eType = wkbMultiPoint;
    2553         275 :     else if (EQUAL(name, "MultiLineString"))
    2554          28 :         eType = wkbMultiLineString;
    2555         247 :     else if (EQUAL(name, "MultiPolygon"))
    2556         223 :         eType = wkbMultiPolygon;
    2557          24 :     else if (EQUAL(name, "GeometryCollection"))
    2558          19 :         eType = wkbGeometryCollection;
    2559             :     else
    2560           5 :         return wkbUnknown;
    2561             : 
    2562             :     json_object *poCoordinates;
    2563        3029 :     if (eType == wkbGeometryCollection)
    2564             :     {
    2565             :         json_object *poGeometries =
    2566          19 :             CPL_json_object_object_get(poObj, "geometries");
    2567          37 :         if (poGeometries &&
    2568          37 :             json_object_get_type(poGeometries) == json_type_array &&
    2569          18 :             json_object_array_length(poGeometries) > 0)
    2570             :         {
    2571          17 :             if (OGR_GT_HasZ(OGRGeoJSONGetOGRGeometryType(
    2572          17 :                     json_object_array_get_idx(poGeometries, 0))))
    2573           7 :                 eType = OGR_GT_SetZ(eType);
    2574             :         }
    2575             :     }
    2576             :     else
    2577             :     {
    2578        3010 :         poCoordinates = CPL_json_object_object_get(poObj, "coordinates");
    2579        6014 :         if (poCoordinates &&
    2580        6014 :             json_object_get_type(poCoordinates) == json_type_array &&
    2581        3004 :             json_object_array_length(poCoordinates) > 0)
    2582             :         {
    2583             :             while (true)
    2584             :             {
    2585        8492 :                 auto poChild = json_object_array_get_idx(poCoordinates, 0);
    2586       16974 :                 if (!(poChild &&
    2587        8482 :                       json_object_get_type(poChild) == json_type_array &&
    2588        5497 :                       json_object_array_length(poChild) > 0))
    2589             :                 {
    2590        2999 :                     if (json_object_array_length(poCoordinates) == 3)
    2591         111 :                         eType = OGR_GT_SetZ(eType);
    2592        2999 :                     break;
    2593             :                 }
    2594        5493 :                 poCoordinates = poChild;
    2595        5493 :             }
    2596             :         }
    2597             :     }
    2598             : 
    2599        3029 :     return eType;
    2600             : }
    2601             : 
    2602             : /************************************************************************/
    2603             : /*                           OGRGeoJSONReadGeometry                     */
    2604             : /************************************************************************/
    2605             : 
    2606        1500 : OGRGeometry *OGRGeoJSONReadGeometry(json_object *poObj,
    2607             :                                     OGRSpatialReference *poParentSRS)
    2608             : {
    2609             : 
    2610        1500 :     OGRGeometry *poGeometry = nullptr;
    2611        1500 :     OGRSpatialReference *poSRS = nullptr;
    2612        1500 :     lh_entry *entry = OGRGeoJSONFindMemberEntryByName(poObj, "crs");
    2613        1500 :     if (entry != nullptr)
    2614             :     {
    2615           4 :         json_object *poObjSrs = (json_object *)entry->v;
    2616           4 :         if (poObjSrs != nullptr)
    2617             :         {
    2618           3 :             poSRS = OGRGeoJSONReadSpatialReference(poObj);
    2619             :         }
    2620             :     }
    2621             : 
    2622        1500 :     OGRSpatialReference *poSRSToAssign = nullptr;
    2623        1500 :     if (entry != nullptr)
    2624             :     {
    2625           4 :         poSRSToAssign = poSRS;
    2626             :     }
    2627        1496 :     else if (poParentSRS)
    2628             :     {
    2629         882 :         poSRSToAssign = poParentSRS;
    2630             :     }
    2631             :     else
    2632             :     {
    2633             :         // Assign WGS84 if no CRS defined on geometry.
    2634         614 :         poSRSToAssign = OGRSpatialReference::GetWGS84SRS();
    2635             :     }
    2636             : 
    2637        1500 :     GeoJSONObject::Type objType = OGRGeoJSONGetType(poObj);
    2638        1500 :     if (GeoJSONObject::ePoint == objType)
    2639         336 :         poGeometry = OGRGeoJSONReadPoint(poObj);
    2640        1164 :     else if (GeoJSONObject::eMultiPoint == objType)
    2641          58 :         poGeometry = OGRGeoJSONReadMultiPoint(poObj);
    2642        1106 :     else if (GeoJSONObject::eLineString == objType)
    2643          49 :         poGeometry = OGRGeoJSONReadLineString(poObj);
    2644        1057 :     else if (GeoJSONObject::eMultiLineString == objType)
    2645          32 :         poGeometry = OGRGeoJSONReadMultiLineString(poObj);
    2646        1025 :     else if (GeoJSONObject::ePolygon == objType)
    2647         610 :         poGeometry = OGRGeoJSONReadPolygon(poObj);
    2648         415 :     else if (GeoJSONObject::eMultiPolygon == objType)
    2649         405 :         poGeometry = OGRGeoJSONReadMultiPolygon(poObj);
    2650          10 :     else if (GeoJSONObject::eGeometryCollection == objType)
    2651          10 :         poGeometry = OGRGeoJSONReadGeometryCollection(poObj, poSRSToAssign);
    2652             :     else
    2653             :     {
    2654           0 :         CPLDebug("GeoJSON", "Unsupported geometry type detected. "
    2655             :                             "Feature gets NULL geometry assigned.");
    2656             :     }
    2657             : 
    2658        1500 :     if (poGeometry && GeoJSONObject::eGeometryCollection != objType)
    2659        1476 :         poGeometry->assignSpatialReference(poSRSToAssign);
    2660             : 
    2661        1500 :     if (poSRS)
    2662           3 :         poSRS->Release();
    2663             : 
    2664        1500 :     return poGeometry;
    2665             : }
    2666             : 
    2667             : /************************************************************************/
    2668             : /*                        OGRGeoJSONGetCoordinate()                     */
    2669             : /************************************************************************/
    2670             : 
    2671      161563 : static double OGRGeoJSONGetCoordinate(json_object *poObj,
    2672             :                                       const char *pszCoordName, int nIndex,
    2673             :                                       bool &bValid)
    2674             : {
    2675      161563 :     json_object *poObjCoord = json_object_array_get_idx(poObj, nIndex);
    2676      161563 :     if (nullptr == poObjCoord)
    2677             :     {
    2678           5 :         CPLDebug("GeoJSON", "Point: got null object for %s.", pszCoordName);
    2679           5 :         bValid = false;
    2680           5 :         return 0.0;
    2681             :     }
    2682             : 
    2683      161558 :     const int iType = json_object_get_type(poObjCoord);
    2684      161558 :     if (json_type_double != iType && json_type_int != iType)
    2685             :     {
    2686           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2687             :                  "Invalid '%s' coordinate. "
    2688             :                  "Type is not double or integer for \'%s\'.",
    2689             :                  pszCoordName, json_object_to_json_string(poObjCoord));
    2690           0 :         bValid = false;
    2691           0 :         return 0.0;
    2692             :     }
    2693             : 
    2694      161558 :     return json_object_get_double(poObjCoord);
    2695             : }
    2696             : 
    2697             : /************************************************************************/
    2698             : /*                           OGRGeoJSONReadRawPoint                     */
    2699             : /************************************************************************/
    2700             : 
    2701       80588 : bool OGRGeoJSONReadRawPoint(json_object *poObj, OGRPoint &point)
    2702             : {
    2703       80588 :     CPLAssert(nullptr != poObj);
    2704             : 
    2705       80588 :     if (json_type_array == json_object_get_type(poObj))
    2706             :     {
    2707       80588 :         const auto nSize = json_object_array_length(poObj);
    2708             : 
    2709       80588 :         if (nSize < GeoJSONObject::eMinCoordinateDimension)
    2710             :         {
    2711           1 :             CPLDebug("GeoJSON", "Invalid coord dimension. "
    2712             :                                 "At least 2 dimensions must be present.");
    2713           1 :             return false;
    2714             :         }
    2715             : 
    2716       80587 :         bool bValid = true;
    2717       80587 :         const double dfX = OGRGeoJSONGetCoordinate(poObj, "x", 0, bValid);
    2718       80587 :         const double dfY = OGRGeoJSONGetCoordinate(poObj, "y", 1, bValid);
    2719       80587 :         point.setX(dfX);
    2720       80587 :         point.setY(dfY);
    2721             : 
    2722             :         // Read Z coordinate.
    2723       80587 :         if (nSize >= GeoJSONObject::eMaxCoordinateDimension)
    2724             :         {
    2725             :             // Don't *expect* mixed-dimension geometries, although the
    2726             :             // spec doesn't explicitly forbid this.
    2727         389 :             const double dfZ = OGRGeoJSONGetCoordinate(poObj, "z", 2, bValid);
    2728         389 :             point.setZ(dfZ);
    2729             :         }
    2730             :         else
    2731             :         {
    2732       80198 :             point.flattenTo2D();
    2733             :         }
    2734       80587 :         return bValid;
    2735             :     }
    2736             : 
    2737           0 :     return false;
    2738             : }
    2739             : 
    2740             : /************************************************************************/
    2741             : /*                           OGRGeoJSONReadPoint                        */
    2742             : /************************************************************************/
    2743             : 
    2744         336 : OGRPoint *OGRGeoJSONReadPoint(json_object *poObj)
    2745             : {
    2746         336 :     CPLAssert(nullptr != poObj);
    2747             : 
    2748         336 :     json_object *poObjCoords = OGRGeoJSONFindMemberByName(poObj, "coordinates");
    2749         336 :     if (nullptr == poObjCoords)
    2750             :     {
    2751           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    2752             :                  "Invalid Point object. Missing \'coordinates\' member.");
    2753           2 :         return nullptr;
    2754             :     }
    2755             : 
    2756         334 :     OGRPoint *poPoint = new OGRPoint();
    2757         334 :     if (!OGRGeoJSONReadRawPoint(poObjCoords, *poPoint))
    2758             :     {
    2759           5 :         CPLDebug("GeoJSON", "Point: raw point parsing failure.");
    2760           5 :         delete poPoint;
    2761           5 :         return nullptr;
    2762             :     }
    2763             : 
    2764         329 :     return poPoint;
    2765             : }
    2766             : 
    2767             : /************************************************************************/
    2768             : /*                           OGRGeoJSONReadMultiPoint                   */
    2769             : /************************************************************************/
    2770             : 
    2771          58 : OGRMultiPoint *OGRGeoJSONReadMultiPoint(json_object *poObj)
    2772             : {
    2773          58 :     CPLAssert(nullptr != poObj);
    2774             : 
    2775          58 :     json_object *poObjPoints = OGRGeoJSONFindMemberByName(poObj, "coordinates");
    2776          58 :     if (nullptr == poObjPoints)
    2777             :     {
    2778           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2779             :                  "Invalid MultiPoint object. "
    2780             :                  "Missing \'coordinates\' member.");
    2781           1 :         return nullptr;
    2782             :     }
    2783             : 
    2784          57 :     OGRMultiPoint *poMultiPoint = nullptr;
    2785          57 :     if (json_type_array == json_object_get_type(poObjPoints))
    2786             :     {
    2787          57 :         const auto nPoints = json_object_array_length(poObjPoints);
    2788             : 
    2789          57 :         poMultiPoint = new OGRMultiPoint();
    2790             : 
    2791         259 :         for (auto i = decltype(nPoints){0}; i < nPoints; ++i)
    2792             :         {
    2793             :             json_object *poObjCoords =
    2794         202 :                 json_object_array_get_idx(poObjPoints, i);
    2795             : 
    2796         202 :             OGRPoint pt;
    2797         401 :             if (poObjCoords != nullptr &&
    2798         199 :                 !OGRGeoJSONReadRawPoint(poObjCoords, pt))
    2799             :             {
    2800           0 :                 delete poMultiPoint;
    2801           0 :                 CPLDebug("GeoJSON", "LineString: raw point parsing failure.");
    2802           0 :                 return nullptr;
    2803             :             }
    2804         202 :             poMultiPoint->addGeometry(&pt);
    2805             :         }
    2806             :     }
    2807             : 
    2808          57 :     return poMultiPoint;
    2809             : }
    2810             : 
    2811             : /************************************************************************/
    2812             : /*                           OGRGeoJSONReadLineString                   */
    2813             : /************************************************************************/
    2814             : 
    2815         102 : OGRLineString *OGRGeoJSONReadLineString(json_object *poObj, bool bRaw)
    2816             : {
    2817         102 :     CPLAssert(nullptr != poObj);
    2818             : 
    2819         102 :     json_object *poObjPoints = nullptr;
    2820             : 
    2821         102 :     if (!bRaw)
    2822             :     {
    2823          49 :         poObjPoints = OGRGeoJSONFindMemberByName(poObj, "coordinates");
    2824          49 :         if (nullptr == poObjPoints)
    2825             :         {
    2826           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    2827             :                      "Invalid LineString object. "
    2828             :                      "Missing \'coordinates\' member.");
    2829           1 :             return nullptr;
    2830             :         }
    2831             :     }
    2832             :     else
    2833             :     {
    2834          53 :         poObjPoints = poObj;
    2835             :     }
    2836             : 
    2837         101 :     OGRLineString *poLine = nullptr;
    2838             : 
    2839         101 :     if (json_type_array == json_object_get_type(poObjPoints))
    2840             :     {
    2841         101 :         const auto nPoints = json_object_array_length(poObjPoints);
    2842             : 
    2843         101 :         poLine = new OGRLineString();
    2844         101 :         poLine->setNumPoints(static_cast<int>(nPoints));
    2845             : 
    2846         298 :         for (auto i = decltype(nPoints){0}; i < nPoints; ++i)
    2847             :         {
    2848             :             json_object *poObjCoords =
    2849         201 :                 json_object_array_get_idx(poObjPoints, i);
    2850         201 :             if (poObjCoords == nullptr)
    2851             :             {
    2852           4 :                 delete poLine;
    2853           4 :                 CPLDebug("GeoJSON", "LineString: got null object.");
    2854           4 :                 return nullptr;
    2855             :             }
    2856             : 
    2857         197 :             OGRPoint pt;
    2858         197 :             if (!OGRGeoJSONReadRawPoint(poObjCoords, pt))
    2859             :             {
    2860           0 :                 delete poLine;
    2861           0 :                 CPLDebug("GeoJSON", "LineString: raw point parsing failure.");
    2862           0 :                 return nullptr;
    2863             :             }
    2864         197 :             if (pt.getCoordinateDimension() == 2)
    2865             :             {
    2866         171 :                 poLine->setPoint(static_cast<int>(i), pt.getX(), pt.getY());
    2867             :             }
    2868             :             else
    2869             :             {
    2870          26 :                 poLine->setPoint(static_cast<int>(i), pt.getX(), pt.getY(),
    2871             :                                  pt.getZ());
    2872             :             }
    2873             :         }
    2874             :     }
    2875             : 
    2876          97 :     return poLine;
    2877             : }
    2878             : 
    2879             : /************************************************************************/
    2880             : /*                           OGRGeoJSONReadMultiLineString              */
    2881             : /************************************************************************/
    2882             : 
    2883          32 : OGRMultiLineString *OGRGeoJSONReadMultiLineString(json_object *poObj)
    2884             : {
    2885          32 :     CPLAssert(nullptr != poObj);
    2886             : 
    2887          32 :     json_object *poObjLines = OGRGeoJSONFindMemberByName(poObj, "coordinates");
    2888          32 :     if (nullptr == poObjLines)
    2889             :     {
    2890           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2891             :                  "Invalid MultiLineString object. "
    2892             :                  "Missing \'coordinates\' member.");
    2893           1 :         return nullptr;
    2894             :     }
    2895             : 
    2896          31 :     OGRMultiLineString *poMultiLine = nullptr;
    2897             : 
    2898          31 :     if (json_type_array == json_object_get_type(poObjLines))
    2899             :     {
    2900          31 :         const auto nLines = json_object_array_length(poObjLines);
    2901             : 
    2902          31 :         poMultiLine = new OGRMultiLineString();
    2903             : 
    2904          86 :         for (auto i = decltype(nLines){0}; i < nLines; ++i)
    2905             :         {
    2906          55 :             json_object *poObjLine = json_object_array_get_idx(poObjLines, i);
    2907             : 
    2908             :             OGRLineString *poLine;
    2909          55 :             if (poObjLine != nullptr)
    2910          53 :                 poLine = OGRGeoJSONReadLineString(poObjLine, true);
    2911             :             else
    2912           2 :                 poLine = new OGRLineString();
    2913             : 
    2914          55 :             if (nullptr != poLine)
    2915             :             {
    2916          53 :                 poMultiLine->addGeometryDirectly(poLine);
    2917             :             }
    2918             :         }
    2919             :     }
    2920             : 
    2921          31 :     return poMultiLine;
    2922             : }
    2923             : 
    2924             : /************************************************************************/
    2925             : /*                           OGRGeoJSONReadLinearRing                   */
    2926             : /************************************************************************/
    2927             : 
    2928        1300 : OGRLinearRing *OGRGeoJSONReadLinearRing(json_object *poObj)
    2929             : {
    2930        1300 :     CPLAssert(nullptr != poObj);
    2931             : 
    2932        1300 :     OGRLinearRing *poRing = nullptr;
    2933             : 
    2934        1300 :     if (json_type_array == json_object_get_type(poObj))
    2935             :     {
    2936        1300 :         const auto nPoints = json_object_array_length(poObj);
    2937             : 
    2938        1300 :         poRing = new OGRLinearRing();
    2939        1300 :         poRing->setNumPoints(static_cast<int>(nPoints));
    2940             : 
    2941       81158 :         for (auto i = decltype(nPoints){0}; i < nPoints; ++i)
    2942             :         {
    2943       79858 :             json_object *poObjCoords = json_object_array_get_idx(poObj, i);
    2944       79858 :             if (poObjCoords == nullptr)
    2945             :             {
    2946           0 :                 delete poRing;
    2947           0 :                 CPLDebug("GeoJSON", "LinearRing: got null object.");
    2948           0 :                 return nullptr;
    2949             :             }
    2950             : 
    2951       79858 :             OGRPoint pt;
    2952       79858 :             if (!OGRGeoJSONReadRawPoint(poObjCoords, pt))
    2953             :             {
    2954           0 :                 delete poRing;
    2955           0 :                 CPLDebug("GeoJSON", "LinearRing: raw point parsing failure.");
    2956           0 :                 return nullptr;
    2957             :             }
    2958             : 
    2959       79858 :             if (2 == pt.getCoordinateDimension())
    2960       79701 :                 poRing->setPoint(static_cast<int>(i), pt.getX(), pt.getY());
    2961             :             else
    2962         157 :                 poRing->setPoint(static_cast<int>(i), pt.getX(), pt.getY(),
    2963             :                                  pt.getZ());
    2964             :         }
    2965             :     }
    2966             : 
    2967        1300 :     return poRing;
    2968             : }
    2969             : 
    2970             : /************************************************************************/
    2971             : /*                           OGRGeoJSONReadPolygon                      */
    2972             : /************************************************************************/
    2973             : 
    2974        1267 : OGRPolygon *OGRGeoJSONReadPolygon(json_object *poObj, bool bRaw)
    2975             : {
    2976        1267 :     CPLAssert(nullptr != poObj);
    2977             : 
    2978        1267 :     json_object *poObjRings = nullptr;
    2979             : 
    2980        1267 :     if (!bRaw)
    2981             :     {
    2982         610 :         poObjRings = OGRGeoJSONFindMemberByName(poObj, "coordinates");
    2983         610 :         if (nullptr == poObjRings)
    2984             :         {
    2985           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    2986             :                      "Invalid Polygon object. "
    2987             :                      "Missing \'coordinates\' member.");
    2988           1 :             return nullptr;
    2989             :         }
    2990             :     }
    2991             :     else
    2992             :     {
    2993         657 :         poObjRings = poObj;
    2994             :     }
    2995             : 
    2996        1266 :     OGRPolygon *poPolygon = nullptr;
    2997             : 
    2998        1266 :     if (json_type_array == json_object_get_type(poObjRings))
    2999             :     {
    3000        1266 :         const auto nRings = json_object_array_length(poObjRings);
    3001        1266 :         if (nRings > 0)
    3002             :         {
    3003        1262 :             json_object *poObjPoints = json_object_array_get_idx(poObjRings, 0);
    3004        1262 :             if (poObjPoints == nullptr)
    3005             :             {
    3006           2 :                 poPolygon = new OGRPolygon();
    3007             :             }
    3008             :             else
    3009             :             {
    3010        1260 :                 OGRLinearRing *poRing = OGRGeoJSONReadLinearRing(poObjPoints);
    3011        1260 :                 if (nullptr != poRing)
    3012             :                 {
    3013        1260 :                     poPolygon = new OGRPolygon();
    3014        1260 :                     poPolygon->addRingDirectly(poRing);
    3015             :                 }
    3016             :             }
    3017             : 
    3018        1303 :             for (auto i = decltype(nRings){1};
    3019        1303 :                  i < nRings && nullptr != poPolygon; ++i)
    3020             :             {
    3021          41 :                 poObjPoints = json_object_array_get_idx(poObjRings, i);
    3022          41 :                 if (poObjPoints != nullptr)
    3023             :                 {
    3024             :                     OGRLinearRing *poRing =
    3025          40 :                         OGRGeoJSONReadLinearRing(poObjPoints);
    3026          40 :                     if (nullptr != poRing)
    3027             :                     {
    3028          40 :                         poPolygon->addRingDirectly(poRing);
    3029             :                     }
    3030             :                 }
    3031             :             }
    3032             :         }
    3033             :         else
    3034             :         {
    3035           4 :             poPolygon = new OGRPolygon();
    3036             :         }
    3037             :     }
    3038             : 
    3039        1266 :     return poPolygon;
    3040             : }
    3041             : 
    3042             : /************************************************************************/
    3043             : /*                           OGRGeoJSONReadMultiPolygon                 */
    3044             : /************************************************************************/
    3045             : 
    3046         405 : OGRMultiPolygon *OGRGeoJSONReadMultiPolygon(json_object *poObj)
    3047             : {
    3048         405 :     CPLAssert(nullptr != poObj);
    3049             : 
    3050         405 :     json_object *poObjPolys = OGRGeoJSONFindMemberByName(poObj, "coordinates");
    3051         405 :     if (nullptr == poObjPolys)
    3052             :     {
    3053           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    3054             :                  "Invalid MultiPolygon object. "
    3055             :                  "Missing \'coordinates\' member.");
    3056           1 :         return nullptr;
    3057             :     }
    3058             : 
    3059         404 :     OGRMultiPolygon *poMultiPoly = nullptr;
    3060             : 
    3061         404 :     if (json_type_array == json_object_get_type(poObjPolys))
    3062             :     {
    3063         404 :         const auto nPolys = json_object_array_length(poObjPolys);
    3064             : 
    3065         404 :         poMultiPoly = new OGRMultiPolygon();
    3066             : 
    3067        1056 :         for (auto i = decltype(nPolys){0}; i < nPolys; ++i)
    3068             :         {
    3069         652 :             json_object *poObjPoly = json_object_array_get_idx(poObjPolys, i);
    3070         652 :             if (poObjPoly == nullptr)
    3071             :             {
    3072           3 :                 poMultiPoly->addGeometryDirectly(new OGRPolygon());
    3073             :             }
    3074             :             else
    3075             :             {
    3076         649 :                 OGRPolygon *poPoly = OGRGeoJSONReadPolygon(poObjPoly, true);
    3077         649 :                 if (nullptr != poPoly)
    3078             :                 {
    3079         649 :                     poMultiPoly->addGeometryDirectly(poPoly);
    3080             :                 }
    3081             :             }
    3082             :         }
    3083             :     }
    3084             : 
    3085         404 :     return poMultiPoly;
    3086             : }
    3087             : 
    3088             : /************************************************************************/
    3089             : /*                           OGRGeoJSONReadGeometryCollection           */
    3090             : /************************************************************************/
    3091             : 
    3092             : OGRGeometryCollection *
    3093          10 : OGRGeoJSONReadGeometryCollection(json_object *poObj, OGRSpatialReference *poSRS)
    3094             : {
    3095          10 :     CPLAssert(nullptr != poObj);
    3096             : 
    3097          10 :     json_object *poObjGeoms = OGRGeoJSONFindMemberByName(poObj, "geometries");
    3098          10 :     if (nullptr == poObjGeoms)
    3099             :     {
    3100           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    3101             :                  "Invalid GeometryCollection object. "
    3102             :                  "Missing \'geometries\' member.");
    3103           1 :         return nullptr;
    3104             :     }
    3105             : 
    3106           9 :     OGRGeometryCollection *poCollection = nullptr;
    3107             : 
    3108           9 :     if (json_type_array == json_object_get_type(poObjGeoms))
    3109             :     {
    3110           9 :         poCollection = new OGRGeometryCollection();
    3111           9 :         poCollection->assignSpatialReference(poSRS);
    3112             : 
    3113           9 :         const auto nGeoms = json_object_array_length(poObjGeoms);
    3114          25 :         for (auto i = decltype(nGeoms){0}; i < nGeoms; ++i)
    3115             :         {
    3116          16 :             json_object *poObjGeom = json_object_array_get_idx(poObjGeoms, i);
    3117          16 :             if (poObjGeom == nullptr)
    3118             :             {
    3119           3 :                 CPLDebug("GeoJSON", "Skipping null sub-geometry");
    3120           3 :                 continue;
    3121             :             }
    3122             : 
    3123          13 :             OGRGeometry *poGeometry = OGRGeoJSONReadGeometry(poObjGeom, poSRS);
    3124          13 :             if (nullptr != poGeometry)
    3125             :             {
    3126          13 :                 poCollection->addGeometryDirectly(poGeometry);
    3127             :             }
    3128             :         }
    3129             :     }
    3130             : 
    3131           9 :     return poCollection;
    3132             : }
    3133             : 
    3134             : /************************************************************************/
    3135             : /*                       OGR_G_CreateGeometryFromJson                   */
    3136             : /************************************************************************/
    3137             : 
    3138             : /** Create a OGR geometry from a GeoJSON geometry object */
    3139          46 : OGRGeometryH OGR_G_CreateGeometryFromJson(const char *pszJson)
    3140             : {
    3141          46 :     if (nullptr == pszJson)
    3142             :     {
    3143             :         // Translation failed.
    3144           0 :         return nullptr;
    3145             :     }
    3146             : 
    3147          46 :     json_object *poObj = nullptr;
    3148          46 :     if (!OGRJSonParse(pszJson, &poObj))
    3149           0 :         return nullptr;
    3150             : 
    3151          46 :     OGRGeometry *poGeometry = OGRGeoJSONReadGeometry(poObj);
    3152             : 
    3153             :     // Release JSON tree.
    3154          46 :     json_object_put(poObj);
    3155             : 
    3156          46 :     return (OGRGeometryH)poGeometry;
    3157             : }
    3158             : 
    3159             : /************************************************************************/
    3160             : /*                       json_ex_get_object_by_path()                   */
    3161             : /************************************************************************/
    3162             : 
    3163         167 : json_object *json_ex_get_object_by_path(json_object *poObj, const char *pszPath)
    3164             : {
    3165         153 :     if (poObj == nullptr || json_object_get_type(poObj) != json_type_object ||
    3166         320 :         pszPath == nullptr || *pszPath == '\0')
    3167             :     {
    3168          14 :         return nullptr;
    3169             :     }
    3170         153 :     char **papszTokens = CSLTokenizeString2(pszPath, ".", 0);
    3171         376 :     for (int i = 0; papszTokens[i] != nullptr; i++)
    3172             :     {
    3173         264 :         poObj = CPL_json_object_object_get(poObj, papszTokens[i]);
    3174         264 :         if (poObj == nullptr)
    3175          41 :             break;
    3176         223 :         if (papszTokens[i + 1] != nullptr)
    3177             :         {
    3178         111 :             if (json_object_get_type(poObj) != json_type_object)
    3179             :             {
    3180           0 :                 poObj = nullptr;
    3181           0 :                 break;
    3182             :             }
    3183             :         }
    3184             :     }
    3185         153 :     CSLDestroy(papszTokens);
    3186         153 :     return poObj;
    3187             : }
    3188             : 
    3189             : /************************************************************************/
    3190             : /*                             OGRJSonParse()                           */
    3191             : /************************************************************************/
    3192             : 
    3193        1310 : bool OGRJSonParse(const char *pszText, json_object **ppoObj, bool bVerboseError)
    3194             : {
    3195        1310 :     if (ppoObj == nullptr)
    3196           0 :         return false;
    3197        1310 :     json_tokener *jstok = json_tokener_new();
    3198        1310 :     const int nLen = pszText == nullptr ? 0 : static_cast<int>(strlen(pszText));
    3199        1310 :     *ppoObj = json_tokener_parse_ex(jstok, pszText, nLen);
    3200        1310 :     if (jstok->err != json_tokener_success)
    3201             :     {
    3202          15 :         if (bVerboseError)
    3203             :         {
    3204          13 :             CPLError(CE_Failure, CPLE_AppDefined,
    3205             :                      "JSON parsing error: %s (at offset %d)",
    3206             :                      json_tokener_error_desc(jstok->err), jstok->char_offset);
    3207             :         }
    3208             : 
    3209          15 :         json_tokener_free(jstok);
    3210          15 :         *ppoObj = nullptr;
    3211          15 :         return false;
    3212             :     }
    3213        1295 :     json_tokener_free(jstok);
    3214        1295 :     return true;
    3215             : }
    3216             : 
    3217             : /************************************************************************/
    3218             : /*                    CPL_json_object_object_get()                      */
    3219             : /************************************************************************/
    3220             : 
    3221             : // This is the same as json_object_object_get() except it will not raise
    3222             : // deprecation warning.
    3223             : 
    3224       34215 : json_object *CPL_json_object_object_get(struct json_object *obj,
    3225             :                                         const char *key)
    3226             : {
    3227       34215 :     json_object *poRet = nullptr;
    3228       34215 :     json_object_object_get_ex(obj, key, &poRet);
    3229       34215 :     return poRet;
    3230             : }
    3231             : 
    3232        2888 : bool OGRGeoJSONGetExtent3D(json_object *poObj, OGREnvelope3D *poEnvelope)
    3233             : {
    3234        2888 :     if (!poEnvelope || !poObj)
    3235             :     {
    3236           0 :         return false;
    3237             :     }
    3238             : 
    3239             :     // poObjCoords can be an array of arrays, this lambda function will
    3240             :     // recursively parse the array
    3241        5776 :     std::function<bool(json_object *, OGREnvelope3D *)> fParseCoords;
    3242      156146 :     fParseCoords = [&fParseCoords](json_object *poObjCoordsIn,
    3243      153254 :                                    OGREnvelope3D *poEnvelopeIn) -> bool
    3244             :     {
    3245      156146 :         if (json_type_array == json_object_get_type(poObjCoordsIn))
    3246             :         {
    3247      156146 :             const auto nItems = json_object_array_length(poObjCoordsIn);
    3248             : 
    3249      156146 :             double dXVal = std::numeric_limits<double>::quiet_NaN();
    3250      156146 :             double dYVal = std::numeric_limits<double>::quiet_NaN();
    3251      156146 :             double dZVal = std::numeric_limits<double>::quiet_NaN();
    3252             : 
    3253      610535 :             for (auto i = decltype(nItems){0}; i < nItems; ++i)
    3254             :             {
    3255             : 
    3256             :                 // Get the i element
    3257             :                 json_object *poObjCoordsElement =
    3258      454406 :                     json_object_array_get_idx(poObjCoordsIn, i);
    3259             : 
    3260      454406 :                 const json_type eType{json_object_get_type(poObjCoordsElement)};
    3261             : 
    3262             :                 // if it is an array, recurse
    3263      454406 :                 if (json_type_array == eType)
    3264             :                 {
    3265      153254 :                     if (!fParseCoords(poObjCoordsElement, poEnvelopeIn))
    3266             :                     {
    3267           1 :                         return false;
    3268             :                     }
    3269             :                 }
    3270      301152 :                 else if (json_type_double == eType || json_type_int == eType)
    3271             :                 {
    3272      301136 :                     switch (i)
    3273             :                     {
    3274      150448 :                         case 0:
    3275             :                         {
    3276      150448 :                             dXVal = json_object_get_double(poObjCoordsElement);
    3277      150448 :                             break;
    3278             :                         }
    3279      150447 :                         case 1:
    3280             :                         {
    3281      150447 :                             dYVal = json_object_get_double(poObjCoordsElement);
    3282      150447 :                             break;
    3283             :                         }
    3284         241 :                         case 2:
    3285             :                         {
    3286         241 :                             dZVal = json_object_get_double(poObjCoordsElement);
    3287         241 :                             break;
    3288             :                         }
    3289           0 :                         default:
    3290           0 :                             return false;
    3291             :                     }
    3292             :                 }
    3293             :                 else
    3294             :                 {
    3295          16 :                     return false;
    3296             :                 }
    3297             :             }
    3298             : 
    3299      156129 :             if (!std::isnan(dXVal) && !std::isnan(dYVal))
    3300             :             {
    3301      150446 :                 if (std::isnan(dZVal))
    3302             :                 {
    3303             :                     static_cast<OGREnvelope *>(poEnvelopeIn)
    3304      150205 :                         ->Merge(dXVal, dYVal);
    3305             :                 }
    3306             :                 else
    3307             :                 {
    3308         241 :                     poEnvelopeIn->Merge(dXVal, dYVal, dZVal);
    3309             :                 }
    3310             :             }
    3311             : 
    3312      156129 :             return true;
    3313             :         }
    3314             :         else
    3315             :         {
    3316           0 :             return false;
    3317             :         }
    3318        2888 :     };
    3319             : 
    3320             :     // This function looks for "coordinates" and for "geometries" to handle
    3321             :     // geometry collections.  It will recurse on itself to handle nested geometry.
    3322        5776 :     std::function<bool(json_object *, OGREnvelope3D *)> fParseGeometry;
    3323        2910 :     fParseGeometry = [&fParseGeometry,
    3324             :                       &fParseCoords](json_object *poObjIn,
    3325        2914 :                                      OGREnvelope3D *poEnvelopeIn) -> bool
    3326             :     {
    3327             :         // Get the "coordinates" array from the JSON object
    3328             :         json_object *poObjCoords =
    3329        2910 :             OGRGeoJSONFindMemberByName(poObjIn, "coordinates");
    3330             : 
    3331             :         // Return if found and not an array
    3332        2910 :         if (poObjCoords && json_object_get_type(poObjCoords) != json_type_array)
    3333             :         {
    3334           0 :             return false;
    3335             :         }
    3336        2910 :         else if (poObjCoords)
    3337             :         {
    3338        2892 :             return fParseCoords(poObjCoords, poEnvelopeIn);
    3339             :         }
    3340             : 
    3341             :         // Try "geometries"
    3342          18 :         if (!poObjCoords)
    3343             :         {
    3344          18 :             poObjCoords = OGRGeoJSONFindMemberByName(poObjIn, "geometries");
    3345             :         }
    3346             : 
    3347             :         // Return if not found or not an array
    3348          28 :         if (!poObjCoords ||
    3349          10 :             json_object_get_type(poObjCoords) != json_type_array)
    3350             :         {
    3351           8 :             return false;
    3352             :         }
    3353             :         else
    3354             :         {
    3355             :             // Loop thgrough the geometries
    3356          10 :             const auto nItems = json_object_array_length(poObjCoords);
    3357          30 :             for (auto i = decltype(nItems){0}; i < nItems; ++i)
    3358             :             {
    3359             :                 json_object *poObjGeometry =
    3360          22 :                     json_object_array_get_idx(poObjCoords, i);
    3361             : 
    3362             :                 // Recurse
    3363          22 :                 if (!fParseGeometry(poObjGeometry, poEnvelopeIn))
    3364             :                 {
    3365           2 :                     return false;
    3366             :                 }
    3367             :             }
    3368           8 :             return true;
    3369             :         }
    3370        2888 :     };
    3371             : 
    3372        2888 :     return fParseGeometry(poObj, poEnvelope);
    3373             : }

Generated by: LCOV version 1.14