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

Generated by: LCOV version 1.14