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

Generated by: LCOV version 1.14