Coverage Report

Created: 2025-06-13 06:29

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