Coverage Report

Created: 2025-12-31 06:48

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