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

Generated by: LCOV version 1.14