LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gpkg - ogrgeopackagedriver.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 253 257 98.4 %
Date: 2024-11-21 22:18:42 Functions: 14 14 100.0 %

          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             : }

Generated by: LCOV version 1.14