Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implements Open FileGDB OGR driver.
5 : * Author: Even Rouault, <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2014, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * Permission is hereby granted, free of charge, to any person obtaining a
11 : * copy of this software and associated documentation files (the "Software"),
12 : * to deal in the Software without restriction, including without limitation
13 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 : * and/or sell copies of the Software, and to permit persons to whom the
15 : * Software is furnished to do so, subject to the following conditions:
16 : *
17 : * The above copyright notice and this permission notice shall be included
18 : * in all copies or substantial portions of the Software.
19 : *
20 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 : * DEALINGS IN THE SOFTWARE.
27 : ****************************************************************************/
28 :
29 : #include "ogrsf_frmts.h"
30 :
31 : #include "ogropenfilegdbdrivercore.h"
32 :
33 : #define ENDS_WITH(str, strLen, end) \
34 : (strLen >= strlen(end) && EQUAL(str + strLen - strlen(end), end))
35 :
36 : /************************************************************************/
37 : /* OGROpenFileGDBDriverIdentify() */
38 : /************************************************************************/
39 :
40 48821 : GDALIdentifyEnum OGROpenFileGDBDriverIdentify(GDALOpenInfo *poOpenInfo,
41 : const char *&pszFilename)
42 : {
43 48821 : if (STARTS_WITH(pszFilename, "OpenFileGDB:"))
44 44 : return GDAL_IDENTIFY_TRUE;
45 :
46 : // FUSIL is a fuzzer
47 : #ifdef FOR_FUSIL
48 : CPLString osOrigFilename(pszFilename);
49 : #endif
50 :
51 : // First check if we have to do any work.
52 48777 : size_t nLen = strlen(pszFilename);
53 48777 : if (ENDS_WITH(pszFilename, nLen, ".gdb") ||
54 46654 : ENDS_WITH(pszFilename, nLen, ".gdb/"))
55 : {
56 : // Check that the filename is really a directory, to avoid confusion
57 : // with Garmin MapSource - gdb format which can be a problem when the
58 : // driver is loaded as a plugin, and loaded before the GPSBabel driver
59 : // (http://trac.osgeo.org/osgeo4w/ticket/245)
60 1299 : if (STARTS_WITH(pszFilename, "/vsicurl/https://github.com/") ||
61 1299 : !poOpenInfo->bStatOK || !poOpenInfo->bIsDirectory)
62 : {
63 : // In case we do not manage to list the directory, try to stat one
64 : // file.
65 : VSIStatBufL stat;
66 476 : if (!(STARTS_WITH(pszFilename, "/vsicurl/") &&
67 0 : VSIStatL(
68 : CPLFormFilename(pszFilename, "a00000001", "gdbtable"),
69 : &stat) == 0))
70 : {
71 476 : return GDAL_IDENTIFY_FALSE;
72 : }
73 : }
74 823 : return GDAL_IDENTIFY_TRUE;
75 : }
76 : /* We also accept zipped GDB */
77 47478 : else if (ENDS_WITH(pszFilename, nLen, ".gdb.zip") ||
78 47349 : ENDS_WITH(pszFilename, nLen, ".gdb.tar") ||
79 : /* Canvec GBs */
80 46532 : (ENDS_WITH(pszFilename, nLen, ".zip") &&
81 12 : (strstr(pszFilename, "_gdb") != nullptr ||
82 12 : strstr(pszFilename, "_GDB") != nullptr)))
83 : {
84 128 : return GDAL_IDENTIFY_TRUE;
85 : }
86 : /* We also accept tables themselves */
87 47350 : else if (ENDS_WITH(pszFilename, nLen, ".gdbtable"))
88 : {
89 102 : return GDAL_IDENTIFY_TRUE;
90 : }
91 : #ifdef FOR_FUSIL
92 : /* To be able to test fuzzer on any auxiliary files used (indexes, etc.) */
93 : else if (strlen(CPLGetBasename(pszFilename)) == 9 &&
94 : CPLGetBasename(pszFilename)[0] == 'a')
95 : {
96 : pszFilename = CPLFormFilename(CPLGetPath(pszFilename),
97 : CPLGetBasename(pszFilename), "gdbtable");
98 : return GDAL_IDENTIFY_TRUE;
99 : }
100 : else if (strlen(CPLGetBasename(CPLGetBasename(pszFilename))) == 9 &&
101 : CPLGetBasename(CPLGetBasename(pszFilename))[0] == 'a')
102 : {
103 : pszFilename = CPLFormFilename(
104 : CPLGetPath(pszFilename),
105 : CPLGetBasename(CPLGetBasename(pszFilename)), "gdbtable");
106 : return GDAL_IDENTIFY_TRUE;
107 : }
108 : #endif
109 :
110 : #ifdef DEBUG
111 : /* For AFL, so that .cur_input is detected as the archive filename */
112 47248 : else if (EQUAL(CPLGetFilename(pszFilename), ".cur_input"))
113 : {
114 : // This file may be recognized or not by this driver,
115 : // but there were not enough elements to judge.
116 9 : return GDAL_IDENTIFY_UNKNOWN;
117 : }
118 : #endif
119 :
120 47241 : else if (EQUAL(pszFilename, "."))
121 : {
122 3 : GDALIdentifyEnum eRet = GDAL_IDENTIFY_FALSE;
123 3 : char *pszCurrentDir = CPLGetCurrentDir();
124 3 : if (pszCurrentDir)
125 : {
126 3 : const char *pszTmp = pszCurrentDir;
127 3 : eRet = OGROpenFileGDBDriverIdentify(poOpenInfo, pszTmp);
128 3 : CPLFree(pszCurrentDir);
129 : }
130 3 : return eRet;
131 : }
132 :
133 : else
134 : {
135 47238 : return GDAL_IDENTIFY_FALSE;
136 : }
137 : }
138 :
139 48267 : static int OGROpenFileGDBDriverIdentify(GDALOpenInfo *poOpenInfo)
140 : {
141 48267 : const char *pszFilename = poOpenInfo->pszFilename;
142 96529 : return OGROpenFileGDBDriverIdentify(poOpenInfo, pszFilename);
143 : }
144 :
145 : /************************************************************************/
146 : /* OGROpenFileGDBDriverSetCommonMetadata() */
147 : /************************************************************************/
148 :
149 1217 : void OGROpenFileGDBDriverSetCommonMetadata(GDALDriver *poDriver)
150 : {
151 1217 : poDriver->SetDescription(DRIVER_NAME);
152 1217 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "ESRI FileGDB");
153 1217 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "gdb");
154 1217 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
155 1217 : "drivers/vector/openfilegdb.html");
156 1217 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
157 1217 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
158 1217 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE_LAYER, "YES");
159 1217 : poDriver->SetMetadataItem(GDAL_DCAP_DELETE_LAYER, "YES");
160 1217 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE_FIELD, "YES");
161 1217 : poDriver->SetMetadataItem(GDAL_DCAP_DELETE_FIELD, "YES");
162 1217 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
163 1217 : poDriver->SetMetadataItem(GDAL_DCAP_CURVE_GEOMETRIES, "YES");
164 1217 : poDriver->SetMetadataItem(GDAL_DCAP_MEASURED_GEOMETRIES, "YES");
165 1217 : poDriver->SetMetadataItem(GDAL_DCAP_Z_GEOMETRIES, "YES");
166 1217 : poDriver->SetMetadataItem(GDAL_DMD_GEOMETRY_FLAGS,
167 : "EquatesMultiAndSingleLineStringDuringWrite "
168 1217 : "EquatesMultiAndSinglePolygonDuringWrite");
169 1217 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATATYPES,
170 : "Integer Real String Date DateTime Binary "
171 1217 : "Integer64 Date Time");
172 1217 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATASUBTYPES,
173 1217 : "Int16 Float32");
174 1217 : poDriver->SetMetadataItem(GDAL_DMD_CREATION_FIELD_DEFN_FLAGS,
175 : "Nullable Default "
176 1217 : "AlternativeName Domain");
177 1217 : poDriver->SetMetadataItem(
178 : GDAL_DMD_ALTER_FIELD_DEFN_FLAGS,
179 1217 : "Name Type Nullable Default Domain AlternativeName");
180 : // see https://support.esri.com/en/technical-article/000010906
181 1217 : poDriver->SetMetadataItem(
182 : GDAL_DMD_ILLEGAL_FIELD_NAMES,
183 : "ADD ALTER AND BETWEEN BY COLUMN CREATE DELETE DROP EXISTS FOR FROM "
184 : "GROUP IN INSERT INTO IS LIKE NOT NULL OR ORDER SELECT SET TABLE "
185 1217 : "UPDATE VALUES WHERE");
186 1217 : poDriver->SetMetadataItem(GDAL_DCAP_NOTNULL_FIELDS, "YES");
187 1217 : poDriver->SetMetadataItem(GDAL_DCAP_DEFAULT_FIELDS, "YES");
188 1217 : poDriver->SetMetadataItem(GDAL_DCAP_NOTNULL_GEOMFIELDS, "YES");
189 1217 : poDriver->SetMetadataItem(GDAL_DCAP_MULTIPLE_VECTOR_LAYERS, "YES");
190 1217 : poDriver->SetMetadataItem(GDAL_DCAP_FIELD_DOMAINS, "YES");
191 1217 : poDriver->SetMetadataItem(GDAL_DCAP_RENAME_LAYERS, "YES");
192 :
193 1217 : poDriver->SetMetadataItem(GDAL_DCAP_RELATIONSHIPS, "YES");
194 1217 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE_RELATIONSHIP, "YES");
195 1217 : poDriver->SetMetadataItem(GDAL_DCAP_DELETE_RELATIONSHIP, "YES");
196 1217 : poDriver->SetMetadataItem(GDAL_DCAP_UPDATE_RELATIONSHIP, "YES");
197 1217 : poDriver->SetMetadataItem(GDAL_DMD_RELATIONSHIP_FLAGS,
198 : "OneToOne OneToMany ManyToMany Composite "
199 1217 : "Association ForwardPathLabel BackwardPathLabel");
200 1217 : poDriver->SetMetadataItem(GDAL_DMD_RELATIONSHIP_RELATED_TABLE_TYPES,
201 1217 : "features media");
202 :
203 1217 : poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "OGRSQL SQLITE");
204 :
205 1217 : poDriver->SetMetadataItem(GDAL_DMD_CREATION_FIELD_DOMAIN_TYPES,
206 1217 : "Coded Range");
207 :
208 1217 : poDriver->SetMetadataItem(GDAL_DMD_ALTER_GEOM_FIELD_DEFN_FLAGS, "Name SRS");
209 1217 : poDriver->SetMetadataItem(GDAL_DCAP_HONOR_GEOM_COORDINATE_PRECISION, "YES");
210 :
211 1217 : poDriver->SetMetadataItem(
212 : GDAL_DMD_OPENOPTIONLIST,
213 : "<OpenOptionList>"
214 : " <Option name='LIST_ALL_TABLES' type='string-select' scope='vector' "
215 : "description='Whether all tables, including system and internal tables "
216 : "(such as GDB_* tables) should be listed' default='NO'>"
217 : " <Value>YES</Value>"
218 : " <Value>NO</Value>"
219 : " </Option>"
220 : " <Option name='NODATA_OR_MASK' type='string' scope='raster' "
221 : "description='AUTO, MASK, NONE or numeric nodata value'/>"
222 1217 : "</OpenOptionList>");
223 :
224 1217 : poDriver->SetMetadataItem(
225 : GDAL_DS_LAYER_CREATIONOPTIONLIST,
226 : "<LayerCreationOptionList>"
227 : " <Option name='TARGET_ARCGIS_VERSION' type='string-select' "
228 : "default='ALL'>"
229 : " <Value>ALL</Value>"
230 : " <Value>ARCGIS_PRO_3_2_OR_LATER</Value>"
231 : " </Option>"
232 : " <Option name='FEATURE_DATASET' type='string' "
233 : "description='FeatureDataset folder into which to put the new layer'/>"
234 : " <Option name='LAYER_ALIAS' type='string' description='Alias of "
235 : "layer name'/>"
236 : " <Option name='GEOMETRY_NAME' type='string' description='Name of "
237 : "geometry column' default='SHAPE'/>"
238 : " <Option name='GEOMETRY_NULLABLE' type='boolean' "
239 : "description='Whether the values of the geometry column can be NULL' "
240 : "default='YES'/>"
241 : " <Option name='FID' type='string' description='Name of OID column' "
242 : "default='OBJECTID'/>"
243 : " <Option name='XYTOLERANCE' type='float' description='Snapping "
244 : "tolerance, used for advanced ArcGIS features like network and "
245 : "topology rules, on 2D coordinates, in the units of the CRS'/>"
246 : " <Option name='ZTOLERANCE' type='float' description='Snapping "
247 : "tolerance, used for advanced ArcGIS features like network and "
248 : "topology rules, on Z coordinates, in the units of the CRS'/>"
249 : " <Option name='MTOLERANCE' type='float' description='Snapping "
250 : "tolerance, used for advanced ArcGIS features like network and "
251 : "topology rules, on M coordinates'/>"
252 : " <Option name='XORIGIN' type='float' description='X origin of the "
253 : "coordinate precision grid'/>"
254 : " <Option name='YORIGIN' type='float' description='Y origin of the "
255 : "coordinate precision grid'/>"
256 : " <Option name='ZORIGIN' type='float' description='Z origin of the "
257 : "coordinate precision grid'/>"
258 : " <Option name='MORIGIN' type='float' description='M origin of the "
259 : "coordinate precision grid'/>"
260 : " <Option name='XYSCALE' type='float' description='X,Y scale of the "
261 : "coordinate precision grid'/>"
262 : " <Option name='ZSCALE' type='float' description='Z scale of the "
263 : "coordinate precision grid'/>"
264 : " <Option name='MSCALE' type='float' description='M scale of the "
265 : "coordinate precision grid'/>"
266 : " <Option name='COLUMN_TYPES' type='string' description='A list of "
267 : "strings of format field_name=fgdb_field_type (separated by comma) to "
268 : "force the FileGDB column type of fields to be created'/>"
269 : " <Option name='DOCUMENTATION' type='string' description='XML "
270 : "documentation'/>"
271 : " <Option name='CONFIGURATION_KEYWORD' type='string-select' "
272 : "description='Customize how data is stored. By default text in UTF-8 "
273 : "and data up to 1TB' default='DEFAULTS'>"
274 : " <Value>DEFAULTS</Value>"
275 : " <Value>MAX_FILE_SIZE_4GB</Value>"
276 : " <Value>MAX_FILE_SIZE_256TB</Value>"
277 : " <Value>TEXT_UTF16</Value>"
278 : " </Option>"
279 : " <Option name='TIME_IN_UTC' type='boolean' description='Whether "
280 : "datetime fields should be considered to be in UTC' default='NO'/>"
281 : " <Option name='CREATE_SHAPE_AREA_AND_LENGTH_FIELDS' type='boolean' "
282 : "description='Whether to create special Shape_Length and Shape_Area "
283 : "fields' default='NO'/>"
284 1217 : "</LayerCreationOptionList>");
285 :
286 : // Setting to another value than the default one doesn't really work
287 : // with the SDK
288 : // Option name='AREA_FIELD_NAME' type='string' description='Name of
289 : // the column that contains the geometry area' default='Shape_Area'
290 : // Option name='length_field_name' type='string' description='Name of
291 : // the column that contains the geometry length'
292 : // default='Shape_Length'
293 :
294 1217 : poDriver->pfnIdentify = OGROpenFileGDBDriverIdentify;
295 1217 : poDriver->SetMetadataItem(GDAL_DCAP_OPEN, "YES");
296 1217 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE, "YES");
297 1217 : }
298 :
299 : /************************************************************************/
300 : /* DeclareDeferredOGROpenFileGDBPlugin() */
301 : /************************************************************************/
302 :
303 : #ifdef PLUGIN_FILENAME
304 : void DeclareDeferredOGROpenFileGDBPlugin()
305 : {
306 : if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
307 : {
308 : return;
309 : }
310 : auto poDriver = new GDALPluginDriverProxy(PLUGIN_FILENAME);
311 : #ifdef PLUGIN_INSTALLATION_MESSAGE
312 : poDriver->SetMetadataItem(GDAL_DMD_PLUGIN_INSTALLATION_MESSAGE,
313 : PLUGIN_INSTALLATION_MESSAGE);
314 : #endif
315 : OGROpenFileGDBDriverSetCommonMetadata(poDriver);
316 : GetGDALDriverManager()->DeclareDeferredPluginDriver(poDriver);
317 : }
318 : #endif
|