Coverage Report

Created: 2025-06-09 08:44

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