Coverage Report

Created: 2026-02-14 09:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/gml/ogrgmllayer.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  OGR
4
 * Purpose:  Implements OGRGMLLayer class.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2002, Frank Warmerdam <warmerdam@pobox.com>
9
 * Copyright (c) 2009-2013, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "ogr_gml.h"
15
#include "gmlutils.h"
16
#include "cpl_conv.h"
17
#include "cpl_port.h"
18
#include "cpl_string.h"
19
#include "ogr_p.h"
20
#include "ogr_api.h"
21
22
#include <limits>
23
24
/************************************************************************/
25
/*                            OGRGMLLayer()                             */
26
/************************************************************************/
27
28
OGRGMLLayer::OGRGMLLayer(const char *pszName, bool bWriterIn,
29
                         OGRGMLDataSource *poDSIn)
30
15.5k
    : poFeatureDefn(new OGRFeatureDefn(
31
15.5k
          pszName + (STARTS_WITH_CI(pszName, "ogr:") ? 4 : 0))),
32
15.5k
      bWriter(bWriterIn), poDS(poDSIn),
33
15.5k
      poFClass(!bWriter ? poDS->GetReader()->GetClass(pszName) : nullptr),
34
      // Compatibility option. Not advertized, because hopefully won't be
35
      // needed. Just put here in case.
36
      bUseOldFIDFormat(
37
15.5k
          CPLTestBool(CPLGetConfigOption("GML_USE_OLD_FID_FORMAT", "FALSE"))),
38
      // Must be in synced in OGR_G_CreateFromGML(), OGRGMLLayer::OGRGMLLayer()
39
      // and GMLReader::GMLReader().
40
      bFaceHoleNegative(
41
15.5k
          CPLTestBool(CPLGetConfigOption("GML_FACE_HOLE_NEGATIVE", "NO")))
42
15.5k
{
43
15.5k
    SetDescription(poFeatureDefn->GetName());
44
15.5k
    poFeatureDefn->Reference();
45
15.5k
    poFeatureDefn->SetGeomType(wkbNone);
46
15.5k
}
47
48
/************************************************************************/
49
/*                            ~OGRGMLLayer()                            */
50
/************************************************************************/
51
52
OGRGMLLayer::~OGRGMLLayer()
53
54
15.5k
{
55
15.5k
    CPLFree(m_pszFIDPrefix);
56
57
15.5k
    if (poFeatureDefn)
58
15.5k
        poFeatureDefn->Release();
59
15.5k
}
60
61
/************************************************************************/
62
/*                            ResetReading()                            */
63
/************************************************************************/
64
65
void OGRGMLLayer::ResetReading()
66
67
4.31k
{
68
4.31k
    if (bWriter)
69
0
        return;
70
71
4.31k
    if (poDS->GetReadMode() == INTERLEAVED_LAYERS ||
72
4.31k
        poDS->GetReadMode() == SEQUENTIAL_LAYERS)
73
1.22k
    {
74
        // Does the last stored feature belong to our layer ? If so, no
75
        // need to reset the reader.
76
1.22k
        if (m_iNextGMLId == 0 && poDS->PeekStoredGMLFeature() != nullptr &&
77
277
            poDS->PeekStoredGMLFeature()->GetClass() == poFClass)
78
277
            return;
79
80
944
        delete poDS->PeekStoredGMLFeature();
81
944
        poDS->SetStoredGMLFeature(nullptr);
82
944
    }
83
84
4.03k
    m_iNextGMLId = 0;
85
4.03k
    m_oSetFIDs.clear();
86
4.03k
    poDS->GetReader()->ResetReading();
87
4.03k
    CPLDebug("GML", "ResetReading()");
88
4.03k
    if (poDS->GetLayerCount() > 1 && poDS->GetReadMode() == STANDARD)
89
1.96k
    {
90
1.96k
        const char *pszElementName = poFClass->GetElementName();
91
1.96k
        const char *pszLastPipe = strrchr(pszElementName, '|');
92
1.96k
        if (pszLastPipe != nullptr)
93
0
            pszElementName = pszLastPipe + 1;
94
1.96k
        poDS->GetReader()->SetFilteredClassName(pszElementName);
95
1.96k
    }
96
4.03k
}
97
98
/************************************************************************/
99
/*                             Increment()                              */
100
/************************************************************************/
101
102
static GIntBig Increment(GIntBig nVal)
103
13.0k
{
104
13.0k
    if (nVal <= GINTBIG_MAX - 1)
105
13.0k
        return nVal + 1;
106
0
    return nVal;
107
13.0k
}
108
109
/************************************************************************/
110
/*                           GetNextFeature()                           */
111
/************************************************************************/
112
113
OGRFeature *OGRGMLLayer::GetNextFeature()
114
115
17.4k
{
116
17.4k
    if (bWriter)
117
0
    {
118
0
        CPLError(CE_Failure, CPLE_NotSupported,
119
0
                 "Cannot read features when writing a GML file");
120
0
        return nullptr;
121
0
    }
122
123
17.4k
    if (poDS->GetLastReadLayer() != this)
124
4.31k
    {
125
4.31k
        if (poDS->GetReadMode() != INTERLEAVED_LAYERS)
126
4.31k
            ResetReading();
127
4.31k
        poDS->SetLastReadLayer(this);
128
4.31k
    }
129
130
17.4k
    const bool bSkipCorruptedFeatures = CPLFetchBool(
131
17.4k
        poDS->GetOpenOptions(), "SKIP_CORRUPTED_FEATURES",
132
17.4k
        CPLTestBool(CPLGetConfigOption("GML_SKIP_CORRUPTED_FEATURES", "NO")));
133
134
    /* ==================================================================== */
135
    /*      Loop till we find and translate a feature meeting all our       */
136
    /*      requirements.                                                   */
137
    /* ==================================================================== */
138
18.1k
    while (true)
139
18.1k
    {
140
18.1k
        GMLFeature *poGMLFeature = poDS->PeekStoredGMLFeature();
141
18.1k
        if (poGMLFeature != nullptr)
142
277
        {
143
277
            poDS->SetStoredGMLFeature(nullptr);
144
277
        }
145
17.8k
        else
146
17.8k
        {
147
17.8k
            poGMLFeature = poDS->GetReader()->NextFeature();
148
17.8k
            if (poGMLFeature == nullptr)
149
4.01k
                return nullptr;
150
151
            // We count reading low level GML features as a feature read for
152
            // work checking purposes, though at least we didn't necessary
153
            // have to turn it into an OGRFeature.
154
13.8k
            m_nFeaturesRead++;
155
13.8k
        }
156
157
        /* --------------------------------------------------------------------
158
         */
159
        /*      Is it of the proper feature class? */
160
        /* --------------------------------------------------------------------
161
         */
162
163
14.1k
        if (poGMLFeature->GetClass() != poFClass)
164
1.03k
        {
165
1.03k
            if (poDS->GetReadMode() == INTERLEAVED_LAYERS ||
166
1.03k
                (poDS->GetReadMode() == SEQUENTIAL_LAYERS && m_iNextGMLId != 0))
167
278
            {
168
278
                CPLAssert(poDS->PeekStoredGMLFeature() == nullptr);
169
278
                poDS->SetStoredGMLFeature(poGMLFeature);
170
278
                return nullptr;
171
278
            }
172
757
            else
173
757
            {
174
757
                delete poGMLFeature;
175
757
                continue;
176
757
            }
177
1.03k
        }
178
179
        /* --------------------------------------------------------------------
180
         */
181
        /*      Extract the fid: */
182
        /*      -Assumes the fids are non-negative integers with an optional */
183
        /*       prefix */
184
        /*      -If a prefix differs from the prefix of the first feature from
185
         */
186
        /*       the poDS then the fids from the poDS are ignored and are */
187
        /*       assigned serially thereafter */
188
        /* --------------------------------------------------------------------
189
         */
190
13.1k
        GIntBig nFID = -1;
191
13.1k
        constexpr size_t MAX_FID_DIGIT_COUNT = 20;
192
13.1k
        const char *pszGML_FID = poGMLFeature->GetFID();
193
13.1k
        if (m_bInvalidFIDFound || pszGML_FID == nullptr || pszGML_FID[0] == 0)
194
12.7k
        {
195
            // do nothing
196
12.7k
        }
197
379
        else if (m_iNextGMLId == 0)
198
231
        {
199
231
            size_t j = 0;
200
231
            size_t i = strlen(pszGML_FID);
201
759
            while (i > 0 && j < MAX_FID_DIGIT_COUNT)
202
759
            {
203
759
                --i;
204
759
                if (!(pszGML_FID[i] >= '0' && pszGML_FID[i] <= '9'))
205
139
                    break;
206
620
                j++;
207
620
                if (i == 0)
208
92
                {
209
92
                    i = std::numeric_limits<size_t>::max();
210
92
                    break;
211
92
                }
212
620
            }
213
            // i points the last character of the fid prefix.
214
231
            if (i != std::numeric_limits<size_t>::max() &&
215
139
                j < MAX_FID_DIGIT_COUNT && m_pszFIDPrefix == nullptr)
216
139
            {
217
139
                m_pszFIDPrefix = static_cast<char *>(CPLMalloc(i + 2));
218
139
                memcpy(m_pszFIDPrefix, pszGML_FID, i + 1);
219
139
                m_pszFIDPrefix[i + 1] = '\0';
220
139
            }
221
            // m_pszFIDPrefix now contains the prefix or NULL if no prefix is
222
            // found.
223
231
            if (j < MAX_FID_DIGIT_COUNT)
224
231
            {
225
231
                char *endptr = nullptr;
226
231
                nFID = std::strtoll(
227
231
                    pszGML_FID +
228
231
                        (i != std::numeric_limits<size_t>::max() ? i + 1 : 0),
229
231
                    &endptr, 10);
230
231
                if (endptr == pszGML_FID + strlen(pszGML_FID))
231
231
                {
232
231
                    if (m_iNextGMLId <= nFID)
233
231
                        m_iNextGMLId = Increment(nFID);
234
231
                }
235
0
                else
236
0
                {
237
0
                    nFID = -1;
238
0
                }
239
231
            }
240
231
        }
241
148
        else  // if( iNextGMLId != 0 ).
242
148
        {
243
148
            const char *pszFIDPrefix_notnull = m_pszFIDPrefix;
244
148
            if (pszFIDPrefix_notnull == nullptr)
245
53
                pszFIDPrefix_notnull = "";
246
148
            const size_t nLenPrefix = strlen(pszFIDPrefix_notnull);
247
248
148
            if (strncmp(pszGML_FID, pszFIDPrefix_notnull, nLenPrefix) == 0 &&
249
133
                strlen(pszGML_FID + nLenPrefix) < MAX_FID_DIGIT_COUNT)
250
132
            {
251
132
                char *endptr = nullptr;
252
132
                nFID = std::strtoll(pszGML_FID + nLenPrefix, &endptr, 10);
253
132
                if (endptr == pszGML_FID + strlen(pszGML_FID))
254
128
                {
255
                    // fid with the prefix. Using its numerical part.
256
128
                    if (m_iNextGMLId <= nFID)
257
66
                        m_iNextGMLId = Increment(nFID);
258
128
                }
259
4
                else
260
4
                {
261
4
                    nFID = -1;
262
4
                }
263
132
            }
264
148
        }
265
266
13.1k
        constexpr size_t MAX_FID_SET_SIZE = 10 * 1000 * 1000;
267
13.1k
        if (nFID >= 0 && m_oSetFIDs.size() < MAX_FID_SET_SIZE)
268
359
        {
269
            // Make sure FIDs are unique
270
359
            if (!cpl::contains(m_oSetFIDs, nFID))
271
311
                m_oSetFIDs.insert(nFID);
272
48
            else
273
48
            {
274
48
                m_oSetFIDs.clear();
275
48
                nFID = -1;
276
48
            }
277
359
        }
278
279
13.1k
        if (nFID < 0)
280
12.8k
        {
281
            // fid without the aforementioned prefix or a valid numerical
282
            // part.
283
12.8k
            m_bInvalidFIDFound = true;
284
12.8k
            nFID = m_iNextGMLId;
285
12.8k
            m_iNextGMLId = Increment(m_iNextGMLId);
286
12.8k
        }
287
288
        /* --------------------------------------------------------------------
289
         */
290
        /*      Does it satisfy the spatial query, if there is one? */
291
        /* --------------------------------------------------------------------
292
         */
293
294
13.1k
        OGRGeometry **papoGeometries = nullptr;
295
13.1k
        const CPLXMLNode *const *papsGeometry = poGMLFeature->GetGeometryList();
296
297
13.1k
        const CPLXMLNode *apsGeometries[2] = {nullptr, nullptr};
298
13.1k
        const CPLXMLNode *psBoundedByGeometry =
299
13.1k
            poGMLFeature->GetBoundedByGeometry();
300
13.1k
        if (psBoundedByGeometry && !(papsGeometry && papsGeometry[0]))
301
0
        {
302
0
            apsGeometries[0] = psBoundedByGeometry;
303
0
            papsGeometry = apsGeometries;
304
0
        }
305
306
13.1k
        OGRGeometry *poGeom = nullptr;
307
308
13.1k
        if (poFeatureDefn->GetGeomFieldCount() > 1)
309
75
        {
310
75
            papoGeometries = static_cast<OGRGeometry **>(CPLCalloc(
311
75
                poFeatureDefn->GetGeomFieldCount(), sizeof(OGRGeometry *)));
312
75
            const char *pszSRSName = poDS->GetGlobalSRSName();
313
473
            for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
314
398
            {
315
398
                const CPLXMLNode *psGeom = poGMLFeature->GetGeometryRef(i);
316
398
                if (psGeom != nullptr)
317
3
                {
318
3
                    const CPLXMLNode *myGeometryList[2] = {psGeom, nullptr};
319
3
                    poGeom = GML_BuildOGRGeometryFromList(
320
3
                        myGeometryList, true,
321
3
                        poDS->GetInvertAxisOrderIfLatLong(), pszSRSName,
322
3
                        poDS->GetConsiderEPSGAsURN(),
323
3
                        poDS->GetSwapCoordinates(),
324
3
                        poDS->GetSecondaryGeometryOption(), m_srsCache.get(),
325
3
                        bFaceHoleNegative);
326
327
                    // Do geometry type changes if needed to match layer
328
                    // geometry type.
329
3
                    if (poGeom != nullptr)
330
3
                    {
331
3
                        auto poGeomUniquePtr =
332
3
                            std::unique_ptr<OGRGeometry>(poGeom);
333
3
                        poGeom = nullptr;
334
3
                        papoGeometries[i] =
335
3
                            OGRGeometryFactory::forceTo(
336
3
                                std::move(poGeomUniquePtr),
337
3
                                poFeatureDefn->GetGeomFieldDefn(i)->GetType())
338
3
                                .release();
339
3
                    }
340
0
                    else
341
0
                    {
342
                        // We assume the createFromGML() function would have
343
                        // already reported the error.
344
0
                        for (int j = 0; j < poFeatureDefn->GetGeomFieldCount();
345
0
                             j++)
346
0
                        {
347
0
                            delete papoGeometries[j];
348
0
                        }
349
0
                        CPLFree(papoGeometries);
350
0
                        delete poGMLFeature;
351
0
                        return nullptr;
352
0
                    }
353
3
                }
354
398
            }
355
356
75
            if (m_poFilterGeom != nullptr && m_iGeomFieldFilter >= 0 &&
357
0
                m_iGeomFieldFilter < poFeatureDefn->GetGeomFieldCount() &&
358
0
                papoGeometries[m_iGeomFieldFilter] &&
359
0
                !FilterGeometry(papoGeometries[m_iGeomFieldFilter]))
360
0
            {
361
0
                for (int j = 0; j < poFeatureDefn->GetGeomFieldCount(); j++)
362
0
                {
363
0
                    delete papoGeometries[j];
364
0
                }
365
0
                CPLFree(papoGeometries);
366
0
                delete poGMLFeature;
367
0
                continue;
368
0
            }
369
75
        }
370
13.0k
        else if (papsGeometry[0] &&
371
3.18k
                 strcmp(papsGeometry[0]->pszValue, "null") == 0)
372
0
        {
373
            // do nothing
374
0
        }
375
13.0k
        else if (papsGeometry[0] != nullptr)
376
3.18k
        {
377
3.18k
            const char *pszSRSName = poDS->GetGlobalSRSName();
378
3.18k
            CPLPushErrorHandler(CPLQuietErrorHandler);
379
3.18k
            poGeom = GML_BuildOGRGeometryFromList(
380
3.18k
                papsGeometry, true, poDS->GetInvertAxisOrderIfLatLong(),
381
3.18k
                pszSRSName, poDS->GetConsiderEPSGAsURN(),
382
3.18k
                poDS->GetSwapCoordinates(), poDS->GetSecondaryGeometryOption(),
383
3.18k
                m_srsCache.get(), bFaceHoleNegative);
384
3.18k
            CPLPopErrorHandler();
385
386
            // Do geometry type changes if needed to match layer geometry type.
387
3.18k
            if (poGeom != nullptr)
388
3.16k
            {
389
3.16k
                poGeom =
390
3.16k
                    OGRGeometryFactory::forceTo(
391
3.16k
                        std::unique_ptr<OGRGeometry>(poGeom), GetGeomType())
392
3.16k
                        .release();
393
3.16k
            }
394
16
            else
395
16
            {
396
16
                const CPLString osLastErrorMsg(CPLGetLastErrorMsg());
397
398
16
                CPLError(
399
16
                    bSkipCorruptedFeatures ? CE_Warning : CE_Failure,
400
16
                    CPLE_AppDefined,
401
16
                    "Geometry of feature " CPL_FRMT_GIB
402
16
                    " %scannot be parsed: %s%s",
403
16
                    nFID, pszGML_FID ? CPLSPrintf("%s ", pszGML_FID) : "",
404
16
                    osLastErrorMsg.c_str(),
405
16
                    bSkipCorruptedFeatures
406
16
                        ? ". Skipping to next feature."
407
16
                        : ". You may set the GML_SKIP_CORRUPTED_FEATURES "
408
16
                          "configuration option to YES to skip to the next "
409
16
                          "feature");
410
16
                delete poGMLFeature;
411
16
                if (bSkipCorruptedFeatures)
412
0
                    continue;
413
16
                return nullptr;
414
16
            }
415
416
3.16k
            if (m_poFilterGeom != nullptr && !FilterGeometry(poGeom))
417
0
            {
418
0
                delete poGMLFeature;
419
0
                delete poGeom;
420
0
                continue;
421
0
            }
422
3.16k
        }
423
424
        /* --------------------------------------------------------------------
425
         */
426
        /*      Convert the whole feature into an OGRFeature. */
427
        /* --------------------------------------------------------------------
428
         */
429
13.0k
        int iDstField = 0;
430
13.0k
        OGRFeature *poOGRFeature = new OGRFeature(poFeatureDefn);
431
432
13.0k
        poOGRFeature->SetFID(nFID);
433
13.0k
        if (poDS->ExposeId())
434
5.78k
        {
435
5.78k
            if (pszGML_FID)
436
731
                poOGRFeature->SetField(iDstField, pszGML_FID);
437
5.78k
            iDstField++;
438
5.78k
        }
439
440
13.0k
        const int nPropertyCount = poFClass->GetPropertyCount();
441
23.7k
        for (int iField = 0; iField < nPropertyCount; iField++, iDstField++)
442
10.6k
        {
443
10.6k
            const GMLProperty *psGMLProperty =
444
10.6k
                poGMLFeature->GetProperty(iField);
445
10.6k
            if (psGMLProperty == nullptr || psGMLProperty->nSubProperties == 0)
446
8.70k
                continue;
447
448
1.93k
            if (EQUAL(psGMLProperty->papszSubProperties[0], OGR_GML_NULL))
449
18
            {
450
18
                poOGRFeature->SetFieldNull(iDstField);
451
18
                continue;
452
18
            }
453
454
1.91k
            switch (poFClass->GetProperty(iField)->GetType())
455
1.91k
            {
456
140
                case GMLPT_Real:
457
140
                {
458
140
                    poOGRFeature->SetField(
459
140
                        iDstField,
460
140
                        CPLAtof(psGMLProperty->papszSubProperties[0]));
461
140
                }
462
140
                break;
463
464
81
                case GMLPT_IntegerList:
465
81
                {
466
81
                    const int nCount = psGMLProperty->nSubProperties;
467
81
                    int *panIntList =
468
81
                        static_cast<int *>(CPLMalloc(sizeof(int) * nCount));
469
470
283
                    for (int i = 0; i < nCount; i++)
471
202
                        panIntList[i] =
472
202
                            atoi(psGMLProperty->papszSubProperties[i]);
473
474
81
                    poOGRFeature->SetField(iDstField, nCount, panIntList);
475
81
                    CPLFree(panIntList);
476
81
                }
477
81
                break;
478
479
11
                case GMLPT_Integer64List:
480
11
                {
481
11
                    const int nCount = psGMLProperty->nSubProperties;
482
11
                    GIntBig *panIntList = static_cast<GIntBig *>(
483
11
                        CPLMalloc(sizeof(GIntBig) * nCount));
484
485
46
                    for (int i = 0; i < nCount; i++)
486
35
                        panIntList[i] =
487
35
                            CPLAtoGIntBig(psGMLProperty->papszSubProperties[i]);
488
489
11
                    poOGRFeature->SetField(iDstField, nCount, panIntList);
490
11
                    CPLFree(panIntList);
491
11
                }
492
11
                break;
493
494
37
                case GMLPT_RealList:
495
37
                {
496
37
                    const int nCount = psGMLProperty->nSubProperties;
497
37
                    double *padfList = static_cast<double *>(
498
37
                        CPLMalloc(sizeof(double) * nCount));
499
500
162
                    for (int i = 0; i < nCount; i++)
501
125
                        padfList[i] =
502
125
                            CPLAtof(psGMLProperty->papszSubProperties[i]);
503
504
37
                    poOGRFeature->SetField(iDstField, nCount, padfList);
505
37
                    CPLFree(padfList);
506
37
                }
507
37
                break;
508
509
393
                case GMLPT_StringList:
510
393
                case GMLPT_FeaturePropertyList:
511
393
                {
512
393
                    poOGRFeature->SetField(iDstField,
513
393
                                           psGMLProperty->papszSubProperties);
514
393
                }
515
393
                break;
516
517
11
                case GMLPT_Boolean:
518
11
                {
519
11
                    if (strcmp(psGMLProperty->papszSubProperties[0], "true") ==
520
11
                            0 ||
521
6
                        strcmp(psGMLProperty->papszSubProperties[0], "1") == 0)
522
5
                    {
523
5
                        poOGRFeature->SetField(iDstField, 1);
524
5
                    }
525
6
                    else if (strcmp(psGMLProperty->papszSubProperties[0],
526
6
                                    "false") == 0 ||
527
0
                             strcmp(psGMLProperty->papszSubProperties[0],
528
0
                                    "0") == 0)
529
6
                    {
530
6
                        poOGRFeature->SetField(iDstField, 0);
531
6
                    }
532
0
                    else
533
0
                    {
534
0
                        poOGRFeature->SetField(
535
0
                            iDstField, psGMLProperty->papszSubProperties[0]);
536
0
                    }
537
11
                    break;
538
393
                }
539
540
0
                case GMLPT_BooleanList:
541
0
                {
542
0
                    const int nCount = psGMLProperty->nSubProperties;
543
0
                    int *panIntList =
544
0
                        static_cast<int *>(CPLMalloc(sizeof(int) * nCount));
545
546
0
                    for (int i = 0; i < nCount; i++)
547
0
                    {
548
0
                        panIntList[i] =
549
0
                            (strcmp(psGMLProperty->papszSubProperties[i],
550
0
                                    "true") == 0 ||
551
0
                             strcmp(psGMLProperty->papszSubProperties[i],
552
0
                                    "1") == 0);
553
0
                    }
554
555
0
                    poOGRFeature->SetField(iDstField, nCount, panIntList);
556
0
                    CPLFree(panIntList);
557
0
                    break;
558
393
                }
559
560
1.24k
                default:
561
1.24k
                    poOGRFeature->SetField(
562
1.24k
                        iDstField, psGMLProperty->papszSubProperties[0]);
563
1.24k
                    break;
564
1.91k
            }
565
1.91k
        }
566
567
13.0k
        delete poGMLFeature;
568
13.0k
        poGMLFeature = nullptr;
569
570
        // Assign the geometry before the attribute filter because
571
        // the attribute filter may use a special field like OGR_GEOMETRY.
572
13.0k
        if (papoGeometries != nullptr)
573
75
        {
574
473
            for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
575
398
            {
576
398
                poOGRFeature->SetGeomFieldDirectly(i, papoGeometries[i]);
577
398
            }
578
75
            CPLFree(papoGeometries);
579
75
            papoGeometries = nullptr;
580
75
        }
581
13.0k
        else
582
13.0k
        {
583
13.0k
            poOGRFeature->SetGeometryDirectly(poGeom);
584
13.0k
        }
585
586
        // Assign SRS.
587
18.4k
        for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
588
5.37k
        {
589
5.37k
            poGeom = poOGRFeature->GetGeomFieldRef(i);
590
5.37k
            if (poGeom != nullptr)
591
3.16k
            {
592
3.16k
                const OGRSpatialReference *poSRS =
593
3.16k
                    poFeatureDefn->GetGeomFieldDefn(i)->GetSpatialRef();
594
3.16k
                if (poSRS != nullptr)
595
194
                    poGeom->assignSpatialReference(poSRS);
596
3.16k
            }
597
5.37k
        }
598
599
        /* --------------------------------------------------------------------
600
         */
601
        /*      Test against the attribute query. */
602
        /* --------------------------------------------------------------------
603
         */
604
13.0k
        if (m_poAttrQuery != nullptr && !m_poAttrQuery->Evaluate(poOGRFeature))
605
0
        {
606
0
            delete poOGRFeature;
607
0
            continue;
608
0
        }
609
610
        // Got the desired feature.
611
13.0k
        return poOGRFeature;
612
13.0k
    }
613
17.4k
}
614
615
/************************************************************************/
616
/*                          GetFeatureCount()                           */
617
/************************************************************************/
618
619
GIntBig OGRGMLLayer::GetFeatureCount(int bForce)
620
621
0
{
622
0
    if (poFClass == nullptr)
623
0
        return 0;
624
625
0
    if (m_poFilterGeom != nullptr || m_poAttrQuery != nullptr)
626
0
        return OGRLayer::GetFeatureCount(bForce);
627
628
    // If the schema is read from a .xsd file, we haven't read
629
    // the feature count, so compute it now.
630
0
    GIntBig nFeatureCount = poFClass->GetFeatureCount();
631
0
    if (nFeatureCount < 0)
632
0
    {
633
0
        nFeatureCount = OGRLayer::GetFeatureCount(bForce);
634
0
        poFClass->SetFeatureCount(nFeatureCount);
635
0
    }
636
637
0
    return nFeatureCount;
638
0
}
639
640
/************************************************************************/
641
/*                             IGetExtent()                             */
642
/************************************************************************/
643
644
OGRErr OGRGMLLayer::IGetExtent(int iGeomField, OGREnvelope *psExtent,
645
                               bool bForce)
646
647
0
{
648
0
    if (GetGeomType() == wkbNone)
649
0
        return OGRERR_FAILURE;
650
651
0
    double dfXMin = 0.0;
652
0
    double dfXMax = 0.0;
653
0
    double dfYMin = 0.0;
654
0
    double dfYMax = 0.0;
655
0
    if (poFClass != nullptr &&
656
0
        poFClass->GetExtents(&dfXMin, &dfXMax, &dfYMin, &dfYMax))
657
0
    {
658
0
        psExtent->MinX = dfXMin;
659
0
        psExtent->MaxX = dfXMax;
660
0
        psExtent->MinY = dfYMin;
661
0
        psExtent->MaxY = dfYMax;
662
663
0
        return OGRERR_NONE;
664
0
    }
665
666
0
    return OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
667
0
}
668
669
/************************************************************************/
670
/*                             GetExtent()                              */
671
/************************************************************************/
672
673
static void GMLWriteField(OGRGMLDataSource *poDS, VSILFILE *fp,
674
                          bool bWriteSpaceIndentation, const char *pszPrefix,
675
                          bool bRemoveAppPrefix, OGRFieldDefn *poFieldDefn,
676
                          const char *pszVal)
677
678
335k
{
679
335k
    const char *pszFieldName = poFieldDefn->GetNameRef();
680
681
338k
    while (*pszVal == ' ')
682
3.52k
        pszVal++;
683
684
335k
    if (bWriteSpaceIndentation)
685
335k
        VSIFPrintfL(fp, "      ");
686
687
335k
    if (bRemoveAppPrefix)
688
0
        poDS->PrintLine(fp, "<%s>%s</%s>", pszFieldName, pszVal, pszFieldName);
689
335k
    else
690
335k
        poDS->PrintLine(fp, "<%s:%s>%s</%s:%s>", pszPrefix, pszFieldName,
691
335k
                        pszVal, pszPrefix, pszFieldName);
692
335k
}
693
694
/************************************************************************/
695
/*                           ICreateFeature()                           */
696
/************************************************************************/
697
698
OGRErr OGRGMLLayer::ICreateFeature(OGRFeature *poFeature)
699
700
433k
{
701
433k
    const bool bIsGML3Output = poDS->IsGML3Output();
702
433k
    VSILFILE *fp = poDS->GetOutputFP();
703
433k
    const bool bWriteSpaceIndentation = poDS->WriteSpaceIndentation();
704
433k
    const char *pszPrefix = poDS->GetAppPrefix();
705
433k
    const bool bRemoveAppPrefix = poDS->RemoveAppPrefix();
706
433k
    const bool bGMLFeatureCollection = poDS->GMLFeatureCollection();
707
708
433k
    if (!bWriter || poDS->HasWriteError())
709
0
        return OGRERR_FAILURE;
710
711
433k
    poFeature->FillUnsetWithDefault(TRUE, nullptr);
712
433k
    if (!poFeature->Validate(OGR_F_VAL_ALL & ~OGR_F_VAL_GEOM_TYPE &
713
433k
                                 ~OGR_F_VAL_ALLOW_NULL_WHEN_DEFAULT,
714
433k
                             TRUE))
715
1.87k
        return OGRERR_FAILURE;
716
717
431k
    if (bWriteSpaceIndentation)
718
431k
        VSIFPrintfL(fp, "  ");
719
431k
    if (bIsGML3Output && !bGMLFeatureCollection)
720
431k
    {
721
431k
        if (bRemoveAppPrefix)
722
0
            poDS->PrintLine(fp, "<featureMember>");
723
431k
        else
724
431k
            poDS->PrintLine(fp, "<%s:featureMember>", pszPrefix);
725
431k
    }
726
0
    else
727
0
    {
728
0
        poDS->PrintLine(fp, "<gml:featureMember>");
729
0
    }
730
731
431k
    if (poFeature->GetFID() == OGRNullFID)
732
431k
        poFeature->SetFID(m_iNextGMLId++);
733
734
431k
    if (bWriteSpaceIndentation)
735
431k
        VSIFPrintfL(fp, "    ");
736
431k
    VSIFPrintfL(fp, "<");
737
431k
    if (!bRemoveAppPrefix)
738
431k
        VSIFPrintfL(fp, "%s:", pszPrefix);
739
740
431k
    int nGMLIdIndex = -1;
741
431k
    if (bIsGML3Output)
742
431k
    {
743
431k
        nGMLIdIndex = poFeatureDefn->GetFieldIndex("gml_id");
744
431k
        if (nGMLIdIndex >= 0 && poFeature->IsFieldSetAndNotNull(nGMLIdIndex))
745
0
            poDS->PrintLine(fp, "%s gml:id=\"%s\">", poFeatureDefn->GetName(),
746
0
                            poFeature->GetFieldAsString(nGMLIdIndex));
747
431k
        else
748
431k
            poDS->PrintLine(fp, "%s gml:id=\"%s." CPL_FRMT_GIB "\">",
749
431k
                            poFeatureDefn->GetName(), poFeatureDefn->GetName(),
750
431k
                            poFeature->GetFID());
751
431k
    }
752
0
    else
753
0
    {
754
0
        nGMLIdIndex = poFeatureDefn->GetFieldIndex("fid");
755
0
        if (bUseOldFIDFormat)
756
0
        {
757
0
            poDS->PrintLine(fp, "%s fid=\"F" CPL_FRMT_GIB "\">",
758
0
                            poFeatureDefn->GetName(), poFeature->GetFID());
759
0
        }
760
0
        else if (nGMLIdIndex >= 0 &&
761
0
                 poFeature->IsFieldSetAndNotNull(nGMLIdIndex))
762
0
        {
763
0
            poDS->PrintLine(fp, "%s fid=\"%s\">", poFeatureDefn->GetName(),
764
0
                            poFeature->GetFieldAsString(nGMLIdIndex));
765
0
        }
766
0
        else
767
0
        {
768
0
            poDS->PrintLine(fp, "%s fid=\"%s." CPL_FRMT_GIB "\">",
769
0
                            poFeatureDefn->GetName(), poFeatureDefn->GetName(),
770
0
                            poFeature->GetFID());
771
0
        }
772
0
    }
773
774
900k
    for (int iGeomField = 0; iGeomField < poFeatureDefn->GetGeomFieldCount();
775
469k
         iGeomField++)
776
469k
    {
777
469k
        const OGRGeomFieldDefn *poFieldDefn =
778
469k
            poFeatureDefn->GetGeomFieldDefn(iGeomField);
779
780
        // Write out Geometry - for now it isn't indented properly.
781
        // GML geometries don't like very much the concept of empty geometry.
782
469k
        OGRGeometry *poGeom = poFeature->GetGeomFieldRef(iGeomField);
783
469k
        if (poGeom != nullptr && !poGeom->IsEmpty())
784
4.24k
        {
785
4.24k
            OGREnvelope3D sGeomBounds;
786
787
4.24k
            const int nCoordDimension = poGeom->getCoordinateDimension();
788
789
4.24k
            poGeom->getEnvelope(&sGeomBounds);
790
4.24k
            if (poDS->HasWriteGlobalSRS())
791
1.22k
                poDS->GrowExtents(&sGeomBounds, nCoordDimension);
792
793
4.24k
            if (poGeom->getSpatialReference() == nullptr &&
794
4.22k
                poFieldDefn->GetSpatialRef() != nullptr)
795
181
                poGeom->assignSpatialReference(poFieldDefn->GetSpatialRef());
796
797
4.24k
            const auto &oCoordPrec = poFieldDefn->GetCoordinatePrecision();
798
799
4.24k
            if (bIsGML3Output && poDS->WriteFeatureBoundedBy())
800
4.24k
            {
801
4.24k
                bool bCoordSwap = false;
802
803
4.24k
                char *pszSRSName =
804
4.24k
                    GML_GetSRSName(poGeom->getSpatialReference(),
805
4.24k
                                   poDS->GetSRSNameFormat(), &bCoordSwap);
806
4.24k
                char szLowerCorner[75] = {};
807
4.24k
                char szUpperCorner[75] = {};
808
809
4.24k
                OGRWktOptions coordOpts;
810
811
4.24k
                if (oCoordPrec.dfXYResolution !=
812
4.24k
                    OGRGeomCoordinatePrecision::UNKNOWN)
813
0
                {
814
0
                    coordOpts.format = OGRWktFormat::F;
815
0
                    coordOpts.xyPrecision =
816
0
                        OGRGeomCoordinatePrecision::ResolutionToPrecision(
817
0
                            oCoordPrec.dfXYResolution);
818
0
                }
819
4.24k
                if (oCoordPrec.dfZResolution !=
820
4.24k
                    OGRGeomCoordinatePrecision::UNKNOWN)
821
0
                {
822
0
                    coordOpts.format = OGRWktFormat::F;
823
0
                    coordOpts.zPrecision =
824
0
                        OGRGeomCoordinatePrecision::ResolutionToPrecision(
825
0
                            oCoordPrec.dfZResolution);
826
0
                }
827
828
4.24k
                std::string wkt;
829
4.24k
                if (bCoordSwap)
830
167
                {
831
167
                    wkt = OGRMakeWktCoordinate(
832
167
                        sGeomBounds.MinY, sGeomBounds.MinX, sGeomBounds.MinZ,
833
167
                        nCoordDimension, coordOpts);
834
167
                    memcpy(szLowerCorner, wkt.data(), wkt.size() + 1);
835
836
167
                    wkt = OGRMakeWktCoordinate(
837
167
                        sGeomBounds.MaxY, sGeomBounds.MaxX, sGeomBounds.MaxZ,
838
167
                        nCoordDimension, coordOpts);
839
167
                    memcpy(szUpperCorner, wkt.data(), wkt.size() + 1);
840
167
                }
841
4.07k
                else
842
4.07k
                {
843
4.07k
                    wkt = OGRMakeWktCoordinate(
844
4.07k
                        sGeomBounds.MinX, sGeomBounds.MinY, sGeomBounds.MinZ,
845
4.07k
                        nCoordDimension, coordOpts);
846
4.07k
                    memcpy(szLowerCorner, wkt.data(), wkt.size() + 1);
847
848
4.07k
                    wkt = OGRMakeWktCoordinate(
849
4.07k
                        sGeomBounds.MaxX, sGeomBounds.MaxY, sGeomBounds.MaxZ,
850
4.07k
                        nCoordDimension, coordOpts);
851
4.07k
                    memcpy(szUpperCorner, wkt.data(), wkt.size() + 1);
852
4.07k
                }
853
4.24k
                if (bWriteSpaceIndentation)
854
4.24k
                    VSIFPrintfL(fp, "      ");
855
4.24k
                poDS->PrintLine(
856
4.24k
                    fp,
857
4.24k
                    "<gml:boundedBy><gml:Envelope%s%s><gml:lowerCorner>%s"
858
4.24k
                    "</gml:lowerCorner><gml:upperCorner>%s</gml:upperCorner>"
859
4.24k
                    "</gml:Envelope></gml:boundedBy>",
860
4.24k
                    (nCoordDimension == 3) ? " srsDimension=\"3\"" : "",
861
4.24k
                    pszSRSName, szLowerCorner, szUpperCorner);
862
4.24k
                CPLFree(pszSRSName);
863
4.24k
            }
864
865
4.24k
            char **papszOptions = nullptr;
866
4.24k
            if (bIsGML3Output)
867
4.24k
            {
868
4.24k
                papszOptions = CSLAddString(papszOptions, "FORMAT=GML3");
869
4.24k
                if (poDS->GetSRSNameFormat() == SRSNAME_SHORT)
870
0
                    papszOptions =
871
0
                        CSLAddString(papszOptions, "SRSNAME_FORMAT=SHORT");
872
4.24k
                else if (poDS->GetSRSNameFormat() == SRSNAME_OGC_URN)
873
4.24k
                    papszOptions =
874
4.24k
                        CSLAddString(papszOptions, "SRSNAME_FORMAT=OGC_URN");
875
0
                else if (poDS->GetSRSNameFormat() == SRSNAME_OGC_URL)
876
0
                    papszOptions =
877
0
                        CSLAddString(papszOptions, "SRSNAME_FORMAT=OGC_URL");
878
4.24k
            }
879
4.24k
            const char *pszSRSDimensionLoc = poDS->GetSRSDimensionLoc();
880
4.24k
            if (pszSRSDimensionLoc != nullptr)
881
0
                papszOptions = CSLSetNameValue(papszOptions, "SRSDIMENSION_LOC",
882
0
                                               pszSRSDimensionLoc);
883
4.24k
            if (poDS->IsGML32Output())
884
4.24k
            {
885
4.24k
                if (poFeatureDefn->GetGeomFieldCount() > 1)
886
286
                    papszOptions = CSLAddString(
887
286
                        papszOptions, CPLSPrintf("GMLID=%s.%s." CPL_FRMT_GIB,
888
286
                                                 poFeatureDefn->GetName(),
889
286
                                                 poFieldDefn->GetNameRef(),
890
286
                                                 poFeature->GetFID()));
891
3.95k
                else
892
3.95k
                    papszOptions = CSLAddString(
893
3.95k
                        papszOptions, CPLSPrintf("GMLID=%s.geom." CPL_FRMT_GIB,
894
3.95k
                                                 poFeatureDefn->GetName(),
895
3.95k
                                                 poFeature->GetFID()));
896
4.24k
            }
897
898
4.24k
            if (oCoordPrec.dfXYResolution !=
899
4.24k
                OGRGeomCoordinatePrecision::UNKNOWN)
900
0
            {
901
0
                papszOptions = CSLAddString(
902
0
                    papszOptions, CPLSPrintf("XY_COORD_RESOLUTION=%g",
903
0
                                             oCoordPrec.dfXYResolution));
904
0
            }
905
4.24k
            if (oCoordPrec.dfZResolution != OGRGeomCoordinatePrecision::UNKNOWN)
906
0
            {
907
0
                papszOptions = CSLAddString(
908
0
                    papszOptions, CPLSPrintf("Z_COORD_RESOLUTION=%g",
909
0
                                             oCoordPrec.dfZResolution));
910
0
            }
911
912
4.24k
            char *pszGeometry = nullptr;
913
4.24k
            if (!bIsGML3Output && OGR_GT_IsNonLinear(poGeom->getGeometryType()))
914
0
            {
915
0
                auto poGeomTmp = OGRGeometryFactory::forceTo(
916
0
                    std::unique_ptr<OGRGeometry>(poGeom->clone()),
917
0
                    OGR_GT_GetLinear(poGeom->getGeometryType()));
918
0
                pszGeometry = poGeomTmp->exportToGML(papszOptions);
919
0
            }
920
4.24k
            else
921
4.24k
            {
922
4.24k
                if (wkbFlatten(poGeom->getGeometryType()) == wkbTriangle)
923
0
                {
924
0
                    pszGeometry = poGeom->exportToGML(papszOptions);
925
926
0
                    const char *pszGMLID =
927
0
                        poDS->IsGML32Output()
928
0
                            ? CPLSPrintf(
929
0
                                  " gml:id=\"%s\"",
930
0
                                  CSLFetchNameValue(papszOptions, "GMLID"))
931
0
                            : "";
932
0
                    char *pszNewGeom = CPLStrdup(
933
0
                        CPLSPrintf("<gml:TriangulatedSurface%s><gml:patches>%s<"
934
0
                                   "/gml:patches></gml:TriangulatedSurface>",
935
0
                                   pszGMLID, pszGeometry));
936
0
                    CPLFree(pszGeometry);
937
0
                    pszGeometry = pszNewGeom;
938
0
                }
939
4.24k
                else
940
4.24k
                {
941
4.24k
                    pszGeometry = poGeom->exportToGML(papszOptions);
942
4.24k
                }
943
4.24k
            }
944
4.24k
            CSLDestroy(papszOptions);
945
4.24k
            if (pszGeometry)
946
4.24k
            {
947
4.24k
                if (bWriteSpaceIndentation)
948
4.24k
                    VSIFPrintfL(fp, "      ");
949
4.24k
                if (bRemoveAppPrefix)
950
0
                    poDS->PrintLine(fp, "<%s>%s</%s>",
951
0
                                    poFieldDefn->GetNameRef(), pszGeometry,
952
0
                                    poFieldDefn->GetNameRef());
953
4.24k
                else
954
4.24k
                    poDS->PrintLine(fp, "<%s:%s>%s</%s:%s>", pszPrefix,
955
4.24k
                                    poFieldDefn->GetNameRef(), pszGeometry,
956
4.24k
                                    pszPrefix, poFieldDefn->GetNameRef());
957
4.24k
            }
958
0
            else
959
0
            {
960
0
                CPLError(CE_Failure, CPLE_AppDefined,
961
0
                         "Export of geometry to GML failed");
962
0
            }
963
4.24k
            CPLFree(pszGeometry);
964
4.24k
        }
965
469k
    }
966
967
    // Write all "set" fields.
968
4.06M
    for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++)
969
3.62M
    {
970
3.62M
        if (iField == nGMLIdIndex)
971
0
            continue;
972
3.62M
        OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
973
974
3.62M
        if (poFeature->IsFieldNull(iField))
975
0
        {
976
0
            const char *pszFieldName = poFieldDefn->GetNameRef();
977
978
0
            if (bWriteSpaceIndentation)
979
0
                VSIFPrintfL(fp, "      ");
980
981
0
            if (bRemoveAppPrefix)
982
0
                poDS->PrintLine(fp, "<%s xsi:nil=\"true\"/>", pszFieldName);
983
0
            else
984
0
                poDS->PrintLine(fp, "<%s:%s xsi:nil=\"true\"/>", pszPrefix,
985
0
                                pszFieldName);
986
0
        }
987
3.62M
        else if (poFeature->IsFieldSet(iField))
988
335k
        {
989
335k
            OGRFieldType eType = poFieldDefn->GetType();
990
335k
            if (eType == OFTStringList)
991
0
            {
992
0
                char **papszIter = poFeature->GetFieldAsStringList(iField);
993
0
                while (papszIter != nullptr && *papszIter != nullptr)
994
0
                {
995
0
                    char *pszEscaped = OGRGetXML_UTF8_EscapedString(*papszIter);
996
0
                    GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix,
997
0
                                  bRemoveAppPrefix, poFieldDefn, pszEscaped);
998
0
                    CPLFree(pszEscaped);
999
1000
0
                    papszIter++;
1001
0
                }
1002
0
            }
1003
335k
            else if (eType == OFTIntegerList)
1004
0
            {
1005
0
                int nCount = 0;
1006
0
                const int *panVals =
1007
0
                    poFeature->GetFieldAsIntegerList(iField, &nCount);
1008
0
                if (poFieldDefn->GetSubType() == OFSTBoolean)
1009
0
                {
1010
0
                    for (int i = 0; i < nCount; i++)
1011
0
                    {
1012
                        // 0 and 1 are OK, but the canonical representation is
1013
                        // false and true.
1014
0
                        GMLWriteField(poDS, fp, bWriteSpaceIndentation,
1015
0
                                      pszPrefix, bRemoveAppPrefix, poFieldDefn,
1016
0
                                      panVals[i] ? "true" : "false");
1017
0
                    }
1018
0
                }
1019
0
                else
1020
0
                {
1021
0
                    for (int i = 0; i < nCount; i++)
1022
0
                    {
1023
0
                        GMLWriteField(poDS, fp, bWriteSpaceIndentation,
1024
0
                                      pszPrefix, bRemoveAppPrefix, poFieldDefn,
1025
0
                                      CPLSPrintf("%d", panVals[i]));
1026
0
                    }
1027
0
                }
1028
0
            }
1029
335k
            else if (eType == OFTInteger64List)
1030
0
            {
1031
0
                int nCount = 0;
1032
0
                const GIntBig *panVals =
1033
0
                    poFeature->GetFieldAsInteger64List(iField, &nCount);
1034
0
                if (poFieldDefn->GetSubType() == OFSTBoolean)
1035
0
                {
1036
0
                    for (int i = 0; i < nCount; i++)
1037
0
                    {
1038
                        // 0 and 1 are OK, but the canonical representation is
1039
                        // false and true.
1040
0
                        GMLWriteField(poDS, fp, bWriteSpaceIndentation,
1041
0
                                      pszPrefix, bRemoveAppPrefix, poFieldDefn,
1042
0
                                      panVals[i] ? "true" : "false");
1043
0
                    }
1044
0
                }
1045
0
                else
1046
0
                {
1047
0
                    for (int i = 0; i < nCount; i++)
1048
0
                    {
1049
0
                        GMLWriteField(poDS, fp, bWriteSpaceIndentation,
1050
0
                                      pszPrefix, bRemoveAppPrefix, poFieldDefn,
1051
0
                                      CPLSPrintf(CPL_FRMT_GIB, panVals[i]));
1052
0
                    }
1053
0
                }
1054
0
            }
1055
335k
            else if (eType == OFTRealList)
1056
0
            {
1057
0
                int nCount = 0;
1058
0
                const double *padfVals =
1059
0
                    poFeature->GetFieldAsDoubleList(iField, &nCount);
1060
0
                for (int i = 0; i < nCount; i++)
1061
0
                {
1062
0
                    char szBuffer[80] = {};
1063
0
                    CPLsnprintf(szBuffer, sizeof(szBuffer), "%.15g",
1064
0
                                padfVals[i]);
1065
0
                    GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix,
1066
0
                                  bRemoveAppPrefix, poFieldDefn, szBuffer);
1067
0
                }
1068
0
            }
1069
335k
            else if ((eType == OFTInteger || eType == OFTInteger64) &&
1070
222
                     poFieldDefn->GetSubType() == OFSTBoolean)
1071
0
            {
1072
                // 0 and 1 are OK, but the canonical representation is false and
1073
                // true.
1074
0
                GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix,
1075
0
                              bRemoveAppPrefix, poFieldDefn,
1076
0
                              (poFeature->GetFieldAsInteger(iField)) ? "true"
1077
0
                                                                     : "false");
1078
0
            }
1079
335k
            else if (eType == OFTDate)
1080
0
            {
1081
0
                const OGRField *poField = poFeature->GetRawFieldRef(iField);
1082
0
                const char *pszXML =
1083
0
                    CPLSPrintf("%04d-%02d-%02d", poField->Date.Year,
1084
0
                               poField->Date.Month, poField->Date.Day);
1085
0
                GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix,
1086
0
                              bRemoveAppPrefix, poFieldDefn, pszXML);
1087
0
            }
1088
335k
            else if (eType == OFTDateTime)
1089
0
            {
1090
0
                char *pszXML =
1091
0
                    OGRGetXMLDateTime(poFeature->GetRawFieldRef(iField));
1092
0
                GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix,
1093
0
                              bRemoveAppPrefix, poFieldDefn, pszXML);
1094
0
                CPLFree(pszXML);
1095
0
            }
1096
335k
            else
1097
335k
            {
1098
335k
                const char *pszRaw = poFeature->GetFieldAsString(iField);
1099
1100
335k
                char *pszEscaped = OGRGetXML_UTF8_EscapedString(pszRaw);
1101
1102
335k
                GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix,
1103
335k
                              bRemoveAppPrefix, poFieldDefn, pszEscaped);
1104
335k
                CPLFree(pszEscaped);
1105
335k
            }
1106
335k
        }
1107
3.62M
    }
1108
1109
431k
    if (bWriteSpaceIndentation)
1110
431k
        VSIFPrintfL(fp, "    ");
1111
431k
    if (bRemoveAppPrefix)
1112
0
        poDS->PrintLine(fp, "</%s>", poFeatureDefn->GetName());
1113
431k
    else
1114
431k
        poDS->PrintLine(fp, "</%s:%s>", pszPrefix, poFeatureDefn->GetName());
1115
431k
    if (bWriteSpaceIndentation)
1116
431k
        VSIFPrintfL(fp, "  ");
1117
431k
    if (bIsGML3Output && !bGMLFeatureCollection)
1118
431k
    {
1119
431k
        if (bRemoveAppPrefix)
1120
0
            poDS->PrintLine(fp, "</featureMember>");
1121
431k
        else
1122
431k
            poDS->PrintLine(fp, "</%s:featureMember>", pszPrefix);
1123
431k
    }
1124
0
    else
1125
0
    {
1126
0
        poDS->PrintLine(fp, "</gml:featureMember>");
1127
0
    }
1128
1129
431k
    return !poDS->HasWriteError() ? OGRERR_NONE : OGRERR_FAILURE;
1130
433k
}
1131
1132
/************************************************************************/
1133
/*                           TestCapability()                           */
1134
/************************************************************************/
1135
1136
int OGRGMLLayer::TestCapability(const char *pszCap) const
1137
1138
5.21k
{
1139
5.21k
    if (EQUAL(pszCap, OLCSequentialWrite))
1140
0
        return bWriter;
1141
1142
5.21k
    else if (EQUAL(pszCap, OLCCreateField))
1143
0
        return bWriter && m_iNextGMLId == 0;
1144
1145
5.21k
    else if (EQUAL(pszCap, OLCCreateGeomField))
1146
0
        return bWriter && m_iNextGMLId == 0;
1147
1148
5.21k
    else if (EQUAL(pszCap, OLCFastGetExtent))
1149
0
    {
1150
0
        if (poFClass == nullptr)
1151
0
            return FALSE;
1152
1153
0
        double dfXMin = 0.0;
1154
0
        double dfXMax = 0.0;
1155
0
        double dfYMin = 0.0;
1156
0
        double dfYMax = 0.0;
1157
1158
0
        return poFClass->GetExtents(&dfXMin, &dfXMax, &dfYMin, &dfYMax);
1159
0
    }
1160
1161
5.21k
    else if (EQUAL(pszCap, OLCFastFeatureCount))
1162
0
    {
1163
0
        if (poFClass == nullptr || m_poFilterGeom != nullptr ||
1164
0
            m_poAttrQuery != nullptr)
1165
0
            return FALSE;
1166
1167
0
        return poFClass->GetFeatureCount() != -1;
1168
0
    }
1169
1170
5.21k
    else if (EQUAL(pszCap, OLCStringsAsUTF8))
1171
0
        return TRUE;
1172
1173
5.21k
    else if (EQUAL(pszCap, OLCCurveGeometries))
1174
3.76k
        return poDS->IsGML3Output();
1175
1176
1.44k
    else if (EQUAL(pszCap, OLCZGeometries))
1177
0
        return TRUE;
1178
1179
1.44k
    else
1180
1.44k
        return FALSE;
1181
5.21k
}
1182
1183
/************************************************************************/
1184
/*                            CreateField()                             */
1185
/************************************************************************/
1186
1187
OGRErr OGRGMLLayer::CreateField(const OGRFieldDefn *poField, int bApproxOK)
1188
1189
38.3k
{
1190
38.3k
    if (!bWriter || m_iNextGMLId != 0)
1191
4.70k
        return OGRERR_FAILURE;
1192
1193
    /* -------------------------------------------------------------------- */
1194
    /*      Enforce XML naming semantics on element name.                   */
1195
    /* -------------------------------------------------------------------- */
1196
33.6k
    OGRFieldDefn oCleanCopy(poField);
1197
33.6k
    char *pszName = CPLStrdup(poField->GetNameRef());
1198
33.6k
    CPLCleanXMLElementName(pszName);
1199
1200
33.6k
    if (strcmp(pszName, poField->GetNameRef()) != 0)
1201
8.48k
    {
1202
8.48k
        if (!bApproxOK)
1203
0
        {
1204
0
            CPLFree(pszName);
1205
0
            CPLError(CE_Failure, CPLE_AppDefined,
1206
0
                     "Unable to create field with name '%s', it would not\n"
1207
0
                     "be valid as an XML element name.",
1208
0
                     poField->GetNameRef());
1209
0
            return OGRERR_FAILURE;
1210
0
        }
1211
1212
8.48k
        oCleanCopy.SetName(pszName);
1213
8.48k
        CPLError(CE_Warning, CPLE_AppDefined,
1214
8.48k
                 "Field name '%s' adjusted to '%s' to be a valid\n"
1215
8.48k
                 "XML element name.",
1216
8.48k
                 poField->GetNameRef(), pszName);
1217
8.48k
    }
1218
1219
33.6k
    CPLFree(pszName);
1220
1221
33.6k
    poFeatureDefn->AddFieldDefn(&oCleanCopy);
1222
1223
33.6k
    return OGRERR_NONE;
1224
33.6k
}
1225
1226
/************************************************************************/
1227
/*                          CreateGeomField()                           */
1228
/************************************************************************/
1229
1230
OGRErr OGRGMLLayer::CreateGeomField(const OGRGeomFieldDefn *poField,
1231
                                    int bApproxOK)
1232
1233
2.55k
{
1234
2.55k
    if (!bWriter || m_iNextGMLId != 0)
1235
0
        return OGRERR_FAILURE;
1236
1237
    /* -------------------------------------------------------------------- */
1238
    /*      Enforce XML naming semantics on element name.                   */
1239
    /* -------------------------------------------------------------------- */
1240
2.55k
    OGRGeomFieldDefn oCleanCopy(poField);
1241
2.55k
    const auto poSRSOri = poField->GetSpatialRef();
1242
2.55k
    poDS->DeclareNewWriteSRS(poSRSOri);
1243
2.55k
    if (poSRSOri)
1244
1.26k
    {
1245
1.26k
        auto poSRS = poSRSOri->Clone();
1246
1.26k
        poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1247
1.26k
        oCleanCopy.SetSpatialRef(poSRS);
1248
1.26k
        poSRS->Release();
1249
1.26k
    }
1250
2.55k
    char *pszName = CPLStrdup(poField->GetNameRef());
1251
2.55k
    CPLCleanXMLElementName(pszName);
1252
1253
2.55k
    if (strcmp(pszName, poField->GetNameRef()) != 0)
1254
1.75k
    {
1255
1.75k
        if (!bApproxOK)
1256
0
        {
1257
0
            CPLFree(pszName);
1258
0
            CPLError(CE_Failure, CPLE_AppDefined,
1259
0
                     "Unable to create field with name '%s', it would not\n"
1260
0
                     "be valid as an XML element name.",
1261
0
                     poField->GetNameRef());
1262
0
            return OGRERR_FAILURE;
1263
0
        }
1264
1265
1.75k
        oCleanCopy.SetName(pszName);
1266
1.75k
        CPLError(CE_Warning, CPLE_AppDefined,
1267
1.75k
                 "Field name '%s' adjusted to '%s' to be a valid\n"
1268
1.75k
                 "XML element name.",
1269
1.75k
                 poField->GetNameRef(), pszName);
1270
1.75k
    }
1271
1272
2.55k
    CPLFree(pszName);
1273
1274
2.55k
    poFeatureDefn->AddGeomFieldDefn(&oCleanCopy);
1275
1276
2.55k
    return OGRERR_NONE;
1277
2.55k
}
1278
1279
/************************************************************************/
1280
/*                             GetDataset()                             */
1281
/************************************************************************/
1282
1283
GDALDataset *OGRGMLLayer::GetDataset()
1284
0
{
1285
0
    return poDS;
1286
0
}