LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gpkg - ogrgeopackagedriver.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 356 372 95.7 %
Date: 2026-04-09 18:10:46 Functions: 19 19 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 "gdalpython.h"
      14             : #include "gdalsubdatasetinfo.h"
      15             : #include "ogr_geopackage.h"
      16             : 
      17             : #include "tilematrixset.hpp"
      18             : #include "gdalalgorithm.h"
      19             : 
      20             : #include <cctype>
      21             : #include <mutex>
      22             : 
      23             : using namespace GDALPy;
      24             : 
      25             : // g++ -g -Wall -fPIC -shared -o ogr_geopackage.so -Iport -Igcore -Iogr
      26             : // -Iogr/ogrsf_frmts -Iogr/ogrsf_frmts/gpkg ogr/ogrsf_frmts/gpkg/*.c* -L. -lgdal
      27             : 
      28         803 : static inline bool ENDS_WITH_CI(const char *a, const char *b)
      29             : {
      30         803 :     return strlen(a) >= strlen(b) && EQUAL(a + strlen(a) - strlen(b), b);
      31             : }
      32             : 
      33             : /************************************************************************/
      34             : /*                    OGRGeoPackageDriverIdentify()                     */
      35             : /************************************************************************/
      36             : 
      37       65431 : static int OGRGeoPackageDriverIdentify(GDALOpenInfo *poOpenInfo,
      38             :                                        std::string &osFilenameInGpkgZip,
      39             :                                        bool bEmitWarning)
      40             : {
      41       65431 :     if (STARTS_WITH_CI(poOpenInfo->pszFilename, "GPKG:"))
      42         520 :         return TRUE;
      43             : 
      44             : #ifdef ENABLE_SQL_GPKG_FORMAT
      45       64911 :     if (poOpenInfo->pabyHeader &&
      46        7207 :         STARTS_WITH(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
      47             :                     "-- SQL GPKG"))
      48             :     {
      49          10 :         return TRUE;
      50             :     }
      51             : #endif
      52             : 
      53             :     // Try to recognize "foo.gpkg.zip"
      54       64901 :     const size_t nFilenameLen = strlen(poOpenInfo->pszFilename);
      55       64901 :     if ((poOpenInfo->nOpenFlags & GDAL_OF_UPDATE) == 0 &&
      56       62711 :         nFilenameLen > strlen(".gpkg.zip") &&
      57       62711 :         !STARTS_WITH(poOpenInfo->pszFilename, "/vsizip/") &&
      58       62669 :         EQUAL(poOpenInfo->pszFilename + nFilenameLen - strlen(".gpkg.zip"),
      59             :               ".gpkg.zip"))
      60             :     {
      61           8 :         int nCountGpkg = 0;
      62             :         const CPLStringList aosFiles(VSIReadDirEx(
      63          24 :             (std::string("/vsizip/") + poOpenInfo->pszFilename).c_str(), 1000));
      64          12 :         for (int i = 0; i < aosFiles.size(); ++i)
      65             :         {
      66           4 :             const size_t nLen = strlen(aosFiles[i]);
      67           8 :             if (nLen > strlen(".gpkg") &&
      68           4 :                 EQUAL(aosFiles[i] + nLen - strlen(".gpkg"), ".gpkg"))
      69             :             {
      70           4 :                 osFilenameInGpkgZip = aosFiles[i];
      71           4 :                 nCountGpkg++;
      72           4 :                 if (nCountGpkg == 2)
      73           0 :                     return FALSE;
      74             :             }
      75             :         }
      76           8 :         return nCountGpkg == 1;
      77             :     }
      78             : 
      79       64893 :     if (poOpenInfo->nHeaderBytes < 100 || poOpenInfo->pabyHeader == nullptr ||
      80        6773 :         !STARTS_WITH(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
      81             :                      "SQLite format 3"))
      82             :     {
      83       62362 :         return FALSE;
      84             :     }
      85             : 
      86             :     /* Requirement 3: File name has to end in "gpkg" */
      87             :     /* http://opengis.github.io/geopackage/#_file_extension_name */
      88             :     /* But be tolerant, if the GPKG application id is found, because some */
      89             :     /* producers don't necessarily honour that requirement (#6396) */
      90        2531 :     const char *pszExt = poOpenInfo->osExtension.c_str();
      91        2531 :     const bool bIsRecognizedExtension =
      92        2531 :         EQUAL(pszExt, "GPKG") || EQUAL(pszExt, "GPKX");
      93             : 
      94             :     /* Requirement 2: application id */
      95             :     /* http://opengis.github.io/geopackage/#_file_format */
      96             :     /* Be tolerant since some datasets don't actually follow that requirement */
      97             :     GUInt32 nApplicationId;
      98        2531 :     memcpy(&nApplicationId, poOpenInfo->pabyHeader + knApplicationIdPos, 4);
      99        2531 :     nApplicationId = CPL_MSBWORD32(nApplicationId);
     100             :     GUInt32 nUserVersion;
     101        2531 :     memcpy(&nUserVersion, poOpenInfo->pabyHeader + knUserVersionPos, 4);
     102        2531 :     nUserVersion = CPL_MSBWORD32(nUserVersion);
     103        2531 :     if (nApplicationId != GP10_APPLICATION_ID &&
     104        2512 :         nApplicationId != GP11_APPLICATION_ID &&
     105        2507 :         nApplicationId != GPKG_APPLICATION_ID)
     106             :     {
     107             : #ifdef DEBUG
     108         243 :         if (EQUAL(CPLGetFilename(poOpenInfo->pszFilename), ".cur_input"))
     109             :         {
     110           1 :             return FALSE;
     111             :         }
     112             : #endif
     113         242 :         if (!bIsRecognizedExtension)
     114         238 :             return FALSE;
     115             : 
     116           4 :         if (bEmitWarning)
     117             :         {
     118             :             GByte abySignature[4 + 1];
     119           2 :             memcpy(abySignature, poOpenInfo->pabyHeader + knApplicationIdPos,
     120             :                    4);
     121           2 :             abySignature[4] = '\0';
     122             : 
     123             :             /* Is this a GPxx version ? */
     124           2 :             const bool bWarn = CPLTestBool(CPLGetConfigOption(
     125             :                 "GPKG_WARN_UNRECOGNIZED_APPLICATION_ID", "YES"));
     126           2 :             if (bWarn)
     127             :             {
     128           1 :                 CPLError(CE_Warning, CPLE_AppDefined,
     129             :                          "GPKG: bad application_id=0x%02X%02X%02X%02X on '%s'",
     130           1 :                          abySignature[0], abySignature[1], abySignature[2],
     131           1 :                          abySignature[3], poOpenInfo->pszFilename);
     132             :             }
     133             :             else
     134             :             {
     135           1 :                 CPLDebug("GPKG",
     136             :                          "bad application_id=0x%02X%02X%02X%02X on '%s'",
     137           1 :                          abySignature[0], abySignature[1], abySignature[2],
     138           1 :                          abySignature[3], poOpenInfo->pszFilename);
     139             :             }
     140           4 :         }
     141             :     }
     142        2288 :     else if (nApplicationId == GPKG_APPLICATION_ID &&
     143             :              // Accept any 102XX version
     144        2264 :              !((nUserVersion >= GPKG_1_2_VERSION &&
     145        2259 :                 nUserVersion < GPKG_1_2_VERSION + 99) ||
     146             :                // Accept any 103XX version
     147        2125 :                (nUserVersion >= GPKG_1_3_VERSION &&
     148        2120 :                 nUserVersion < GPKG_1_3_VERSION + 99) ||
     149             :                // Accept any 104XX version
     150        2121 :                (nUserVersion >= GPKG_1_4_VERSION &&
     151        2116 :                 nUserVersion < GPKG_1_4_VERSION + 99)))
     152             :     {
     153             : #ifdef DEBUG
     154           9 :         if (EQUAL(CPLGetFilename(poOpenInfo->pszFilename), ".cur_input"))
     155             :         {
     156           1 :             return FALSE;
     157             :         }
     158             : #endif
     159           8 :         if (!bIsRecognizedExtension)
     160           0 :             return FALSE;
     161             : 
     162           8 :         if (bEmitWarning)
     163             :         {
     164             :             GByte abySignature[4 + 1];
     165           4 :             memcpy(abySignature, poOpenInfo->pabyHeader + knUserVersionPos, 4);
     166           4 :             abySignature[4] = '\0';
     167             : 
     168           4 :             const bool bWarn = CPLTestBool(CPLGetConfigOption(
     169             :                 "GPKG_WARN_UNRECOGNIZED_APPLICATION_ID", "YES"));
     170           4 :             if (bWarn)
     171             :             {
     172           2 :                 if (nUserVersion > GPKG_1_4_VERSION)
     173             :                 {
     174           1 :                     CPLError(CE_Warning, CPLE_AppDefined,
     175             :                              "This version of GeoPackage "
     176             :                              "user_version=0x%02X%02X%02X%02X "
     177             :                              "(%u, v%d.%d.%d) on '%s' may only be "
     178             :                              "partially supported",
     179           1 :                              abySignature[0], abySignature[1], abySignature[2],
     180           1 :                              abySignature[3], nUserVersion,
     181           1 :                              nUserVersion / 10000, (nUserVersion % 10000) / 100,
     182             :                              nUserVersion % 100, poOpenInfo->pszFilename);
     183             :                 }
     184             :                 else
     185             :                 {
     186           1 :                     CPLError(CE_Warning, CPLE_AppDefined,
     187             :                              "GPKG: unrecognized user_version="
     188             :                              "0x%02X%02X%02X%02X (%u) on '%s'",
     189           1 :                              abySignature[0], abySignature[1], abySignature[2],
     190           1 :                              abySignature[3], nUserVersion,
     191             :                              poOpenInfo->pszFilename);
     192             :                 }
     193             :             }
     194             :             else
     195             :             {
     196           2 :                 if (nUserVersion > GPKG_1_4_VERSION)
     197             :                 {
     198           1 :                     CPLDebug("GPKG",
     199             :                              "This version of GeoPackage "
     200             :                              "user_version=0x%02X%02X%02X%02X "
     201             :                              "(%u, v%d.%d.%d) on '%s' may only be "
     202             :                              "partially supported",
     203           1 :                              abySignature[0], abySignature[1], abySignature[2],
     204           1 :                              abySignature[3], nUserVersion,
     205           1 :                              nUserVersion / 10000, (nUserVersion % 10000) / 100,
     206             :                              nUserVersion % 100, poOpenInfo->pszFilename);
     207             :                 }
     208             :                 else
     209             :                 {
     210           1 :                     CPLDebug("GPKG",
     211             :                              "unrecognized user_version=0x%02X%02X%02X%02X"
     212             :                              "(%u) on '%s'",
     213           1 :                              abySignature[0], abySignature[1], abySignature[2],
     214           1 :                              abySignature[3], nUserVersion,
     215             :                              poOpenInfo->pszFilename);
     216             :                 }
     217             :             }
     218           8 :         }
     219             :     }
     220        4558 :     else if (!bIsRecognizedExtension
     221             : #ifdef DEBUG
     222          34 :              && !EQUAL(CPLGetFilename(poOpenInfo->pszFilename), ".cur_input")
     223             : #endif
     224          34 :              && !(STARTS_WITH(poOpenInfo->pszFilename, "/vsizip/") &&
     225        2313 :                   poOpenInfo->IsExtensionEqualToCI("zip")) &&
     226          32 :              !STARTS_WITH(poOpenInfo->pszFilename, "/vsigzip/"))
     227             :     {
     228          32 :         if (bEmitWarning)
     229             :         {
     230          16 :             CPLError(CE_Warning, CPLE_AppDefined,
     231             :                      "File %s has GPKG application_id, but non conformant file "
     232             :                      "extension",
     233             :                      poOpenInfo->pszFilename);
     234             :         }
     235             :     }
     236             : 
     237        3094 :     if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) != 0 &&
     238         803 :         ENDS_WITH_CI(poOpenInfo->pszFilename, ".gti.gpkg"))
     239             :     {
     240             :         // Most likely handled by GTI driver, but we can't be sure
     241          33 :         return GDAL_IDENTIFY_UNKNOWN;
     242             :     }
     243             : 
     244        2258 :     return TRUE;
     245             : }
     246             : 
     247       64081 : static int OGRGeoPackageDriverIdentify(GDALOpenInfo *poOpenInfo)
     248             : {
     249      128162 :     std::string osIgnored;
     250      128162 :     return OGRGeoPackageDriverIdentify(poOpenInfo, osIgnored, false);
     251             : }
     252             : 
     253             : /************************************************************************/
     254             : /*                OGRGeoPackageDriverGetSubdatasetInfo()                */
     255             : /************************************************************************/
     256             : 
     257             : struct OGRGeoPackageDriverSubdatasetInfo final : public GDALSubdatasetInfo
     258             : {
     259             :   public:
     260          11 :     explicit OGRGeoPackageDriverSubdatasetInfo(const std::string &fileName)
     261          11 :         : GDALSubdatasetInfo(fileName)
     262             :     {
     263          11 :     }
     264             : 
     265             :     // GDALSubdatasetInfo interface
     266             :   private:
     267             :     void parseFileName() override;
     268             : };
     269             : 
     270          11 : void OGRGeoPackageDriverSubdatasetInfo::parseFileName()
     271             : {
     272          11 :     if (!STARTS_WITH_CI(m_fileName.c_str(), "GPKG:"))
     273             :     {
     274           1 :         return;
     275             :     }
     276             : 
     277          11 :     CPLStringList aosParts{CSLTokenizeString2(m_fileName.c_str(), ":", 0)};
     278          11 :     const int iPartsCount{CSLCount(aosParts)};
     279             : 
     280          11 :     if (iPartsCount == 3 || iPartsCount == 4)
     281             :     {
     282             : 
     283           9 :         m_driverPrefixComponent = aosParts[0];
     284             : 
     285           9 :         int subdatasetIndex{2};
     286             :         const bool hasDriveLetter{
     287          14 :             strlen(aosParts[1]) == 1 &&
     288           5 :             std::isalpha(static_cast<unsigned char>(aosParts[1][0]))};
     289             : 
     290             :         // Check for drive letter
     291           9 :         if (iPartsCount == 4)
     292             :         {
     293             :             // Invalid
     294           4 :             if (!hasDriveLetter)
     295             :             {
     296           0 :                 return;
     297             :             }
     298           4 :             m_pathComponent = aosParts[1];
     299           4 :             m_pathComponent.append(":");
     300           4 :             m_pathComponent.append(aosParts[2]);
     301           4 :             subdatasetIndex++;
     302             :         }
     303             :         else  // count is 3
     304             :         {
     305           5 :             if (hasDriveLetter)
     306             :             {
     307           1 :                 return;
     308             :             }
     309           4 :             m_pathComponent = aosParts[1];
     310             :         }
     311             : 
     312           8 :         m_subdatasetComponent = aosParts[subdatasetIndex];
     313             :     }
     314             : }
     315             : 
     316             : static GDALSubdatasetInfo *
     317        2731 : OGRGeoPackageDriverGetSubdatasetInfo(const char *pszFileName)
     318             : {
     319        2731 :     if (STARTS_WITH_CI(pszFileName, "GPKG:"))
     320             :     {
     321             :         std::unique_ptr<GDALSubdatasetInfo> info =
     322          11 :             std::make_unique<OGRGeoPackageDriverSubdatasetInfo>(pszFileName);
     323          30 :         if (!info->GetSubdatasetComponent().empty() &&
     324          19 :             !info->GetPathComponent().empty())
     325             :         {
     326           8 :             return info.release();
     327             :         }
     328             :     }
     329        2723 :     return nullptr;
     330             : }
     331             : 
     332             : /************************************************************************/
     333             : /*                                Open()                                */
     334             : /************************************************************************/
     335             : 
     336        1350 : static GDALDataset *OGRGeoPackageDriverOpen(GDALOpenInfo *poOpenInfo)
     337             : {
     338        2700 :     std::string osFilenameInGpkgZip;
     339        1350 :     if (OGRGeoPackageDriverIdentify(poOpenInfo, osFilenameInGpkgZip, true) ==
     340             :         GDAL_IDENTIFY_FALSE)
     341           0 :         return nullptr;
     342             : 
     343        1350 :     GDALGeoPackageDataset *poDS = new GDALGeoPackageDataset();
     344             : 
     345        1350 :     if (!poDS->Open(poOpenInfo, osFilenameInGpkgZip))
     346             :     {
     347          18 :         delete poDS;
     348          18 :         poDS = nullptr;
     349             :     }
     350             : 
     351        1350 :     return poDS;
     352             : }
     353             : 
     354             : /************************************************************************/
     355             : /*                               Create()                               */
     356             : /************************************************************************/
     357             : 
     358        1045 : static GDALDataset *OGRGeoPackageDriverCreate(const char *pszFilename,
     359             :                                               int nXSize, int nYSize,
     360             :                                               int nBands, GDALDataType eDT,
     361             :                                               CSLConstList papszOptions)
     362             : {
     363        1045 :     if (strcmp(pszFilename, ":memory:") != 0)
     364             :     {
     365        1041 :         const size_t nFilenameLen = strlen(pszFilename);
     366        1041 :         if (nFilenameLen > strlen(".gpkg.zip") &&
     367        1040 :             !STARTS_WITH(pszFilename, "/vsizip/") &&
     368        1040 :             EQUAL(pszFilename + nFilenameLen - strlen(".gpkg.zip"),
     369             :                   ".gpkg.zip"))
     370             :         {
     371             :             // do nothing
     372             :         }
     373             :         else
     374             :         {
     375        2078 :             const std::string osExt = CPLGetExtensionSafe(pszFilename);
     376             :             const bool bIsRecognizedExtension =
     377        1039 :                 EQUAL(osExt.c_str(), "GPKG") || EQUAL(osExt.c_str(), "GPKX");
     378        1039 :             if (!bIsRecognizedExtension)
     379             :             {
     380          36 :                 CPLError(
     381             :                     CE_Warning, CPLE_AppDefined,
     382             :                     "The filename extension should be 'gpkg' instead of '%s' "
     383             :                     "to conform to the GPKG specification.",
     384             :                     osExt.c_str());
     385             :             }
     386             :         }
     387             :     }
     388             : 
     389        1045 :     GDALGeoPackageDataset *poDS = new GDALGeoPackageDataset();
     390             : 
     391        1045 :     if (!poDS->Create(pszFilename, nXSize, nYSize, nBands, eDT, papszOptions))
     392             :     {
     393          40 :         delete poDS;
     394          40 :         poDS = nullptr;
     395             :     }
     396             : 
     397        1045 :     return poDS;
     398             : }
     399             : 
     400             : /************************************************************************/
     401             : /*                               Delete()                               */
     402             : /************************************************************************/
     403             : 
     404         132 : static CPLErr OGRGeoPackageDriverDelete(const char *pszFilename)
     405             : 
     406             : {
     407         264 :     std::string osAuxXml(pszFilename);
     408         132 :     osAuxXml += ".aux.xml";
     409             :     VSIStatBufL sStat;
     410         132 :     if (VSIStatL(osAuxXml.c_str(), &sStat) == 0)
     411           3 :         CPL_IGNORE_RET_VAL(VSIUnlink(osAuxXml.c_str()));
     412             : 
     413         132 :     if (VSIUnlink(pszFilename) == 0)
     414         121 :         return CE_None;
     415             :     else
     416          11 :         return CE_Failure;
     417             : }
     418             : 
     419             : /************************************************************************/
     420             : /*                            GDALGPKGDriver                            */
     421             : /************************************************************************/
     422             : 
     423             : class GDALGPKGDriver final : public GDALDriver
     424             : {
     425             :     std::recursive_mutex m_oMutex{};
     426             :     bool m_bInitialized = false;
     427             : 
     428             :     void InitializeCreationOptionList();
     429             : 
     430             :   public:
     431        1800 :     GDALGPKGDriver() = default;
     432             : 
     433             :     const char *GetMetadataItem(const char *pszName,
     434             :                                 const char *pszDomain) override;
     435             : 
     436         597 :     CSLConstList GetMetadata(const char *pszDomain) override
     437             :     {
     438        1194 :         std::lock_guard oLock(m_oMutex);
     439         597 :         InitializeCreationOptionList();
     440        1194 :         return GDALDriver::GetMetadata(pszDomain);
     441             :     }
     442             : };
     443             : 
     444       56688 : const char *GDALGPKGDriver::GetMetadataItem(const char *pszName,
     445             :                                             const char *pszDomain)
     446             : {
     447      113376 :     std::lock_guard oLock(m_oMutex);
     448       56688 :     if (EQUAL(pszName, GDAL_DMD_CREATIONOPTIONLIST))
     449             :     {
     450        1564 :         InitializeCreationOptionList();
     451             :     }
     452      113376 :     return GDALDriver::GetMetadataItem(pszName, pszDomain);
     453             : }
     454             : 
     455             : #define COMPRESSION_OPTIONS                                                    \
     456             :     "  <Option name='TILE_FORMAT' type='string-select' scope='raster' "        \
     457             :     "description='Format to use to create tiles' default='AUTO'>"              \
     458             :     "    <Value>AUTO</Value>"                                                  \
     459             :     "    <Value>PNG_JPEG</Value>"                                              \
     460             :     "    <Value>PNG</Value>"                                                   \
     461             :     "    <Value>PNG8</Value>"                                                  \
     462             :     "    <Value>JPEG</Value>"                                                  \
     463             :     "    <Value>WEBP</Value>"                                                  \
     464             :     "    <Value>TIFF</Value>"                                                  \
     465             :     "  </Option>"                                                              \
     466             :     "  <Option name='QUALITY' type='int' min='1' max='100' scope='raster' "    \
     467             :     "description='Quality for JPEG and WEBP tiles' default='75'/>"             \
     468             :     "  <Option name='ZLEVEL' type='int' min='1' max='9' scope='raster' "       \
     469             :     "description='DEFLATE compression level for PNG tiles' default='6'/>"      \
     470             :     "  <Option name='DITHER' type='boolean' scope='raster' "                   \
     471             :     "description='Whether to apply Floyd-Steinberg dithering (for "            \
     472             :     "TILE_FORMAT=PNG8)' default='NO'/>"
     473             : 
     474        2161 : void GDALGPKGDriver::InitializeCreationOptionList()
     475             : {
     476        2161 :     if (m_bInitialized)
     477        1912 :         return;
     478         249 :     m_bInitialized = true;
     479             : 
     480         249 :     const char *pszCOBegin =
     481             :         "<CreationOptionList>"
     482             :         "  <Option name='RASTER_TABLE' type='string' scope='raster' "
     483             :         "description='Name of tile user table'/>"
     484             :         "  <Option name='APPEND_SUBDATASET' type='boolean' scope='raster' "
     485             :         "description='Set to YES to add a new tile user table to an existing "
     486             :         "GeoPackage instead of replacing it' default='NO'/>"
     487             :         "  <Option name='RASTER_IDENTIFIER' type='string' scope='raster' "
     488             :         "description='Human-readable identifier (e.g. short name)'/>"
     489             :         "  <Option name='RASTER_DESCRIPTION' type='string' scope='raster' "
     490             :         "description='Human-readable description'/>"
     491             :         "  <Option name='BLOCKSIZE' type='int' scope='raster' "
     492             :         "description='Block size in pixels' default='256' max='4096'/>"
     493             :         "  <Option name='BLOCKXSIZE' type='int' scope='raster' "
     494             :         "description='Block width in pixels' default='256' max='4096'/>"
     495             :         "  <Option name='BLOCKYSIZE' type='int' scope='raster' "
     496             :         "description='Block height in pixels' default='256' "
     497             :         "max='4096'/>" COMPRESSION_OPTIONS
     498             :         "  <Option name='TILING_SCHEME' type='string' scope='raster' "
     499             :         "description='Which tiling scheme to use: pre-defined value or custom "
     500             :         "inline/outline JSON definition' default='CUSTOM'>"
     501             :         "    <Value>CUSTOM</Value>"
     502             :         "    <Value>GoogleCRS84Quad</Value>"
     503             :         "    <Value>PseudoTMS_GlobalGeodetic</Value>"
     504             :         "    <Value>PseudoTMS_GlobalMercator</Value>";
     505             : 
     506         249 :     const char *pszCOEnd =
     507             :         "  </Option>"
     508             :         "  <Option name='ZOOM_LEVEL' type='integer' scope='raster' "
     509             :         "description='Zoom level of full resolution. Only "
     510             :         "used for TILING_SCHEME != CUSTOM' min='0' max='30'/>"
     511             :         "  <Option name='ZOOM_LEVEL_STRATEGY' type='string-select' "
     512             :         "scope='raster' description='Strategy to determine zoom level. Only "
     513             :         "used for TILING_SCHEME != CUSTOM' default='AUTO'>"
     514             :         "    <Value>AUTO</Value>"
     515             :         "    <Value>LOWER</Value>"
     516             :         "    <Value>UPPER</Value>"
     517             :         "  </Option>"
     518             :         "  <Option name='RESAMPLING' type='string-select' scope='raster' "
     519             :         "description='Resampling algorithm. Only used for TILING_SCHEME != "
     520             :         "CUSTOM' default='BILINEAR'>"
     521             :         "    <Value>NEAREST</Value>"
     522             :         "    <Value>BILINEAR</Value>"
     523             :         "    <Value>CUBIC</Value>"
     524             :         "    <Value>CUBICSPLINE</Value>"
     525             :         "    <Value>LANCZOS</Value>"
     526             :         "    <Value>MODE</Value>"
     527             :         "    <Value>AVERAGE</Value>"
     528             :         "  </Option>"
     529             :         "  <Option name='PRECISION' type='float' scope='raster' "
     530             :         "description='Smallest significant value. Only used for tiled gridded "
     531             :         "coverage datasets' default='1'/>"
     532             :         "  <Option name='UOM' type='string' scope='raster' description='Unit "
     533             :         "of Measurement. Only used for tiled gridded coverage datasets' />"
     534             :         "  <Option name='FIELD_NAME' type='string' scope='raster' "
     535             :         "description='Field name. Only used for tiled gridded coverage "
     536             :         "datasets' default='Height'/>"
     537             :         "  <Option name='QUANTITY_DEFINITION' type='string' scope='raster' "
     538             :         "description='Description of the field. Only used for tiled gridded "
     539             :         "coverage datasets' default='Height'/>"
     540             :         "  <Option name='GRID_CELL_ENCODING' type='string-select' "
     541             :         "scope='raster' description='Grid cell encoding. Only used for tiled "
     542             :         "gridded coverage datasets' default='grid-value-is-center'>"
     543             :         "     <Value>grid-value-is-center</Value>"
     544             :         "     <Value>grid-value-is-area</Value>"
     545             :         "     <Value>grid-value-is-corner</Value>"
     546             :         "  </Option>"
     547             :         "  <Option name='VERSION' type='string-select' description='Set "
     548             :         "GeoPackage version (for application_id and user_version fields)' "
     549             :         "default='AUTO'>"
     550             :         "     <Value>AUTO</Value>"
     551             :         "     <Value>1.0</Value>"
     552             :         "     <Value>1.1</Value>"
     553             :         "     <Value>1.2</Value>"
     554             :         "     <Value>1.3</Value>"
     555             :         "     <Value>1.4</Value>"
     556             :         "  </Option>"
     557             :         "  <Option name='DATETIME_FORMAT' type='string-select' "
     558             :         "description='How to encode DateTime not in UTC' default='WITH_TZ'>"
     559             :         "     <Value>WITH_TZ</Value>"
     560             :         "     <Value>UTC</Value>"
     561             :         "  </Option>"
     562             : #ifdef ENABLE_GPKG_OGR_CONTENTS
     563             :         "  <Option name='ADD_GPKG_OGR_CONTENTS' type='boolean' "
     564             :         "description='Whether to add a gpkg_ogr_contents table to keep feature "
     565             :         "count' default='YES'/>"
     566             : #endif
     567             :         "  <Option name='CRS_WKT_EXTENSION' type='boolean' "
     568             :         "description='Whether to create the database with the crs_wkt "
     569             :         "extension'/>"
     570             :         "  <Option name='METADATA_TABLES' type='boolean' "
     571             :         "description='Whether to create the metadata related system tables'/>"
     572             :         "</CreationOptionList>";
     573             : 
     574         498 :     std::string osOptions(pszCOBegin);
     575         498 :     const auto tmsList = gdal::TileMatrixSet::listPredefinedTileMatrixSets();
     576        2490 :     for (const auto &tmsName : tmsList)
     577             :     {
     578        4482 :         const auto poTM = gdal::TileMatrixSet::parse(tmsName.c_str());
     579        4482 :         if (poTM && poTM->haveAllLevelsSameTopLeft() &&
     580        2241 :             poTM->haveAllLevelsSameTileSize() &&
     581        6225 :             poTM->hasOnlyPowerOfTwoVaryingScales() &&
     582        1743 :             !poTM->hasVariableMatrixWidth())
     583             :         {
     584        1743 :             osOptions += "    <Value>";
     585        1743 :             osOptions += tmsName;
     586        1743 :             osOptions += "</Value>";
     587             :         }
     588             :     }
     589         249 :     osOptions += pszCOEnd;
     590             : 
     591         249 :     SetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST, osOptions.c_str());
     592             : }
     593             : 
     594             : /************************************************************************/
     595             : /*                     OGRGeoPackageRepackAlgorithm                     */
     596             : /************************************************************************/
     597             : 
     598             : #ifndef _
     599             : #define _(x) x
     600             : #endif
     601             : 
     602             : class OGRGeoPackageRepackAlgorithm final : public GDALAlgorithm
     603             : {
     604             :   public:
     605         140 :     OGRGeoPackageRepackAlgorithm()
     606         140 :         : GDALAlgorithm("repack", "Repack/vacuum in-place a GeoPackage dataset",
     607         140 :                         "/programs/gdal_driver_gpkg_repack.html")
     608             :     {
     609         140 :         constexpr int type = GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_UPDATE;
     610             :         auto &arg =
     611         280 :             AddArg("dataset", 0, _("GeoPackage dataset"), &m_dataset, type)
     612         140 :                 .SetPositional()
     613         140 :                 .SetRequired();
     614         140 :         SetAutoCompleteFunctionForFilename(arg, type);
     615         140 :     }
     616             : 
     617             :   protected:
     618             :     bool RunImpl(GDALProgressFunc, void *) override;
     619             : 
     620             :   private:
     621             :     GDALArgDatasetValue m_dataset{};
     622             : };
     623             : 
     624           2 : bool OGRGeoPackageRepackAlgorithm::RunImpl(GDALProgressFunc, void *)
     625             : {
     626             :     auto poDS =
     627           2 :         dynamic_cast<GDALGeoPackageDataset *>(m_dataset.GetDatasetRef());
     628           2 :     if (!poDS)
     629             :     {
     630           1 :         ReportError(CE_Failure, CPLE_AppDefined, "%s is not a GeoPackage",
     631           1 :                     m_dataset.GetName().c_str());
     632           1 :         return false;
     633             :     }
     634           1 :     CPLErrorReset();
     635           1 :     delete poDS->ExecuteSQL("VACUUM", nullptr, nullptr);
     636           1 :     return CPLGetLastErrorType() == CE_None;
     637             : }
     638             : 
     639             : /************************************************************************/
     640             : /*                    OGRGeoPackageValidateAlgorithm                    */
     641             : /************************************************************************/
     642             : 
     643             : class OGRGeoPackageValidateAlgorithm final : public GDALAlgorithm
     644             : {
     645             :   public:
     646         142 :     OGRGeoPackageValidateAlgorithm()
     647         142 :         : GDALAlgorithm("validate",
     648             :                         "Validate conformance of a GeoPackage dataset against "
     649             :                         "the GeoPackage specification",
     650         142 :                         "/programs/gdal_driver_gpkg_validate.html")
     651             :     {
     652         142 :         constexpr int type = GDAL_OF_RASTER | GDAL_OF_VECTOR;
     653             :         auto &arg =
     654         284 :             AddArg("dataset", 0, _("GeoPackage dataset"), &m_dataset, type)
     655         142 :                 .SetPositional()
     656         142 :                 .SetRequired();
     657         142 :         SetAutoCompleteFunctionForFilename(arg, type);
     658             : 
     659             :         AddArg("full-check", 0, _("Whether to perform full check"),
     660         284 :                &m_fullCheck)
     661         142 :             .SetDefault(m_fullCheck);
     662             : 
     663         142 :         AddOutputStringArg(&m_output);
     664         142 :         AddArg("verbose", 'v', _("Turn on verbose mode"), &m_verbose);
     665         142 :         AddProgressArg();
     666         142 :     }
     667             : 
     668             :   protected:
     669             :     bool RunImpl(GDALProgressFunc, void *) override;
     670             : 
     671             :   private:
     672             :     GDALArgDatasetValue m_dataset{};
     673             :     bool m_fullCheck = false;
     674             :     bool m_verbose = false;
     675             :     std::string m_output{};
     676             : };
     677             : 
     678             : /************************************************************************/
     679             : /*                              RunImpl()                               */
     680             : /************************************************************************/
     681             : 
     682           4 : bool OGRGeoPackageValidateAlgorithm::RunImpl(GDALProgressFunc, void *)
     683             : {
     684             :     auto poDS =
     685           4 :         dynamic_cast<GDALGeoPackageDataset *>(m_dataset.GetDatasetRef());
     686           4 :     if (!poDS)
     687             :     {
     688           0 :         ReportError(CE_Failure, CPLE_AppDefined, "%s is not a GeoPackage",
     689           0 :                     m_dataset.GetName().c_str());
     690           0 :         return false;
     691             :     }
     692             : 
     693           4 :     if (!GDALPythonInitialize())
     694           0 :         return false;
     695             : 
     696           8 :     GIL_Holder oHolder(false);
     697             : 
     698           8 :     const CPLString osModuleName(CPLSPrintf("gpkg_validate_module_%p", this));
     699             :     PyObject *poCompiledString =
     700           4 :         Py_CompileString("from osgeo_utils.samples.validate_gpkg import "
     701             :                          "main, get_output_string\n",
     702             :                          osModuleName, Py_file_input);
     703           4 :     if (poCompiledString == nullptr || PyErr_Occurred())
     704             :     {
     705           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Couldn't compile code:\n%s",
     706           0 :                  GetPyExceptionString().c_str());
     707           0 :         return false;
     708             :     }
     709             :     PyObject *poModule =
     710           4 :         PyImport_ExecCodeModule(osModuleName, poCompiledString);
     711           4 :     Py_DecRef(poCompiledString);
     712             : 
     713           4 :     if (poModule == nullptr || PyErr_Occurred())
     714             :     {
     715           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s",
     716           0 :                  GetPyExceptionString().c_str());
     717           0 :         return false;
     718             :     }
     719             : 
     720           4 :     PyObject *poMain = PyObject_GetAttrString(poModule, "main");
     721           4 :     CPLAssert(poMain);
     722             :     PyObject *poGetOutput =
     723           4 :         PyObject_GetAttrString(poModule, "get_output_string");
     724           4 :     CPLAssert(poGetOutput);
     725             : 
     726           4 :     Py_DecRef(poModule);
     727             : 
     728          24 :     std::vector<std::string> args{"dummy", "-k", m_dataset.GetName()};
     729           4 :     if (m_quiet)
     730           0 :         args.push_back("-q");
     731           4 :     else if (m_verbose)
     732           1 :         args.push_back("-v");
     733           4 :     if (m_fullCheck)
     734           2 :         args.push_back("--extra");
     735             : 
     736           4 :     PyObject *pyArgv = PyTuple_New(args.size());
     737          19 :     for (size_t i = 0; i < args.size(); ++i)
     738             :     {
     739          15 :         PyTuple_SetItem(pyArgv, i, PyUnicode_FromString(args[i].c_str()));
     740             :     }
     741           4 :     PyObject *pyKwargs = PyDict_New();
     742           4 :     PyDict_SetItemString(pyKwargs, "argv", pyArgv);
     743           4 :     PyDict_SetItemString(pyKwargs, "output_in_string", PyBool_FromLong(true));
     744           4 :     PyObject *pyArgs = PyTuple_New(0);
     745           4 :     PyObject *pRetValue = PyObject_Call(poMain, pyArgs, pyKwargs);
     746           4 :     const auto nRetValue = pRetValue ? PyLong_AsLong(pRetValue) : -1;
     747           4 :     Py_DecRef(pyArgs);
     748           4 :     Py_DecRef(pyKwargs);
     749           4 :     Py_DecRef(pRetValue);
     750             : 
     751           4 :     if (!m_quiet || nRetValue)
     752             :     {
     753           4 :         pyArgs = PyTuple_New(0);
     754           4 :         pyKwargs = PyDict_New();
     755           4 :         PyObject *poMsg = PyObject_Call(poGetOutput, pyArgs, pyKwargs);
     756           4 :         Py_DecRef(pyArgs);
     757           4 :         Py_DecRef(pyKwargs);
     758           4 :         if (poMsg)
     759           4 :             m_output = GetString(poMsg);
     760           4 :         Py_DecRef(poMsg);
     761             :     }
     762             : 
     763           4 :     Py_DecRef(poMain);
     764           4 :     Py_DecRef(poGetOutput);
     765             : 
     766           4 :     if (nRetValue)
     767           1 :         m_output += "\nValidation failed";
     768           3 :     else if (!m_quiet)
     769             :     {
     770           3 :         if (!m_output.empty())
     771           1 :             m_output += '\n';
     772           3 :         m_output += "Validation succeeded";
     773             :     }
     774             : 
     775           4 :     if (nRetValue && !IsCalledFromCommandLine())
     776             :     {
     777           1 :         ReportError(CE_Failure, CPLE_AppDefined, "%s", m_output.c_str());
     778             :     }
     779             : 
     780           4 :     return nRetValue == 0;
     781             : }
     782             : 
     783             : /************************************************************************/
     784             : /*              OGRGeoPackageDriverInstantiateAlgorithm()               */
     785             : /************************************************************************/
     786             : 
     787             : static GDALAlgorithm *
     788         282 : OGRGeoPackageDriverInstantiateAlgorithm(const std::vector<std::string> &aosPath)
     789             : {
     790         282 :     if (aosPath.size() == 1 && aosPath[0] == "repack")
     791             :     {
     792         140 :         return std::make_unique<OGRGeoPackageRepackAlgorithm>().release();
     793             :     }
     794         142 :     else if (aosPath.size() == 1 && aosPath[0] == "validate")
     795             :     {
     796         142 :         return std::make_unique<OGRGeoPackageValidateAlgorithm>().release();
     797             :     }
     798             :     else
     799             :     {
     800           0 :         return nullptr;
     801             :     }
     802             : }
     803             : 
     804             : /************************************************************************/
     805             : /*                       RegisterOGRGeoPackage()                        */
     806             : /************************************************************************/
     807             : 
     808        2063 : void RegisterOGRGeoPackage()
     809             : {
     810        2063 :     if (GDALGetDriverByName("GPKG") != nullptr)
     811         263 :         return;
     812             : 
     813        1800 :     GDALDriver *poDriver = new GDALGPKGDriver();
     814             : 
     815        1800 :     poDriver->SetDescription("GPKG");
     816        1800 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
     817        1800 :     poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
     818        1800 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATE_LAYER, "YES");
     819        1800 :     poDriver->SetMetadataItem(GDAL_DCAP_DELETE_LAYER, "YES");
     820        1800 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATE_FIELD, "YES");
     821        1800 :     poDriver->SetMetadataItem(GDAL_DCAP_DELETE_FIELD, "YES");
     822        1800 :     poDriver->SetMetadataItem(GDAL_DCAP_REORDER_FIELDS, "YES");
     823        1800 :     poDriver->SetMetadataItem(GDAL_DCAP_CURVE_GEOMETRIES, "YES");
     824        1800 :     poDriver->SetMetadataItem(GDAL_DCAP_MEASURED_GEOMETRIES, "YES");
     825        1800 :     poDriver->SetMetadataItem(GDAL_DCAP_Z_GEOMETRIES, "YES");
     826        1800 :     poDriver->SetMetadataItem(GDAL_DCAP_CAN_READ_AFTER_DELETE, "YES");
     827        1800 :     poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES");
     828        1800 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATE_SUBDATASETS, "YES");
     829        1800 :     poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS,
     830        1800 :                               "NATIVE OGRSQL SQLITE");
     831             : 
     832        1800 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "GeoPackage");
     833        1800 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "gpkg gpkg.zip");
     834        1800 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/vector/gpkg.html");
     835        1800 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
     836        1800 :                               "Byte Int16 UInt16 Float32");
     837             : 
     838        1800 :     poDriver->SetMetadataItem(
     839             :         GDAL_DMD_OPENOPTIONLIST,
     840             :         "<OpenOptionList>"
     841             :         "  <Option name='LIST_ALL_TABLES' type='string-select' scope='vector' "
     842             :         "description='Whether all tables, including those non listed in "
     843             :         "gpkg_contents, should be listed' default='AUTO'>"
     844             :         "    <Value>AUTO</Value>"
     845             :         "    <Value>YES</Value>"
     846             :         "    <Value>NO</Value>"
     847             :         "  </Option>"
     848             :         "  <Option name='TABLE' type='string' scope='raster' description='Name "
     849             :         "of tile user-table'/>"
     850             :         "  <Option name='ZOOM_LEVEL' type='integer' scope='raster' "
     851             :         "description='Zoom level of full resolution. If not specified, maximum "
     852             :         "non-empty zoom level'/>"
     853             :         "  <Option name='BAND_COUNT' type='string-select' scope='raster' "
     854             :         "description='Number of raster bands (only for Byte data type)' "
     855             :         "default='AUTO'>"
     856             :         "    <Value>AUTO</Value>"
     857             :         "    <Value>1</Value>"
     858             :         "    <Value>2</Value>"
     859             :         "    <Value>3</Value>"
     860             :         "    <Value>4</Value>"
     861             :         "  </Option>"
     862             :         "  <Option name='MINX' type='float' scope='raster' "
     863             :         "description='Minimum X of area of interest'/>"
     864             :         "  <Option name='MINY' type='float' scope='raster' "
     865             :         "description='Minimum Y of area of interest'/>"
     866             :         "  <Option name='MAXX' type='float' scope='raster' "
     867             :         "description='Maximum X of area of interest'/>"
     868             :         "  <Option name='MAXY' type='float' scope='raster' "
     869             :         "description='Maximum Y of area of interest'/>"
     870             :         "  <Option name='USE_TILE_EXTENT' type='boolean' scope='raster' "
     871             :         "description='Use tile extent of content to determine area of "
     872             :         "interest' default='NO'/>"
     873             :         "  <Option name='WHERE' type='string' scope='raster' description='SQL "
     874             :         "WHERE clause to be appended to tile requests'/>" COMPRESSION_OPTIONS
     875             :         "  <Option name='PRELUDE_STATEMENTS' type='string' "
     876             :         "scope='raster,vector' description='SQL statement(s) to send on the "
     877             :         "SQLite connection before any other ones'/>"
     878             :         "  <Option name='NOLOCK' type='boolean' description='Whether the "
     879             :         "database should be opened in nolock mode'/>"
     880             :         "  <Option name='IMMUTABLE' type='boolean' description='Whether the "
     881             :         "database should be opened in immutable mode'/>"
     882        1800 :         "</OpenOptionList>");
     883        1800 :     poDriver->SetMetadataItem(GDAL_DMD_OVERVIEW_CREATIONOPTIONLIST,
     884             :                               "<OverviewCreationOptionList>"
     885        1800 :                               "</OverviewCreationOptionList>");
     886             : 
     887        1800 :     poDriver->SetMetadataItem(
     888             :         GDAL_DS_LAYER_CREATIONOPTIONLIST,
     889             :         "<LayerCreationOptionList>"
     890             :         "  <Option name='LAUNDER' type='boolean' description='Whether layer "
     891             :         "and field names will be laundered.' default='NO'/>"
     892             :         "  <Option name='GEOMETRY_NAME' type='string' description='Name of "
     893             :         "geometry column.' default='geom' deprecated_alias='GEOMETRY_COLUMN'/>"
     894             :         "  <Option name='GEOMETRY_NULLABLE' type='boolean' "
     895             :         "description='Whether the values of the geometry column can be NULL' "
     896             :         "default='YES'/>"
     897             :         "  <Option name='SRID' type='integer' description='Forced srs_id of "
     898             :         "the "
     899             :         "entry in the gpkg_spatial_ref_sys table to point to'/>"
     900             :         "  <Option name='DISCARD_COORD_LSB' type='boolean' "
     901             :         "description='Whether the geometry coordinate precision should be used "
     902             :         "to set to zero non-significant least-significant bits of geometries. "
     903             :         "Helps when further compression is used' default='NO'/>"
     904             :         "  <Option name='UNDO_DISCARD_COORD_LSB_ON_READING' type='boolean' "
     905             :         "description='Whether to ask GDAL to take into coordinate precision to "
     906             :         "undo the effects of DISCARD_COORD_LSB' default='NO'/>"
     907             :         "  <Option name='FID' type='string' description='Name of the FID "
     908             :         "column to create' default='fid'/>"
     909             :         "  <Option name='OVERWRITE' type='boolean' description='Whether to "
     910             :         "overwrite an existing table with the layer name to be created' "
     911             :         "default='NO'/>"
     912             :         "  <Option name='PRECISION' type='boolean' description='Whether text "
     913             :         "fields created should keep the width' default='YES'/>"
     914             :         "  <Option name='TRUNCATE_FIELDS' type='boolean' description='Whether "
     915             :         "to truncate text content that exceeds maximum width' default='NO'/>"
     916             :         "  <Option name='SPATIAL_INDEX' type='boolean' description='Whether to "
     917             :         "create a spatial index' default='YES'/>"
     918             :         "  <Option name='IDENTIFIER' type='string' description='Identifier of "
     919             :         "the layer, as put in the contents table'/>"
     920             :         "  <Option name='DESCRIPTION' type='string' description='Description "
     921             :         "of the layer, as put in the contents table'/>"
     922             :         "  <Option name='ASPATIAL_VARIANT' type='string-select' "
     923             :         "description='How to register non spatial tables' "
     924             :         "default='GPKG_ATTRIBUTES'>"
     925             :         "     <Value>GPKG_ATTRIBUTES</Value>"
     926             :         "     <Value>NOT_REGISTERED</Value>"
     927             :         "  </Option>"
     928             :         "  <Option name='DATETIME_PRECISION' type='string-select' "
     929             :         "description='Number of components of datetime fields' "
     930             :         "default='AUTO'>"
     931             :         "     <Value>AUTO</Value>"
     932             :         "     <Value>MILLISECOND</Value>"
     933             :         "     <Value>SECOND</Value>"
     934             :         "     <Value>MINUTE</Value>"
     935             :         "  </Option>"
     936        1800 :         "</LayerCreationOptionList>");
     937             : 
     938        1800 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATATYPES,
     939             :                               "Integer Integer64 Real String Date DateTime "
     940        1800 :                               "Binary");
     941        1800 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATASUBTYPES,
     942        1800 :                               "Boolean Int16 Float32 JSON");
     943        1800 :     poDriver->SetMetadataItem(GDAL_DMD_CREATION_FIELD_DEFN_FLAGS,
     944             :                               "WidthPrecision Nullable Default Unique "
     945        1800 :                               "Comment AlternativeName Domain");
     946             : 
     947        1800 :     poDriver->SetMetadataItem(GDAL_DMD_ALTER_FIELD_DEFN_FLAGS,
     948             :                               "Name Type WidthPrecision Nullable Default "
     949        1800 :                               "Unique Domain AlternativeName Comment");
     950             : 
     951        1800 :     poDriver->SetMetadataItem(GDAL_DCAP_NOTNULL_FIELDS, "YES");
     952        1800 :     poDriver->SetMetadataItem(GDAL_DCAP_DEFAULT_FIELDS, "YES");
     953        1800 :     poDriver->SetMetadataItem(GDAL_DCAP_UNIQUE_FIELDS, "YES");
     954        1800 :     poDriver->SetMetadataItem(GDAL_DCAP_NOTNULL_GEOMFIELDS, "YES");
     955        1800 :     poDriver->SetMetadataItem(GDAL_DCAP_MULTIPLE_VECTOR_LAYERS, "YES");
     956        1800 :     poDriver->SetMetadataItem(GDAL_DCAP_FIELD_DOMAINS, "YES");
     957        1800 :     poDriver->SetMetadataItem(GDAL_DCAP_RELATIONSHIPS, "YES");
     958        1800 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATE_RELATIONSHIP, "YES");
     959        1800 :     poDriver->SetMetadataItem(GDAL_DCAP_DELETE_RELATIONSHIP, "YES");
     960        1800 :     poDriver->SetMetadataItem(GDAL_DCAP_UPDATE_RELATIONSHIP, "YES");
     961        1800 :     poDriver->SetMetadataItem(GDAL_DCAP_FLUSHCACHE_CONSISTENT_STATE, "YES");
     962        1800 :     poDriver->SetMetadataItem(GDAL_DCAP_UPSERT, "YES");
     963             : 
     964        1800 :     poDriver->SetMetadataItem(GDAL_DMD_RELATIONSHIP_FLAGS,
     965        1800 :                               "ManyToMany Association");
     966             : 
     967        1800 :     poDriver->SetMetadataItem(GDAL_DCAP_RENAME_LAYERS, "YES");
     968        1800 :     poDriver->SetMetadataItem(GDAL_DMD_CREATION_FIELD_DOMAIN_TYPES,
     969        1800 :                               "Coded Range Glob");
     970             : 
     971        1800 :     poDriver->SetMetadataItem(GDAL_DMD_ALTER_GEOM_FIELD_DEFN_FLAGS,
     972        1800 :                               "Name SRS CoordinateEpoch");
     973             : 
     974        1800 :     poDriver->SetMetadataItem(
     975             :         GDAL_DMD_RELATIONSHIP_RELATED_TABLE_TYPES,
     976        1800 :         "features media simple_attributes attributes tiles");
     977             : 
     978        1800 :     poDriver->SetMetadataItem(GDAL_DCAP_HONOR_GEOM_COORDINATE_PRECISION, "YES");
     979             : 
     980             : #ifdef ENABLE_SQL_GPKG_FORMAT
     981        1800 :     poDriver->SetMetadataItem("ENABLE_SQL_GPKG_FORMAT", "YES");
     982             : #endif
     983             : #ifdef SQLITE_HAS_COLUMN_METADATA
     984        1800 :     poDriver->SetMetadataItem("SQLITE_HAS_COLUMN_METADATA", "YES");
     985             : #endif
     986             : 
     987        1800 :     poDriver->SetMetadataItem(GDAL_DCAP_UPDATE, "YES");
     988        1800 :     poDriver->SetMetadataItem(GDAL_DMD_UPDATE_ITEMS,
     989             :                               "DatasetMetadata BandMetadata RasterValues "
     990        1800 :                               "LayerMetadata Features");
     991             : 
     992        1800 :     poDriver->pfnOpen = OGRGeoPackageDriverOpen;
     993        1800 :     poDriver->pfnIdentify = OGRGeoPackageDriverIdentify;
     994        1800 :     poDriver->pfnCreate = OGRGeoPackageDriverCreate;
     995        1800 :     poDriver->pfnCreateCopy = GDALGeoPackageDataset::CreateCopy;
     996        1800 :     poDriver->pfnDelete = OGRGeoPackageDriverDelete;
     997        1800 :     poDriver->pfnGetSubdatasetInfoFunc = OGRGeoPackageDriverGetSubdatasetInfo;
     998             : 
     999        1800 :     poDriver->pfnInstantiateAlgorithm = OGRGeoPackageDriverInstantiateAlgorithm;
    1000        3600 :     poDriver->DeclareAlgorithm({"repack"});
    1001        3600 :     poDriver->DeclareAlgorithm({"validate"});
    1002             : 
    1003        1800 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
    1004             : 
    1005        1800 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1006             : }

Generated by: LCOV version 1.14