LCOV - code coverage report
Current view: top level - ogr - ogrgeojsongeometry.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 425 452 94.0 %
Date: 2026-02-23 15:56:29 Functions: 31 31 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 std::unique_ptr<OGRPoint> OGRGeoJSONReadPoint(json_object *poObj,
      14             :                                                      bool bHasM);
      15             : static std::unique_ptr<OGRMultiPoint>
      16             : OGRGeoJSONReadMultiPoint(json_object *poObj, bool bHasM);
      17             : static std::unique_ptr<OGRLineString>
      18             : OGRGeoJSONReadLineString(json_object *poObj, bool bHasM, bool bRaw);
      19             : static std::unique_ptr<OGRMultiLineString>
      20             : OGRGeoJSONReadMultiLineString(json_object *poObj, bool bHasM);
      21             : static std::unique_ptr<OGRLinearRing>
      22             : OGRGeoJSONReadLinearRing(json_object *poObj, bool bHasM);
      23             : static std::unique_ptr<OGRMultiPolygon>
      24             : OGRGeoJSONReadMultiPolygon(json_object *poObj, bool bHasM);
      25             : static std::unique_ptr<OGRGeometryCollection>
      26             : OGRGeoJSONReadGeometryCollection(json_object *poObj, bool bHasM,
      27             :                                  const OGRSpatialReference *poSRS);
      28             : static std::unique_ptr<OGRCircularString>
      29             : OGRGeoJSONReadCircularString(json_object *poObj, bool bHasM);
      30             : static std::unique_ptr<OGRCompoundCurve>
      31             : OGRGeoJSONReadCompoundCurve(json_object *poObj, bool bHasM,
      32             :                             const OGRSpatialReference *poSRS);
      33             : static std::unique_ptr<OGRCurvePolygon>
      34             : OGRGeoJSONReadCurvePolygon(json_object *poObj, bool bHasM);
      35             : static std::unique_ptr<OGRMultiCurve>
      36             : OGRGeoJSONReadMultiCurve(json_object *poObj, bool bHasM,
      37             :                          const OGRSpatialReference *poSRS);
      38             : static std::unique_ptr<OGRMultiSurface>
      39             : OGRGeoJSONReadMultiSurface(json_object *poObj, bool bHasM,
      40             :                            const OGRSpatialReference *poSRS);
      41             : 
      42             : /************************************************************************/
      43             : /*                          OGRGeoJSONGetType                           */
      44             : /************************************************************************/
      45             : 
      46        3371 : GeoJSONObject::Type OGRGeoJSONGetType(json_object *poObj)
      47             : {
      48        3371 :     if (nullptr == poObj)
      49           0 :         return GeoJSONObject::eUnknown;
      50             : 
      51        3371 :     json_object *poObjType = OGRGeoJSONFindMemberByName(poObj, "type");
      52        3371 :     if (nullptr == poObjType)
      53           2 :         return GeoJSONObject::eUnknown;
      54             : 
      55        3369 :     const char *name = json_object_get_string(poObjType);
      56             : 
      57             : #define ASSOC(x)                                                               \
      58             :     {                                                                          \
      59             :         #x, GeoJSONObject::e##x                                                \
      60             :     }
      61             : 
      62             :     static const struct
      63             :     {
      64             :         const char *pszName;
      65             :         GeoJSONObject::Type eType;
      66             :     } tabAssoc[] = {
      67             :         ASSOC(Point),
      68             :         ASSOC(LineString),
      69             :         ASSOC(Polygon),
      70             :         ASSOC(MultiPoint),
      71             :         ASSOC(MultiLineString),
      72             :         ASSOC(MultiPolygon),
      73             :         ASSOC(GeometryCollection),
      74             :         ASSOC(CircularString),
      75             :         ASSOC(CompoundCurve),
      76             :         ASSOC(CurvePolygon),
      77             :         ASSOC(MultiCurve),
      78             :         ASSOC(MultiSurface),
      79             :         ASSOC(Feature),
      80             :         ASSOC(FeatureCollection),
      81             :     };
      82             : 
      83             : #undef ASSOC
      84             : 
      85       21734 :     for (const auto &assoc : tabAssoc)
      86             :     {
      87       21734 :         if (EQUAL(name, assoc.pszName))
      88        3369 :             return assoc.eType;
      89             :     }
      90             : 
      91           0 :     return GeoJSONObject::eUnknown;
      92             : }
      93             : 
      94             : /************************************************************************/
      95             : /*                        OGRJSONFGHasMeasure()                         */
      96             : /************************************************************************/
      97             : 
      98        8539 : bool OGRJSONFGHasMeasure(json_object *poObj, bool bUpperLevelMValue)
      99             : {
     100        8539 :     bool bHasM = bUpperLevelMValue;
     101        8539 :     if (json_object *pojMeasures =
     102        8539 :             CPL_json_object_object_get(poObj, "measures"))
     103             :     {
     104             :         json_object *poEnabled =
     105          63 :             CPL_json_object_object_get(pojMeasures, "enabled");
     106          63 :         bHasM = json_object_get_boolean(poEnabled);
     107             :     }
     108        8539 :     return bHasM;
     109             : }
     110             : 
     111             : /************************************************************************/
     112             : /*                        asAssocGeometryTypes[]                        */
     113             : /************************************************************************/
     114             : 
     115             : #define ASSOC(x) {#x, wkb##x}
     116             : 
     117             : static const struct
     118             : {
     119             :     const char *pszName;
     120             :     OGRwkbGeometryType eType;
     121             : } asAssocGeometryTypes[] = {
     122             :     ASSOC(Point),
     123             :     ASSOC(LineString),
     124             :     ASSOC(Polygon),
     125             :     ASSOC(MultiPoint),
     126             :     ASSOC(MultiLineString),
     127             :     ASSOC(MultiPolygon),
     128             :     ASSOC(GeometryCollection),
     129             :     ASSOC(CircularString),
     130             :     ASSOC(CompoundCurve),
     131             :     ASSOC(CurvePolygon),
     132             :     ASSOC(MultiCurve),
     133             :     ASSOC(MultiSurface),
     134             : };
     135             : 
     136             : #undef ASSOC
     137             : 
     138             : /************************************************************************/
     139             : /*                    OGRGeoJSONGetOGRGeometryType()                    */
     140             : /************************************************************************/
     141             : 
     142        4758 : OGRwkbGeometryType OGRGeoJSONGetOGRGeometryType(json_object *poObj, bool bHasM)
     143             : {
     144        4758 :     if (nullptr == poObj)
     145           1 :         return wkbUnknown;
     146             : 
     147        4757 :     json_object *poObjType = CPL_json_object_object_get(poObj, "type");
     148        4757 :     if (nullptr == poObjType)
     149           0 :         return wkbUnknown;
     150             : 
     151        4757 :     const char *name = json_object_get_string(poObjType);
     152             : 
     153        4757 :     OGRwkbGeometryType eType = wkbNone;
     154       14939 :     for (const auto &assoc : asAssocGeometryTypes)
     155             :     {
     156       14933 :         if (EQUAL(name, assoc.pszName))
     157             :         {
     158        4751 :             eType = assoc.eType;
     159        4751 :             break;
     160             :         }
     161             :     }
     162        4757 :     if (eType == wkbNone)
     163           6 :         return wkbUnknown;
     164             : 
     165        4751 :     bHasM = OGRJSONFGHasMeasure(poObj, bHasM);
     166             : 
     167             :     json_object *poCoordinates;
     168        4751 :     if (eType == wkbGeometryCollection || eType == wkbMultiCurve ||
     169        4713 :         eType == wkbMultiSurface || eType == wkbCompoundCurve ||
     170             :         eType == wkbCurvePolygon)
     171             :     {
     172             :         json_object *poGeometries =
     173          68 :             CPL_json_object_object_get(poObj, "geometries");
     174         135 :         if (poGeometries &&
     175         135 :             json_object_get_type(poGeometries) == json_type_array &&
     176          67 :             json_object_array_length(poGeometries) > 0)
     177             :         {
     178          65 :             const auto subGeomType = OGRGeoJSONGetOGRGeometryType(
     179             :                 json_object_array_get_idx(poGeometries, 0), bHasM);
     180          65 :             if (OGR_GT_HasZ(subGeomType))
     181          29 :                 eType = OGR_GT_SetZ(eType);
     182          68 :         }
     183             :     }
     184             :     else
     185             :     {
     186        4683 :         poCoordinates = CPL_json_object_object_get(poObj, "coordinates");
     187        9360 :         if (poCoordinates &&
     188        9360 :             json_object_get_type(poCoordinates) == json_type_array &&
     189        4677 :             json_object_array_length(poCoordinates) > 0)
     190             :         {
     191             :             while (true)
     192             :             {
     193       13391 :                 auto poChild = json_object_array_get_idx(poCoordinates, 0);
     194       26772 :                 if (!(poChild &&
     195       13381 :                       json_object_get_type(poChild) == json_type_array &&
     196        8723 :                       json_object_array_length(poChild) > 0))
     197             :                 {
     198             :                     const auto nLength =
     199        4672 :                         json_object_array_length(poCoordinates);
     200        4672 :                     if ((bHasM && nLength == 4) || (!bHasM && nLength == 3))
     201         139 :                         eType = OGR_GT_SetZ(eType);
     202        4672 :                     break;
     203             :                 }
     204        8719 :                 poCoordinates = poChild;
     205        8719 :             }
     206             :         }
     207             :     }
     208        4751 :     if (bHasM)
     209          48 :         eType = OGR_GT_SetM(eType);
     210             : 
     211        4751 :     return eType;
     212             : }
     213             : 
     214             : /************************************************************************/
     215             : /*                     OGRGeoJSONGetGeometryName()                      */
     216             : /************************************************************************/
     217             : 
     218        1856 : const char *OGRGeoJSONGetGeometryName(OGRGeometry const *poGeometry)
     219             : {
     220        1856 :     CPLAssert(nullptr != poGeometry);
     221             : 
     222        1856 :     const OGRwkbGeometryType eType = wkbFlatten(poGeometry->getGeometryType());
     223             : 
     224        5817 :     for (const auto &assoc : asAssocGeometryTypes)
     225             :     {
     226        5816 :         if (eType == assoc.eType)
     227             :         {
     228        1855 :             return assoc.pszName;
     229             :         }
     230             :     }
     231           1 :     return "Unknown";
     232             : }
     233             : 
     234             : /************************************************************************/
     235             : /*                        OGRGeoJSONReadGeometry                        */
     236             : /************************************************************************/
     237             : 
     238             : std::unique_ptr<OGRGeometry>
     239        2171 : OGRGeoJSONReadGeometry(json_object *poObj, bool bHasM,
     240             :                        const OGRSpatialReference *poParentSRS)
     241             : {
     242             : 
     243        2171 :     std::unique_ptr<OGRGeometry> poGeometry;
     244        2171 :     OGRSpatialReference *poSRS = nullptr;
     245        2171 :     lh_entry *entry = OGRGeoJSONFindMemberEntryByName(poObj, "crs");
     246        2171 :     if (entry != nullptr)
     247             :     {
     248           4 :         json_object *poObjSrs =
     249             :             static_cast<json_object *>(const_cast<void *>(entry->v));
     250           4 :         if (poObjSrs != nullptr)
     251             :         {
     252           3 :             poSRS = OGRGeoJSONReadSpatialReference(poObj);
     253             :         }
     254             :     }
     255             : 
     256        2171 :     const OGRSpatialReference *poSRSToAssign = nullptr;
     257        2171 :     if (entry != nullptr)
     258             :     {
     259           4 :         poSRSToAssign = poSRS;
     260             :     }
     261        2167 :     else if (poParentSRS)
     262             :     {
     263        1433 :         poSRSToAssign = poParentSRS;
     264             :     }
     265             :     else
     266             :     {
     267             :         // Assign WGS84 if no CRS defined on geometry.
     268         734 :         poSRSToAssign = OGRSpatialReference::GetWGS84SRS();
     269             :     }
     270             : 
     271        2171 :     bHasM = OGRJSONFGHasMeasure(poObj, bHasM);
     272             : 
     273        2171 :     const auto objType = OGRGeoJSONGetType(poObj);
     274        2171 :     switch (objType)
     275             :     {
     276         373 :         case GeoJSONObject::ePoint:
     277         373 :             poGeometry = OGRGeoJSONReadPoint(poObj, bHasM);
     278         373 :             break;
     279             : 
     280         119 :         case GeoJSONObject::eLineString:
     281         238 :             poGeometry = OGRGeoJSONReadLineString(poObj, bHasM,
     282         119 :                                                   /* bRaw = */ false);
     283         119 :             break;
     284             : 
     285        1037 :         case GeoJSONObject::ePolygon:
     286             :             poGeometry =
     287        1037 :                 OGRGeoJSONReadPolygon(poObj, bHasM, /* bRaw = */ false);
     288        1037 :             break;
     289             : 
     290          64 :         case GeoJSONObject::eMultiPoint:
     291          64 :             poGeometry = OGRGeoJSONReadMultiPoint(poObj, bHasM);
     292          64 :             break;
     293             : 
     294          39 :         case GeoJSONObject::eMultiLineString:
     295          39 :             poGeometry = OGRGeoJSONReadMultiLineString(poObj, bHasM);
     296          39 :             break;
     297             : 
     298         415 :         case GeoJSONObject::eMultiPolygon:
     299         415 :             poGeometry = OGRGeoJSONReadMultiPolygon(poObj, bHasM);
     300         415 :             break;
     301             : 
     302          11 :         case GeoJSONObject::eGeometryCollection:
     303             :             poGeometry =
     304          11 :                 OGRGeoJSONReadGeometryCollection(poObj, bHasM, poSRSToAssign);
     305          11 :             break;
     306             : 
     307          47 :         case GeoJSONObject::eCircularString:
     308          47 :             poGeometry = OGRGeoJSONReadCircularString(poObj, bHasM);
     309          47 :             break;
     310             : 
     311          28 :         case GeoJSONObject::eCompoundCurve:
     312             :             poGeometry =
     313          28 :                 OGRGeoJSONReadCompoundCurve(poObj, bHasM, poSRSToAssign);
     314          28 :             break;
     315             : 
     316          20 :         case GeoJSONObject::eCurvePolygon:
     317          20 :             poGeometry = OGRGeoJSONReadCurvePolygon(poObj, bHasM);
     318          20 :             break;
     319             : 
     320           9 :         case GeoJSONObject::eMultiCurve:
     321           9 :             poGeometry = OGRGeoJSONReadMultiCurve(poObj, bHasM, poSRSToAssign);
     322           9 :             break;
     323             : 
     324           9 :         case GeoJSONObject::eMultiSurface:
     325             :             poGeometry =
     326           9 :                 OGRGeoJSONReadMultiSurface(poObj, bHasM, poSRSToAssign);
     327           9 :             break;
     328             : 
     329           0 :         case GeoJSONObject::eFeature:
     330             :         case GeoJSONObject::eFeatureCollection:
     331             :             [[fallthrough]];
     332             :         case GeoJSONObject::eUnknown:
     333           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     334             :                      "Unsupported geometry type detected. "
     335             :                      "Feature gets NULL geometry assigned.");
     336           0 :             break;
     337             :     }
     338             : 
     339        2171 :     if (poGeometry && GeoJSONObject::eGeometryCollection != objType)
     340        2111 :         poGeometry->assignSpatialReference(poSRSToAssign);
     341             : 
     342        2171 :     if (poSRS)
     343           3 :         poSRS->Release();
     344             : 
     345        2171 :     return poGeometry;
     346             : }
     347             : 
     348             : /************************************************************************/
     349             : /*                        GetJSONConstructName()                        */
     350             : /************************************************************************/
     351             : 
     352          27 : static const char *GetJSONConstructName(json_type eType)
     353             : {
     354          27 :     switch (eType)
     355             :     {
     356           6 :         case json_type_null:
     357           6 :             break;
     358           0 :         case json_type_boolean:
     359           0 :             return "boolean";
     360           0 :         case json_type_double:
     361           0 :             return "double";
     362           0 :         case json_type_int:
     363           0 :             return "int";
     364           0 :         case json_type_object:
     365           0 :             return "object";
     366           0 :         case json_type_array:
     367           0 :             return "array";
     368          21 :         case json_type_string:
     369          21 :             return "string";
     370             :     }
     371           6 :     return "null";
     372             : }
     373             : 
     374             : /************************************************************************/
     375             : /*                      OGRGeoJSONGetCoordinate()                       */
     376             : /************************************************************************/
     377             : 
     378      185275 : static double OGRGeoJSONGetCoordinate(json_object *poObj,
     379             :                                       const char *pszCoordName, int nIndex,
     380             :                                       bool &bValid)
     381             : {
     382      185275 :     json_object *poObjCoord = json_object_array_get_idx(poObj, nIndex);
     383      185275 :     if (nullptr == poObjCoord)
     384             :     {
     385           6 :         CPLDebug("GeoJSON", "Point: got null object for %s.", pszCoordName);
     386           6 :         bValid = false;
     387           6 :         return 0.0;
     388             :     }
     389             : 
     390      185269 :     const json_type eType = json_object_get_type(poObjCoord);
     391      185269 :     if (json_type_double != eType && json_type_int != eType)
     392             :     {
     393           6 :         CPLError(CE_Failure, CPLE_AppDefined,
     394             :                  "OGRGeoJSONGetCoordinate(): invalid '%s' coordinate. "
     395             :                  "Unexpected type %s for '%s'. Expected double or integer.",
     396             :                  pszCoordName, GetJSONConstructName(eType),
     397             :                  json_object_to_json_string(poObjCoord));
     398           6 :         bValid = false;
     399           6 :         return 0.0;
     400             :     }
     401             : 
     402      185263 :     return json_object_get_double(poObjCoord);
     403             : }
     404             : 
     405             : /************************************************************************/
     406             : /*                        OGRGeoJSONReadRawPoint                        */
     407             : /************************************************************************/
     408             : 
     409       92259 : static bool OGRGeoJSONReadRawPoint(json_object *poObj, OGRPoint &point,
     410             :                                    bool bHasM)
     411             : {
     412       92259 :     if (json_type_array == json_object_get_type(poObj))
     413             :     {
     414       92247 :         const int nSize = static_cast<int>(json_object_array_length(poObj));
     415             : 
     416       92247 :         if (nSize < GeoJSONObject::eMinCoordinateDimension)
     417             :         {
     418           3 :             CPLError(CE_Warning, CPLE_AppDefined,
     419             :                      "OGRGeoJSONReadRawPoint(): "
     420             :                      "Invalid coord dimension for '%s'. "
     421             :                      "At least 2 dimensions must be present.",
     422             :                      json_object_to_json_string(poObj));
     423           3 :             return false;
     424             :         }
     425             : 
     426       92244 :         bool bValid = true;
     427       92244 :         const double dfX = OGRGeoJSONGetCoordinate(poObj, "x", 0, bValid);
     428       92244 :         const double dfY = OGRGeoJSONGetCoordinate(poObj, "y", 1, bValid);
     429       92244 :         point.setX(dfX);
     430       92244 :         point.setY(dfY);
     431             : 
     432             :         // Read Z and/or M coordinate.
     433       92244 :         if (nSize > GeoJSONObject::eMinCoordinateDimension)
     434             :         {
     435         670 :             const int nMaxDim =
     436         670 :                 bHasM ? GeoJSONObject::eMaxCoordinateDimensionJSONFG
     437             :                       : GeoJSONObject::eMaxCoordinateDimensionGeoJSON;
     438         670 :             if (nSize > nMaxDim)
     439             :             {
     440           6 :                 CPLErrorOnce(CE_Warning, CPLE_AppDefined,
     441             :                              "OGRGeoJSONReadRawPoint(): too many members in "
     442             :                              "array '%s': %d. At most %d are handled. Ignoring "
     443             :                              "extra members.",
     444             :                              json_object_to_json_string(poObj), nSize, nMaxDim);
     445             :             }
     446             :             // Don't *expect* mixed-dimension geometries, although the
     447             :             // spec doesn't explicitly forbid this.
     448         670 :             if (nSize == 4 || (nSize == 3 && !bHasM))
     449             :             {
     450             :                 const double dfZ =
     451         579 :                     OGRGeoJSONGetCoordinate(poObj, "z", 2, bValid);
     452         579 :                 point.setZ(dfZ);
     453             :             }
     454             : 
     455         670 :             if (bHasM)
     456             :             {
     457             :                 const double dfM =
     458         208 :                     OGRGeoJSONGetCoordinate(poObj, "m", nSize - 1, bValid);
     459         208 :                 point.setM(dfM);
     460             :             }
     461             :         }
     462             :         else
     463             :         {
     464       91574 :             point.flattenTo2D();
     465             :         }
     466       92244 :         return bValid;
     467             :     }
     468             :     else
     469             :     {
     470          12 :         CPLError(CE_Failure, CPLE_AppDefined,
     471             :                  "OGRGeoJSONReadRawPoint(): invalid Point. "
     472             :                  "Unexpected type %s for '%s'. Expected array.",
     473             :                  GetJSONConstructName(json_object_get_type(poObj)),
     474             :                  json_object_to_json_string(poObj));
     475             :     }
     476             : 
     477          12 :     return false;
     478             : }
     479             : 
     480             : /************************************************************************/
     481             : /*                         OGRGeoJSONReadPoint                          */
     482             : /************************************************************************/
     483             : 
     484         373 : std::unique_ptr<OGRPoint> OGRGeoJSONReadPoint(json_object *poObj, bool bHasM)
     485             : {
     486         373 :     if (!poObj)
     487             :     {
     488           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     489             :                  "OGRGeoJSONReadPoint(): invalid Point object. Got null.");
     490           0 :         return nullptr;
     491             :     }
     492         373 :     json_object *poObjCoords = OGRGeoJSONFindMemberByName(poObj, "coordinates");
     493         373 :     if (nullptr == poObjCoords)
     494             :     {
     495           4 :         CPLError(CE_Failure, CPLE_AppDefined,
     496             :                  "OGRGeoJSONReadPoint(): invalid Point object. "
     497             :                  "Missing \'coordinates\' member.");
     498           4 :         return nullptr;
     499             :     }
     500             : 
     501         738 :     auto poPoint = std::make_unique<OGRPoint>();
     502         369 :     if (!OGRGeoJSONReadRawPoint(poObjCoords, *poPoint, bHasM))
     503             :     {
     504          10 :         return nullptr;
     505             :     }
     506             : 
     507         359 :     return poPoint;
     508             : }
     509             : 
     510             : /************************************************************************/
     511             : /*                       OGRGeoJSONReadMultiPoint                       */
     512             : /************************************************************************/
     513             : 
     514          64 : std::unique_ptr<OGRMultiPoint> OGRGeoJSONReadMultiPoint(json_object *poObj,
     515             :                                                         bool bHasM)
     516             : {
     517          64 :     if (!poObj)
     518             :     {
     519           0 :         CPLError(
     520             :             CE_Failure, CPLE_AppDefined,
     521             :             "OGRGeoJSONReadMultiPoint(): invalid MultiPoint object. Got null.");
     522           0 :         return nullptr;
     523             :     }
     524          64 :     json_object *poObjPoints = OGRGeoJSONFindMemberByName(poObj, "coordinates");
     525          64 :     if (nullptr == poObjPoints)
     526             :     {
     527           3 :         CPLError(CE_Failure, CPLE_AppDefined,
     528             :                  "Invalid MultiPoint object. "
     529             :                  "Missing \'coordinates\' member.");
     530           3 :         return nullptr;
     531             :     }
     532             : 
     533          61 :     std::unique_ptr<OGRMultiPoint> poMultiPoint;
     534          61 :     if (json_type_array == json_object_get_type(poObjPoints))
     535             :     {
     536          60 :         const auto nPoints = json_object_array_length(poObjPoints);
     537             : 
     538          60 :         poMultiPoint = std::make_unique<OGRMultiPoint>();
     539             : 
     540         259 :         for (auto i = decltype(nPoints){0}; i < nPoints; ++i)
     541             :         {
     542             :             json_object *poObjCoords =
     543         203 :                 json_object_array_get_idx(poObjPoints, i);
     544             : 
     545         203 :             OGRPoint pt;
     546         203 :             if (!OGRGeoJSONReadRawPoint(poObjCoords, pt, bHasM))
     547             :             {
     548           4 :                 return nullptr;
     549             :             }
     550         199 :             poMultiPoint->addGeometry(&pt);
     551             :         }
     552             :     }
     553             :     else
     554             :     {
     555           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     556             :                  "OGRGeoJSONReadMultiPoint(): invalid MultiPoint. "
     557             :                  "Unexpected type %s for '%s'. Expected array.",
     558             :                  GetJSONConstructName(json_object_get_type(poObjPoints)),
     559             :                  json_object_to_json_string(poObjPoints));
     560             :     }
     561             : 
     562          57 :     return poMultiPoint;
     563             : }
     564             : 
     565             : /************************************************************************/
     566             : /*                      OGRGeoJSONReadSimpleCurve                       */
     567             : /************************************************************************/
     568             : 
     569             : template <class T>
     570        2073 : static std::unique_ptr<T> OGRGeoJSONReadSimpleCurve(const char *pszFuncName,
     571             :                                                     json_object *poObj,
     572             :                                                     bool bHasM, bool bRaw)
     573             : {
     574        2073 :     if (!poObj)
     575             :     {
     576           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     577             :                  "%s(): invalid LineString object. Got null.", pszFuncName);
     578           2 :         return nullptr;
     579             :     }
     580        2071 :     json_object *poObjPoints = nullptr;
     581             : 
     582        2071 :     if (!bRaw)
     583             :     {
     584         166 :         poObjPoints = OGRGeoJSONFindMemberByName(poObj, "coordinates");
     585         166 :         if (nullptr == poObjPoints)
     586             :         {
     587           3 :             CPLError(CE_Failure, CPLE_AppDefined,
     588             :                      "Invalid LineString object. "
     589             :                      "Missing \'coordinates\' member.");
     590           3 :             return nullptr;
     591             :         }
     592             :     }
     593             :     else
     594             :     {
     595        1905 :         poObjPoints = poObj;
     596             :     }
     597             : 
     598        2068 :     std::unique_ptr<T> poLine;
     599             : 
     600        2068 :     if (json_type_array == json_object_get_type(poObjPoints))
     601             :     {
     602        2064 :         const int nPoints =
     603        2064 :             static_cast<int>(json_object_array_length(poObjPoints));
     604             : 
     605        2064 :         poLine = std::make_unique<T>();
     606        2064 :         poLine->setNumPoints(nPoints);
     607             : 
     608       93739 :         for (int i = 0; i < nPoints; ++i)
     609             :         {
     610             :             json_object *poObjCoords =
     611       91687 :                 json_object_array_get_idx(poObjPoints, i);
     612             : 
     613       91687 :             OGRPoint pt;
     614       91687 :             if (!OGRGeoJSONReadRawPoint(poObjCoords, pt, bHasM))
     615             :             {
     616          12 :                 return nullptr;
     617             :             }
     618       91675 :             if (pt.Is3D())
     619         371 :                 poLine->set3D(true);
     620       91675 :             if (pt.IsMeasured())
     621         200 :                 poLine->setMeasured(true);
     622       91675 :             poLine->setPoint(i, &pt);
     623             :         }
     624             :     }
     625             :     else
     626             :     {
     627           4 :         CPLError(CE_Failure, CPLE_AppDefined,
     628             :                  "%s(): invalid geometry. "
     629             :                  "Unexpected type %s for '%s'. Expected array.",
     630             :                  pszFuncName,
     631             :                  GetJSONConstructName(json_object_get_type(poObjPoints)),
     632             :                  json_object_to_json_string(poObjPoints));
     633             :     }
     634             : 
     635        2056 :     return poLine;
     636             : }
     637             : 
     638             : /************************************************************************/
     639             : /*                       OGRGeoJSONReadLineString                       */
     640             : /************************************************************************/
     641             : 
     642         178 : std::unique_ptr<OGRLineString> OGRGeoJSONReadLineString(json_object *poObj,
     643             :                                                         bool bHasM, bool bRaw)
     644             : {
     645             :     return OGRGeoJSONReadSimpleCurve<OGRLineString>(__func__, poObj, bHasM,
     646         178 :                                                     bRaw);
     647             : }
     648             : 
     649             : /************************************************************************/
     650             : /*                     OGRGeoJSONReadCircularString                     */
     651             : /************************************************************************/
     652             : 
     653             : std::unique_ptr<OGRCircularString>
     654          47 : OGRGeoJSONReadCircularString(json_object *poObj, bool bHasM)
     655             : {
     656             :     return OGRGeoJSONReadSimpleCurve<OGRCircularString>(__func__, poObj, bHasM,
     657          47 :                                                         /* bRaw = */ false);
     658             : }
     659             : 
     660             : /************************************************************************/
     661             : /*                    OGRGeoJSONReadMultiLineString                     */
     662             : /************************************************************************/
     663             : 
     664             : std::unique_ptr<OGRMultiLineString>
     665          39 : OGRGeoJSONReadMultiLineString(json_object *poObj, bool bHasM)
     666             : {
     667          39 :     CPLAssert(nullptr != poObj);
     668             : 
     669          39 :     json_object *poObjLines = OGRGeoJSONFindMemberByName(poObj, "coordinates");
     670          39 :     if (nullptr == poObjLines)
     671             :     {
     672           3 :         CPLError(CE_Failure, CPLE_AppDefined,
     673             :                  "Invalid MultiLineString object. "
     674             :                  "Missing \'coordinates\' member.");
     675           3 :         return nullptr;
     676             :     }
     677             : 
     678          36 :     std::unique_ptr<OGRMultiLineString> poMultiLine;
     679             : 
     680          36 :     if (json_type_array == json_object_get_type(poObjLines))
     681             :     {
     682          35 :         const auto nLines = json_object_array_length(poObjLines);
     683             : 
     684          35 :         poMultiLine = std::make_unique<OGRMultiLineString>();
     685             : 
     686          94 :         for (auto i = decltype(nLines){0}; i < nLines; ++i)
     687             :         {
     688          59 :             json_object *poObjLine = json_object_array_get_idx(poObjLines, i);
     689             : 
     690             :             auto poLine =
     691         118 :                 OGRGeoJSONReadLineString(poObjLine, bHasM, /* bRaw = */ true);
     692          59 :             if (poLine)
     693             :             {
     694          52 :                 poMultiLine->addGeometry(std::move(poLine));
     695             :             }
     696             :         }
     697             :     }
     698             :     else
     699             :     {
     700           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     701             :                  "OGRGeoJSONReadLineString(): invalid LineString. "
     702             :                  "Unexpected type %s for '%s'. Expected array.",
     703             :                  GetJSONConstructName(json_object_get_type(poObjLines)),
     704             :                  json_object_to_json_string(poObjLines));
     705             :     }
     706             : 
     707          36 :     return poMultiLine;
     708             : }
     709             : 
     710             : /************************************************************************/
     711             : /*                       OGRGeoJSONReadLinearRing                       */
     712             : /************************************************************************/
     713             : 
     714        1848 : std::unique_ptr<OGRLinearRing> OGRGeoJSONReadLinearRing(json_object *poObj,
     715             :                                                         bool bHasM)
     716             : {
     717             :     return OGRGeoJSONReadSimpleCurve<OGRLinearRing>(__func__, poObj, bHasM,
     718        1848 :                                                     /* bRaw = */ true);
     719             : }
     720             : 
     721             : /************************************************************************/
     722             : /*                        OGRGeoJSONReadPolygon                         */
     723             : /************************************************************************/
     724             : 
     725        1698 : std::unique_ptr<OGRPolygon> OGRGeoJSONReadPolygon(json_object *poObj,
     726             :                                                   bool bHasM, bool bRaw)
     727             : {
     728        1698 :     if (!poObj)
     729             :     {
     730           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     731             :                  "OGRGeoJSONReadPolygon(): invalid Polygon object. Got null.");
     732           0 :         return nullptr;
     733             :     }
     734        1698 :     json_object *poObjRings = nullptr;
     735             : 
     736        1698 :     if (!bRaw)
     737             :     {
     738        1037 :         poObjRings = OGRGeoJSONFindMemberByName(poObj, "coordinates");
     739        1037 :         if (nullptr == poObjRings)
     740             :         {
     741           3 :             CPLError(CE_Failure, CPLE_AppDefined,
     742             :                      "Invalid Polygon object. "
     743             :                      "Missing \'coordinates\' member.");
     744           3 :             return nullptr;
     745             :         }
     746             :     }
     747             :     else
     748             :     {
     749         661 :         poObjRings = poObj;
     750             :     }
     751             : 
     752        1695 :     std::unique_ptr<OGRPolygon> poPolygon;
     753             : 
     754        1695 :     if (json_type_array == json_object_get_type(poObjRings))
     755             :     {
     756        1693 :         const auto nRings = json_object_array_length(poObjRings);
     757        1693 :         if (nRings > 0)
     758             :         {
     759        1689 :             json_object *poObjPoints = json_object_array_get_idx(poObjRings, 0);
     760        1689 :             if (!poObjPoints)
     761             :             {
     762           2 :                 poPolygon = std::make_unique<OGRPolygon>();
     763             :             }
     764             :             else
     765             :             {
     766        3374 :                 auto poRing = OGRGeoJSONReadLinearRing(poObjPoints, bHasM);
     767        1687 :                 if (poRing)
     768             :                 {
     769        1681 :                     poPolygon = std::make_unique<OGRPolygon>();
     770        1681 :                     poPolygon->addRing(std::move(poRing));
     771             :                 }
     772             :             }
     773             : 
     774        1851 :             for (auto i = decltype(nRings){1};
     775        1851 :                  i < nRings && nullptr != poPolygon; ++i)
     776             :             {
     777         162 :                 poObjPoints = json_object_array_get_idx(poObjRings, i);
     778         162 :                 if (poObjPoints)
     779             :                 {
     780         322 :                     auto poRing = OGRGeoJSONReadLinearRing(poObjPoints, bHasM);
     781         161 :                     if (poRing)
     782             :                     {
     783         161 :                         poPolygon->addRing(std::move(poRing));
     784             :                     }
     785             :                 }
     786             :             }
     787             :         }
     788             :         else
     789             :         {
     790           4 :             poPolygon = std::make_unique<OGRPolygon>();
     791             :         }
     792             :     }
     793             :     else
     794             :     {
     795           2 :         CPLError(CE_Warning, CPLE_AppDefined,
     796             :                  "OGRGeoJSONReadPolygon(): unexpected type of JSON construct "
     797             :                  "%s for '%s'. Expected array.",
     798             :                  GetJSONConstructName(json_object_get_type(poObjRings)),
     799             :                  json_object_to_json_string(poObjRings));
     800             :     }
     801             : 
     802        1695 :     return poPolygon;
     803             : }
     804             : 
     805             : /************************************************************************/
     806             : /*                      OGRGeoJSONReadMultiPolygon                      */
     807             : /************************************************************************/
     808             : 
     809         415 : std::unique_ptr<OGRMultiPolygon> OGRGeoJSONReadMultiPolygon(json_object *poObj,
     810             :                                                             bool bHasM)
     811             : {
     812         415 :     CPLAssert(nullptr != poObj);
     813             : 
     814         415 :     json_object *poObjPolys = OGRGeoJSONFindMemberByName(poObj, "coordinates");
     815         415 :     if (nullptr == poObjPolys)
     816             :     {
     817           3 :         CPLError(CE_Failure, CPLE_AppDefined,
     818             :                  "Invalid MultiPolygon object. "
     819             :                  "Missing \'coordinates\' member.");
     820           3 :         return nullptr;
     821             :     }
     822             : 
     823         412 :     std::unique_ptr<OGRMultiPolygon> poMultiPoly;
     824             : 
     825         412 :     if (json_type_array == json_object_get_type(poObjPolys))
     826             :     {
     827             :         const int nPolys =
     828         411 :             static_cast<int>(json_object_array_length(poObjPolys));
     829             : 
     830         411 :         poMultiPoly = std::make_unique<OGRMultiPolygon>();
     831             : 
     832        1070 :         for (int i = 0; i < nPolys; ++i)
     833             :         {
     834         659 :             json_object *poObjPoly = json_object_array_get_idx(poObjPolys, i);
     835         659 :             if (!poObjPoly)
     836             :             {
     837           6 :                 poMultiPoly->addGeometryDirectly(
     838           6 :                     std::make_unique<OGRPolygon>().release());
     839             :             }
     840             :             else
     841             :             {
     842             :                 auto poPoly =
     843        1312 :                     OGRGeoJSONReadPolygon(poObjPoly, bHasM, /* bRaw = */ true);
     844         656 :                 if (poPoly)
     845             :                 {
     846         652 :                     poMultiPoly->addGeometry(std::move(poPoly));
     847             :                 }
     848             :             }
     849             :         }
     850             :     }
     851             :     else
     852             :     {
     853           1 :         CPLError(CE_Warning, CPLE_AppDefined,
     854             :                  "OGRGeoJSONReadMultiPolygon(): unexpected type of JSON "
     855             :                  "construct %s for '%s'. Expected array.",
     856             :                  GetJSONConstructName(json_object_get_type(poObjPolys)),
     857             :                  json_object_to_json_string(poObjPolys));
     858             :     }
     859             : 
     860         412 :     return poMultiPoly;
     861             : }
     862             : 
     863             : /************************************************************************/
     864             : /*                       OGRGeoJSONReadCollection                       */
     865             : /************************************************************************/
     866             : 
     867             : template <class T>
     868             : static std::unique_ptr<T>
     869          77 : OGRGeoJSONReadCollection(const char *pszFuncName, const char *pszGeomTypeName,
     870             :                          json_object *poObj, bool bHasM,
     871             :                          const OGRSpatialReference *poSRS)
     872             : {
     873          77 :     CPLAssert(nullptr != poObj);
     874             : 
     875          77 :     json_object *poObjGeoms = OGRGeoJSONFindMemberByName(poObj, "geometries");
     876          77 :     if (nullptr == poObjGeoms)
     877             :     {
     878           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     879             :                  "Invalid %s object. "
     880             :                  "Missing \'geometries\' member.",
     881             :                  pszGeomTypeName);
     882           1 :         return nullptr;
     883             :     }
     884             : 
     885          76 :     std::unique_ptr<T> poCollection;
     886             : 
     887          76 :     if (json_type_array == json_object_get_type(poObjGeoms))
     888             :     {
     889          76 :         poCollection = std::make_unique<T>();
     890          76 :         poCollection->assignSpatialReference(poSRS);
     891             : 
     892          76 :         const int nGeoms =
     893          76 :             static_cast<int>(json_object_array_length(poObjGeoms));
     894         229 :         for (int i = 0; i < nGeoms; ++i)
     895             :         {
     896         157 :             json_object *poObjGeom = json_object_array_get_idx(poObjGeoms, i);
     897         157 :             if (!poObjGeom)
     898             :             {
     899           3 :                 CPLError(CE_Warning, CPLE_AppDefined,
     900             :                          "%s(): skipping null "
     901             :                          "sub-geometry",
     902             :                          pszFuncName);
     903           3 :                 continue;
     904             :             }
     905             : 
     906         154 :             auto poGeometry = OGRGeoJSONReadGeometry(poObjGeom, bHasM, poSRS);
     907         154 :             if (poGeometry)
     908             :             {
     909             :                 if constexpr (std::is_same_v<T, OGRCompoundCurve>)
     910             :                 {
     911          56 :                     auto eFlatType = wkbFlatten(poGeometry->getGeometryType());
     912          56 :                     if (eFlatType == wkbLineString ||
     913             :                         eFlatType == wkbCircularString)
     914             :                     {
     915          55 :                         if (poCollection->addCurve(std::unique_ptr<OGRCurve>(
     916          55 :                                 poGeometry.release()->toCurve())) !=
     917             :                             OGRERR_NONE)
     918           0 :                             return nullptr;
     919             :                     }
     920             :                     else
     921             :                     {
     922           1 :                         CPLError(CE_Warning, CPLE_AppDefined,
     923             :                                  "%s(): member of a CompoundCurve is not a "
     924             :                                  "LineString or CircularString.",
     925             :                                  pszFuncName);
     926           1 :                         return nullptr;
     927             :                     }
     928             :                 }
     929             :                 else if constexpr (std::is_same_v<T, OGRCurvePolygon>)
     930             :                 {
     931          41 :                     auto eFlatType = wkbFlatten(poGeometry->getGeometryType());
     932          41 :                     if (eFlatType == wkbLineString ||
     933          11 :                         eFlatType == wkbCircularString ||
     934             :                         eFlatType == wkbCompoundCurve)
     935             :                     {
     936          40 :                         if (poCollection->addRing(std::unique_ptr<OGRCurve>(
     937          40 :                                 poGeometry.release()->toCurve())) !=
     938             :                             OGRERR_NONE)
     939           0 :                             return nullptr;
     940             :                     }
     941             :                     else
     942             :                     {
     943           1 :                         CPLError(CE_Warning, CPLE_AppDefined,
     944             :                                  "%s(): member of a CurvePolygon is not a "
     945             :                                  "LineString, CircularString or CompoundCurve.",
     946             :                                  pszFuncName);
     947           1 :                         return nullptr;
     948             :                     }
     949             :                 }
     950             :                 else
     951             :                 {
     952          57 :                     const auto eChildType = poGeometry->getGeometryType();
     953          57 :                     if (poCollection->addGeometry(std::move(poGeometry)) !=
     954             :                         OGRERR_NONE)
     955             :                     {
     956           2 :                         CPLError(
     957             :                             CE_Warning, CPLE_AppDefined,
     958             :                             "%s(): Invalid child geometry type (%s) for %s",
     959             :                             pszFuncName, OGRToOGCGeomType(eChildType),
     960             :                             pszGeomTypeName);
     961           2 :                         return nullptr;
     962             :                     }
     963             :                 }
     964             :             }
     965             :         }
     966             :     }
     967             :     else
     968             :     {
     969           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     970             :                  "%s(): unexpected type of JSON "
     971             :                  "construct %s for '%s'. Expected array.",
     972             :                  pszFuncName,
     973             :                  GetJSONConstructName(json_object_get_type(poObjGeoms)),
     974             :                  json_object_to_json_string(poObjGeoms));
     975             :     }
     976             : 
     977          72 :     return poCollection;
     978             : }
     979             : 
     980             : /************************************************************************/
     981             : /*                   OGRGeoJSONReadGeometryCollection                   */
     982             : /************************************************************************/
     983             : 
     984             : std::unique_ptr<OGRGeometryCollection>
     985          11 : OGRGeoJSONReadGeometryCollection(json_object *poObj, bool bHasM,
     986             :                                  const OGRSpatialReference *poSRS)
     987             : {
     988             :     return OGRGeoJSONReadCollection<OGRGeometryCollection>(
     989          11 :         __func__, "GeometryCollection", poObj, bHasM, poSRS);
     990             : }
     991             : 
     992             : /************************************************************************/
     993             : /*                     OGRGeoJSONReadCompoundCurve                      */
     994             : /************************************************************************/
     995             : 
     996             : std::unique_ptr<OGRCompoundCurve>
     997          28 : OGRGeoJSONReadCompoundCurve(json_object *poObj, bool bHasM,
     998             :                             const OGRSpatialReference *poSRS)
     999             : {
    1000             :     return OGRGeoJSONReadCollection<OGRCompoundCurve>(__func__, "CompoundCurve",
    1001          28 :                                                       poObj, bHasM, poSRS);
    1002             : }
    1003             : 
    1004             : /************************************************************************/
    1005             : /*                      OGRGeoJSONReadCurvePolygon                      */
    1006             : /************************************************************************/
    1007             : 
    1008          20 : std::unique_ptr<OGRCurvePolygon> OGRGeoJSONReadCurvePolygon(json_object *poObj,
    1009             :                                                             bool bHasM)
    1010             : {
    1011             :     return OGRGeoJSONReadCollection<OGRCurvePolygon>(
    1012          20 :         __func__, "CurvePolygon", poObj, bHasM, /* poSRS = */ nullptr);
    1013             : }
    1014             : 
    1015             : /************************************************************************/
    1016             : /*                       OGRGeoJSONReadMultiCurve                       */
    1017             : /************************************************************************/
    1018             : 
    1019             : std::unique_ptr<OGRMultiCurve>
    1020           9 : OGRGeoJSONReadMultiCurve(json_object *poObj, bool bHasM,
    1021             :                          const OGRSpatialReference *poSRS)
    1022             : {
    1023             :     return OGRGeoJSONReadCollection<OGRMultiCurve>(__func__, "MultiCurve",
    1024           9 :                                                    poObj, bHasM, poSRS);
    1025             : }
    1026             : 
    1027             : /************************************************************************/
    1028             : /*                      OGRGeoJSONReadMultiSurface                      */
    1029             : /************************************************************************/
    1030             : 
    1031             : std::unique_ptr<OGRMultiSurface>
    1032           9 : OGRGeoJSONReadMultiSurface(json_object *poObj, bool bHasM,
    1033             :                            const OGRSpatialReference *poSRS)
    1034             : {
    1035             :     return OGRGeoJSONReadCollection<OGRMultiSurface>(__func__, "MultiSurface",
    1036           9 :                                                      poObj, bHasM, poSRS);
    1037             : }
    1038             : 
    1039             : /************************************************************************/
    1040             : /*                    OGRGeoJSONReadSpatialReference                    */
    1041             : /************************************************************************/
    1042             : 
    1043         520 : OGRSpatialReference *OGRGeoJSONReadSpatialReference(json_object *poObj)
    1044             : {
    1045             : 
    1046             :     /* -------------------------------------------------------------------- */
    1047             :     /*      Read spatial reference definition.                              */
    1048             :     /* -------------------------------------------------------------------- */
    1049         520 :     OGRSpatialReference *poSRS = nullptr;
    1050             : 
    1051         520 :     json_object *poObjSrs = OGRGeoJSONFindMemberByName(poObj, "crs");
    1052         520 :     if (nullptr != poObjSrs)
    1053             :     {
    1054             :         json_object *poObjSrsType =
    1055          75 :             OGRGeoJSONFindMemberByName(poObjSrs, "type");
    1056          75 :         if (poObjSrsType == nullptr)
    1057           1 :             return nullptr;
    1058             : 
    1059          74 :         const char *pszSrsType = json_object_get_string(poObjSrsType);
    1060             : 
    1061             :         // TODO: Add URL and URN types support.
    1062          74 :         if (STARTS_WITH_CI(pszSrsType, "NAME"))
    1063             :         {
    1064             :             json_object *poObjSrsProps =
    1065          54 :                 OGRGeoJSONFindMemberByName(poObjSrs, "properties");
    1066          54 :             if (poObjSrsProps == nullptr)
    1067           2 :                 return nullptr;
    1068             : 
    1069             :             json_object *poNameURL =
    1070          52 :                 OGRGeoJSONFindMemberByName(poObjSrsProps, "name");
    1071          52 :             if (poNameURL == nullptr)
    1072           2 :                 return nullptr;
    1073             : 
    1074          50 :             const char *pszName = json_object_get_string(poNameURL);
    1075             : 
    1076          50 :             if (EQUAL(pszName, "urn:ogc:def:crs:OGC:1.3:CRS84"))
    1077          12 :                 pszName = "EPSG:4326";
    1078             : 
    1079          50 :             poSRS = new OGRSpatialReference();
    1080          50 :             poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1081          50 :             if (OGRERR_NONE !=
    1082          50 :                 poSRS->SetFromUserInput(
    1083             :                     pszName,
    1084             :                     OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()))
    1085             :             {
    1086           2 :                 delete poSRS;
    1087           2 :                 poSRS = nullptr;
    1088             :             }
    1089             :         }
    1090             : 
    1091          20 :         else if (STARTS_WITH_CI(pszSrsType, "EPSG"))
    1092             :         {
    1093             :             json_object *poObjSrsProps =
    1094           7 :                 OGRGeoJSONFindMemberByName(poObjSrs, "properties");
    1095           7 :             if (poObjSrsProps == nullptr)
    1096           2 :                 return nullptr;
    1097             : 
    1098             :             json_object *poObjCode =
    1099           5 :                 OGRGeoJSONFindMemberByName(poObjSrsProps, "code");
    1100           5 :             if (poObjCode == nullptr)
    1101           2 :                 return nullptr;
    1102             : 
    1103           3 :             int nEPSG = json_object_get_int(poObjCode);
    1104             : 
    1105           3 :             poSRS = new OGRSpatialReference();
    1106           3 :             poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1107           3 :             if (OGRERR_NONE != poSRS->importFromEPSG(nEPSG))
    1108             :             {
    1109           2 :                 delete poSRS;
    1110           2 :                 poSRS = nullptr;
    1111             :             }
    1112             :         }
    1113             : 
    1114          13 :         else if (STARTS_WITH_CI(pszSrsType, "URL") ||
    1115          13 :                  STARTS_WITH_CI(pszSrsType, "LINK"))
    1116             :         {
    1117             :             json_object *poObjSrsProps =
    1118           6 :                 OGRGeoJSONFindMemberByName(poObjSrs, "properties");
    1119           6 :             if (poObjSrsProps == nullptr)
    1120           2 :                 return nullptr;
    1121             : 
    1122             :             json_object *poObjURL =
    1123           4 :                 OGRGeoJSONFindMemberByName(poObjSrsProps, "url");
    1124             : 
    1125           4 :             if (nullptr == poObjURL)
    1126             :             {
    1127           4 :                 poObjURL = OGRGeoJSONFindMemberByName(poObjSrsProps, "href");
    1128             :             }
    1129           4 :             if (poObjURL == nullptr)
    1130           2 :                 return nullptr;
    1131             : 
    1132           2 :             const char *pszURL = json_object_get_string(poObjURL);
    1133             : 
    1134           2 :             poSRS = new OGRSpatialReference();
    1135           2 :             poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1136           2 :             if (OGRERR_NONE != poSRS->importFromUrl(pszURL))
    1137             :             {
    1138           2 :                 delete poSRS;
    1139           2 :                 poSRS = nullptr;
    1140           2 :             }
    1141             :         }
    1142             : 
    1143           7 :         else if (EQUAL(pszSrsType, "OGC"))
    1144             :         {
    1145             :             json_object *poObjSrsProps =
    1146           7 :                 OGRGeoJSONFindMemberByName(poObjSrs, "properties");
    1147           7 :             if (poObjSrsProps == nullptr)
    1148           2 :                 return nullptr;
    1149             : 
    1150             :             json_object *poObjURN =
    1151           5 :                 OGRGeoJSONFindMemberByName(poObjSrsProps, "urn");
    1152           5 :             if (poObjURN == nullptr)
    1153           2 :                 return nullptr;
    1154             : 
    1155           3 :             poSRS = new OGRSpatialReference();
    1156           3 :             poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1157           3 :             if (OGRERR_NONE !=
    1158           3 :                 poSRS->importFromURN(json_object_get_string(poObjURN)))
    1159             :             {
    1160           2 :                 delete poSRS;
    1161           2 :                 poSRS = nullptr;
    1162             :             }
    1163             :         }
    1164             :     }
    1165             : 
    1166             :     // Strip AXIS, since geojson has (easting, northing) / (longitude, latitude)
    1167             :     // order.  According to http://www.geojson.org/geojson-spec.html#id2 :
    1168             :     // "Point coordinates are in x, y order (easting, northing for projected
    1169             :     // coordinates, longitude, latitude for geographic coordinates)".
    1170         503 :     if (poSRS != nullptr)
    1171             :     {
    1172          50 :         OGR_SRSNode *poGEOGCS = poSRS->GetAttrNode("GEOGCS");
    1173          50 :         if (poGEOGCS != nullptr)
    1174          50 :             poGEOGCS->StripNodes("AXIS");
    1175             :     }
    1176             : 
    1177         503 :     return poSRS;
    1178             : }
    1179             : 
    1180             : /************************************************************************/
    1181             : /*                     OGR_G_CreateGeometryFromJson                     */
    1182             : /************************************************************************/
    1183             : 
    1184             : /** Create a OGR geometry from a GeoJSON geometry object */
    1185          46 : OGRGeometryH OGR_G_CreateGeometryFromJson(const char *pszJson)
    1186             : {
    1187          46 :     if (nullptr == pszJson)
    1188             :     {
    1189             :         // Translation failed.
    1190           0 :         return nullptr;
    1191             :     }
    1192             : 
    1193          46 :     json_object *poObj = nullptr;
    1194          46 :     if (!OGRJSonParse(pszJson, &poObj))
    1195           0 :         return nullptr;
    1196             : 
    1197             :     OGRGeometry *poGeometry =
    1198          92 :         OGRGeoJSONReadGeometry(poObj, /* bHasM = */ false,
    1199             :                                /* OGRSpatialReference* = */ nullptr)
    1200          46 :             .release();
    1201             : 
    1202             :     // Release JSON tree.
    1203          46 :     json_object_put(poObj);
    1204             : 
    1205          46 :     return OGRGeometry::ToHandle(poGeometry);
    1206             : }
    1207             : 
    1208             : /*! @endcond */

Generated by: LCOV version 1.14