LCOV - code coverage report
Current view: top level - ogr - gml2ogrgeometry.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1544 1767 87.4 %
Date: 2025-11-27 10:26:27 Functions: 20 20 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GML Reader
       4             :  * Purpose:  Code to translate between GML and OGR geometry forms.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2002, Frank Warmerdam
       9             :  * Copyright (c) 2009-2014, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  *****************************************************************************
      13             :  *
      14             :  * Independent Security Audit 2003/04/17 Andrey Kiselev:
      15             :  *   Completed audit of this module. All functions may be used without buffer
      16             :  *   overflows and stack corruptions with any kind of input data.
      17             :  *
      18             :  * Security Audit 2003/03/28 warmerda:
      19             :  *   Completed security audit.  I believe that this module may be safely used
      20             :  *   to parse, arbitrary GML potentially provided by a hostile source without
      21             :  *   compromising the system.
      22             :  *
      23             :  */
      24             : 
      25             : #include "cpl_port.h"
      26             : #include "ogr_api.h"
      27             : 
      28             : #include <algorithm>
      29             : #include <cassert>
      30             : #include <cctype>
      31             : #include <cmath>
      32             : #include <cstdlib>
      33             : #include <cstring>
      34             : 
      35             : #include "cpl_conv.h"
      36             : #include "cpl_error.h"
      37             : #include "cpl_mem_cache.h"
      38             : #include "cpl_minixml.h"
      39             : #include "cpl_string.h"
      40             : #include "ogr_core.h"
      41             : #include "ogr_geometry.h"
      42             : #include "ogr_p.h"
      43             : #include "ogr_spatialref.h"
      44             : #include "ogr_srs_api.h"
      45             : #include "ogr_geo_utils.h"
      46             : 
      47             : constexpr double kdfD2R = M_PI / 180.0;
      48             : constexpr double kdf2PI = 2.0 * M_PI;
      49             : 
      50             : /************************************************************************/
      51             : /*                        GMLGetCoordTokenPos()                         */
      52             : /************************************************************************/
      53             : 
      54      813974 : static const char *GMLGetCoordTokenPos(const char *pszStr,
      55             :                                        const char **ppszNextToken)
      56             : {
      57             :     char ch;
      58             :     while (true)
      59             :     {
      60             :         // cppcheck-suppress nullPointerRedundantCheck
      61      813974 :         ch = *pszStr;
      62      813974 :         if (ch == '\0')
      63             :         {
      64        2180 :             *ppszNextToken = pszStr;
      65        2180 :             return nullptr;
      66             :         }
      67      811794 :         else if (!(ch == '\n' || ch == '\r' || ch == '\t' || ch == ' ' ||
      68             :                    ch == ','))
      69      407177 :             break;
      70      404617 :         pszStr++;
      71             :     }
      72             : 
      73      407177 :     const char *pszToken = pszStr;
      74     4186180 :     while ((ch = *pszStr) != '\0')
      75             :     {
      76     4183130 :         if (ch == '\n' || ch == '\r' || ch == '\t' || ch == ' ' || ch == ',')
      77             :         {
      78      404124 :             *ppszNextToken = pszStr;
      79      404124 :             return pszToken;
      80             :         }
      81     3779000 :         pszStr++;
      82             :     }
      83        3053 :     *ppszNextToken = pszStr;
      84        3053 :     return pszToken;
      85             : }
      86             : 
      87             : /************************************************************************/
      88             : /*                           BareGMLElement()                           */
      89             : /*                                                                      */
      90             : /*      Returns the passed string with any namespace prefix             */
      91             : /*      stripped off.                                                   */
      92             : /************************************************************************/
      93             : 
      94       31827 : static const char *BareGMLElement(const char *pszInput)
      95             : 
      96             : {
      97       31827 :     const char *pszReturn = strchr(pszInput, ':');
      98       31827 :     if (pszReturn == nullptr)
      99       23518 :         pszReturn = pszInput;
     100             :     else
     101        8309 :         pszReturn++;
     102             : 
     103       31827 :     return pszReturn;
     104             : }
     105             : 
     106             : /************************************************************************/
     107             : /*                          FindBareXMLChild()                          */
     108             : /*                                                                      */
     109             : /*      Find a child node with the indicated "bare" name, that is       */
     110             : /*      after any namespace qualifiers have been stripped off.          */
     111             : /************************************************************************/
     112             : 
     113       12597 : static const CPLXMLNode *FindBareXMLChild(const CPLXMLNode *psParent,
     114             :                                           const char *pszBareName)
     115             : 
     116             : {
     117       12597 :     const CPLXMLNode *psCandidate = psParent->psChild;
     118             : 
     119       27132 :     while (psCandidate != nullptr)
     120             :     {
     121       38119 :         if (psCandidate->eType == CXT_Element &&
     122       15392 :             EQUAL(BareGMLElement(psCandidate->pszValue), pszBareName))
     123        8192 :             return psCandidate;
     124             : 
     125       14535 :         psCandidate = psCandidate->psNext;
     126             :     }
     127             : 
     128        4405 :     return nullptr;
     129             : }
     130             : 
     131             : /************************************************************************/
     132             : /*                           GetElementText()                           */
     133             : /************************************************************************/
     134             : 
     135        3668 : static const char *GetElementText(const CPLXMLNode *psElement)
     136             : 
     137             : {
     138        3668 :     if (psElement == nullptr)
     139           0 :         return nullptr;
     140             : 
     141        3668 :     const CPLXMLNode *psChild = psElement->psChild;
     142             : 
     143        5619 :     while (psChild != nullptr)
     144             :     {
     145        5611 :         if (psChild->eType == CXT_Text)
     146        3660 :             return psChild->pszValue;
     147             : 
     148        1951 :         psChild = psChild->psNext;
     149             :     }
     150             : 
     151           8 :     return nullptr;
     152             : }
     153             : 
     154             : /************************************************************************/
     155             : /*                           GetChildElement()                          */
     156             : /************************************************************************/
     157             : 
     158        2021 : static const CPLXMLNode *GetChildElement(const CPLXMLNode *psElement)
     159             : 
     160             : {
     161        2021 :     if (psElement == nullptr)
     162          13 :         return nullptr;
     163             : 
     164        2008 :     const CPLXMLNode *psChild = psElement->psChild;
     165             : 
     166        2221 :     while (psChild != nullptr)
     167             :     {
     168        2203 :         if (psChild->eType == CXT_Element)
     169        1990 :             return psChild;
     170             : 
     171         213 :         psChild = psChild->psNext;
     172             :     }
     173             : 
     174          18 :     return nullptr;
     175             : }
     176             : 
     177             : /************************************************************************/
     178             : /*                    GetElementOrientation()                           */
     179             : /*     Returns true for positive orientation.                           */
     180             : /************************************************************************/
     181             : 
     182        1266 : static bool GetElementOrientation(const CPLXMLNode *psElement)
     183             : {
     184        1266 :     if (psElement == nullptr)
     185           0 :         return true;
     186             : 
     187        1266 :     const CPLXMLNode *psChild = psElement->psChild;
     188             : 
     189        2124 :     while (psChild != nullptr)
     190             :     {
     191        1270 :         if (psChild->eType == CXT_Attribute &&
     192         416 :             EQUAL(psChild->pszValue, "orientation"))
     193         412 :             return EQUAL(psChild->psChild->pszValue, "+");
     194             : 
     195         858 :         psChild = psChild->psNext;
     196             :     }
     197             : 
     198         854 :     return true;
     199             : }
     200             : 
     201             : /************************************************************************/
     202             : /*                              AddPoint()                              */
     203             : /*                                                                      */
     204             : /*      Add a point to the passed geometry.                             */
     205             : /************************************************************************/
     206             : 
     207      193642 : static bool AddPoint(OGRGeometry *poGeometry, double dfX, double dfY,
     208             :                      double dfZ, int nDimension)
     209             : 
     210             : {
     211      193642 :     const OGRwkbGeometryType eType = wkbFlatten(poGeometry->getGeometryType());
     212      193642 :     if (eType == wkbPoint)
     213             :     {
     214        1066 :         OGRPoint *poPoint = poGeometry->toPoint();
     215             : 
     216        1066 :         if (!poPoint->IsEmpty())
     217             :         {
     218           2 :             CPLError(CE_Failure, CPLE_AppDefined,
     219             :                      "More than one coordinate for <Point> element.");
     220           2 :             return false;
     221             :         }
     222             : 
     223        1064 :         poPoint->setX(dfX);
     224        1064 :         poPoint->setY(dfY);
     225        1064 :         if (nDimension == 3)
     226          25 :             poPoint->setZ(dfZ);
     227             : 
     228        1064 :         return true;
     229             :     }
     230      192576 :     else if (eType == wkbLineString || eType == wkbCircularString)
     231             :     {
     232      192576 :         OGRSimpleCurve *poCurve = poGeometry->toSimpleCurve();
     233      192576 :         if (nDimension == 3)
     234       20707 :             poCurve->addPoint(dfX, dfY, dfZ);
     235             :         else
     236      171869 :             poCurve->addPoint(dfX, dfY);
     237             : 
     238      192576 :         return true;
     239             :     }
     240             : 
     241           0 :     CPLAssert(false);
     242             :     return false;
     243             : }
     244             : 
     245             : /************************************************************************/
     246             : /*                        ParseGMLCoordinates()                         */
     247             : /************************************************************************/
     248             : 
     249        3465 : static bool ParseGMLCoordinates(const CPLXMLNode *psGeomNode,
     250             :                                 OGRGeometry *poGeometry, int nSRSDimension)
     251             : 
     252             : {
     253             :     const CPLXMLNode *psCoordinates =
     254        3465 :         FindBareXMLChild(psGeomNode, "coordinates");
     255             : 
     256             :     /* -------------------------------------------------------------------- */
     257             :     /*      Handle <coordinates> case.                                      */
     258             :     /*      Note that we don't do a strict validation, so we accept and     */
     259             :     /*      sometimes generate output whereas we should just reject it.     */
     260             :     /* -------------------------------------------------------------------- */
     261        3465 :     if (psCoordinates != nullptr)
     262             :     {
     263         528 :         const char *pszCoordString = GetElementText(psCoordinates);
     264             : 
     265             :         const char *pszDecimal =
     266         528 :             CPLGetXMLValue(psCoordinates, "decimal", nullptr);
     267         528 :         char chDecimal = '.';
     268         528 :         if (pszDecimal != nullptr)
     269             :         {
     270          35 :             if (strlen(pszDecimal) != 1 ||
     271          33 :                 (pszDecimal[0] >= '0' && pszDecimal[0] <= '9'))
     272             :             {
     273           3 :                 CPLError(CE_Failure, CPLE_AppDefined,
     274             :                          "Wrong value for decimal attribute");
     275           3 :                 return false;
     276             :             }
     277          32 :             chDecimal = pszDecimal[0];
     278             :         }
     279             : 
     280         525 :         const char *pszCS = CPLGetXMLValue(psCoordinates, "cs", nullptr);
     281         525 :         char chCS = ',';
     282         525 :         if (pszCS != nullptr)
     283             :         {
     284          38 :             if (strlen(pszCS) != 1 || (pszCS[0] >= '0' && pszCS[0] <= '9'))
     285             :             {
     286           3 :                 CPLError(CE_Failure, CPLE_AppDefined,
     287             :                          "Wrong value for cs attribute");
     288           3 :                 return false;
     289             :             }
     290          35 :             chCS = pszCS[0];
     291             :         }
     292         522 :         const char *pszTS = CPLGetXMLValue(psCoordinates, "ts", nullptr);
     293         522 :         char chTS = ' ';
     294         522 :         if (pszTS != nullptr)
     295             :         {
     296          36 :             if (strlen(pszTS) != 1 || (pszTS[0] >= '0' && pszTS[0] <= '9'))
     297             :             {
     298           3 :                 CPLError(CE_Failure, CPLE_AppDefined,
     299             :                          "Wrong value for ts attribute");
     300           3 :                 return false;
     301             :             }
     302          33 :             chTS = pszTS[0];
     303             :         }
     304             : 
     305         519 :         if (pszCoordString == nullptr)
     306             :         {
     307           1 :             poGeometry->empty();
     308           1 :             return true;
     309             :         }
     310             : 
     311             :         // Skip leading whitespace. See
     312             :         // https://github.com/OSGeo/gdal/issues/5494
     313         535 :         while (*pszCoordString != '\0' &&
     314         535 :                isspace(static_cast<unsigned char>(*pszCoordString)))
     315             :         {
     316          17 :             pszCoordString++;
     317             :         }
     318             : 
     319         518 :         int iCoord = 0;
     320             :         const OGRwkbGeometryType eType =
     321         518 :             wkbFlatten(poGeometry->getGeometryType());
     322             :         OGRSimpleCurve *poCurve =
     323         415 :             (eType == wkbLineString || eType == wkbCircularString)
     324         933 :                 ? poGeometry->toSimpleCurve()
     325         518 :                 : nullptr;
     326        1137 :         for (int iter = (eType == wkbPoint ? 1 : 0); iter < 2; iter++)
     327             :         {
     328         621 :             const char *pszStr = pszCoordString;
     329         621 :             double dfX = 0;
     330         621 :             double dfY = 0;
     331         621 :             iCoord = 0;
     332        2359 :             while (*pszStr != '\0')
     333             :             {
     334        1740 :                 int nDimension = 2;
     335             :                 // parse out 2 or 3 tuple.
     336        1740 :                 if (iter == 1)
     337             :                 {
     338        1078 :                     if (chDecimal == '.')
     339        1076 :                         dfX = OGRFastAtof(pszStr);
     340             :                     else
     341           2 :                         dfX = CPLAtofDelim(pszStr, chDecimal);
     342             :                 }
     343       13727 :                 while (*pszStr != '\0' && *pszStr != chCS &&
     344       11992 :                        !isspace(static_cast<unsigned char>(*pszStr)))
     345       11987 :                     pszStr++;
     346             : 
     347        1740 :                 if (*pszStr == '\0')
     348             :                 {
     349           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
     350             :                              "Corrupt <coordinates> value.");
     351           1 :                     return false;
     352             :                 }
     353        1739 :                 else if (chCS == ',' && pszCS == nullptr &&
     354        1623 :                          isspace(static_cast<unsigned char>(*pszStr)))
     355             :                 {
     356             :                     // In theory, the coordinates inside a coordinate tuple
     357             :                     // should be separated by a comma. However it has been found
     358             :                     // in the wild that the coordinates are in rare cases
     359             :                     // separated by a space, and the tuples by a comma. See:
     360             :                     // https://52north.org/twiki/bin/view/Processing/WPS-IDWExtension-ObservationCollectionExample
     361             :                     // or
     362             :                     // http://agisdemo.faa.gov/aixmServices/getAllFeaturesByLocatorId?locatorId=DFW
     363           5 :                     chCS = ' ';
     364           5 :                     chTS = ',';
     365             :                 }
     366             : 
     367        1739 :                 pszStr++;
     368             : 
     369        1739 :                 if (iter == 1)
     370             :                 {
     371        1077 :                     if (chDecimal == '.')
     372        1075 :                         dfY = OGRFastAtof(pszStr);
     373             :                     else
     374           2 :                         dfY = CPLAtofDelim(pszStr, chDecimal);
     375             :                 }
     376       13615 :                 while (*pszStr != '\0' && *pszStr != chCS && *pszStr != chTS &&
     377       11878 :                        !isspace(static_cast<unsigned char>(*pszStr)))
     378       11876 :                     pszStr++;
     379             : 
     380        1739 :                 double dfZ = 0.0;
     381        1739 :                 if (*pszStr == chCS)
     382             :                 {
     383          62 :                     pszStr++;
     384          62 :                     if (iter == 1)
     385             :                     {
     386          34 :                         if (chDecimal == '.')
     387          33 :                             dfZ = OGRFastAtof(pszStr);
     388             :                         else
     389           1 :                             dfZ = CPLAtofDelim(pszStr, chDecimal);
     390             :                     }
     391          62 :                     nDimension = 3;
     392         286 :                     while (*pszStr != '\0' && *pszStr != chCS &&
     393         588 :                            *pszStr != chTS &&
     394         250 :                            !isspace(static_cast<unsigned char>(*pszStr)))
     395         240 :                         pszStr++;
     396             :                 }
     397             : 
     398        1739 :                 if (*pszStr == chTS)
     399             :                 {
     400        1121 :                     pszStr++;
     401             :                 }
     402             : 
     403        1935 :                 while (isspace(static_cast<unsigned char>(*pszStr)))
     404         196 :                     pszStr++;
     405             : 
     406        1739 :                 if (iter == 1)
     407             :                 {
     408        1077 :                     if (poCurve)
     409             :                     {
     410         662 :                         if (nDimension == 3)
     411          28 :                             poCurve->setPoint(iCoord, dfX, dfY, dfZ);
     412             :                         else
     413         634 :                             poCurve->setPoint(iCoord, dfX, dfY);
     414             :                     }
     415         415 :                     else if (!AddPoint(poGeometry, dfX, dfY, dfZ, nDimension))
     416           1 :                         return false;
     417             :                 }
     418             : 
     419        1738 :                 iCoord++;
     420             :             }
     421             : 
     422         619 :             if (poCurve && iter == 0)
     423             :             {
     424         103 :                 poCurve->setNumPoints(iCoord);
     425             :             }
     426             :         }
     427             : 
     428         516 :         return iCoord > 0;
     429             :     }
     430             : 
     431             :     /* -------------------------------------------------------------------- */
     432             :     /*      Is this a "pos"?  GML 3 construct.                              */
     433             :     /*      Parse if it exist a series of pos elements (this would allow    */
     434             :     /*      the correct parsing of gml3.1.1 geometries such as linestring    */
     435             :     /*      defined with pos elements.                                      */
     436             :     /* -------------------------------------------------------------------- */
     437        2937 :     bool bHasFoundPosElement = false;
     438        7687 :     for (const CPLXMLNode *psPos = psGeomNode->psChild; psPos != nullptr;
     439        4750 :          psPos = psPos->psNext)
     440             :     {
     441        4753 :         if (psPos->eType != CXT_Element)
     442        3856 :             continue;
     443             : 
     444        3154 :         const char *pszSubElement = BareGMLElement(psPos->pszValue);
     445             : 
     446        3154 :         if (EQUAL(pszSubElement, "pointProperty"))
     447             :         {
     448           2 :             for (const CPLXMLNode *psPointPropertyIter = psPos->psChild;
     449           4 :                  psPointPropertyIter != nullptr;
     450           2 :                  psPointPropertyIter = psPointPropertyIter->psNext)
     451             :             {
     452           2 :                 if (psPointPropertyIter->eType != CXT_Element)
     453           0 :                     continue;
     454             : 
     455             :                 const char *pszBareElement =
     456           2 :                     BareGMLElement(psPointPropertyIter->pszValue);
     457           2 :                 if (EQUAL(pszBareElement, "Point") ||
     458           0 :                     EQUAL(pszBareElement, "ElevatedPoint"))
     459             :                 {
     460           2 :                     OGRPoint oPoint;
     461           2 :                     if (ParseGMLCoordinates(psPointPropertyIter, &oPoint,
     462             :                                             nSRSDimension))
     463             :                     {
     464           2 :                         const bool bSuccess = AddPoint(
     465             :                             poGeometry, oPoint.getX(), oPoint.getY(),
     466             :                             oPoint.getZ(), oPoint.getCoordinateDimension());
     467           2 :                         if (bSuccess)
     468           2 :                             bHasFoundPosElement = true;
     469             :                         else
     470           0 :                             return false;
     471             :                     }
     472             :                 }
     473             :             }
     474             : 
     475           2 :             if (psPos->psChild && psPos->psChild->eType == CXT_Attribute &&
     476           0 :                 psPos->psChild->psNext == nullptr &&
     477           0 :                 strcmp(psPos->psChild->pszValue, "xlink:href") == 0)
     478             :             {
     479           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     480             :                          "Cannot resolve xlink:href='%s'. "
     481             :                          "Try setting GML_SKIP_RESOLVE_ELEMS=NONE",
     482           0 :                          psPos->psChild->psChild->pszValue);
     483             :             }
     484             : 
     485           2 :             continue;
     486             :         }
     487             : 
     488        3152 :         if (!EQUAL(pszSubElement, "pos"))
     489        2255 :             continue;
     490             : 
     491         897 :         const char *pszPos = GetElementText(psPos);
     492         897 :         if (pszPos == nullptr)
     493             :         {
     494           1 :             poGeometry->empty();
     495           1 :             return true;
     496             :         }
     497             : 
     498         896 :         const char *pszCur = pszPos;
     499         896 :         const char *pszX = GMLGetCoordTokenPos(pszCur, &pszCur);
     500         896 :         const char *pszY = (pszCur[0] != '\0')
     501         896 :                                ? GMLGetCoordTokenPos(pszCur, &pszCur)
     502         896 :                                : nullptr;
     503         896 :         const char *pszZ = (pszCur[0] != '\0')
     504         896 :                                ? GMLGetCoordTokenPos(pszCur, &pszCur)
     505         896 :                                : nullptr;
     506             : 
     507         896 :         if (pszY == nullptr)
     508             :         {
     509           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     510             :                      "Did not get 2+ values in <gml:pos>%s</gml:pos> tuple.",
     511             :                      pszPos);
     512           1 :             return false;
     513             :         }
     514             : 
     515         895 :         const double dfX = OGRFastAtof(pszX);
     516         895 :         const double dfY = OGRFastAtof(pszY);
     517         895 :         const double dfZ = (pszZ != nullptr) ? OGRFastAtof(pszZ) : 0.0;
     518             :         const bool bSuccess =
     519         895 :             AddPoint(poGeometry, dfX, dfY, dfZ, (pszZ != nullptr) ? 3 : 2);
     520             : 
     521         895 :         if (bSuccess)
     522         894 :             bHasFoundPosElement = true;
     523             :         else
     524           1 :             return false;
     525             :     }
     526             : 
     527        2934 :     if (bHasFoundPosElement)
     528         721 :         return true;
     529             : 
     530             :     /* -------------------------------------------------------------------- */
     531             :     /*      Is this a "posList"?  GML 3 construct (SF profile).             */
     532             :     /* -------------------------------------------------------------------- */
     533        2213 :     const CPLXMLNode *psPosList = FindBareXMLChild(psGeomNode, "posList");
     534             : 
     535        2213 :     if (psPosList != nullptr)
     536             :     {
     537        2187 :         int nDimension = 2;
     538             : 
     539             :         // Try to detect the presence of an srsDimension attribute
     540             :         // This attribute is only available for gml3.1.1 but not
     541             :         // available for gml3.1 SF.
     542             :         const char *pszSRSDimension =
     543        2187 :             CPLGetXMLValue(psPosList, "srsDimension", nullptr);
     544             :         // If not found at the posList level, try on the enclosing element.
     545        2187 :         if (pszSRSDimension == nullptr)
     546             :             pszSRSDimension =
     547         810 :                 CPLGetXMLValue(psGeomNode, "srsDimension", nullptr);
     548        2187 :         if (pszSRSDimension != nullptr)
     549        1378 :             nDimension = atoi(pszSRSDimension);
     550         809 :         else if (nSRSDimension != 0)
     551             :             // Or use one coming from a still higher level element (#5606).
     552         429 :             nDimension = nSRSDimension;
     553             : 
     554        2187 :         if (nDimension != 2 && nDimension != 3)
     555             :         {
     556           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     557             :                      "srsDimension = %d not supported", nDimension);
     558           1 :             return false;
     559             :         }
     560             : 
     561        2186 :         const char *pszPosList = GetElementText(psPosList);
     562        2186 :         if (pszPosList == nullptr)
     563             :         {
     564           3 :             poGeometry->empty();
     565           3 :             return true;
     566             :         }
     567             : 
     568        2183 :         bool bSuccess = false;
     569        2183 :         const char *pszCur = pszPosList;
     570             :         while (true)
     571             :         {
     572      194511 :             const char *pszX = GMLGetCoordTokenPos(pszCur, &pszCur);
     573      194511 :             if (pszX == nullptr && bSuccess)
     574        2180 :                 break;
     575      192331 :             const char *pszY = (pszCur[0] != '\0')
     576      192331 :                                    ? GMLGetCoordTokenPos(pszCur, &pszCur)
     577      192331 :                                    : nullptr;
     578       20707 :             const char *pszZ = (nDimension == 3 && pszCur[0] != '\0')
     579      213038 :                                    ? GMLGetCoordTokenPos(pszCur, &pszCur)
     580      192331 :                                    : nullptr;
     581             : 
     582      192331 :             if (pszY == nullptr || (nDimension == 3 && pszZ == nullptr))
     583             :             {
     584           3 :                 CPLError(CE_Failure, CPLE_AppDefined,
     585             :                          "Did not get at least %d values or invalid number of "
     586             :                          "set of coordinates <gml:posList>%s</gml:posList>",
     587             :                          nDimension, pszPosList);
     588           3 :                 return false;
     589             :             }
     590             : 
     591      192328 :             double dfX = OGRFastAtof(pszX);
     592      192328 :             double dfY = OGRFastAtof(pszY);
     593      192328 :             double dfZ = (pszZ != nullptr) ? OGRFastAtof(pszZ) : 0.0;
     594      192328 :             bSuccess = AddPoint(poGeometry, dfX, dfY, dfZ, nDimension);
     595             : 
     596      192328 :             if (!bSuccess || pszCur == nullptr)
     597             :                 break;
     598      192328 :         }
     599             : 
     600        2180 :         return bSuccess;
     601             :     }
     602             : 
     603             :     /* -------------------------------------------------------------------- */
     604             :     /*      Handle form with a list of <coord> items each with an <X>,      */
     605             :     /*      and <Y> element.                                                */
     606             :     /* -------------------------------------------------------------------- */
     607          26 :     int iCoord = 0;
     608          26 :     for (const CPLXMLNode *psCoordNode = psGeomNode->psChild;
     609          43 :          psCoordNode != nullptr; psCoordNode = psCoordNode->psNext)
     610             :     {
     611          38 :         if (psCoordNode->eType != CXT_Element ||
     612          18 :             !EQUAL(BareGMLElement(psCoordNode->pszValue), "coord"))
     613          15 :             continue;
     614             : 
     615           5 :         const CPLXMLNode *psXNode = FindBareXMLChild(psCoordNode, "X");
     616           5 :         const CPLXMLNode *psYNode = FindBareXMLChild(psCoordNode, "Y");
     617           5 :         const CPLXMLNode *psZNode = FindBareXMLChild(psCoordNode, "Z");
     618             : 
     619           7 :         if (psXNode == nullptr || psYNode == nullptr ||
     620           5 :             GetElementText(psXNode) == nullptr ||
     621          11 :             GetElementText(psYNode) == nullptr ||
     622           0 :             (psZNode != nullptr && GetElementText(psZNode) == nullptr))
     623             :         {
     624           3 :             CPLError(CE_Failure, CPLE_AppDefined,
     625             :                      "Corrupt <coord> element, missing <X> or <Y> element?");
     626           3 :             return false;
     627             :         }
     628             : 
     629           2 :         double dfX = OGRFastAtof(GetElementText(psXNode));
     630           2 :         double dfY = OGRFastAtof(GetElementText(psYNode));
     631             : 
     632           2 :         int nDimension = 2;
     633           2 :         double dfZ = 0.0;
     634           2 :         if (psZNode != nullptr && GetElementText(psZNode) != nullptr)
     635             :         {
     636           0 :             dfZ = OGRFastAtof(GetElementText(psZNode));
     637           0 :             nDimension = 3;
     638             :         }
     639             : 
     640           2 :         if (!AddPoint(poGeometry, dfX, dfY, dfZ, nDimension))
     641           0 :             return false;
     642             : 
     643           2 :         iCoord++;
     644             :     }
     645             : 
     646          23 :     return iCoord > 0;
     647             : }
     648             : 
     649             : #ifdef HAVE_GEOS
     650             : /************************************************************************/
     651             : /*                         GML2FaceExtRing()                            */
     652             : /*                                                                      */
     653             : /*      Identifies the "good" Polygon within the collection returned    */
     654             : /*      by GEOSPolygonize()                                             */
     655             : /*      short rationale: GEOSPolygonize() will possibly return a        */
     656             : /*      collection of many Polygons; only one is the "good" one,        */
     657             : /*      (including both exterior- and interior-rings)                   */
     658             : /*      any other simply represents a single "hole", and should be      */
     659             : /*      consequently ignored at all.                                    */
     660             : /************************************************************************/
     661             : 
     662         114 : static std::unique_ptr<OGRPolygon> GML2FaceExtRing(const OGRGeometry *poGeom)
     663             : {
     664             :     const OGRGeometryCollection *poColl =
     665         114 :         dynamic_cast<const OGRGeometryCollection *>(poGeom);
     666         114 :     if (poColl == nullptr)
     667             :     {
     668           0 :         CPLError(CE_Fatal, CPLE_AppDefined,
     669             :                  "dynamic_cast failed.  Expected OGRGeometryCollection.");
     670           0 :         return nullptr;
     671             :     }
     672             : 
     673         114 :     const OGRPolygon *poPolygonExterior = nullptr;
     674         114 :     const OGRPolygon *poPolygonInterior = nullptr;
     675         114 :     int iExterior = 0;
     676         114 :     int iInterior = 0;
     677             : 
     678         256 :     for (const auto *poChild : *poColl)
     679             :     {
     680             :         // A collection of Polygons is expected to be found.
     681         142 :         if (wkbFlatten(poChild->getGeometryType()) == wkbPolygon)
     682             :         {
     683         142 :             const OGRPolygon *poPoly = poChild->toPolygon();
     684         142 :             if (poPoly->getNumInteriorRings() > 0)
     685             :             {
     686          20 :                 poPolygonExterior = poPoly;
     687          20 :                 iExterior++;
     688             :             }
     689             :             else
     690             :             {
     691         122 :                 poPolygonInterior = poPoly;
     692         122 :                 iInterior++;
     693             :             }
     694             :         }
     695             :         else
     696             :         {
     697           0 :             return nullptr;
     698             :         }
     699             :     }
     700             : 
     701         114 :     if (poPolygonInterior && iExterior == 0 && iInterior == 1)
     702             :     {
     703             :         // There is a single Polygon within the collection.
     704          94 :         return std::unique_ptr<OGRPolygon>(poPolygonInterior->clone());
     705             :     }
     706          40 :     else if (poPolygonExterior && iExterior == 1 &&
     707          20 :              iInterior == poColl->getNumGeometries() - 1)
     708             :     {
     709             :         // Return the unique Polygon containing holes.
     710          20 :         return std::unique_ptr<OGRPolygon>(poPolygonExterior->clone());
     711             :     }
     712             : 
     713           0 :     return nullptr;
     714             : }
     715             : #endif
     716             : 
     717             : /************************************************************************/
     718             : /*                   GML2OGRGeometry_AddToCompositeCurve()              */
     719             : /************************************************************************/
     720             : 
     721             : static bool
     722          53 : GML2OGRGeometry_AddToCompositeCurve(OGRCompoundCurve *poCC,
     723             :                                     std::unique_ptr<OGRGeometry> poGeom,
     724             :                                     bool &bChildrenAreAllLineString)
     725             : {
     726          53 :     if (poGeom == nullptr || !OGR_GT_IsCurve(poGeom->getGeometryType()))
     727             :     {
     728           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     729             :                  "CompositeCurve: Got %s geometry as Member instead of a "
     730             :                  "curve.",
     731           0 :                  poGeom ? poGeom->getGeometryName() : "NULL");
     732           0 :         return false;
     733             :     }
     734             : 
     735             :     // Crazy but allowed by GML: composite in composite.
     736          53 :     if (wkbFlatten(poGeom->getGeometryType()) == wkbCompoundCurve)
     737             :     {
     738             :         auto poCCChild = std::unique_ptr<OGRCompoundCurve>(
     739           2 :             poGeom.release()->toCompoundCurve());
     740           5 :         while (poCCChild->getNumCurves() != 0)
     741             :         {
     742           4 :             auto poCurve = std::unique_ptr<OGRCurve>(poCCChild->stealCurve(0));
     743           4 :             if (wkbFlatten(poCurve->getGeometryType()) != wkbLineString)
     744           2 :                 bChildrenAreAllLineString = false;
     745           4 :             if (poCC->addCurve(std::move(poCurve)) != OGRERR_NONE)
     746             :             {
     747           1 :                 return false;
     748             :             }
     749             :         }
     750             :     }
     751             :     else
     752             :     {
     753          51 :         if (wkbFlatten(poGeom->getGeometryType()) != wkbLineString)
     754          14 :             bChildrenAreAllLineString = false;
     755             : 
     756          51 :         auto poCurve = std::unique_ptr<OGRCurve>(poGeom.release()->toCurve());
     757          51 :         if (poCC->addCurve(std::move(poCurve)) != OGRERR_NONE)
     758             :         {
     759           0 :             return false;
     760             :         }
     761             :     }
     762          52 :     return true;
     763             : }
     764             : 
     765             : /************************************************************************/
     766             : /*                   GML2OGRGeometry_AddToMultiSurface()                */
     767             : /************************************************************************/
     768             : 
     769          93 : static bool GML2OGRGeometry_AddToMultiSurface(
     770             :     OGRMultiSurface *poMS, std::unique_ptr<OGRGeometry> poGeom,
     771             :     const char *pszMemberElement, bool &bChildrenAreAllPolygons)
     772             : {
     773          93 :     if (poGeom == nullptr)
     774             :     {
     775           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid %s", pszMemberElement);
     776           1 :         return false;
     777             :     }
     778             : 
     779          92 :     OGRwkbGeometryType eType = wkbFlatten(poGeom->getGeometryType());
     780          92 :     if (eType == wkbPolygon || eType == wkbCurvePolygon)
     781             :     {
     782          89 :         if (eType != wkbPolygon)
     783          11 :             bChildrenAreAllPolygons = false;
     784             : 
     785          89 :         if (poMS->addGeometry(std::move(poGeom)) != OGRERR_NONE)
     786             :         {
     787           0 :             return false;
     788             :         }
     789             :     }
     790           3 :     else if (eType == wkbMultiPolygon || eType == wkbMultiSurface)
     791             :     {
     792           2 :         OGRMultiSurface *poMS2 = poGeom->toMultiSurface();
     793           6 :         for (int i = 0; i < poMS2->getNumGeometries(); i++)
     794             :         {
     795           4 :             if (wkbFlatten(poMS2->getGeometryRef(i)->getGeometryType()) !=
     796             :                 wkbPolygon)
     797           0 :                 bChildrenAreAllPolygons = false;
     798             : 
     799           4 :             if (poMS->addGeometry(poMS2->getGeometryRef(i)) != OGRERR_NONE)
     800             :             {
     801           0 :                 return false;
     802             :             }
     803           2 :         }
     804             :     }
     805             :     else
     806             :     {
     807           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Got %s geometry as %s.",
     808           1 :                  poGeom->getGeometryName(), pszMemberElement);
     809           1 :         return false;
     810             :     }
     811          91 :     return true;
     812             : }
     813             : 
     814             : /************************************************************************/
     815             : /*                        GetUOMInMetre()                               */
     816             : /************************************************************************/
     817             : 
     818          36 : static double GetUOMInMetre(const char *pszUnits, const char *pszAttribute,
     819             :                             const char *pszId)
     820             : {
     821          36 :     if (!pszUnits || EQUAL(pszUnits, "m"))
     822           7 :         return 1.0;
     823             : 
     824          29 :     if (EQUAL(pszUnits, "km"))
     825           3 :         return 1000.0;
     826             : 
     827          26 :     if (EQUAL(pszUnits, "nm") || EQUAL(pszUnits, "[nmi_i]"))
     828          20 :         return CPLAtof(SRS_UL_INTL_NAUT_MILE_CONV);
     829             : 
     830           6 :     if (EQUAL(pszUnits, "mi"))
     831           0 :         return CPLAtof(SRS_UL_INTL_STAT_MILE_CONV);
     832             : 
     833           6 :     if (EQUAL(pszUnits, "ft"))
     834           1 :         return CPLAtof(SRS_UL_INTL_FOOT_CONV);
     835             : 
     836           5 :     if (pszId)
     837             :     {
     838           1 :         CPLError(CE_Warning, CPLE_AppDefined,
     839             :                  "GML geometry id='%s': Unhandled distance unit '%s' in "
     840             :                  "attribute '%s'",
     841             :                  pszId, pszUnits, pszAttribute);
     842             :     }
     843             :     else
     844             :     {
     845           4 :         CPLError(CE_Warning, CPLE_AppDefined,
     846             :                  "Unhandled distance unit '%s' in attribute '%s'", pszUnits,
     847             :                  pszAttribute);
     848             :     }
     849           5 :     return -1;
     850             : }
     851             : 
     852             : /************************************************************************/
     853             : /*                            GetSemiMajor()                            */
     854             : /************************************************************************/
     855             : 
     856          28 : static double GetSemiMajor(const OGRSpatialReference *poSRS)
     857             : {
     858          28 :     double dfSemiMajor = poSRS->GetSemiMajor();
     859             :     // Standardize on OGR_GREATCIRCLE_DEFAULT_RADIUS for Earth ellipsoids.
     860          28 :     if (std::fabs(dfSemiMajor - OGR_GREATCIRCLE_DEFAULT_RADIUS) <
     861             :         0.05 * OGR_GREATCIRCLE_DEFAULT_RADIUS)
     862          28 :         dfSemiMajor = OGR_GREATCIRCLE_DEFAULT_RADIUS;
     863          28 :     return dfSemiMajor;
     864             : }
     865             : 
     866             : /************************************************************************/
     867             : /*                      GML2OGRGeometry_XMLNode()                       */
     868             : /*                                                                      */
     869             : /*      Translates the passed XMLnode and its children into an         */
     870             : /*      OGRGeometry.  This is used recursively for geometry             */
     871             : /*      collections.                                                    */
     872             : /************************************************************************/
     873             : 
     874             : static std::unique_ptr<OGRGeometry> GML2OGRGeometry_XMLNode_Internal(
     875             :     const CPLXMLNode *psNode, const char *pszId,
     876             :     int nPseudoBoolGetSecondaryGeometryOption, int nRecLevel, int nSRSDimension,
     877             :     const char *pszSRSName,
     878             :     lru11::Cache<std::string, std::shared_ptr<OGRSpatialReference>> &oSRSCache,
     879             :     bool bIgnoreGSG = false, bool bOrientation = true,
     880             :     bool bFaceHoleNegative = false);
     881             : 
     882        2626 : OGRGeometry *GML2OGRGeometry_XMLNode(const CPLXMLNode *psNode,
     883             :                                      int nPseudoBoolGetSecondaryGeometryOption,
     884             :                                      int nRecLevel, int nSRSDimension,
     885             :                                      bool bIgnoreGSG, bool bOrientation,
     886             :                                      bool bFaceHoleNegative, const char *pszId)
     887             : 
     888             : {
     889        2626 :     lru11::Cache<std::string, std::shared_ptr<OGRSpatialReference>> oSRSCache;
     890        5252 :     return GML2OGRGeometry_XMLNode_Internal(
     891             :                psNode, pszId, nPseudoBoolGetSecondaryGeometryOption, nRecLevel,
     892             :                nSRSDimension, nullptr, oSRSCache, bIgnoreGSG, bOrientation,
     893             :                bFaceHoleNegative)
     894        5252 :         .release();
     895             : }
     896             : 
     897             : static void ReportError(const char *pszId, CPLErr eErr, const char *fmt, ...)
     898             :     CPL_PRINT_FUNC_FORMAT(3, 4);
     899             : 
     900         114 : static void ReportError(const char *pszId, CPLErr eErr, const char *fmt, ...)
     901             : {
     902             :     va_list ap;
     903         114 :     va_start(ap, fmt);
     904         114 :     if (pszId)
     905             :     {
     906           0 :         std::string osMsg("GML geometry id='");
     907           0 :         osMsg += pszId;
     908           0 :         osMsg += "': ";
     909           0 :         osMsg += CPLString().vPrintf(fmt, ap);
     910           0 :         CPLError(eErr, CPLE_AppDefined, "%s", osMsg.c_str());
     911             :     }
     912             :     else
     913             :     {
     914         114 :         CPLErrorV(eErr, CPLE_AppDefined, fmt, ap);
     915             :     }
     916         114 :     va_end(ap);
     917         114 : }
     918             : 
     919        8014 : static std::unique_ptr<OGRGeometry> GML2OGRGeometry_XMLNode_Internal(
     920             :     const CPLXMLNode *psNode, const char *pszId,
     921             :     int nPseudoBoolGetSecondaryGeometryOption, int nRecLevel, int nSRSDimension,
     922             :     const char *pszSRSName,
     923             :     lru11::Cache<std::string, std::shared_ptr<OGRSpatialReference>> &oSRSCache,
     924             :     bool bIgnoreGSG, bool bOrientation, bool bFaceHoleNegative)
     925             : {
     926             :     // constexpr bool bCastToLinearTypeIfPossible = true;  // Hard-coded for
     927             :     // now.
     928             : 
     929             :     // We need this nRecLevel == 0 check, otherwise this could result in
     930             :     // multiple revisit of the same node, and exponential complexity.
     931        8014 :     if (nRecLevel == 0 && psNode != nullptr &&
     932        2626 :         strcmp(psNode->pszValue, "?xml") == 0)
     933           4 :         psNode = psNode->psNext;
     934        8016 :     while (psNode != nullptr && psNode->eType == CXT_Comment)
     935           2 :         psNode = psNode->psNext;
     936        8014 :     if (psNode == nullptr)
     937           2 :         return nullptr;
     938             : 
     939             :     const char *pszSRSDimension =
     940        8012 :         CPLGetXMLValue(psNode, "srsDimension", nullptr);
     941        8012 :     if (pszSRSDimension != nullptr)
     942         621 :         nSRSDimension = atoi(pszSRSDimension);
     943             : 
     944        8012 :     if (pszSRSName == nullptr)
     945        6089 :         pszSRSName = CPLGetXMLValue(psNode, "srsName", nullptr);
     946             : 
     947        8012 :     if (!pszId && nRecLevel == 0)
     948             :     {
     949        2624 :         pszId = CPLGetXMLValue(psNode, "gml:id", nullptr);
     950             :     }
     951             : 
     952        8012 :     const char *pszBaseGeometry = BareGMLElement(psNode->pszValue);
     953        8012 :     if (nPseudoBoolGetSecondaryGeometryOption < 0)
     954        1093 :         nPseudoBoolGetSecondaryGeometryOption =
     955        1093 :             CPLTestBool(CPLGetConfigOption("GML_GET_SECONDARY_GEOM", "NO"));
     956             :     bool bGetSecondaryGeometry =
     957        8012 :         bIgnoreGSG ? false : CPL_TO_BOOL(nPseudoBoolGetSecondaryGeometryOption);
     958             : 
     959             : #define ReportFailure(...) ReportError(pszId, CE_Failure, __VA_ARGS__)
     960             : 
     961             : #define ReportWarning(...) ReportError(pszId, CE_Warning, __VA_ARGS__)
     962             : 
     963             :     // Arbitrary value, but certainly large enough for reasonable usages.
     964        8012 :     if (nRecLevel == 32)
     965             :     {
     966           1 :         ReportFailure(
     967             :             "Too many recursion levels (%d) while parsing GML geometry.",
     968             :             nRecLevel);
     969           1 :         return nullptr;
     970             :     }
     971             : 
     972        8011 :     if (bGetSecondaryGeometry)
     973           0 :         if (!(EQUAL(pszBaseGeometry, "directedEdge") ||
     974           0 :               EQUAL(pszBaseGeometry, "TopoCurve")))
     975           0 :             return nullptr;
     976             : 
     977             :     /* -------------------------------------------------------------------- */
     978             :     /*      Polygon / PolygonPatch / Rectangle                              */
     979             :     /* -------------------------------------------------------------------- */
     980        8011 :     if (EQUAL(pszBaseGeometry, "Polygon") ||
     981        7242 :         EQUAL(pszBaseGeometry, "PolygonPatch") ||
     982        7183 :         EQUAL(pszBaseGeometry, "Rectangle"))
     983             :     {
     984             :         // Find outer ring.
     985         830 :         const CPLXMLNode *psChild = FindBareXMLChild(psNode, "outerBoundaryIs");
     986         830 :         if (psChild == nullptr)
     987         747 :             psChild = FindBareXMLChild(psNode, "exterior");
     988             : 
     989         830 :         psChild = GetChildElement(psChild);
     990         830 :         if (psChild == nullptr)
     991             :         {
     992             :             // <gml:Polygon/> is invalid GML2, but valid GML3, so be tolerant.
     993           5 :             return std::make_unique<OGRPolygon>();
     994             :         }
     995             : 
     996             :         // Translate outer ring and add to polygon.
     997             :         auto poGeom = GML2OGRGeometry_XMLNode_Internal(
     998             :             psChild, pszId, nPseudoBoolGetSecondaryGeometryOption,
     999        1650 :             nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache);
    1000         825 :         if (poGeom == nullptr)
    1001             :         {
    1002           3 :             ReportFailure("Invalid exterior ring");
    1003           3 :             return nullptr;
    1004             :         }
    1005             : 
    1006         822 :         if (!OGR_GT_IsCurve(poGeom->getGeometryType()))
    1007             :         {
    1008           1 :             ReportFailure("%s: Got %s geometry as outerBoundaryIs.",
    1009             :                           pszBaseGeometry, poGeom->getGeometryName());
    1010           1 :             return nullptr;
    1011             :         }
    1012             : 
    1013        1611 :         if (wkbFlatten(poGeom->getGeometryType()) == wkbLineString &&
    1014         790 :             !EQUAL(poGeom->getGeometryName(), "LINEARRING"))
    1015             :         {
    1016           8 :             OGRCurve *poCurve = poGeom.release()->toCurve();
    1017           8 :             auto poLinearRing = OGRCurve::CastToLinearRing(poCurve);
    1018           8 :             if (!poLinearRing)
    1019           1 :                 return nullptr;
    1020           7 :             poGeom.reset(poLinearRing);
    1021             :         }
    1022             : 
    1023         820 :         std::unique_ptr<OGRCurvePolygon> poCP;
    1024         820 :         bool bIsPolygon = false;
    1025         820 :         assert(poGeom);  // to please cppcheck
    1026         820 :         if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
    1027             :         {
    1028         789 :             poCP = std::make_unique<OGRPolygon>();
    1029         789 :             bIsPolygon = true;
    1030             :         }
    1031             :         else
    1032             :         {
    1033          31 :             poCP = std::make_unique<OGRCurvePolygon>();
    1034          31 :             bIsPolygon = false;
    1035             :         }
    1036             : 
    1037             :         {
    1038             :             auto poCurve =
    1039         820 :                 std::unique_ptr<OGRCurve>(poGeom.release()->toCurve());
    1040         820 :             if (poCP->addRing(std::move(poCurve)) != OGRERR_NONE)
    1041             :             {
    1042           0 :                 return nullptr;
    1043             :             }
    1044             :         }
    1045             : 
    1046             :         // Find all inner rings
    1047        3189 :         for (psChild = psNode->psChild; psChild != nullptr;
    1048        2369 :              psChild = psChild->psNext)
    1049             :         {
    1050        3208 :             if (psChild->eType == CXT_Element &&
    1051         837 :                 (EQUAL(BareGMLElement(psChild->pszValue), "innerBoundaryIs") ||
    1052         833 :                  EQUAL(BareGMLElement(psChild->pszValue), "interior")))
    1053             :             {
    1054          17 :                 const CPLXMLNode *psInteriorChild = GetChildElement(psChild);
    1055           0 :                 std::unique_ptr<OGRGeometry> poGeomInterior;
    1056          17 :                 if (psInteriorChild != nullptr)
    1057          32 :                     poGeomInterior = GML2OGRGeometry_XMLNode_Internal(
    1058             :                         psInteriorChild, pszId,
    1059             :                         nPseudoBoolGetSecondaryGeometryOption, nRecLevel + 1,
    1060          16 :                         nSRSDimension, pszSRSName, oSRSCache);
    1061          17 :                 if (poGeomInterior == nullptr)
    1062             :                 {
    1063           1 :                     ReportFailure("Invalid interior ring");
    1064           1 :                     return nullptr;
    1065             :                 }
    1066             : 
    1067          16 :                 if (!OGR_GT_IsCurve(poGeomInterior->getGeometryType()))
    1068             :                 {
    1069           1 :                     ReportFailure("%s: Got %s geometry as innerBoundaryIs.",
    1070             :                                   pszBaseGeometry,
    1071             :                                   poGeomInterior->getGeometryName());
    1072           1 :                     return nullptr;
    1073             :                 }
    1074             : 
    1075          15 :                 if (bIsPolygon)
    1076             :                 {
    1077          11 :                     if (!EQUAL(poGeomInterior->getGeometryName(), "LINEARRING"))
    1078             :                     {
    1079           1 :                         if (wkbFlatten(poGeomInterior->getGeometryType()) ==
    1080             :                             wkbLineString)
    1081             :                         {
    1082             :                             OGRLineString *poLS =
    1083           0 :                                 poGeomInterior.release()->toLineString();
    1084             :                             auto poLinearRing =
    1085           0 :                                 OGRCurve::CastToLinearRing(poLS);
    1086           0 :                             if (!poLinearRing)
    1087           0 :                                 return nullptr;
    1088           0 :                             poGeomInterior.reset(poLinearRing);
    1089             :                         }
    1090             :                         else
    1091             :                         {
    1092             :                             // Might fail if some rings are not closed.
    1093             :                             // We used to be tolerant about that with Polygon.
    1094             :                             // but we have become stricter with CurvePolygon.
    1095             :                             auto poCPNew = std::unique_ptr<OGRCurvePolygon>(
    1096           1 :                                 OGRSurface::CastToCurvePolygon(poCP.release()));
    1097           1 :                             if (!poCPNew)
    1098             :                             {
    1099           0 :                                 return nullptr;
    1100             :                             }
    1101           1 :                             poCP = std::move(poCPNew);
    1102           1 :                             bIsPolygon = false;
    1103             :                         }
    1104             :                     }
    1105             :                 }
    1106             :                 else
    1107             :                 {
    1108           4 :                     if (EQUAL(poGeomInterior->getGeometryName(), "LINEARRING"))
    1109             :                     {
    1110           2 :                         OGRCurve *poCurve = poGeomInterior.release()->toCurve();
    1111           2 :                         poGeomInterior.reset(
    1112           2 :                             OGRCurve::CastToLineString(poCurve));
    1113             :                     }
    1114             :                 }
    1115             :                 auto poCurve = std::unique_ptr<OGRCurve>(
    1116          15 :                     poGeomInterior.release()->toCurve());
    1117          15 :                 if (poCP->addRing(std::move(poCurve)) != OGRERR_NONE)
    1118             :                 {
    1119           0 :                     return nullptr;
    1120             :                 }
    1121             :             }
    1122             :         }
    1123             : 
    1124         818 :         return poCP;
    1125             :     }
    1126             : 
    1127             :     /* -------------------------------------------------------------------- */
    1128             :     /*      Triangle                                                        */
    1129             :     /* -------------------------------------------------------------------- */
    1130             : 
    1131        7181 :     if (EQUAL(pszBaseGeometry, "Triangle"))
    1132             :     {
    1133             :         // Find outer ring.
    1134          11 :         const CPLXMLNode *psChild = FindBareXMLChild(psNode, "exterior");
    1135          11 :         if (!psChild)
    1136           1 :             return nullptr;
    1137             : 
    1138          10 :         psChild = GetChildElement(psChild);
    1139          10 :         if (psChild == nullptr)
    1140             :         {
    1141           0 :             ReportFailure("Empty Triangle");
    1142           0 :             return std::make_unique<OGRTriangle>();
    1143             :         }
    1144             : 
    1145             :         // Translate outer ring and add to Triangle.
    1146             :         auto poGeom = GML2OGRGeometry_XMLNode_Internal(
    1147             :             psChild, pszId, nPseudoBoolGetSecondaryGeometryOption,
    1148          20 :             nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache);
    1149          10 :         if (poGeom == nullptr)
    1150             :         {
    1151           0 :             ReportFailure("Invalid exterior ring");
    1152           0 :             return nullptr;
    1153             :         }
    1154             : 
    1155          10 :         if (!OGR_GT_IsCurve(poGeom->getGeometryType()))
    1156             :         {
    1157           0 :             ReportFailure("%s: Got %s geometry as outerBoundaryIs.",
    1158             :                           pszBaseGeometry, poGeom->getGeometryName());
    1159           0 :             return nullptr;
    1160             :         }
    1161             : 
    1162          20 :         if (wkbFlatten(poGeom->getGeometryType()) == wkbLineString &&
    1163          10 :             !EQUAL(poGeom->getGeometryName(), "LINEARRING"))
    1164             :         {
    1165           1 :             poGeom.reset(
    1166           1 :                 OGRCurve::CastToLinearRing(poGeom.release()->toCurve()));
    1167             :         }
    1168             : 
    1169          19 :         if (poGeom == nullptr ||
    1170           9 :             !EQUAL(poGeom->getGeometryName(), "LINEARRING"))
    1171             :         {
    1172           1 :             return nullptr;
    1173             :         }
    1174             : 
    1175          18 :         auto poTriangle = std::make_unique<OGRTriangle>();
    1176          18 :         auto poCurve = std::unique_ptr<OGRCurve>(poGeom.release()->toCurve());
    1177           9 :         if (poTriangle->addRing(std::move(poCurve)) != OGRERR_NONE)
    1178             :         {
    1179           0 :             return nullptr;
    1180             :         }
    1181             : 
    1182           9 :         return poTriangle;
    1183             :     }
    1184             : 
    1185             :     /* -------------------------------------------------------------------- */
    1186             :     /*      LinearRing                                                      */
    1187             :     /* -------------------------------------------------------------------- */
    1188        7170 :     if (EQUAL(pszBaseGeometry, "LinearRing"))
    1189             :     {
    1190        1600 :         auto poLinearRing = std::make_unique<OGRLinearRing>();
    1191             : 
    1192         800 :         if (!ParseGMLCoordinates(psNode, poLinearRing.get(), nSRSDimension))
    1193             :         {
    1194           1 :             return nullptr;
    1195             :         }
    1196             : 
    1197         799 :         return poLinearRing;
    1198             :     }
    1199             : 
    1200             :     const auto storeArcByCenterPointParameters =
    1201          16 :         [pszId, &oSRSCache](const CPLXMLNode *psChild, const char *l_pszSRSName,
    1202             :                             bool &bIsApproximateArc,
    1203             :                             double &dfLastCurveApproximateArcRadius,
    1204             :                             bool &bLastCurveWasApproximateArcInvertedAxisOrder,
    1205          32 :                             double &dfSemiMajor)
    1206             :     {
    1207          16 :         const CPLXMLNode *psRadius = FindBareXMLChild(psChild, "radius");
    1208          16 :         if (psRadius && psRadius->eType == CXT_Element)
    1209             :         {
    1210          16 :             const char *pszUnits = CPLGetXMLValue(psRadius, "uom", nullptr);
    1211          16 :             const double dfUOMConv = GetUOMInMetre(pszUnits, "radius", pszId);
    1212             :             const double dfRadiusRaw =
    1213          16 :                 CPLAtof(CPLGetXMLValue(psRadius, nullptr, "0"));
    1214          16 :             const double dfRadius =
    1215          16 :                 dfUOMConv > 0 ? dfRadiusRaw * dfUOMConv : dfRadiusRaw;
    1216          16 :             bool bSRSUnitIsDegree = false;
    1217          16 :             bool bInvertedAxisOrder = false;
    1218          16 :             if (l_pszSRSName != nullptr)
    1219             :             {
    1220          16 :                 std::shared_ptr<OGRSpatialReference> poSRS;
    1221          16 :                 if (!oSRSCache.tryGet(l_pszSRSName, poSRS))
    1222             :                 {
    1223           0 :                     poSRS = std::make_shared<OGRSpatialReference>();
    1224           0 :                     if (poSRS->SetFromUserInput(
    1225             :                             l_pszSRSName,
    1226             :                             OGRSpatialReference::
    1227           0 :                                 SET_FROM_USER_INPUT_LIMITATIONS_get()) !=
    1228             :                         OGRERR_NONE)
    1229           0 :                         poSRS.reset();
    1230           0 :                     oSRSCache.insert(l_pszSRSName, poSRS);
    1231             :                 }
    1232          16 :                 if (poSRS && poSRS->IsGeographic())
    1233             :                 {
    1234             :                     bInvertedAxisOrder =
    1235          14 :                         CPL_TO_BOOL(poSRS->EPSGTreatsAsLatLong());
    1236          14 :                     dfSemiMajor = GetSemiMajor(poSRS.get());
    1237          14 :                     bSRSUnitIsDegree = fabs(poSRS->GetAngularUnits(nullptr) -
    1238          14 :                                             CPLAtof(SRS_UA_DEGREE_CONV)) < 1e-8;
    1239             :                 }
    1240             :             }
    1241          16 :             if (bSRSUnitIsDegree && dfUOMConv > 0)
    1242             :             {
    1243          14 :                 bIsApproximateArc = true;
    1244          14 :                 dfLastCurveApproximateArcRadius = dfRadius;
    1245          14 :                 bLastCurveWasApproximateArcInvertedAxisOrder =
    1246             :                     bInvertedAxisOrder;
    1247             :             }
    1248             :         }
    1249          16 :     };
    1250             : 
    1251             :     const auto connectArcByCenterPointToOtherSegments =
    1252          46 :         [](OGRGeometry *poGeom, OGRCompoundCurve *poCC,
    1253             :            const bool bIsApproximateArc, const bool bLastCurveWasApproximateArc,
    1254             :            const double dfLastCurveApproximateArcRadius,
    1255             :            const bool bLastCurveWasApproximateArcInvertedAxisOrder,
    1256             :            const double dfSemiMajor)
    1257             :     {
    1258          46 :         if (bIsApproximateArc)
    1259             :         {
    1260           5 :             if (poGeom->getGeometryType() == wkbLineString)
    1261             :             {
    1262             :                 OGRCurve *poPreviousCurve =
    1263           5 :                     poCC->getCurve(poCC->getNumCurves() - 1);
    1264           5 :                 OGRLineString *poLS = poGeom->toLineString();
    1265          10 :                 if (poPreviousCurve->getNumPoints() >= 2 &&
    1266           5 :                     poLS->getNumPoints() >= 2)
    1267             :                 {
    1268          10 :                     OGRPoint p;
    1269          10 :                     OGRPoint p2;
    1270           5 :                     poPreviousCurve->EndPoint(&p);
    1271           5 :                     poLS->StartPoint(&p2);
    1272           5 :                     double dfDistance = 0.0;
    1273           5 :                     if (bLastCurveWasApproximateArcInvertedAxisOrder)
    1274           4 :                         dfDistance = OGR_GreatCircle_Distance(
    1275             :                             p.getX(), p.getY(), p2.getX(), p2.getY(),
    1276             :                             dfSemiMajor);
    1277             :                     else
    1278           1 :                         dfDistance = OGR_GreatCircle_Distance(
    1279             :                             p.getY(), p.getX(), p2.getY(), p2.getX(),
    1280             :                             dfSemiMajor);
    1281             :                     // CPLDebug("OGR", "%f %f",
    1282             :                     //          dfDistance,
    1283             :                     //          dfLastCurveApproximateArcRadius
    1284             :                     //          / 10.0 );
    1285           5 :                     if (dfDistance < dfLastCurveApproximateArcRadius / 5.0)
    1286             :                     {
    1287           5 :                         CPLDebug("OGR", "Moving approximate start of "
    1288             :                                         "ArcByCenterPoint to end of "
    1289             :                                         "previous curve");
    1290           5 :                         poLS->setPoint(0, &p);
    1291             :                     }
    1292             :                 }
    1293             :             }
    1294             :         }
    1295          41 :         else if (bLastCurveWasApproximateArc)
    1296             :         {
    1297             :             OGRCurve *poPreviousCurve =
    1298           5 :                 poCC->getCurve(poCC->getNumCurves() - 1);
    1299           5 :             if (poPreviousCurve->getGeometryType() == wkbLineString)
    1300             :             {
    1301           5 :                 OGRLineString *poLS = poPreviousCurve->toLineString();
    1302           5 :                 OGRCurve *poAsCurve = poGeom->toCurve();
    1303             : 
    1304           5 :                 if (poLS->getNumPoints() >= 2 && poAsCurve->getNumPoints() >= 2)
    1305             :                 {
    1306          10 :                     OGRPoint p;
    1307          10 :                     OGRPoint p2;
    1308           5 :                     poAsCurve->StartPoint(&p);
    1309           5 :                     poLS->EndPoint(&p2);
    1310           5 :                     double dfDistance = 0.0;
    1311           5 :                     if (bLastCurveWasApproximateArcInvertedAxisOrder)
    1312           4 :                         dfDistance = OGR_GreatCircle_Distance(
    1313             :                             p.getX(), p.getY(), p2.getX(), p2.getY(),
    1314             :                             dfSemiMajor);
    1315             :                     else
    1316           1 :                         dfDistance = OGR_GreatCircle_Distance(
    1317             :                             p.getY(), p.getX(), p2.getY(), p2.getX(),
    1318             :                             dfSemiMajor);
    1319             :                     // CPLDebug(
    1320             :                     //    "OGR", "%f %f",
    1321             :                     //    dfDistance,
    1322             :                     //    dfLastCurveApproximateArcRadius / 10.0 );
    1323             : 
    1324             :                     // "A-311 WHEELER AFB OAHU, HI.xml" needs more
    1325             :                     // than 10%.
    1326           5 :                     if (dfDistance < dfLastCurveApproximateArcRadius / 5.0)
    1327             :                     {
    1328           5 :                         CPLDebug("OGR", "Moving approximate end of last "
    1329             :                                         "ArcByCenterPoint to start of the "
    1330             :                                         "current curve");
    1331           5 :                         poLS->setPoint(poLS->getNumPoints() - 1, &p);
    1332             :                     }
    1333             :                 }
    1334             :             }
    1335             :         }
    1336          46 :     };
    1337             : 
    1338             :     /* -------------------------------------------------------------------- */
    1339             :     /*      Ring GML3                                                       */
    1340             :     /* -------------------------------------------------------------------- */
    1341        6370 :     if (EQUAL(pszBaseGeometry, "Ring"))
    1342             :     {
    1343          49 :         std::unique_ptr<OGRCurve> poRing;
    1344          49 :         std::unique_ptr<OGRCompoundCurve> poCC;
    1345          49 :         bool bChildrenAreAllLineString = true;
    1346             : 
    1347          49 :         bool bLastCurveWasApproximateArc = false;
    1348          49 :         bool bLastCurveWasApproximateArcInvertedAxisOrder = false;
    1349          49 :         double dfLastCurveApproximateArcRadius = 0.0;
    1350             : 
    1351          49 :         bool bIsFirstChild = true;
    1352          49 :         bool bFirstChildIsApproximateArc = false;
    1353          49 :         double dfFirstChildApproximateArcRadius = 0.0;
    1354          49 :         bool bFirstChildWasApproximateArcInvertedAxisOrder = false;
    1355             : 
    1356          49 :         double dfSemiMajor = OGR_GREATCIRCLE_DEFAULT_RADIUS;
    1357             : 
    1358         117 :         for (const CPLXMLNode *psChild = psNode->psChild; psChild != nullptr;
    1359          68 :              psChild = psChild->psNext)
    1360             :         {
    1361         142 :             if (psChild->eType == CXT_Element &&
    1362          71 :                 EQUAL(BareGMLElement(psChild->pszValue), "curveMember"))
    1363             :             {
    1364          70 :                 const CPLXMLNode *psCurveChild = GetChildElement(psChild);
    1365           0 :                 std::unique_ptr<OGRGeometry> poGeom;
    1366          70 :                 if (psCurveChild != nullptr)
    1367             :                 {
    1368         138 :                     poGeom = GML2OGRGeometry_XMLNode_Internal(
    1369             :                         psCurveChild, pszId,
    1370             :                         nPseudoBoolGetSecondaryGeometryOption, nRecLevel + 1,
    1371          69 :                         nSRSDimension, pszSRSName, oSRSCache);
    1372             :                 }
    1373             :                 else
    1374             :                 {
    1375           1 :                     if (psChild->psChild &&
    1376           0 :                         psChild->psChild->eType == CXT_Attribute &&
    1377           0 :                         psChild->psChild->psNext == nullptr &&
    1378           0 :                         strcmp(psChild->psChild->pszValue, "xlink:href") == 0)
    1379             :                     {
    1380           0 :                         ReportWarning("Cannot resolve xlink:href='%s'. "
    1381             :                                       "Try setting GML_SKIP_RESOLVE_ELEMS=NONE",
    1382             :                                       psChild->psChild->psChild->pszValue);
    1383             :                     }
    1384           1 :                     return nullptr;
    1385             :                 }
    1386             : 
    1387             :                 // Try to join multiline string to one linestring.
    1388         137 :                 if (poGeom &&
    1389         137 :                     wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
    1390             :                 {
    1391           0 :                     poGeom.reset(OGRGeometryFactory::forceToLineString(
    1392             :                         poGeom.release(), false));
    1393             :                 }
    1394             : 
    1395         137 :                 if (poGeom == nullptr ||
    1396          68 :                     !OGR_GT_IsCurve(poGeom->getGeometryType()))
    1397             :                 {
    1398           2 :                     return nullptr;
    1399             :                 }
    1400             : 
    1401          67 :                 if (wkbFlatten(poGeom->getGeometryType()) != wkbLineString)
    1402          32 :                     bChildrenAreAllLineString = false;
    1403             : 
    1404             :                 // Ad-hoc logic to handle nicely connecting ArcByCenterPoint
    1405             :                 // with consecutive curves, as found in some AIXM files.
    1406          67 :                 bool bIsApproximateArc = false;
    1407             :                 const CPLXMLNode *psChild2, *psChild3;
    1408          67 :                 if (strcmp(BareGMLElement(psCurveChild->pszValue), "Curve") ==
    1409          63 :                         0 &&
    1410          63 :                     (psChild2 = GetChildElement(psCurveChild)) != nullptr &&
    1411          63 :                     strcmp(BareGMLElement(psChild2->pszValue), "segments") ==
    1412          63 :                         0 &&
    1413         193 :                     (psChild3 = GetChildElement(psChild2)) != nullptr &&
    1414          63 :                     strcmp(BareGMLElement(psChild3->pszValue),
    1415             :                            "ArcByCenterPoint") == 0)
    1416             :                 {
    1417           2 :                     storeArcByCenterPointParameters(
    1418             :                         psChild3, pszSRSName, bIsApproximateArc,
    1419             :                         dfLastCurveApproximateArcRadius,
    1420             :                         bLastCurveWasApproximateArcInvertedAxisOrder,
    1421             :                         dfSemiMajor);
    1422           2 :                     if (bIsFirstChild && bIsApproximateArc)
    1423             :                     {
    1424           1 :                         bFirstChildIsApproximateArc = true;
    1425           1 :                         dfFirstChildApproximateArcRadius =
    1426             :                             dfLastCurveApproximateArcRadius;
    1427           1 :                         bFirstChildWasApproximateArcInvertedAxisOrder =
    1428             :                             bLastCurveWasApproximateArcInvertedAxisOrder;
    1429             :                     }
    1430           1 :                     else if (psChild3->psNext)
    1431             :                     {
    1432           0 :                         bIsApproximateArc = false;
    1433             :                     }
    1434             :                 }
    1435          67 :                 bIsFirstChild = false;
    1436             : 
    1437          67 :                 if (poCC == nullptr && poRing == nullptr)
    1438             :                 {
    1439          44 :                     poRing.reset(poGeom.release()->toCurve());
    1440             :                 }
    1441             :                 else
    1442             :                 {
    1443          23 :                     if (poCC == nullptr)
    1444             :                     {
    1445           6 :                         poCC = std::make_unique<OGRCompoundCurve>();
    1446           6 :                         bool bIgnored = false;
    1447           6 :                         if (!GML2OGRGeometry_AddToCompositeCurve(
    1448           6 :                                 poCC.get(), std::move(poRing), bIgnored))
    1449             :                         {
    1450           0 :                             return nullptr;
    1451             :                         }
    1452           6 :                         poRing.reset();
    1453             :                     }
    1454             : 
    1455          23 :                     connectArcByCenterPointToOtherSegments(
    1456             :                         poGeom.get(), poCC.get(), bIsApproximateArc,
    1457             :                         bLastCurveWasApproximateArc,
    1458             :                         dfLastCurveApproximateArcRadius,
    1459             :                         bLastCurveWasApproximateArcInvertedAxisOrder,
    1460             :                         dfSemiMajor);
    1461             : 
    1462             :                     auto poCurve =
    1463          23 :                         std::unique_ptr<OGRCurve>(poGeom.release()->toCurve());
    1464             : 
    1465          23 :                     bool bIgnored = false;
    1466          23 :                     if (!GML2OGRGeometry_AddToCompositeCurve(
    1467          23 :                             poCC.get(), std::move(poCurve), bIgnored))
    1468             :                     {
    1469           0 :                         return nullptr;
    1470             :                     }
    1471             :                 }
    1472             : 
    1473          67 :                 bLastCurveWasApproximateArc = bIsApproximateArc;
    1474             :             }
    1475             :         }
    1476             : 
    1477             :         /* Detect if the last object in the following hierarchy is a
    1478             :            ArcByCenterPoint <gml:Ring> <gml:curveMember> (may be repeated)
    1479             :                     <gml:Curve>
    1480             :                         <gml:segments>
    1481             :                             ....
    1482             :                             <gml:ArcByCenterPoint ... />
    1483             :                         </gml:segments>
    1484             :                     </gml:Curve>
    1485             :                 </gml:curveMember>
    1486             :             </gml:Ring>
    1487             :         */
    1488          46 :         bool bLastChildIsApproximateArc = false;
    1489         114 :         for (const CPLXMLNode *psChild = psNode->psChild; psChild != nullptr;
    1490          68 :              psChild = psChild->psNext)
    1491             :         {
    1492         136 :             if (psChild->eType == CXT_Element &&
    1493          68 :                 EQUAL(BareGMLElement(psChild->pszValue), "curveMember"))
    1494             :             {
    1495          67 :                 const CPLXMLNode *psCurveMemberChild = GetChildElement(psChild);
    1496         134 :                 if (psCurveMemberChild &&
    1497         134 :                     psCurveMemberChild->eType == CXT_Element &&
    1498          67 :                     EQUAL(BareGMLElement(psCurveMemberChild->pszValue),
    1499             :                           "Curve"))
    1500             :                 {
    1501             :                     const CPLXMLNode *psCurveChild =
    1502          63 :                         GetChildElement(psCurveMemberChild);
    1503         126 :                     if (psCurveChild && psCurveChild->eType == CXT_Element &&
    1504          63 :                         EQUAL(BareGMLElement(psCurveChild->pszValue),
    1505             :                               "segments"))
    1506             :                     {
    1507          63 :                         for (const CPLXMLNode *psChild2 = psCurveChild->psChild;
    1508         143 :                              psChild2 != nullptr; psChild2 = psChild2->psNext)
    1509             :                         {
    1510         160 :                             if (psChild2->eType == CXT_Element &&
    1511          80 :                                 EQUAL(BareGMLElement(psChild2->pszValue),
    1512             :                                       "ArcByCenterPoint"))
    1513             :                             {
    1514           7 :                                 storeArcByCenterPointParameters(
    1515             :                                     psChild2, pszSRSName,
    1516             :                                     bLastChildIsApproximateArc,
    1517             :                                     dfLastCurveApproximateArcRadius,
    1518             :                                     bLastCurveWasApproximateArcInvertedAxisOrder,
    1519             :                                     dfSemiMajor);
    1520             :                             }
    1521             :                             else
    1522             :                             {
    1523          73 :                                 bLastChildIsApproximateArc = false;
    1524             :                             }
    1525             :                         }
    1526             :                     }
    1527             :                     else
    1528             :                     {
    1529           0 :                         bLastChildIsApproximateArc = false;
    1530             :                     }
    1531             :                 }
    1532             :                 else
    1533             :                 {
    1534           4 :                     bLastChildIsApproximateArc = false;
    1535             :                 }
    1536             :             }
    1537             :             else
    1538             :             {
    1539           1 :                 bLastChildIsApproximateArc = false;
    1540             :             }
    1541             :         }
    1542             : 
    1543          46 :         if (poRing)
    1544             :         {
    1545          76 :             if (poRing->getNumPoints() >= 2 && bFirstChildIsApproximateArc &&
    1546          77 :                 !poRing->get_IsClosed() &&
    1547           1 :                 wkbFlatten(poRing->getGeometryType()) == wkbLineString)
    1548             :             {
    1549           1 :                 OGRLineString *poLS = poRing->toLineString();
    1550             : 
    1551           2 :                 OGRPoint p;
    1552           2 :                 OGRPoint p2;
    1553           1 :                 poLS->StartPoint(&p);
    1554           1 :                 poLS->EndPoint(&p2);
    1555           1 :                 double dfDistance = 0.0;
    1556           1 :                 if (bFirstChildWasApproximateArcInvertedAxisOrder)
    1557           1 :                     dfDistance = OGR_GreatCircle_Distance(
    1558             :                         p.getX(), p.getY(), p2.getX(), p2.getY(), dfSemiMajor);
    1559             :                 else
    1560           0 :                     dfDistance = OGR_GreatCircle_Distance(
    1561             :                         p.getY(), p.getX(), p2.getY(), p2.getX(), dfSemiMajor);
    1562           1 :                 if (dfDistance < dfFirstChildApproximateArcRadius / 5.0)
    1563             :                 {
    1564           1 :                     CPLDebug("OGR", "Moving approximate start of "
    1565             :                                     "ArcByCenterPoint to end of "
    1566             :                                     "curve");
    1567           1 :                     poLS->setPoint(0, &p2);
    1568             :                 }
    1569             :             }
    1570          74 :             else if (poRing->getNumPoints() >= 2 &&
    1571          75 :                      bLastChildIsApproximateArc && !poRing->get_IsClosed() &&
    1572           1 :                      wkbFlatten(poRing->getGeometryType()) == wkbLineString)
    1573             :             {
    1574           1 :                 OGRLineString *poLS = poRing->toLineString();
    1575             : 
    1576           2 :                 OGRPoint p;
    1577           2 :                 OGRPoint p2;
    1578           1 :                 poLS->StartPoint(&p);
    1579           1 :                 poLS->EndPoint(&p2);
    1580           1 :                 double dfDistance = 0.0;
    1581           1 :                 if (bLastCurveWasApproximateArcInvertedAxisOrder)
    1582           1 :                     dfDistance = OGR_GreatCircle_Distance(
    1583             :                         p.getX(), p.getY(), p2.getX(), p2.getY(), dfSemiMajor);
    1584             :                 else
    1585           0 :                     dfDistance = OGR_GreatCircle_Distance(
    1586             :                         p.getY(), p.getX(), p2.getY(), p2.getX(), dfSemiMajor);
    1587           1 :                 if (dfDistance < dfLastCurveApproximateArcRadius / 5.0)
    1588             :                 {
    1589           1 :                     CPLDebug("OGR", "Moving approximate end of "
    1590             :                                     "ArcByCenterPoint to start of "
    1591             :                                     "curve");
    1592           1 :                     poLS->setPoint(poLS->getNumPoints() - 1, &p);
    1593             :                 }
    1594             :             }
    1595             : 
    1596          38 :             if (poRing->getNumPoints() < 2 || !poRing->get_IsClosed())
    1597             :             {
    1598           0 :                 ReportFailure("Non-closed ring");
    1599           0 :                 return nullptr;
    1600             :             }
    1601          38 :             return poRing;
    1602             :         }
    1603             : 
    1604           8 :         if (poCC == nullptr)
    1605           2 :             return nullptr;
    1606             : 
    1607           6 :         else if (/* bCastToLinearTypeIfPossible &&*/ bChildrenAreAllLineString)
    1608             :         {
    1609          10 :             return std::unique_ptr<OGRLinearRing>(
    1610          10 :                 OGRCurve::CastToLinearRing(poCC.release()));
    1611             :         }
    1612             :         else
    1613             :         {
    1614           1 :             if (poCC->getNumPoints() < 2 || !poCC->get_IsClosed())
    1615             :             {
    1616           0 :                 ReportFailure("Non-closed ring");
    1617           0 :                 return nullptr;
    1618             :             }
    1619           1 :             return poCC;
    1620             :         }
    1621             :     }
    1622             : 
    1623             :     /* -------------------------------------------------------------------- */
    1624             :     /*      LineString                                                      */
    1625             :     /* -------------------------------------------------------------------- */
    1626        6321 :     if (EQUAL(pszBaseGeometry, "LineString") ||
    1627        5541 :         EQUAL(pszBaseGeometry, "LineStringSegment") ||
    1628        4860 :         EQUAL(pszBaseGeometry, "Geodesic") ||
    1629        4858 :         EQUAL(pszBaseGeometry, "GeodesicString"))
    1630             :     {
    1631        2956 :         auto poLine = std::make_unique<OGRLineString>();
    1632             : 
    1633        1478 :         if (!ParseGMLCoordinates(psNode, poLine.get(), nSRSDimension))
    1634             :         {
    1635          10 :             return nullptr;
    1636             :         }
    1637             : 
    1638        1468 :         return poLine;
    1639             :     }
    1640             : 
    1641             :     /* -------------------------------------------------------------------- */
    1642             :     /*      Arc                                                             */
    1643             :     /* -------------------------------------------------------------------- */
    1644        4843 :     if (EQUAL(pszBaseGeometry, "Arc"))
    1645             :     {
    1646          50 :         auto poCC = std::make_unique<OGRCircularString>();
    1647             : 
    1648          25 :         if (!ParseGMLCoordinates(psNode, poCC.get(), nSRSDimension))
    1649             :         {
    1650           1 :             return nullptr;
    1651             :         }
    1652             : 
    1653             :         // Normally a gml:Arc has only 3 points of controls, but in the
    1654             :         // wild we sometimes find GML with 5 points, so accept any odd
    1655             :         // number >= 3 (ArcString should be used for > 3 points)
    1656          24 :         if (poCC->getNumPoints() < 3 || (poCC->getNumPoints() % 2) != 1)
    1657             :         {
    1658           2 :             ReportFailure("Bad number of points in Arc");
    1659           2 :             return nullptr;
    1660             :         }
    1661             : 
    1662          22 :         return poCC;
    1663             :     }
    1664             : 
    1665             :     /* -------------------------------------------------------------------- */
    1666             :     /*     ArcString                                                        */
    1667             :     /* -------------------------------------------------------------------- */
    1668        4818 :     if (EQUAL(pszBaseGeometry, "ArcString"))
    1669             :     {
    1670          56 :         auto poCC = std::make_unique<OGRCircularString>();
    1671             : 
    1672          28 :         if (!ParseGMLCoordinates(psNode, poCC.get(), nSRSDimension))
    1673             :         {
    1674           1 :             return nullptr;
    1675             :         }
    1676             : 
    1677          27 :         if (poCC->getNumPoints() < 3 || (poCC->getNumPoints() % 2) != 1)
    1678             :         {
    1679           2 :             ReportFailure("Bad number of points in ArcString");
    1680           2 :             return nullptr;
    1681             :         }
    1682             : 
    1683          25 :         return poCC;
    1684             :     }
    1685             : 
    1686             :     /* -------------------------------------------------------------------- */
    1687             :     /*      Circle                                                          */
    1688             :     /* -------------------------------------------------------------------- */
    1689        4790 :     if (EQUAL(pszBaseGeometry, "Circle"))
    1690             :     {
    1691          58 :         auto poLine = std::make_unique<OGRLineString>();
    1692             : 
    1693          29 :         if (!ParseGMLCoordinates(psNode, poLine.get(), nSRSDimension))
    1694             :         {
    1695           1 :             return nullptr;
    1696             :         }
    1697             : 
    1698          28 :         if (poLine->getNumPoints() != 3)
    1699             :         {
    1700           1 :             ReportFailure("Bad number of points in Circle");
    1701           1 :             return nullptr;
    1702             :         }
    1703             : 
    1704          27 :         double R = 0.0;
    1705          27 :         double cx = 0.0;
    1706          27 :         double cy = 0.0;
    1707          27 :         double alpha0 = 0.0;
    1708          27 :         double alpha1 = 0.0;
    1709          27 :         double alpha2 = 0.0;
    1710         162 :         if (!OGRGeometryFactory::GetCurveParameters(
    1711          81 :                 poLine->getX(0), poLine->getY(0), poLine->getX(1),
    1712          81 :                 poLine->getY(1), poLine->getX(2), poLine->getY(2), R, cx, cy,
    1713             :                 alpha0, alpha1, alpha2))
    1714             :         {
    1715           0 :             return nullptr;
    1716             :         }
    1717             : 
    1718          54 :         auto poCC = std::make_unique<OGRCircularString>();
    1719          54 :         OGRPoint p;
    1720          27 :         poLine->getPoint(0, &p);
    1721          27 :         poCC->addPoint(&p);
    1722          27 :         poLine->getPoint(1, &p);
    1723          27 :         poCC->addPoint(&p);
    1724          27 :         poLine->getPoint(2, &p);
    1725          27 :         poCC->addPoint(&p);
    1726          27 :         const double alpha4 =
    1727          27 :             alpha2 > alpha0 ? alpha0 + kdf2PI : alpha0 - kdf2PI;
    1728          27 :         const double alpha3 = (alpha2 + alpha4) / 2.0;
    1729          27 :         const double x = cx + R * cos(alpha3);
    1730          27 :         const double y = cy + R * sin(alpha3);
    1731          27 :         if (poCC->getCoordinateDimension() == 3)
    1732           0 :             poCC->addPoint(x, y, p.getZ());
    1733             :         else
    1734          27 :             poCC->addPoint(x, y);
    1735          27 :         poLine->getPoint(0, &p);
    1736          27 :         poCC->addPoint(&p);
    1737          27 :         return poCC;
    1738             :     }
    1739             : 
    1740             :     /* -------------------------------------------------------------------- */
    1741             :     /*      ArcByBulge                                                      */
    1742             :     /* -------------------------------------------------------------------- */
    1743        4761 :     if (EQUAL(pszBaseGeometry, "ArcByBulge"))
    1744             :     {
    1745           6 :         const CPLXMLNode *psChild = FindBareXMLChild(psNode, "bulge");
    1746           6 :         if (psChild == nullptr || psChild->eType != CXT_Element ||
    1747           4 :             psChild->psChild == nullptr)
    1748             :         {
    1749           3 :             ReportFailure("Missing bulge element.");
    1750           3 :             return nullptr;
    1751             :         }
    1752           3 :         const double dfBulge = CPLAtof(psChild->psChild->pszValue);
    1753             : 
    1754           3 :         psChild = FindBareXMLChild(psNode, "normal");
    1755           3 :         if (psChild == nullptr || psChild->eType != CXT_Element)
    1756             :         {
    1757           0 :             ReportFailure("Missing normal element.");
    1758           0 :             return nullptr;
    1759             :         }
    1760           3 :         double dfNormal = CPLAtof(psChild->psChild->pszValue);
    1761             : 
    1762           6 :         auto poLS = std::make_unique<OGRLineString>();
    1763           3 :         if (!ParseGMLCoordinates(psNode, poLS.get(), nSRSDimension))
    1764             :         {
    1765           1 :             return nullptr;
    1766             :         }
    1767             : 
    1768           2 :         if (poLS->getNumPoints() != 2)
    1769             :         {
    1770           1 :             ReportFailure("Bad number of points in ArcByBulge");
    1771           1 :             return nullptr;
    1772             :         }
    1773             : 
    1774           2 :         auto poCC = std::make_unique<OGRCircularString>();
    1775           2 :         OGRPoint p;
    1776           1 :         poLS->getPoint(0, &p);
    1777           1 :         poCC->addPoint(&p);
    1778             : 
    1779           1 :         const double dfMidX = (poLS->getX(0) + poLS->getX(1)) / 2.0;
    1780           1 :         const double dfMidY = (poLS->getY(0) + poLS->getY(1)) / 2.0;
    1781           1 :         const double dfDirX = (poLS->getX(1) - poLS->getX(0)) / 2.0;
    1782           1 :         const double dfDirY = (poLS->getY(1) - poLS->getY(0)) / 2.0;
    1783           1 :         double dfNormX = -dfDirY;
    1784           1 :         double dfNormY = dfDirX;
    1785           1 :         const double dfNorm = sqrt(dfNormX * dfNormX + dfNormY * dfNormY);
    1786           1 :         if (dfNorm != 0.0)
    1787             :         {
    1788           1 :             dfNormX /= dfNorm;
    1789           1 :             dfNormY /= dfNorm;
    1790             :         }
    1791           1 :         const double dfNewX = dfMidX + dfNormX * dfBulge * dfNormal;
    1792           1 :         const double dfNewY = dfMidY + dfNormY * dfBulge * dfNormal;
    1793             : 
    1794           1 :         if (poCC->getCoordinateDimension() == 3)
    1795           0 :             poCC->addPoint(dfNewX, dfNewY, p.getZ());
    1796             :         else
    1797           1 :             poCC->addPoint(dfNewX, dfNewY);
    1798             : 
    1799           1 :         poLS->getPoint(1, &p);
    1800           1 :         poCC->addPoint(&p);
    1801             : 
    1802           1 :         return poCC;
    1803             :     }
    1804             : 
    1805             :     /* -------------------------------------------------------------------- */
    1806             :     /*      ArcByCenterPoint                                                */
    1807             :     /* -------------------------------------------------------------------- */
    1808        4755 :     if (EQUAL(pszBaseGeometry, "ArcByCenterPoint"))
    1809             :     {
    1810          15 :         const CPLXMLNode *psChild = FindBareXMLChild(psNode, "radius");
    1811          15 :         if (psChild == nullptr || psChild->eType != CXT_Element)
    1812             :         {
    1813           1 :             ReportFailure("Missing radius element.");
    1814           1 :             return nullptr;
    1815             :         }
    1816          14 :         const char *pszUnits = CPLGetXMLValue(psChild, "uom", nullptr);
    1817          14 :         const double dfUOMConv = GetUOMInMetre(pszUnits, "radius", pszId);
    1818             :         const double dfRadiusRaw =
    1819          14 :             CPLAtof(CPLGetXMLValue(psChild, nullptr, "0"));
    1820          14 :         double dfRadius = dfUOMConv > 0 ? dfRadiusRaw * dfUOMConv : dfRadiusRaw;
    1821             : 
    1822          14 :         psChild = FindBareXMLChild(psNode, "startAngle");
    1823          14 :         if (psChild == nullptr || psChild->eType != CXT_Element)
    1824             :         {
    1825           0 :             ReportFailure("Missing startAngle element.");
    1826           0 :             return nullptr;
    1827             :         }
    1828             :         const double dfStartAngle =
    1829          14 :             CPLAtof(CPLGetXMLValue(psChild, nullptr, "0"));
    1830             : 
    1831          14 :         psChild = FindBareXMLChild(psNode, "endAngle");
    1832          14 :         if (psChild == nullptr || psChild->eType != CXT_Element)
    1833             :         {
    1834           1 :             ReportFailure("Missing endAngle element.");
    1835           1 :             return nullptr;
    1836             :         }
    1837             :         const double dfEndAngle =
    1838          13 :             CPLAtof(CPLGetXMLValue(psChild, nullptr, "0"));
    1839             : 
    1840          26 :         OGRPoint p;
    1841          13 :         if (!ParseGMLCoordinates(psNode, &p, nSRSDimension))
    1842             :         {
    1843           1 :             return nullptr;
    1844             :         }
    1845             : 
    1846          12 :         bool bSRSUnitIsDegree = false;
    1847          12 :         bool bInvertedAxisOrder = false;
    1848          12 :         double dfSemiMajor = OGR_GREATCIRCLE_DEFAULT_RADIUS;
    1849          12 :         if (pszSRSName != nullptr)
    1850             :         {
    1851          10 :             std::shared_ptr<OGRSpatialReference> poSRS;
    1852          10 :             if (!oSRSCache.tryGet(pszSRSName, poSRS))
    1853             :             {
    1854           8 :                 poSRS = std::make_shared<OGRSpatialReference>();
    1855           8 :                 if (poSRS->SetFromUserInput(
    1856             :                         pszSRSName,
    1857             :                         OGRSpatialReference::
    1858           8 :                             SET_FROM_USER_INPUT_LIMITATIONS_get()) !=
    1859             :                     OGRERR_NONE)
    1860           0 :                     poSRS.reset();
    1861           8 :                 oSRSCache.insert(pszSRSName, poSRS);
    1862             :             }
    1863          10 :             if (poSRS)
    1864             :             {
    1865          10 :                 if (poSRS->IsGeographic())
    1866             :                 {
    1867           6 :                     dfSemiMajor = GetSemiMajor(poSRS.get());
    1868             :                     bInvertedAxisOrder =
    1869           6 :                         CPL_TO_BOOL(poSRS->EPSGTreatsAsLatLong());
    1870           6 :                     bSRSUnitIsDegree = fabs(poSRS->GetAngularUnits(nullptr) -
    1871           6 :                                             CPLAtof(SRS_UA_DEGREE_CONV)) < 1e-8;
    1872             :                 }
    1873           4 :                 else if (poSRS->IsProjected())
    1874             :                 {
    1875           4 :                     dfSemiMajor = GetSemiMajor(poSRS.get());
    1876             :                     bInvertedAxisOrder =
    1877           4 :                         CPL_TO_BOOL(poSRS->EPSGTreatsAsNorthingEasting());
    1878             : 
    1879             :                     const double dfSRSUnitsToMetre =
    1880           4 :                         poSRS->GetLinearUnits(nullptr);
    1881           4 :                     if (dfSRSUnitsToMetre > 0)
    1882           4 :                         dfRadius /= dfSRSUnitsToMetre;
    1883             :                 }
    1884             :             }
    1885             :         }
    1886             : 
    1887          12 :         double dfCenterX = p.getX();
    1888          12 :         double dfCenterY = p.getY();
    1889             : 
    1890          12 :         if (bSRSUnitIsDegree && dfUOMConv > 0)
    1891             :         {
    1892          12 :             auto poLS = std::make_unique<OGRLineString>();
    1893           6 :             const double dfStep = OGRGeometryFactory::GetDefaultArcStepSize();
    1894           6 :             const double dfSign = dfStartAngle < dfEndAngle ? 1 : -1;
    1895           6 :             for (double dfAngle = dfStartAngle;
    1896         156 :                  (dfAngle - dfEndAngle) * dfSign < 0;
    1897         150 :                  dfAngle += dfSign * dfStep)
    1898             :             {
    1899         150 :                 double dfLong = 0.0;
    1900         150 :                 double dfLat = 0.0;
    1901         150 :                 if (bInvertedAxisOrder)
    1902             :                 {
    1903         138 :                     OGR_GreatCircle_ExtendPosition(
    1904             :                         dfCenterX, dfCenterY, dfRadius,
    1905             :                         // See
    1906             :                         // https://ext.eurocontrol.int/aixm_confluence/display/ACG/ArcByCenterPoint+Interpretation+Summary
    1907             :                         dfAngle, dfSemiMajor, &dfLat, &dfLong);
    1908         138 :                     p.setX(dfLat);  // yes, external code will do the swap later
    1909         138 :                     p.setY(dfLong);
    1910             :                 }
    1911             :                 else
    1912             :                 {
    1913          12 :                     OGR_GreatCircle_ExtendPosition(
    1914             :                         dfCenterY, dfCenterX, dfRadius, 90 - dfAngle,
    1915             :                         dfSemiMajor, &dfLat, &dfLong);
    1916          12 :                     p.setX(dfLong);
    1917          12 :                     p.setY(dfLat);
    1918             :                 }
    1919         150 :                 poLS->addPoint(&p);
    1920             :             }
    1921             : 
    1922           6 :             double dfLong = 0.0;
    1923           6 :             double dfLat = 0.0;
    1924           6 :             if (bInvertedAxisOrder)
    1925             :             {
    1926           5 :                 OGR_GreatCircle_ExtendPosition(dfCenterX, dfCenterY, dfRadius,
    1927             :                                                dfEndAngle, dfSemiMajor, &dfLat,
    1928             :                                                &dfLong);
    1929           5 :                 p.setX(dfLat);  // yes, external code will do the swap later
    1930           5 :                 p.setY(dfLong);
    1931             :             }
    1932             :             else
    1933             :             {
    1934           1 :                 OGR_GreatCircle_ExtendPosition(dfCenterY, dfCenterX, dfRadius,
    1935             :                                                90 - dfEndAngle, dfSemiMajor,
    1936             :                                                &dfLat, &dfLong);
    1937           1 :                 p.setX(dfLong);
    1938           1 :                 p.setY(dfLat);
    1939             :             }
    1940           6 :             poLS->addPoint(&p);
    1941             : 
    1942           6 :             return poLS;
    1943             :         }
    1944             : 
    1945           6 :         if (bInvertedAxisOrder)
    1946           1 :             std::swap(dfCenterX, dfCenterY);
    1947             : 
    1948          12 :         auto poCC = std::make_unique<OGRCircularString>();
    1949           6 :         p.setX(dfCenterX + dfRadius * cos(dfStartAngle * kdfD2R));
    1950           6 :         p.setY(dfCenterY + dfRadius * sin(dfStartAngle * kdfD2R));
    1951           6 :         poCC->addPoint(&p);
    1952           6 :         const double dfAverageAngle = (dfStartAngle + dfEndAngle) / 2.0;
    1953           6 :         p.setX(dfCenterX + dfRadius * cos(dfAverageAngle * kdfD2R));
    1954           6 :         p.setY(dfCenterY + dfRadius * sin(dfAverageAngle * kdfD2R));
    1955           6 :         poCC->addPoint(&p);
    1956           6 :         p.setX(dfCenterX + dfRadius * cos(dfEndAngle * kdfD2R));
    1957           6 :         p.setY(dfCenterY + dfRadius * sin(dfEndAngle * kdfD2R));
    1958           6 :         poCC->addPoint(&p);
    1959             : 
    1960           6 :         if (bInvertedAxisOrder)
    1961           1 :             poCC->swapXY();
    1962             : 
    1963           6 :         return poCC;
    1964             :     }
    1965             : 
    1966             :     /* -------------------------------------------------------------------- */
    1967             :     /*      CircleByCenterPoint                                             */
    1968             :     /* -------------------------------------------------------------------- */
    1969        4740 :     if (EQUAL(pszBaseGeometry, "CircleByCenterPoint"))
    1970             :     {
    1971           7 :         const CPLXMLNode *psChild = FindBareXMLChild(psNode, "radius");
    1972           7 :         if (psChild == nullptr || psChild->eType != CXT_Element)
    1973             :         {
    1974           1 :             ReportFailure("Missing radius element.");
    1975           1 :             return nullptr;
    1976             :         }
    1977           6 :         const char *pszUnits = CPLGetXMLValue(psChild, "uom", nullptr);
    1978           6 :         const double dfUOMConv = GetUOMInMetre(pszUnits, "radius", pszId);
    1979             :         const double dfRadiusRaw =
    1980           6 :             CPLAtof(CPLGetXMLValue(psChild, nullptr, "0"));
    1981           6 :         double dfRadius = dfUOMConv > 0 ? dfRadiusRaw * dfUOMConv : dfRadiusRaw;
    1982             : 
    1983          12 :         OGRPoint p;
    1984           6 :         if (!ParseGMLCoordinates(psNode, &p, nSRSDimension))
    1985             :         {
    1986           1 :             return nullptr;
    1987             :         }
    1988             : 
    1989           5 :         bool bSRSUnitIsDegree = false;
    1990           5 :         bool bInvertedAxisOrder = false;
    1991           5 :         double dfSemiMajor = OGR_GREATCIRCLE_DEFAULT_RADIUS;
    1992           5 :         if (pszSRSName != nullptr)
    1993             :         {
    1994           8 :             OGRSpatialReference oSRS;
    1995           4 :             if (oSRS.SetFromUserInput(pszSRSName) == OGRERR_NONE)
    1996             :             {
    1997           4 :                 if (oSRS.IsGeographic())
    1998             :                 {
    1999           4 :                     dfSemiMajor = GetSemiMajor(&oSRS);
    2000             :                     bInvertedAxisOrder =
    2001           4 :                         CPL_TO_BOOL(oSRS.EPSGTreatsAsLatLong());
    2002           4 :                     bSRSUnitIsDegree = fabs(oSRS.GetAngularUnits(nullptr) -
    2003           4 :                                             CPLAtof(SRS_UA_DEGREE_CONV)) < 1e-8;
    2004             :                 }
    2005           0 :                 else if (oSRS.IsProjected())
    2006             :                 {
    2007           0 :                     dfSemiMajor = GetSemiMajor(&oSRS);
    2008             :                     bInvertedAxisOrder =
    2009           0 :                         CPL_TO_BOOL(oSRS.EPSGTreatsAsNorthingEasting());
    2010             : 
    2011             :                     const double dfSRSUnitsToMetre =
    2012           0 :                         oSRS.GetLinearUnits(nullptr);
    2013           0 :                     if (dfSRSUnitsToMetre > 0)
    2014           0 :                         dfRadius /= dfSRSUnitsToMetre;
    2015             :                 }
    2016             :             }
    2017             :         }
    2018             : 
    2019           5 :         double dfCenterX = p.getX();
    2020           5 :         double dfCenterY = p.getY();
    2021             : 
    2022           5 :         if (bSRSUnitIsDegree && dfUOMConv > 0)
    2023             :         {
    2024           4 :             auto poLS = std::make_unique<OGRLineString>();
    2025           2 :             const double dfStep = OGRGeometryFactory::GetDefaultArcStepSize();
    2026         182 :             for (double dfAngle = 0; dfAngle < 360; dfAngle += dfStep)
    2027             :             {
    2028         180 :                 double dfLong = 0.0;
    2029         180 :                 double dfLat = 0.0;
    2030         180 :                 if (bInvertedAxisOrder)
    2031             :                 {
    2032          90 :                     OGR_GreatCircle_ExtendPosition(
    2033             :                         dfCenterX, dfCenterY, dfRadius, dfAngle, dfSemiMajor,
    2034             :                         &dfLat, &dfLong);
    2035          90 :                     p.setX(dfLat);  // yes, external code will do the swap later
    2036          90 :                     p.setY(dfLong);
    2037             :                 }
    2038             :                 else
    2039             :                 {
    2040          90 :                     OGR_GreatCircle_ExtendPosition(
    2041             :                         dfCenterY, dfCenterX, dfRadius, dfAngle, dfSemiMajor,
    2042             :                         &dfLat, &dfLong);
    2043          90 :                     p.setX(dfLong);
    2044          90 :                     p.setY(dfLat);
    2045             :                 }
    2046         180 :                 poLS->addPoint(&p);
    2047             :             }
    2048           2 :             poLS->getPoint(0, &p);
    2049           2 :             poLS->addPoint(&p);
    2050           2 :             return poLS;
    2051             :         }
    2052             : 
    2053           3 :         if (bInvertedAxisOrder)
    2054           2 :             std::swap(dfCenterX, dfCenterY);
    2055             : 
    2056           6 :         auto poCC = std::make_unique<OGRCircularString>();
    2057           3 :         p.setX(dfCenterX - dfRadius);
    2058           3 :         p.setY(dfCenterY);
    2059           3 :         poCC->addPoint(&p);
    2060           3 :         p.setX(dfCenterX);
    2061           3 :         p.setY(dfCenterY + dfRadius);
    2062           3 :         poCC->addPoint(&p);
    2063           3 :         p.setX(dfCenterX + dfRadius);
    2064           3 :         p.setY(dfCenterY);
    2065           3 :         poCC->addPoint(&p);
    2066           3 :         p.setX(dfCenterX);
    2067           3 :         p.setY(dfCenterY - dfRadius);
    2068           3 :         poCC->addPoint(&p);
    2069           3 :         p.setX(dfCenterX - dfRadius);
    2070           3 :         p.setY(dfCenterY);
    2071           3 :         poCC->addPoint(&p);
    2072             : 
    2073           3 :         if (bInvertedAxisOrder)
    2074           2 :             poCC->swapXY();
    2075             : 
    2076           3 :         return poCC;
    2077             :     }
    2078             : 
    2079             :     /* -------------------------------------------------------------------- */
    2080             :     /*      PointType                                                       */
    2081             :     /* -------------------------------------------------------------------- */
    2082        4733 :     if (EQUAL(pszBaseGeometry, "PointType") ||
    2083        4733 :         EQUAL(pszBaseGeometry, "Point") ||
    2084        3669 :         EQUAL(pszBaseGeometry, "ElevatedPoint") ||
    2085        3668 :         EQUAL(pszBaseGeometry, "ConnectionPoint"))
    2086             :     {
    2087        2130 :         auto poPoint = std::make_unique<OGRPoint>();
    2088             : 
    2089        1065 :         if (!ParseGMLCoordinates(psNode, poPoint.get(), nSRSDimension))
    2090             :         {
    2091          20 :             return nullptr;
    2092             :         }
    2093             : 
    2094        1045 :         return poPoint;
    2095             :     }
    2096             : 
    2097             :     /* -------------------------------------------------------------------- */
    2098             :     /*      Box                                                             */
    2099             :     /* -------------------------------------------------------------------- */
    2100        3668 :     if (EQUAL(pszBaseGeometry, "BoxType") || EQUAL(pszBaseGeometry, "Box"))
    2101             :     {
    2102          12 :         OGRLineString oPoints;
    2103             : 
    2104           6 :         if (!ParseGMLCoordinates(psNode, &oPoints, nSRSDimension))
    2105           1 :             return nullptr;
    2106             : 
    2107           5 :         if (oPoints.getNumPoints() < 2)
    2108           1 :             return nullptr;
    2109             : 
    2110           8 :         auto poBoxRing = std::make_unique<OGRLinearRing>();
    2111           8 :         auto poBoxPoly = std::make_unique<OGRPolygon>();
    2112             : 
    2113           4 :         poBoxRing->setNumPoints(5);
    2114           4 :         poBoxRing->setPoint(0, oPoints.getX(0), oPoints.getY(0),
    2115             :                             oPoints.getZ(0));
    2116           4 :         poBoxRing->setPoint(1, oPoints.getX(1), oPoints.getY(0),
    2117             :                             oPoints.getZ(0));
    2118           4 :         poBoxRing->setPoint(2, oPoints.getX(1), oPoints.getY(1),
    2119             :                             oPoints.getZ(1));
    2120           4 :         poBoxRing->setPoint(3, oPoints.getX(0), oPoints.getY(1),
    2121             :                             oPoints.getZ(0));
    2122           4 :         poBoxRing->setPoint(4, oPoints.getX(0), oPoints.getY(0),
    2123             :                             oPoints.getZ(0));
    2124           4 :         poBoxRing->set3D(oPoints.Is3D());
    2125             : 
    2126           4 :         poBoxPoly->addRing(std::move(poBoxRing));
    2127             : 
    2128           4 :         return poBoxPoly;
    2129             :     }
    2130             : 
    2131             :     /* -------------------------------------------------------------------- */
    2132             :     /*      Envelope                                                        */
    2133             :     /* -------------------------------------------------------------------- */
    2134        3662 :     if (EQUAL(pszBaseGeometry, "Envelope"))
    2135             :     {
    2136             :         const CPLXMLNode *psLowerCorner =
    2137          26 :             FindBareXMLChild(psNode, "lowerCorner");
    2138             :         const CPLXMLNode *psUpperCorner =
    2139          26 :             FindBareXMLChild(psNode, "upperCorner");
    2140          26 :         if (psLowerCorner == nullptr || psUpperCorner == nullptr)
    2141           2 :             return nullptr;
    2142          24 :         const char *pszLowerCorner = GetElementText(psLowerCorner);
    2143          24 :         const char *pszUpperCorner = GetElementText(psUpperCorner);
    2144          24 :         if (pszLowerCorner == nullptr || pszUpperCorner == nullptr)
    2145           1 :             return nullptr;
    2146          23 :         char **papszLowerCorner = CSLTokenizeString(pszLowerCorner);
    2147          23 :         char **papszUpperCorner = CSLTokenizeString(pszUpperCorner);
    2148          23 :         const int nTokenCountLC = CSLCount(papszLowerCorner);
    2149          23 :         const int nTokenCountUC = CSLCount(papszUpperCorner);
    2150          23 :         if (nTokenCountLC < 2 || nTokenCountUC < 2)
    2151             :         {
    2152           0 :             CSLDestroy(papszLowerCorner);
    2153           0 :             CSLDestroy(papszUpperCorner);
    2154           0 :             return nullptr;
    2155             :         }
    2156             : 
    2157          23 :         const double dfLLX = CPLAtof(papszLowerCorner[0]);
    2158          23 :         const double dfLLY = CPLAtof(papszLowerCorner[1]);
    2159          23 :         const double dfURX = CPLAtof(papszUpperCorner[0]);
    2160          23 :         const double dfURY = CPLAtof(papszUpperCorner[1]);
    2161          23 :         CSLDestroy(papszLowerCorner);
    2162          23 :         CSLDestroy(papszUpperCorner);
    2163             : 
    2164          46 :         auto poEnvelopeRing = std::make_unique<OGRLinearRing>();
    2165          46 :         auto poPoly = std::make_unique<OGRPolygon>();
    2166             : 
    2167          23 :         poEnvelopeRing->setNumPoints(5);
    2168          23 :         poEnvelopeRing->setPoint(0, dfLLX, dfLLY);
    2169          23 :         poEnvelopeRing->setPoint(1, dfURX, dfLLY);
    2170          23 :         poEnvelopeRing->setPoint(2, dfURX, dfURY);
    2171          23 :         poEnvelopeRing->setPoint(3, dfLLX, dfURY);
    2172          23 :         poEnvelopeRing->setPoint(4, dfLLX, dfLLY);
    2173          23 :         poPoly->addRing(std::move(poEnvelopeRing));
    2174             : 
    2175          23 :         return poPoly;
    2176             :     }
    2177             : 
    2178             :     /* --------------------------------------------------------------------- */
    2179             :     /*      MultiPolygon / MultiSurface / CompositeSurface                   */
    2180             :     /*                                                                       */
    2181             :     /* For CompositeSurface, this is a very rough approximation to deal with */
    2182             :     /* it as a MultiPolygon, because it can several faces of a 3D volume.    */
    2183             :     /* --------------------------------------------------------------------- */
    2184        3636 :     if (EQUAL(pszBaseGeometry, "MultiPolygon") ||
    2185        3614 :         EQUAL(pszBaseGeometry, "MultiSurface") ||
    2186        3546 :         EQUAL(pszBaseGeometry, "Shell") ||  // CityGML 3 uses this
    2187        3546 :         EQUAL(pszBaseGeometry, "CompositeSurface"))
    2188             :     {
    2189             :         std::unique_ptr<OGRMultiSurface> poMS =
    2190          91 :             EQUAL(pszBaseGeometry, "MultiPolygon")
    2191         113 :                 ? std::make_unique<OGRMultiPolygon>()
    2192         204 :                 : std::make_unique<OGRMultiSurface>();
    2193          91 :         bool bReconstructTopology = false;
    2194          91 :         bool bChildrenAreAllPolygons = true;
    2195             : 
    2196             :         // Iterate over children.
    2197         253 :         for (const CPLXMLNode *psChild = psNode->psChild; psChild != nullptr;
    2198         162 :              psChild = psChild->psNext)
    2199             :         {
    2200         164 :             const char *pszMemberElement = BareGMLElement(psChild->pszValue);
    2201         164 :             if (psChild->eType == CXT_Element &&
    2202          97 :                 (EQUAL(pszMemberElement, "polygonMember") ||
    2203          76 :                  EQUAL(pszMemberElement, "surfaceMember")))
    2204             :             {
    2205          91 :                 const CPLXMLNode *psSurfaceChild = GetChildElement(psChild);
    2206             : 
    2207          91 :                 if (psSurfaceChild != nullptr)
    2208             :                 {
    2209             :                     // Cf #5421 where there are PolygonPatch with only inner
    2210             :                     // rings.
    2211             :                     const CPLXMLNode *psPolygonPatch =
    2212          90 :                         GetChildElement(GetChildElement(psSurfaceChild));
    2213          90 :                     const CPLXMLNode *psPolygonPatchChild = nullptr;
    2214         178 :                     if (psPolygonPatch != nullptr &&
    2215          88 :                         psPolygonPatch->eType == CXT_Element &&
    2216          88 :                         EQUAL(BareGMLElement(psPolygonPatch->pszValue),
    2217          13 :                               "PolygonPatch") &&
    2218             :                         (psPolygonPatchChild =
    2219         191 :                              GetChildElement(psPolygonPatch)) != nullptr &&
    2220          13 :                         EQUAL(BareGMLElement(psPolygonPatchChild->pszValue),
    2221             :                               "interior"))
    2222             :                     {
    2223             :                         // Find all inner rings
    2224           1 :                         for (const CPLXMLNode *psChild2 =
    2225             :                                  psPolygonPatch->psChild;
    2226           2 :                              psChild2 != nullptr; psChild2 = psChild2->psNext)
    2227             :                         {
    2228           2 :                             if (psChild2->eType == CXT_Element &&
    2229           1 :                                 (EQUAL(BareGMLElement(psChild2->pszValue),
    2230             :                                        "interior")))
    2231             :                             {
    2232             :                                 const CPLXMLNode *psInteriorChild =
    2233           1 :                                     GetChildElement(psChild2);
    2234             :                                 auto poRing =
    2235             :                                     psInteriorChild == nullptr
    2236             :                                         ? nullptr
    2237             :                                         : GML2OGRGeometry_XMLNode_Internal(
    2238             :                                               psInteriorChild, pszId,
    2239             :                                               nPseudoBoolGetSecondaryGeometryOption,
    2240             :                                               nRecLevel + 1, nSRSDimension,
    2241           1 :                                               pszSRSName, oSRSCache);
    2242           1 :                                 if (poRing == nullptr)
    2243             :                                 {
    2244           0 :                                     ReportFailure("Invalid interior ring");
    2245           0 :                                     return nullptr;
    2246             :                                 }
    2247           1 :                                 if (!EQUAL(poRing->getGeometryName(),
    2248             :                                            "LINEARRING"))
    2249             :                                 {
    2250           0 :                                     ReportFailure("%s: Got %s geometry as "
    2251             :                                                   "innerBoundaryIs instead of "
    2252             :                                                   "LINEARRING.",
    2253             :                                                   pszBaseGeometry,
    2254             :                                                   poRing->getGeometryName());
    2255           0 :                                     return nullptr;
    2256             :                                 }
    2257             : 
    2258           1 :                                 bReconstructTopology = true;
    2259           2 :                                 auto poPolygon = std::make_unique<OGRPolygon>();
    2260             :                                 auto poLinearRing =
    2261             :                                     std::unique_ptr<OGRLinearRing>(
    2262           1 :                                         poRing.release()->toLinearRing());
    2263           1 :                                 poPolygon->addRing(std::move(poLinearRing));
    2264           1 :                                 poMS->addGeometry(std::move(poPolygon));
    2265             :                             }
    2266             :                         }
    2267             :                     }
    2268             :                     else
    2269             :                     {
    2270             :                         auto poGeom = GML2OGRGeometry_XMLNode_Internal(
    2271             :                             psSurfaceChild, pszId,
    2272             :                             nPseudoBoolGetSecondaryGeometryOption,
    2273             :                             nRecLevel + 1, nSRSDimension, pszSRSName,
    2274          89 :                             oSRSCache);
    2275          89 :                         if (!GML2OGRGeometry_AddToMultiSurface(
    2276          89 :                                 poMS.get(), std::move(poGeom), pszMemberElement,
    2277             :                                 bChildrenAreAllPolygons))
    2278             :                         {
    2279           2 :                             return nullptr;
    2280             :                         }
    2281             :                     }
    2282          89 :                 }
    2283             :             }
    2284          73 :             else if (psChild->eType == CXT_Element &&
    2285           6 :                      EQUAL(pszMemberElement, "surfaceMembers"))
    2286             :             {
    2287           5 :                 for (const CPLXMLNode *psChild2 = psChild->psChild;
    2288          11 :                      psChild2 != nullptr; psChild2 = psChild2->psNext)
    2289             :                 {
    2290           6 :                     pszMemberElement = BareGMLElement(psChild2->pszValue);
    2291           6 :                     if (psChild2->eType == CXT_Element &&
    2292           5 :                         (EQUAL(pszMemberElement, "Surface") ||
    2293           3 :                          EQUAL(pszMemberElement, "Polygon") ||
    2294           2 :                          EQUAL(pszMemberElement, "PolygonPatch") ||
    2295           2 :                          EQUAL(pszMemberElement,
    2296           2 :                                "Shell") ||  // CityGML 3 uses this
    2297           2 :                          EQUAL(pszMemberElement, "CompositeSurface")))
    2298             :                     {
    2299             :                         auto poGeom = GML2OGRGeometry_XMLNode_Internal(
    2300             :                             psChild2, pszId,
    2301             :                             nPseudoBoolGetSecondaryGeometryOption,
    2302             :                             nRecLevel + 1, nSRSDimension, pszSRSName,
    2303           4 :                             oSRSCache);
    2304           4 :                         if (!GML2OGRGeometry_AddToMultiSurface(
    2305           4 :                                 poMS.get(), std::move(poGeom), pszMemberElement,
    2306             :                                 bChildrenAreAllPolygons))
    2307             :                         {
    2308           0 :                             return nullptr;
    2309             :                         }
    2310             :                     }
    2311             :                 }
    2312             :             }
    2313             :         }
    2314             : 
    2315          89 :         if (bReconstructTopology && bChildrenAreAllPolygons)
    2316             :         {
    2317             :             auto poMPoly =
    2318           1 :                 wkbFlatten(poMS->getGeometryType()) == wkbMultiSurface
    2319             :                     ? std::unique_ptr<OGRMultiPolygon>(
    2320             :                           OGRMultiSurface::CastToMultiPolygon(poMS.release()))
    2321             :                     : std::unique_ptr<OGRMultiPolygon>(
    2322           2 :                           poMS.release()->toMultiPolygon());
    2323           1 :             const int nPolygonCount = poMPoly->getNumGeometries();
    2324           2 :             std::vector<OGRGeometry *> apoPolygons;
    2325           1 :             apoPolygons.reserve(nPolygonCount);
    2326           4 :             for (int i = 0; i < nPolygonCount; i++)
    2327             :             {
    2328           3 :                 apoPolygons.emplace_back(poMPoly->getGeometryRef(0));
    2329           3 :                 poMPoly->removeGeometry(0, FALSE);
    2330             :             }
    2331           1 :             poMPoly.reset();
    2332           1 :             int bResultValidGeometry = FALSE;
    2333             :             return std::unique_ptr<OGRGeometry>(
    2334             :                 OGRGeometryFactory::organizePolygons(
    2335           1 :                     apoPolygons.data(), nPolygonCount, &bResultValidGeometry));
    2336             :         }
    2337             :         else
    2338             :         {
    2339          88 :             if (/* bCastToLinearTypeIfPossible && */
    2340          88 :                 wkbFlatten(poMS->getGeometryType()) == wkbMultiSurface &&
    2341             :                 bChildrenAreAllPolygons)
    2342             :             {
    2343         114 :                 return std::unique_ptr<OGRMultiPolygon>(
    2344          57 :                     OGRMultiSurface::CastToMultiPolygon(poMS.release()));
    2345             :             }
    2346             : 
    2347          31 :             return poMS;
    2348             :         }
    2349             :     }
    2350             : 
    2351             :     /* -------------------------------------------------------------------- */
    2352             :     /*      MultiPoint                                                      */
    2353             :     /* -------------------------------------------------------------------- */
    2354        3545 :     if (EQUAL(pszBaseGeometry, "MultiPoint"))
    2355             :     {
    2356          64 :         auto poMP = std::make_unique<OGRMultiPoint>();
    2357             : 
    2358             :         // Collect points.
    2359         112 :         for (const CPLXMLNode *psChild = psNode->psChild; psChild != nullptr;
    2360          80 :              psChild = psChild->psNext)
    2361             :         {
    2362         131 :             if (psChild->eType == CXT_Element &&
    2363          49 :                 EQUAL(BareGMLElement(psChild->pszValue), "pointMember"))
    2364             :             {
    2365          41 :                 const CPLXMLNode *psPointChild = GetChildElement(psChild);
    2366             : 
    2367          41 :                 if (psPointChild != nullptr)
    2368             :                 {
    2369             :                     auto poPointMember = GML2OGRGeometry_XMLNode_Internal(
    2370             :                         psPointChild, pszId,
    2371             :                         nPseudoBoolGetSecondaryGeometryOption, nRecLevel + 1,
    2372          40 :                         nSRSDimension, pszSRSName, oSRSCache);
    2373          80 :                     if (poPointMember == nullptr ||
    2374          40 :                         wkbFlatten(poPointMember->getGeometryType()) !=
    2375             :                             wkbPoint)
    2376             :                     {
    2377           1 :                         ReportFailure("MultiPoint: Got %s geometry as "
    2378             :                                       "pointMember instead of POINT",
    2379             :                                       poPointMember
    2380             :                                           ? poPointMember->getGeometryName()
    2381             :                                           : "NULL");
    2382           1 :                         return nullptr;
    2383             :                     }
    2384             : 
    2385          39 :                     poMP->addGeometry(std::move(poPointMember));
    2386             :                 }
    2387             :             }
    2388          49 :             else if (psChild->eType == CXT_Element &&
    2389           8 :                      EQUAL(BareGMLElement(psChild->pszValue), "pointMembers"))
    2390             :             {
    2391           7 :                 for (const CPLXMLNode *psChild2 = psChild->psChild;
    2392          14 :                      psChild2 != nullptr; psChild2 = psChild2->psNext)
    2393             :                 {
    2394          15 :                     if (psChild2->eType == CXT_Element &&
    2395           7 :                         (EQUAL(BareGMLElement(psChild2->pszValue), "Point")))
    2396             :                     {
    2397             :                         auto poGeom = GML2OGRGeometry_XMLNode_Internal(
    2398             :                             psChild2, pszId,
    2399             :                             nPseudoBoolGetSecondaryGeometryOption,
    2400             :                             nRecLevel + 1, nSRSDimension, pszSRSName,
    2401           6 :                             oSRSCache);
    2402           6 :                         if (poGeom == nullptr)
    2403             :                         {
    2404           1 :                             ReportFailure("Invalid %s",
    2405             :                                           BareGMLElement(psChild2->pszValue));
    2406           1 :                             return nullptr;
    2407             :                         }
    2408             : 
    2409           5 :                         if (wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
    2410             :                         {
    2411             :                             auto poPoint = std::unique_ptr<OGRPoint>(
    2412           5 :                                 poGeom.release()->toPoint());
    2413           5 :                             poMP->addGeometry(std::move(poPoint));
    2414             :                         }
    2415             :                         else
    2416             :                         {
    2417           0 :                             ReportFailure("Got %s geometry as pointMember "
    2418             :                                           "instead of POINT.",
    2419             :                                           poGeom->getGeometryName());
    2420           0 :                             return nullptr;
    2421             :                         }
    2422             :                     }
    2423             :                 }
    2424             :             }
    2425             :         }
    2426             : 
    2427          30 :         return poMP;
    2428             :     }
    2429             : 
    2430             :     /* -------------------------------------------------------------------- */
    2431             :     /*      MultiLineString                                                 */
    2432             :     /* -------------------------------------------------------------------- */
    2433        3513 :     if (EQUAL(pszBaseGeometry, "MultiLineString"))
    2434             :     {
    2435          36 :         auto poMLS = std::make_unique<OGRMultiLineString>();
    2436             : 
    2437             :         // Collect lines.
    2438          49 :         for (const CPLXMLNode *psChild = psNode->psChild; psChild != nullptr;
    2439          31 :              psChild = psChild->psNext)
    2440             :         {
    2441          53 :             if (psChild->eType == CXT_Element &&
    2442          20 :                 EQUAL(BareGMLElement(psChild->pszValue), "lineStringMember"))
    2443             :             {
    2444          19 :                 const CPLXMLNode *psLineStringChild = GetChildElement(psChild);
    2445             :                 auto poGeom = psLineStringChild == nullptr
    2446             :                                   ? nullptr
    2447             :                                   : GML2OGRGeometry_XMLNode_Internal(
    2448             :                                         psLineStringChild, pszId,
    2449             :                                         nPseudoBoolGetSecondaryGeometryOption,
    2450             :                                         nRecLevel + 1, nSRSDimension,
    2451          19 :                                         pszSRSName, oSRSCache);
    2452          37 :                 if (poGeom == nullptr ||
    2453          18 :                     wkbFlatten(poGeom->getGeometryType()) != wkbLineString)
    2454             :                 {
    2455           2 :                     ReportFailure("MultiLineString: Got %s geometry as Member "
    2456             :                                   "instead of LINESTRING.",
    2457             :                                   poGeom ? poGeom->getGeometryName() : "NULL");
    2458           2 :                     return nullptr;
    2459             :                 }
    2460             : 
    2461          17 :                 poMLS->addGeometry(std::move(poGeom));
    2462             :             }
    2463             :         }
    2464             : 
    2465          16 :         return poMLS;
    2466             :     }
    2467             : 
    2468             :     /* -------------------------------------------------------------------- */
    2469             :     /*      MultiCurve                                                      */
    2470             :     /* -------------------------------------------------------------------- */
    2471        3495 :     if (EQUAL(pszBaseGeometry, "MultiCurve"))
    2472             :     {
    2473         116 :         auto poMC = std::make_unique<OGRMultiCurve>();
    2474          58 :         bool bChildrenAreAllLineString = true;
    2475             : 
    2476             :         // Collect curveMembers.
    2477         141 :         for (const CPLXMLNode *psChild = psNode->psChild; psChild != nullptr;
    2478          83 :              psChild = psChild->psNext)
    2479             :         {
    2480         151 :             if (psChild->eType == CXT_Element &&
    2481          62 :                 EQUAL(BareGMLElement(psChild->pszValue), "curveMember"))
    2482             :             {
    2483          55 :                 const CPLXMLNode *psChild2 = GetChildElement(psChild);
    2484          55 :                 if (psChild2 != nullptr)  // Empty curveMember is valid.
    2485             :                 {
    2486             :                     auto poGeom = GML2OGRGeometry_XMLNode_Internal(
    2487             :                         psChild2, pszId, nPseudoBoolGetSecondaryGeometryOption,
    2488          54 :                         nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache);
    2489         104 :                     if (poGeom == nullptr ||
    2490          50 :                         !OGR_GT_IsCurve(poGeom->getGeometryType()))
    2491             :                     {
    2492           4 :                         ReportFailure("MultiCurve: Got %s geometry as Member "
    2493             :                                       "instead of a curve.",
    2494             :                                       poGeom ? poGeom->getGeometryName()
    2495             :                                              : "NULL");
    2496           4 :                         return nullptr;
    2497             :                     }
    2498             : 
    2499          50 :                     if (wkbFlatten(poGeom->getGeometryType()) != wkbLineString)
    2500          12 :                         bChildrenAreAllLineString = false;
    2501             : 
    2502          50 :                     if (poMC->addGeometry(std::move(poGeom)) != OGRERR_NONE)
    2503             :                     {
    2504           0 :                         return nullptr;
    2505             :                     }
    2506             :                 }
    2507             :             }
    2508          41 :             else if (psChild->eType == CXT_Element &&
    2509           7 :                      EQUAL(BareGMLElement(psChild->pszValue), "curveMembers"))
    2510             :             {
    2511           6 :                 for (const CPLXMLNode *psChild2 = psChild->psChild;
    2512          10 :                      psChild2 != nullptr; psChild2 = psChild2->psNext)
    2513             :                 {
    2514           6 :                     if (psChild2->eType == CXT_Element)
    2515             :                     {
    2516             :                         auto poGeom = GML2OGRGeometry_XMLNode_Internal(
    2517             :                             psChild2, pszId,
    2518             :                             nPseudoBoolGetSecondaryGeometryOption,
    2519             :                             nRecLevel + 1, nSRSDimension, pszSRSName,
    2520           5 :                             oSRSCache);
    2521           8 :                         if (poGeom == nullptr ||
    2522           3 :                             !OGR_GT_IsCurve(poGeom->getGeometryType()))
    2523             :                         {
    2524           2 :                             ReportFailure("MultiCurve: Got %s geometry as "
    2525             :                                           "Member instead of a curve.",
    2526             :                                           poGeom ? poGeom->getGeometryName()
    2527             :                                                  : "NULL");
    2528           2 :                             return nullptr;
    2529             :                         }
    2530             : 
    2531           3 :                         if (wkbFlatten(poGeom->getGeometryType()) !=
    2532             :                             wkbLineString)
    2533           0 :                             bChildrenAreAllLineString = false;
    2534             : 
    2535           3 :                         if (poMC->addGeometry(std::move(poGeom)) != OGRERR_NONE)
    2536             :                         {
    2537           0 :                             return nullptr;
    2538             :                         }
    2539             :                     }
    2540             :                 }
    2541             :             }
    2542             :         }
    2543             : 
    2544          52 :         if (/* bCastToLinearTypeIfPossible && */ bChildrenAreAllLineString)
    2545             :         {
    2546          80 :             return std::unique_ptr<OGRMultiLineString>(
    2547          40 :                 OGRMultiCurve::CastToMultiLineString(poMC.release()));
    2548             :         }
    2549             : 
    2550          12 :         return poMC;
    2551             :     }
    2552             : 
    2553             :     /* -------------------------------------------------------------------- */
    2554             :     /*      CompositeCurve                                                  */
    2555             :     /* -------------------------------------------------------------------- */
    2556        3437 :     if (EQUAL(pszBaseGeometry, "CompositeCurve"))
    2557             :     {
    2558          36 :         auto poCC = std::make_unique<OGRCompoundCurve>();
    2559          18 :         bool bChildrenAreAllLineString = true;
    2560             : 
    2561             :         // Collect curveMembers.
    2562          41 :         for (const CPLXMLNode *psChild = psNode->psChild; psChild != nullptr;
    2563          23 :              psChild = psChild->psNext)
    2564             :         {
    2565          47 :             if (psChild->eType == CXT_Element &&
    2566          23 :                 EQUAL(BareGMLElement(psChild->pszValue), "curveMember"))
    2567             :             {
    2568          22 :                 const CPLXMLNode *psChild2 = GetChildElement(psChild);
    2569          22 :                 if (psChild2 != nullptr)  // Empty curveMember is valid.
    2570             :                 {
    2571             :                     auto poGeom = GML2OGRGeometry_XMLNode_Internal(
    2572             :                         psChild2, pszId, nPseudoBoolGetSecondaryGeometryOption,
    2573          22 :                         nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache);
    2574          22 :                     if (!GML2OGRGeometry_AddToCompositeCurve(
    2575          22 :                             poCC.get(), std::move(poGeom),
    2576             :                             bChildrenAreAllLineString))
    2577             :                     {
    2578           1 :                         return nullptr;
    2579             :                     }
    2580             :                 }
    2581             :             }
    2582           3 :             else if (psChild->eType == CXT_Element &&
    2583           1 :                      EQUAL(BareGMLElement(psChild->pszValue), "curveMembers"))
    2584             :             {
    2585           1 :                 for (const CPLXMLNode *psChild2 = psChild->psChild;
    2586           4 :                      psChild2 != nullptr; psChild2 = psChild2->psNext)
    2587             :                 {
    2588           3 :                     if (psChild2->eType == CXT_Element)
    2589             :                     {
    2590             :                         auto poGeom = GML2OGRGeometry_XMLNode_Internal(
    2591             :                             psChild2, pszId,
    2592             :                             nPseudoBoolGetSecondaryGeometryOption,
    2593             :                             nRecLevel + 1, nSRSDimension, pszSRSName,
    2594           2 :                             oSRSCache);
    2595           2 :                         if (!GML2OGRGeometry_AddToCompositeCurve(
    2596           2 :                                 poCC.get(), std::move(poGeom),
    2597             :                                 bChildrenAreAllLineString))
    2598             :                         {
    2599           0 :                             return nullptr;
    2600             :                         }
    2601             :                     }
    2602             :                 }
    2603             :             }
    2604             :         }
    2605             : 
    2606          17 :         if (/* bCastToLinearTypeIfPossible && */ bChildrenAreAllLineString)
    2607             :         {
    2608          10 :             return std::unique_ptr<OGRLineString>(
    2609          10 :                 OGRCurve::CastToLineString(poCC.release()));
    2610             :         }
    2611             : 
    2612          12 :         return poCC;
    2613             :     }
    2614             : 
    2615             :     /* -------------------------------------------------------------------- */
    2616             :     /*      Curve                                                           */
    2617             :     /* -------------------------------------------------------------------- */
    2618        3419 :     if (EQUAL(pszBaseGeometry, "Curve") ||
    2619        2667 :         EQUAL(pszBaseGeometry, "ElevatedCurve") /* AIXM */)
    2620             :     {
    2621         758 :         const CPLXMLNode *psChild = FindBareXMLChild(psNode, "segments");
    2622         758 :         if (psChild == nullptr)
    2623             :         {
    2624           5 :             ReportFailure("GML3 Curve geometry lacks segments element.");
    2625           5 :             return nullptr;
    2626             :         }
    2627             : 
    2628             :         auto poGeom = GML2OGRGeometry_XMLNode_Internal(
    2629             :             psChild, pszId, nPseudoBoolGetSecondaryGeometryOption,
    2630        1506 :             nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache);
    2631         753 :         if (poGeom == nullptr || !OGR_GT_IsCurve(poGeom->getGeometryType()))
    2632             :         {
    2633           2 :             ReportFailure(
    2634             :                 "Curve: Got %s geometry as Member instead of segments.",
    2635             :                 poGeom ? poGeom->getGeometryName() : "NULL");
    2636           2 :             return nullptr;
    2637             :         }
    2638             : 
    2639         751 :         return poGeom;
    2640             :     }
    2641             : 
    2642             :     /* -------------------------------------------------------------------- */
    2643             :     /*      segments                                                        */
    2644             :     /* -------------------------------------------------------------------- */
    2645        2661 :     if (EQUAL(pszBaseGeometry, "segments"))
    2646             :     {
    2647         757 :         std::unique_ptr<OGRCurve> poCurve;
    2648         757 :         std::unique_ptr<OGRCompoundCurve> poCC;
    2649         757 :         bool bChildrenAreAllLineString = true;
    2650             : 
    2651         757 :         bool bLastCurveWasApproximateArc = false;
    2652         757 :         bool bLastCurveWasApproximateArcInvertedAxisOrder = false;
    2653         757 :         double dfLastCurveApproximateArcRadius = 0.0;
    2654         757 :         double dfSemiMajor = OGR_GREATCIRCLE_DEFAULT_RADIUS;
    2655             : 
    2656        1531 :         for (const CPLXMLNode *psChild = psNode->psChild; psChild != nullptr;
    2657         774 :              psChild = psChild->psNext)
    2658             : 
    2659             :         {
    2660         780 :             if (psChild->eType == CXT_Element
    2661             :                 // && (EQUAL(BareGMLElement(psChild->pszValue),
    2662             :                 //           "LineStringSegment") ||
    2663             :                 //     EQUAL(BareGMLElement(psChild->pszValue),
    2664             :                 //           "GeodesicString") ||
    2665             :                 //    EQUAL(BareGMLElement(psChild->pszValue), "Arc") ||
    2666             :                 //    EQUAL(BareGMLElement(psChild->pszValue), "Circle"))
    2667             :             )
    2668             :             {
    2669             :                 auto poGeom = GML2OGRGeometry_XMLNode_Internal(
    2670             :                     psChild, pszId, nPseudoBoolGetSecondaryGeometryOption,
    2671         779 :                     nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache);
    2672        1553 :                 if (poGeom == nullptr ||
    2673         774 :                     !OGR_GT_IsCurve(poGeom->getGeometryType()))
    2674             :                 {
    2675           6 :                     ReportFailure("segments: Got %s geometry as Member "
    2676             :                                   "instead of curve.",
    2677             :                                   poGeom ? poGeom->getGeometryName() : "NULL");
    2678           6 :                     return nullptr;
    2679             :                 }
    2680             : 
    2681             :                 // Ad-hoc logic to handle nicely connecting ArcByCenterPoint
    2682             :                 // with consecutive curves, as found in some AIXM files.
    2683         773 :                 bool bIsApproximateArc = false;
    2684         773 :                 if (strcmp(BareGMLElement(psChild->pszValue),
    2685         773 :                            "ArcByCenterPoint") == 0)
    2686             :                 {
    2687           7 :                     storeArcByCenterPointParameters(
    2688             :                         psChild, pszSRSName, bIsApproximateArc,
    2689             :                         dfLastCurveApproximateArcRadius,
    2690             :                         bLastCurveWasApproximateArcInvertedAxisOrder,
    2691             :                         dfSemiMajor);
    2692             :                 }
    2693             : 
    2694         773 :                 if (wkbFlatten(poGeom->getGeometryType()) != wkbLineString)
    2695          70 :                     bChildrenAreAllLineString = false;
    2696             : 
    2697         773 :                 if (poCC == nullptr && poCurve == nullptr)
    2698             :                 {
    2699         750 :                     poCurve.reset(poGeom.release()->toCurve());
    2700             :                 }
    2701             :                 else
    2702             :                 {
    2703          23 :                     if (poCC == nullptr)
    2704             :                     {
    2705          13 :                         poCC = std::make_unique<OGRCompoundCurve>();
    2706          13 :                         if (poCC->addCurve(std::move(poCurve)) != OGRERR_NONE)
    2707             :                         {
    2708           0 :                             return nullptr;
    2709             :                         }
    2710          13 :                         poCurve.reset();
    2711             :                     }
    2712             : 
    2713          23 :                     connectArcByCenterPointToOtherSegments(
    2714             :                         poGeom.get(), poCC.get(), bIsApproximateArc,
    2715             :                         bLastCurveWasApproximateArc,
    2716             :                         dfLastCurveApproximateArcRadius,
    2717             :                         bLastCurveWasApproximateArcInvertedAxisOrder,
    2718             :                         dfSemiMajor);
    2719             : 
    2720             :                     auto poAsCurve =
    2721          23 :                         std::unique_ptr<OGRCurve>(poGeom.release()->toCurve());
    2722          23 :                     if (poCC->addCurve(std::move(poAsCurve)) != OGRERR_NONE)
    2723             :                     {
    2724           0 :                         return nullptr;
    2725             :                     }
    2726             :                 }
    2727             : 
    2728         773 :                 bLastCurveWasApproximateArc = bIsApproximateArc;
    2729             :             }
    2730             :         }
    2731             : 
    2732         751 :         if (poCurve != nullptr)
    2733         737 :             return poCurve;
    2734          14 :         if (poCC == nullptr)
    2735           1 :             return std::make_unique<OGRLineString>();
    2736             : 
    2737          13 :         if (/* bCastToLinearTypeIfPossible && */ bChildrenAreAllLineString)
    2738             :         {
    2739           6 :             return std::unique_ptr<OGRLineString>(
    2740           6 :                 OGRCurve::CastToLineString(poCC.release()));
    2741             :         }
    2742             : 
    2743          10 :         return poCC;
    2744             :     }
    2745             : 
    2746             :     /* -------------------------------------------------------------------- */
    2747             :     /*      MultiGeometry                                                   */
    2748             :     /* CAUTION: OGR < 1.8.0 produced GML with GeometryCollection, which is  */
    2749             :     /* not a valid GML 2 keyword! The right name is MultiGeometry. Let's be */
    2750             :     /* tolerant with the non compliant files we produced.                   */
    2751             :     /* -------------------------------------------------------------------- */
    2752        1904 :     if (EQUAL(pszBaseGeometry, "MultiGeometry") ||
    2753        1819 :         EQUAL(pszBaseGeometry, "GeometryCollection"))
    2754             :     {
    2755         170 :         auto poGC = std::make_unique<OGRGeometryCollection>();
    2756             : 
    2757             :         // Collect geoms.
    2758         156 :         for (const CPLXMLNode *psChild = psNode->psChild; psChild != nullptr;
    2759          71 :              psChild = psChild->psNext)
    2760             :         {
    2761         196 :             if (psChild->eType == CXT_Element &&
    2762          91 :                 EQUAL(BareGMLElement(psChild->pszValue), "geometryMember"))
    2763             :             {
    2764          88 :                 const CPLXMLNode *psGeometryChild = GetChildElement(psChild);
    2765             : 
    2766          88 :                 if (psGeometryChild != nullptr)
    2767             :                 {
    2768             :                     auto poGeom = GML2OGRGeometry_XMLNode_Internal(
    2769             :                         psGeometryChild, pszId,
    2770             :                         nPseudoBoolGetSecondaryGeometryOption, nRecLevel + 1,
    2771          87 :                         nSRSDimension, pszSRSName, oSRSCache);
    2772          87 :                     if (poGeom == nullptr)
    2773             :                     {
    2774          33 :                         ReportFailure(
    2775             :                             "GeometryCollection: Failed to get geometry "
    2776             :                             "in geometryMember");
    2777          33 :                         return nullptr;
    2778             :                     }
    2779             : 
    2780          54 :                     poGC->addGeometry(std::move(poGeom));
    2781             :                 }
    2782             :             }
    2783          20 :             else if (psChild->eType == CXT_Element &&
    2784           3 :                      EQUAL(BareGMLElement(psChild->pszValue),
    2785             :                            "geometryMembers"))
    2786             :             {
    2787           2 :                 for (const CPLXMLNode *psChild2 = psChild->psChild;
    2788           4 :                      psChild2 != nullptr; psChild2 = psChild2->psNext)
    2789             :                 {
    2790           3 :                     if (psChild2->eType == CXT_Element)
    2791             :                     {
    2792             :                         auto poGeom = GML2OGRGeometry_XMLNode_Internal(
    2793             :                             psChild2, pszId,
    2794             :                             nPseudoBoolGetSecondaryGeometryOption,
    2795             :                             nRecLevel + 1, nSRSDimension, pszSRSName,
    2796           2 :                             oSRSCache);
    2797           2 :                         if (poGeom == nullptr)
    2798             :                         {
    2799           1 :                             ReportFailure(
    2800             :                                 "GeometryCollection: Failed to get geometry "
    2801             :                                 "in geometryMember");
    2802           1 :                             return nullptr;
    2803             :                         }
    2804             : 
    2805           1 :                         poGC->addGeometry(std::move(poGeom));
    2806             :                     }
    2807             :                 }
    2808             :             }
    2809             :         }
    2810             : 
    2811          51 :         return poGC;
    2812             :     }
    2813             : 
    2814             :     /* -------------------------------------------------------------------- */
    2815             :     /*      Directed Edge                                                   */
    2816             :     /* -------------------------------------------------------------------- */
    2817        1819 :     if (EQUAL(pszBaseGeometry, "directedEdge"))
    2818             :     {
    2819             :         // Collect edge.
    2820        1226 :         const CPLXMLNode *psEdge = FindBareXMLChild(psNode, "Edge");
    2821        1226 :         if (psEdge == nullptr)
    2822             :         {
    2823           0 :             ReportFailure("Failed to get Edge element in directedEdge");
    2824           0 :             return nullptr;
    2825             :         }
    2826             : 
    2827             :         // TODO(schwehr): Localize vars after removing gotos.
    2828        1226 :         std::unique_ptr<OGRGeometry> poGeom;
    2829        1226 :         const CPLXMLNode *psNodeElement = nullptr;
    2830        1226 :         const CPLXMLNode *psPointProperty = nullptr;
    2831        1226 :         const CPLXMLNode *psPoint = nullptr;
    2832        1226 :         bool bNodeOrientation = true;
    2833        1226 :         std::unique_ptr<OGRPoint> poPositiveNode;
    2834        1226 :         std::unique_ptr<OGRPoint> poNegativeNode;
    2835             : 
    2836        1226 :         const bool bEdgeOrientation = GetElementOrientation(psNode);
    2837             : 
    2838        1226 :         if (bGetSecondaryGeometry)
    2839             :         {
    2840             :             const CPLXMLNode *psdirectedNode =
    2841           0 :                 FindBareXMLChild(psEdge, "directedNode");
    2842           0 :             if (psdirectedNode == nullptr)
    2843           0 :                 goto nonode;
    2844             : 
    2845           0 :             bNodeOrientation = GetElementOrientation(psdirectedNode);
    2846             : 
    2847           0 :             psNodeElement = FindBareXMLChild(psdirectedNode, "Node");
    2848           0 :             if (psNodeElement == nullptr)
    2849           0 :                 goto nonode;
    2850             : 
    2851           0 :             psPointProperty = FindBareXMLChild(psNodeElement, "pointProperty");
    2852           0 :             if (psPointProperty == nullptr)
    2853             :                 psPointProperty =
    2854           0 :                     FindBareXMLChild(psNodeElement, "connectionPointProperty");
    2855           0 :             if (psPointProperty == nullptr)
    2856           0 :                 goto nonode;
    2857             : 
    2858           0 :             psPoint = FindBareXMLChild(psPointProperty, "Point");
    2859           0 :             if (psPoint == nullptr)
    2860           0 :                 psPoint = FindBareXMLChild(psPointProperty, "ConnectionPoint");
    2861           0 :             if (psPoint == nullptr)
    2862           0 :                 goto nonode;
    2863             : 
    2864           0 :             poGeom = GML2OGRGeometry_XMLNode_Internal(
    2865             :                 psPoint, pszId, nPseudoBoolGetSecondaryGeometryOption,
    2866           0 :                 nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache, true);
    2867           0 :             if (poGeom == nullptr ||
    2868           0 :                 wkbFlatten(poGeom->getGeometryType()) != wkbPoint)
    2869             :             {
    2870             :                 // ReportFailure(
    2871             :                 //           "Got %s geometry as Member instead of POINT.",
    2872             :                 //           poGeom ? poGeom->getGeometryName() : "NULL" );
    2873           0 :                 goto nonode;
    2874             :             }
    2875             : 
    2876             :             {
    2877           0 :                 OGRPoint *poPoint = poGeom.release()->toPoint();
    2878           0 :                 if ((bNodeOrientation == bEdgeOrientation) != bOrientation)
    2879           0 :                     poPositiveNode.reset(poPoint);
    2880             :                 else
    2881           0 :                     poNegativeNode.reset(poPoint);
    2882             :             }
    2883             : 
    2884             :             // Look for the other node.
    2885           0 :             psdirectedNode = psdirectedNode->psNext;
    2886           0 :             while (psdirectedNode != nullptr &&
    2887           0 :                    !EQUAL(psdirectedNode->pszValue, "directedNode"))
    2888           0 :                 psdirectedNode = psdirectedNode->psNext;
    2889           0 :             if (psdirectedNode == nullptr)
    2890           0 :                 goto nonode;
    2891             : 
    2892           0 :             if (GetElementOrientation(psdirectedNode) == bNodeOrientation)
    2893           0 :                 goto nonode;
    2894             : 
    2895           0 :             psNodeElement = FindBareXMLChild(psEdge, "Node");
    2896           0 :             if (psNodeElement == nullptr)
    2897           0 :                 goto nonode;
    2898             : 
    2899           0 :             psPointProperty = FindBareXMLChild(psNodeElement, "pointProperty");
    2900           0 :             if (psPointProperty == nullptr)
    2901             :                 psPointProperty =
    2902           0 :                     FindBareXMLChild(psNodeElement, "connectionPointProperty");
    2903           0 :             if (psPointProperty == nullptr)
    2904           0 :                 goto nonode;
    2905             : 
    2906           0 :             psPoint = FindBareXMLChild(psPointProperty, "Point");
    2907           0 :             if (psPoint == nullptr)
    2908           0 :                 psPoint = FindBareXMLChild(psPointProperty, "ConnectionPoint");
    2909           0 :             if (psPoint == nullptr)
    2910           0 :                 goto nonode;
    2911             : 
    2912           0 :             poGeom = GML2OGRGeometry_XMLNode_Internal(
    2913             :                 psPoint, pszId, nPseudoBoolGetSecondaryGeometryOption,
    2914           0 :                 nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache, true);
    2915           0 :             if (poGeom == nullptr ||
    2916           0 :                 wkbFlatten(poGeom->getGeometryType()) != wkbPoint)
    2917             :             {
    2918             :                 // ReportFailure(
    2919             :                 //           "Got %s geometry as Member instead of POINT.",
    2920             :                 //           poGeom ? poGeom->getGeometryName() : "NULL" );
    2921           0 :                 goto nonode;
    2922             :             }
    2923             : 
    2924             :             {
    2925           0 :                 OGRPoint *poPoint = poGeom.release()->toPoint();
    2926           0 :                 if ((bNodeOrientation == bEdgeOrientation) != bOrientation)
    2927           0 :                     poNegativeNode.reset(poPoint);
    2928             :                 else
    2929           0 :                     poPositiveNode.reset(poPoint);
    2930             :             }
    2931             : 
    2932             :             {
    2933             :                 // Create a scope so that poMP can be initialized with goto
    2934             :                 // above and label below.
    2935           0 :                 auto poMP = std::make_unique<OGRMultiPoint>();
    2936           0 :                 poMP->addGeometry(std::move(poNegativeNode));
    2937           0 :                 poMP->addGeometry(std::move(poPositiveNode));
    2938             : 
    2939           0 :                 return poMP;
    2940             :             }
    2941           0 :         nonode:;
    2942             :         }
    2943             : 
    2944             :         // Collect curveproperty.
    2945             :         const CPLXMLNode *psCurveProperty =
    2946        1226 :             FindBareXMLChild(psEdge, "curveProperty");
    2947        1226 :         if (psCurveProperty == nullptr)
    2948             :         {
    2949           0 :             ReportFailure("directedEdge: Failed to get curveProperty in Edge");
    2950           0 :             return nullptr;
    2951             :         }
    2952             : 
    2953             :         const CPLXMLNode *psCurve =
    2954        1226 :             FindBareXMLChild(psCurveProperty, "LineString");
    2955        1226 :         if (psCurve == nullptr)
    2956         628 :             psCurve = FindBareXMLChild(psCurveProperty, "Curve");
    2957        1226 :         if (psCurve == nullptr)
    2958             :         {
    2959           0 :             ReportFailure("directedEdge: Failed to get LineString or "
    2960             :                           "Curve tag in curveProperty");
    2961           0 :             return nullptr;
    2962             :         }
    2963             : 
    2964             :         auto poLineStringBeforeCast = GML2OGRGeometry_XMLNode_Internal(
    2965             :             psCurve, pszId, nPseudoBoolGetSecondaryGeometryOption,
    2966        2452 :             nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache, true);
    2967        2452 :         if (poLineStringBeforeCast == nullptr ||
    2968        1226 :             wkbFlatten(poLineStringBeforeCast->getGeometryType()) !=
    2969             :                 wkbLineString)
    2970             :         {
    2971           0 :             ReportFailure("Got %s geometry as Member instead of LINESTRING.",
    2972             :                           poLineStringBeforeCast
    2973             :                               ? poLineStringBeforeCast->getGeometryName()
    2974             :                               : "NULL");
    2975           0 :             return nullptr;
    2976             :         }
    2977             :         auto poLineString = std::unique_ptr<OGRLineString>(
    2978        2452 :             poLineStringBeforeCast.release()->toLineString());
    2979             : 
    2980        1226 :         if (bGetSecondaryGeometry)
    2981             :         {
    2982             :             // Choose a point based on the orientation.
    2983           0 :             poNegativeNode = std::make_unique<OGRPoint>();
    2984           0 :             poPositiveNode = std::make_unique<OGRPoint>();
    2985           0 :             if (bEdgeOrientation == bOrientation)
    2986             :             {
    2987           0 :                 poLineString->StartPoint(poNegativeNode.get());
    2988           0 :                 poLineString->EndPoint(poPositiveNode.get());
    2989             :             }
    2990             :             else
    2991             :             {
    2992           0 :                 poLineString->StartPoint(poPositiveNode.get());
    2993           0 :                 poLineString->EndPoint(poNegativeNode.get());
    2994             :             }
    2995             : 
    2996           0 :             auto poMP = std::make_unique<OGRMultiPoint>();
    2997           0 :             poMP->addGeometry(std::move(poNegativeNode));
    2998           0 :             poMP->addGeometry(std::move(poPositiveNode));
    2999           0 :             return poMP;
    3000             :         }
    3001             : 
    3002             :         // correct orientation of the line string
    3003        1226 :         if (bEdgeOrientation != bOrientation)
    3004             :         {
    3005         392 :             poLineString->reversePoints();
    3006             :         }
    3007        1226 :         return poLineString;
    3008             :     }
    3009             : 
    3010             :     /* -------------------------------------------------------------------- */
    3011             :     /*      TopoCurve                                                       */
    3012             :     /* -------------------------------------------------------------------- */
    3013         593 :     if (EQUAL(pszBaseGeometry, "TopoCurve"))
    3014             :     {
    3015         390 :         std::unique_ptr<OGRMultiLineString> poMLS;
    3016         390 :         std::unique_ptr<OGRMultiPoint> poMP;
    3017             : 
    3018         390 :         if (bGetSecondaryGeometry)
    3019           0 :             poMP = std::make_unique<OGRMultiPoint>();
    3020             :         else
    3021         390 :             poMLS = std::make_unique<OGRMultiLineString>();
    3022             : 
    3023             :         // Collect directedEdges.
    3024         816 :         for (const CPLXMLNode *psChild = psNode->psChild; psChild != nullptr;
    3025         426 :              psChild = psChild->psNext)
    3026             :         {
    3027         852 :             if (psChild->eType == CXT_Element &&
    3028         426 :                 EQUAL(BareGMLElement(psChild->pszValue), "directedEdge"))
    3029             :             {
    3030             :                 auto poGeom = GML2OGRGeometry_XMLNode_Internal(
    3031             :                     psChild, pszId, nPseudoBoolGetSecondaryGeometryOption,
    3032         426 :                     nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache);
    3033         426 :                 if (poGeom == nullptr)
    3034             :                 {
    3035           0 :                     ReportFailure("Failed to get geometry in directedEdge");
    3036           0 :                     return nullptr;
    3037             :                 }
    3038             : 
    3039             :                 // Add the two points corresponding to the two nodes to poMP.
    3040         426 :                 if (bGetSecondaryGeometry &&
    3041           0 :                     wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint)
    3042             :                 {
    3043             :                     auto poMultiPoint = std::unique_ptr<OGRMultiPoint>(
    3044           0 :                         poGeom.release()->toMultiPoint());
    3045             : 
    3046             :                     // TODO: TopoCurve geometries with more than one
    3047             :                     //       directedEdge elements were not tested.
    3048           0 :                     if (poMP->getNumGeometries() <= 0 ||
    3049           0 :                         !(poMP->getGeometryRef(poMP->getNumGeometries() - 1)
    3050           0 :                               ->Equals(poMultiPoint->getGeometryRef(0))))
    3051             :                     {
    3052           0 :                         poMP->addGeometry(poMultiPoint->getGeometryRef(0));
    3053             :                     }
    3054           0 :                     poMP->addGeometry(poMultiPoint->getGeometryRef(1));
    3055             :                 }
    3056         852 :                 else if (!bGetSecondaryGeometry &&
    3057         426 :                          wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
    3058             :                 {
    3059         426 :                     poMLS->addGeometry(std::move(poGeom));
    3060             :                 }
    3061             :                 else
    3062             :                 {
    3063           0 :                     ReportFailure("Got %s geometry as Member instead of %s.",
    3064             :                                   poGeom->getGeometryName(),
    3065             :                                   bGetSecondaryGeometry ? "MULTIPOINT"
    3066             :                                                         : "LINESTRING");
    3067           0 :                     return nullptr;
    3068             :                 }
    3069             :             }
    3070             :         }
    3071             : 
    3072         390 :         if (bGetSecondaryGeometry)
    3073           0 :             return poMP;
    3074             : 
    3075         390 :         return poMLS;
    3076             :     }
    3077             : 
    3078             :     /* -------------------------------------------------------------------- */
    3079             :     /*      TopoSurface                                                     */
    3080             :     /* -------------------------------------------------------------------- */
    3081         203 :     if (EQUAL(pszBaseGeometry, "TopoSurface"))
    3082             :     {
    3083             :         /****************************************************************/
    3084             :         /* applying the FaceHoleNegative = false rules                  */
    3085             :         /*                                                              */
    3086             :         /* - each <TopoSurface> is expected to represent a MultiPolygon */
    3087             :         /* - each <Face> is expected to represent a distinct Polygon,   */
    3088             :         /*   this including any possible Interior Ring (holes);         */
    3089             :         /*   orientation="+/-" plays no role at all to identify "holes" */
    3090             :         /* - each <Edge> within a <Face> may indifferently represent    */
    3091             :         /*   an element of the Exterior or Interior Boundary; relative  */
    3092             :         /*   order of <Edges> is absolutely irrelevant.                 */
    3093             :         /****************************************************************/
    3094             :         /* Contributor: Alessandro Furieri, a.furieri@lqt.it            */
    3095             :         /* Developed for Faunalia (http://www.faunalia.it)              */
    3096             :         /* with funding from Regione Toscana -                          */
    3097             :         /* Settore SISTEMA INFORMATIVO TERRITORIALE ED AMBIENTALE       */
    3098             :         /****************************************************************/
    3099          79 :         if (!bFaceHoleNegative)
    3100             :         {
    3101          59 :             if (bGetSecondaryGeometry)
    3102           0 :                 return nullptr;
    3103             : 
    3104             : #ifndef HAVE_GEOS
    3105             :             static bool bWarningAlreadyEmitted = false;
    3106             :             if (!bWarningAlreadyEmitted)
    3107             :             {
    3108             :                 ReportFailure(
    3109             :                     "Interpreating that GML TopoSurface geometry requires GDAL "
    3110             :                     "to be built with GEOS support.  As a workaround, you can "
    3111             :                     "try defining the GML_FACE_HOLE_NEGATIVE configuration "
    3112             :                     "option to YES, so that the 'old' interpretation algorithm "
    3113             :                     "is used. But be warned that the result might be "
    3114             :                     "incorrect.");
    3115             :                 bWarningAlreadyEmitted = true;
    3116             :             }
    3117             :             return nullptr;
    3118             : #else
    3119         118 :             auto poTS = std::make_unique<OGRMultiPolygon>();
    3120             : 
    3121             :             // Collect directed faces.
    3122          59 :             for (const CPLXMLNode *psChild = psNode->psChild;
    3123         173 :                  psChild != nullptr; psChild = psChild->psNext)
    3124             :             {
    3125         228 :                 if (psChild->eType == CXT_Element &&
    3126         114 :                     EQUAL(BareGMLElement(psChild->pszValue), "directedFace"))
    3127             :                 {
    3128             :                     // Collect next face (psChild->psChild).
    3129         114 :                     const CPLXMLNode *psFaceChild = GetChildElement(psChild);
    3130             : 
    3131           0 :                     while (
    3132         228 :                         psFaceChild != nullptr &&
    3133         114 :                         !(psFaceChild->eType == CXT_Element &&
    3134         114 :                           EQUAL(BareGMLElement(psFaceChild->pszValue), "Face")))
    3135           0 :                         psFaceChild = psFaceChild->psNext;
    3136             : 
    3137         114 :                     if (psFaceChild == nullptr)
    3138           0 :                         continue;
    3139             : 
    3140             :                     auto poCollectedGeom =
    3141         114 :                         std::make_unique<OGRMultiLineString>();
    3142             : 
    3143             :                     // Collect directed edges of the face.
    3144         114 :                     for (const CPLXMLNode *psDirectedEdgeChild =
    3145             :                              psFaceChild->psChild;
    3146         856 :                          psDirectedEdgeChild != nullptr;
    3147         742 :                          psDirectedEdgeChild = psDirectedEdgeChild->psNext)
    3148             :                     {
    3149        1370 :                         if (psDirectedEdgeChild->eType == CXT_Element &&
    3150         628 :                             EQUAL(BareGMLElement(psDirectedEdgeChild->pszValue),
    3151             :                                   "directedEdge"))
    3152             :                         {
    3153             :                             auto poEdgeGeom = GML2OGRGeometry_XMLNode_Internal(
    3154             :                                 psDirectedEdgeChild, pszId,
    3155             :                                 nPseudoBoolGetSecondaryGeometryOption,
    3156             :                                 nRecLevel + 1, nSRSDimension, pszSRSName,
    3157         628 :                                 oSRSCache, true);
    3158             : 
    3159        1256 :                             if (poEdgeGeom == nullptr ||
    3160         628 :                                 wkbFlatten(poEdgeGeom->getGeometryType()) !=
    3161             :                                     wkbLineString)
    3162             :                             {
    3163           0 :                                 ReportFailure(
    3164             :                                     "Failed to get geometry in directedEdge");
    3165           0 :                                 return nullptr;
    3166             :                             }
    3167             : 
    3168         628 :                             poCollectedGeom->addGeometry(std::move(poEdgeGeom));
    3169             :                         }
    3170             :                     }
    3171             : 
    3172             :                     auto poFaceCollectionGeom = std::unique_ptr<OGRGeometry>(
    3173         114 :                         poCollectedGeom->Polygonize());
    3174         114 :                     if (poFaceCollectionGeom == nullptr)
    3175             :                     {
    3176           0 :                         ReportFailure("Failed to assemble Edges in Face");
    3177           0 :                         return nullptr;
    3178             :                     }
    3179             : 
    3180             :                     auto poFaceGeom =
    3181         114 :                         GML2FaceExtRing(poFaceCollectionGeom.get());
    3182             : 
    3183         114 :                     if (poFaceGeom == nullptr)
    3184             :                     {
    3185           0 :                         ReportFailure("Failed to build Polygon for Face");
    3186           0 :                         return nullptr;
    3187             :                     }
    3188             :                     else
    3189             :                     {
    3190         114 :                         int iCount = poTS->getNumGeometries();
    3191         114 :                         if (iCount == 0)
    3192             :                         {
    3193             :                             // Inserting the first Polygon.
    3194          59 :                             poTS->addGeometry(std::move(poFaceGeom));
    3195             :                         }
    3196             :                         else
    3197             :                         {
    3198             :                             // Using Union to add the current Polygon.
    3199             :                             auto poUnion = std::unique_ptr<OGRGeometry>(
    3200          55 :                                 poTS->Union(poFaceGeom.get()));
    3201          55 :                             if (poUnion == nullptr)
    3202             :                             {
    3203           0 :                                 ReportFailure("Failed Union for TopoSurface");
    3204           0 :                                 return nullptr;
    3205             :                             }
    3206          55 :                             if (wkbFlatten(poUnion->getGeometryType()) ==
    3207             :                                 wkbPolygon)
    3208             :                             {
    3209             :                                 // Forcing to be a MultiPolygon.
    3210          34 :                                 poTS = std::make_unique<OGRMultiPolygon>();
    3211          34 :                                 poTS->addGeometry(std::move(poUnion));
    3212             :                             }
    3213          21 :                             else if (wkbFlatten(poUnion->getGeometryType()) ==
    3214             :                                      wkbMultiPolygon)
    3215             :                             {
    3216          21 :                                 poTS.reset(poUnion.release()->toMultiPolygon());
    3217             :                             }
    3218             :                             else
    3219             :                             {
    3220           0 :                                 ReportFailure(
    3221             :                                     "Unexpected geometry type resulting "
    3222             :                                     "from Union for TopoSurface");
    3223           0 :                                 return nullptr;
    3224             :                             }
    3225             :                         }
    3226             :                     }
    3227             :                 }
    3228             :             }
    3229             : 
    3230          59 :             return poTS;
    3231             : #endif  // HAVE_GEOS
    3232             :         }
    3233             : 
    3234             :         /****************************************************************/
    3235             :         /* applying the FaceHoleNegative = true rules                   */
    3236             :         /*                                                              */
    3237             :         /* - each <TopoSurface> is expected to represent a MultiPolygon */
    3238             :         /* - any <Face> declaring orientation="+" is expected to        */
    3239             :         /*   represent an Exterior Ring (no holes are allowed)          */
    3240             :         /* - any <Face> declaring orientation="-" is expected to        */
    3241             :         /*   represent an Interior Ring (hole) belonging to the latest  */
    3242             :         /*   Exterior Ring.                                             */
    3243             :         /* - <Edges> within the same <Face> are expected to be          */
    3244             :         /*   arranged in geometrically adjacent and consecutive         */
    3245             :         /*   sequence.                                                  */
    3246             :         /****************************************************************/
    3247          20 :         if (bGetSecondaryGeometry)
    3248           0 :             return nullptr;
    3249          20 :         bool bFaceOrientation = true;
    3250          40 :         auto poTS = std::make_unique<OGRPolygon>();
    3251             : 
    3252             :         // Collect directed faces.
    3253          60 :         for (const CPLXMLNode *psChild = psNode->psChild; psChild != nullptr;
    3254          40 :              psChild = psChild->psNext)
    3255             :         {
    3256          78 :             if (psChild->eType == CXT_Element &&
    3257          38 :                 EQUAL(BareGMLElement(psChild->pszValue), "directedFace"))
    3258             :             {
    3259          38 :                 bFaceOrientation = GetElementOrientation(psChild);
    3260             : 
    3261             :                 // Collect next face (psChild->psChild).
    3262          38 :                 const CPLXMLNode *psFaceChild = GetChildElement(psChild);
    3263          76 :                 while (psFaceChild != nullptr &&
    3264          38 :                        !EQUAL(BareGMLElement(psFaceChild->pszValue), "Face"))
    3265           0 :                     psFaceChild = psFaceChild->psNext;
    3266             : 
    3267          38 :                 if (psFaceChild == nullptr)
    3268           0 :                     continue;
    3269             : 
    3270          38 :                 auto poFaceGeom = std::make_unique<OGRLinearRing>();
    3271             : 
    3272             :                 // Collect directed edges of the face.
    3273          38 :                 for (const CPLXMLNode *psDirectedEdgeChild =
    3274             :                          psFaceChild->psChild;
    3275         246 :                      psDirectedEdgeChild != nullptr;
    3276         208 :                      psDirectedEdgeChild = psDirectedEdgeChild->psNext)
    3277             :                 {
    3278         380 :                     if (psDirectedEdgeChild->eType == CXT_Element &&
    3279         172 :                         EQUAL(BareGMLElement(psDirectedEdgeChild->pszValue),
    3280             :                               "directedEdge"))
    3281             :                     {
    3282             :                         auto poEdgeGeom = GML2OGRGeometry_XMLNode_Internal(
    3283             :                             psDirectedEdgeChild, pszId,
    3284             :                             nPseudoBoolGetSecondaryGeometryOption,
    3285             :                             nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache,
    3286         172 :                             true, bFaceOrientation);
    3287             : 
    3288         344 :                         if (poEdgeGeom == nullptr ||
    3289         172 :                             wkbFlatten(poEdgeGeom->getGeometryType()) !=
    3290             :                                 wkbLineString)
    3291             :                         {
    3292           0 :                             ReportFailure(
    3293             :                                 "Failed to get geometry in directedEdge");
    3294           0 :                             return nullptr;
    3295             :                         }
    3296             : 
    3297             :                         auto poEdgeGeomLS = std::unique_ptr<OGRLineString>(
    3298         344 :                             poEdgeGeom.release()->toLineString());
    3299         172 :                         if (!bFaceOrientation)
    3300             :                         {
    3301          30 :                             OGRLineString *poLS = poEdgeGeomLS.get();
    3302          30 :                             OGRLineString *poAddLS = poFaceGeom.get();
    3303             : 
    3304             :                             // TODO(schwehr): Use AlmostEqual.
    3305          30 :                             const double epsilon = 1.0e-14;
    3306          30 :                             if (poAddLS->getNumPoints() < 2)
    3307             :                             {
    3308             :                                 // Skip it.
    3309             :                             }
    3310          18 :                             else if (poLS->getNumPoints() > 0 &&
    3311          36 :                                      fabs(poLS->getX(poLS->getNumPoints() - 1) -
    3312          18 :                                           poAddLS->getX(0)) < epsilon &&
    3313          36 :                                      fabs(poLS->getY(poLS->getNumPoints() - 1) -
    3314          54 :                                           poAddLS->getY(0)) < epsilon &&
    3315          18 :                                      fabs(poLS->getZ(poLS->getNumPoints() - 1) -
    3316          18 :                                           poAddLS->getZ(0)) < epsilon)
    3317             :                             {
    3318             :                                 // Skip the first point of the new linestring to
    3319             :                                 // avoid invalidate duplicate points.
    3320          18 :                                 poLS->addSubLineString(poAddLS, 1);
    3321             :                             }
    3322             :                             else
    3323             :                             {
    3324             :                                 // Add the whole new line string.
    3325           0 :                                 poLS->addSubLineString(poAddLS);
    3326             :                             }
    3327          30 :                             poFaceGeom->empty();
    3328             :                         }
    3329             :                         // TODO(schwehr): Suspicious that poLS overwritten
    3330             :                         // without else.
    3331         172 :                         OGRLineString *poLS = poFaceGeom.get();
    3332         172 :                         OGRLineString *poAddLS = poEdgeGeomLS.get();
    3333         172 :                         if (poAddLS->getNumPoints() < 2)
    3334             :                         {
    3335             :                             // Skip it.
    3336             :                         }
    3337         172 :                         else if (poLS->getNumPoints() > 0 &&
    3338         232 :                                  fabs(poLS->getX(poLS->getNumPoints() - 1) -
    3339         116 :                                       poAddLS->getX(0)) < 1e-14 &&
    3340         232 :                                  fabs(poLS->getY(poLS->getNumPoints() - 1) -
    3341         404 :                                       poAddLS->getY(0)) < 1e-14 &&
    3342         116 :                                  fabs(poLS->getZ(poLS->getNumPoints() - 1) -
    3343         116 :                                       poAddLS->getZ(0)) < 1e-14)
    3344             :                         {
    3345             :                             // Skip the first point of the new linestring to
    3346             :                             // avoid invalidate duplicate points.
    3347         116 :                             poLS->addSubLineString(poAddLS, 1);
    3348             :                         }
    3349             :                         else
    3350             :                         {
    3351             :                             // Add the whole new line string.
    3352          56 :                             poLS->addSubLineString(poAddLS);
    3353             :                         }
    3354             :                     }
    3355             :                 }
    3356             : 
    3357             :                 // if( poFaceGeom == NULL )
    3358             :                 // {
    3359             :                 //     ReportFailure(
    3360             :                 //               "Failed to get Face geometry in directedFace"
    3361             :                 //               );
    3362             :                 //     delete poFaceGeom;
    3363             :                 //     return NULL;
    3364             :                 // }
    3365             : 
    3366          38 :                 poTS->addRing(std::move(poFaceGeom));
    3367             :             }
    3368             :         }
    3369             : 
    3370             :         // if( poTS == NULL )
    3371             :         // {
    3372             :         //     ReportFailure(
    3373             :         //               "Failed to get TopoSurface geometry" );
    3374             :         //     delete poTS;
    3375             :         //     return NULL;
    3376             :         // }
    3377             : 
    3378          20 :         return poTS;
    3379             :     }
    3380             : 
    3381             :     /* -------------------------------------------------------------------- */
    3382             :     /*      Surface                                                         */
    3383             :     /* -------------------------------------------------------------------- */
    3384         124 :     if (EQUAL(pszBaseGeometry, "Surface") ||
    3385          81 :         EQUAL(pszBaseGeometry, "ElevatedSurface") /* AIXM */)
    3386             :     {
    3387             :         // Find outer ring.
    3388          45 :         const CPLXMLNode *psChild = FindBareXMLChild(psNode, "patches");
    3389          45 :         if (psChild == nullptr)
    3390           3 :             psChild = FindBareXMLChild(psNode, "polygonPatches");
    3391          45 :         if (psChild == nullptr)
    3392           3 :             psChild = FindBareXMLChild(psNode, "trianglePatches");
    3393             : 
    3394          45 :         psChild = GetChildElement(psChild);
    3395          45 :         if (psChild == nullptr)
    3396             :         {
    3397             :             // <gml:Surface/> and <gml:Surface><gml:patches/></gml:Surface> are
    3398             :             // valid GML.
    3399           3 :             return std::make_unique<OGRPolygon>();
    3400             :         }
    3401             : 
    3402          42 :         OGRMultiSurface *poMSPtr = nullptr;
    3403          42 :         std::unique_ptr<OGRGeometry> poResultPoly;
    3404          42 :         std::unique_ptr<OGRGeometry> poResultTri;
    3405          42 :         OGRTriangulatedSurface *poTINPtr = nullptr;
    3406          90 :         for (; psChild != nullptr; psChild = psChild->psNext)
    3407             :         {
    3408          96 :             if (psChild->eType == CXT_Element &&
    3409          48 :                 (EQUAL(BareGMLElement(psChild->pszValue), "PolygonPatch") ||
    3410           5 :                  EQUAL(BareGMLElement(psChild->pszValue), "Rectangle")))
    3411             :             {
    3412             :                 auto poGeom = GML2OGRGeometry_XMLNode_Internal(
    3413             :                     psChild, pszId, nPseudoBoolGetSecondaryGeometryOption,
    3414          44 :                     nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache);
    3415          44 :                 if (poGeom == nullptr)
    3416             :                 {
    3417           0 :                     return nullptr;
    3418             :                 }
    3419             : 
    3420             :                 const OGRwkbGeometryType eGeomType =
    3421          44 :                     wkbFlatten(poGeom->getGeometryType());
    3422             : 
    3423          44 :                 if (poResultPoly == nullptr)
    3424          40 :                     poResultPoly = std::move(poGeom);
    3425             :                 else
    3426             :                 {
    3427           4 :                     if (poMSPtr == nullptr)
    3428             :                     {
    3429           0 :                         std::unique_ptr<OGRMultiSurface> poMS;
    3430           3 :                         if (wkbFlatten(poResultPoly->getGeometryType()) ==
    3431           3 :                                 wkbPolygon &&
    3432             :                             eGeomType == wkbPolygon)
    3433           2 :                             poMS = std::make_unique<OGRMultiPolygon>();
    3434             :                         else
    3435           1 :                             poMS = std::make_unique<OGRMultiSurface>();
    3436             :                         OGRErr eErr =
    3437           3 :                             poMS->addGeometry(std::move(poResultPoly));
    3438           3 :                         CPL_IGNORE_RET_VAL(eErr);
    3439           3 :                         CPLAssert(eErr == OGRERR_NONE);
    3440           3 :                         poResultPoly = std::move(poMS);
    3441           3 :                         poMSPtr = cpl::down_cast<OGRMultiSurface *>(
    3442             :                             poResultPoly.get());
    3443             :                     }
    3444           2 :                     else if (eGeomType != wkbPolygon &&
    3445           1 :                              wkbFlatten(poResultPoly->getGeometryType()) ==
    3446             :                                  wkbMultiPolygon)
    3447             :                     {
    3448             :                         OGRMultiPolygon *poMultiPoly =
    3449           1 :                             poResultPoly.release()->toMultiPolygon();
    3450           1 :                         poResultPoly.reset(
    3451           1 :                             OGRMultiPolygon::CastToMultiSurface(poMultiPoly));
    3452           1 :                         poMSPtr = cpl::down_cast<OGRMultiSurface *>(
    3453             :                             poResultPoly.get());
    3454             :                     }
    3455           4 :                     OGRErr eErr = poMSPtr->addGeometry(std::move(poGeom));
    3456           4 :                     CPL_IGNORE_RET_VAL(eErr);
    3457           4 :                     CPLAssert(eErr == OGRERR_NONE);
    3458             :                 }
    3459             :             }
    3460           8 :             else if (psChild->eType == CXT_Element &&
    3461           4 :                      EQUAL(BareGMLElement(psChild->pszValue), "Triangle"))
    3462             :             {
    3463             :                 auto poGeom = GML2OGRGeometry_XMLNode_Internal(
    3464             :                     psChild, pszId, nPseudoBoolGetSecondaryGeometryOption,
    3465           3 :                     nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache);
    3466           3 :                 if (poGeom == nullptr)
    3467             :                 {
    3468           0 :                     return nullptr;
    3469             :                 }
    3470             : 
    3471           3 :                 if (poResultTri == nullptr)
    3472           2 :                     poResultTri = std::move(poGeom);
    3473             :                 else
    3474             :                 {
    3475           1 :                     if (poTINPtr == nullptr)
    3476             :                     {
    3477           1 :                         auto poTIN = std::make_unique<OGRTriangulatedSurface>();
    3478             :                         OGRErr eErr =
    3479           1 :                             poTIN->addGeometry(std::move(poResultTri));
    3480           1 :                         CPL_IGNORE_RET_VAL(eErr);
    3481           1 :                         CPLAssert(eErr == OGRERR_NONE);
    3482           1 :                         poResultTri = std::move(poTIN);
    3483           1 :                         poTINPtr = cpl::down_cast<OGRTriangulatedSurface *>(
    3484             :                             poResultTri.get());
    3485             :                     }
    3486           1 :                     OGRErr eErr = poTINPtr->addGeometry(std::move(poGeom));
    3487           1 :                     CPL_IGNORE_RET_VAL(eErr);
    3488           1 :                     CPLAssert(eErr == OGRERR_NONE);
    3489             :                 }
    3490             :             }
    3491             :         }
    3492             : 
    3493          42 :         if (poResultTri == nullptr && poResultPoly == nullptr)
    3494           1 :             return nullptr;
    3495             : 
    3496          41 :         if (poResultTri == nullptr)
    3497          39 :             return poResultPoly;
    3498           2 :         else if (poResultPoly == nullptr)
    3499           1 :             return poResultTri;
    3500             :         else
    3501             :         {
    3502           2 :             auto poGC = std::make_unique<OGRGeometryCollection>();
    3503           1 :             poGC->addGeometry(std::move(poResultTri));
    3504           1 :             poGC->addGeometry(std::move(poResultPoly));
    3505           1 :             return poGC;
    3506             :         }
    3507             :     }
    3508             : 
    3509             :     /* -------------------------------------------------------------------- */
    3510             :     /*      TriangulatedSurface                                             */
    3511             :     /* -------------------------------------------------------------------- */
    3512          79 :     if (EQUAL(pszBaseGeometry, "TriangulatedSurface") ||
    3513          75 :         EQUAL(pszBaseGeometry, "Tin"))
    3514             :     {
    3515             :         // Find trianglePatches.
    3516           5 :         const CPLXMLNode *psChild = FindBareXMLChild(psNode, "trianglePatches");
    3517           5 :         if (psChild == nullptr)
    3518           4 :             psChild = FindBareXMLChild(psNode, "patches");
    3519             : 
    3520           5 :         psChild = GetChildElement(psChild);
    3521           5 :         if (psChild == nullptr)
    3522             :         {
    3523           1 :             ReportFailure("Missing <trianglePatches> for %s.", pszBaseGeometry);
    3524           1 :             return nullptr;
    3525             :         }
    3526             : 
    3527           8 :         auto poTIN = std::make_unique<OGRTriangulatedSurface>();
    3528           9 :         for (; psChild != nullptr; psChild = psChild->psNext)
    3529             :         {
    3530          12 :             if (psChild->eType == CXT_Element &&
    3531           6 :                 EQUAL(BareGMLElement(psChild->pszValue), "Triangle"))
    3532             :             {
    3533             :                 auto poTriangle = GML2OGRGeometry_XMLNode_Internal(
    3534             :                     psChild, pszId, nPseudoBoolGetSecondaryGeometryOption,
    3535           6 :                     nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache);
    3536           6 :                 if (poTriangle == nullptr)
    3537             :                 {
    3538           1 :                     return nullptr;
    3539             :                 }
    3540             :                 else
    3541             :                 {
    3542           5 :                     poTIN->addGeometry(std::move(poTriangle));
    3543             :                 }
    3544             :             }
    3545             :         }
    3546             : 
    3547           3 :         return poTIN;
    3548             :     }
    3549             : 
    3550             :     /* -------------------------------------------------------------------- */
    3551             :     /*      PolyhedralSurface                                               */
    3552             :     /* -------------------------------------------------------------------- */
    3553          74 :     if (EQUAL(pszBaseGeometry, "PolyhedralSurface"))
    3554             :     {
    3555             :         // Find polygonPatches.
    3556          11 :         const CPLXMLNode *psParent = FindBareXMLChild(psNode, "polygonPatches");
    3557          11 :         if (psParent == nullptr)
    3558             :         {
    3559           2 :             if (GetChildElement(psNode) == nullptr)
    3560             :             {
    3561             :                 // This is empty PolyhedralSurface.
    3562           1 :                 return std::make_unique<OGRPolyhedralSurface>();
    3563             :             }
    3564             :             else
    3565             :             {
    3566           1 :                 ReportFailure("Missing <polygonPatches> for %s.",
    3567             :                               pszBaseGeometry);
    3568           1 :                 return nullptr;
    3569             :             }
    3570             :         }
    3571             : 
    3572           9 :         const CPLXMLNode *psChild = GetChildElement(psParent);
    3573           9 :         if (psChild == nullptr)
    3574             :         {
    3575             :             // This is empty PolyhedralSurface.
    3576           1 :             return std::make_unique<OGRPolyhedralSurface>();
    3577             :         }
    3578           8 :         else if (!EQUAL(BareGMLElement(psChild->pszValue), "PolygonPatch"))
    3579             :         {
    3580           1 :             ReportFailure("Missing <PolygonPatch> for %s.", pszBaseGeometry);
    3581           1 :             return nullptr;
    3582             :         }
    3583             : 
    3584             :         // Each psParent has the tags corresponding to <gml:polygonPatches>
    3585             :         // Each psChild has the tags corresponding to <gml:PolygonPatch>
    3586             :         // Each PolygonPatch has a set of polygons enclosed in a
    3587             :         // OGRPolyhedralSurface.
    3588          14 :         auto poGC = std::make_unique<OGRGeometryCollection>();
    3589          15 :         for (; psParent != nullptr; psParent = psParent->psNext)
    3590             :         {
    3591           9 :             psChild = GetChildElement(psParent);
    3592           9 :             if (psChild == nullptr)
    3593           1 :                 continue;
    3594           8 :             auto poPS = std::make_unique<OGRPolyhedralSurface>();
    3595          23 :             for (; psChild != nullptr; psChild = psChild->psNext)
    3596             :             {
    3597          31 :                 if (psChild->eType == CXT_Element &&
    3598          15 :                     EQUAL(BareGMLElement(psChild->pszValue), "PolygonPatch"))
    3599             :                 {
    3600             :                     auto poPolygon = GML2OGRGeometry_XMLNode_Internal(
    3601             :                         psChild, pszId, nPseudoBoolGetSecondaryGeometryOption,
    3602          15 :                         nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache);
    3603          15 :                     if (poPolygon == nullptr)
    3604             :                     {
    3605           1 :                         ReportFailure("Wrong geometry type for %s.",
    3606             :                                       pszBaseGeometry);
    3607           1 :                         return nullptr;
    3608             :                     }
    3609             : 
    3610          14 :                     else if (wkbFlatten(poPolygon->getGeometryType()) ==
    3611             :                              wkbPolygon)
    3612             :                     {
    3613          13 :                         poPS->addGeometry(std::move(poPolygon));
    3614             :                     }
    3615           1 :                     else if (wkbFlatten(poPolygon->getGeometryType()) ==
    3616             :                              wkbCurvePolygon)
    3617             :                     {
    3618           1 :                         poPS->addGeometryDirectly(
    3619             :                             OGRGeometryFactory::forceToPolygon(
    3620             :                                 poPolygon.release()));
    3621             :                     }
    3622             :                     else
    3623             :                     {
    3624           0 :                         ReportFailure("Wrong geometry type for %s.",
    3625             :                                       pszBaseGeometry);
    3626           0 :                         return nullptr;
    3627             :                     }
    3628             :                 }
    3629             :             }
    3630           7 :             poGC->addGeometry(std::move(poPS));
    3631             :         }
    3632             : 
    3633           6 :         if (poGC->getNumGeometries() == 0)
    3634             :         {
    3635           0 :             return nullptr;
    3636             :         }
    3637           6 :         else if (poGC->getNumGeometries() == 1)
    3638             :         {
    3639             :             auto poResult =
    3640          10 :                 std::unique_ptr<OGRGeometry>(poGC->getGeometryRef(0));
    3641           5 :             poGC->removeGeometry(0, FALSE);
    3642           5 :             return poResult;
    3643             :         }
    3644             :         else
    3645             :         {
    3646           1 :             return poGC;
    3647             :         }
    3648             :     }
    3649             : 
    3650             :     /* -------------------------------------------------------------------- */
    3651             :     /*      Solid                                                           */
    3652             :     /* -------------------------------------------------------------------- */
    3653          63 :     if (EQUAL(pszBaseGeometry, "Solid"))
    3654             :     {
    3655          21 :         const CPLXMLNode *psChild = FindBareXMLChild(psNode, "interior");
    3656          21 :         if (psChild != nullptr)
    3657             :         {
    3658             :             static bool bWarnedOnce = false;
    3659           1 :             if (!bWarnedOnce)
    3660             :             {
    3661           1 :                 ReportWarning("<interior> elements of <Solid> are ignored");
    3662           1 :                 bWarnedOnce = true;
    3663             :             }
    3664             :         }
    3665             : 
    3666             :         // Find exterior element.
    3667          21 :         psChild = FindBareXMLChild(psNode, "exterior");
    3668             : 
    3669          21 :         if (nSRSDimension == 0)
    3670          21 :             nSRSDimension = 3;
    3671             : 
    3672          21 :         psChild = GetChildElement(psChild);
    3673          21 :         if (psChild == nullptr)
    3674             :         {
    3675             :             // <gml:Solid/> and <gml:Solid><gml:exterior/></gml:Solid> are valid
    3676             :             // GML.
    3677           3 :             return std::make_unique<OGRPolyhedralSurface>();
    3678             :         }
    3679             : 
    3680          29 :         if (EQUAL(BareGMLElement(psChild->pszValue), "CompositeSurface") ||
    3681          11 :             EQUAL(BareGMLElement(psChild->pszValue), "Shell"))
    3682             :         {
    3683          22 :             auto poPS = std::make_unique<OGRPolyhedralSurface>();
    3684             : 
    3685             :             // Iterate over children.
    3686          86 :             for (psChild = psChild->psChild; psChild != nullptr;
    3687          75 :                  psChild = psChild->psNext)
    3688             :             {
    3689             :                 const char *pszMemberElement =
    3690          75 :                     BareGMLElement(psChild->pszValue);
    3691          75 :                 if (psChild->eType == CXT_Element &&
    3692          73 :                     (EQUAL(pszMemberElement, "polygonMember") ||
    3693          73 :                      EQUAL(pszMemberElement, "surfaceMember")))
    3694             :                 {
    3695          73 :                     const CPLXMLNode *psSurfaceChild = GetChildElement(psChild);
    3696             : 
    3697          73 :                     if (psSurfaceChild != nullptr)
    3698             :                     {
    3699             :                         auto poGeom = GML2OGRGeometry_XMLNode_Internal(
    3700             :                             psSurfaceChild, pszId,
    3701             :                             nPseudoBoolGetSecondaryGeometryOption,
    3702             :                             nRecLevel + 1, nSRSDimension, pszSRSName,
    3703         146 :                             oSRSCache);
    3704         146 :                         if (poGeom != nullptr &&
    3705          73 :                             wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
    3706             :                         {
    3707          73 :                             poPS->addGeometry(std::move(poGeom));
    3708             :                         }
    3709             :                     }
    3710             :                 }
    3711             :             }
    3712          11 :             return poPS;
    3713             :         }
    3714             : 
    3715             :         // Get the geometry inside <exterior>.
    3716             :         auto poGeom = GML2OGRGeometry_XMLNode_Internal(
    3717             :             psChild, pszId, nPseudoBoolGetSecondaryGeometryOption,
    3718          14 :             nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache);
    3719           7 :         if (poGeom == nullptr)
    3720             :         {
    3721           6 :             ReportFailure("Invalid exterior element");
    3722           6 :             return nullptr;
    3723             :         }
    3724             : 
    3725           1 :         return poGeom;
    3726             :     }
    3727             : 
    3728             :     /* -------------------------------------------------------------------- */
    3729             :     /*      OrientableCurve                                                 */
    3730             :     /* -------------------------------------------------------------------- */
    3731          42 :     if (EQUAL(pszBaseGeometry, "OrientableCurve"))
    3732             :     {
    3733             :         // Find baseCurve.
    3734           7 :         const CPLXMLNode *psChild = FindBareXMLChild(psNode, "baseCurve");
    3735             : 
    3736           7 :         psChild = GetChildElement(psChild);
    3737           7 :         if (psChild == nullptr)
    3738             :         {
    3739           3 :             ReportFailure("Missing <baseCurve> for OrientableCurve.");
    3740           3 :             return nullptr;
    3741             :         }
    3742             : 
    3743             :         auto poGeom = GML2OGRGeometry_XMLNode_Internal(
    3744             :             psChild, pszId, nPseudoBoolGetSecondaryGeometryOption,
    3745           8 :             nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache);
    3746           4 :         if (!poGeom || !OGR_GT_IsCurve(poGeom->getGeometryType()))
    3747             :         {
    3748           2 :             ReportFailure("baseCurve of OrientableCurve is not a curve.");
    3749           2 :             return nullptr;
    3750             :         }
    3751           2 :         if (!GetElementOrientation(psNode))
    3752             :         {
    3753           1 :             poGeom->toCurve()->reversePoints();
    3754             :         }
    3755           2 :         return poGeom;
    3756             :     }
    3757             : 
    3758             :     /* -------------------------------------------------------------------- */
    3759             :     /*      OrientableSurface                                               */
    3760             :     /* -------------------------------------------------------------------- */
    3761          35 :     if (EQUAL(pszBaseGeometry, "OrientableSurface"))
    3762             :     {
    3763             :         // Find baseSurface.
    3764           5 :         const CPLXMLNode *psChild = FindBareXMLChild(psNode, "baseSurface");
    3765             : 
    3766           5 :         psChild = GetChildElement(psChild);
    3767           5 :         if (psChild == nullptr)
    3768             :         {
    3769           3 :             ReportFailure("Missing <baseSurface> for OrientableSurface.");
    3770           3 :             return nullptr;
    3771             :         }
    3772             : 
    3773             :         return GML2OGRGeometry_XMLNode_Internal(
    3774             :             psChild, pszId, nPseudoBoolGetSecondaryGeometryOption,
    3775           2 :             nRecLevel + 1, nSRSDimension, pszSRSName, oSRSCache);
    3776             :     }
    3777             : 
    3778             :     /* -------------------------------------------------------------------- */
    3779             :     /*      SimplePolygon, SimpleRectangle, SimpleTriangle                  */
    3780             :     /*      (GML 3.3 compact encoding)                                      */
    3781             :     /* -------------------------------------------------------------------- */
    3782          30 :     if (EQUAL(pszBaseGeometry, "SimplePolygon") ||
    3783          26 :         EQUAL(pszBaseGeometry, "SimpleRectangle"))
    3784             :     {
    3785          10 :         auto poRing = std::make_unique<OGRLinearRing>();
    3786             : 
    3787           5 :         if (!ParseGMLCoordinates(psNode, poRing.get(), nSRSDimension))
    3788             :         {
    3789           2 :             return nullptr;
    3790             :         }
    3791             : 
    3792           3 :         poRing->closeRings();
    3793             : 
    3794           6 :         auto poPolygon = std::make_unique<OGRPolygon>();
    3795           3 :         poPolygon->addRing(std::move(poRing));
    3796           3 :         return poPolygon;
    3797             :     }
    3798             : 
    3799          25 :     if (EQUAL(pszBaseGeometry, "SimpleTriangle"))
    3800             :     {
    3801           2 :         auto poRing = std::make_unique<OGRLinearRing>();
    3802             : 
    3803           1 :         if (!ParseGMLCoordinates(psNode, poRing.get(), nSRSDimension))
    3804             :         {
    3805           0 :             return nullptr;
    3806             :         }
    3807             : 
    3808           1 :         poRing->closeRings();
    3809             : 
    3810           2 :         auto poTriangle = std::make_unique<OGRTriangle>();
    3811           1 :         poTriangle->addRing(std::move(poRing));
    3812           1 :         return poTriangle;
    3813             :     }
    3814             : 
    3815             :     /* -------------------------------------------------------------------- */
    3816             :     /*      SimpleMultiPoint (GML 3.3 compact encoding)                     */
    3817             :     /* -------------------------------------------------------------------- */
    3818          24 :     if (EQUAL(pszBaseGeometry, "SimpleMultiPoint"))
    3819             :     {
    3820           8 :         auto poLS = std::make_unique<OGRLineString>();
    3821             : 
    3822           4 :         if (!ParseGMLCoordinates(psNode, poLS.get(), nSRSDimension))
    3823             :         {
    3824           2 :             return nullptr;
    3825             :         }
    3826             : 
    3827           4 :         auto poMP = std::make_unique<OGRMultiPoint>();
    3828           2 :         int nPoints = poLS->getNumPoints();
    3829           4 :         for (int i = 0; i < nPoints; i++)
    3830             :         {
    3831           2 :             auto poPoint = std::make_unique<OGRPoint>();
    3832           2 :             poLS->getPoint(i, poPoint.get());
    3833           2 :             poMP->addGeometry(std::move(poPoint));
    3834             :         }
    3835           2 :         return poMP;
    3836             :     }
    3837             : 
    3838          20 :     if (strcmp(pszBaseGeometry, "null") == 0)
    3839             :     {
    3840           1 :         return nullptr;
    3841             :     }
    3842             : 
    3843          19 :     ReportFailure("Unrecognized geometry type <%s>.", pszBaseGeometry);
    3844             : 
    3845          19 :     return nullptr;
    3846             : }
    3847             : 
    3848             : /************************************************************************/
    3849             : /*                      OGR_G_CreateFromGMLTree()                       */
    3850             : /************************************************************************/
    3851             : 
    3852             : /** Create geometry from GML */
    3853         250 : OGRGeometryH OGR_G_CreateFromGMLTree(const CPLXMLNode *psTree)
    3854             : 
    3855             : {
    3856         250 :     return OGRGeometry::ToHandle(GML2OGRGeometry_XMLNode(psTree, -1));
    3857             : }
    3858             : 
    3859             : /************************************************************************/
    3860             : /*                        OGR_G_CreateFromGML()                         */
    3861             : /************************************************************************/
    3862             : 
    3863             : /**
    3864             :  * \brief Create geometry from GML.
    3865             :  *
    3866             :  * This method translates a fragment of GML containing only the geometry
    3867             :  * portion into a corresponding OGRGeometry.  There are many limitations
    3868             :  * on the forms of GML geometries supported by this parser, but they are
    3869             :  * too numerous to list here.
    3870             :  *
    3871             :  * The following GML2 elements are parsed : Point, LineString, Polygon,
    3872             :  * MultiPoint, MultiLineString, MultiPolygon, MultiGeometry.
    3873             :  *
    3874             :  * The following GML3 elements are parsed : Surface,
    3875             :  * MultiSurface, PolygonPatch, Triangle, Rectangle, Curve, MultiCurve,
    3876             :  * CompositeCurve, LineStringSegment, Arc, Circle, CompositeSurface,
    3877             :  * Shell, OrientableSurface, Solid, Tin, TriangulatedSurface.
    3878             :  *
    3879             :  * Arc and Circle elements are returned as curves by default. Stroking to
    3880             :  * linestrings can be done with
    3881             :  * OGR_G_ForceTo(hGeom, OGR_GT_GetLinear(OGR_G_GetGeometryType(hGeom)), NULL).
    3882             :  * A 4 degrees step is used by default, unless the user
    3883             :  * has overridden the value with the OGR_ARC_STEPSIZE configuration variable.
    3884             :  *
    3885             :  * The C++ method OGRGeometryFactory::createFromGML() is the same as
    3886             :  * this function.
    3887             :  *
    3888             :  * @param pszGML The GML fragment for the geometry.
    3889             :  *
    3890             :  * @return a geometry on success, or NULL on error.
    3891             :  *
    3892             :  * @see OGR_G_ForceTo()
    3893             :  * @see OGR_GT_GetLinear()
    3894             :  * @see OGR_G_GetGeometryType()
    3895             :  */
    3896             : 
    3897         848 : OGRGeometryH OGR_G_CreateFromGML(const char *pszGML)
    3898             : 
    3899             : {
    3900         848 :     if (pszGML == nullptr || strlen(pszGML) == 0)
    3901             :     {
    3902           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3903             :                  "GML Geometry is empty in OGR_G_CreateFromGML().");
    3904           0 :         return nullptr;
    3905             :     }
    3906             : 
    3907             :     /* -------------------------------------------------------------------- */
    3908             :     /*      Try to parse the XML snippet using the MiniXML API.  If this    */
    3909             :     /*      fails, we assume the minixml api has already posted a CPL       */
    3910             :     /*      error, and just return NULL.                                    */
    3911             :     /* -------------------------------------------------------------------- */
    3912         848 :     CPLXMLNode *psGML = CPLParseXMLString(pszGML);
    3913             : 
    3914         848 :     if (psGML == nullptr)
    3915           3 :         return nullptr;
    3916             : 
    3917             :     /* -------------------------------------------------------------------- */
    3918             :     /*      Convert geometry recursively.                                   */
    3919             :     /* -------------------------------------------------------------------- */
    3920             :     // Must be in synced in OGR_G_CreateFromGML(), OGRGMLLayer::OGRGMLLayer()
    3921             :     // and GMLReader::GMLReader().
    3922             :     const bool bFaceHoleNegative =
    3923         845 :         CPLTestBool(CPLGetConfigOption("GML_FACE_HOLE_NEGATIVE", "NO"));
    3924         845 :     OGRGeometry *poGeometry = GML2OGRGeometry_XMLNode(psGML, -1, 0, 0, false,
    3925             :                                                       true, bFaceHoleNegative);
    3926             : 
    3927         845 :     CPLDestroyXMLNode(psGML);
    3928             : 
    3929         845 :     return OGRGeometry::ToHandle(poGeometry);
    3930             : }

Generated by: LCOV version 1.14