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

Generated by: LCOV version 1.14