Coverage Report

Created: 2026-08-11 08:26

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