Coverage Report

Created: 2026-05-16 08:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/gml/ogrgmldriver.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  OGR
4
 * Purpose:  OGRGMLDriver implementation
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2002, Frank Warmerdam <warmerdam@pobox.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "ogr_gml.h"
14
#include "cpl_conv.h"
15
#include "cpl_multiproc.h"
16
#include "gmlreaderp.h"
17
18
/************************************************************************/
19
/*                        OGRGMLDriverIdentify()                        */
20
/************************************************************************/
21
22
static int OGRGMLDriverIdentify(GDALOpenInfo *poOpenInfo)
23
24
143k
{
25
143k
    if (poOpenInfo->fpL == nullptr)
26
49.3k
    {
27
49.3k
        if (strstr(poOpenInfo->pszFilename, "xsd=") != nullptr)
28
6.75k
            return -1; /* must be later checked */
29
42.5k
        return FALSE;
30
49.3k
    }
31
    /* Might be a OS-Mastermap gzipped GML, so let be nice and try to open */
32
    /* it transparently with /vsigzip/ */
33
94.3k
    else if (poOpenInfo->pabyHeader[0] == 0x1f &&
34
357
             poOpenInfo->pabyHeader[1] == 0x8b &&
35
350
             poOpenInfo->IsExtensionEqualToCI("gz") &&
36
46
             !STARTS_WITH(poOpenInfo->pszFilename, "/vsigzip/"))
37
46
    {
38
46
        return -1; /* must be later checked */
39
46
    }
40
94.2k
    else
41
94.2k
    {
42
94.2k
        const char *szPtr =
43
94.2k
            reinterpret_cast<const char *>(poOpenInfo->pabyHeader);
44
45
94.2k
        if ((static_cast<unsigned char>(szPtr[0]) == 0xEF) &&
46
1.76k
            (static_cast<unsigned char>(szPtr[1]) == 0xBB) &&
47
1.58k
            (static_cast<unsigned char>(szPtr[2]) == 0xBF))
48
377
        {
49
377
            szPtr += 3;
50
377
        }
51
        /* --------------------------------------------------------------------
52
         */
53
        /*      Here, we expect the opening chevrons of GML tree root element */
54
        /* --------------------------------------------------------------------
55
         */
56
94.2k
        if (szPtr[0] != '<')
57
79.8k
            return FALSE;
58
59
14.3k
        if (!poOpenInfo->TryToIngest(4096))
60
0
            return FALSE;
61
62
14.3k
        if (poOpenInfo->IsSingleAllowedDriver("GML"))
63
0
            return TRUE;
64
65
14.3k
        return OGRGMLDataSource::CheckHeader(
66
14.3k
            reinterpret_cast<const char *>(poOpenInfo->pabyHeader));
67
14.3k
    }
68
143k
}
69
70
/************************************************************************/
71
/*                                Open()                                */
72
/************************************************************************/
73
74
static GDALDataset *OGRGMLDriverOpen(GDALOpenInfo *poOpenInfo)
75
76
7.40k
{
77
7.40k
    if (poOpenInfo->eAccess == GA_Update)
78
0
        return nullptr;
79
80
7.40k
    if (OGRGMLDriverIdentify(poOpenInfo) == FALSE)
81
60
        return nullptr;
82
83
7.34k
    OGRGMLDataSource *poDS = new OGRGMLDataSource();
84
85
7.34k
    if (!poDS->Open(poOpenInfo))
86
3.41k
    {
87
3.41k
        delete poDS;
88
3.41k
        return nullptr;
89
3.41k
    }
90
3.93k
    else
91
3.93k
        return poDS;
92
7.34k
}
93
94
/************************************************************************/
95
/*                               Create()                               */
96
/************************************************************************/
97
98
static GDALDataset *
99
OGRGMLDriverCreate(const char *pszName, CPL_UNUSED int nBands,
100
                   CPL_UNUSED int nXSize, CPL_UNUSED int nYSize,
101
                   CPL_UNUSED GDALDataType eDT, CSLConstList papszOptions)
102
341
{
103
341
    OGRGMLDataSource *poDS = new OGRGMLDataSource();
104
105
341
    if (!poDS->Create(pszName, papszOptions))
106
0
    {
107
0
        delete poDS;
108
0
        return nullptr;
109
0
    }
110
341
    else
111
341
        return poDS;
112
341
}
113
114
/************************************************************************/
115
/*                           RegisterOGRGML()                           */
116
/************************************************************************/
117
118
void RegisterOGRGML()
119
120
22
{
121
22
    if (GDALGetDriverByName("GML") != nullptr)
122
0
        return;
123
124
22
    GDALDriver *poDriver = new GDALDriver();
125
126
22
    poDriver->SetDescription("GML");
127
22
    poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
128
22
    poDriver->SetMetadataItem(GDAL_DCAP_CREATE_LAYER, "YES");
129
22
    poDriver->SetMetadataItem(GDAL_DCAP_CREATE_FIELD, "YES");
130
22
    poDriver->SetMetadataItem(GDAL_DCAP_CURVE_GEOMETRIES, "YES");
131
22
    poDriver->SetMetadataItem(GDAL_DCAP_Z_GEOMETRIES, "YES");
132
22
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
133
22
                              "Geography Markup Language (GML)");
134
22
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "gml");
135
22
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "gml xml");
136
22
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/vector/gml.html");
137
22
    poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "OGRSQL SQLITE");
138
139
22
    poDriver->SetMetadataItem(
140
22
        GDAL_DMD_OPENOPTIONLIST,
141
22
        "<OpenOptionList>"
142
22
        "  <Option name='XSD' type='string' description='Name of the related "
143
22
        "application schema file (.xsd).'/>"
144
22
        "  <Option name='GFS_TEMPLATE' type='string' description='Filename of "
145
22
        "a .gfs template file to apply.'/>"
146
22
        "  <Option name='WRITE_GFS' type='string-select' description='Whether "
147
22
        "to write a .gfs file' default='AUTO'>"
148
22
        "    <Value>AUTO</Value>"
149
22
        "    <Value>YES</Value>"
150
22
        "    <Value>NO</Value>"
151
22
        "  </Option>"
152
22
        "  <Option name='FORCE_SRS_DETECTION' type='boolean' "
153
22
        "description='Force a full scan to detect the SRS of layers.' "
154
22
        "default='NO'/>"
155
22
        "  <Option name='EMPTY_AS_NULL' type='boolean' description='Force "
156
22
        "empty fields to be reported as NULL. Set to NO so that not-nullable "
157
22
        "fields can be exposed' default='YES'/>"
158
22
        "  <Option name='GML_ATTRIBUTES_TO_OGR_FIELDS' type='boolean' "
159
22
        "description='Whether GML attributes should be reported as OGR fields' "
160
22
        "default='NO'/>"
161
22
        "  <Option name='INVERT_AXIS_ORDER_IF_LAT_LONG' type='boolean' "
162
22
        "description='Whether to present SRS and coordinate ordering in "
163
22
        "traditional GIS order' default='YES'/>"
164
22
        "  <Option name='CONSIDER_EPSG_AS_URN' type='string-select' "
165
22
        "description='Whether to consider srsName like EPSG:XXXX as respecting "
166
22
        "EPSG axis order' default='AUTO'>"
167
22
        "    <Value>AUTO</Value>"
168
22
        "    <Value>YES</Value>"
169
22
        "    <Value>NO</Value>"
170
22
        "  </Option>"
171
22
        "  <Option name='SWAP_COORDINATES' type='string-select' "
172
22
        "description='Whether the order of geometry coordinates should be "
173
22
        "inverted.' "
174
22
        "default='AUTO'>"
175
22
        "    <Value>AUTO</Value>"
176
22
        "    <Value>YES</Value>"
177
22
        "    <Value>NO</Value>"
178
22
        "  </Option>"
179
22
        "  <Option name='READ_MODE' type='string-select' description='Read "
180
22
        "mode' default='AUTO'>"
181
22
        "    <Value>AUTO</Value>"
182
22
        "    <Value>STANDARD</Value>"
183
22
        "    <Value>SEQUENTIAL_LAYERS</Value>"
184
22
        "    <Value>INTERLEAVED_LAYERS</Value>"
185
22
        "  </Option>"
186
22
        "  <Option name='EXPOSE_GML_ID' type='string-select' "
187
22
        "description='Whether to make feature gml:id as a gml_id attribute' "
188
22
        "default='AUTO'>"
189
22
        "    <Value>AUTO</Value>"
190
22
        "    <Value>YES</Value>"
191
22
        "    <Value>NO</Value>"
192
22
        "  </Option>"
193
22
        "  <Option name='EXPOSE_FID' type='string-select' description='Whether "
194
22
        "to make feature fid as a fid attribute' default='AUTO'>"
195
22
        "    <Value>AUTO</Value>"
196
22
        "    <Value>YES</Value>"
197
22
        "    <Value>NO</Value>"
198
22
        "  </Option>"
199
22
        "  <Option name='OGR_SCHEMA' type='string' description='"
200
22
        "Partially or totally overrides the auto-detected schema to use for "
201
22
        "creating the layer. "
202
22
        "The overrides are defined as a JSON list of field definitions. "
203
22
        "This can be a filename or a JSON string or a URL.'/>"
204
22
        "  <Option name='DOWNLOAD_SCHEMA' type='boolean' description='Whether "
205
22
        "to download the remote application schema if needed (only for WFS "
206
22
        "currently)' default='YES'/>"
207
22
        "  <Option name='REGISTRY' type='string' description='Filename of the "
208
22
        "registry with application schemas.'/>"
209
22
        "  <Option name='USE_BBOX' type='boolean' description='Whether "
210
22
        "to use gml:boundedBy at feature level as feature geometry, "
211
22
        "if there are no other geometry' default='NO'/>"
212
22
        "  <Option name='USE_SCHEMA_IMPORT' type='boolean' "
213
22
        "description='Whether "
214
22
        "to read schema for imports along with includes or not' default='NO'/>"
215
22
        "  <Option name='SKIP_CORRUPTED_FEATURES' type='boolean' "
216
22
        "description='Whether to skip features that cannot be parsed instead "
217
22
        "of failing' default='NO'/>"
218
22
        "  <Option name='SKIP_RESOLVE_ELEMS' type='string' "
219
22
        "description='Configure xlink element resolution. Set to NONE to "
220
22
        "resolve all elements, set to ALL to skip all xlink elements, "
221
22
        "set to HUGE to store linked elements in a temporary SQLite DB, "
222
22
        "set to a comma separated list of names of specific elements to be "
223
22
        "skipped.' "
224
22
        "default='ALL'/>"
225
22
        "</OpenOptionList>");
226
227
22
    poDriver->SetMetadataItem(
228
22
        GDAL_DMD_CREATIONOPTIONLIST,
229
22
        "<CreationOptionList>"
230
22
        "  <Option name='XSISCHEMAURI' type='string' description='URI to be "
231
22
        "inserted as the schema location.'/>"
232
22
        "  <Option name='XSISCHEMA' type='string-select' description='where to "
233
22
        "write a .xsd application schema. INTERNAL should not normally be "
234
22
        "used' default='EXTERNAL'>"
235
22
        "    <Value>EXTERNAL</Value>"
236
22
        "    <Value>INTERNAL</Value>"
237
22
        "    <Value>OFF</Value>"
238
22
        "  </Option>"
239
22
        "  <Option name='PREFIX' type='string' description='Prefix for the "
240
22
        "application target namespace.' default='ogr'/>"
241
22
        "  <Option name='STRIP_PREFIX' type='boolean' description='Whether to "
242
22
        "avoid writing the prefix of the application target namespace in the "
243
22
        "GML file.' default='NO'/>"
244
22
        "  <Option name='TARGET_NAMESPACE' type='string' "
245
22
        "description='Application target namespace.' "
246
22
        "default='http://ogr.maptools.org/'/>"
247
22
        "  <Option name='FORMAT' type='string-select' description='Version of "
248
22
        "GML to use' default='GML3.2'>"
249
22
        "    <Value>GML2</Value>"
250
22
        "    <Value>GML3</Value>"
251
22
        "    <Value>GML3.2</Value>"
252
22
        "    <Value>GML3Deegree</Value>"
253
22
        "  </Option>"
254
22
        "  <Option name='GML_FEATURE_COLLECTION' type='boolean' "
255
22
        "description='Whether to use the gml:FeatureCollection. Only valid for "
256
22
        "FORMAT=GML3/GML3.2' default='NO'/>"
257
22
        "  <Option name='GML3_LONGSRS' type='boolean' description='Whether to "
258
22
        "write SRS with \"urn:ogc:def:crs:EPSG::\" prefix with GML3* versions' "
259
22
        "default='YES'/>"
260
22
        "  <Option name='SRSNAME_FORMAT' type='string-select' "
261
22
        "description='Format of srsName (for GML3* versions)' "
262
22
        "default='OGC_URL'>"
263
22
        "    <Value>SHORT</Value>"
264
22
        "    <Value>OGC_URN</Value>"
265
22
        "    <Value>OGC_URL</Value>"
266
22
        "  </Option>"
267
22
        "  <Option name='WRITE_FEATURE_BOUNDED_BY' type='boolean' "
268
22
        "description='Whether to write &lt;gml:boundedBy&gt; element for each "
269
22
        "feature with GML3* versions' default='YES'/>"
270
22
        "  <Option name='SPACE_INDENTATION' type='boolean' "
271
22
        "description='Whether to indent the output for readability' "
272
22
        "default='YES'/>"
273
22
        "  <Option name='SRSDIMENSION_LOC' type='string-select' "
274
22
        "description='(only valid for FORMAT=GML3xx) Location where to put "
275
22
        "srsDimension attribute' default='POSLIST'>"
276
22
        "    <Value>POSLIST</Value>"
277
22
        "    <Value>GEOMETRY</Value>"
278
22
        "    <Value>GEOMETRY,POSLIST</Value>"
279
22
        "  </Option>"
280
22
        "  <Option name='GML_ID' type='string' description='Value of feature "
281
22
        "collection gml:id (GML 3.2 only)' default='aFeatureCollection'/>"
282
22
        "  <Option name='NAME' type='string' description='Content of GML name "
283
22
        "element'/>"
284
22
        "  <Option name='DESCRIPTION' type='string' description='Content of "
285
22
        "GML description element'/>"
286
22
        "</CreationOptionList>");
287
288
22
    poDriver->SetMetadataItem(GDAL_DS_LAYER_CREATIONOPTIONLIST,
289
22
                              "<LayerCreationOptionList/>");
290
22
    poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATATYPES,
291
22
                              "Integer Integer64 Real String Date DateTime "
292
22
                              "IntegerList Integer64List RealList StringList");
293
22
    poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATASUBTYPES,
294
22
                              "Boolean Int16 Float32");
295
22
    poDriver->SetMetadataItem(GDAL_DMD_CREATION_FIELD_DEFN_FLAGS,
296
22
                              "WidthPrecision Nullable Unique Comment");
297
298
22
    poDriver->SetMetadataItem(GDAL_DCAP_NOTNULL_FIELDS, "YES");
299
22
    poDriver->SetMetadataItem(GDAL_DCAP_UNIQUE_FIELDS, "YES");
300
22
    poDriver->SetMetadataItem(GDAL_DCAP_NOTNULL_GEOMFIELDS, "YES");
301
22
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
302
22
    poDriver->SetMetadataItem(GDAL_DCAP_MULTIPLE_VECTOR_LAYERS, "YES");
303
22
    poDriver->SetMetadataItem(GDAL_DCAP_HONOR_GEOM_COORDINATE_PRECISION, "YES");
304
22
    poDriver->SetMetadataItem(GDAL_DCAP_REOPEN_AFTER_WRITE_REQUIRED, "YES");
305
22
    poDriver->SetMetadataItem(GDAL_DCAP_CAN_READ_AFTER_DELETE, "YES");
306
307
22
    poDriver->pfnOpen = OGRGMLDriverOpen;
308
22
    poDriver->pfnIdentify = OGRGMLDriverIdentify;
309
22
    poDriver->pfnCreate = OGRGMLDriverCreate;
310
311
22
    GetGDALDriverManager()->RegisterDriver(poDriver);
312
22
}