LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/geojson - ogrtopojsonreader.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 376 383 98.2 %
Date: 2024-05-02 22:57:13 Functions: 14 14 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implementation of OGRTopoJSONReader class
       5             :  * Author:   Even Rouault, even dot rouault at spatialys.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2013, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * Permission is hereby granted, free of charge, to any person obtaining a
      11             :  * copy of this software and associated documentation files (the "Software"),
      12             :  * to deal in the Software without restriction, including without limitation
      13             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      14             :  * and/or sell copies of the Software, and to permit persons to whom the
      15             :  * Software is furnished to do so, subject to the following conditions:
      16             :  *
      17             :  * The above copyright notice and this permission notice shall be included
      18             :  * in all copies or substantial portions of the Software.
      19             :  *
      20             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      21             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      22             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      23             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      24             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      25             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      26             :  * DEALINGS IN THE SOFTWARE.
      27             :  ****************************************************************************/
      28             : 
      29             : #include "ogrgeojsonreader.h"
      30             : #include "ogrgeojsonutils.h"
      31             : #include "ogr_geojson.h"
      32             : #include <json.h>  // JSON-C
      33             : #include "ogr_api.h"
      34             : 
      35             : /************************************************************************/
      36             : /*                          OGRTopoJSONReader()                         */
      37             : /************************************************************************/
      38             : 
      39           4 : OGRTopoJSONReader::OGRTopoJSONReader() : poGJObject_(nullptr)
      40             : {
      41           4 : }
      42             : 
      43             : /************************************************************************/
      44             : /*                         ~OGRTopoJSONReader()                         */
      45             : /************************************************************************/
      46             : 
      47           8 : OGRTopoJSONReader::~OGRTopoJSONReader()
      48             : {
      49           4 :     if (nullptr != poGJObject_)
      50             :     {
      51           3 :         json_object_put(poGJObject_);
      52             :     }
      53             : 
      54           4 :     poGJObject_ = nullptr;
      55           4 : }
      56             : 
      57             : /************************************************************************/
      58             : /*                           Parse()                                    */
      59             : /************************************************************************/
      60             : 
      61           4 : OGRErr OGRTopoJSONReader::Parse(const char *pszText, bool bLooseIdentification)
      62             : {
      63           4 :     json_object *jsobj = nullptr;
      64           4 :     if (bLooseIdentification)
      65             :     {
      66           1 :         CPLPushErrorHandler(CPLQuietErrorHandler);
      67             :     }
      68           4 :     const bool bOK = nullptr != pszText && OGRJSonParse(pszText, &jsobj, true);
      69           4 :     if (bLooseIdentification)
      70             :     {
      71           1 :         CPLPopErrorHandler();
      72           1 :         CPLErrorReset();
      73             :     }
      74           4 :     if (!bOK)
      75             :     {
      76           1 :         return OGRERR_CORRUPT_DATA;
      77             :     }
      78             : 
      79             :     // JSON tree is shared for while lifetime of the reader object
      80             :     // and will be released in the destructor.
      81           3 :     poGJObject_ = jsobj;
      82           3 :     return OGRERR_NONE;
      83             : }
      84             : 
      85             : typedef struct
      86             : {
      87             :     double dfScale0;
      88             :     double dfScale1;
      89             :     double dfTranslate0;
      90             :     double dfTranslate1;
      91             :     bool bElementExists;
      92             : } ScalingParams;
      93             : 
      94             : /************************************************************************/
      95             : /*                            ParsePoint()                              */
      96             : /************************************************************************/
      97             : 
      98          89 : static bool ParsePoint(json_object *poPoint, double *pdfX, double *pdfY)
      99             : {
     100         176 :     if (poPoint != nullptr &&
     101         175 :         json_type_array == json_object_get_type(poPoint) &&
     102          86 :         json_object_array_length(poPoint) == 2)
     103             :     {
     104          80 :         json_object *poX = json_object_array_get_idx(poPoint, 0);
     105          80 :         json_object *poY = json_object_array_get_idx(poPoint, 1);
     106         159 :         if (poX != nullptr &&
     107          79 :             (json_type_int == json_object_get_type(poX) ||
     108          79 :              json_type_double == json_object_get_type(poX)) &&
     109         159 :             poY != nullptr &&
     110          79 :             (json_type_int == json_object_get_type(poY) ||
     111           6 :              json_type_double == json_object_get_type(poY)))
     112             :         {
     113          79 :             *pdfX = json_object_get_double(poX);
     114          79 :             *pdfY = json_object_get_double(poY);
     115          79 :             return true;
     116             :         }
     117             :     }
     118          10 :     return false;
     119             : }
     120             : 
     121             : /************************************************************************/
     122             : /*                             ParseArc()                               */
     123             : /************************************************************************/
     124             : 
     125          30 : static void ParseArc(OGRLineString *poLS, json_object *poArcsDB, int nArcID,
     126             :                      bool bReverse, ScalingParams *psParams)
     127             : {
     128          30 :     json_object *poArcDB = json_object_array_get_idx(poArcsDB, nArcID);
     129          30 :     if (poArcDB == nullptr || json_type_array != json_object_get_type(poArcDB))
     130           1 :         return;
     131          29 :     auto nPoints = json_object_array_length(poArcDB);
     132          29 :     double dfAccX = 0.0;
     133          29 :     double dfAccY = 0.0;
     134          29 :     int nBaseIndice = poLS->getNumPoints();
     135         109 :     for (auto i = decltype(nPoints){0}; i < nPoints; i++)
     136             :     {
     137          80 :         json_object *poPoint = json_object_array_get_idx(poArcDB, i);
     138          80 :         double dfX = 0.0;
     139          80 :         double dfY = 0.0;
     140          80 :         if (ParsePoint(poPoint, &dfX, &dfY))
     141             :         {
     142          76 :             if (psParams->bElementExists)
     143             :             {
     144          66 :                 dfAccX += dfX;
     145          66 :                 dfAccY += dfY;
     146          66 :                 dfX = dfAccX * psParams->dfScale0 + psParams->dfTranslate0;
     147          66 :                 dfY = dfAccY * psParams->dfScale1 + psParams->dfTranslate1;
     148             :             }
     149             :             else
     150             :             {
     151          10 :                 dfX = dfX * psParams->dfScale0 + psParams->dfTranslate0;
     152          10 :                 dfY = dfY * psParams->dfScale1 + psParams->dfTranslate1;
     153             :             }
     154          76 :             if (i == 0)
     155             :             {
     156          24 :                 if (!bReverse && poLS->getNumPoints() > 0)
     157             :                 {
     158           3 :                     poLS->setNumPoints(nBaseIndice + static_cast<int>(nPoints) -
     159             :                                        1);
     160           3 :                     nBaseIndice--;
     161           3 :                     continue;
     162             :                 }
     163          21 :                 else if (bReverse && poLS->getNumPoints() > 0)
     164             :                 {
     165           4 :                     poLS->setNumPoints(nBaseIndice + static_cast<int>(nPoints) -
     166             :                                        1);
     167           4 :                     nPoints--;
     168           4 :                     if (nPoints == 0)
     169           0 :                         break;
     170             :                 }
     171             :                 else
     172          17 :                     poLS->setNumPoints(nBaseIndice + static_cast<int>(nPoints));
     173             :             }
     174             : 
     175          73 :             if (!bReverse)
     176          46 :                 poLS->setPoint(nBaseIndice + static_cast<int>(i), dfX, dfY);
     177             :             else
     178          27 :                 poLS->setPoint(nBaseIndice + static_cast<int>(nPoints) - 1 -
     179             :                                    static_cast<int>(i),
     180             :                                dfX, dfY);
     181             :         }
     182             :     }
     183             : }
     184             : 
     185             : /************************************************************************/
     186             : /*                        ParseLineString()                             */
     187             : /************************************************************************/
     188             : 
     189          27 : static void ParseLineString(OGRLineString *poLS, json_object *poRing,
     190             :                             json_object *poArcsDB, ScalingParams *psParams)
     191             : {
     192          27 :     const auto nArcsDB = json_object_array_length(poArcsDB);
     193             : 
     194          27 :     const auto nArcsRing = json_object_array_length(poRing);
     195          60 :     for (auto j = decltype(nArcsRing){0}; j < nArcsRing; j++)
     196             :     {
     197          33 :         json_object *poArcId = json_object_array_get_idx(poRing, j);
     198          65 :         if (poArcId != nullptr &&
     199          32 :             json_type_int == json_object_get_type(poArcId))
     200             :         {
     201          32 :             int nArcId = json_object_get_int(poArcId);
     202          32 :             bool bReverse = false;
     203          32 :             if (nArcId < 0)
     204             :             {
     205          10 :                 nArcId = -(nArcId + 1);
     206          10 :                 bReverse = true;
     207             :             }
     208          32 :             if (nArcId < static_cast<int>(nArcsDB))
     209             :             {
     210          30 :                 ParseArc(poLS, poArcsDB, nArcId, bReverse, psParams);
     211             :             }
     212             :         }
     213             :     }
     214          27 : }
     215             : 
     216             : /************************************************************************/
     217             : /*                          ParsePolygon()                              */
     218             : /************************************************************************/
     219             : 
     220           8 : static void ParsePolygon(OGRPolygon *poPoly, json_object *poArcsObj,
     221             :                          json_object *poArcsDB, ScalingParams *psParams)
     222             : {
     223           8 :     const auto nRings = json_object_array_length(poArcsObj);
     224          16 :     for (auto i = decltype(nRings){0}; i < nRings; i++)
     225             :     {
     226           8 :         OGRLinearRing *poLR = new OGRLinearRing();
     227             : 
     228           8 :         json_object *poRing = json_object_array_get_idx(poArcsObj, i);
     229          15 :         if (poRing != nullptr &&
     230           7 :             json_type_array == json_object_get_type(poRing))
     231             :         {
     232           6 :             ParseLineString(poLR, poRing, poArcsDB, psParams);
     233             :         }
     234           8 :         poLR->closeRings();
     235           8 :         if (poLR->getNumPoints() < 4)
     236             :         {
     237           2 :             CPLDebug("TopoJSON", "Discarding polygon ring made of %d points",
     238           2 :                      poLR->getNumPoints());
     239           2 :             delete poLR;
     240             :         }
     241             :         else
     242             :         {
     243           6 :             poPoly->addRingDirectly(poLR);
     244             :         }
     245             :     }
     246           8 : }
     247             : 
     248             : /************************************************************************/
     249             : /*                       ParseMultiLineString()                         */
     250             : /************************************************************************/
     251             : 
     252           5 : static void ParseMultiLineString(OGRMultiLineString *poMLS,
     253             :                                  json_object *poArcsObj, json_object *poArcsDB,
     254             :                                  ScalingParams *psParams)
     255             : {
     256           5 :     const auto nRings = json_object_array_length(poArcsObj);
     257          10 :     for (auto i = decltype(nRings){0}; i < nRings; i++)
     258             :     {
     259           5 :         OGRLineString *poLS = new OGRLineString();
     260           5 :         poMLS->addGeometryDirectly(poLS);
     261             : 
     262           5 :         json_object *poRing = json_object_array_get_idx(poArcsObj, i);
     263           9 :         if (poRing != nullptr &&
     264           4 :             json_type_array == json_object_get_type(poRing))
     265             :         {
     266           4 :             ParseLineString(poLS, poRing, poArcsDB, psParams);
     267             :         }
     268             :     }
     269           5 : }
     270             : 
     271             : /************************************************************************/
     272             : /*                       ParseMultiPolygon()                            */
     273             : /************************************************************************/
     274             : 
     275           4 : static void ParseMultiPolygon(OGRMultiPolygon *poMultiPoly,
     276             :                               json_object *poArcsObj, json_object *poArcsDB,
     277             :                               ScalingParams *psParams)
     278             : {
     279           4 :     const auto nPolys = json_object_array_length(poArcsObj);
     280           8 :     for (auto i = decltype(nPolys){0}; i < nPolys; i++)
     281             :     {
     282           4 :         OGRPolygon *poPoly = new OGRPolygon();
     283             : 
     284           4 :         json_object *poPolyArcs = json_object_array_get_idx(poArcsObj, i);
     285           7 :         if (poPolyArcs != nullptr &&
     286           3 :             json_type_array == json_object_get_type(poPolyArcs))
     287             :         {
     288           3 :             ParsePolygon(poPoly, poPolyArcs, poArcsDB, psParams);
     289             :         }
     290             : 
     291           4 :         if (poPoly->IsEmpty())
     292             :         {
     293           2 :             delete poPoly;
     294             :         }
     295             :         else
     296             :         {
     297           2 :             poMultiPoly->addGeometryDirectly(poPoly);
     298             :         }
     299             :     }
     300           4 : }
     301             : 
     302             : /************************************************************************/
     303             : /*                          ParseObject()                               */
     304             : /************************************************************************/
     305             : 
     306          58 : static void ParseObject(const char *pszId, json_object *poObj,
     307             :                         OGRGeoJSONLayer *poLayer, json_object *poArcsDB,
     308             :                         ScalingParams *psParams)
     309             : {
     310          58 :     json_object *poType = OGRGeoJSONFindMemberByName(poObj, "type");
     311          58 :     if (poType == nullptr || json_object_get_type(poType) != json_type_string)
     312           0 :         return;
     313          58 :     const char *pszType = json_object_get_string(poType);
     314             : 
     315          58 :     json_object *poArcsObj = OGRGeoJSONFindMemberByName(poObj, "arcs");
     316             :     json_object *poCoordinatesObj =
     317          58 :         OGRGeoJSONFindMemberByName(poObj, "coordinates");
     318          58 :     if (strcmp(pszType, "Point") == 0 || strcmp(pszType, "MultiPoint") == 0)
     319             :     {
     320          26 :         if (poCoordinatesObj == nullptr ||
     321          11 :             json_type_array != json_object_get_type(poCoordinatesObj))
     322           6 :             return;
     323             :     }
     324             :     else
     325             :     {
     326          78 :         if (poArcsObj == nullptr ||
     327          35 :             json_type_array != json_object_get_type(poArcsObj))
     328          12 :             return;
     329             :     }
     330             : 
     331          40 :     if (pszId == nullptr)
     332             :     {
     333          38 :         json_object *poId = OGRGeoJSONFindMemberByName(poObj, "id");
     334          40 :         if (poId != nullptr &&
     335           2 :             (json_type_string == json_object_get_type(poId) ||
     336           1 :              json_type_int == json_object_get_type(poId)))
     337             :         {
     338           2 :             pszId = json_object_get_string(poId);
     339             :         }
     340             :     }
     341             : 
     342          40 :     OGRFeature *poFeature = new OGRFeature(poLayer->GetLayerDefn());
     343          40 :     if (pszId != nullptr)
     344           4 :         poFeature->SetField("id", pszId);
     345             : 
     346          40 :     json_object *poProperties = OGRGeoJSONFindMemberByName(poObj, "properties");
     347          47 :     if (poProperties != nullptr &&
     348           7 :         json_type_object == json_object_get_type(poProperties))
     349             :     {
     350             :         json_object_iter it;
     351           6 :         it.key = nullptr;
     352           6 :         it.val = nullptr;
     353           6 :         it.entry = nullptr;
     354          12 :         json_object_object_foreachC(poProperties, it)
     355             :         {
     356           6 :             const int nField = poFeature->GetFieldIndex(it.key);
     357           6 :             OGRGeoJSONReaderSetField(poLayer, poFeature, nField, it.key, it.val,
     358             :                                      false, 0);
     359             :         }
     360             :     }
     361             : 
     362          40 :     OGRGeometry *poGeom = nullptr;
     363          40 :     if (strcmp(pszType, "Point") == 0)
     364             :     {
     365           4 :         double dfX = 0.0;
     366           4 :         double dfY = 0.0;
     367           4 :         if (ParsePoint(poCoordinatesObj, &dfX, &dfY))
     368             :         {
     369           1 :             dfX = dfX * psParams->dfScale0 + psParams->dfTranslate0;
     370           1 :             dfY = dfY * psParams->dfScale1 + psParams->dfTranslate1;
     371           1 :             poGeom = new OGRPoint(dfX, dfY);
     372             :         }
     373             :         else
     374             :         {
     375           3 :             poGeom = new OGRPoint();
     376             :         }
     377             :     }
     378          36 :     else if (strcmp(pszType, "MultiPoint") == 0)
     379             :     {
     380           5 :         OGRMultiPoint *poMP = new OGRMultiPoint();
     381           5 :         poGeom = poMP;
     382           5 :         const auto nTuples = json_object_array_length(poCoordinatesObj);
     383          10 :         for (auto i = decltype(nTuples){0}; i < nTuples; i++)
     384             :         {
     385             :             json_object *poPair =
     386           5 :                 json_object_array_get_idx(poCoordinatesObj, i);
     387           5 :             double dfX = 0.0;
     388           5 :             double dfY = 0.0;
     389           5 :             if (ParsePoint(poPair, &dfX, &dfY))
     390             :             {
     391           2 :                 dfX = dfX * psParams->dfScale0 + psParams->dfTranslate0;
     392           2 :                 dfY = dfY * psParams->dfScale1 + psParams->dfTranslate1;
     393           2 :                 poMP->addGeometryDirectly(new OGRPoint(dfX, dfY));
     394             :             }
     395             :         }
     396             :     }
     397          31 :     else if (strcmp(pszType, "LineString") == 0)
     398             :     {
     399          17 :         OGRLineString *poLS = new OGRLineString();
     400          17 :         poGeom = poLS;
     401          17 :         ParseLineString(poLS, poArcsObj, poArcsDB, psParams);
     402             :     }
     403          14 :     else if (strcmp(pszType, "MultiLineString") == 0)
     404             :     {
     405           5 :         OGRMultiLineString *poMLS = new OGRMultiLineString();
     406           5 :         poGeom = poMLS;
     407           5 :         ParseMultiLineString(poMLS, poArcsObj, poArcsDB, psParams);
     408             :     }
     409           9 :     else if (strcmp(pszType, "Polygon") == 0)
     410             :     {
     411           5 :         OGRPolygon *poPoly = new OGRPolygon();
     412           5 :         poGeom = poPoly;
     413           5 :         ParsePolygon(poPoly, poArcsObj, poArcsDB, psParams);
     414             :     }
     415           4 :     else if (strcmp(pszType, "MultiPolygon") == 0)
     416             :     {
     417           4 :         OGRMultiPolygon *poMultiPoly = new OGRMultiPolygon();
     418           4 :         poGeom = poMultiPoly;
     419           4 :         ParseMultiPolygon(poMultiPoly, poArcsObj, poArcsDB, psParams);
     420             :     }
     421             : 
     422          40 :     if (poGeom != nullptr)
     423          40 :         poFeature->SetGeometryDirectly(poGeom);
     424          40 :     poLayer->AddFeature(poFeature);
     425          40 :     delete poFeature;
     426             : }
     427             : 
     428             : /************************************************************************/
     429             : /*                        EstablishLayerDefn()                          */
     430             : /************************************************************************/
     431             : 
     432             : static void
     433          58 : EstablishLayerDefn(int nPrevFieldIdx, std::vector<int> &anCurFieldIndices,
     434             :                    std::map<std::string, int> &oMapFieldNameToIdx,
     435             :                    std::vector<std::unique_ptr<OGRFieldDefn>> &apoFieldDefn,
     436             :                    gdal::DirectedAcyclicGraph<int, std::string> &dag,
     437             :                    json_object *poObj,
     438             :                    std::set<int> &aoSetUndeterminedTypeFields)
     439             : {
     440          58 :     json_object *poObjProps = OGRGeoJSONFindMemberByName(poObj, "properties");
     441          65 :     if (nullptr != poObjProps &&
     442           7 :         json_object_get_type(poObjProps) == json_type_object)
     443             :     {
     444             :         json_object_iter it;
     445           6 :         it.key = nullptr;
     446           6 :         it.val = nullptr;
     447           6 :         it.entry = nullptr;
     448             : 
     449          12 :         json_object_object_foreachC(poObjProps, it)
     450             :         {
     451           6 :             anCurFieldIndices.clear();
     452           6 :             OGRGeoJSONReaderAddOrUpdateField(
     453           6 :                 anCurFieldIndices, oMapFieldNameToIdx, apoFieldDefn, it.key,
     454             :                 it.val, false, 0, false, false, aoSetUndeterminedTypeFields);
     455          12 :             for (int idx : anCurFieldIndices)
     456             :             {
     457           6 :                 dag.addNode(idx, apoFieldDefn[idx]->GetNameRef());
     458           6 :                 if (nPrevFieldIdx != -1)
     459             :                 {
     460           6 :                     dag.addEdge(nPrevFieldIdx, idx);
     461             :                 }
     462           6 :                 nPrevFieldIdx = idx;
     463             :             }
     464             :         }
     465             :     }
     466          58 : }
     467             : 
     468             : /************************************************************************/
     469             : /*                        ParseObjectMain()                             */
     470             : /************************************************************************/
     471             : 
     472             : static bool
     473          65 : ParseObjectMain(const char *pszId, json_object *poObj,
     474             :                 OGRGeoJSONDataSource *poDS, OGRGeoJSONLayer **ppoMainLayer,
     475             :                 json_object *poArcs, ScalingParams *psParams,
     476             :                 std::vector<int> &anCurFieldIndices,
     477             :                 std::map<std::string, int> &oMapFieldNameToIdx,
     478             :                 std::vector<std::unique_ptr<OGRFieldDefn>> &apoFieldDefn,
     479             :                 gdal::DirectedAcyclicGraph<int, std::string> &dag,
     480             :                 std::set<int> &aoSetUndeterminedTypeFields)
     481             : {
     482          65 :     bool bNeedSecondPass = false;
     483             : 
     484          65 :     if (poObj != nullptr && json_type_object == json_object_get_type(poObj))
     485             :     {
     486          63 :         json_object *poType = OGRGeoJSONFindMemberByName(poObj, "type");
     487         125 :         if (poType != nullptr &&
     488          62 :             json_type_string == json_object_get_type(poType))
     489             :         {
     490          61 :             const char *pszType = json_object_get_string(poType);
     491          61 :             if (strcmp(pszType, "GeometryCollection") == 0)
     492             :             {
     493             :                 json_object *poGeometries =
     494           5 :                     OGRGeoJSONFindMemberByName(poObj, "geometries");
     495           8 :                 if (poGeometries != nullptr &&
     496           3 :                     json_type_array == json_object_get_type(poGeometries))
     497             :                 {
     498           3 :                     if (pszId == nullptr)
     499             :                     {
     500             :                         json_object *poId =
     501           1 :                             OGRGeoJSONFindMemberByName(poObj, "id");
     502           2 :                         if (poId != nullptr &&
     503           1 :                             (json_type_string == json_object_get_type(poId) ||
     504           0 :                              json_type_int == json_object_get_type(poId)))
     505             :                         {
     506           1 :                             pszId = json_object_get_string(poId);
     507             :                         }
     508             :                     }
     509             : 
     510             :                     OGRGeoJSONLayer *poLayer =
     511             :                         new OGRGeoJSONLayer(pszId ? pszId : "TopoJSON", nullptr,
     512           3 :                                             wkbUnknown, poDS, nullptr);
     513           3 :                     OGRFeatureDefn *poDefn = poLayer->GetLayerDefn();
     514             : 
     515             :                     const auto nGeometries =
     516           3 :                         json_object_array_length(poGeometries);
     517             :                     // First pass to establish schema.
     518             : 
     519           6 :                     std::vector<int> anCurFieldIndicesLocal;
     520           6 :                     std::map<std::string, int> oMapFieldNameToIdxLocal;
     521             :                     std::vector<std::unique_ptr<OGRFieldDefn>>
     522           6 :                         apoFieldDefnLocal;
     523           6 :                     gdal::DirectedAcyclicGraph<int, std::string> dagLocal;
     524           6 :                     std::set<int> aoSetUndeterminedTypeFieldsLocal;
     525             : 
     526             :                     apoFieldDefnLocal.emplace_back(
     527           3 :                         std::make_unique<OGRFieldDefn>("id", OFTString));
     528           3 :                     oMapFieldNameToIdxLocal["id"] = 0;
     529           3 :                     dagLocal.addNode(0, "id");
     530           3 :                     const int nPrevFieldIdx = 0;
     531             : 
     532           6 :                     for (auto i = decltype(nGeometries){0}; i < nGeometries;
     533             :                          i++)
     534             :                     {
     535             :                         json_object *poGeom =
     536           3 :                             json_object_array_get_idx(poGeometries, i);
     537           6 :                         if (poGeom != nullptr &&
     538           3 :                             json_type_object == json_object_get_type(poGeom))
     539             :                         {
     540           3 :                             EstablishLayerDefn(
     541             :                                 nPrevFieldIdx, anCurFieldIndicesLocal,
     542             :                                 oMapFieldNameToIdxLocal, apoFieldDefnLocal,
     543             :                                 dagLocal, poGeom,
     544             :                                 aoSetUndeterminedTypeFieldsLocal);
     545             :                         }
     546             :                     }
     547             : 
     548           6 :                     const auto sortedFields = dagLocal.getTopologicalOrdering();
     549           3 :                     CPLAssert(sortedFields.size() == apoFieldDefnLocal.size());
     550             :                     {
     551           6 :                         auto oTemporaryUnsealer(poDefn->GetTemporaryUnsealer());
     552           7 :                         for (int idx : sortedFields)
     553             :                         {
     554           4 :                             poDefn->AddFieldDefn(apoFieldDefnLocal[idx].get());
     555             :                         }
     556             :                     }
     557             : 
     558             :                     // Second pass to build objects.
     559           6 :                     for (auto i = decltype(nGeometries){0}; i < nGeometries;
     560             :                          i++)
     561             :                     {
     562             :                         json_object *poGeom =
     563           3 :                             json_object_array_get_idx(poGeometries, i);
     564           6 :                         if (poGeom != nullptr &&
     565           3 :                             json_type_object == json_object_get_type(poGeom))
     566             :                         {
     567           3 :                             ParseObject(nullptr, poGeom, poLayer, poArcs,
     568             :                                         psParams);
     569             :                         }
     570             :                     }
     571             : 
     572           3 :                     poLayer->DetectGeometryType();
     573           3 :                     poDS->AddLayer(poLayer);
     574             :                 }
     575             :             }
     576          56 :             else if (strcmp(pszType, "Point") == 0 ||
     577          49 :                      strcmp(pszType, "MultiPoint") == 0 ||
     578          41 :                      strcmp(pszType, "LineString") == 0 ||
     579          24 :                      strcmp(pszType, "MultiLineString") == 0 ||
     580          16 :                      strcmp(pszType, "Polygon") == 0 ||
     581           8 :                      strcmp(pszType, "MultiPolygon") == 0)
     582             :             {
     583          55 :                 if (*ppoMainLayer == nullptr)
     584             :                 {
     585           3 :                     *ppoMainLayer = new OGRGeoJSONLayer(
     586           3 :                         "TopoJSON", nullptr, wkbUnknown, poDS, nullptr);
     587             :                     apoFieldDefn.emplace_back(
     588           3 :                         std::make_unique<OGRFieldDefn>("id", OFTString));
     589           3 :                     oMapFieldNameToIdx["id"] = 0;
     590           3 :                     dag.addNode(0, "id");
     591             :                 }
     592             : 
     593          55 :                 const int nPrevFieldIdx = 0;
     594          55 :                 EstablishLayerDefn(nPrevFieldIdx, anCurFieldIndices,
     595             :                                    oMapFieldNameToIdx, apoFieldDefn, dag, poObj,
     596             :                                    aoSetUndeterminedTypeFields);
     597             : 
     598          55 :                 bNeedSecondPass = true;
     599             :             }
     600             :         }
     601             :     }
     602          65 :     return bNeedSecondPass;
     603             : }
     604             : 
     605             : /************************************************************************/
     606             : /*                     ParseObjectMainSecondPass()                      */
     607             : /************************************************************************/
     608             : 
     609          65 : static void ParseObjectMainSecondPass(const char *pszId, json_object *poObj,
     610             :                                       OGRGeoJSONLayer **ppoMainLayer,
     611             :                                       json_object *poArcs,
     612             :                                       ScalingParams *psParams)
     613             : {
     614          65 :     if (poObj != nullptr && json_type_object == json_object_get_type(poObj))
     615             :     {
     616          63 :         json_object *poType = OGRGeoJSONFindMemberByName(poObj, "type");
     617         125 :         if (poType != nullptr &&
     618          62 :             json_type_string == json_object_get_type(poType))
     619             :         {
     620          61 :             const char *pszType = json_object_get_string(poType);
     621          61 :             if (strcmp(pszType, "Point") == 0 ||
     622          54 :                 strcmp(pszType, "MultiPoint") == 0 ||
     623          46 :                 strcmp(pszType, "LineString") == 0 ||
     624          29 :                 strcmp(pszType, "MultiLineString") == 0 ||
     625          21 :                 strcmp(pszType, "Polygon") == 0 ||
     626          13 :                 strcmp(pszType, "MultiPolygon") == 0)
     627             :             {
     628          55 :                 ParseObject(pszId, poObj, *ppoMainLayer, poArcs, psParams);
     629             :             }
     630             :         }
     631             :     }
     632          65 : }
     633             : 
     634             : /************************************************************************/
     635             : /*                           ReadLayers()                               */
     636             : /************************************************************************/
     637             : 
     638           3 : void OGRTopoJSONReader::ReadLayers(OGRGeoJSONDataSource *poDS)
     639             : {
     640           3 :     if (nullptr == poGJObject_)
     641             :     {
     642           0 :         CPLDebug("TopoJSON",
     643             :                  "Missing parsed TopoJSON data. Forgot to call Parse()?");
     644           0 :         return;
     645             :     }
     646             : 
     647             :     ScalingParams sParams;
     648           3 :     sParams.dfScale0 = 1.0;
     649           3 :     sParams.dfScale1 = 1.0;
     650           3 :     sParams.dfTranslate0 = 0.0;
     651           3 :     sParams.dfTranslate1 = 0.0;
     652           3 :     sParams.bElementExists = false;
     653             :     json_object *poObjTransform =
     654           3 :         OGRGeoJSONFindMemberByName(poGJObject_, "transform");
     655           5 :     if (nullptr != poObjTransform &&
     656           2 :         json_type_object == json_object_get_type(poObjTransform))
     657             :     {
     658             :         json_object *poObjScale =
     659           2 :             OGRGeoJSONFindMemberByName(poObjTransform, "scale");
     660           4 :         if (nullptr != poObjScale &&
     661           4 :             json_type_array == json_object_get_type(poObjScale) &&
     662           2 :             json_object_array_length(poObjScale) == 2)
     663             :         {
     664           2 :             json_object *poScale0 = json_object_array_get_idx(poObjScale, 0);
     665           2 :             json_object *poScale1 = json_object_array_get_idx(poObjScale, 1);
     666           4 :             if (poScale0 != nullptr &&
     667           2 :                 (json_object_get_type(poScale0) == json_type_double ||
     668           2 :                  json_object_get_type(poScale0) == json_type_int) &&
     669           4 :                 poScale1 != nullptr &&
     670           2 :                 (json_object_get_type(poScale1) == json_type_double ||
     671           1 :                  json_object_get_type(poScale1) == json_type_int))
     672             :             {
     673           2 :                 sParams.dfScale0 = json_object_get_double(poScale0);
     674           2 :                 sParams.dfScale1 = json_object_get_double(poScale1);
     675           2 :                 sParams.bElementExists = true;
     676             :             }
     677             :         }
     678             : 
     679             :         json_object *poObjTranslate =
     680           2 :             OGRGeoJSONFindMemberByName(poObjTransform, "translate");
     681           4 :         if (nullptr != poObjTranslate &&
     682           4 :             json_type_array == json_object_get_type(poObjTranslate) &&
     683           2 :             json_object_array_length(poObjTranslate) == 2)
     684             :         {
     685             :             json_object *poTranslate0 =
     686           2 :                 json_object_array_get_idx(poObjTranslate, 0);
     687             :             json_object *poTranslate1 =
     688           2 :                 json_object_array_get_idx(poObjTranslate, 1);
     689           4 :             if (poTranslate0 != nullptr &&
     690           2 :                 (json_object_get_type(poTranslate0) == json_type_double ||
     691           2 :                  json_object_get_type(poTranslate0) == json_type_int) &&
     692           4 :                 poTranslate1 != nullptr &&
     693           2 :                 (json_object_get_type(poTranslate1) == json_type_double ||
     694           1 :                  json_object_get_type(poTranslate1) == json_type_int))
     695             :             {
     696           2 :                 sParams.dfTranslate0 = json_object_get_double(poTranslate0);
     697           2 :                 sParams.dfTranslate1 = json_object_get_double(poTranslate1);
     698           2 :                 sParams.bElementExists = true;
     699             :             }
     700             :         }
     701             :     }
     702             : 
     703           3 :     json_object *poArcs = OGRGeoJSONFindMemberByName(poGJObject_, "arcs");
     704           3 :     if (poArcs == nullptr || json_type_array != json_object_get_type(poArcs))
     705           0 :         return;
     706             : 
     707           3 :     OGRGeoJSONLayer *poMainLayer = nullptr;
     708             : 
     709           3 :     json_object *poObjects = OGRGeoJSONFindMemberByName(poGJObject_, "objects");
     710           3 :     if (poObjects == nullptr)
     711           0 :         return;
     712             : 
     713           6 :     std::vector<int> anCurFieldIndices;
     714           6 :     std::map<std::string, int> oMapFieldNameToIdx;
     715           6 :     std::vector<std::unique_ptr<OGRFieldDefn>> apoFieldDefn;
     716           6 :     gdal::DirectedAcyclicGraph<int, std::string> dag;
     717             : 
     718           6 :     std::set<int> aoSetUndeterminedTypeFields;
     719           3 :     if (json_type_object == json_object_get_type(poObjects))
     720             :     {
     721             :         json_object_iter it;
     722           2 :         it.key = nullptr;
     723           2 :         it.val = nullptr;
     724           2 :         it.entry = nullptr;
     725           2 :         bool bNeedSecondPass = false;
     726           6 :         json_object_object_foreachC(poObjects, it)
     727             :         {
     728           4 :             json_object *poObj = it.val;
     729           4 :             bNeedSecondPass |=
     730           4 :                 ParseObjectMain(it.key, poObj, poDS, &poMainLayer, poArcs,
     731             :                                 &sParams, anCurFieldIndices, oMapFieldNameToIdx,
     732             :                                 apoFieldDefn, dag, aoSetUndeterminedTypeFields);
     733             :         }
     734           2 :         if (bNeedSecondPass)
     735             :         {
     736           2 :             OGRFeatureDefn *poDefn = poMainLayer->GetLayerDefn();
     737           4 :             const auto sortedFields = dag.getTopologicalOrdering();
     738           2 :             CPLAssert(sortedFields.size() == apoFieldDefn.size());
     739           4 :             auto oTemporaryUnsealer(poDefn->GetTemporaryUnsealer());
     740           4 :             for (int idx : sortedFields)
     741             :             {
     742           2 :                 poDefn->AddFieldDefn(apoFieldDefn[idx].get());
     743             :             }
     744             : 
     745           2 :             it.key = nullptr;
     746           2 :             it.val = nullptr;
     747           2 :             it.entry = nullptr;
     748           6 :             json_object_object_foreachC(poObjects, it)
     749             :             {
     750           4 :                 json_object *poObj = it.val;
     751           4 :                 ParseObjectMainSecondPass(it.key, poObj, &poMainLayer, poArcs,
     752             :                                           &sParams);
     753             :             }
     754             :         }
     755             :     }
     756           1 :     else if (json_type_array == json_object_get_type(poObjects))
     757             :     {
     758           1 :         const auto nObjects = json_object_array_length(poObjects);
     759           1 :         bool bNeedSecondPass = false;
     760          62 :         for (auto i = decltype(nObjects){0}; i < nObjects; i++)
     761             :         {
     762          61 :             json_object *poObj = json_object_array_get_idx(poObjects, i);
     763          61 :             bNeedSecondPass |=
     764          61 :                 ParseObjectMain(nullptr, poObj, poDS, &poMainLayer, poArcs,
     765             :                                 &sParams, anCurFieldIndices, oMapFieldNameToIdx,
     766             :                                 apoFieldDefn, dag, aoSetUndeterminedTypeFields);
     767             :         }
     768           1 :         if (bNeedSecondPass)
     769             :         {
     770           1 :             OGRFeatureDefn *poDefn = poMainLayer->GetLayerDefn();
     771           2 :             const auto sortedFields = dag.getTopologicalOrdering();
     772           1 :             CPLAssert(sortedFields.size() == apoFieldDefn.size());
     773           2 :             auto oTemporaryUnsealer(poDefn->GetTemporaryUnsealer());
     774           3 :             for (int idx : sortedFields)
     775             :             {
     776           2 :                 poDefn->AddFieldDefn(apoFieldDefn[idx].get());
     777             :             }
     778             : 
     779          62 :             for (auto i = decltype(nObjects){0}; i < nObjects; i++)
     780             :             {
     781          61 :                 json_object *poObj = json_object_array_get_idx(poObjects, i);
     782          61 :                 ParseObjectMainSecondPass(nullptr, poObj, &poMainLayer, poArcs,
     783             :                                           &sParams);
     784             :             }
     785             :         }
     786             :     }
     787             : 
     788           3 :     if (poMainLayer != nullptr)
     789             :     {
     790           3 :         poMainLayer->DetectGeometryType();
     791           3 :         poDS->AddLayer(poMainLayer);
     792             :     }
     793             : }

Generated by: LCOV version 1.14