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

Generated by: LCOV version 1.14