Coverage Report

Created: 2025-06-09 08:44

/src/gdal/ogr/ogrsf_frmts/gmlutils/gmlfeatureclass.cpp
Line
Count
Source (jump to first uncovered line)
1
/**********************************************************************
2
 *
3
 * Project:  GML Reader
4
 * Purpose:  Implementation of GMLFeatureClass.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 **********************************************************************
8
 * Copyright (c) 2002, Frank Warmerdam
9
 * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "cpl_port.h"
15
#include "gmlfeature.h"
16
17
#include <cmath>
18
#include <cstddef>
19
#include <cstdio>
20
#include <cstdlib>
21
#include <cstring>
22
#include <string>
23
24
#include "cpl_conv.h"
25
#include "cpl_error.h"
26
#include "cpl_minixml.h"
27
#include "cpl_string.h"
28
#include "ogr_core.h"
29
#include "ogr_p.h"
30
#include "ogr_geometry.h"
31
32
/************************************************************************/
33
/*                          GMLFeatureClass()                           */
34
/************************************************************************/
35
36
GMLFeatureClass::GMLFeatureClass(const char *pszName)
37
135k
    : m_pszName(CPLStrdup(pszName)), m_pszElementName(nullptr),
38
135k
      n_nNameLen(static_cast<int>(strlen(pszName))), n_nElementNameLen(0),
39
135k
      m_nPropertyCount(0), m_papoProperty(nullptr), m_nGeometryPropertyCount(0),
40
135k
      m_papoGeometryProperty(nullptr), m_bSchemaLocked(false),
41
135k
      m_nFeatureCount(-1),  // Unknown.
42
135k
      m_pszExtraInfo(nullptr), m_bHaveExtents(false), m_dfXMin(0.0),
43
135k
      m_dfXMax(0.0), m_dfYMin(0.0), m_dfYMax(0.0), m_pszSRSName(nullptr),
44
135k
      m_bSRSNameConsistent(true)
45
135k
{
46
135k
}
47
48
/************************************************************************/
49
/*                          ~GMLFeatureClass()                          */
50
/************************************************************************/
51
52
GMLFeatureClass::~GMLFeatureClass()
53
54
135k
{
55
135k
    CPLFree(m_pszName);
56
135k
    CPLFree(m_pszElementName);
57
58
333k
    for (int i = 0; i < m_nPropertyCount; i++)
59
197k
        delete m_papoProperty[i];
60
135k
    CPLFree(m_papoProperty);
61
62
135k
    ClearGeometryProperties();
63
64
135k
    CPLFree(m_pszSRSName);
65
135k
}
66
67
/************************************************************************/
68
/*                         StealProperties()                            */
69
/************************************************************************/
70
71
void GMLFeatureClass::StealProperties()
72
382
{
73
382
    m_nPropertyCount = 0;
74
382
    CPLFree(m_papoProperty);
75
382
    m_papoProperty = nullptr;
76
382
    m_oMapPropertyNameToIndex.clear();
77
382
    m_oMapPropertySrcElementToIndex.clear();
78
382
}
79
80
/************************************************************************/
81
/*                       StealGeometryProperties()                      */
82
/************************************************************************/
83
84
void GMLFeatureClass::StealGeometryProperties()
85
382
{
86
382
    m_nGeometryPropertyCount = 0;
87
382
    CPLFree(m_papoGeometryProperty);
88
382
    m_papoGeometryProperty = nullptr;
89
382
}
90
91
/************************************************************************/
92
/*                            SetName()                                 */
93
/************************************************************************/
94
95
void GMLFeatureClass::SetName(const char *pszNewName)
96
383
{
97
383
    CPLFree(m_pszName);
98
383
    m_pszName = CPLStrdup(pszNewName);
99
383
}
100
101
/************************************************************************/
102
/*                           GetProperty(int)                           */
103
/************************************************************************/
104
105
GMLPropertyDefn *GMLFeatureClass::GetProperty(int iIndex) const
106
107
1.31M
{
108
1.31M
    if (iIndex < 0 || iIndex >= m_nPropertyCount)
109
216k
        return nullptr;
110
111
1.09M
    return m_papoProperty[iIndex];
112
1.31M
}
113
114
/************************************************************************/
115
/*                          GetPropertyIndex()                          */
116
/************************************************************************/
117
118
int GMLFeatureClass::GetPropertyIndex(const char *pszName) const
119
120
245k
{
121
245k
    auto oIter = m_oMapPropertyNameToIndex.find(CPLString(pszName).toupper());
122
245k
    if (oIter != m_oMapPropertyNameToIndex.end())
123
26.9k
        return oIter->second;
124
125
218k
    return -1;
126
245k
}
127
128
/************************************************************************/
129
/*                        GetPropertyIndexBySrcElement()                */
130
/************************************************************************/
131
132
int GMLFeatureClass::GetPropertyIndexBySrcElement(const char *pszElement,
133
                                                  int nLen) const
134
135
16.1k
{
136
16.1k
    auto oIter =
137
16.1k
        m_oMapPropertySrcElementToIndex.find(CPLString(pszElement, nLen));
138
16.1k
    if (oIter != m_oMapPropertySrcElementToIndex.end())
139
0
        return oIter->second;
140
141
16.1k
    return -1;
142
16.1k
}
143
144
/************************************************************************/
145
/*                            AddProperty()                             */
146
/************************************************************************/
147
148
int GMLFeatureClass::AddProperty(GMLPropertyDefn *poDefn, int iPos)
149
150
208k
{
151
208k
    if (GetProperty(poDefn->GetName()) != nullptr)
152
8.58k
    {
153
8.58k
        CPLError(CE_Warning, CPLE_AppDefined,
154
8.58k
                 "Field with same name (%s) already exists in (%s). "
155
8.58k
                 "Skipping newer ones",
156
8.58k
                 poDefn->GetName(), m_pszName);
157
8.58k
        return -1;
158
8.58k
    }
159
160
199k
    m_nPropertyCount++;
161
199k
    m_papoProperty = static_cast<GMLPropertyDefn **>(
162
199k
        CPLRealloc(m_papoProperty, sizeof(void *) * m_nPropertyCount));
163
164
199k
    if (iPos < 0)
165
199k
    {
166
199k
        iPos = m_nPropertyCount - 1;
167
199k
    }
168
197
    else if (iPos < m_nPropertyCount - 1)
169
168
    {
170
168
        memmove(m_papoProperty + iPos + 1, m_papoProperty + iPos,
171
168
                (m_nPropertyCount - 1 - iPos) * sizeof(GMLPropertyDefn *));
172
168
        for (auto &iter : m_oMapPropertyNameToIndex)
173
2.96k
        {
174
2.96k
            if (iter.second >= iPos)
175
2.91k
                iter.second++;
176
2.96k
        }
177
168
        for (auto &iter : m_oMapPropertySrcElementToIndex)
178
2.96k
        {
179
2.96k
            if (iter.second >= iPos)
180
2.91k
                iter.second++;
181
2.96k
        }
182
168
    }
183
184
199k
    m_papoProperty[iPos] = poDefn;
185
199k
    m_oMapPropertyNameToIndex[CPLString(poDefn->GetName()).toupper()] = iPos;
186
199k
    if (m_oMapPropertySrcElementToIndex.find(poDefn->GetSrcElement()) ==
187
199k
        m_oMapPropertySrcElementToIndex.end())
188
199k
    {
189
199k
        m_oMapPropertySrcElementToIndex[poDefn->GetSrcElement()] = iPos;
190
199k
    }
191
192
199k
    return iPos;
193
208k
}
194
195
/************************************************************************/
196
/*                         GetGeometryProperty(int)                      */
197
/************************************************************************/
198
199
GMLGeometryPropertyDefn *GMLFeatureClass::GetGeometryProperty(int iIndex) const
200
114k
{
201
114k
    if (iIndex < 0 || iIndex >= m_nGeometryPropertyCount)
202
0
        return nullptr;
203
204
114k
    return m_papoGeometryProperty[iIndex];
205
114k
}
206
207
/************************************************************************/
208
/*                   GetGeometryPropertyIndexBySrcElement()             */
209
/************************************************************************/
210
211
int GMLFeatureClass::GetGeometryPropertyIndexBySrcElement(
212
    const char *pszElement) const
213
214
129k
{
215
688k
    for (int i = 0; i < m_nGeometryPropertyCount; i++)
216
566k
        if (strcmp(pszElement, m_papoGeometryProperty[i]->GetSrcElement()) == 0)
217
7.99k
            return i;
218
219
121k
    return -1;
220
129k
}
221
222
/************************************************************************/
223
/*                         AddGeometryProperty()                        */
224
/************************************************************************/
225
226
int GMLFeatureClass::AddGeometryProperty(GMLGeometryPropertyDefn *poDefn)
227
228
119k
{
229
119k
    if (GetGeometryPropertyIndexBySrcElement(poDefn->GetSrcElement()) >= 0)
230
2.09k
    {
231
2.09k
        CPLError(CE_Warning, CPLE_AppDefined,
232
2.09k
                 "Geometry field with same name (%s) already exists in (%s). "
233
2.09k
                 "Skipping newer ones",
234
2.09k
                 poDefn->GetSrcElement(), m_pszName);
235
2.09k
        return -1;
236
2.09k
    }
237
238
117k
    m_nGeometryPropertyCount++;
239
117k
    m_papoGeometryProperty = static_cast<GMLGeometryPropertyDefn **>(CPLRealloc(
240
117k
        m_papoGeometryProperty, sizeof(void *) * m_nGeometryPropertyCount));
241
242
117k
    m_papoGeometryProperty[m_nGeometryPropertyCount - 1] = poDefn;
243
244
117k
    return m_nGeometryPropertyCount - 1;
245
119k
}
246
247
/************************************************************************/
248
/*                       ClearGeometryProperties()                      */
249
/************************************************************************/
250
251
void GMLFeatureClass::ClearGeometryProperties()
252
135k
{
253
250k
    for (int i = 0; i < m_nGeometryPropertyCount; i++)
254
115k
        delete m_papoGeometryProperty[i];
255
135k
    CPLFree(m_papoGeometryProperty);
256
135k
    m_nGeometryPropertyCount = 0;
257
135k
    m_papoGeometryProperty = nullptr;
258
135k
}
259
260
/************************************************************************/
261
/*                         HasFeatureProperties()                       */
262
/************************************************************************/
263
264
bool GMLFeatureClass::HasFeatureProperties()
265
220k
{
266
540k
    for (int i = 0; i < m_nPropertyCount; i++)
267
319k
    {
268
319k
        if (m_papoProperty[i]->GetType() == GMLPT_FeatureProperty ||
269
319k
            m_papoProperty[i]->GetType() == GMLPT_FeaturePropertyList)
270
0
            return true;
271
319k
    }
272
220k
    return false;
273
220k
}
274
275
/************************************************************************/
276
/*                           SetElementName()                           */
277
/************************************************************************/
278
279
void GMLFeatureClass::SetElementName(const char *pszElementName)
280
281
526
{
282
526
    CPLFree(m_pszElementName);
283
526
    m_pszElementName = CPLStrdup(pszElementName);
284
526
    n_nElementNameLen = static_cast<int>(strlen(pszElementName));
285
526
}
286
287
/************************************************************************/
288
/*                           GetElementName()                           */
289
/************************************************************************/
290
291
const char *GMLFeatureClass::GetElementName() const
292
293
1.12M
{
294
1.12M
    if (m_pszElementName == nullptr)
295
1.12M
        return m_pszName;
296
297
306
    return m_pszElementName;
298
1.12M
}
299
300
/************************************************************************/
301
/*                           GetElementName()                           */
302
/************************************************************************/
303
304
size_t GMLFeatureClass::GetElementNameLen() const
305
306
347k
{
307
347k
    if (m_pszElementName == nullptr)
308
346k
        return n_nNameLen;
309
310
474
    return n_nElementNameLen;
311
347k
}
312
313
/************************************************************************/
314
/*                         GetFeatureCount()                          */
315
/************************************************************************/
316
317
GIntBig GMLFeatureClass::GetFeatureCount()
318
369k
{
319
369k
    return m_nFeatureCount;
320
369k
}
321
322
/************************************************************************/
323
/*                          SetFeatureCount()                           */
324
/************************************************************************/
325
326
void GMLFeatureClass::SetFeatureCount(GIntBig nNewCount)
327
328
118k
{
329
118k
    m_nFeatureCount = nNewCount;
330
118k
}
331
332
/************************************************************************/
333
/*                            GetExtraInfo()                            */
334
/************************************************************************/
335
336
const char *GMLFeatureClass::GetExtraInfo()
337
0
{
338
0
    return m_pszExtraInfo;
339
0
}
340
341
/************************************************************************/
342
/*                            SetExtraInfo()                            */
343
/************************************************************************/
344
345
void GMLFeatureClass::SetExtraInfo(const char *pszExtraInfo)
346
347
0
{
348
0
    CPLFree(m_pszExtraInfo);
349
0
    m_pszExtraInfo = nullptr;
350
351
0
    if (pszExtraInfo != nullptr)
352
0
        m_pszExtraInfo = CPLStrdup(pszExtraInfo);
353
0
}
354
355
/************************************************************************/
356
/*                             SetExtents()                             */
357
/************************************************************************/
358
359
void GMLFeatureClass::SetExtents(double dfXMin, double dfXMax, double dfYMin,
360
                                 double dfYMax)
361
362
15.8k
{
363
15.8k
    m_dfXMin = dfXMin;
364
15.8k
    m_dfXMax = dfXMax;
365
15.8k
    m_dfYMin = dfYMin;
366
15.8k
    m_dfYMax = dfYMax;
367
368
15.8k
    m_bHaveExtents = true;
369
15.8k
}
370
371
/************************************************************************/
372
/*                             GetExtents()                             */
373
/************************************************************************/
374
375
bool GMLFeatureClass::GetExtents(double *pdfXMin, double *pdfXMax,
376
                                 double *pdfYMin, double *pdfYMax)
377
378
11.0k
{
379
11.0k
    if (m_bHaveExtents)
380
10.5k
    {
381
10.5k
        *pdfXMin = m_dfXMin;
382
10.5k
        *pdfXMax = m_dfXMax;
383
10.5k
        *pdfYMin = m_dfYMin;
384
10.5k
        *pdfYMax = m_dfYMax;
385
10.5k
    }
386
387
11.0k
    return m_bHaveExtents;
388
11.0k
}
389
390
/************************************************************************/
391
/*                            SetSRSName()                              */
392
/************************************************************************/
393
394
void GMLFeatureClass::SetSRSName(const char *pszSRSName)
395
396
7.02k
{
397
7.02k
    m_bSRSNameConsistent = true;
398
7.02k
    CPLFree(m_pszSRSName);
399
7.02k
    m_pszSRSName = pszSRSName ? CPLStrdup(pszSRSName) : nullptr;
400
7.02k
}
401
402
/************************************************************************/
403
/*                           MergeSRSName()                             */
404
/************************************************************************/
405
406
void GMLFeatureClass::MergeSRSName(const char *pszSRSName)
407
408
12.9k
{
409
12.9k
    if (!m_bSRSNameConsistent)
410
9.41k
        return;
411
412
3.48k
    if (m_pszSRSName == nullptr)
413
2.97k
    {
414
2.97k
        if (pszSRSName)
415
302
            m_pszSRSName = CPLStrdup(pszSRSName);
416
2.97k
    }
417
510
    else
418
510
    {
419
510
        m_bSRSNameConsistent =
420
510
            pszSRSName != nullptr && strcmp(m_pszSRSName, pszSRSName) == 0;
421
510
        if (!m_bSRSNameConsistent)
422
370
        {
423
370
            CPLFree(m_pszSRSName);
424
370
            m_pszSRSName = nullptr;
425
370
        }
426
510
    }
427
3.48k
}
428
429
/************************************************************************/
430
/*                         InitializeFromXML()                          */
431
/************************************************************************/
432
433
bool GMLFeatureClass::InitializeFromXML(CPLXMLNode *psRoot)
434
435
34
{
436
    // Do some rudimentary checking that this is a well formed node.
437
34
    if (psRoot == nullptr || psRoot->eType != CXT_Element ||
438
34
        !EQUAL(psRoot->pszValue, "GMLFeatureClass"))
439
0
    {
440
0
        CPLError(CE_Failure, CPLE_AppDefined,
441
0
                 "GMLFeatureClass::InitializeFromXML() called on %s node!",
442
0
                 psRoot ? psRoot->pszValue : "(null)");
443
0
        return false;
444
0
    }
445
446
34
    if (CPLGetXMLValue(psRoot, "Name", nullptr) == nullptr)
447
0
    {
448
0
        CPLError(CE_Failure, CPLE_AppDefined,
449
0
                 "GMLFeatureClass has no <Name> element.");
450
0
        return false;
451
0
    }
452
453
    // Collect base info.
454
34
    CPLFree(m_pszName);
455
34
    m_pszName = CPLStrdup(CPLGetXMLValue(psRoot, "Name", nullptr));
456
34
    n_nNameLen = static_cast<int>(strlen(m_pszName));
457
458
34
    SetElementName(CPLGetXMLValue(psRoot, "ElementPath", m_pszName));
459
460
    // Collect geometry properties.
461
34
    bool bHasValidGeometryName = false;
462
34
    bool bHasValidGeometryElementPath = false;
463
34
    bool bHasFoundGeomType = false;
464
34
    bool bHasFoundGeomElements = false;
465
34
    const char *pszGName = "";
466
34
    const char *pszGPath = "";
467
34
    OGRwkbGeometryType nGeomType = wkbUnknown;
468
469
34
    const auto FlattenGeomTypeFromInt = [](int eType)
470
34
    {
471
34
        eType = eType & (~wkb25DBitInternalUse);
472
34
        if (eType >= 1000 && eType < 2000)  // ISO Z.
473
0
            return eType - 1000;
474
34
        if (eType >= 2000 && eType < 3000)  // ISO M.
475
0
            return eType - 2000;
476
34
        if (eType >= 3000 && eType < 4000)  // ISO ZM.
477
0
            return eType - 3000;
478
34
        return eType;
479
34
    };
480
481
34
    CPLXMLNode *psThis = nullptr;
482
102
    for (psThis = psRoot->psChild; psThis != nullptr; psThis = psThis->psNext)
483
102
    {
484
102
        if (psThis->eType == CXT_Element &&
485
102
            EQUAL(psThis->pszValue, "GeomPropertyDefn"))
486
0
        {
487
0
            const char *pszName = CPLGetXMLValue(psThis, "Name", "");
488
0
            const char *pszElementPath =
489
0
                CPLGetXMLValue(psThis, "ElementPath", "");
490
0
            const char *pszType = CPLGetXMLValue(psThis, "Type", nullptr);
491
0
            const bool bNullable =
492
0
                CPLTestBool(CPLGetXMLValue(psThis, "Nullable", "true"));
493
0
            nGeomType = wkbUnknown;
494
0
            if (pszType != nullptr && !EQUAL(pszType, "0"))
495
0
            {
496
0
                int nGeomTypeInt = atoi(pszType);
497
0
                const int nFlattenGeomTypeInt =
498
0
                    FlattenGeomTypeFromInt(nGeomTypeInt);
499
0
                if (nGeomTypeInt != 0 &&
500
0
                    !(nFlattenGeomTypeInt >= static_cast<int>(wkbPoint) &&
501
0
                      nFlattenGeomTypeInt <= static_cast<int>(wkbTIN)))
502
0
                {
503
0
                    CPLError(CE_Warning, CPLE_AppDefined,
504
0
                             "Unrecognized geometry type : %s", pszType);
505
0
                }
506
0
                else if (nGeomTypeInt == 0)
507
0
                {
508
0
                    nGeomType = OGRFromOGCGeomType(pszType);
509
0
                }
510
0
                else
511
0
                {
512
0
                    nGeomType = static_cast<OGRwkbGeometryType>(nGeomTypeInt);
513
0
                }
514
0
            }
515
0
            bHasFoundGeomElements = true;
516
0
            auto poDefn = new GMLGeometryPropertyDefn(pszName, pszElementPath,
517
0
                                                      nGeomType, -1, bNullable);
518
0
            if (AddGeometryProperty(poDefn) < 0)
519
0
                delete poDefn;
520
0
            bHasValidGeometryName = false;
521
0
            bHasValidGeometryElementPath = false;
522
0
            bHasFoundGeomType = false;
523
0
        }
524
102
        else if (psThis->eType == CXT_Element &&
525
102
                 strcmp(psThis->pszValue, "GeometryName") == 0)
526
0
        {
527
0
            bHasFoundGeomElements = true;
528
529
0
            if (bHasValidGeometryName)
530
0
            {
531
0
                auto poDefn = new GMLGeometryPropertyDefn(pszGName, pszGPath,
532
0
                                                          nGeomType, -1, true);
533
0
                if (AddGeometryProperty(poDefn) < 0)
534
0
                    delete poDefn;
535
                // bHasValidGeometryName = false;
536
0
                bHasValidGeometryElementPath = false;
537
0
                bHasFoundGeomType = false;
538
0
                pszGPath = "";
539
0
                nGeomType = wkbUnknown;
540
0
            }
541
0
            pszGName = CPLGetXMLValue(psThis, nullptr, "");
542
0
            bHasValidGeometryName = true;
543
0
        }
544
102
        else if (psThis->eType == CXT_Element &&
545
102
                 strcmp(psThis->pszValue, "GeometryElementPath") == 0)
546
0
        {
547
0
            bHasFoundGeomElements = true;
548
549
0
            if (bHasValidGeometryElementPath)
550
0
            {
551
0
                auto poDefn = new GMLGeometryPropertyDefn(pszGName, pszGPath,
552
0
                                                          nGeomType, -1, true);
553
0
                if (AddGeometryProperty(poDefn) < 0)
554
0
                    delete poDefn;
555
0
                bHasValidGeometryName = false;
556
                // bHasValidGeometryElementPath = false;
557
0
                bHasFoundGeomType = false;
558
0
                pszGName = "";
559
0
                nGeomType = wkbUnknown;
560
0
            }
561
0
            pszGPath = CPLGetXMLValue(psThis, nullptr, "");
562
0
            bHasValidGeometryElementPath = true;
563
0
        }
564
102
        else if (psThis->eType == CXT_Element &&
565
102
                 strcmp(psThis->pszValue, "GeometryType") == 0)
566
34
        {
567
34
            bHasFoundGeomElements = true;
568
569
34
            if (bHasFoundGeomType)
570
0
            {
571
0
                auto poDefn = new GMLGeometryPropertyDefn(pszGName, pszGPath,
572
0
                                                          nGeomType, -1, true);
573
0
                if (AddGeometryProperty(poDefn) < 0)
574
0
                    delete poDefn;
575
0
                bHasValidGeometryName = false;
576
0
                bHasValidGeometryElementPath = false;
577
                // bHasFoundGeomType = false;
578
0
                pszGName = "";
579
0
                pszGPath = "";
580
0
            }
581
34
            const char *pszGeometryType =
582
34
                CPLGetXMLValue(psThis, nullptr, nullptr);
583
34
            nGeomType = wkbUnknown;
584
34
            if (pszGeometryType != nullptr && !EQUAL(pszGeometryType, "0"))
585
34
            {
586
34
                const int nGeomTypeInt = atoi(pszGeometryType);
587
34
                const int nFlattenGeomTypeInt =
588
34
                    FlattenGeomTypeFromInt(nGeomTypeInt);
589
34
                if (nGeomTypeInt == 100 || EQUAL(pszGeometryType, "NONE"))
590
34
                {
591
34
                    bHasValidGeometryElementPath = false;
592
34
                    bHasFoundGeomType = false;
593
34
                    break;
594
34
                }
595
0
                else if (nGeomTypeInt != 0 &&
596
0
                         !(nFlattenGeomTypeInt >= static_cast<int>(wkbPoint) &&
597
0
                           nFlattenGeomTypeInt <= static_cast<int>(wkbTIN)))
598
0
                {
599
0
                    CPLError(CE_Warning, CPLE_AppDefined,
600
0
                             "Unrecognized geometry type : %s",
601
0
                             pszGeometryType);
602
0
                }
603
0
                else if (nGeomTypeInt == 0)
604
0
                {
605
0
                    nGeomType = OGRFromOGCGeomType(pszGeometryType);
606
0
                }
607
0
                else
608
0
                {
609
0
                    nGeomType = static_cast<OGRwkbGeometryType>(nGeomTypeInt);
610
0
                }
611
34
            }
612
0
            bHasFoundGeomType = true;
613
0
        }
614
102
    }
615
616
    // If there was a dangling <GeometryElementPath> or <GeometryType> or
617
    // that no explicit geometry information has been found, then add
618
    // a geometry field.
619
34
    if (bHasValidGeometryElementPath || bHasFoundGeomType ||
620
34
        !bHasFoundGeomElements)
621
0
    {
622
0
        auto poDefn = new GMLGeometryPropertyDefn(pszGName, pszGPath, nGeomType,
623
0
                                                  -1, true);
624
0
        if (AddGeometryProperty(poDefn) < 0)
625
0
            delete poDefn;
626
0
    }
627
628
34
    SetSRSName(CPLGetXMLValue(psRoot, "SRSName", nullptr));
629
630
    // Collect dataset specific info.
631
34
    CPLXMLNode *psDSI = CPLGetXMLNode(psRoot, "DatasetSpecificInfo");
632
34
    if (psDSI != nullptr)
633
23
    {
634
23
        const char *pszValue = CPLGetXMLValue(psDSI, "FeatureCount", nullptr);
635
23
        if (pszValue != nullptr)
636
23
            SetFeatureCount(CPLAtoGIntBig(pszValue));
637
638
        // Eventually we should support XML subtrees.
639
23
        pszValue = CPLGetXMLValue(psDSI, "ExtraInfo", nullptr);
640
23
        if (pszValue != nullptr)
641
0
            SetExtraInfo(pszValue);
642
643
23
        if (CPLGetXMLValue(psDSI, "ExtentXMin", nullptr) != nullptr &&
644
23
            CPLGetXMLValue(psDSI, "ExtentXMax", nullptr) != nullptr &&
645
23
            CPLGetXMLValue(psDSI, "ExtentYMin", nullptr) != nullptr &&
646
23
            CPLGetXMLValue(psDSI, "ExtentYMax", nullptr) != nullptr)
647
0
        {
648
0
            SetExtents(CPLAtof(CPLGetXMLValue(psDSI, "ExtentXMin", "0.0")),
649
0
                       CPLAtof(CPLGetXMLValue(psDSI, "ExtentXMax", "0.0")),
650
0
                       CPLAtof(CPLGetXMLValue(psDSI, "ExtentYMin", "0.0")),
651
0
                       CPLAtof(CPLGetXMLValue(psDSI, "ExtentYMax", "0.0")));
652
0
        }
653
23
    }
654
655
    // Collect property definitions.
656
159
    for (psThis = psRoot->psChild; psThis != nullptr; psThis = psThis->psNext)
657
125
    {
658
125
        if (psThis->eType == CXT_Element &&
659
125
            EQUAL(psThis->pszValue, "PropertyDefn"))
660
0
        {
661
0
            const char *pszName = CPLGetXMLValue(psThis, "Name", nullptr);
662
0
            const char *pszType = CPLGetXMLValue(psThis, "Type", "Untyped");
663
0
            const char *pszSubType = CPLGetXMLValue(psThis, "Subtype", "");
664
0
            const char *pszCondition =
665
0
                CPLGetXMLValue(psThis, "Condition", nullptr);
666
0
            const bool bNullable =
667
0
                CPLTestBool(CPLGetXMLValue(psThis, "Nullable", "true"));
668
0
            const bool bUnique =
669
0
                CPLTestBool(CPLGetXMLValue(psThis, "Unique", "false"));
670
0
            const char *pszComment = CPLGetXMLValue(psThis, "Comment", nullptr);
671
672
0
            if (pszName == nullptr)
673
0
            {
674
0
                CPLError(
675
0
                    CE_Failure, CPLE_AppDefined,
676
0
                    "GMLFeatureClass %s has a PropertyDefn without a <Name>.",
677
0
                    m_pszName);
678
0
                return false;
679
0
            }
680
681
0
            GMLPropertyDefn *poPDefn = new GMLPropertyDefn(
682
0
                pszName, CPLGetXMLValue(psThis, "ElementPath", nullptr));
683
684
0
            poPDefn->SetNullable(bNullable);
685
0
            poPDefn->SetUnique(bUnique);
686
0
            if (EQUAL(pszType, "Untyped"))
687
0
            {
688
0
                poPDefn->SetType(GMLPT_Untyped);
689
0
            }
690
0
            else if (EQUAL(pszType, "String"))
691
0
            {
692
0
                if (EQUAL(pszSubType, "Boolean"))
693
0
                {
694
0
                    poPDefn->SetType(GMLPT_Boolean);
695
0
                    poPDefn->SetWidth(1);
696
0
                }
697
0
                else if (EQUAL(pszSubType, "Date"))
698
0
                {
699
0
                    poPDefn->SetType(GMLPT_Date);
700
0
                }
701
0
                else if (EQUAL(pszSubType, "Time"))
702
0
                {
703
0
                    poPDefn->SetType(GMLPT_Time);
704
0
                }
705
0
                else if (EQUAL(pszSubType, "Datetime"))
706
0
                {
707
0
                    poPDefn->SetType(GMLPT_DateTime);
708
0
                }
709
0
                else
710
0
                {
711
0
                    poPDefn->SetType(GMLPT_String);
712
0
                    poPDefn->SetWidth(
713
0
                        atoi(CPLGetXMLValue(psThis, "Width", "0")));
714
0
                }
715
0
            }
716
0
            else if (EQUAL(pszType, "Integer"))
717
0
            {
718
0
                if (EQUAL(pszSubType, "Short"))
719
0
                {
720
0
                    poPDefn->SetType(GMLPT_Short);
721
0
                }
722
0
                else if (EQUAL(pszSubType, "Integer64"))
723
0
                {
724
0
                    poPDefn->SetType(GMLPT_Integer64);
725
0
                }
726
0
                else
727
0
                {
728
0
                    poPDefn->SetType(GMLPT_Integer);
729
0
                }
730
0
                poPDefn->SetWidth(atoi(CPLGetXMLValue(psThis, "Width", "0")));
731
0
            }
732
0
            else if (EQUAL(pszType, "Real"))
733
0
            {
734
0
                if (EQUAL(pszSubType, "Float"))
735
0
                {
736
0
                    poPDefn->SetType(GMLPT_Float);
737
0
                }
738
0
                else
739
0
                {
740
0
                    poPDefn->SetType(GMLPT_Real);
741
0
                }
742
0
                poPDefn->SetWidth(atoi(CPLGetXMLValue(psThis, "Width", "0")));
743
0
                poPDefn->SetPrecision(
744
0
                    atoi(CPLGetXMLValue(psThis, "Precision", "0")));
745
0
            }
746
0
            else if (EQUAL(pszType, "StringList"))
747
0
            {
748
0
                if (EQUAL(pszSubType, "Boolean"))
749
0
                    poPDefn->SetType(GMLPT_BooleanList);
750
0
                else
751
0
                    poPDefn->SetType(GMLPT_StringList);
752
0
            }
753
0
            else if (EQUAL(pszType, "IntegerList"))
754
0
            {
755
0
                if (EQUAL(pszSubType, "Integer64"))
756
0
                    poPDefn->SetType(GMLPT_Integer64List);
757
0
                else
758
0
                    poPDefn->SetType(GMLPT_IntegerList);
759
0
            }
760
0
            else if (EQUAL(pszType, "RealList"))
761
0
            {
762
0
                poPDefn->SetType(GMLPT_RealList);
763
0
            }
764
0
            else if (EQUAL(pszType, "Complex"))
765
0
            {
766
0
                poPDefn->SetType(GMLPT_Complex);
767
0
            }
768
0
            else if (EQUAL(pszType, "FeatureProperty"))
769
0
            {
770
0
                poPDefn->SetType(GMLPT_FeatureProperty);
771
0
            }
772
0
            else if (EQUAL(pszType, "FeaturePropertyList"))
773
0
            {
774
0
                poPDefn->SetType(GMLPT_FeaturePropertyList);
775
0
            }
776
0
            else
777
0
            {
778
0
                CPLError(CE_Failure, CPLE_AppDefined,
779
0
                         "Unrecognized property type (%s) in (%s).", pszType,
780
0
                         pszName);
781
0
                delete poPDefn;
782
0
                return false;
783
0
            }
784
0
            if (pszCondition != nullptr)
785
0
                poPDefn->SetCondition(pszCondition);
786
0
            if (pszComment != nullptr)
787
0
                poPDefn->SetDocumentation(pszComment);
788
789
0
            if (AddProperty(poPDefn) < 0)
790
0
                delete poPDefn;
791
0
        }
792
125
    }
793
794
34
    return true;
795
34
}
796
797
/************************************************************************/
798
/*                           SerializeToXML()                           */
799
/************************************************************************/
800
801
CPLXMLNode *GMLFeatureClass::SerializeToXML()
802
803
195
{
804
    // Set feature class and core information.
805
195
    CPLXMLNode *psRoot =
806
195
        CPLCreateXMLNode(nullptr, CXT_Element, "GMLFeatureClass");
807
808
195
    CPLCreateXMLElementAndValue(psRoot, "Name", GetName());
809
195
    CPLCreateXMLElementAndValue(psRoot, "ElementPath", GetElementName());
810
811
195
    if (m_nGeometryPropertyCount > 1)
812
0
    {
813
0
        for (int i = 0; i < m_nGeometryPropertyCount; i++)
814
0
        {
815
0
            GMLGeometryPropertyDefn *poGeomFDefn = m_papoGeometryProperty[i];
816
817
0
            CPLXMLNode *psPDefnNode =
818
0
                CPLCreateXMLNode(psRoot, CXT_Element, "GeomPropertyDefn");
819
0
            if (strlen(poGeomFDefn->GetName()) > 0)
820
0
                CPLCreateXMLElementAndValue(psPDefnNode, "Name",
821
0
                                            poGeomFDefn->GetName());
822
0
            if (poGeomFDefn->GetSrcElement() != nullptr &&
823
0
                strlen(poGeomFDefn->GetSrcElement()) > 0)
824
0
                CPLCreateXMLElementAndValue(psPDefnNode, "ElementPath",
825
0
                                            poGeomFDefn->GetSrcElement());
826
827
0
            if (poGeomFDefn->GetType() != 0 /* wkbUnknown */)
828
0
            {
829
0
                char szValue[128] = {};
830
831
0
                const OGRwkbGeometryType eType =
832
0
                    static_cast<OGRwkbGeometryType>(poGeomFDefn->GetType());
833
834
0
                CPLString osStr(OGRToOGCGeomType(eType));
835
0
                if (wkbHasZ(eType))
836
0
                    osStr += "Z";
837
0
                CPLCreateXMLNode(psPDefnNode, CXT_Comment, osStr.c_str());
838
839
0
                snprintf(szValue, sizeof(szValue), "%d", eType);
840
0
                CPLCreateXMLElementAndValue(psPDefnNode, "Type", szValue);
841
0
            }
842
0
        }
843
0
    }
844
195
    else if (m_nGeometryPropertyCount == 1)
845
0
    {
846
0
        GMLGeometryPropertyDefn *poGeomFDefn = m_papoGeometryProperty[0];
847
848
0
        if (strlen(poGeomFDefn->GetName()) > 0)
849
0
            CPLCreateXMLElementAndValue(psRoot, "GeometryName",
850
0
                                        poGeomFDefn->GetName());
851
852
0
        if (poGeomFDefn->GetSrcElement() != nullptr &&
853
0
            strlen(poGeomFDefn->GetSrcElement()) > 0)
854
0
            CPLCreateXMLElementAndValue(psRoot, "GeometryElementPath",
855
0
                                        poGeomFDefn->GetSrcElement());
856
857
0
        if (poGeomFDefn->GetType() != 0 /* wkbUnknown */)
858
0
        {
859
0
            char szValue[128] = {};
860
861
0
            OGRwkbGeometryType eType =
862
0
                static_cast<OGRwkbGeometryType>(poGeomFDefn->GetType());
863
864
0
            CPLString osStr(OGRToOGCGeomType(eType));
865
0
            if (wkbHasZ(eType))
866
0
                osStr += "Z";
867
0
            CPLCreateXMLNode(psRoot, CXT_Comment, osStr.c_str());
868
869
0
            snprintf(szValue, sizeof(szValue), "%d", eType);
870
0
            CPLCreateXMLElementAndValue(psRoot, "GeometryType", szValue);
871
0
        }
872
0
    }
873
195
    else
874
195
    {
875
195
        CPLCreateXMLElementAndValue(psRoot, "GeometryType", "100");
876
195
    }
877
878
195
    const char *pszSRSName = GetSRSName();
879
195
    if (pszSRSName)
880
6
    {
881
6
        CPLCreateXMLElementAndValue(psRoot, "SRSName", pszSRSName);
882
6
    }
883
884
    // Write out dataset specific information.
885
195
    if (m_bHaveExtents || m_nFeatureCount != -1 || m_pszExtraInfo != nullptr)
886
141
    {
887
141
        CPLXMLNode *psDSI =
888
141
            CPLCreateXMLNode(psRoot, CXT_Element, "DatasetSpecificInfo");
889
890
141
        if (m_nFeatureCount != -1)
891
141
        {
892
141
            char szValue[128] = {};
893
894
141
            snprintf(szValue, sizeof(szValue), CPL_FRMT_GIB, m_nFeatureCount);
895
141
            CPLCreateXMLElementAndValue(psDSI, "FeatureCount", szValue);
896
141
        }
897
898
141
        if (m_bHaveExtents && fabs(m_dfXMin) < 1e100 &&
899
141
            fabs(m_dfXMax) < 1e100 && fabs(m_dfYMin) < 1e100 &&
900
141
            fabs(m_dfYMax) < 1e100)
901
0
        {
902
0
            char szValue[128] = {};
903
904
0
            CPLsnprintf(szValue, sizeof(szValue), "%.5f", m_dfXMin);
905
0
            CPLCreateXMLElementAndValue(psDSI, "ExtentXMin", szValue);
906
907
0
            CPLsnprintf(szValue, sizeof(szValue), "%.5f", m_dfXMax);
908
0
            CPLCreateXMLElementAndValue(psDSI, "ExtentXMax", szValue);
909
910
0
            CPLsnprintf(szValue, sizeof(szValue), "%.5f", m_dfYMin);
911
0
            CPLCreateXMLElementAndValue(psDSI, "ExtentYMin", szValue);
912
913
0
            CPLsnprintf(szValue, sizeof(szValue), "%.5f", m_dfYMax);
914
0
            CPLCreateXMLElementAndValue(psDSI, "ExtentYMax", szValue);
915
0
        }
916
917
141
        if (m_pszExtraInfo)
918
0
            CPLCreateXMLElementAndValue(psDSI, "ExtraInfo", m_pszExtraInfo);
919
141
    }
920
921
195
    CPLXMLNode *psLastChild = psRoot->psChild;
922
732
    while (psLastChild->psNext)
923
537
    {
924
537
        psLastChild = psLastChild->psNext;
925
537
    }
926
927
    // Emit property information.
928
195
    for (int iProperty = 0; iProperty < GetPropertyCount(); iProperty++)
929
0
    {
930
0
        GMLPropertyDefn *poPDefn = GetProperty(iProperty);
931
0
        const char *pszTypeName = "Unknown";
932
0
        CPL_IGNORE_RET_VAL(pszTypeName);  // Make CSA happy
933
934
0
        CPLXMLNode *psPDefnNode =
935
0
            CPLCreateXMLNode(nullptr, CXT_Element, "PropertyDefn");
936
0
        psLastChild->psNext = psPDefnNode;
937
0
        psLastChild = psPDefnNode;
938
0
        CPLCreateXMLElementAndValue(psPDefnNode, "Name", poPDefn->GetName());
939
0
        CPLCreateXMLElementAndValue(psPDefnNode, "ElementPath",
940
0
                                    poPDefn->GetSrcElement());
941
0
        const auto gmlType = poPDefn->GetType();
942
0
        const char *pszSubTypeName = nullptr;
943
0
        switch (gmlType)
944
0
        {
945
0
            case GMLPT_Untyped:
946
0
                pszTypeName = "Untyped";
947
0
                break;
948
949
0
            case GMLPT_String:
950
0
                pszTypeName = "String";
951
0
                break;
952
953
0
            case GMLPT_Boolean:
954
0
                pszTypeName = "String";
955
0
                pszSubTypeName = "Boolean";
956
0
                break;
957
958
0
            case GMLPT_Date:
959
0
                pszTypeName = "String";
960
0
                pszSubTypeName = "Date";
961
0
                break;
962
963
0
            case GMLPT_Time:
964
0
                pszTypeName = "String";
965
0
                pszSubTypeName = "Time";
966
0
                break;
967
968
0
            case GMLPT_DateTime:
969
0
                pszTypeName = "String";
970
0
                pszSubTypeName = "DateTime";
971
0
                break;
972
973
0
            case GMLPT_Integer:
974
0
                pszTypeName = "Integer";
975
0
                break;
976
977
0
            case GMLPT_Short:
978
0
                pszTypeName = "Integer";
979
0
                pszSubTypeName = "Short";
980
0
                break;
981
982
0
            case GMLPT_Integer64:
983
0
                pszTypeName = "Integer";
984
0
                pszSubTypeName = "Integer64";
985
0
                break;
986
987
0
            case GMLPT_Real:
988
0
                pszTypeName = "Real";
989
0
                break;
990
991
0
            case GMLPT_Float:
992
0
                pszTypeName = "Real";
993
0
                pszSubTypeName = "Float";
994
0
                break;
995
996
0
            case GMLPT_Complex:
997
0
                pszTypeName = "Complex";
998
0
                break;
999
1000
0
            case GMLPT_IntegerList:
1001
0
                pszTypeName = "IntegerList";
1002
0
                break;
1003
1004
0
            case GMLPT_Integer64List:
1005
0
                pszTypeName = "IntegerList";
1006
0
                pszSubTypeName = "Integer64";
1007
0
                break;
1008
1009
0
            case GMLPT_RealList:
1010
0
                pszTypeName = "RealList";
1011
0
                break;
1012
1013
0
            case GMLPT_StringList:
1014
0
                pszTypeName = "StringList";
1015
0
                break;
1016
1017
0
            case GMLPT_BooleanList:
1018
0
                pszTypeName = "StringList";
1019
0
                pszSubTypeName = "Boolean";
1020
0
                break;
1021
1022
            // Should not happen in practice for now because this is not
1023
            // autodetected.
1024
0
            case GMLPT_FeatureProperty:
1025
0
                pszTypeName = "FeatureProperty";
1026
0
                break;
1027
1028
            // Should not happen in practice for now because this is not
1029
            // autodetected.
1030
0
            case GMLPT_FeaturePropertyList:
1031
0
                pszTypeName = "FeaturePropertyList";
1032
0
                break;
1033
0
        }
1034
0
        CPLCreateXMLElementAndValue(psPDefnNode, "Type", pszTypeName);
1035
0
        if (pszSubTypeName)
1036
0
            CPLCreateXMLElementAndValue(psPDefnNode, "Subtype", pszSubTypeName);
1037
1038
0
        if (EQUAL(pszTypeName, "String"))
1039
0
        {
1040
0
            char szMaxLength[48] = {};
1041
0
            snprintf(szMaxLength, sizeof(szMaxLength), "%d",
1042
0
                     poPDefn->GetWidth());
1043
0
            CPLCreateXMLElementAndValue(psPDefnNode, "Width", szMaxLength);
1044
0
        }
1045
0
        if (poPDefn->GetWidth() > 0 && EQUAL(pszTypeName, "Integer"))
1046
0
        {
1047
0
            char szLength[48] = {};
1048
0
            snprintf(szLength, sizeof(szLength), "%d", poPDefn->GetWidth());
1049
0
            CPLCreateXMLElementAndValue(psPDefnNode, "Width", szLength);
1050
0
        }
1051
0
        if (poPDefn->GetWidth() > 0 && EQUAL(pszTypeName, "Real"))
1052
0
        {
1053
0
            char szLength[48] = {};
1054
0
            snprintf(szLength, sizeof(szLength), "%d", poPDefn->GetWidth());
1055
0
            CPLCreateXMLElementAndValue(psPDefnNode, "Width", szLength);
1056
0
            char szPrecision[48] = {};
1057
0
            snprintf(szPrecision, sizeof(szPrecision), "%d",
1058
0
                     poPDefn->GetPrecision());
1059
0
            CPLCreateXMLElementAndValue(psPDefnNode, "Precision", szPrecision);
1060
0
        }
1061
0
        if (!poPDefn->GetDocumentation().empty())
1062
0
        {
1063
0
            CPLCreateXMLElementAndValue(psPDefnNode, "Comment",
1064
0
                                        poPDefn->GetDocumentation().c_str());
1065
0
        }
1066
0
    }
1067
1068
195
    return psRoot;
1069
195
}
1070
1071
/************************************************************************/
1072
/*                       GML_GetOGRFieldType()                          */
1073
/************************************************************************/
1074
1075
OGRFieldType GML_GetOGRFieldType(GMLPropertyType eType,
1076
                                 OGRFieldSubType &eSubType)
1077
160k
{
1078
160k
    OGRFieldType eFType = OFTString;
1079
160k
    if (eType == GMLPT_Untyped)
1080
7.89k
        eFType = OFTString;
1081
152k
    else if (eType == GMLPT_String)
1082
26.8k
        eFType = OFTString;
1083
125k
    else if (eType == GMLPT_Integer)
1084
1.20k
        eFType = OFTInteger;
1085
124k
    else if (eType == GMLPT_Boolean)
1086
221
    {
1087
221
        eFType = OFTInteger;
1088
221
        eSubType = OFSTBoolean;
1089
221
    }
1090
124k
    else if (eType == GMLPT_Short)
1091
0
    {
1092
0
        eFType = OFTInteger;
1093
0
        eSubType = OFSTInt16;
1094
0
    }
1095
124k
    else if (eType == GMLPT_Integer64)
1096
3.40k
        eFType = OFTInteger64;
1097
120k
    else if (eType == GMLPT_Real)
1098
3.57k
        eFType = OFTReal;
1099
117k
    else if (eType == GMLPT_Float)
1100
0
    {
1101
0
        eFType = OFTReal;
1102
0
        eSubType = OFSTFloat32;
1103
0
    }
1104
117k
    else if (eType == GMLPT_StringList)
1105
43.4k
        eFType = OFTStringList;
1106
73.9k
    else if (eType == GMLPT_IntegerList)
1107
783
        eFType = OFTIntegerList;
1108
73.1k
    else if (eType == GMLPT_BooleanList)
1109
546
    {
1110
546
        eFType = OFTIntegerList;
1111
546
        eSubType = OFSTBoolean;
1112
546
    }
1113
72.6k
    else if (eType == GMLPT_Integer64List)
1114
70.5k
        eFType = OFTInteger64List;
1115
2.01k
    else if (eType == GMLPT_RealList)
1116
1.72k
        eFType = OFTRealList;
1117
288
    else if (eType == GMLPT_Date)
1118
121
        eFType = OFTDate;
1119
167
    else if (eType == GMLPT_Time)
1120
79
        eFType = OFTTime;
1121
88
    else if (eType == GMLPT_DateTime)
1122
88
        eFType = OFTDateTime;
1123
0
    else if (eType == GMLPT_FeaturePropertyList)
1124
0
        eFType = OFTStringList;
1125
160k
    return eFType;
1126
160k
}
1127
1128
/************************************************************************/
1129
/*                       GML_FromOGRFieldType()                          */
1130
/************************************************************************/
1131
1132
GMLPropertyType GML_FromOGRFieldType(OGRFieldType eType,
1133
                                     OGRFieldSubType eSubType)
1134
0
{
1135
0
    GMLPropertyType type{GMLPT_Untyped};
1136
0
    switch (eType)
1137
0
    {
1138
0
        case OFTString:
1139
0
            type = GMLPT_String;
1140
0
            break;
1141
0
        case OFTInteger:
1142
0
        {
1143
0
            if (eSubType == OFSTBoolean)
1144
0
                type = GMLPT_Boolean;
1145
0
            else if (eSubType == OFSTInt16)
1146
0
                type = GMLPT_Short;
1147
0
            else
1148
0
                type = GMLPT_Integer;
1149
0
            break;
1150
0
        }
1151
0
        case OFTReal:
1152
0
            type = GMLPT_Real;
1153
0
            break;
1154
0
        case OFTDate:
1155
0
            type = GMLPT_Date;
1156
0
            break;
1157
0
        case OFTDateTime:
1158
0
            type = GMLPT_DateTime;
1159
0
            break;
1160
0
        case OFTTime:
1161
0
            type = GMLPT_Time;
1162
0
            break;
1163
0
        default:
1164
0
            type = GMLPT_Untyped;
1165
0
            break;
1166
0
    }
1167
0
    return type;
1168
0
}