LCOV - code coverage report
Current view: top level - ogr - ogrgeojsongeometry.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 396 419 94.5 %
Date: 2025-03-28 11:40:40 Functions: 17 17 100.0 %

          Line data    Source code
       1             : // SPDX-License-Identifier: MIT
       2             : // Copyright 2007, Mateusz Loskot
       3             : // Copyright 2008-2024, Even Rouault <even.rouault at spatialys.com>
       4             : 
       5             : /*! @cond Doxygen_Suppress */
       6             : 
       7             : #include "ogrgeojsongeometry.h"
       8             : #include "ogrlibjsonutils.h"
       9             : 
      10             : #include "ogr_geometry.h"
      11             : #include "ogr_spatialref.h"
      12             : 
      13             : static OGRPoint *OGRGeoJSONReadPoint(json_object *poObj);
      14             : static OGRMultiPoint *OGRGeoJSONReadMultiPoint(json_object *poObj);
      15             : static OGRLineString *OGRGeoJSONReadLineString(json_object *poObj,
      16             :                                                bool bRaw = false);
      17             : static OGRMultiLineString *OGRGeoJSONReadMultiLineString(json_object *poObj);
      18             : static OGRLinearRing *OGRGeoJSONReadLinearRing(json_object *poObj);
      19             : static OGRMultiPolygon *OGRGeoJSONReadMultiPolygon(json_object *poObj);
      20             : static OGRGeometryCollection *
      21             : OGRGeoJSONReadGeometryCollection(json_object *poObj,
      22             :                                  OGRSpatialReference *poSRS = nullptr);
      23             : 
      24             : /************************************************************************/
      25             : /*                           OGRGeoJSONGetType                          */
      26             : /************************************************************************/
      27             : 
      28        2990 : GeoJSONObject::Type OGRGeoJSONGetType(json_object *poObj)
      29             : {
      30        2990 :     if (nullptr == poObj)
      31           0 :         return GeoJSONObject::eUnknown;
      32             : 
      33        2990 :     json_object *poObjType = OGRGeoJSONFindMemberByName(poObj, "type");
      34        2990 :     if (nullptr == poObjType)
      35           2 :         return GeoJSONObject::eUnknown;
      36             : 
      37        2988 :     const char *name = json_object_get_string(poObjType);
      38        2988 :     if (EQUAL(name, "Point"))
      39         388 :         return GeoJSONObject::ePoint;
      40        2600 :     else if (EQUAL(name, "LineString"))
      41          67 :         return GeoJSONObject::eLineString;
      42        2533 :     else if (EQUAL(name, "Polygon"))
      43        1025 :         return GeoJSONObject::ePolygon;
      44        1508 :     else if (EQUAL(name, "MultiPoint"))
      45         153 :         return GeoJSONObject::eMultiPoint;
      46        1355 :     else if (EQUAL(name, "MultiLineString"))
      47          52 :         return GeoJSONObject::eMultiLineString;
      48        1303 :     else if (EQUAL(name, "MultiPolygon"))
      49         436 :         return GeoJSONObject::eMultiPolygon;
      50         867 :     else if (EQUAL(name, "GeometryCollection"))
      51          14 :         return GeoJSONObject::eGeometryCollection;
      52         853 :     else if (EQUAL(name, "Feature"))
      53         567 :         return GeoJSONObject::eFeature;
      54         286 :     else if (EQUAL(name, "FeatureCollection"))
      55         286 :         return GeoJSONObject::eFeatureCollection;
      56             :     else
      57           0 :         return GeoJSONObject::eUnknown;
      58             : }
      59             : 
      60             : /************************************************************************/
      61             : /*                   OGRGeoJSONGetOGRGeometryType()                     */
      62             : /************************************************************************/
      63             : 
      64        4627 : OGRwkbGeometryType OGRGeoJSONGetOGRGeometryType(json_object *poObj)
      65             : {
      66        4627 :     if (nullptr == poObj)
      67           1 :         return wkbUnknown;
      68             : 
      69        4626 :     json_object *poObjType = CPL_json_object_object_get(poObj, "type");
      70        4626 :     if (nullptr == poObjType)
      71           0 :         return wkbUnknown;
      72             : 
      73        4626 :     OGRwkbGeometryType eType = wkbUnknown;
      74        4626 :     const char *name = json_object_get_string(poObjType);
      75        4626 :     if (EQUAL(name, "Point"))
      76         337 :         eType = wkbPoint;
      77        4289 :     else if (EQUAL(name, "LineString"))
      78          53 :         eType = wkbLineString;
      79        4236 :     else if (EQUAL(name, "Polygon"))
      80        3936 :         eType = wkbPolygon;
      81         300 :     else if (EQUAL(name, "MultiPoint"))
      82          25 :         eType = wkbMultiPoint;
      83         275 :     else if (EQUAL(name, "MultiLineString"))
      84          28 :         eType = wkbMultiLineString;
      85         247 :     else if (EQUAL(name, "MultiPolygon"))
      86         223 :         eType = wkbMultiPolygon;
      87          24 :     else if (EQUAL(name, "GeometryCollection"))
      88          19 :         eType = wkbGeometryCollection;
      89             :     else
      90           5 :         return wkbUnknown;
      91             : 
      92             :     json_object *poCoordinates;
      93        4621 :     if (eType == wkbGeometryCollection)
      94             :     {
      95             :         json_object *poGeometries =
      96          19 :             CPL_json_object_object_get(poObj, "geometries");
      97          37 :         if (poGeometries &&
      98          37 :             json_object_get_type(poGeometries) == json_type_array &&
      99          18 :             json_object_array_length(poGeometries) > 0)
     100             :         {
     101          17 :             if (OGR_GT_HasZ(OGRGeoJSONGetOGRGeometryType(
     102          17 :                     json_object_array_get_idx(poGeometries, 0))))
     103           7 :                 eType = OGR_GT_SetZ(eType);
     104             :         }
     105             :     }
     106             :     else
     107             :     {
     108        4602 :         poCoordinates = CPL_json_object_object_get(poObj, "coordinates");
     109        9198 :         if (poCoordinates &&
     110        9198 :             json_object_get_type(poCoordinates) == json_type_array &&
     111        4596 :             json_object_array_length(poCoordinates) > 0)
     112             :         {
     113             :             while (true)
     114             :             {
     115       13226 :                 auto poChild = json_object_array_get_idx(poCoordinates, 0);
     116       26442 :                 if (!(poChild &&
     117       13216 :                       json_object_get_type(poChild) == json_type_array &&
     118        8639 :                       json_object_array_length(poChild) > 0))
     119             :                 {
     120        4591 :                     if (json_object_array_length(poCoordinates) == 3)
     121         111 :                         eType = OGR_GT_SetZ(eType);
     122        4591 :                     break;
     123             :                 }
     124        8635 :                 poCoordinates = poChild;
     125        8635 :             }
     126             :         }
     127             :     }
     128             : 
     129        4621 :     return eType;
     130             : }
     131             : 
     132             : /************************************************************************/
     133             : /*                           OGRGeoJSONReadGeometry                     */
     134             : /************************************************************************/
     135             : 
     136        1938 : OGRGeometry *OGRGeoJSONReadGeometry(json_object *poObj,
     137             :                                     OGRSpatialReference *poParentSRS)
     138             : {
     139             : 
     140        1938 :     OGRGeometry *poGeometry = nullptr;
     141        1938 :     OGRSpatialReference *poSRS = nullptr;
     142        1938 :     lh_entry *entry = OGRGeoJSONFindMemberEntryByName(poObj, "crs");
     143        1938 :     if (entry != nullptr)
     144             :     {
     145           4 :         json_object *poObjSrs =
     146             :             static_cast<json_object *>(const_cast<void *>(entry->v));
     147           4 :         if (poObjSrs != nullptr)
     148             :         {
     149           3 :             poSRS = OGRGeoJSONReadSpatialReference(poObj);
     150             :         }
     151             :     }
     152             : 
     153        1938 :     OGRSpatialReference *poSRSToAssign = nullptr;
     154        1938 :     if (entry != nullptr)
     155             :     {
     156           4 :         poSRSToAssign = poSRS;
     157             :     }
     158        1934 :     else if (poParentSRS)
     159             :     {
     160        1319 :         poSRSToAssign = poParentSRS;
     161             :     }
     162             :     else
     163             :     {
     164             :         // Assign WGS84 if no CRS defined on geometry.
     165         615 :         poSRSToAssign = OGRSpatialReference::GetWGS84SRS();
     166             :     }
     167             : 
     168        1938 :     GeoJSONObject::Type objType = OGRGeoJSONGetType(poObj);
     169        1938 :     if (GeoJSONObject::ePoint == objType)
     170         353 :         poGeometry = OGRGeoJSONReadPoint(poObj);
     171        1585 :     else if (GeoJSONObject::eMultiPoint == objType)
     172          63 :         poGeometry = OGRGeoJSONReadMultiPoint(poObj);
     173        1522 :     else if (GeoJSONObject::eLineString == objType)
     174          53 :         poGeometry = OGRGeoJSONReadLineString(poObj);
     175        1469 :     else if (GeoJSONObject::eMultiLineString == objType)
     176          38 :         poGeometry = OGRGeoJSONReadMultiLineString(poObj);
     177        1431 :     else if (GeoJSONObject::ePolygon == objType)
     178        1009 :         poGeometry = OGRGeoJSONReadPolygon(poObj);
     179         422 :     else if (GeoJSONObject::eMultiPolygon == objType)
     180         412 :         poGeometry = OGRGeoJSONReadMultiPolygon(poObj);
     181          10 :     else if (GeoJSONObject::eGeometryCollection == objType)
     182          10 :         poGeometry = OGRGeoJSONReadGeometryCollection(poObj, poSRSToAssign);
     183             :     else
     184             :     {
     185           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     186             :                  "Unsupported geometry type detected. "
     187             :                  "Feature gets NULL geometry assigned.");
     188             :     }
     189             : 
     190        1938 :     if (poGeometry && GeoJSONObject::eGeometryCollection != objType)
     191        1883 :         poGeometry->assignSpatialReference(poSRSToAssign);
     192             : 
     193        1938 :     if (poSRS)
     194           3 :         poSRS->Release();
     195             : 
     196        1938 :     return poGeometry;
     197             : }
     198             : 
     199             : /************************************************************************/
     200             : /*                       GetJSONConstructName()                         */
     201             : /************************************************************************/
     202             : 
     203          27 : static const char *GetJSONConstructName(json_type eType)
     204             : {
     205          27 :     switch (eType)
     206             :     {
     207           6 :         case json_type_null:
     208           6 :             break;
     209           0 :         case json_type_boolean:
     210           0 :             return "boolean";
     211           0 :         case json_type_double:
     212           0 :             return "double";
     213           0 :         case json_type_int:
     214           0 :             return "int";
     215           0 :         case json_type_object:
     216           0 :             return "object";
     217           0 :         case json_type_array:
     218           0 :             return "array";
     219          21 :         case json_type_string:
     220          21 :             return "string";
     221             :     }
     222           6 :     return "null";
     223             : }
     224             : 
     225             : /************************************************************************/
     226             : /*                        OGRGeoJSONGetCoordinate()                     */
     227             : /************************************************************************/
     228             : 
     229      180319 : static double OGRGeoJSONGetCoordinate(json_object *poObj,
     230             :                                       const char *pszCoordName, int nIndex,
     231             :                                       bool &bValid)
     232             : {
     233      180319 :     json_object *poObjCoord = json_object_array_get_idx(poObj, nIndex);
     234      180319 :     if (nullptr == poObjCoord)
     235             :     {
     236           6 :         CPLDebug("GeoJSON", "Point: got null object for %s.", pszCoordName);
     237           6 :         bValid = false;
     238           6 :         return 0.0;
     239             :     }
     240             : 
     241      180313 :     const json_type eType = json_object_get_type(poObjCoord);
     242      180313 :     if (json_type_double != eType && json_type_int != eType)
     243             :     {
     244           6 :         CPLError(CE_Failure, CPLE_AppDefined,
     245             :                  "OGRGeoJSONGetCoordinate(): invalid '%s' coordinate. "
     246             :                  "Unexpected type %s for '%s'. Expected double or integer.",
     247             :                  pszCoordName, GetJSONConstructName(eType),
     248             :                  json_object_to_json_string(poObjCoord));
     249           6 :         bValid = false;
     250           6 :         return 0.0;
     251             :     }
     252             : 
     253      180307 :     return json_object_get_double(poObjCoord);
     254             : }
     255             : 
     256             : /************************************************************************/
     257             : /*                           OGRGeoJSONReadRawPoint                     */
     258             : /************************************************************************/
     259             : 
     260       89988 : static bool OGRGeoJSONReadRawPoint(json_object *poObj, OGRPoint &point)
     261             : {
     262       89988 :     if (json_type_array == json_object_get_type(poObj))
     263             :     {
     264       89976 :         const int nSize = static_cast<int>(json_object_array_length(poObj));
     265             : 
     266       89976 :         if (nSize < GeoJSONObject::eMinCoordinateDimension)
     267             :         {
     268           3 :             CPLError(CE_Warning, CPLE_AppDefined,
     269             :                      "OGRGeoJSONReadRawPoint(): "
     270             :                      "Invalid coord dimension for '%s'. "
     271             :                      "At least 2 dimensions must be present.",
     272             :                      json_object_to_json_string(poObj));
     273           3 :             return false;
     274             :         }
     275             : 
     276       89973 :         bool bValid = true;
     277       89973 :         const double dfX = OGRGeoJSONGetCoordinate(poObj, "x", 0, bValid);
     278       89973 :         const double dfY = OGRGeoJSONGetCoordinate(poObj, "y", 1, bValid);
     279       89973 :         point.setX(dfX);
     280       89973 :         point.setY(dfY);
     281             : 
     282             :         // Read Z coordinate.
     283       89973 :         if (nSize >= GeoJSONObject::eMaxCoordinateDimension)
     284             :         {
     285         373 :             if (nSize > GeoJSONObject::eMaxCoordinateDimension)
     286             :             {
     287           6 :                 CPLErrorOnce(CE_Warning, CPLE_AppDefined,
     288             :                              "OGRGeoJSONReadRawPoint(): too many members in "
     289             :                              "array '%s': %d. At most %d are handled. Ignoring "
     290             :                              "extra members.",
     291             :                              json_object_to_json_string(poObj), nSize,
     292             :                              GeoJSONObject::eMaxCoordinateDimension);
     293             :             }
     294             :             // Don't *expect* mixed-dimension geometries, although the
     295             :             // spec doesn't explicitly forbid this.
     296         373 :             const double dfZ = OGRGeoJSONGetCoordinate(poObj, "z", 2, bValid);
     297         373 :             point.setZ(dfZ);
     298             :         }
     299             :         else
     300             :         {
     301       89600 :             point.flattenTo2D();
     302             :         }
     303       89973 :         return bValid;
     304             :     }
     305             :     else
     306             :     {
     307          12 :         CPLError(CE_Failure, CPLE_AppDefined,
     308             :                  "OGRGeoJSONReadRawPoint(): invalid Point. "
     309             :                  "Unexpected type %s for '%s'. Expected array.",
     310             :                  GetJSONConstructName(json_object_get_type(poObj)),
     311             :                  json_object_to_json_string(poObj));
     312             :     }
     313             : 
     314          12 :     return false;
     315             : }
     316             : 
     317             : /************************************************************************/
     318             : /*                           OGRGeoJSONReadPoint                        */
     319             : /************************************************************************/
     320             : 
     321         353 : OGRPoint *OGRGeoJSONReadPoint(json_object *poObj)
     322             : {
     323         353 :     if (!poObj)
     324             :     {
     325           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     326             :                  "OGRGeoJSONReadPoint(): invalid Point object. Got null.");
     327           0 :         return nullptr;
     328             :     }
     329         353 :     json_object *poObjCoords = OGRGeoJSONFindMemberByName(poObj, "coordinates");
     330         353 :     if (nullptr == poObjCoords)
     331             :     {
     332           4 :         CPLError(CE_Failure, CPLE_AppDefined,
     333             :                  "OGRGeoJSONReadPoint(): invalid Point object. "
     334             :                  "Missing \'coordinates\' member.");
     335           4 :         return nullptr;
     336             :     }
     337             : 
     338         698 :     auto poPoint = std::make_unique<OGRPoint>();
     339         349 :     if (!OGRGeoJSONReadRawPoint(poObjCoords, *poPoint))
     340             :     {
     341          10 :         return nullptr;
     342             :     }
     343             : 
     344         339 :     return poPoint.release();
     345             : }
     346             : 
     347             : /************************************************************************/
     348             : /*                           OGRGeoJSONReadMultiPoint                   */
     349             : /************************************************************************/
     350             : 
     351          63 : OGRMultiPoint *OGRGeoJSONReadMultiPoint(json_object *poObj)
     352             : {
     353          63 :     if (!poObj)
     354             :     {
     355           0 :         CPLError(
     356             :             CE_Failure, CPLE_AppDefined,
     357             :             "OGRGeoJSONReadMultiPoint(): invalid MultiPoint object. Got null.");
     358           0 :         return nullptr;
     359             :     }
     360          63 :     json_object *poObjPoints = OGRGeoJSONFindMemberByName(poObj, "coordinates");
     361          63 :     if (nullptr == poObjPoints)
     362             :     {
     363           3 :         CPLError(CE_Failure, CPLE_AppDefined,
     364             :                  "Invalid MultiPoint object. "
     365             :                  "Missing \'coordinates\' member.");
     366           3 :         return nullptr;
     367             :     }
     368             : 
     369          60 :     std::unique_ptr<OGRMultiPoint> poMultiPoint;
     370          60 :     if (json_type_array == json_object_get_type(poObjPoints))
     371             :     {
     372          59 :         const auto nPoints = json_object_array_length(poObjPoints);
     373             : 
     374          59 :         poMultiPoint = std::make_unique<OGRMultiPoint>();
     375             : 
     376         257 :         for (auto i = decltype(nPoints){0}; i < nPoints; ++i)
     377             :         {
     378             :             json_object *poObjCoords =
     379         202 :                 json_object_array_get_idx(poObjPoints, i);
     380             : 
     381         202 :             OGRPoint pt;
     382         202 :             if (!OGRGeoJSONReadRawPoint(poObjCoords, pt))
     383             :             {
     384           4 :                 return nullptr;
     385             :             }
     386         198 :             poMultiPoint->addGeometry(&pt);
     387             :         }
     388             :     }
     389             :     else
     390             :     {
     391           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     392             :                  "OGRGeoJSONReadMultiPoint(): invalid MultiPoint. "
     393             :                  "Unexpected type %s for '%s'. Expected array.",
     394             :                  GetJSONConstructName(json_object_get_type(poObjPoints)),
     395             :                  json_object_to_json_string(poObjPoints));
     396             :     }
     397             : 
     398          56 :     return poMultiPoint.release();
     399             : }
     400             : 
     401             : /************************************************************************/
     402             : /*                           OGRGeoJSONReadLineString                   */
     403             : /************************************************************************/
     404             : 
     405         111 : OGRLineString *OGRGeoJSONReadLineString(json_object *poObj, bool bRaw)
     406             : {
     407         111 :     if (!poObj)
     408             :     {
     409           2 :         CPLError(
     410             :             CE_Failure, CPLE_AppDefined,
     411             :             "OGRGeoJSONReadLineString(): invalid LineString object. Got null.");
     412           2 :         return nullptr;
     413             :     }
     414         109 :     json_object *poObjPoints = nullptr;
     415             : 
     416         109 :     if (!bRaw)
     417             :     {
     418          53 :         poObjPoints = OGRGeoJSONFindMemberByName(poObj, "coordinates");
     419          53 :         if (nullptr == poObjPoints)
     420             :         {
     421           3 :             CPLError(CE_Failure, CPLE_AppDefined,
     422             :                      "Invalid LineString object. "
     423             :                      "Missing \'coordinates\' member.");
     424           3 :             return nullptr;
     425             :         }
     426             :     }
     427             :     else
     428             :     {
     429          56 :         poObjPoints = poObj;
     430             :     }
     431             : 
     432         106 :     std::unique_ptr<OGRLineString> poLine;
     433             : 
     434         106 :     if (json_type_array == json_object_get_type(poObjPoints))
     435             :     {
     436         104 :         const auto nPoints = json_object_array_length(poObjPoints);
     437             : 
     438         104 :         poLine = std::make_unique<OGRLineString>();
     439         104 :         poLine->setNumPoints(static_cast<int>(nPoints));
     440             : 
     441         299 :         for (auto i = decltype(nPoints){0}; i < nPoints; ++i)
     442             :         {
     443             :             json_object *poObjCoords =
     444         203 :                 json_object_array_get_idx(poObjPoints, i);
     445             : 
     446         203 :             OGRPoint pt;
     447         203 :             if (!OGRGeoJSONReadRawPoint(poObjCoords, pt))
     448             :             {
     449           8 :                 return nullptr;
     450             :             }
     451         195 :             if (pt.getCoordinateDimension() == 2)
     452             :             {
     453         169 :                 poLine->setPoint(static_cast<int>(i), pt.getX(), pt.getY());
     454             :             }
     455             :             else
     456             :             {
     457          26 :                 poLine->setPoint(static_cast<int>(i), pt.getX(), pt.getY(),
     458             :                                  pt.getZ());
     459             :             }
     460             :         }
     461             :     }
     462             :     else
     463             :     {
     464           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     465             :                  "OGRGeoJSONReadLineString(): invalid MultiLineString. "
     466             :                  "Unexpected type %s for '%s'. Expected array.",
     467             :                  GetJSONConstructName(json_object_get_type(poObjPoints)),
     468             :                  json_object_to_json_string(poObjPoints));
     469             :     }
     470             : 
     471          98 :     return poLine.release();
     472             : }
     473             : 
     474             : /************************************************************************/
     475             : /*                           OGRGeoJSONReadMultiLineString              */
     476             : /************************************************************************/
     477             : 
     478          38 : OGRMultiLineString *OGRGeoJSONReadMultiLineString(json_object *poObj)
     479             : {
     480          38 :     CPLAssert(nullptr != poObj);
     481             : 
     482          38 :     json_object *poObjLines = OGRGeoJSONFindMemberByName(poObj, "coordinates");
     483          38 :     if (nullptr == poObjLines)
     484             :     {
     485           3 :         CPLError(CE_Failure, CPLE_AppDefined,
     486             :                  "Invalid MultiLineString object. "
     487             :                  "Missing \'coordinates\' member.");
     488           3 :         return nullptr;
     489             :     }
     490             : 
     491          35 :     std::unique_ptr<OGRMultiLineString> poMultiLine;
     492             : 
     493          35 :     if (json_type_array == json_object_get_type(poObjLines))
     494             :     {
     495          34 :         const auto nLines = json_object_array_length(poObjLines);
     496             : 
     497          34 :         poMultiLine = std::make_unique<OGRMultiLineString>();
     498             : 
     499          92 :         for (auto i = decltype(nLines){0}; i < nLines; ++i)
     500             :         {
     501          58 :             json_object *poObjLine = json_object_array_get_idx(poObjLines, i);
     502             : 
     503          58 :             OGRLineString *poLine = OGRGeoJSONReadLineString(poObjLine, true);
     504          58 :             if (poLine)
     505             :             {
     506          51 :                 poMultiLine->addGeometryDirectly(poLine);
     507             :             }
     508             :         }
     509             :     }
     510             :     else
     511             :     {
     512           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     513             :                  "OGRGeoJSONReadLineString(): invalid LineString. "
     514             :                  "Unexpected type %s for '%s'. Expected array.",
     515             :                  GetJSONConstructName(json_object_get_type(poObjLines)),
     516             :                  json_object_to_json_string(poObjLines));
     517             :     }
     518             : 
     519          35 :     return poMultiLine.release();
     520             : }
     521             : 
     522             : /************************************************************************/
     523             : /*                           OGRGeoJSONReadLinearRing                   */
     524             : /************************************************************************/
     525             : 
     526        1695 : OGRLinearRing *OGRGeoJSONReadLinearRing(json_object *poObj)
     527             : {
     528        1695 :     CPLAssert(nullptr != poObj);
     529             : 
     530        1695 :     std::unique_ptr<OGRLinearRing> poRing;
     531             : 
     532        1695 :     if (json_type_array == json_object_get_type(poObj))
     533             :     {
     534        1693 :         const auto nPoints = json_object_array_length(poObj);
     535             : 
     536        1693 :         poRing = std::make_unique<OGRLinearRing>();
     537        1693 :         poRing->setNumPoints(static_cast<int>(nPoints));
     538             : 
     539       90923 :         for (auto i = decltype(nPoints){0}; i < nPoints; ++i)
     540             :         {
     541       89234 :             json_object *poObjCoords = json_object_array_get_idx(poObj, i);
     542             : 
     543       89234 :             OGRPoint pt;
     544       89234 :             if (!OGRGeoJSONReadRawPoint(poObjCoords, pt))
     545             :             {
     546           4 :                 return nullptr;
     547             :             }
     548             : 
     549       89230 :             if (2 == pt.getCoordinateDimension())
     550       89089 :                 poRing->setPoint(static_cast<int>(i), pt.getX(), pt.getY());
     551             :             else
     552         141 :                 poRing->setPoint(static_cast<int>(i), pt.getX(), pt.getY(),
     553             :                                  pt.getZ());
     554             :         }
     555             :     }
     556             :     else
     557             :     {
     558           2 :         CPLError(CE_Warning, CPLE_AppDefined,
     559             :                  "OGRGeoJSONReadLinearRing(): unexpected type of JSON "
     560             :                  "construct %s for '%s'. Expected array.",
     561             :                  GetJSONConstructName(json_object_get_type(poObj)),
     562             :                  json_object_to_json_string(poObj));
     563             :     }
     564             : 
     565        1691 :     return poRing.release();
     566             : }
     567             : 
     568             : /************************************************************************/
     569             : /*                           OGRGeoJSONReadPolygon                      */
     570             : /************************************************************************/
     571             : 
     572        1666 : OGRPolygon *OGRGeoJSONReadPolygon(json_object *poObj, bool bRaw)
     573             : {
     574        1666 :     if (!poObj)
     575             :     {
     576           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     577             :                  "OGRGeoJSONReadPolygon(): invalid Polygon object. Got null.");
     578           0 :         return nullptr;
     579             :     }
     580        1666 :     json_object *poObjRings = nullptr;
     581             : 
     582        1666 :     if (!bRaw)
     583             :     {
     584        1009 :         poObjRings = OGRGeoJSONFindMemberByName(poObj, "coordinates");
     585        1009 :         if (nullptr == poObjRings)
     586             :         {
     587           3 :             CPLError(CE_Failure, CPLE_AppDefined,
     588             :                      "Invalid Polygon object. "
     589             :                      "Missing \'coordinates\' member.");
     590           3 :             return nullptr;
     591             :         }
     592             :     }
     593             :     else
     594             :     {
     595         657 :         poObjRings = poObj;
     596             :     }
     597             : 
     598        1663 :     std::unique_ptr<OGRPolygon> poPolygon;
     599             : 
     600        1663 :     if (json_type_array == json_object_get_type(poObjRings))
     601             :     {
     602        1661 :         const auto nRings = json_object_array_length(poObjRings);
     603        1661 :         if (nRings > 0)
     604             :         {
     605        1657 :             json_object *poObjPoints = json_object_array_get_idx(poObjRings, 0);
     606        1657 :             if (!poObjPoints)
     607             :             {
     608           2 :                 poPolygon = std::make_unique<OGRPolygon>();
     609             :             }
     610             :             else
     611             :             {
     612        1655 :                 OGRLinearRing *poRing = OGRGeoJSONReadLinearRing(poObjPoints);
     613        1655 :                 if (poRing)
     614             :                 {
     615        1649 :                     poPolygon = std::make_unique<OGRPolygon>();
     616        1649 :                     poPolygon->addRingDirectly(poRing);
     617             :                 }
     618             :             }
     619             : 
     620        1698 :             for (auto i = decltype(nRings){1};
     621        1698 :                  i < nRings && nullptr != poPolygon; ++i)
     622             :             {
     623          41 :                 poObjPoints = json_object_array_get_idx(poObjRings, i);
     624          41 :                 if (poObjPoints)
     625             :                 {
     626             :                     OGRLinearRing *poRing =
     627          40 :                         OGRGeoJSONReadLinearRing(poObjPoints);
     628          40 :                     if (poRing)
     629             :                     {
     630          40 :                         poPolygon->addRingDirectly(poRing);
     631             :                     }
     632             :                 }
     633             :             }
     634             :         }
     635             :         else
     636             :         {
     637           4 :             poPolygon = std::make_unique<OGRPolygon>();
     638             :         }
     639             :     }
     640             :     else
     641             :     {
     642           2 :         CPLError(CE_Warning, CPLE_AppDefined,
     643             :                  "OGRGeoJSONReadPolygon(): unexpected type of JSON construct "
     644             :                  "%s for '%s'. Expected array.",
     645             :                  GetJSONConstructName(json_object_get_type(poObjRings)),
     646             :                  json_object_to_json_string(poObjRings));
     647             :     }
     648             : 
     649        1663 :     return poPolygon.release();
     650             : }
     651             : 
     652             : /************************************************************************/
     653             : /*                           OGRGeoJSONReadMultiPolygon                 */
     654             : /************************************************************************/
     655             : 
     656         412 : OGRMultiPolygon *OGRGeoJSONReadMultiPolygon(json_object *poObj)
     657             : {
     658         412 :     CPLAssert(nullptr != poObj);
     659             : 
     660         412 :     json_object *poObjPolys = OGRGeoJSONFindMemberByName(poObj, "coordinates");
     661         412 :     if (nullptr == poObjPolys)
     662             :     {
     663           3 :         CPLError(CE_Failure, CPLE_AppDefined,
     664             :                  "Invalid MultiPolygon object. "
     665             :                  "Missing \'coordinates\' member.");
     666           3 :         return nullptr;
     667             :     }
     668             : 
     669         409 :     std::unique_ptr<OGRMultiPolygon> poMultiPoly;
     670             : 
     671         409 :     if (json_type_array == json_object_get_type(poObjPolys))
     672             :     {
     673         408 :         const auto nPolys = json_object_array_length(poObjPolys);
     674             : 
     675         408 :         poMultiPoly = std::make_unique<OGRMultiPolygon>();
     676             : 
     677        1064 :         for (auto i = decltype(nPolys){0}; i < nPolys; ++i)
     678             :         {
     679         656 :             json_object *poObjPoly = json_object_array_get_idx(poObjPolys, i);
     680         656 :             if (!poObjPoly)
     681             :             {
     682           6 :                 poMultiPoly->addGeometryDirectly(
     683           6 :                     std::make_unique<OGRPolygon>().release());
     684             :             }
     685             :             else
     686             :             {
     687         653 :                 OGRPolygon *poPoly = OGRGeoJSONReadPolygon(poObjPoly, true);
     688         653 :                 if (poPoly)
     689             :                 {
     690         649 :                     poMultiPoly->addGeometryDirectly(poPoly);
     691             :                 }
     692             :             }
     693             :         }
     694             :     }
     695             :     else
     696             :     {
     697           1 :         CPLError(CE_Warning, CPLE_AppDefined,
     698             :                  "OGRGeoJSONReadMultiPolygon(): unexpected type of JSON "
     699             :                  "construct %s for '%s'. Expected array.",
     700             :                  GetJSONConstructName(json_object_get_type(poObjPolys)),
     701             :                  json_object_to_json_string(poObjPolys));
     702             :     }
     703             : 
     704         409 :     return poMultiPoly.release();
     705             : }
     706             : 
     707             : /************************************************************************/
     708             : /*                    OGRGeoJSONReadGeometryCollection                  */
     709             : /************************************************************************/
     710             : 
     711             : OGRGeometryCollection *
     712          10 : OGRGeoJSONReadGeometryCollection(json_object *poObj, OGRSpatialReference *poSRS)
     713             : {
     714          10 :     CPLAssert(nullptr != poObj);
     715             : 
     716          10 :     json_object *poObjGeoms = OGRGeoJSONFindMemberByName(poObj, "geometries");
     717          10 :     if (nullptr == poObjGeoms)
     718             :     {
     719           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     720             :                  "Invalid GeometryCollection object. "
     721             :                  "Missing \'geometries\' member.");
     722           1 :         return nullptr;
     723             :     }
     724             : 
     725           9 :     std::unique_ptr<OGRGeometryCollection> poCollection;
     726             : 
     727           9 :     if (json_type_array == json_object_get_type(poObjGeoms))
     728             :     {
     729           9 :         poCollection = std::make_unique<OGRGeometryCollection>();
     730           9 :         poCollection->assignSpatialReference(poSRS);
     731             : 
     732           9 :         const auto nGeoms = json_object_array_length(poObjGeoms);
     733          25 :         for (auto i = decltype(nGeoms){0}; i < nGeoms; ++i)
     734             :         {
     735          16 :             json_object *poObjGeom = json_object_array_get_idx(poObjGeoms, i);
     736          16 :             if (!poObjGeom)
     737             :             {
     738           3 :                 CPLError(CE_Warning, CPLE_AppDefined,
     739             :                          "OGRGeoJSONReadGeometryCollection(): skipping null "
     740             :                          "sub-geometry");
     741           3 :                 continue;
     742             :             }
     743             : 
     744          13 :             OGRGeometry *poGeometry = OGRGeoJSONReadGeometry(poObjGeom, poSRS);
     745          13 :             if (poGeometry)
     746             :             {
     747          13 :                 poCollection->addGeometryDirectly(poGeometry);
     748             :             }
     749             :         }
     750             :     }
     751             :     else
     752             :     {
     753           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     754             :                  "OGRGeoJSONReadGeometryCollection(): unexpected type of JSON "
     755             :                  "construct %s for '%s'. Expected array.",
     756             :                  GetJSONConstructName(json_object_get_type(poObjGeoms)),
     757             :                  json_object_to_json_string(poObjGeoms));
     758             :     }
     759             : 
     760           9 :     return poCollection.release();
     761             : }
     762             : 
     763             : /************************************************************************/
     764             : /*                           OGRGeoJSONGetGeometryName()                */
     765             : /************************************************************************/
     766             : 
     767        1666 : const char *OGRGeoJSONGetGeometryName(OGRGeometry const *poGeometry)
     768             : {
     769        1666 :     CPLAssert(nullptr != poGeometry);
     770             : 
     771        1666 :     const OGRwkbGeometryType eType = wkbFlatten(poGeometry->getGeometryType());
     772             : 
     773        1666 :     if (wkbPoint == eType)
     774         161 :         return "Point";
     775        1505 :     else if (wkbLineString == eType)
     776          56 :         return "LineString";
     777        1449 :     else if (wkbPolygon == eType)
     778        1282 :         return "Polygon";
     779         167 :     else if (wkbMultiPoint == eType)
     780          64 :         return "MultiPoint";
     781         103 :     else if (wkbMultiLineString == eType)
     782          34 :         return "MultiLineString";
     783          69 :     else if (wkbMultiPolygon == eType)
     784          43 :         return "MultiPolygon";
     785          26 :     else if (wkbGeometryCollection == eType)
     786          25 :         return "GeometryCollection";
     787             : 
     788           1 :     return "Unknown";
     789             : }
     790             : 
     791             : /************************************************************************/
     792             : /*                    OGRGeoJSONReadSpatialReference                    */
     793             : /************************************************************************/
     794             : 
     795         495 : OGRSpatialReference *OGRGeoJSONReadSpatialReference(json_object *poObj)
     796             : {
     797             : 
     798             :     /* -------------------------------------------------------------------- */
     799             :     /*      Read spatial reference definition.                              */
     800             :     /* -------------------------------------------------------------------- */
     801         495 :     OGRSpatialReference *poSRS = nullptr;
     802             : 
     803         495 :     json_object *poObjSrs = OGRGeoJSONFindMemberByName(poObj, "crs");
     804         495 :     if (nullptr != poObjSrs)
     805             :     {
     806             :         json_object *poObjSrsType =
     807          70 :             OGRGeoJSONFindMemberByName(poObjSrs, "type");
     808          70 :         if (poObjSrsType == nullptr)
     809           1 :             return nullptr;
     810             : 
     811          69 :         const char *pszSrsType = json_object_get_string(poObjSrsType);
     812             : 
     813             :         // TODO: Add URL and URN types support.
     814          69 :         if (STARTS_WITH_CI(pszSrsType, "NAME"))
     815             :         {
     816             :             json_object *poObjSrsProps =
     817          49 :                 OGRGeoJSONFindMemberByName(poObjSrs, "properties");
     818          49 :             if (poObjSrsProps == nullptr)
     819           2 :                 return nullptr;
     820             : 
     821             :             json_object *poNameURL =
     822          47 :                 OGRGeoJSONFindMemberByName(poObjSrsProps, "name");
     823          47 :             if (poNameURL == nullptr)
     824           2 :                 return nullptr;
     825             : 
     826          45 :             const char *pszName = json_object_get_string(poNameURL);
     827             : 
     828             :             // Mostly to emulate GDAL 2.x behavior
     829             :             // See https://github.com/OSGeo/gdal/issues/2035
     830          45 :             if (EQUAL(pszName, "urn:ogc:def:crs:OGC:1.3:CRS84"))
     831           9 :                 pszName = "EPSG:4326";
     832             : 
     833          45 :             poSRS = new OGRSpatialReference();
     834          45 :             poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     835          45 :             if (OGRERR_NONE !=
     836          45 :                 poSRS->SetFromUserInput(
     837             :                     pszName,
     838             :                     OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()))
     839             :             {
     840           2 :                 delete poSRS;
     841           2 :                 poSRS = nullptr;
     842             :             }
     843             :         }
     844             : 
     845          20 :         else if (STARTS_WITH_CI(pszSrsType, "EPSG"))
     846             :         {
     847             :             json_object *poObjSrsProps =
     848           7 :                 OGRGeoJSONFindMemberByName(poObjSrs, "properties");
     849           7 :             if (poObjSrsProps == nullptr)
     850           2 :                 return nullptr;
     851             : 
     852             :             json_object *poObjCode =
     853           5 :                 OGRGeoJSONFindMemberByName(poObjSrsProps, "code");
     854           5 :             if (poObjCode == nullptr)
     855           2 :                 return nullptr;
     856             : 
     857           3 :             int nEPSG = json_object_get_int(poObjCode);
     858             : 
     859           3 :             poSRS = new OGRSpatialReference();
     860           3 :             poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     861           3 :             if (OGRERR_NONE != poSRS->importFromEPSG(nEPSG))
     862             :             {
     863           2 :                 delete poSRS;
     864           2 :                 poSRS = nullptr;
     865             :             }
     866             :         }
     867             : 
     868          13 :         else if (STARTS_WITH_CI(pszSrsType, "URL") ||
     869          13 :                  STARTS_WITH_CI(pszSrsType, "LINK"))
     870             :         {
     871             :             json_object *poObjSrsProps =
     872           6 :                 OGRGeoJSONFindMemberByName(poObjSrs, "properties");
     873           6 :             if (poObjSrsProps == nullptr)
     874           2 :                 return nullptr;
     875             : 
     876             :             json_object *poObjURL =
     877           4 :                 OGRGeoJSONFindMemberByName(poObjSrsProps, "url");
     878             : 
     879           4 :             if (nullptr == poObjURL)
     880             :             {
     881           4 :                 poObjURL = OGRGeoJSONFindMemberByName(poObjSrsProps, "href");
     882             :             }
     883           4 :             if (poObjURL == nullptr)
     884           2 :                 return nullptr;
     885             : 
     886           2 :             const char *pszURL = json_object_get_string(poObjURL);
     887             : 
     888           2 :             poSRS = new OGRSpatialReference();
     889           2 :             poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     890           2 :             if (OGRERR_NONE != poSRS->importFromUrl(pszURL))
     891             :             {
     892           2 :                 delete poSRS;
     893           2 :                 poSRS = nullptr;
     894           2 :             }
     895             :         }
     896             : 
     897           7 :         else if (EQUAL(pszSrsType, "OGC"))
     898             :         {
     899             :             json_object *poObjSrsProps =
     900           7 :                 OGRGeoJSONFindMemberByName(poObjSrs, "properties");
     901           7 :             if (poObjSrsProps == nullptr)
     902           2 :                 return nullptr;
     903             : 
     904             :             json_object *poObjURN =
     905           5 :                 OGRGeoJSONFindMemberByName(poObjSrsProps, "urn");
     906           5 :             if (poObjURN == nullptr)
     907           2 :                 return nullptr;
     908             : 
     909           3 :             poSRS = new OGRSpatialReference();
     910           3 :             poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     911           3 :             if (OGRERR_NONE !=
     912           3 :                 poSRS->importFromURN(json_object_get_string(poObjURN)))
     913             :             {
     914           2 :                 delete poSRS;
     915           2 :                 poSRS = nullptr;
     916             :             }
     917             :         }
     918             :     }
     919             : 
     920             :     // Strip AXIS, since geojson has (easting, northing) / (longitude, latitude)
     921             :     // order.  According to http://www.geojson.org/geojson-spec.html#id2 :
     922             :     // "Point coordinates are in x, y order (easting, northing for projected
     923             :     // coordinates, longitude, latitude for geographic coordinates)".
     924         478 :     if (poSRS != nullptr)
     925             :     {
     926          45 :         OGR_SRSNode *poGEOGCS = poSRS->GetAttrNode("GEOGCS");
     927          45 :         if (poGEOGCS != nullptr)
     928          45 :             poGEOGCS->StripNodes("AXIS");
     929             :     }
     930             : 
     931         478 :     return poSRS;
     932             : }
     933             : 
     934             : /************************************************************************/
     935             : /*                       OGR_G_CreateGeometryFromJson                   */
     936             : /************************************************************************/
     937             : 
     938             : /** Create a OGR geometry from a GeoJSON geometry object */
     939          46 : OGRGeometryH OGR_G_CreateGeometryFromJson(const char *pszJson)
     940             : {
     941          46 :     if (nullptr == pszJson)
     942             :     {
     943             :         // Translation failed.
     944           0 :         return nullptr;
     945             :     }
     946             : 
     947          46 :     json_object *poObj = nullptr;
     948          46 :     if (!OGRJSonParse(pszJson, &poObj))
     949           0 :         return nullptr;
     950             : 
     951          46 :     OGRGeometry *poGeometry = OGRGeoJSONReadGeometry(poObj);
     952             : 
     953             :     // Release JSON tree.
     954          46 :     json_object_put(poObj);
     955             : 
     956          46 :     return OGRGeometry::ToHandle(poGeometry);
     957             : }
     958             : 
     959             : /*! @endcond */

Generated by: LCOV version 1.14