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