LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/geojson - ogrtopojsonreader.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 381 392 97.2 %
Date: 2025-05-31 00:00:17 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           0 :         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 :     OGRFeature *poFeature = new 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, nField, it.key, it.val,
     344             :                                      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(poFeature);
     411          78 :     delete poFeature;
     412             : }
     413             : 
     414             : /************************************************************************/
     415             : /*                        EstablishLayerDefn()                          */
     416             : /************************************************************************/
     417             : 
     418             : static void
     419         114 : EstablishLayerDefn(int nPrevFieldIdx, std::vector<int> &anCurFieldIndices,
     420             :                    std::map<std::string, int> &oMapFieldNameToIdx,
     421             :                    std::vector<std::unique_ptr<OGRFieldDefn>> &apoFieldDefn,
     422             :                    gdal::DirectedAcyclicGraph<int, std::string> &dag,
     423             :                    json_object *poObj,
     424             :                    std::set<int> &aoSetUndeterminedTypeFields)
     425             : {
     426         114 :     json_object *poObjProps = OGRGeoJSONFindMemberByName(poObj, "properties");
     427         128 :     if (nullptr != poObjProps &&
     428          14 :         json_object_get_type(poObjProps) == json_type_object)
     429             :     {
     430             :         json_object_iter it;
     431          12 :         it.key = nullptr;
     432          12 :         it.val = nullptr;
     433          12 :         it.entry = nullptr;
     434             : 
     435          24 :         json_object_object_foreachC(poObjProps, it)
     436             :         {
     437          12 :             anCurFieldIndices.clear();
     438          12 :             OGRGeoJSONReaderAddOrUpdateField(
     439          12 :                 anCurFieldIndices, oMapFieldNameToIdx, apoFieldDefn, it.key,
     440             :                 it.val, false, 0, false, false, aoSetUndeterminedTypeFields);
     441          24 :             for (int idx : anCurFieldIndices)
     442             :             {
     443          12 :                 dag.addNode(idx, apoFieldDefn[idx]->GetNameRef());
     444          12 :                 if (nPrevFieldIdx != -1)
     445             :                 {
     446          12 :                     dag.addEdge(nPrevFieldIdx, idx);
     447             :                 }
     448          12 :                 nPrevFieldIdx = idx;
     449             :             }
     450             :         }
     451             :     }
     452         114 : }
     453             : 
     454             : /************************************************************************/
     455             : /*                        ParseObjectMain()                             */
     456             : /************************************************************************/
     457             : 
     458             : static bool
     459         128 : ParseObjectMain(const char *pszId, json_object *poObj,
     460             :                 const OGRSpatialReference *poSRS, OGRGeoJSONDataSource *poDS,
     461             :                 OGRGeoJSONLayer **ppoMainLayer, json_object *poArcs,
     462             :                 ScalingParams *psParams, std::vector<int> &anCurFieldIndices,
     463             :                 std::map<std::string, int> &oMapFieldNameToIdx,
     464             :                 std::vector<std::unique_ptr<OGRFieldDefn>> &apoFieldDefn,
     465             :                 gdal::DirectedAcyclicGraph<int, std::string> &dag,
     466             :                 std::set<int> &aoSetUndeterminedTypeFields)
     467             : {
     468         128 :     bool bNeedSecondPass = false;
     469             : 
     470         128 :     if (poObj != nullptr && json_type_object == json_object_get_type(poObj))
     471             :     {
     472         124 :         json_object *poType = OGRGeoJSONFindMemberByName(poObj, "type");
     473         246 :         if (poType != nullptr &&
     474         122 :             json_type_string == json_object_get_type(poType))
     475             :         {
     476         120 :             const char *pszType = json_object_get_string(poType);
     477         120 :             if (strcmp(pszType, "GeometryCollection") == 0)
     478             :             {
     479             :                 json_object *poGeometries =
     480           9 :                     OGRGeoJSONFindMemberByName(poObj, "geometries");
     481          14 :                 if (poGeometries != nullptr &&
     482           5 :                     json_type_array == json_object_get_type(poGeometries))
     483             :                 {
     484           5 :                     if (pszId == nullptr)
     485             :                     {
     486             :                         json_object *poId =
     487           2 :                             OGRGeoJSONFindMemberByName(poObj, "id");
     488           4 :                         if (poId != nullptr &&
     489           2 :                             (json_type_string == json_object_get_type(poId) ||
     490           0 :                              json_type_int == json_object_get_type(poId)))
     491             :                         {
     492           2 :                             pszId = json_object_get_string(poId);
     493             :                         }
     494             :                     }
     495             : 
     496             :                     OGRGeoJSONLayer *poLayer =
     497             :                         new OGRGeoJSONLayer(pszId ? pszId : "TopoJSON", nullptr,
     498           5 :                                             wkbUnknown, poDS, nullptr);
     499           5 :                     poLayer->SetSupportsZGeometries(false);
     500           5 :                     OGRFeatureDefn *poDefn = poLayer->GetLayerDefn();
     501             : 
     502           5 :                     whileUnsealing(poDefn)->GetGeomFieldDefn(0)->SetSpatialRef(
     503             :                         poSRS);
     504             : 
     505             :                     const auto nGeometries =
     506           5 :                         json_object_array_length(poGeometries);
     507             :                     // First pass to establish schema.
     508             : 
     509          10 :                     std::vector<int> anCurFieldIndicesLocal;
     510          10 :                     std::map<std::string, int> oMapFieldNameToIdxLocal;
     511             :                     std::vector<std::unique_ptr<OGRFieldDefn>>
     512          10 :                         apoFieldDefnLocal;
     513          10 :                     gdal::DirectedAcyclicGraph<int, std::string> dagLocal;
     514          10 :                     std::set<int> aoSetUndeterminedTypeFieldsLocal;
     515             : 
     516             :                     apoFieldDefnLocal.emplace_back(
     517           5 :                         std::make_unique<OGRFieldDefn>("id", OFTString));
     518           5 :                     oMapFieldNameToIdxLocal["id"] = 0;
     519           5 :                     dagLocal.addNode(0, "id");
     520           5 :                     const int nPrevFieldIdx = 0;
     521             : 
     522          10 :                     for (auto i = decltype(nGeometries){0}; i < nGeometries;
     523             :                          i++)
     524             :                     {
     525             :                         json_object *poGeom =
     526           5 :                             json_object_array_get_idx(poGeometries, i);
     527          10 :                         if (poGeom != nullptr &&
     528           5 :                             json_type_object == json_object_get_type(poGeom))
     529             :                         {
     530           5 :                             EstablishLayerDefn(
     531             :                                 nPrevFieldIdx, anCurFieldIndicesLocal,
     532             :                                 oMapFieldNameToIdxLocal, apoFieldDefnLocal,
     533             :                                 dagLocal, poGeom,
     534             :                                 aoSetUndeterminedTypeFieldsLocal);
     535             :                         }
     536             :                     }
     537             : 
     538          10 :                     const auto sortedFields = dagLocal.getTopologicalOrdering();
     539           5 :                     CPLAssert(sortedFields.size() == apoFieldDefnLocal.size());
     540             :                     {
     541          10 :                         auto oTemporaryUnsealer(poDefn->GetTemporaryUnsealer());
     542          12 :                         for (int idx : sortedFields)
     543             :                         {
     544           7 :                             poDefn->AddFieldDefn(apoFieldDefnLocal[idx].get());
     545             :                         }
     546             :                     }
     547             : 
     548             :                     // Second pass to build objects.
     549          10 :                     for (auto i = decltype(nGeometries){0}; i < nGeometries;
     550             :                          i++)
     551             :                     {
     552             :                         json_object *poGeom =
     553           5 :                             json_object_array_get_idx(poGeometries, i);
     554          10 :                         if (poGeom != nullptr &&
     555           5 :                             json_type_object == json_object_get_type(poGeom))
     556             :                         {
     557           5 :                             ParseObject(nullptr, poGeom, poLayer, poArcs,
     558             :                                         psParams);
     559             :                         }
     560             :                     }
     561             : 
     562           5 :                     poLayer->DetectGeometryType();
     563           5 :                     poDS->AddLayer(poLayer);
     564             :                 }
     565             :             }
     566         111 :             else if (strcmp(pszType, "Point") == 0 ||
     567          97 :                      strcmp(pszType, "MultiPoint") == 0 ||
     568          81 :                      strcmp(pszType, "LineString") == 0 ||
     569          48 :                      strcmp(pszType, "MultiLineString") == 0 ||
     570          32 :                      strcmp(pszType, "Polygon") == 0 ||
     571          16 :                      strcmp(pszType, "MultiPolygon") == 0)
     572             :             {
     573         109 :                 if (*ppoMainLayer == nullptr)
     574             :                 {
     575           5 :                     *ppoMainLayer = new OGRGeoJSONLayer(
     576           5 :                         "TopoJSON", nullptr, wkbUnknown, poDS, nullptr);
     577             : 
     578           5 :                     (*ppoMainLayer)->SetSupportsZGeometries(false);
     579             : 
     580          10 :                     whileUnsealing((*ppoMainLayer)->GetLayerDefn())
     581           5 :                         ->GetGeomFieldDefn(0)
     582           5 :                         ->SetSpatialRef(poSRS);
     583             : 
     584             :                     apoFieldDefn.emplace_back(
     585           5 :                         std::make_unique<OGRFieldDefn>("id", OFTString));
     586           5 :                     oMapFieldNameToIdx["id"] = 0;
     587           5 :                     dag.addNode(0, "id");
     588             :                 }
     589             : 
     590         109 :                 const int nPrevFieldIdx = 0;
     591         109 :                 EstablishLayerDefn(nPrevFieldIdx, anCurFieldIndices,
     592             :                                    oMapFieldNameToIdx, apoFieldDefn, dag, poObj,
     593             :                                    aoSetUndeterminedTypeFields);
     594             : 
     595         109 :                 bNeedSecondPass = true;
     596             :             }
     597             :         }
     598             :     }
     599         128 :     return bNeedSecondPass;
     600             : }
     601             : 
     602             : /************************************************************************/
     603             : /*                     ParseObjectMainSecondPass()                      */
     604             : /************************************************************************/
     605             : 
     606         128 : static void ParseObjectMainSecondPass(const char *pszId, json_object *poObj,
     607             :                                       OGRGeoJSONLayer **ppoMainLayer,
     608             :                                       json_object *poArcs,
     609             :                                       ScalingParams *psParams)
     610             : {
     611         128 :     if (poObj != nullptr && json_type_object == json_object_get_type(poObj))
     612             :     {
     613         124 :         json_object *poType = OGRGeoJSONFindMemberByName(poObj, "type");
     614         246 :         if (poType != nullptr &&
     615         122 :             json_type_string == json_object_get_type(poType))
     616             :         {
     617         120 :             const char *pszType = json_object_get_string(poType);
     618         120 :             if (strcmp(pszType, "Point") == 0 ||
     619         106 :                 strcmp(pszType, "MultiPoint") == 0 ||
     620          90 :                 strcmp(pszType, "LineString") == 0 ||
     621          57 :                 strcmp(pszType, "MultiLineString") == 0 ||
     622          41 :                 strcmp(pszType, "Polygon") == 0 ||
     623          25 :                 strcmp(pszType, "MultiPolygon") == 0)
     624             :             {
     625         109 :                 ParseObject(pszId, poObj, *ppoMainLayer, poArcs, psParams);
     626             :             }
     627             :         }
     628             :     }
     629         128 : }
     630             : 
     631             : /************************************************************************/
     632             : /*                           ReadLayers()                               */
     633             : /************************************************************************/
     634             : 
     635           5 : void OGRTopoJSONReader::ReadLayers(OGRGeoJSONDataSource *poDS)
     636             : {
     637           5 :     if (nullptr == poGJObject_)
     638             :     {
     639           0 :         CPLDebug("TopoJSON",
     640             :                  "Missing parsed TopoJSON data. Forgot to call Parse()?");
     641           0 :         return;
     642             :     }
     643             : 
     644           5 :     poDS->SetSupportsZGeometries(false);
     645             : 
     646             :     ScalingParams sParams;
     647           5 :     sParams.dfScale0 = 1.0;
     648           5 :     sParams.dfScale1 = 1.0;
     649           5 :     sParams.dfTranslate0 = 0.0;
     650           5 :     sParams.dfTranslate1 = 0.0;
     651           5 :     sParams.bElementExists = false;
     652             :     json_object *poObjTransform =
     653           5 :         OGRGeoJSONFindMemberByName(poGJObject_, "transform");
     654           9 :     if (nullptr != poObjTransform &&
     655           4 :         json_type_object == json_object_get_type(poObjTransform))
     656             :     {
     657             :         json_object *poObjScale =
     658           4 :             OGRGeoJSONFindMemberByName(poObjTransform, "scale");
     659           8 :         if (nullptr != poObjScale &&
     660           8 :             json_type_array == json_object_get_type(poObjScale) &&
     661           4 :             json_object_array_length(poObjScale) == 2)
     662             :         {
     663           4 :             json_object *poScale0 = json_object_array_get_idx(poObjScale, 0);
     664           4 :             json_object *poScale1 = json_object_array_get_idx(poObjScale, 1);
     665           8 :             if (poScale0 != nullptr &&
     666           4 :                 (json_object_get_type(poScale0) == json_type_double ||
     667           4 :                  json_object_get_type(poScale0) == json_type_int) &&
     668           8 :                 poScale1 != nullptr &&
     669           4 :                 (json_object_get_type(poScale1) == json_type_double ||
     670           2 :                  json_object_get_type(poScale1) == json_type_int))
     671             :             {
     672           4 :                 sParams.dfScale0 = json_object_get_double(poScale0);
     673           4 :                 sParams.dfScale1 = json_object_get_double(poScale1);
     674           4 :                 sParams.bElementExists = true;
     675             :             }
     676             :         }
     677             : 
     678             :         json_object *poObjTranslate =
     679           4 :             OGRGeoJSONFindMemberByName(poObjTransform, "translate");
     680           8 :         if (nullptr != poObjTranslate &&
     681           8 :             json_type_array == json_object_get_type(poObjTranslate) &&
     682           4 :             json_object_array_length(poObjTranslate) == 2)
     683             :         {
     684             :             json_object *poTranslate0 =
     685           4 :                 json_object_array_get_idx(poObjTranslate, 0);
     686             :             json_object *poTranslate1 =
     687           4 :                 json_object_array_get_idx(poObjTranslate, 1);
     688           8 :             if (poTranslate0 != nullptr &&
     689           4 :                 (json_object_get_type(poTranslate0) == json_type_double ||
     690           4 :                  json_object_get_type(poTranslate0) == json_type_int) &&
     691           8 :                 poTranslate1 != nullptr &&
     692           4 :                 (json_object_get_type(poTranslate1) == json_type_double ||
     693           2 :                  json_object_get_type(poTranslate1) == json_type_int))
     694             :             {
     695           4 :                 sParams.dfTranslate0 = json_object_get_double(poTranslate0);
     696           4 :                 sParams.dfTranslate1 = json_object_get_double(poTranslate1);
     697           4 :                 sParams.bElementExists = true;
     698             :             }
     699             :         }
     700             :     }
     701             : 
     702           5 :     json_object *poArcs = OGRGeoJSONFindMemberByName(poGJObject_, "arcs");
     703           5 :     if (poArcs == nullptr || json_type_array != json_object_get_type(poArcs))
     704           0 :         return;
     705             : 
     706           5 :     OGRGeoJSONLayer *poMainLayer = nullptr;
     707             : 
     708           5 :     json_object *poObjects = OGRGeoJSONFindMemberByName(poGJObject_, "objects");
     709           5 :     if (poObjects == nullptr)
     710           0 :         return;
     711             : 
     712           5 :     OGRSpatialReference *poSRS = OGRGeoJSONReadSpatialReference(poGJObject_);
     713             : 
     714          10 :     std::vector<int> anCurFieldIndices;
     715          10 :     std::map<std::string, int> oMapFieldNameToIdx;
     716          10 :     std::vector<std::unique_ptr<OGRFieldDefn>> apoFieldDefn;
     717          10 :     gdal::DirectedAcyclicGraph<int, std::string> dag;
     718             : 
     719          10 :     std::set<int> aoSetUndeterminedTypeFields;
     720           5 :     if (json_type_object == json_object_get_type(poObjects))
     721             :     {
     722             :         json_object_iter it;
     723           3 :         it.key = nullptr;
     724           3 :         it.val = nullptr;
     725           3 :         it.entry = nullptr;
     726           3 :         bool bNeedSecondPass = false;
     727           9 :         json_object_object_foreachC(poObjects, it)
     728             :         {
     729           6 :             json_object *poObj = it.val;
     730           6 :             bNeedSecondPass |= ParseObjectMain(
     731           6 :                 it.key, poObj, poSRS, poDS, &poMainLayer, poArcs, &sParams,
     732             :                 anCurFieldIndices, oMapFieldNameToIdx, apoFieldDefn, dag,
     733             :                 aoSetUndeterminedTypeFields);
     734             :         }
     735           3 :         if (bNeedSecondPass)
     736             :         {
     737           3 :             OGRFeatureDefn *poDefn = poMainLayer->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 :             OGRFeatureDefn *poDefn = poMainLayer->GetLayerDefn();
     772           4 :             const auto sortedFields = dag.getTopologicalOrdering();
     773           2 :             CPLAssert(sortedFields.size() == apoFieldDefn.size());
     774           4 :             auto oTemporaryUnsealer(poDefn->GetTemporaryUnsealer());
     775           6 :             for (int idx : sortedFields)
     776             :             {
     777           4 :                 poDefn->AddFieldDefn(apoFieldDefn[idx].get());
     778             :             }
     779             : 
     780         124 :             for (auto i = decltype(nObjects){0}; i < nObjects; i++)
     781             :             {
     782         122 :                 json_object *poObj = json_object_array_get_idx(poObjects, i);
     783         122 :                 ParseObjectMainSecondPass(nullptr, poObj, &poMainLayer, poArcs,
     784             :                                           &sParams);
     785             :             }
     786             :         }
     787             :     }
     788             : 
     789           5 :     if (poMainLayer != nullptr)
     790             :     {
     791           5 :         poMainLayer->DetectGeometryType();
     792           5 :         poDS->AddLayer(poMainLayer);
     793             :     }
     794             : 
     795           5 :     if (poSRS)
     796           1 :         poSRS->Release();
     797             : }

Generated by: LCOV version 1.14