Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GeoPackage Translator
4 : * Purpose: Implements GeoPackageDriver.
5 : * Author: Paul Ramsey <pramsey@boundlessgeo.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2013, Paul Ramsey <pramsey@boundlessgeo.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "ogr_geopackage.h"
14 :
15 : #include "tilematrixset.hpp"
16 :
17 : #include <cctype>
18 :
19 : // g++ -g -Wall -fPIC -shared -o ogr_geopackage.so -Iport -Igcore -Iogr
20 : // -Iogr/ogrsf_frmts -Iogr/ogrsf_frmts/gpkg ogr/ogrsf_frmts/gpkg/*.c* -L. -lgdal
21 :
22 652 : static inline bool ENDS_WITH_CI(const char *a, const char *b)
23 : {
24 652 : return strlen(a) >= strlen(b) && EQUAL(a + strlen(a) - strlen(b), b);
25 : }
26 :
27 : /************************************************************************/
28 : /* OGRGeoPackageDriverIdentify() */
29 : /************************************************************************/
30 :
31 55590 : static int OGRGeoPackageDriverIdentify(GDALOpenInfo *poOpenInfo,
32 : std::string &osFilenameInGpkgZip,
33 : bool bEmitWarning)
34 : {
35 55590 : if (STARTS_WITH_CI(poOpenInfo->pszFilename, "GPKG:"))
36 480 : return TRUE;
37 :
38 : #ifdef ENABLE_SQL_GPKG_FORMAT
39 55110 : if (poOpenInfo->pabyHeader &&
40 5769 : STARTS_WITH(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
41 : "-- SQL GPKG"))
42 : {
43 10 : return TRUE;
44 : }
45 : #endif
46 :
47 : // Try to recognize "foo.gpkg.zip"
48 55100 : const size_t nFilenameLen = strlen(poOpenInfo->pszFilename);
49 55100 : if ((poOpenInfo->nOpenFlags & GDAL_OF_UPDATE) == 0 &&
50 52827 : nFilenameLen > strlen(".gpkg.zip") &&
51 52827 : !STARTS_WITH(poOpenInfo->pszFilename, "/vsizip/") &&
52 52806 : EQUAL(poOpenInfo->pszFilename + nFilenameLen - strlen(".gpkg.zip"),
53 : ".gpkg.zip"))
54 : {
55 6 : int nCountGpkg = 0;
56 : const CPLStringList aosFiles(VSIReadDirEx(
57 18 : (std::string("/vsizip/") + poOpenInfo->pszFilename).c_str(), 1000));
58 8 : for (int i = 0; i < aosFiles.size(); ++i)
59 : {
60 2 : const size_t nLen = strlen(aosFiles[i]);
61 4 : if (nLen > strlen(".gpkg") &&
62 2 : EQUAL(aosFiles[i] + nLen - strlen(".gpkg"), ".gpkg"))
63 : {
64 2 : osFilenameInGpkgZip = aosFiles[i];
65 2 : nCountGpkg++;
66 2 : if (nCountGpkg == 2)
67 0 : return FALSE;
68 : }
69 : }
70 6 : return nCountGpkg == 1;
71 : }
72 :
73 55094 : if (poOpenInfo->nHeaderBytes < 100 || poOpenInfo->pabyHeader == nullptr ||
74 5325 : !STARTS_WITH(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
75 : "SQLite format 3"))
76 : {
77 53217 : return FALSE;
78 : }
79 :
80 : /* Requirement 3: File name has to end in "gpkg" */
81 : /* http://opengis.github.io/geopackage/#_file_extension_name */
82 : /* But be tolerant, if the GPKG application id is found, because some */
83 : /* producers don't necessarily honour that requirement (#6396) */
84 1877 : const char *pszExt = CPLGetExtension(poOpenInfo->pszFilename);
85 1877 : const bool bIsRecognizedExtension =
86 1877 : EQUAL(pszExt, "GPKG") || EQUAL(pszExt, "GPKX");
87 :
88 : /* Requirement 2: application id */
89 : /* http://opengis.github.io/geopackage/#_file_format */
90 : /* Be tolerant since some datasets don't actually follow that requirement */
91 : GUInt32 nApplicationId;
92 1877 : memcpy(&nApplicationId, poOpenInfo->pabyHeader + knApplicationIdPos, 4);
93 1877 : nApplicationId = CPL_MSBWORD32(nApplicationId);
94 : GUInt32 nUserVersion;
95 1877 : memcpy(&nUserVersion, poOpenInfo->pabyHeader + knUserVersionPos, 4);
96 1877 : nUserVersion = CPL_MSBWORD32(nUserVersion);
97 1877 : if (nApplicationId != GP10_APPLICATION_ID &&
98 1862 : nApplicationId != GP11_APPLICATION_ID &&
99 1857 : nApplicationId != GPKG_APPLICATION_ID)
100 : {
101 : #ifdef DEBUG
102 206 : if (EQUAL(CPLGetFilename(poOpenInfo->pszFilename), ".cur_input"))
103 : {
104 1 : return FALSE;
105 : }
106 : #endif
107 205 : if (!bIsRecognizedExtension)
108 201 : return FALSE;
109 :
110 4 : if (bEmitWarning)
111 : {
112 : GByte abySignature[4 + 1];
113 2 : memcpy(abySignature, poOpenInfo->pabyHeader + knApplicationIdPos,
114 : 4);
115 2 : abySignature[4] = '\0';
116 :
117 : /* Is this a GPxx version ? */
118 2 : const bool bWarn = CPLTestBool(CPLGetConfigOption(
119 : "GPKG_WARN_UNRECOGNIZED_APPLICATION_ID", "YES"));
120 2 : if (bWarn)
121 : {
122 1 : CPLError(CE_Warning, CPLE_AppDefined,
123 : "GPKG: bad application_id=0x%02X%02X%02X%02X on '%s'",
124 1 : abySignature[0], abySignature[1], abySignature[2],
125 1 : abySignature[3], poOpenInfo->pszFilename);
126 : }
127 : else
128 : {
129 1 : CPLDebug("GPKG",
130 : "bad application_id=0x%02X%02X%02X%02X on '%s'",
131 1 : abySignature[0], abySignature[1], abySignature[2],
132 1 : abySignature[3], poOpenInfo->pszFilename);
133 : }
134 4 : }
135 : }
136 1671 : else if (nApplicationId == GPKG_APPLICATION_ID &&
137 : // Accept any 102XX version
138 1651 : !((nUserVersion >= GPKG_1_2_VERSION &&
139 1646 : nUserVersion < GPKG_1_2_VERSION + 99) ||
140 : // Accept any 103XX version
141 37 : (nUserVersion >= GPKG_1_3_VERSION &&
142 32 : nUserVersion < GPKG_1_3_VERSION + 99) ||
143 : // Accept any 104XX version
144 33 : (nUserVersion >= GPKG_1_4_VERSION &&
145 28 : nUserVersion < GPKG_1_4_VERSION + 99)))
146 : {
147 : #ifdef DEBUG
148 9 : if (EQUAL(CPLGetFilename(poOpenInfo->pszFilename), ".cur_input"))
149 : {
150 1 : return FALSE;
151 : }
152 : #endif
153 8 : if (!bIsRecognizedExtension)
154 0 : return FALSE;
155 :
156 8 : if (bEmitWarning)
157 : {
158 : GByte abySignature[4 + 1];
159 4 : memcpy(abySignature, poOpenInfo->pabyHeader + knUserVersionPos, 4);
160 4 : abySignature[4] = '\0';
161 :
162 4 : const bool bWarn = CPLTestBool(CPLGetConfigOption(
163 : "GPKG_WARN_UNRECOGNIZED_APPLICATION_ID", "YES"));
164 4 : if (bWarn)
165 : {
166 2 : if (nUserVersion > GPKG_1_4_VERSION)
167 : {
168 1 : CPLError(CE_Warning, CPLE_AppDefined,
169 : "This version of GeoPackage "
170 : "user_version=0x%02X%02X%02X%02X "
171 : "(%u, v%d.%d.%d) on '%s' may only be "
172 : "partially supported",
173 1 : abySignature[0], abySignature[1], abySignature[2],
174 1 : abySignature[3], nUserVersion,
175 1 : nUserVersion / 10000, (nUserVersion % 10000) / 100,
176 : nUserVersion % 100, poOpenInfo->pszFilename);
177 : }
178 : else
179 : {
180 1 : CPLError(CE_Warning, CPLE_AppDefined,
181 : "GPKG: unrecognized user_version="
182 : "0x%02X%02X%02X%02X (%u) on '%s'",
183 1 : abySignature[0], abySignature[1], abySignature[2],
184 1 : abySignature[3], nUserVersion,
185 : poOpenInfo->pszFilename);
186 : }
187 : }
188 : else
189 : {
190 2 : if (nUserVersion > GPKG_1_4_VERSION)
191 : {
192 1 : CPLDebug("GPKG",
193 : "This version of GeoPackage "
194 : "user_version=0x%02X%02X%02X%02X "
195 : "(%u, v%d.%d.%d) on '%s' may only be "
196 : "partially supported",
197 1 : abySignature[0], abySignature[1], abySignature[2],
198 1 : abySignature[3], nUserVersion,
199 1 : nUserVersion / 10000, (nUserVersion % 10000) / 100,
200 : nUserVersion % 100, poOpenInfo->pszFilename);
201 : }
202 : else
203 : {
204 1 : CPLDebug("GPKG",
205 : "unrecognized user_version=0x%02X%02X%02X%02X"
206 : "(%u) on '%s'",
207 1 : abySignature[0], abySignature[1], abySignature[2],
208 1 : abySignature[3], nUserVersion,
209 : poOpenInfo->pszFilename);
210 : }
211 : }
212 8 : }
213 : }
214 3324 : else if (!bIsRecognizedExtension
215 : #ifdef DEBUG
216 50 : && !EQUAL(CPLGetFilename(poOpenInfo->pszFilename), ".cur_input")
217 : #endif
218 50 : && !(STARTS_WITH(poOpenInfo->pszFilename, "/vsizip/") &&
219 1712 : EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "zip")) &&
220 48 : !STARTS_WITH(poOpenInfo->pszFilename, "/vsigzip/"))
221 : {
222 48 : if (bEmitWarning)
223 : {
224 24 : CPLError(CE_Warning, CPLE_AppDefined,
225 : "File %s has GPKG application_id, but non conformant file "
226 : "extension",
227 : poOpenInfo->pszFilename);
228 : }
229 : }
230 :
231 2326 : if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) != 0 &&
232 652 : ENDS_WITH_CI(poOpenInfo->pszFilename, ".gti.gpkg"))
233 : {
234 : // Most likely handled by GTI driver, but we can't be sure
235 33 : return GDAL_IDENTIFY_UNKNOWN;
236 : }
237 :
238 1641 : return TRUE;
239 : }
240 :
241 54574 : static int OGRGeoPackageDriverIdentify(GDALOpenInfo *poOpenInfo)
242 : {
243 109132 : std::string osIgnored;
244 109132 : return OGRGeoPackageDriverIdentify(poOpenInfo, osIgnored, false);
245 : }
246 :
247 : /************************************************************************/
248 : /* OGRGeoPackageDriverGetSubdatasetInfo() */
249 : /************************************************************************/
250 :
251 : struct OGRGeoPackageDriverSubdatasetInfo : public GDALSubdatasetInfo
252 : {
253 : public:
254 11 : explicit OGRGeoPackageDriverSubdatasetInfo(const std::string &fileName)
255 11 : : GDALSubdatasetInfo(fileName)
256 : {
257 11 : }
258 :
259 : // GDALSubdatasetInfo interface
260 : private:
261 11 : void parseFileName() override
262 : {
263 11 : if (!STARTS_WITH_CI(m_fileName.c_str(), "GPKG:"))
264 : {
265 1 : return;
266 : }
267 :
268 11 : CPLStringList aosParts{CSLTokenizeString2(m_fileName.c_str(), ":", 0)};
269 11 : const int iPartsCount{CSLCount(aosParts)};
270 :
271 11 : if (iPartsCount == 3 || iPartsCount == 4)
272 : {
273 :
274 9 : m_driverPrefixComponent = aosParts[0];
275 :
276 9 : int subdatasetIndex{2};
277 : const bool hasDriveLetter{
278 14 : strlen(aosParts[1]) == 1 &&
279 5 : std::isalpha(static_cast<unsigned char>(aosParts[1][0]))};
280 :
281 : // Check for drive letter
282 9 : if (iPartsCount == 4)
283 : {
284 : // Invalid
285 4 : if (!hasDriveLetter)
286 : {
287 0 : return;
288 : }
289 4 : m_pathComponent = aosParts[1];
290 4 : m_pathComponent.append(":");
291 4 : m_pathComponent.append(aosParts[2]);
292 4 : subdatasetIndex++;
293 : }
294 : else // count is 3
295 : {
296 5 : if (hasDriveLetter)
297 : {
298 1 : return;
299 : }
300 4 : m_pathComponent = aosParts[1];
301 : }
302 :
303 8 : m_subdatasetComponent = aosParts[subdatasetIndex];
304 : }
305 : }
306 : };
307 :
308 : static GDALSubdatasetInfo *
309 2599 : OGRGeoPackageDriverGetSubdatasetInfo(const char *pszFileName)
310 : {
311 2599 : if (STARTS_WITH_CI(pszFileName, "GPKG:"))
312 : {
313 : std::unique_ptr<GDALSubdatasetInfo> info =
314 11 : std::make_unique<OGRGeoPackageDriverSubdatasetInfo>(pszFileName);
315 30 : if (!info->GetSubdatasetComponent().empty() &&
316 19 : !info->GetPathComponent().empty())
317 : {
318 8 : return info.release();
319 : }
320 : }
321 2591 : return nullptr;
322 : }
323 :
324 : /************************************************************************/
325 : /* Open() */
326 : /************************************************************************/
327 :
328 1028 : static GDALDataset *OGRGeoPackageDriverOpen(GDALOpenInfo *poOpenInfo)
329 : {
330 2056 : std::string osFilenameInGpkgZip;
331 1028 : if (OGRGeoPackageDriverIdentify(poOpenInfo, osFilenameInGpkgZip, true) ==
332 : GDAL_IDENTIFY_FALSE)
333 0 : return nullptr;
334 :
335 1028 : GDALGeoPackageDataset *poDS = new GDALGeoPackageDataset();
336 :
337 1028 : if (!poDS->Open(poOpenInfo, osFilenameInGpkgZip))
338 : {
339 16 : delete poDS;
340 16 : poDS = nullptr;
341 : }
342 :
343 1028 : return poDS;
344 : }
345 :
346 : /************************************************************************/
347 : /* Create() */
348 : /************************************************************************/
349 :
350 755 : static GDALDataset *OGRGeoPackageDriverCreate(const char *pszFilename,
351 : int nXSize, int nYSize,
352 : int nBands, GDALDataType eDT,
353 : char **papszOptions)
354 : {
355 755 : if (strcmp(pszFilename, ":memory:") != 0)
356 : {
357 751 : const size_t nFilenameLen = strlen(pszFilename);
358 751 : if (nFilenameLen > strlen(".gpkg.zip") &&
359 750 : !STARTS_WITH(pszFilename, "/vsizip/") &&
360 750 : EQUAL(pszFilename + nFilenameLen - strlen(".gpkg.zip"),
361 : ".gpkg.zip"))
362 : {
363 : // do nothing
364 : }
365 : else
366 : {
367 749 : const char *pszExt = CPLGetExtension(pszFilename);
368 749 : const bool bIsRecognizedExtension =
369 749 : EQUAL(pszExt, "GPKG") || EQUAL(pszExt, "GPKX");
370 749 : if (!bIsRecognizedExtension)
371 : {
372 85 : CPLError(
373 : CE_Warning, CPLE_AppDefined,
374 : "The filename extension should be 'gpkg' instead of '%s' "
375 : "to conform to the GPKG specification.",
376 : pszExt);
377 : }
378 : }
379 : }
380 :
381 755 : GDALGeoPackageDataset *poDS = new GDALGeoPackageDataset();
382 :
383 755 : if (!poDS->Create(pszFilename, nXSize, nYSize, nBands, eDT, papszOptions))
384 : {
385 35 : delete poDS;
386 35 : poDS = nullptr;
387 : }
388 :
389 755 : return poDS;
390 : }
391 :
392 : /************************************************************************/
393 : /* Delete() */
394 : /************************************************************************/
395 :
396 120 : static CPLErr OGRGeoPackageDriverDelete(const char *pszFilename)
397 :
398 : {
399 240 : std::string osAuxXml(pszFilename);
400 120 : osAuxXml += ".aux.xml";
401 : VSIStatBufL sStat;
402 120 : if (VSIStatL(osAuxXml.c_str(), &sStat) == 0)
403 3 : CPL_IGNORE_RET_VAL(VSIUnlink(osAuxXml.c_str()));
404 :
405 120 : if (VSIUnlink(pszFilename) == 0)
406 110 : return CE_None;
407 : else
408 10 : return CE_Failure;
409 : }
410 :
411 : /************************************************************************/
412 : /* RegisterOGRGeoPackage() */
413 : /************************************************************************/
414 :
415 : class GDALGPKGDriver final : public GDALDriver
416 : {
417 : bool m_bInitialized = false;
418 :
419 : void InitializeCreationOptionList();
420 :
421 : public:
422 1293 : GDALGPKGDriver() = default;
423 :
424 45995 : const char *GetMetadataItem(const char *pszName,
425 : const char *pszDomain) override
426 : {
427 45995 : if (EQUAL(pszName, GDAL_DMD_CREATIONOPTIONLIST))
428 : {
429 995 : InitializeCreationOptionList();
430 : }
431 45995 : return GDALDriver::GetMetadataItem(pszName, pszDomain);
432 : }
433 :
434 129 : char **GetMetadata(const char *pszDomain) override
435 : {
436 129 : InitializeCreationOptionList();
437 129 : return GDALDriver::GetMetadata(pszDomain);
438 : }
439 : };
440 :
441 : #define COMPRESSION_OPTIONS \
442 : " <Option name='TILE_FORMAT' type='string-select' scope='raster' " \
443 : "description='Format to use to create tiles' default='AUTO'>" \
444 : " <Value>AUTO</Value>" \
445 : " <Value>PNG_JPEG</Value>" \
446 : " <Value>PNG</Value>" \
447 : " <Value>PNG8</Value>" \
448 : " <Value>JPEG</Value>" \
449 : " <Value>WEBP</Value>" \
450 : " <Value>TIFF</Value>" \
451 : " </Option>" \
452 : " <Option name='QUALITY' type='int' min='1' max='100' scope='raster' " \
453 : "description='Quality for JPEG and WEBP tiles' default='75'/>" \
454 : " <Option name='ZLEVEL' type='int' min='1' max='9' scope='raster' " \
455 : "description='DEFLATE compression level for PNG tiles' default='6'/>" \
456 : " <Option name='DITHER' type='boolean' scope='raster' " \
457 : "description='Whether to apply Floyd-Steinberg dithering (for " \
458 : "TILE_FORMAT=PNG8)' default='NO'/>"
459 :
460 1124 : void GDALGPKGDriver::InitializeCreationOptionList()
461 : {
462 1124 : if (m_bInitialized)
463 1080 : return;
464 44 : m_bInitialized = true;
465 :
466 44 : const char *pszCOBegin =
467 : "<CreationOptionList>"
468 : " <Option name='RASTER_TABLE' type='string' scope='raster' "
469 : "description='Name of tile user table'/>"
470 : " <Option name='APPEND_SUBDATASET' type='boolean' scope='raster' "
471 : "description='Set to YES to add a new tile user table to an existing "
472 : "GeoPackage instead of replacing it' default='NO'/>"
473 : " <Option name='RASTER_IDENTIFIER' type='string' scope='raster' "
474 : "description='Human-readable identifier (e.g. short name)'/>"
475 : " <Option name='RASTER_DESCRIPTION' type='string' scope='raster' "
476 : "description='Human-readable description'/>"
477 : " <Option name='BLOCKSIZE' type='int' scope='raster' "
478 : "description='Block size in pixels' default='256' max='4096'/>"
479 : " <Option name='BLOCKXSIZE' type='int' scope='raster' "
480 : "description='Block width in pixels' default='256' max='4096'/>"
481 : " <Option name='BLOCKYSIZE' type='int' scope='raster' "
482 : "description='Block height in pixels' default='256' "
483 : "max='4096'/>" COMPRESSION_OPTIONS
484 : " <Option name='TILING_SCHEME' type='string' scope='raster' "
485 : "description='Which tiling scheme to use: pre-defined value or custom "
486 : "inline/outline JSON definition' default='CUSTOM'>"
487 : " <Value>CUSTOM</Value>"
488 : " <Value>GoogleCRS84Quad</Value>"
489 : " <Value>PseudoTMS_GlobalGeodetic</Value>"
490 : " <Value>PseudoTMS_GlobalMercator</Value>";
491 :
492 44 : const char *pszCOEnd =
493 : " </Option>"
494 : " <Option name='ZOOM_LEVEL' type='integer' scope='raster' "
495 : "description='Zoom level of full resolution. Only "
496 : "used for TILING_SCHEME != CUSTOM' min='0' max='30'/>"
497 : " <Option name='ZOOM_LEVEL_STRATEGY' type='string-select' "
498 : "scope='raster' description='Strategy to determine zoom level. Only "
499 : "used for TILING_SCHEME != CUSTOM' default='AUTO'>"
500 : " <Value>AUTO</Value>"
501 : " <Value>LOWER</Value>"
502 : " <Value>UPPER</Value>"
503 : " </Option>"
504 : " <Option name='RESAMPLING' type='string-select' scope='raster' "
505 : "description='Resampling algorithm. Only used for TILING_SCHEME != "
506 : "CUSTOM' default='BILINEAR'>"
507 : " <Value>NEAREST</Value>"
508 : " <Value>BILINEAR</Value>"
509 : " <Value>CUBIC</Value>"
510 : " <Value>CUBICSPLINE</Value>"
511 : " <Value>LANCZOS</Value>"
512 : " <Value>MODE</Value>"
513 : " <Value>AVERAGE</Value>"
514 : " </Option>"
515 : " <Option name='PRECISION' type='float' scope='raster' "
516 : "description='Smallest significant value. Only used for tiled gridded "
517 : "coverage datasets' default='1'/>"
518 : " <Option name='UOM' type='string' scope='raster' description='Unit "
519 : "of Measurement. Only used for tiled gridded coverage datasets' />"
520 : " <Option name='FIELD_NAME' type='string' scope='raster' "
521 : "description='Field name. Only used for tiled gridded coverage "
522 : "datasets' default='Height'/>"
523 : " <Option name='QUANTITY_DEFINITION' type='string' scope='raster' "
524 : "description='Description of the field. Only used for tiled gridded "
525 : "coverage datasets' default='Height'/>"
526 : " <Option name='GRID_CELL_ENCODING' type='string-select' "
527 : "scope='raster' description='Grid cell encoding. Only used for tiled "
528 : "gridded coverage datasets' default='grid-value-is-center'>"
529 : " <Value>grid-value-is-center</Value>"
530 : " <Value>grid-value-is-area</Value>"
531 : " <Value>grid-value-is-corner</Value>"
532 : " </Option>"
533 : " <Option name='VERSION' type='string-select' description='Set "
534 : "GeoPackage version (for application_id and user_version fields)' "
535 : "default='AUTO'>"
536 : " <Value>AUTO</Value>"
537 : " <Value>1.0</Value>"
538 : " <Value>1.1</Value>"
539 : " <Value>1.2</Value>"
540 : " <Value>1.3</Value>"
541 : " <Value>1.4</Value>"
542 : " </Option>"
543 : " <Option name='DATETIME_FORMAT' type='string-select' "
544 : "description='How to encode DateTime not in UTC' default='WITH_TZ'>"
545 : " <Value>WITH_TZ</Value>"
546 : " <Value>UTC</Value>"
547 : " </Option>"
548 : #ifdef ENABLE_GPKG_OGR_CONTENTS
549 : " <Option name='ADD_GPKG_OGR_CONTENTS' type='boolean' "
550 : "description='Whether to add a gpkg_ogr_contents table to keep feature "
551 : "count' default='YES'/>"
552 : #endif
553 : " <Option name='CRS_WKT_EXTENSION' type='boolean' "
554 : "description='Whether to create the database with the crs_wkt "
555 : "extension'/>"
556 : " <Option name='METADATA_TABLES' type='boolean' "
557 : "description='Whether to create the metadata related system tables'/>"
558 : "</CreationOptionList>";
559 :
560 88 : std::string osOptions(pszCOBegin);
561 88 : const auto tmsList = gdal::TileMatrixSet::listPredefinedTileMatrixSets();
562 308 : for (const auto &tmsName : tmsList)
563 : {
564 528 : const auto poTM = gdal::TileMatrixSet::parse(tmsName.c_str());
565 528 : if (poTM && poTM->haveAllLevelsSameTopLeft() &&
566 264 : poTM->haveAllLevelsSameTileSize() &&
567 704 : poTM->hasOnlyPowerOfTwoVaryingScales() &&
568 176 : !poTM->hasVariableMatrixWidth())
569 : {
570 176 : osOptions += " <Value>";
571 176 : osOptions += tmsName;
572 176 : osOptions += "</Value>";
573 : }
574 : }
575 44 : osOptions += pszCOEnd;
576 :
577 44 : SetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST, osOptions.c_str());
578 : }
579 :
580 1595 : void RegisterOGRGeoPackage()
581 : {
582 1595 : if (GDALGetDriverByName("GPKG") != nullptr)
583 302 : return;
584 :
585 1293 : GDALDriver *poDriver = new GDALGPKGDriver();
586 :
587 1293 : poDriver->SetDescription("GPKG");
588 1293 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
589 1293 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
590 1293 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE_LAYER, "YES");
591 1293 : poDriver->SetMetadataItem(GDAL_DCAP_DELETE_LAYER, "YES");
592 1293 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE_FIELD, "YES");
593 1293 : poDriver->SetMetadataItem(GDAL_DCAP_DELETE_FIELD, "YES");
594 1293 : poDriver->SetMetadataItem(GDAL_DCAP_REORDER_FIELDS, "YES");
595 1293 : poDriver->SetMetadataItem(GDAL_DCAP_CURVE_GEOMETRIES, "YES");
596 1293 : poDriver->SetMetadataItem(GDAL_DCAP_MEASURED_GEOMETRIES, "YES");
597 1293 : poDriver->SetMetadataItem(GDAL_DCAP_Z_GEOMETRIES, "YES");
598 1293 : poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES");
599 1293 : poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS,
600 1293 : "NATIVE OGRSQL SQLITE");
601 :
602 1293 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "GeoPackage");
603 1293 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "gpkg gpkg.zip");
604 1293 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/vector/gpkg.html");
605 1293 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
606 1293 : "Byte Int16 UInt16 Float32");
607 :
608 1293 : poDriver->SetMetadataItem(
609 : GDAL_DMD_OPENOPTIONLIST,
610 : "<OpenOptionList>"
611 : " <Option name='LIST_ALL_TABLES' type='string-select' scope='vector' "
612 : "description='Whether all tables, including those non listed in "
613 : "gpkg_contents, should be listed' default='AUTO'>"
614 : " <Value>AUTO</Value>"
615 : " <Value>YES</Value>"
616 : " <Value>NO</Value>"
617 : " </Option>"
618 : " <Option name='TABLE' type='string' scope='raster' description='Name "
619 : "of tile user-table'/>"
620 : " <Option name='ZOOM_LEVEL' type='integer' scope='raster' "
621 : "description='Zoom level of full resolution. If not specified, maximum "
622 : "non-empty zoom level'/>"
623 : " <Option name='BAND_COUNT' type='string-select' scope='raster' "
624 : "description='Number of raster bands (only for Byte data type)' "
625 : "default='AUTO'>"
626 : " <Value>AUTO</Value>"
627 : " <Value>1</Value>"
628 : " <Value>2</Value>"
629 : " <Value>3</Value>"
630 : " <Value>4</Value>"
631 : " </Option>"
632 : " <Option name='MINX' type='float' scope='raster' "
633 : "description='Minimum X of area of interest'/>"
634 : " <Option name='MINY' type='float' scope='raster' "
635 : "description='Minimum Y of area of interest'/>"
636 : " <Option name='MAXX' type='float' scope='raster' "
637 : "description='Maximum X of area of interest'/>"
638 : " <Option name='MAXY' type='float' scope='raster' "
639 : "description='Maximum Y of area of interest'/>"
640 : " <Option name='USE_TILE_EXTENT' type='boolean' scope='raster' "
641 : "description='Use tile extent of content to determine area of "
642 : "interest' default='NO'/>"
643 : " <Option name='WHERE' type='string' scope='raster' description='SQL "
644 : "WHERE clause to be appended to tile requests'/>" COMPRESSION_OPTIONS
645 : " <Option name='PRELUDE_STATEMENTS' type='string' "
646 : "scope='raster,vector' description='SQL statement(s) to send on the "
647 : "SQLite connection before any other ones'/>"
648 : " <Option name='NOLOCK' type='boolean' description='Whether the "
649 : "database should be opened in nolock mode'/>"
650 : " <Option name='IMMUTABLE' type='boolean' description='Whether the "
651 : "database should be opened in immutable mode'/>"
652 1293 : "</OpenOptionList>");
653 :
654 1293 : poDriver->SetMetadataItem(
655 : GDAL_DS_LAYER_CREATIONOPTIONLIST,
656 : "<LayerCreationOptionList>"
657 : " <Option name='LAUNDER' type='boolean' description='Whether layer "
658 : "and field names will be laundered.' default='NO'/>"
659 : " <Option name='GEOMETRY_NAME' type='string' description='Name of "
660 : "geometry column.' default='geom' deprecated_alias='GEOMETRY_COLUMN'/>"
661 : " <Option name='GEOMETRY_NULLABLE' type='boolean' "
662 : "description='Whether the values of the geometry column can be NULL' "
663 : "default='YES'/>"
664 : " <Option name='SRID' type='integer' description='Forced srs_id of "
665 : "the "
666 : "entry in the gpkg_spatial_ref_sys table to point to'/>"
667 : " <Option name='DISCARD_COORD_LSB' type='boolean' "
668 : "description='Whether the geometry coordinate precision should be used "
669 : "to set to zero non-significant least-significant bits of geometries. "
670 : "Helps when further compression is used' default='NO'/>"
671 : " <Option name='UNDO_DISCARD_COORD_LSB_ON_READING' type='boolean' "
672 : "description='Whether to ask GDAL to take into coordinate precision to "
673 : "undo the effects of DISCARD_COORD_LSB' default='NO'/>"
674 : " <Option name='FID' type='string' description='Name of the FID "
675 : "column to create' default='fid'/>"
676 : " <Option name='OVERWRITE' type='boolean' description='Whether to "
677 : "overwrite an existing table with the layer name to be created' "
678 : "default='NO'/>"
679 : " <Option name='PRECISION' type='boolean' description='Whether text "
680 : "fields created should keep the width' default='YES'/>"
681 : " <Option name='TRUNCATE_FIELDS' type='boolean' description='Whether "
682 : "to truncate text content that exceeds maximum width' default='NO'/>"
683 : " <Option name='SPATIAL_INDEX' type='boolean' description='Whether to "
684 : "create a spatial index' default='YES'/>"
685 : " <Option name='IDENTIFIER' type='string' description='Identifier of "
686 : "the layer, as put in the contents table'/>"
687 : " <Option name='DESCRIPTION' type='string' description='Description "
688 : "of the layer, as put in the contents table'/>"
689 : " <Option name='ASPATIAL_VARIANT' type='string-select' "
690 : "description='How to register non spatial tables' "
691 : "default='GPKG_ATTRIBUTES'>"
692 : " <Value>GPKG_ATTRIBUTES</Value>"
693 : " <Value>NOT_REGISTERED</Value>"
694 : " </Option>"
695 : " <Option name='DATETIME_PRECISION' type='string-select' "
696 : "description='Number of components of datetime fields' "
697 : "default='AUTO'>"
698 : " <Value>AUTO</Value>"
699 : " <Value>MILLISECOND</Value>"
700 : " <Value>SECOND</Value>"
701 : " <Value>MINUTE</Value>"
702 : " </Option>"
703 1293 : "</LayerCreationOptionList>");
704 :
705 1293 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATATYPES,
706 : "Integer Integer64 Real String Date DateTime "
707 1293 : "Binary");
708 1293 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATASUBTYPES,
709 1293 : "Boolean Int16 Float32 JSON");
710 1293 : poDriver->SetMetadataItem(GDAL_DMD_CREATION_FIELD_DEFN_FLAGS,
711 : "WidthPrecision Nullable Default Unique "
712 1293 : "Comment AlternativeName Domain");
713 :
714 1293 : poDriver->SetMetadataItem(GDAL_DMD_ALTER_FIELD_DEFN_FLAGS,
715 : "Name Type WidthPrecision Nullable Default "
716 1293 : "Unique Domain AlternativeName Comment");
717 :
718 1293 : poDriver->SetMetadataItem(GDAL_DCAP_NOTNULL_FIELDS, "YES");
719 1293 : poDriver->SetMetadataItem(GDAL_DCAP_DEFAULT_FIELDS, "YES");
720 1293 : poDriver->SetMetadataItem(GDAL_DCAP_UNIQUE_FIELDS, "YES");
721 1293 : poDriver->SetMetadataItem(GDAL_DCAP_NOTNULL_GEOMFIELDS, "YES");
722 1293 : poDriver->SetMetadataItem(GDAL_DCAP_MULTIPLE_VECTOR_LAYERS, "YES");
723 1293 : poDriver->SetMetadataItem(GDAL_DCAP_FIELD_DOMAINS, "YES");
724 1293 : poDriver->SetMetadataItem(GDAL_DCAP_RELATIONSHIPS, "YES");
725 1293 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE_RELATIONSHIP, "YES");
726 1293 : poDriver->SetMetadataItem(GDAL_DCAP_DELETE_RELATIONSHIP, "YES");
727 1293 : poDriver->SetMetadataItem(GDAL_DCAP_UPDATE_RELATIONSHIP, "YES");
728 1293 : poDriver->SetMetadataItem(GDAL_DCAP_FLUSHCACHE_CONSISTENT_STATE, "YES");
729 :
730 1293 : poDriver->SetMetadataItem(GDAL_DMD_RELATIONSHIP_FLAGS,
731 1293 : "ManyToMany Association");
732 :
733 1293 : poDriver->SetMetadataItem(GDAL_DCAP_RENAME_LAYERS, "YES");
734 1293 : poDriver->SetMetadataItem(GDAL_DMD_CREATION_FIELD_DOMAIN_TYPES,
735 1293 : "Coded Range Glob");
736 :
737 1293 : poDriver->SetMetadataItem(GDAL_DMD_ALTER_GEOM_FIELD_DEFN_FLAGS,
738 1293 : "Name SRS CoordinateEpoch");
739 :
740 1293 : poDriver->SetMetadataItem(
741 : GDAL_DMD_RELATIONSHIP_RELATED_TABLE_TYPES,
742 1293 : "features media simple_attributes attributes tiles");
743 :
744 1293 : poDriver->SetMetadataItem(GDAL_DCAP_HONOR_GEOM_COORDINATE_PRECISION, "YES");
745 :
746 : #ifdef ENABLE_SQL_GPKG_FORMAT
747 1293 : poDriver->SetMetadataItem("ENABLE_SQL_GPKG_FORMAT", "YES");
748 : #endif
749 : #ifdef SQLITE_HAS_COLUMN_METADATA
750 1293 : poDriver->SetMetadataItem("SQLITE_HAS_COLUMN_METADATA", "YES");
751 : #endif
752 :
753 1293 : poDriver->pfnOpen = OGRGeoPackageDriverOpen;
754 1293 : poDriver->pfnIdentify = OGRGeoPackageDriverIdentify;
755 1293 : poDriver->pfnCreate = OGRGeoPackageDriverCreate;
756 1293 : poDriver->pfnCreateCopy = GDALGeoPackageDataset::CreateCopy;
757 1293 : poDriver->pfnDelete = OGRGeoPackageDriverDelete;
758 1293 : poDriver->pfnGetSubdatasetInfoFunc = OGRGeoPackageDriverGetSubdatasetInfo;
759 :
760 1293 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
761 :
762 1293 : GetGDALDriverManager()->RegisterDriver(poDriver);
763 : }
|