LCOV - code coverage report
Current view: top level - ogr - gml2ogrgeometry.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1536 1751 87.7 %
Date: 2026-01-15 23:17:29 Functions: 20 20 100.0 %

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

Generated by: LCOV version 1.14