Coverage Report

Created: 2026-02-14 06:52

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