LCOV - code coverage report
Current view: top level - frmts/mbtiles - mbtilesdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1277 1719 74.3 %
Date: 2024-04-27 17:22:41 Functions: 64 69 92.8 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL MBTiles driver
       4             :  * Purpose:  Implement GDAL MBTiles support using OGR SQLite driver
       5             :  * Author:   Even Rouault, Even Rouault <even.rouault at spatialys.com>
       6             :  *
       7             :  **********************************************************************
       8             :  * Copyright (c) 2012-2016, Even Rouault <even.rouault at spatialys.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             : #if defined(HAVE_SQLITE) && defined(HAVE_GEOS)
      30             : // Needed by mvtutils.h
      31             : #define HAVE_MVT_WRITE_SUPPORT
      32             : #endif
      33             : 
      34             : #include "gdal_frmts.h"
      35             : #include "gdal_pam.h"
      36             : #include "ogr_api.h"
      37             : #include "cpl_json.h"
      38             : #include "cpl_vsil_curl_priv.h"
      39             : #include "gpkgmbtilescommon.h"
      40             : #include "gdal_utils.h"
      41             : #include "gdalwarper.h"
      42             : #include "mvtutils.h"
      43             : #include "ogrsqlitevfs.h"
      44             : #include "ogrsqlitebase.h"
      45             : 
      46             : #include "zlib.h"
      47             : #include "ogrgeojsonreader.h"
      48             : 
      49             : #include <math.h>
      50             : #include <algorithm>
      51             : #include <memory>
      52             : #include <vector>
      53             : 
      54             : static const char *const apszAllowedDrivers[] = {"JPEG", "PNG", "WEBP",
      55             :                                                  nullptr};
      56             : 
      57             : #define SRS_EPSG_3857                                                          \
      58             :     "PROJCS[\"WGS 84 / Pseudo-Mercator\",GEOGCS[\"WGS "                        \
      59             :     "84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS "                                  \
      60             :     "84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY["      \
      61             :     "\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]]" \
      62             :     ",UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"       \
      63             :     "AUTHORITY[\"EPSG\",\"4326\"]],PROJECTION[\"Mercator_1SP\"],PARAMETER["    \
      64             :     "\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_" \
      65             :     "easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[" \
      66             :     "\"EPSG\",\"9001\"]],AXIS[\"X\",EAST],AXIS[\"Y\",NORTH],EXTENSION["        \
      67             :     "\"PROJ4\",\"+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 "     \
      68             :     "+x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext  "                \
      69             :     "+no_defs\"],AUTHORITY[\"EPSG\",\"3857\"]]"
      70             : 
      71             : #define SPHERICAL_RADIUS 6378137.0
      72             : #define MAX_GM (SPHERICAL_RADIUS * M_PI)  // 20037508.342789244
      73             : 
      74             : // TileMatrixSet origin : caution this is in GeoPackage / WMTS convention ! That
      75             : // is upper-left corner
      76             : #define TMS_ORIGIN_X -MAX_GM
      77             : #define TMS_ORIGIN_Y MAX_GM
      78             : 
      79             : #if defined(DEBUG) || defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) ||     \
      80             :     defined(ALLOW_FORMAT_DUMPS)
      81             : // Enable accepting a SQL dump (starting with a "-- SQL MBTILES" line) as a
      82             : // valid file. This makes fuzzer life easier
      83             : #define ENABLE_SQL_SQLITE_FORMAT
      84             : #endif
      85             : 
      86             : constexpr int knDEFAULT_BLOCK_SIZE = 256;
      87             : 
      88             : class MBTilesBand;
      89             : 
      90             : /************************************************************************/
      91             : /*                         MBTILESOpenSQLiteDB()                        */
      92             : /************************************************************************/
      93             : 
      94          75 : static OGRDataSourceH MBTILESOpenSQLiteDB(const char *pszFilename,
      95             :                                           GDALAccess eAccess)
      96             : {
      97          75 :     const char *l_apszAllowedDrivers[] = {"SQLITE", nullptr};
      98         150 :     return (OGRDataSourceH)GDALOpenEx(
      99         150 :         (CPLString("SQLITE:") + pszFilename).c_str(),
     100             :         GDAL_OF_VECTOR | GDAL_OF_INTERNAL |
     101             :             ((eAccess == GA_Update) ? GDAL_OF_UPDATE : 0),
     102         150 :         l_apszAllowedDrivers, nullptr, nullptr);
     103             : }
     104             : 
     105             : /************************************************************************/
     106             : /* ==================================================================== */
     107             : /*                              MBTilesDataset                          */
     108             : /* ==================================================================== */
     109             : /************************************************************************/
     110             : 
     111             : class MBTilesDataset final : public GDALPamDataset,
     112             :                              public GDALGPKGMBTilesLikePseudoDataset
     113             : {
     114             :     friend class MBTilesBand;
     115             :     friend class MBTilesVectorLayer;
     116             : 
     117             :   public:
     118             :     MBTilesDataset();
     119             : 
     120             :     virtual ~MBTilesDataset();
     121             : 
     122             :     virtual CPLErr GetGeoTransform(double *padfGeoTransform) override;
     123             :     virtual CPLErr SetGeoTransform(double *padfGeoTransform) override;
     124             :     const OGRSpatialReference *GetSpatialRef() const override;
     125             :     CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override;
     126             : 
     127             :     virtual char **GetMetadataDomainList() override;
     128             :     virtual char **GetMetadata(const char *pszDomain = "") override;
     129             :     virtual const char *GetMetadataItem(const char *pszName,
     130             :                                         const char *pszDomain = "") override;
     131             : 
     132             :     virtual CPLErr IBuildOverviews(const char *pszResampling, int nOverviews,
     133             :                                    const int *panOverviewList, int nBandsIn,
     134             :                                    const int * /* panBandList */,
     135             :                                    GDALProgressFunc pfnProgress,
     136             :                                    void *pProgressData,
     137             :                                    CSLConstList papszOptions) override;
     138             : 
     139        1142 :     virtual int GetLayerCount() override
     140             :     {
     141        1142 :         return static_cast<int>(m_apoLayers.size());
     142             :     }
     143             : 
     144             :     virtual OGRLayer *GetLayer(int) override;
     145             : 
     146             :     static GDALDataset *Open(GDALOpenInfo *);
     147             :     static int Identify(GDALOpenInfo *);
     148             :     static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
     149             :                                int nBandsIn, GDALDataType eDT,
     150             :                                char **papszOptions);
     151             :     static GDALDataset *CreateCopy(const char *pszFilename,
     152             :                                    GDALDataset *poSrcDS, int bStrict,
     153             :                                    char **papszOptions,
     154             :                                    GDALProgressFunc pfnProgress,
     155             :                                    void *pProgressData);
     156             : 
     157             :     char *FindKey(int iPixel, int iLine);
     158             : 
     159             :     bool HasNonEmptyGrids();
     160             : 
     161             :   private:
     162             :     bool m_bWriteBounds;
     163             :     CPLString m_osBounds;
     164             :     CPLString m_osCenter;
     165             :     bool m_bWriteMinMaxZoom;
     166             :     MBTilesDataset *poMainDS;
     167             :     bool m_bGeoTransformValid;
     168             :     double m_adfGeoTransform[6];
     169             :     int m_nMinZoomLevel = 0;
     170             :     OGRSpatialReference m_oSRS{};
     171             : 
     172             :     int m_nOverviewCount;
     173             :     MBTilesDataset **m_papoOverviewDS;
     174             : 
     175             :     OGRDataSourceH hDS;
     176             :     sqlite3 *hDB;
     177             : 
     178             :     sqlite3_vfs *pMyVFS;
     179             : 
     180             :     bool bFetchedMetadata;
     181             :     CPLStringList aosList;
     182             : 
     183             :     int nHasNonEmptyGrids;
     184             : 
     185             :     bool m_bInFlushCache;
     186             : 
     187             :     CPLString m_osMetadataMemFilename;
     188             :     CPLString m_osClip;
     189             :     std::vector<std::unique_ptr<OGRLayer>> m_apoLayers;
     190             : 
     191             :     void ParseCompressionOptions(char **papszOptions);
     192             :     CPLErr FinalizeRasterRegistration();
     193             :     void ComputeTileAndPixelShifts();
     194             :     bool InitRaster(MBTilesDataset *poParentDS, int nZoomLevel, int nBandCount,
     195             :                     int nTileSize, double dfGDALMinX, double dfGDALMinY,
     196             :                     double dfGDALMaxX, double dfGDALMaxY);
     197             : 
     198             :     bool CreateInternal(const char *pszFilename, int nXSize, int nYSize,
     199             :                         int nBandsIn, GDALDataType eDT, char **papszOptions);
     200             :     void InitVector(double dfMinX, double dfMinY, double dfMaxX, double dfMaxY,
     201             :                     bool bZoomLevelFromSpatialFilter, bool bJsonField);
     202             : 
     203             :   protected:
     204             :     // Coming from GDALGPKGMBTilesLikePseudoDataset
     205             : 
     206             :     virtual CPLErr IFlushCacheWithErrCode(bool bAtClosing) override;
     207             : 
     208        1963 :     virtual int IGetRasterCount() override
     209             :     {
     210        1963 :         return nBands;
     211             :     }
     212             : 
     213        3310 :     virtual GDALRasterBand *IGetRasterBand(int nBand) override
     214             :     {
     215        3310 :         return GetRasterBand(nBand);
     216             :     }
     217             : 
     218         546 :     virtual sqlite3 *IGetDB() override
     219             :     {
     220         546 :         return hDB;
     221             :     }
     222             : 
     223         927 :     virtual bool IGetUpdate() override
     224             :     {
     225         927 :         return eAccess == GA_Update;
     226             :     }
     227             : 
     228             :     virtual bool ICanIWriteBlock() override;
     229             :     virtual OGRErr IStartTransaction() override;
     230             :     virtual OGRErr ICommitTransaction() override;
     231             : 
     232          22 :     virtual const char *IGetFilename() override
     233             :     {
     234          22 :         return GetDescription();
     235             :     }
     236             : 
     237             :     virtual int GetRowFromIntoTopConvention(int nRow) override;
     238             : };
     239             : 
     240             : /************************************************************************/
     241             : /* ==================================================================== */
     242             : /*                          MBTilesVectorLayer                          */
     243             : /* ==================================================================== */
     244             : /************************************************************************/
     245             : 
     246             : class MBTilesVectorLayer final : public OGRLayer
     247             : {
     248             :     MBTilesDataset *m_poDS = nullptr;
     249             :     OGRFeatureDefn *m_poFeatureDefn = nullptr;
     250             :     OGRLayerH m_hTileIteratorLyr = nullptr;
     251             :     bool m_bEOF = false;
     252             :     CPLString m_osTmpFilename;
     253             :     GDALDatasetH m_hTileDS = nullptr;
     254             :     GIntBig m_nFeatureCount = -1;
     255             :     int m_nX = 0;
     256             :     int m_nY = 0;
     257             :     OGREnvelope m_sExtent;
     258             :     int m_nFilterMinX = 0;
     259             :     int m_nFilterMinY = 0;
     260             :     int m_nFilterMaxX = 0;
     261             :     int m_nFilterMaxY = 0;
     262             :     int m_nZoomLevel = 0;
     263             :     bool m_bZoomLevelAuto = false;
     264             :     bool m_bJsonField = false;
     265             : 
     266             :     OGRFeature *GetNextRawFeature();
     267             :     OGRFeature *GetNextSrcFeature();
     268             :     OGRFeature *CreateFeatureFrom(OGRFeature *poSrcFeature);
     269             : 
     270             :   public:
     271             :     MBTilesVectorLayer(MBTilesDataset *poDS, const char *pszLayerName,
     272             :                        const CPLJSONObject &oFields,
     273             :                        const CPLJSONArray &oAttributesFromTileStats,
     274             :                        bool bJsonField, double dfMinX, double dfMinY,
     275             :                        double dfMaxX, double dfMaxY,
     276             :                        OGRwkbGeometryType eGeomType,
     277             :                        bool bZoomLevelFromSpatialFilter);
     278             :     ~MBTilesVectorLayer();
     279             : 
     280             :     virtual void ResetReading() override;
     281             :     virtual OGRFeature *GetNextFeature() override;
     282             : 
     283         849 :     virtual OGRFeatureDefn *GetLayerDefn() override
     284             :     {
     285         849 :         return m_poFeatureDefn;
     286             :     }
     287             : 
     288             :     virtual GIntBig GetFeatureCount(int bForce) override;
     289             :     virtual int TestCapability(const char *) override;
     290             : 
     291             :     OGRErr GetExtent(OGREnvelope *psExtent, int bForce) override;
     292             : 
     293           4 :     virtual OGRErr GetExtent(int iGeomField, OGREnvelope *psExtent,
     294             :                              int bForce) override
     295             :     {
     296           4 :         return OGRLayer::GetExtent(iGeomField, psExtent, bForce);
     297             :     }
     298             : 
     299             :     virtual void SetSpatialFilter(OGRGeometry *) override;
     300             : 
     301          13 :     virtual void SetSpatialFilter(int iGeomField, OGRGeometry *poGeom) override
     302             :     {
     303          13 :         OGRLayer::SetSpatialFilter(iGeomField, poGeom);
     304          13 :     }
     305             : 
     306             :     virtual OGRFeature *GetFeature(GIntBig nFID) override;
     307             : };
     308             : 
     309             : /************************************************************************/
     310             : /* ==================================================================== */
     311             : /*                              MBTilesBand                             */
     312             : /* ==================================================================== */
     313             : /************************************************************************/
     314             : 
     315             : class MBTilesBand final : public GDALGPKGMBTilesLikeRasterBand
     316             : {
     317             :     friend class MBTilesDataset;
     318             : 
     319             :     CPLString osLocationInfo;
     320             : 
     321             :   public:
     322             :     explicit MBTilesBand(MBTilesDataset *poDS, int nTileSize);
     323             : 
     324             :     virtual int GetOverviewCount() override;
     325             :     virtual GDALRasterBand *GetOverview(int nLevel) override;
     326             : 
     327             :     virtual char **GetMetadataDomainList() override;
     328             :     virtual const char *GetMetadataItem(const char *pszName,
     329             :                                         const char *pszDomain = "") override;
     330             : };
     331             : 
     332             : /************************************************************************/
     333             : /*                            MBTilesBand()                             */
     334             : /************************************************************************/
     335             : 
     336         718 : MBTilesBand::MBTilesBand(MBTilesDataset *poDSIn, int nTileSize)
     337         718 :     : GDALGPKGMBTilesLikeRasterBand(poDSIn, nTileSize, nTileSize)
     338             : {
     339         718 : }
     340             : 
     341             : /************************************************************************/
     342             : /*                           utf8decode()                               */
     343             : /************************************************************************/
     344             : 
     345           0 : static unsigned utf8decode(const char *p, const char *end, int *len)
     346             : {
     347           0 :     unsigned char c = *(unsigned char *)p;
     348           0 :     if (c < 0x80)
     349             :     {
     350           0 :         *len = 1;
     351           0 :         return c;
     352             :     }
     353           0 :     else if (c < 0xc2)
     354             :     {
     355           0 :         goto FAIL;
     356             :     }
     357           0 :     if (p + 1 >= end || (p[1] & 0xc0) != 0x80)
     358           0 :         goto FAIL;
     359           0 :     if (c < 0xe0)
     360             :     {
     361           0 :         *len = 2;
     362           0 :         return ((p[0] & 0x1f) << 6) + ((p[1] & 0x3f));
     363             :     }
     364           0 :     else if (c == 0xe0)
     365             :     {
     366           0 :         if (((unsigned char *)p)[1] < 0xa0)
     367           0 :             goto FAIL;
     368           0 :         goto UTF8_3;
     369             :     }
     370             : #if STRICT_RFC3629
     371             :     else if (c == 0xed)
     372             :     {
     373             :         // RFC 3629 says surrogate chars are illegal.
     374             :         if (((unsigned char *)p)[1] >= 0xa0)
     375             :             goto FAIL;
     376             :         goto UTF8_3;
     377             :     }
     378             :     else if (c == 0xef)
     379             :     {
     380             :         // 0xfffe and 0xffff are also illegal characters
     381             :         if (((unsigned char *)p)[1] == 0xbf && ((unsigned char *)p)[2] >= 0xbe)
     382             :             goto FAIL;
     383             :         goto UTF8_3;
     384             :     }
     385             : #endif
     386           0 :     else if (c < 0xf0)
     387             :     {
     388           0 :     UTF8_3:
     389           0 :         if (p + 2 >= end || (p[2] & 0xc0) != 0x80)
     390           0 :             goto FAIL;
     391           0 :         *len = 3;
     392           0 :         return ((p[0] & 0x0f) << 12) + ((p[1] & 0x3f) << 6) + ((p[2] & 0x3f));
     393             :     }
     394           0 :     else if (c == 0xf0)
     395             :     {
     396           0 :         if (((unsigned char *)p)[1] < 0x90)
     397           0 :             goto FAIL;
     398           0 :         goto UTF8_4;
     399             :     }
     400           0 :     else if (c < 0xf4)
     401             :     {
     402           0 :     UTF8_4:
     403           0 :         if (p + 3 >= end || (p[2] & 0xc0) != 0x80 || (p[3] & 0xc0) != 0x80)
     404           0 :             goto FAIL;
     405           0 :         *len = 4;
     406             : #if STRICT_RFC3629
     407             :         // RFC 3629 says all codes ending in fffe or ffff are illegal:
     408             :         if ((p[1] & 0xf) == 0xf && ((unsigned char *)p)[2] == 0xbf &&
     409             :             ((unsigned char *)p)[3] >= 0xbe)
     410             :             goto FAIL;
     411             : #endif
     412           0 :         return ((p[0] & 0x07) << 18) + ((p[1] & 0x3f) << 12) +
     413           0 :                ((p[2] & 0x3f) << 6) + ((p[3] & 0x3f));
     414             :     }
     415           0 :     else if (c == 0xf4)
     416             :     {
     417           0 :         if (((unsigned char *)p)[1] > 0x8f)
     418           0 :             goto FAIL;  // after 0x10ffff
     419           0 :         goto UTF8_4;
     420             :     }
     421             :     else
     422             :     {
     423           0 :     FAIL:
     424           0 :         *len = 1;
     425           0 :         return 0xfffd;  // Unicode REPLACEMENT CHARACTER
     426             :     }
     427             : }
     428             : 
     429             : /************************************************************************/
     430             : /*                          HasNonEmptyGrids()                          */
     431             : /************************************************************************/
     432             : 
     433           0 : bool MBTilesDataset::HasNonEmptyGrids()
     434             : {
     435             :     OGRLayerH hSQLLyr;
     436             :     OGRFeatureH hFeat;
     437             : 
     438           0 :     if (poMainDS)
     439           0 :         return poMainDS->HasNonEmptyGrids();
     440             : 
     441           0 :     if (nHasNonEmptyGrids >= 0)
     442           0 :         return nHasNonEmptyGrids != FALSE;
     443             : 
     444           0 :     nHasNonEmptyGrids = false;
     445             : 
     446           0 :     if (OGR_DS_GetLayerByName(hDS, "grids") == nullptr)
     447           0 :         return false;
     448             : 
     449           0 :     const char *pszSQL = "SELECT type FROM sqlite_master WHERE name = 'grids'";
     450           0 :     CPLDebug("MBTILES", "%s", pszSQL);
     451           0 :     hSQLLyr = OGR_DS_ExecuteSQL(hDS, pszSQL, nullptr, nullptr);
     452           0 :     if (hSQLLyr == nullptr)
     453           0 :         return false;
     454             : 
     455           0 :     hFeat = OGR_L_GetNextFeature(hSQLLyr);
     456           0 :     if (hFeat == nullptr || !OGR_F_IsFieldSetAndNotNull(hFeat, 0))
     457             :     {
     458           0 :         OGR_F_Destroy(hFeat);
     459           0 :         OGR_DS_ReleaseResultSet(hDS, hSQLLyr);
     460           0 :         return false;
     461             :     }
     462             : 
     463           0 :     bool bGridsIsView = strcmp(OGR_F_GetFieldAsString(hFeat, 0), "view") == 0;
     464             : 
     465           0 :     OGR_F_Destroy(hFeat);
     466           0 :     OGR_DS_ReleaseResultSet(hDS, hSQLLyr);
     467             : 
     468           0 :     nHasNonEmptyGrids = TRUE;
     469             : 
     470             :     /* In the case 'grids' is a view (and a join between the 'map' and
     471             :      * 'grid_utfgrid' layers */
     472             :     /* the cost of evaluating a join is very long, even if grid_utfgrid is empty
     473             :      */
     474             :     /* so check it is not empty */
     475           0 :     if (bGridsIsView)
     476             :     {
     477             :         OGRLayerH hGridUTFGridLyr;
     478           0 :         hGridUTFGridLyr = OGR_DS_GetLayerByName(hDS, "grid_utfgrid");
     479           0 :         if (hGridUTFGridLyr != nullptr)
     480             :         {
     481           0 :             OGR_L_ResetReading(hGridUTFGridLyr);
     482           0 :             hFeat = OGR_L_GetNextFeature(hGridUTFGridLyr);
     483           0 :             OGR_F_Destroy(hFeat);
     484             : 
     485           0 :             nHasNonEmptyGrids = hFeat != nullptr;
     486             :         }
     487             :     }
     488             : 
     489           0 :     return nHasNonEmptyGrids != FALSE;
     490             : }
     491             : 
     492             : /************************************************************************/
     493             : /*                             FindKey()                                */
     494             : /************************************************************************/
     495             : 
     496           0 : char *MBTilesDataset::FindKey(int iPixel, int iLine)
     497             : {
     498             :     int nBlockXSize;
     499             :     int nBlockYSize;
     500           0 :     GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
     501             : 
     502             :     // Compute shift between GDAL origin and TileMatrixSet origin
     503             :     // Caution this is in GeoPackage / WMTS convention ! That is upper-left
     504             :     // corner
     505           0 :     const int nShiftXPixels = (int)floor(
     506           0 :         0.5 + (m_adfGeoTransform[0] - TMS_ORIGIN_X) / m_adfGeoTransform[1]);
     507           0 :     const int nShiftYPixelsFromGPKGOrigin = (int)floor(
     508           0 :         0.5 + (m_adfGeoTransform[3] - TMS_ORIGIN_Y) / m_adfGeoTransform[5]);
     509             : 
     510           0 :     const int iLineFromGPKGOrigin = iLine + nShiftYPixelsFromGPKGOrigin;
     511           0 :     const int iLineFromMBTilesOrigin =
     512           0 :         m_nTileMatrixHeight * nBlockYSize - 1 - iLineFromGPKGOrigin;
     513           0 :     const int iPixelFromMBTilesOrigin = iPixel + nShiftXPixels;
     514             : 
     515           0 :     const int nTileColumn = iPixelFromMBTilesOrigin / nBlockXSize;
     516           0 :     const int nTileRow = iLineFromMBTilesOrigin / nBlockYSize;
     517           0 :     int nColInTile = iPixelFromMBTilesOrigin % nBlockXSize;
     518           0 :     int nRowInTile = nBlockYSize - 1 - (iLineFromMBTilesOrigin % nBlockYSize);
     519             : 
     520           0 :     char *pszKey = nullptr;
     521             : 
     522             :     OGRLayerH hSQLLyr;
     523             :     OGRFeatureH hFeat;
     524           0 :     json_object *poGrid = nullptr;
     525             :     int i;
     526             : 
     527             :     /* See https://github.com/mapbox/utfgrid-spec/blob/master/1.0/utfgrid.md */
     528             :     /* for the explanation of the following process */
     529             :     const char *pszSQL =
     530           0 :         CPLSPrintf("SELECT grid FROM grids WHERE "
     531             :                    "zoom_level = %d AND tile_column = %d AND tile_row = %d",
     532             :                    m_nZoomLevel, nTileColumn, nTileRow);
     533           0 :     CPLDebug("MBTILES", "%s", pszSQL);
     534           0 :     hSQLLyr = OGR_DS_ExecuteSQL(hDS, pszSQL, nullptr, nullptr);
     535           0 :     if (hSQLLyr == nullptr)
     536           0 :         return nullptr;
     537             : 
     538           0 :     hFeat = OGR_L_GetNextFeature(hSQLLyr);
     539           0 :     if (hFeat == nullptr || !OGR_F_IsFieldSetAndNotNull(hFeat, 0))
     540             :     {
     541           0 :         OGR_F_Destroy(hFeat);
     542           0 :         OGR_DS_ReleaseResultSet(hDS, hSQLLyr);
     543           0 :         return nullptr;
     544             :     }
     545             : 
     546           0 :     int nDataSize = 0;
     547           0 :     GByte *pabyData = OGR_F_GetFieldAsBinary(hFeat, 0, &nDataSize);
     548             : 
     549           0 :     int nUncompressedSize = nBlockXSize * nBlockYSize;
     550           0 :     GByte *pabyUncompressed = (GByte *)VSIMalloc(nUncompressedSize + 1);
     551           0 :     if (pabyUncompressed == nullptr)
     552             :     {
     553           0 :         OGR_F_Destroy(hFeat);
     554           0 :         OGR_DS_ReleaseResultSet(hDS, hSQLLyr);
     555           0 :         return nullptr;
     556             :     }
     557             : 
     558             :     z_stream sStream;
     559           0 :     memset(&sStream, 0, sizeof(sStream));
     560           0 :     if (inflateInit(&sStream) != Z_OK)
     561             :     {
     562           0 :         OGR_F_Destroy(hFeat);
     563           0 :         OGR_DS_ReleaseResultSet(hDS, hSQLLyr);
     564           0 :         CPLFree(pabyUncompressed);
     565           0 :         return nullptr;
     566             :     }
     567           0 :     sStream.next_in = pabyData;
     568           0 :     sStream.avail_in = nDataSize;
     569           0 :     sStream.next_out = pabyUncompressed;
     570           0 :     sStream.avail_out = nUncompressedSize;
     571           0 :     int nStatus = inflate(&sStream, Z_FINISH);
     572           0 :     inflateEnd(&sStream);
     573           0 :     if (nStatus != Z_OK && nStatus != Z_STREAM_END)
     574             :     {
     575           0 :         CPLDebug("MBTILES", "Error unzipping grid");
     576           0 :         nUncompressedSize = 0;
     577           0 :         pabyUncompressed[nUncompressedSize] = 0;
     578             :     }
     579             :     else
     580             :     {
     581           0 :         nUncompressedSize -= sStream.avail_out;
     582           0 :         pabyUncompressed[nUncompressedSize] = 0;
     583             :         // CPLDebug("MBTILES", "Grid size = %d", nUncompressedSize);
     584             :         // CPLDebug("MBTILES", "Grid value = %s", (const
     585             :         // char*)pabyUncompressed);
     586             :     }
     587             : 
     588           0 :     json_object *jsobj = nullptr;
     589             : 
     590           0 :     if (nUncompressedSize == 0)
     591             :     {
     592           0 :         goto end;
     593             :     }
     594             : 
     595           0 :     if (!OGRJSonParse((const char *)pabyUncompressed, &jsobj, true))
     596             :     {
     597           0 :         goto end;
     598             :     }
     599             : 
     600           0 :     if (json_object_is_type(jsobj, json_type_object))
     601             :     {
     602           0 :         poGrid = CPL_json_object_object_get(jsobj, "grid");
     603             :     }
     604           0 :     if (poGrid != nullptr && json_object_is_type(poGrid, json_type_array))
     605             :     {
     606             :         int nFactor;
     607             :         json_object *poRow;
     608           0 :         char *pszRow = nullptr;
     609             : 
     610           0 :         const int nLines = static_cast<int>(json_object_array_length(poGrid));
     611           0 :         if (nLines == 0)
     612           0 :             goto end;
     613             : 
     614           0 :         nFactor = nBlockXSize / nLines;
     615           0 :         nRowInTile /= nFactor;
     616           0 :         nColInTile /= nFactor;
     617             : 
     618           0 :         poRow = json_object_array_get_idx(poGrid, nRowInTile);
     619             : 
     620             :         /* Extract line of interest in grid */
     621           0 :         if (poRow != nullptr && json_object_is_type(poRow, json_type_string))
     622             :         {
     623           0 :             pszRow = CPLStrdup(json_object_get_string(poRow));
     624             :         }
     625             : 
     626           0 :         if (pszRow == nullptr)
     627           0 :             goto end;
     628             : 
     629             :         /* Unapply JSON encoding */
     630           0 :         for (i = 0; pszRow[i] != '\0'; i++)
     631             :         {
     632           0 :             unsigned char c = ((GByte *)pszRow)[i];
     633           0 :             if (c >= 93)
     634           0 :                 c--;
     635           0 :             if (c >= 35)
     636           0 :                 c--;
     637           0 :             if (c < 32)
     638             :             {
     639           0 :                 CPLDebug("MBTILES", "Invalid character at byte %d", i);
     640           0 :                 break;
     641             :             }
     642           0 :             c -= 32;
     643           0 :             ((GByte *)pszRow)[i] = c;
     644             :         }
     645             : 
     646           0 :         if (pszRow[i] == '\0')
     647             :         {
     648           0 :             char *pszEnd = pszRow + i;
     649             : 
     650           0 :             int iCol = 0;
     651           0 :             i = 0;
     652           0 :             int nKey = -1;
     653           0 :             while (pszRow + i < pszEnd)
     654             :             {
     655           0 :                 int len = 0;
     656           0 :                 unsigned int res = utf8decode(pszRow + i, pszEnd, &len);
     657             : 
     658             :                 /* Invalid UTF8 ? */
     659           0 :                 if (res > 127 && len == 1)
     660           0 :                     break;
     661             : 
     662           0 :                 if (iCol == nColInTile)
     663             :                 {
     664           0 :                     nKey = (int)res;
     665             :                     // CPLDebug("MBTILES", "Key index = %d", nKey);
     666           0 :                     break;
     667             :                 }
     668           0 :                 i += len;
     669           0 :                 iCol++;
     670             :             }
     671             : 
     672             :             /* Find key */
     673           0 :             json_object *poKeys = CPL_json_object_object_get(jsobj, "keys");
     674           0 :             if (nKey >= 0 && poKeys != nullptr &&
     675           0 :                 json_object_is_type(poKeys, json_type_array) &&
     676           0 :                 nKey < static_cast<int>(json_object_array_length(poKeys)))
     677             :             {
     678           0 :                 json_object *poKey = json_object_array_get_idx(poKeys, nKey);
     679           0 :                 if (poKey != nullptr &&
     680           0 :                     json_object_is_type(poKey, json_type_string))
     681             :                 {
     682           0 :                     pszKey = CPLStrdup(json_object_get_string(poKey));
     683             :                 }
     684             :             }
     685             :         }
     686             : 
     687           0 :         CPLFree(pszRow);
     688             :     }
     689             : 
     690           0 : end:
     691           0 :     if (jsobj)
     692           0 :         json_object_put(jsobj);
     693           0 :     VSIFree(pabyUncompressed);
     694           0 :     if (hFeat)
     695           0 :         OGR_F_Destroy(hFeat);
     696           0 :     if (hSQLLyr)
     697           0 :         OGR_DS_ReleaseResultSet(hDS, hSQLLyr);
     698             : 
     699           0 :     return pszKey;
     700             : }
     701             : 
     702             : /************************************************************************/
     703             : /*                      GetMetadataDomainList()                         */
     704             : /************************************************************************/
     705             : 
     706           0 : char **MBTilesBand::GetMetadataDomainList()
     707             : {
     708           0 :     return CSLAddString(GDALPamRasterBand::GetMetadataDomainList(),
     709           0 :                         "LocationInfo");
     710             : }
     711             : 
     712             : /************************************************************************/
     713             : /*                         GetMetadataItem()                            */
     714             : /************************************************************************/
     715             : 
     716          22 : const char *MBTilesBand::GetMetadataItem(const char *pszName,
     717             :                                          const char *pszDomain)
     718             : {
     719          22 :     MBTilesDataset *poGDS = (MBTilesDataset *)poDS;
     720             : 
     721             :     /* ==================================================================== */
     722             :     /*      LocationInfo handling.                                          */
     723             :     /* ==================================================================== */
     724          22 :     if (poGDS->hDS != nullptr && pszDomain != nullptr &&
     725           0 :         EQUAL(pszDomain, "LocationInfo") &&
     726           0 :         (STARTS_WITH_CI(pszName, "Pixel_") ||
     727           0 :          STARTS_WITH_CI(pszName, "GeoPixel_")))
     728             :     {
     729             :         int iPixel, iLine;
     730             : 
     731           0 :         if (!poGDS->HasNonEmptyGrids())
     732           0 :             return nullptr;
     733             : 
     734             :         /* --------------------------------------------------------------------
     735             :          */
     736             :         /*      What pixel are we aiming at? */
     737             :         /* --------------------------------------------------------------------
     738             :          */
     739           0 :         if (STARTS_WITH_CI(pszName, "Pixel_"))
     740             :         {
     741           0 :             if (sscanf(pszName + 6, "%d_%d", &iPixel, &iLine) != 2)
     742           0 :                 return nullptr;
     743             :         }
     744           0 :         else if (STARTS_WITH_CI(pszName, "GeoPixel_"))
     745             :         {
     746             :             double adfGeoTransform[6];
     747             :             double adfInvGeoTransform[6];
     748             :             double dfGeoX, dfGeoY;
     749             : 
     750           0 :             dfGeoX = CPLAtof(pszName + 9);
     751           0 :             const char *pszUnderscore = strchr(pszName + 9, '_');
     752           0 :             if (!pszUnderscore)
     753           0 :                 return nullptr;
     754           0 :             dfGeoY = CPLAtof(pszUnderscore + 1);
     755             : 
     756           0 :             if (GetDataset() == nullptr)
     757           0 :                 return nullptr;
     758             : 
     759           0 :             if (GetDataset()->GetGeoTransform(adfGeoTransform) != CE_None)
     760           0 :                 return nullptr;
     761             : 
     762           0 :             if (!GDALInvGeoTransform(adfGeoTransform, adfInvGeoTransform))
     763           0 :                 return nullptr;
     764             : 
     765           0 :             iPixel = (int)floor(adfInvGeoTransform[0] +
     766           0 :                                 adfInvGeoTransform[1] * dfGeoX +
     767           0 :                                 adfInvGeoTransform[2] * dfGeoY);
     768           0 :             iLine = (int)floor(adfInvGeoTransform[3] +
     769           0 :                                adfInvGeoTransform[4] * dfGeoX +
     770           0 :                                adfInvGeoTransform[5] * dfGeoY);
     771             :         }
     772             :         else
     773           0 :             return nullptr;
     774             : 
     775           0 :         if (iPixel < 0 || iLine < 0 || iPixel >= GetXSize() ||
     776           0 :             iLine >= GetYSize())
     777           0 :             return nullptr;
     778             : 
     779           0 :         char *pszKey = poGDS->FindKey(iPixel, iLine);
     780             : 
     781           0 :         if (pszKey != nullptr)
     782             :         {
     783             :             // CPLDebug("MBTILES", "Key = %s", pszKey);
     784             : 
     785           0 :             osLocationInfo = "<LocationInfo>";
     786           0 :             osLocationInfo += "<Key>";
     787             :             char *pszXMLEscaped =
     788           0 :                 CPLEscapeString(pszKey, -1, CPLES_XML_BUT_QUOTES);
     789           0 :             osLocationInfo += pszXMLEscaped;
     790           0 :             CPLFree(pszXMLEscaped);
     791           0 :             osLocationInfo += "</Key>";
     792             : 
     793           0 :             if (OGR_DS_GetLayerByName(poGDS->hDS, "grid_data") != nullptr &&
     794           0 :                 strchr(pszKey, '\'') == nullptr)
     795             :             {
     796             :                 OGRLayerH hSQLLyr;
     797             :                 OGRFeatureH hFeat;
     798             : 
     799             :                 const char *pszSQL =
     800           0 :                     CPLSPrintf("SELECT key_json FROM keymap WHERE "
     801             :                                "key_name = '%s'",
     802             :                                pszKey);
     803           0 :                 CPLDebug("MBTILES", "%s", pszSQL);
     804             :                 hSQLLyr =
     805           0 :                     OGR_DS_ExecuteSQL(poGDS->hDS, pszSQL, nullptr, nullptr);
     806           0 :                 if (hSQLLyr)
     807             :                 {
     808           0 :                     hFeat = OGR_L_GetNextFeature(hSQLLyr);
     809           0 :                     if (hFeat != nullptr &&
     810           0 :                         OGR_F_IsFieldSetAndNotNull(hFeat, 0))
     811             :                     {
     812           0 :                         const char *pszJSon = OGR_F_GetFieldAsString(hFeat, 0);
     813             :                         // CPLDebug("MBTILES", "JSon = %s", pszJSon);
     814             : 
     815           0 :                         osLocationInfo += "<JSon>";
     816             : #ifdef CPLES_XML_BUT_QUOTES
     817             :                         pszXMLEscaped =
     818           0 :                             CPLEscapeString(pszJSon, -1, CPLES_XML_BUT_QUOTES);
     819             : #else
     820             :                         pszXMLEscaped = CPLEscapeString(pszJSon, -1, CPLES_XML);
     821             : #endif
     822           0 :                         osLocationInfo += pszXMLEscaped;
     823           0 :                         CPLFree(pszXMLEscaped);
     824           0 :                         osLocationInfo += "</JSon>";
     825             :                     }
     826           0 :                     OGR_F_Destroy(hFeat);
     827             :                 }
     828           0 :                 OGR_DS_ReleaseResultSet(poGDS->hDS, hSQLLyr);
     829             :             }
     830             : 
     831           0 :             osLocationInfo += "</LocationInfo>";
     832             : 
     833           0 :             CPLFree(pszKey);
     834             : 
     835           0 :             return osLocationInfo.c_str();
     836             :         }
     837             : 
     838           0 :         return nullptr;
     839             :     }
     840             :     else
     841          22 :         return GDALPamRasterBand::GetMetadataItem(pszName, pszDomain);
     842             : }
     843             : 
     844             : /************************************************************************/
     845             : /*                         GetOverviewCount()                           */
     846             : /************************************************************************/
     847             : 
     848           6 : int MBTilesBand::GetOverviewCount()
     849             : {
     850           6 :     MBTilesDataset *poGDS = (MBTilesDataset *)poDS;
     851             : 
     852           6 :     if (poGDS->m_nOverviewCount >= 1)
     853           5 :         return poGDS->m_nOverviewCount;
     854             :     else
     855           1 :         return GDALPamRasterBand::GetOverviewCount();
     856             : }
     857             : 
     858             : /************************************************************************/
     859             : /*                              GetOverview()                           */
     860             : /************************************************************************/
     861             : 
     862           7 : GDALRasterBand *MBTilesBand::GetOverview(int nLevel)
     863             : {
     864           7 :     MBTilesDataset *poGDS = (MBTilesDataset *)poDS;
     865             : 
     866           7 :     if (poGDS->m_nOverviewCount == 0)
     867           0 :         return GDALPamRasterBand::GetOverview(nLevel);
     868             : 
     869           7 :     if (nLevel < 0 || nLevel >= poGDS->m_nOverviewCount)
     870           0 :         return nullptr;
     871             : 
     872           7 :     GDALDataset *poOvrDS = poGDS->m_papoOverviewDS[nLevel];
     873           7 :     if (poOvrDS)
     874           7 :         return poOvrDS->GetRasterBand(nBand);
     875             :     else
     876           0 :         return nullptr;
     877             : }
     878             : 
     879             : /************************************************************************/
     880             : /*                         MBTilesDataset()                          */
     881             : /************************************************************************/
     882             : 
     883         272 : MBTilesDataset::MBTilesDataset()
     884             : {
     885         272 :     m_bWriteBounds = true;
     886         272 :     m_bWriteMinMaxZoom = true;
     887         272 :     poMainDS = nullptr;
     888         272 :     m_nOverviewCount = 0;
     889         272 :     hDS = nullptr;
     890         272 :     m_papoOverviewDS = nullptr;
     891         272 :     bFetchedMetadata = false;
     892         272 :     nHasNonEmptyGrids = -1;
     893         272 :     hDB = nullptr;
     894         272 :     pMyVFS = nullptr;
     895             : 
     896         272 :     m_bGeoTransformValid = false;
     897         272 :     m_adfGeoTransform[0] = 0.0;
     898         272 :     m_adfGeoTransform[1] = 1.0;
     899         272 :     m_adfGeoTransform[2] = 0.0;
     900         272 :     m_adfGeoTransform[3] = 0.0;
     901         272 :     m_adfGeoTransform[4] = 0.0;
     902         272 :     m_adfGeoTransform[5] = 1.0;
     903         272 :     m_bInFlushCache = false;
     904             : 
     905         272 :     m_osRasterTable = "tiles";
     906         272 :     m_eTF = GPKG_TF_PNG;
     907             : 
     908         272 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     909         272 :     m_oSRS.importFromEPSG(3857);
     910         272 : }
     911             : 
     912             : /************************************************************************/
     913             : /*                        ~MBTilesDataset()                             */
     914             : /************************************************************************/
     915             : 
     916         544 : MBTilesDataset::~MBTilesDataset()
     917             : {
     918             :     // Need to explicitly clear it before close hDS
     919         272 :     m_apoLayers.clear();
     920             : 
     921         272 :     FlushCache(true);
     922             : 
     923         272 :     if (poMainDS == nullptr)
     924             :     {
     925          99 :         if (m_papoOverviewDS)
     926             :         {
     927         231 :             for (int i = 0; i < m_nOverviewCount; i++)
     928         173 :                 delete m_papoOverviewDS[i];
     929          58 :             CPLFree(m_papoOverviewDS);
     930             :         }
     931             : 
     932          99 :         if (hDS != nullptr)
     933             :         {
     934          58 :             OGRReleaseDataSource(hDS);
     935          58 :             hDB = nullptr;
     936             :         }
     937          99 :         if (hDB != nullptr)
     938             :         {
     939          41 :             sqlite3_close(hDB);
     940             : 
     941          41 :             if (pMyVFS)
     942             :             {
     943          23 :                 sqlite3_vfs_unregister(pMyVFS);
     944          23 :                 CPLFree(pMyVFS->pAppData);
     945          23 :                 CPLFree(pMyVFS);
     946             :             }
     947             :         }
     948             :     }
     949             : 
     950         272 :     if (!m_osMetadataMemFilename.empty())
     951             :     {
     952          30 :         VSIUnlink(m_osMetadataMemFilename);
     953             :     }
     954         544 : }
     955             : 
     956             : /************************************************************************/
     957             : /*                         IStartTransaction()                          */
     958             : /************************************************************************/
     959             : 
     960          24 : OGRErr MBTilesDataset::IStartTransaction()
     961             : {
     962          24 :     char *pszErrMsg = nullptr;
     963          24 :     const int rc = sqlite3_exec(hDB, "BEGIN", nullptr, nullptr, &pszErrMsg);
     964          24 :     if (rc != SQLITE_OK)
     965             :     {
     966           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s transaction failed: %s",
     967             :                  "BEGIN", pszErrMsg);
     968           0 :         sqlite3_free(pszErrMsg);
     969           0 :         return OGRERR_FAILURE;
     970             :     }
     971             : 
     972          24 :     return OGRERR_NONE;
     973             : }
     974             : 
     975             : /************************************************************************/
     976             : /*                         ICommitTransaction()                         */
     977             : /************************************************************************/
     978             : 
     979          24 : OGRErr MBTilesDataset::ICommitTransaction()
     980             : {
     981          24 :     char *pszErrMsg = nullptr;
     982          24 :     const int rc = sqlite3_exec(hDB, "COMMIT", nullptr, nullptr, &pszErrMsg);
     983          24 :     if (rc != SQLITE_OK)
     984             :     {
     985           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s transaction failed: %s",
     986             :                  "COMMIT", pszErrMsg);
     987           0 :         sqlite3_free(pszErrMsg);
     988           0 :         return OGRERR_FAILURE;
     989             :     }
     990             : 
     991          24 :     return OGRERR_NONE;
     992             : }
     993             : 
     994             : /************************************************************************/
     995             : /*                         ICanIWriteBlock()                            */
     996             : /************************************************************************/
     997             : 
     998         155 : bool MBTilesDataset::ICanIWriteBlock()
     999             : {
    1000         155 :     if (eAccess != GA_Update)
    1001             :     {
    1002           0 :         CPLError(
    1003             :             CE_Failure, CPLE_NotSupported,
    1004             :             "IWriteBlock() not supported on dataset opened in read-only mode");
    1005           0 :         return false;
    1006             :     }
    1007             : 
    1008         155 :     if (!m_bGeoTransformValid)
    1009             :     {
    1010           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1011             :                  "IWriteBlock() not supported if georeferencing not set");
    1012           0 :         return false;
    1013             :     }
    1014         155 :     return true;
    1015             : }
    1016             : 
    1017             : /************************************************************************/
    1018             : /*                         IFlushCacheWithErrCode()                     */
    1019             : /************************************************************************/
    1020             : 
    1021        3160 : CPLErr MBTilesDataset::IFlushCacheWithErrCode(bool bAtClosing)
    1022             : 
    1023             : {
    1024        3160 :     if (m_bInFlushCache)
    1025        2410 :         return CE_None;
    1026         750 :     m_bInFlushCache = true;
    1027             :     // Short circuit GDALPamDataset to avoid serialization to .aux.xml
    1028         750 :     GDALDataset::FlushCache(bAtClosing);
    1029             : 
    1030         750 :     CPLErr eErr = FlushTiles();
    1031             : 
    1032         750 :     m_bInFlushCache = false;
    1033         750 :     return eErr;
    1034             : }
    1035             : 
    1036             : /************************************************************************/
    1037             : /*                         ICanIWriteBlock()                            */
    1038             : /************************************************************************/
    1039             : 
    1040         546 : int MBTilesDataset::GetRowFromIntoTopConvention(int nRow)
    1041             : {
    1042         546 :     return m_nTileMatrixHeight - 1 - nRow;
    1043             : }
    1044             : 
    1045             : /************************************************************************/
    1046             : /*                          GetGeoTransform()                           */
    1047             : /************************************************************************/
    1048             : 
    1049          31 : CPLErr MBTilesDataset::GetGeoTransform(double *padfGeoTransform)
    1050             : {
    1051          31 :     memcpy(padfGeoTransform, m_adfGeoTransform, 6 * sizeof(double));
    1052          31 :     return (m_bGeoTransformValid) ? CE_None : CE_Failure;
    1053             : }
    1054             : 
    1055             : /************************************************************************/
    1056             : /*                     SphericalMercatorToLongLat()                     */
    1057             : /************************************************************************/
    1058             : 
    1059          69 : static void SphericalMercatorToLongLat(double *x, double *y)
    1060             : {
    1061          69 :     double lng = *x / SPHERICAL_RADIUS / M_PI * 180;
    1062          69 :     double lat = 2 * (atan(exp(*y / SPHERICAL_RADIUS)) - M_PI / 4) / M_PI * 180;
    1063          69 :     *x = lng;
    1064          69 :     *y = lat;
    1065          69 : }
    1066             : 
    1067             : /************************************************************************/
    1068             : /*                     LongLatToSphericalMercator()                     */
    1069             : /************************************************************************/
    1070             : 
    1071         108 : static void LongLatToSphericalMercator(double *x, double *y)
    1072             : {
    1073         108 :     double X = SPHERICAL_RADIUS * (*x) / 180 * M_PI;
    1074         108 :     double Y = SPHERICAL_RADIUS * log(tan(M_PI / 4 + 0.5 * (*y) / 180 * M_PI));
    1075         108 :     *x = X;
    1076         108 :     *y = Y;
    1077         108 : }
    1078             : 
    1079             : /************************************************************************/
    1080             : /*                          SetGeoTransform()                           */
    1081             : /************************************************************************/
    1082             : 
    1083          28 : CPLErr MBTilesDataset::SetGeoTransform(double *padfGeoTransform)
    1084             : {
    1085          28 :     if (eAccess != GA_Update)
    1086             :     {
    1087           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    1088             :                  "SetGeoTransform() not supported on read-only dataset");
    1089           1 :         return CE_Failure;
    1090             :     }
    1091          27 :     if (m_bGeoTransformValid)
    1092             :     {
    1093           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    1094             :                  "Cannot modify geotransform once set");
    1095           1 :         return CE_Failure;
    1096             :     }
    1097          26 :     if (padfGeoTransform[2] != 0.0 || padfGeoTransform[4] != 0 ||
    1098          26 :         padfGeoTransform[5] > 0.0)
    1099             :     {
    1100           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    1101             :                  "Only north-up non rotated geotransform supported");
    1102           1 :         return CE_Failure;
    1103             :     }
    1104             : 
    1105          25 :     if (m_bWriteBounds)
    1106             :     {
    1107          46 :         CPLString osBounds(m_osBounds);
    1108          23 :         if (osBounds.empty())
    1109             :         {
    1110          23 :             double minx = padfGeoTransform[0];
    1111          23 :             double miny =
    1112          23 :                 padfGeoTransform[3] + nRasterYSize * padfGeoTransform[5];
    1113          23 :             double maxx =
    1114          23 :                 padfGeoTransform[0] + nRasterXSize * padfGeoTransform[1];
    1115          23 :             double maxy = padfGeoTransform[3];
    1116             : 
    1117          23 :             SphericalMercatorToLongLat(&minx, &miny);
    1118          23 :             SphericalMercatorToLongLat(&maxx, &maxy);
    1119          23 :             if (fabs(minx + 180) < 1e-7)
    1120             :             {
    1121           3 :                 minx = -180.0;
    1122             :             }
    1123          23 :             if (fabs(maxx - 180) < 1e-7)
    1124             :             {
    1125           3 :                 maxx = 180.0;
    1126             :             }
    1127             : 
    1128             :             // Clamp latitude so that when transformed back to EPSG:3857, we
    1129             :             // don't have too big northings
    1130          23 :             double tmpx = 0.0;
    1131          23 :             double ok_maxy = MAX_GM;
    1132          23 :             SphericalMercatorToLongLat(&tmpx, &ok_maxy);
    1133          23 :             if (maxy > ok_maxy)
    1134           0 :                 maxy = ok_maxy;
    1135          23 :             if (miny < -ok_maxy)
    1136           0 :                 miny = -ok_maxy;
    1137             : 
    1138          23 :             osBounds.Printf("%.18g,%.18g,%.18g,%.18g", minx, miny, maxx, maxy);
    1139             :         }
    1140             : 
    1141          23 :         char *pszSQL = sqlite3_mprintf(
    1142             :             "INSERT INTO metadata (name, value) VALUES ('bounds', '%q')",
    1143             :             osBounds.c_str());
    1144          23 :         sqlite3_exec(hDB, pszSQL, nullptr, nullptr, nullptr);
    1145          23 :         sqlite3_free(pszSQL);
    1146             : 
    1147          23 :         if (!m_osCenter.empty())
    1148             :         {
    1149           0 :             pszSQL = sqlite3_mprintf(
    1150             :                 "INSERT INTO metadata (name, value) VALUES ('center', '%q')",
    1151             :                 m_osCenter.c_str());
    1152           0 :             sqlite3_exec(hDB, pszSQL, nullptr, nullptr, nullptr);
    1153           0 :             sqlite3_free(pszSQL);
    1154             :         }
    1155             :     }
    1156             : 
    1157             :     int nBlockXSize;
    1158             :     int nBlockYSize;
    1159          25 :     GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
    1160          25 :     const double dfPixelXSizeZoomLevel0 = 2 * MAX_GM / nBlockXSize;
    1161          25 :     const double dfPixelYSizeZoomLevel0 = 2 * MAX_GM / nBlockYSize;
    1162         181 :     for (m_nZoomLevel = 0; m_nZoomLevel < 25; m_nZoomLevel++)
    1163             :     {
    1164         180 :         double dfExpectedPixelXSize =
    1165         180 :             dfPixelXSizeZoomLevel0 / (1 << m_nZoomLevel);
    1166         180 :         double dfExpectedPixelYSize =
    1167         180 :             dfPixelYSizeZoomLevel0 / (1 << m_nZoomLevel);
    1168         180 :         if (fabs(padfGeoTransform[1] - dfExpectedPixelXSize) <
    1169         180 :                 1e-8 * dfExpectedPixelXSize &&
    1170          24 :             fabs(fabs(padfGeoTransform[5]) - dfExpectedPixelYSize) <
    1171          24 :                 1e-8 * dfExpectedPixelYSize)
    1172             :         {
    1173          24 :             break;
    1174             :         }
    1175             :     }
    1176          25 :     if (m_nZoomLevel == 25)
    1177             :     {
    1178           1 :         m_nZoomLevel = -1;
    1179           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    1180             :                  "Could not find an appropriate zoom level that matches raster "
    1181             :                  "pixel size");
    1182           1 :         return CE_Failure;
    1183             :     }
    1184             : 
    1185          24 :     memcpy(m_adfGeoTransform, padfGeoTransform, 6 * sizeof(double));
    1186          24 :     m_bGeoTransformValid = true;
    1187             : 
    1188          24 :     return FinalizeRasterRegistration();
    1189             : }
    1190             : 
    1191             : /************************************************************************/
    1192             : /*                      ComputeTileAndPixelShifts()                     */
    1193             : /************************************************************************/
    1194             : 
    1195         255 : void MBTilesDataset::ComputeTileAndPixelShifts()
    1196             : {
    1197             :     int nTileWidth, nTileHeight;
    1198         255 :     GetRasterBand(1)->GetBlockSize(&nTileWidth, &nTileHeight);
    1199             : 
    1200             :     // Compute shift between GDAL origin and TileMatrixSet origin
    1201             :     // Caution this is in GeoPackage / WMTS convention ! That is upper-left
    1202             :     // corner
    1203         255 :     int nShiftXPixels = (int)floor(0.5 + (m_adfGeoTransform[0] - TMS_ORIGIN_X) /
    1204         255 :                                              m_adfGeoTransform[1]);
    1205         255 :     m_nShiftXTiles = (int)floor(1.0 * nShiftXPixels / nTileWidth);
    1206         255 :     m_nShiftXPixelsMod =
    1207         255 :         ((nShiftXPixels % nTileWidth) + nTileWidth) % nTileWidth;
    1208         255 :     int nShiftYPixels = (int)floor(0.5 + (m_adfGeoTransform[3] - TMS_ORIGIN_Y) /
    1209         255 :                                              m_adfGeoTransform[5]);
    1210         255 :     m_nShiftYTiles = (int)floor(1.0 * nShiftYPixels / nTileHeight);
    1211         255 :     m_nShiftYPixelsMod =
    1212         255 :         ((nShiftYPixels % nTileHeight) + nTileHeight) % nTileHeight;
    1213         255 : }
    1214             : 
    1215             : /************************************************************************/
    1216             : /*                      FinalizeRasterRegistration()                    */
    1217             : /************************************************************************/
    1218             : 
    1219          24 : CPLErr MBTilesDataset::FinalizeRasterRegistration()
    1220             : {
    1221          24 :     m_nTileMatrixWidth = (1 << m_nZoomLevel);
    1222          24 :     m_nTileMatrixHeight = (1 << m_nZoomLevel);
    1223             : 
    1224          24 :     ComputeTileAndPixelShifts();
    1225             : 
    1226          24 :     double dfGDALMinX = m_adfGeoTransform[0];
    1227          24 :     double dfGDALMinY =
    1228          24 :         m_adfGeoTransform[3] + nRasterYSize * m_adfGeoTransform[5];
    1229          24 :     double dfGDALMaxX =
    1230          24 :         m_adfGeoTransform[0] + nRasterXSize * m_adfGeoTransform[1];
    1231          24 :     double dfGDALMaxY = m_adfGeoTransform[3];
    1232             : 
    1233          24 :     m_nOverviewCount = m_nZoomLevel;
    1234          48 :     m_papoOverviewDS = (MBTilesDataset **)CPLCalloc(sizeof(MBTilesDataset *),
    1235          24 :                                                     m_nOverviewCount);
    1236             : 
    1237          24 :     if (m_bWriteMinMaxZoom)
    1238             :     {
    1239          24 :         char *pszSQL = sqlite3_mprintf(
    1240             :             "INSERT INTO metadata (name, value) VALUES ('minzoom', '%d')",
    1241             :             m_nZoomLevel);
    1242          24 :         sqlite3_exec(hDB, pszSQL, nullptr, nullptr, nullptr);
    1243          24 :         sqlite3_free(pszSQL);
    1244          24 :         pszSQL = sqlite3_mprintf(
    1245             :             "INSERT INTO metadata (name, value) VALUES ('maxzoom', '%d')",
    1246             :             m_nZoomLevel);
    1247          24 :         sqlite3_exec(hDB, pszSQL, nullptr, nullptr, nullptr);
    1248          24 :         sqlite3_free(pszSQL);
    1249             :     }
    1250             : 
    1251         155 :     for (int i = 0; i < m_nOverviewCount; i++)
    1252             :     {
    1253         131 :         MBTilesDataset *poOvrDS = new MBTilesDataset();
    1254         131 :         poOvrDS->ShareLockWithParentDataset(this);
    1255             :         int nBlockSize;
    1256         131 :         GetRasterBand(1)->GetBlockSize(&nBlockSize, &nBlockSize);
    1257         131 :         poOvrDS->InitRaster(this, i, nBands, nBlockSize, dfGDALMinX, dfGDALMinY,
    1258             :                             dfGDALMaxX, dfGDALMaxY);
    1259             : 
    1260         131 :         m_papoOverviewDS[m_nZoomLevel - 1 - i] = poOvrDS;
    1261             :     }
    1262             : 
    1263          24 :     return CE_None;
    1264             : }
    1265             : 
    1266             : /************************************************************************/
    1267             : /*                         InitRaster()                                 */
    1268             : /************************************************************************/
    1269             : 
    1270         231 : bool MBTilesDataset::InitRaster(MBTilesDataset *poParentDS, int nZoomLevel,
    1271             :                                 int nBandCount, int nTileSize,
    1272             :                                 double dfGDALMinX, double dfGDALMinY,
    1273             :                                 double dfGDALMaxX, double dfGDALMaxY)
    1274             : {
    1275         231 :     m_nZoomLevel = nZoomLevel;
    1276         231 :     m_nTileMatrixWidth = 1 << nZoomLevel;
    1277         231 :     m_nTileMatrixHeight = 1 << nZoomLevel;
    1278             : 
    1279         231 :     const int nTileWidth = nTileSize;
    1280         231 :     const int nTileHeight = nTileSize;
    1281         231 :     const double dfPixelXSize = 2 * MAX_GM / nTileWidth / (1 << nZoomLevel);
    1282         231 :     const double dfPixelYSize = 2 * MAX_GM / nTileHeight / (1 << nZoomLevel);
    1283             : 
    1284         231 :     m_bGeoTransformValid = true;
    1285         231 :     m_adfGeoTransform[0] = dfGDALMinX;
    1286         231 :     m_adfGeoTransform[1] = dfPixelXSize;
    1287         231 :     m_adfGeoTransform[3] = dfGDALMaxY;
    1288         231 :     m_adfGeoTransform[5] = -dfPixelYSize;
    1289         231 :     double dfRasterXSize = 0.5 + (dfGDALMaxX - dfGDALMinX) / dfPixelXSize;
    1290         231 :     double dfRasterYSize = 0.5 + (dfGDALMaxY - dfGDALMinY) / dfPixelYSize;
    1291         231 :     if (dfRasterXSize > INT_MAX || dfRasterYSize > INT_MAX)
    1292           0 :         return false;
    1293         231 :     nRasterXSize = (int)dfRasterXSize;
    1294         231 :     nRasterYSize = (int)dfRasterYSize;
    1295             : 
    1296         231 :     m_pabyCachedTiles =
    1297         231 :         (GByte *)VSI_MALLOC3_VERBOSE(4 * 4, nTileWidth, nTileHeight);
    1298         231 :     if (m_pabyCachedTiles == nullptr)
    1299             :     {
    1300           0 :         return false;
    1301             :     }
    1302             : 
    1303         231 :     if (poParentDS)
    1304             :     {
    1305         173 :         eAccess = poParentDS->eAccess;
    1306             :     }
    1307             : 
    1308         894 :     for (int i = 1; i <= nBandCount; i++)
    1309         663 :         SetBand(i, new MBTilesBand(this, nTileSize));
    1310             : 
    1311         231 :     ComputeTileAndPixelShifts();
    1312             : 
    1313         231 :     GDALDataset::SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
    1314         231 :     GDALDataset::SetMetadataItem("ZOOM_LEVEL", CPLSPrintf("%d", m_nZoomLevel));
    1315             : 
    1316         231 :     if (poParentDS)
    1317             :     {
    1318         173 :         m_poParentDS = poParentDS;
    1319         173 :         poMainDS = poParentDS;
    1320         173 :         hDS = poParentDS->hDS;
    1321         173 :         hDB = poParentDS->hDB;
    1322         173 :         m_eTF = poParentDS->m_eTF;
    1323         173 :         m_nQuality = poParentDS->m_nQuality;
    1324         173 :         m_nZLevel = poParentDS->m_nZLevel;
    1325         173 :         m_bDither = poParentDS->m_bDither;
    1326         173 :         m_osWHERE = poParentDS->m_osWHERE;
    1327         173 :         SetDescription(CPLSPrintf("%s - zoom_level=%d",
    1328         173 :                                   poParentDS->GetDescription(), m_nZoomLevel));
    1329             :     }
    1330             : 
    1331         231 :     return true;
    1332             : }
    1333             : 
    1334             : /************************************************************************/
    1335             : /*                         GetSpatialRef()                              */
    1336             : /************************************************************************/
    1337             : 
    1338           3 : const OGRSpatialReference *MBTilesDataset::GetSpatialRef() const
    1339             : {
    1340           3 :     return &m_oSRS;
    1341             : }
    1342             : 
    1343             : /************************************************************************/
    1344             : /*                           SetSpatialRef()                            */
    1345             : /************************************************************************/
    1346             : 
    1347           3 : CPLErr MBTilesDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
    1348             : {
    1349           3 :     if (eAccess != GA_Update)
    1350             :     {
    1351           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    1352             :                  "SetSpatialRef() not supported on read-only dataset");
    1353           1 :         return CE_Failure;
    1354             :     }
    1355             : 
    1356           2 :     if (poSRS == nullptr || poSRS->GetAuthorityName(nullptr) == nullptr ||
    1357           1 :         !EQUAL(poSRS->GetAuthorityName(nullptr), "EPSG") ||
    1358           5 :         poSRS->GetAuthorityCode(nullptr) == nullptr ||
    1359           1 :         !EQUAL(poSRS->GetAuthorityCode(nullptr), "3857"))
    1360             :     {
    1361           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    1362             :                  "Only EPSG:3857 supported on MBTiles dataset");
    1363           1 :         return CE_Failure;
    1364             :     }
    1365           1 :     return CE_None;
    1366             : }
    1367             : 
    1368             : /************************************************************************/
    1369             : /*                      GetMetadataDomainList()                         */
    1370             : /************************************************************************/
    1371             : 
    1372           0 : char **MBTilesDataset::GetMetadataDomainList()
    1373             : {
    1374           0 :     return BuildMetadataDomainList(GDALDataset::GetMetadataDomainList(), TRUE,
    1375           0 :                                    "", nullptr);
    1376             : }
    1377             : 
    1378             : /************************************************************************/
    1379             : /*                            GetMetadata()                             */
    1380             : /************************************************************************/
    1381             : 
    1382         102 : char **MBTilesDataset::GetMetadata(const char *pszDomain)
    1383             : {
    1384         102 :     if (hDS == nullptr || (pszDomain != nullptr && !EQUAL(pszDomain, "")))
    1385          29 :         return GDALPamDataset::GetMetadata(pszDomain);
    1386             : 
    1387          73 :     if (bFetchedMetadata)
    1388          15 :         return aosList.List();
    1389             : 
    1390          58 :     bFetchedMetadata = true;
    1391          58 :     aosList = CPLStringList(GDALPamDataset::GetMetadata(), FALSE);
    1392             : 
    1393          58 :     OGRLayerH hSQLLyr = OGR_DS_ExecuteSQL(
    1394             :         hDS, "SELECT name, value FROM metadata WHERE name != 'json' LIMIT 1000",
    1395             :         nullptr, nullptr);
    1396          58 :     if (hSQLLyr == nullptr)
    1397           0 :         return nullptr;
    1398             : 
    1399          58 :     if (OGR_FD_GetFieldCount(OGR_L_GetLayerDefn(hSQLLyr)) != 2)
    1400             :     {
    1401           0 :         OGR_DS_ReleaseResultSet(hDS, hSQLLyr);
    1402           0 :         return nullptr;
    1403             :     }
    1404             : 
    1405             :     OGRFeatureH hFeat;
    1406         555 :     while ((hFeat = OGR_L_GetNextFeature(hSQLLyr)) != nullptr)
    1407             :     {
    1408         994 :         if (OGR_F_IsFieldSetAndNotNull(hFeat, 0) &&
    1409         497 :             OGR_F_IsFieldSetAndNotNull(hFeat, 1))
    1410             :         {
    1411         994 :             CPLString osName = OGR_F_GetFieldAsString(hFeat, 0);
    1412         994 :             CPLString osValue = OGR_F_GetFieldAsString(hFeat, 1);
    1413         994 :             if (osName[0] != '\0' && !STARTS_WITH(osValue, "function(") &&
    1414         497 :                 strstr(osValue, "<img ") == nullptr &&
    1415         497 :                 strstr(osValue, "<p>") == nullptr &&
    1416        1491 :                 strstr(osValue, "</p>") == nullptr &&
    1417         497 :                 strstr(osValue, "<div") == nullptr)
    1418             :             {
    1419         497 :                 aosList.AddNameValue(osName, osValue);
    1420             :             }
    1421             :         }
    1422         497 :         OGR_F_Destroy(hFeat);
    1423             :     }
    1424          58 :     OGR_DS_ReleaseResultSet(hDS, hSQLLyr);
    1425             : 
    1426          58 :     return aosList.List();
    1427             : }
    1428             : 
    1429             : /************************************************************************/
    1430             : /*                         GetMetadataItem()                            */
    1431             : /************************************************************************/
    1432             : 
    1433          87 : const char *MBTilesDataset::GetMetadataItem(const char *pszName,
    1434             :                                             const char *pszDomain)
    1435             : {
    1436          87 :     if (pszDomain == nullptr || EQUAL(pszDomain, ""))
    1437             :     {
    1438          81 :         const char *pszValue = CSLFetchNameValue(GetMetadata(), pszName);
    1439          81 :         if (pszValue)
    1440          63 :             return pszValue;
    1441             :     }
    1442          24 :     return GDALPamDataset::GetMetadataItem(pszName, pszDomain);
    1443             : }
    1444             : 
    1445             : /************************************************************************/
    1446             : /*                              GetLayer()                              */
    1447             : /************************************************************************/
    1448             : 
    1449          57 : OGRLayer *MBTilesDataset::GetLayer(int iLayer)
    1450             : 
    1451             : {
    1452          57 :     if (iLayer < 0 || iLayer >= GetLayerCount())
    1453           2 :         return nullptr;
    1454          55 :     return m_apoLayers[iLayer].get();
    1455             : }
    1456             : 
    1457             : /************************************************************************/
    1458             : /*                        MBTilesVectorLayer()                          */
    1459             : /************************************************************************/
    1460             : 
    1461          35 : MBTilesVectorLayer::MBTilesVectorLayer(
    1462             :     MBTilesDataset *poDS, const char *pszLayerName,
    1463             :     const CPLJSONObject &oFields, const CPLJSONArray &oAttributesFromTileStats,
    1464             :     bool bJsonField, double dfMinX, double dfMinY, double dfMaxX, double dfMaxY,
    1465          35 :     OGRwkbGeometryType eGeomType, bool bZoomLevelFromSpatialFilter)
    1466          35 :     : m_poDS(poDS), m_poFeatureDefn(new OGRFeatureDefn(pszLayerName)),
    1467          70 :       m_bJsonField(bJsonField)
    1468             : {
    1469          35 :     SetDescription(pszLayerName);
    1470          35 :     m_poFeatureDefn->SetGeomType(eGeomType);
    1471          35 :     OGRSpatialReference *poSRS = new OGRSpatialReference();
    1472          35 :     poSRS->SetFromUserInput(SRS_EPSG_3857);
    1473          35 :     m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
    1474          35 :     poSRS->Release();
    1475          35 :     m_poFeatureDefn->Reference();
    1476             : 
    1477          35 :     if (m_bJsonField)
    1478             :     {
    1479           2 :         OGRFieldDefn oFieldDefnId("mvt_id", OFTInteger64);
    1480           1 :         m_poFeatureDefn->AddFieldDefn(&oFieldDefnId);
    1481             :     }
    1482             :     else
    1483             :     {
    1484          34 :         OGRMVTInitFields(m_poFeatureDefn, oFields, oAttributesFromTileStats);
    1485             :     }
    1486             : 
    1487          35 :     m_sExtent.MinX = dfMinX;
    1488          35 :     m_sExtent.MinY = dfMinY;
    1489          35 :     m_sExtent.MaxX = dfMaxX;
    1490          35 :     m_sExtent.MaxY = dfMaxY;
    1491             : 
    1492          35 :     m_nZoomLevel = m_poDS->m_nZoomLevel;
    1493          35 :     m_bZoomLevelAuto = bZoomLevelFromSpatialFilter;
    1494          35 :     MBTilesVectorLayer::SetSpatialFilter(nullptr);
    1495             : 
    1496             :     // If the metadata contains an empty fields object, this may be a sign
    1497             :     // that it doesn't know the schema. In that case check if a tile has
    1498             :     // attributes, and in that case create a json field.
    1499          35 :     if (!m_bJsonField && oFields.IsValid() && oFields.GetChildren().empty())
    1500             :     {
    1501           7 :         m_bJsonField = true;
    1502           7 :         OGRFeature *poSrcFeature = GetNextSrcFeature();
    1503           7 :         m_bJsonField = false;
    1504             : 
    1505           7 :         if (poSrcFeature)
    1506             :         {
    1507             :             // There is at least the mvt_id field
    1508           7 :             if (poSrcFeature->GetFieldCount() > 1)
    1509             :             {
    1510           1 :                 m_bJsonField = true;
    1511             :             }
    1512           7 :             delete poSrcFeature;
    1513             :         }
    1514           7 :         MBTilesVectorLayer::ResetReading();
    1515             :     }
    1516             : 
    1517          35 :     if (m_bJsonField)
    1518             :     {
    1519           4 :         OGRFieldDefn oFieldDefn("json", OFTString);
    1520           2 :         m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
    1521             :     }
    1522          35 : }
    1523             : 
    1524             : /************************************************************************/
    1525             : /*                       ~MBTilesVectorLayer()                          */
    1526             : /************************************************************************/
    1527             : 
    1528          70 : MBTilesVectorLayer::~MBTilesVectorLayer()
    1529             : {
    1530          35 :     m_poFeatureDefn->Release();
    1531          35 :     if (m_hTileIteratorLyr)
    1532          13 :         OGR_DS_ReleaseResultSet(m_poDS->hDS, m_hTileIteratorLyr);
    1533          35 :     if (!m_osTmpFilename.empty())
    1534             :     {
    1535          13 :         VSIUnlink(m_osTmpFilename);
    1536             :     }
    1537          35 :     if (m_hTileDS)
    1538           9 :         GDALClose(m_hTileDS);
    1539          70 : }
    1540             : 
    1541             : /************************************************************************/
    1542             : /*                           TestCapability()                           */
    1543             : /************************************************************************/
    1544             : 
    1545          35 : int MBTilesVectorLayer::TestCapability(const char *pszCap)
    1546             : {
    1547          35 :     if (EQUAL(pszCap, OLCStringsAsUTF8) ||
    1548          23 :         EQUAL(pszCap, OLCFastSpatialFilter) || EQUAL(pszCap, OLCFastGetExtent))
    1549             :     {
    1550          14 :         return TRUE;
    1551             :     }
    1552          21 :     return FALSE;
    1553             : }
    1554             : 
    1555             : /************************************************************************/
    1556             : /*                             GetExtent()                              */
    1557             : /************************************************************************/
    1558             : 
    1559           4 : OGRErr MBTilesVectorLayer::GetExtent(OGREnvelope *psExtent, int)
    1560             : {
    1561           4 :     *psExtent = m_sExtent;
    1562           4 :     return OGRERR_NONE;
    1563             : }
    1564             : 
    1565             : /************************************************************************/
    1566             : /*                          ResetReading()                              */
    1567             : /************************************************************************/
    1568             : 
    1569         106 : void MBTilesVectorLayer::ResetReading()
    1570             : {
    1571         106 :     if (m_hTileDS)
    1572          43 :         GDALClose(m_hTileDS);
    1573         106 :     m_hTileDS = nullptr;
    1574         106 :     m_bEOF = false;
    1575         106 :     if (m_hTileIteratorLyr)
    1576          93 :         OGR_DS_ReleaseResultSet(m_poDS->hDS, m_hTileIteratorLyr);
    1577         106 :     CPLString osSQL;
    1578             :     osSQL.Printf("SELECT tile_column, tile_row, tile_data FROM tiles "
    1579             :                  "WHERE zoom_level = %d "
    1580             :                  "AND tile_column BETWEEN %d AND %d "
    1581             :                  "AND tile_row BETWEEN %d AND %d",
    1582             :                  m_nZoomLevel, m_nFilterMinX, m_nFilterMaxX, m_nFilterMinY,
    1583         106 :                  m_nFilterMaxY);
    1584         106 :     m_hTileIteratorLyr =
    1585         106 :         OGR_DS_ExecuteSQL(m_poDS->hDS, osSQL.c_str(), nullptr, nullptr);
    1586         106 : }
    1587             : 
    1588             : /************************************************************************/
    1589             : /*                         SetSpatialFilter()                           */
    1590             : /************************************************************************/
    1591             : 
    1592          60 : void MBTilesVectorLayer::SetSpatialFilter(OGRGeometry *poGeomIn)
    1593             : {
    1594          60 :     OGRLayer::SetSpatialFilter(poGeomIn);
    1595             : 
    1596          60 :     if (m_poFilterGeom != nullptr && m_sFilterEnvelope.MinX <= -MAX_GM &&
    1597           2 :         m_sFilterEnvelope.MinY <= -MAX_GM && m_sFilterEnvelope.MaxX >= MAX_GM &&
    1598           2 :         m_sFilterEnvelope.MaxY >= MAX_GM)
    1599             :     {
    1600           2 :         if (m_bZoomLevelAuto)
    1601             :         {
    1602           0 :             m_nZoomLevel = m_poDS->m_nMinZoomLevel;
    1603             :         }
    1604           2 :         m_nFilterMinX = 0;
    1605           2 :         m_nFilterMinY = 0;
    1606           2 :         m_nFilterMaxX = (1 << m_nZoomLevel) - 1;
    1607           2 :         m_nFilterMaxY = (1 << m_nZoomLevel) - 1;
    1608             :     }
    1609          58 :     else if (m_poFilterGeom != nullptr &&
    1610           6 :              m_sFilterEnvelope.MinX >= -10 * MAX_GM &&
    1611           6 :              m_sFilterEnvelope.MinY >= -10 * MAX_GM &&
    1612           6 :              m_sFilterEnvelope.MaxX <= 10 * MAX_GM &&
    1613           6 :              m_sFilterEnvelope.MaxY <= 10 * MAX_GM)
    1614             :     {
    1615           6 :         if (m_bZoomLevelAuto)
    1616             :         {
    1617             :             double dfExtent =
    1618           0 :                 std::min(m_sFilterEnvelope.MaxX - m_sFilterEnvelope.MinX,
    1619           0 :                          m_sFilterEnvelope.MaxY - m_sFilterEnvelope.MinY);
    1620           0 :             m_nZoomLevel = std::max(
    1621           0 :                 m_poDS->m_nMinZoomLevel,
    1622           0 :                 std::min(static_cast<int>(0.5 + log(2 * MAX_GM / dfExtent) /
    1623             :                                                     log(2.0)),
    1624           0 :                          m_poDS->m_nZoomLevel));
    1625           0 :             CPLDebug("MBTILES", "Zoom level = %d", m_nZoomLevel);
    1626             :         }
    1627           6 :         const double dfTileDim = 2 * MAX_GM / (1 << m_nZoomLevel);
    1628           6 :         m_nFilterMinX =
    1629          12 :             std::max(0, static_cast<int>(floor(
    1630           6 :                             (m_sFilterEnvelope.MinX + MAX_GM) / dfTileDim)));
    1631           6 :         m_nFilterMinY =
    1632          12 :             std::max(0, static_cast<int>(floor(
    1633           6 :                             (m_sFilterEnvelope.MinY + MAX_GM) / dfTileDim)));
    1634           6 :         m_nFilterMaxX =
    1635          12 :             std::min(static_cast<int>(
    1636           6 :                          ceil((m_sFilterEnvelope.MaxX + MAX_GM) / dfTileDim)),
    1637           6 :                      (1 << m_nZoomLevel) - 1);
    1638           6 :         m_nFilterMaxY =
    1639          12 :             std::min(static_cast<int>(
    1640           6 :                          ceil((m_sFilterEnvelope.MaxY + MAX_GM) / dfTileDim)),
    1641           6 :                      (1 << m_nZoomLevel) - 1);
    1642             :     }
    1643             :     else
    1644             :     {
    1645          52 :         if (m_bZoomLevelAuto)
    1646             :         {
    1647           0 :             m_nZoomLevel = m_poDS->m_nZoomLevel;
    1648             :         }
    1649          52 :         m_nFilterMinX = 0;
    1650          52 :         m_nFilterMinY = 0;
    1651          52 :         m_nFilterMaxX = (1 << m_nZoomLevel) - 1;
    1652          52 :         m_nFilterMaxY = (1 << m_nZoomLevel) - 1;
    1653             :     }
    1654          60 : }
    1655             : 
    1656             : /************************************************************************/
    1657             : /*                          GetNextFeature()                            */
    1658             : /************************************************************************/
    1659             : 
    1660         147 : OGRFeature *MBTilesVectorLayer::GetNextFeature()
    1661             : {
    1662             :     while (true)
    1663             :     {
    1664         147 :         OGRFeature *poFeature = GetNextRawFeature();
    1665         147 :         if (poFeature == nullptr)
    1666          28 :             return nullptr;
    1667             : 
    1668         280 :         if ((m_poFilterGeom == nullptr ||
    1669         225 :              FilterGeometry(poFeature->GetGeometryRef())) &&
    1670         106 :             (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
    1671             :         {
    1672          78 :             return poFeature;
    1673             :         }
    1674             : 
    1675          41 :         delete poFeature;
    1676          41 :     }
    1677             : }
    1678             : 
    1679             : /************************************************************************/
    1680             : /*                         GetFeatureCount()                            */
    1681             : /************************************************************************/
    1682             : 
    1683          15 : GIntBig MBTilesVectorLayer::GetFeatureCount(int bForce)
    1684             : {
    1685          15 :     if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr)
    1686             :     {
    1687           9 :         if (m_nFeatureCount < 0)
    1688             :         {
    1689           1 :             m_nFeatureCount = 0;
    1690           1 :             ResetReading();
    1691           5 :             while (m_hTileIteratorLyr != nullptr)
    1692             :             {
    1693           5 :                 OGRFeatureH hFeat = OGR_L_GetNextFeature(m_hTileIteratorLyr);
    1694           5 :                 if (hFeat == nullptr)
    1695             :                 {
    1696           1 :                     break;
    1697             :                 }
    1698           4 :                 m_nX = OGR_F_GetFieldAsInteger(hFeat, 0);
    1699             :                 // MBTiles y origin is bottom based, whereas MVT directory
    1700             :                 // is top based
    1701           4 :                 m_nY =
    1702           4 :                     (1 << m_nZoomLevel) - 1 - OGR_F_GetFieldAsInteger(hFeat, 1);
    1703           4 :                 int nDataSize = 0;
    1704           4 :                 GByte *pabyData = OGR_F_GetFieldAsBinary(hFeat, 2, &nDataSize);
    1705           4 :                 GByte *pabyDataDup = static_cast<GByte *>(CPLMalloc(nDataSize));
    1706           4 :                 memcpy(pabyDataDup, pabyData, nDataSize);
    1707           4 :                 OGR_F_Destroy(hFeat);
    1708             : 
    1709           4 :                 if (!m_osTmpFilename.empty())
    1710             :                 {
    1711           3 :                     VSIUnlink(m_osTmpFilename);
    1712             :                 }
    1713             :                 m_osTmpFilename =
    1714           4 :                     CPLSPrintf("/vsimem/mvt_%p_%d_%d.pbf", this, m_nX, m_nY);
    1715           4 :                 VSIFCloseL(VSIFileFromMemBuffer(m_osTmpFilename, pabyDataDup,
    1716             :                                                 nDataSize, true));
    1717             : 
    1718           4 :                 const char *l_apszAllowedDrivers[] = {"MVT", nullptr};
    1719           4 :                 if (m_hTileDS)
    1720           0 :                     GDALClose(m_hTileDS);
    1721           4 :                 char **papszOpenOptions = nullptr;
    1722             :                 papszOpenOptions =
    1723           4 :                     CSLSetNameValue(papszOpenOptions, "METADATA_FILE",
    1724           4 :                                     m_poDS->m_osMetadataMemFilename.c_str());
    1725           4 :                 m_hTileDS =
    1726           4 :                     GDALOpenEx(("MVT:" + m_osTmpFilename).c_str(),
    1727             :                                GDAL_OF_VECTOR | GDAL_OF_INTERNAL,
    1728             :                                l_apszAllowedDrivers, papszOpenOptions, nullptr);
    1729           4 :                 CSLDestroy(papszOpenOptions);
    1730           4 :                 if (m_hTileDS)
    1731             :                 {
    1732             :                     OGRLayerH hLayer =
    1733           4 :                         GDALDatasetGetLayerByName(m_hTileDS, GetName());
    1734           4 :                     if (hLayer)
    1735             :                     {
    1736           4 :                         m_nFeatureCount += OGR_L_GetFeatureCount(hLayer, true);
    1737             :                     }
    1738           4 :                     GDALClose(m_hTileDS);
    1739           4 :                     m_hTileDS = nullptr;
    1740             :                 }
    1741             :             }
    1742           1 :             ResetReading();
    1743             :         }
    1744           9 :         return m_nFeatureCount;
    1745             :     }
    1746           6 :     return OGRLayer::GetFeatureCount(bForce);
    1747             : }
    1748             : 
    1749             : /************************************************************************/
    1750             : /*                        GetNextSrcFeature()                           */
    1751             : /************************************************************************/
    1752             : 
    1753         154 : OGRFeature *MBTilesVectorLayer::GetNextSrcFeature()
    1754             : {
    1755         154 :     if (m_bEOF)
    1756             :     {
    1757           1 :         return nullptr;
    1758             :     }
    1759         153 :     if (m_hTileIteratorLyr == nullptr)
    1760             :     {
    1761          12 :         ResetReading();
    1762          12 :         if (m_hTileIteratorLyr == nullptr)
    1763             :         {
    1764           0 :             return nullptr;
    1765             :         }
    1766             :     }
    1767             : 
    1768         153 :     OGRFeatureH hTileFeat = nullptr;
    1769         254 :     if (m_hTileDS == nullptr ||
    1770         101 :         (hTileFeat = OGR_L_GetNextFeature(
    1771         101 :              GDALDatasetGetLayerByName(m_hTileDS, GetName()))) == nullptr)
    1772             :     {
    1773             :         while (true)
    1774             :         {
    1775         163 :             OGRFeatureH hFeat = OGR_L_GetNextFeature(m_hTileIteratorLyr);
    1776         163 :             if (hFeat == nullptr)
    1777             :             {
    1778          27 :                 m_bEOF = true;
    1779          27 :                 return nullptr;
    1780             :             }
    1781         136 :             m_nX = OGR_F_GetFieldAsInteger(hFeat, 0);
    1782             :             // MBTiles y origin is bottom based, whereas MVT directory
    1783             :             // is top based
    1784         136 :             m_nY = (1 << m_nZoomLevel) - 1 - OGR_F_GetFieldAsInteger(hFeat, 1);
    1785         136 :             CPLDebug("MBTiles", "X=%d, Y=%d", m_nX, m_nY);
    1786             : 
    1787         136 :             int nDataSize = 0;
    1788         136 :             GByte *pabyData = OGR_F_GetFieldAsBinary(hFeat, 2, &nDataSize);
    1789         136 :             GByte *pabyDataDup = static_cast<GByte *>(CPLMalloc(nDataSize));
    1790         136 :             memcpy(pabyDataDup, pabyData, nDataSize);
    1791         136 :             OGR_F_Destroy(hFeat);
    1792             : 
    1793         136 :             if (!m_osTmpFilename.empty())
    1794             :             {
    1795         124 :                 VSIUnlink(m_osTmpFilename);
    1796             :             }
    1797             :             m_osTmpFilename =
    1798         136 :                 CPLSPrintf("/vsimem/mvt_%p_%d_%d.pbf", this, m_nX, m_nY);
    1799         136 :             VSIFCloseL(VSIFileFromMemBuffer(m_osTmpFilename, pabyDataDup,
    1800             :                                             nDataSize, true));
    1801             : 
    1802         136 :             const char *l_apszAllowedDrivers[] = {"MVT", nullptr};
    1803         136 :             if (m_hTileDS)
    1804          72 :                 GDALClose(m_hTileDS);
    1805         136 :             char **papszOpenOptions = nullptr;
    1806             :             papszOpenOptions =
    1807         136 :                 CSLSetNameValue(papszOpenOptions, "X", CPLSPrintf("%d", m_nX));
    1808             :             papszOpenOptions =
    1809         136 :                 CSLSetNameValue(papszOpenOptions, "Y", CPLSPrintf("%d", m_nY));
    1810         136 :             papszOpenOptions = CSLSetNameValue(papszOpenOptions, "Z",
    1811             :                                                CPLSPrintf("%d", m_nZoomLevel));
    1812         136 :             papszOpenOptions = CSLSetNameValue(
    1813             :                 papszOpenOptions, "METADATA_FILE",
    1814         136 :                 m_bJsonField ? "" : m_poDS->m_osMetadataMemFilename.c_str());
    1815         136 :             if (!m_poDS->m_osClip.empty())
    1816             :             {
    1817             :                 papszOpenOptions =
    1818           3 :                     CSLSetNameValue(papszOpenOptions, "CLIP", m_poDS->m_osClip);
    1819             :             }
    1820         136 :             m_hTileDS =
    1821         136 :                 GDALOpenEx(("MVT:" + m_osTmpFilename).c_str(),
    1822             :                            GDAL_OF_VECTOR | GDAL_OF_INTERNAL,
    1823             :                            l_apszAllowedDrivers, papszOpenOptions, nullptr);
    1824         136 :             CSLDestroy(papszOpenOptions);
    1825         136 :             if (m_hTileDS)
    1826             :             {
    1827         136 :                 if (GDALDatasetGetLayerByName(m_hTileDS, GetName()))
    1828             :                 {
    1829         128 :                     hTileFeat = OGR_L_GetNextFeature(
    1830         128 :                         GDALDatasetGetLayerByName(m_hTileDS, GetName()));
    1831         128 :                     if (hTileFeat)
    1832         124 :                         break;
    1833             :                 }
    1834          12 :                 GDALClose(m_hTileDS);
    1835          12 :                 m_hTileDS = nullptr;
    1836             :             }
    1837          12 :         }
    1838             :     }
    1839             : 
    1840         126 :     return reinterpret_cast<OGRFeature *>(hTileFeat);
    1841             : }
    1842             : 
    1843             : /************************************************************************/
    1844             : /*                         CreateFeatureFrom()                          */
    1845             : /************************************************************************/
    1846             : 
    1847         121 : OGRFeature *MBTilesVectorLayer::CreateFeatureFrom(OGRFeature *poSrcFeature)
    1848             : {
    1849             : 
    1850         121 :     return OGRMVTCreateFeatureFrom(poSrcFeature, m_poFeatureDefn, m_bJsonField,
    1851         242 :                                    GetSpatialRef());
    1852             : }
    1853             : 
    1854             : /************************************************************************/
    1855             : /*                        GetNextRawFeature()                           */
    1856             : /************************************************************************/
    1857             : 
    1858         147 : OGRFeature *MBTilesVectorLayer::GetNextRawFeature()
    1859             : {
    1860         147 :     OGRFeature *poSrcFeat = GetNextSrcFeature();
    1861         147 :     if (poSrcFeat == nullptr)
    1862          28 :         return nullptr;
    1863             : 
    1864         119 :     const GIntBig nFIDBase =
    1865         119 :         (static_cast<GIntBig>(m_nY) << m_nZoomLevel) | m_nX;
    1866         119 :     OGRFeature *poFeature = CreateFeatureFrom(poSrcFeat);
    1867         119 :     poFeature->SetFID((poSrcFeat->GetFID() << (2 * m_nZoomLevel)) | nFIDBase);
    1868         119 :     delete poSrcFeat;
    1869             : 
    1870         119 :     return poFeature;
    1871             : }
    1872             : 
    1873             : /************************************************************************/
    1874             : /*                           GetFeature()                               */
    1875             : /************************************************************************/
    1876             : 
    1877           5 : OGRFeature *MBTilesVectorLayer::GetFeature(GIntBig nFID)
    1878             : {
    1879           5 :     const int nZ = m_nZoomLevel;
    1880           5 :     const int nX = static_cast<int>(nFID & ((1 << nZ) - 1));
    1881           5 :     const int nY = static_cast<int>((nFID >> nZ) & ((1 << nZ) - 1));
    1882           5 :     const GIntBig nTileFID = nFID >> (2 * nZ);
    1883             : 
    1884          10 :     CPLString osSQL;
    1885             :     osSQL.Printf("SELECT tile_data FROM tiles "
    1886             :                  "WHERE zoom_level = %d AND "
    1887             :                  "tile_column = %d AND tile_row = %d",
    1888           5 :                  m_nZoomLevel, nX, (1 << nZ) - 1 - nY);
    1889             :     auto hSQLLyr =
    1890           5 :         OGR_DS_ExecuteSQL(m_poDS->hDS, osSQL.c_str(), nullptr, nullptr);
    1891           5 :     if (hSQLLyr == nullptr)
    1892           0 :         return nullptr;
    1893           5 :     auto hFeat = OGR_L_GetNextFeature(hSQLLyr);
    1894           5 :     if (hFeat == nullptr)
    1895             :     {
    1896           0 :         OGR_DS_ReleaseResultSet(m_poDS->hDS, hSQLLyr);
    1897           0 :         return nullptr;
    1898             :     }
    1899           5 :     int nDataSize = 0;
    1900           5 :     GByte *pabyData = OGR_F_GetFieldAsBinary(hFeat, 0, &nDataSize);
    1901           5 :     GByte *pabyDataDup = static_cast<GByte *>(CPLMalloc(nDataSize));
    1902           5 :     memcpy(pabyDataDup, pabyData, nDataSize);
    1903           5 :     OGR_F_Destroy(hFeat);
    1904           5 :     OGR_DS_ReleaseResultSet(m_poDS->hDS, hSQLLyr);
    1905             : 
    1906             :     CPLString osTmpFilename =
    1907           5 :         CPLSPrintf("/vsimem/mvt_getfeature_%p_%d_%d.pbf", this, nX, nY);
    1908           5 :     VSIFCloseL(
    1909             :         VSIFileFromMemBuffer(osTmpFilename, pabyDataDup, nDataSize, true));
    1910             : 
    1911           5 :     const char *l_apszAllowedDrivers[] = {"MVT", nullptr};
    1912           5 :     char **papszOpenOptions = nullptr;
    1913             :     papszOpenOptions =
    1914           5 :         CSLSetNameValue(papszOpenOptions, "X", CPLSPrintf("%d", nX));
    1915             :     papszOpenOptions =
    1916           5 :         CSLSetNameValue(papszOpenOptions, "Y", CPLSPrintf("%d", nY));
    1917             :     papszOpenOptions =
    1918           5 :         CSLSetNameValue(papszOpenOptions, "Z", CPLSPrintf("%d", m_nZoomLevel));
    1919           5 :     papszOpenOptions = CSLSetNameValue(
    1920             :         papszOpenOptions, "METADATA_FILE",
    1921           5 :         m_bJsonField ? "" : m_poDS->m_osMetadataMemFilename.c_str());
    1922           5 :     if (!m_poDS->m_osClip.empty())
    1923             :     {
    1924             :         papszOpenOptions =
    1925           0 :             CSLSetNameValue(papszOpenOptions, "CLIP", m_poDS->m_osClip);
    1926             :     }
    1927           5 :     auto hTileDS = GDALOpenEx(("MVT:" + osTmpFilename).c_str(),
    1928             :                               GDAL_OF_VECTOR | GDAL_OF_INTERNAL,
    1929             :                               l_apszAllowedDrivers, papszOpenOptions, nullptr);
    1930           5 :     CSLDestroy(papszOpenOptions);
    1931             : 
    1932           5 :     OGRFeature *poFeature = nullptr;
    1933           5 :     if (hTileDS)
    1934             :     {
    1935           5 :         OGRLayerH hLayer = GDALDatasetGetLayerByName(hTileDS, GetName());
    1936           5 :         if (hLayer)
    1937             :         {
    1938             :             OGRFeature *poUnderlyingFeature = reinterpret_cast<OGRFeature *>(
    1939           5 :                 OGR_L_GetFeature(hLayer, nTileFID));
    1940           5 :             if (poUnderlyingFeature)
    1941             :             {
    1942           2 :                 poFeature = CreateFeatureFrom(poUnderlyingFeature);
    1943           2 :                 poFeature->SetFID(nFID);
    1944             :             }
    1945           5 :             delete poUnderlyingFeature;
    1946             :         }
    1947             :     }
    1948           5 :     GDALClose(hTileDS);
    1949             : 
    1950           5 :     VSIUnlink(osTmpFilename);
    1951             : 
    1952           5 :     return poFeature;
    1953             : }
    1954             : 
    1955             : /************************************************************************/
    1956             : /*                           InitVector()                               */
    1957             : /************************************************************************/
    1958             : 
    1959          30 : void MBTilesDataset::InitVector(double dfMinX, double dfMinY, double dfMaxX,
    1960             :                                 double dfMaxY, bool bZoomLevelFromSpatialFilter,
    1961             :                                 bool bJsonField)
    1962             : {
    1963          30 :     const char *pszSQL = "SELECT value FROM metadata WHERE name = 'json'";
    1964          30 :     CPLDebug("MBTILES", "%s", pszSQL);
    1965          60 :     CPLJSONDocument oJsonDoc;
    1966          60 :     CPLJSONDocument oDoc;
    1967          30 :     auto hSQLLyr = OGR_DS_ExecuteSQL(hDS, pszSQL, nullptr, nullptr);
    1968          30 :     if (hSQLLyr)
    1969             :     {
    1970          30 :         auto hFeat = OGR_L_GetNextFeature(hSQLLyr);
    1971          30 :         if (hFeat)
    1972             :         {
    1973          30 :             auto pszJson = OGR_F_GetFieldAsString(hFeat, 0);
    1974          30 :             oDoc.GetRoot().Add("json", pszJson);
    1975          30 :             CPL_IGNORE_RET_VAL(
    1976          30 :                 oJsonDoc.LoadMemory(reinterpret_cast<const GByte *>(pszJson)));
    1977          30 :             OGR_F_Destroy(hFeat);
    1978             :         }
    1979          30 :         OGR_DS_ReleaseResultSet(hDS, hSQLLyr);
    1980             :     }
    1981             : 
    1982          30 :     m_osMetadataMemFilename = CPLSPrintf("/vsimem/%p_metadata.json", this);
    1983          30 :     oDoc.Save(m_osMetadataMemFilename);
    1984             : 
    1985          60 :     CPLJSONArray oVectorLayers;
    1986          30 :     oVectorLayers.Deinit();
    1987             : 
    1988          60 :     CPLJSONArray oTileStatLayers;
    1989          30 :     oTileStatLayers.Deinit();
    1990             : 
    1991          30 :     oVectorLayers = oJsonDoc.GetRoot().GetArray("vector_layers");
    1992             : 
    1993          30 :     oTileStatLayers = oJsonDoc.GetRoot().GetArray("tilestats/layers");
    1994             : 
    1995          65 :     for (int i = 0; i < oVectorLayers.Size(); i++)
    1996             :     {
    1997         105 :         CPLJSONObject oId = oVectorLayers[i].GetObj("id");
    1998          35 :         if (oId.IsValid() && oId.GetType() == CPLJSONObject::Type::String)
    1999             :         {
    2000          35 :             OGRwkbGeometryType eGeomType = wkbUnknown;
    2001          35 :             if (oTileStatLayers.IsValid())
    2002             :             {
    2003          34 :                 eGeomType = OGRMVTFindGeomTypeFromTileStat(
    2004          68 :                     oTileStatLayers, oId.ToString().c_str());
    2005             :             }
    2006             : 
    2007         105 :             CPLJSONObject oFields = oVectorLayers[i].GetObj("fields");
    2008             :             CPLJSONArray oAttributesFromTileStats =
    2009             :                 OGRMVTFindAttributesFromTileStat(oTileStatLayers,
    2010          70 :                                                  oId.ToString().c_str());
    2011          35 :             m_apoLayers.push_back(
    2012          70 :                 std::unique_ptr<OGRLayer>(new MBTilesVectorLayer(
    2013          70 :                     this, oId.ToString().c_str(), oFields,
    2014             :                     oAttributesFromTileStats, bJsonField, dfMinX, dfMinY,
    2015          35 :                     dfMaxX, dfMaxY, eGeomType, bZoomLevelFromSpatialFilter)));
    2016             :         }
    2017             :     }
    2018          30 : }
    2019             : 
    2020             : /************************************************************************/
    2021             : /*                             Identify()                               */
    2022             : /************************************************************************/
    2023             : 
    2024       58867 : int MBTilesDataset::Identify(GDALOpenInfo *poOpenInfo)
    2025             : {
    2026             : #ifdef ENABLE_SQL_SQLITE_FORMAT
    2027       58867 :     if (poOpenInfo->pabyHeader &&
    2028       11242 :         STARTS_WITH((const char *)poOpenInfo->pabyHeader, "-- SQL MBTILES"))
    2029             :     {
    2030           2 :         return TRUE;
    2031             :     }
    2032             : #endif
    2033             : 
    2034       58865 :     if ((EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "MBTILES") ||
    2035             :          // Allow direct Amazon S3 signed URLs that contains .mbtiles in the
    2036             :          // middle of the URL
    2037       58594 :          strstr(poOpenInfo->pszFilename, ".mbtiles") != nullptr) &&
    2038      117607 :         poOpenInfo->nHeaderBytes >= 1024 && poOpenInfo->pabyHeader &&
    2039         149 :         STARTS_WITH_CI((const char *)poOpenInfo->pabyHeader, "SQLite Format 3"))
    2040             :     {
    2041         149 :         return TRUE;
    2042             :     }
    2043             : 
    2044       58715 :     return FALSE;
    2045             : }
    2046             : 
    2047             : /************************************************************************/
    2048             : /*                        MBTilesGetMinMaxZoomLevel()                   */
    2049             : /************************************************************************/
    2050             : 
    2051          75 : static int MBTilesGetMinMaxZoomLevel(OGRDataSourceH hDS, int bHasMap,
    2052             :                                      int &nMinLevel, int &nMaxLevel)
    2053             : {
    2054             :     OGRLayerH hSQLLyr;
    2055             :     OGRFeatureH hFeat;
    2056          75 :     int bHasMinMaxLevel = FALSE;
    2057             : 
    2058          75 :     const char *pszSQL =
    2059             :         "SELECT value FROM metadata WHERE name = 'minzoom' UNION ALL "
    2060             :         "SELECT value FROM metadata WHERE name = 'maxzoom'";
    2061          75 :     CPLDebug("MBTILES", "%s", pszSQL);
    2062          75 :     hSQLLyr = OGR_DS_ExecuteSQL(hDS, pszSQL, nullptr, nullptr);
    2063          75 :     if (hSQLLyr)
    2064             :     {
    2065          75 :         hFeat = OGR_L_GetNextFeature(hSQLLyr);
    2066          75 :         if (hFeat)
    2067             :         {
    2068          71 :             int bHasMinLevel = FALSE;
    2069          71 :             if (OGR_F_IsFieldSetAndNotNull(hFeat, 0))
    2070             :             {
    2071          71 :                 nMinLevel = OGR_F_GetFieldAsInteger(hFeat, 0);
    2072          71 :                 bHasMinLevel = TRUE;
    2073             :             }
    2074          71 :             OGR_F_Destroy(hFeat);
    2075             : 
    2076          71 :             if (bHasMinLevel)
    2077             :             {
    2078          71 :                 hFeat = OGR_L_GetNextFeature(hSQLLyr);
    2079          71 :                 if (hFeat)
    2080             :                 {
    2081          71 :                     if (OGR_F_IsFieldSetAndNotNull(hFeat, 0))
    2082             :                     {
    2083          71 :                         nMaxLevel = OGR_F_GetFieldAsInteger(hFeat, 0);
    2084          71 :                         bHasMinMaxLevel = TRUE;
    2085             :                     }
    2086          71 :                     OGR_F_Destroy(hFeat);
    2087             :                 }
    2088             :             }
    2089             :         }
    2090             : 
    2091          75 :         OGR_DS_ReleaseResultSet(hDS, hSQLLyr);
    2092             :     }
    2093             : 
    2094          75 :     if (!bHasMinMaxLevel)
    2095             :     {
    2096             : #define OPTIMIZED_FOR_VSICURL
    2097             : #ifdef OPTIMIZED_FOR_VSICURL
    2098             :         int iLevel;
    2099          40 :         for (iLevel = 0; nMinLevel < 0 && iLevel <= 32; iLevel++)
    2100             :         {
    2101          36 :             pszSQL = CPLSPrintf(
    2102             :                 "SELECT zoom_level FROM %s WHERE zoom_level = %d LIMIT 1",
    2103             :                 (bHasMap) ? "map" : "tiles", iLevel);
    2104          36 :             CPLDebug("MBTILES", "%s", pszSQL);
    2105          36 :             hSQLLyr = OGR_DS_ExecuteSQL(hDS, pszSQL, nullptr, nullptr);
    2106          36 :             if (hSQLLyr)
    2107             :             {
    2108          36 :                 hFeat = OGR_L_GetNextFeature(hSQLLyr);
    2109          36 :                 if (hFeat)
    2110             :                 {
    2111           3 :                     nMinLevel = iLevel;
    2112           3 :                     OGR_F_Destroy(hFeat);
    2113             :                 }
    2114          36 :                 OGR_DS_ReleaseResultSet(hDS, hSQLLyr);
    2115             :             }
    2116             :         }
    2117             : 
    2118           4 :         if (nMinLevel < 0)
    2119           1 :             return FALSE;
    2120             : 
    2121          99 :         for (iLevel = 32; nMaxLevel < 0 && iLevel >= nMinLevel; iLevel--)
    2122             :         {
    2123          96 :             pszSQL = CPLSPrintf(
    2124             :                 "SELECT zoom_level FROM %s WHERE zoom_level = %d LIMIT 1",
    2125             :                 (bHasMap) ? "map" : "tiles", iLevel);
    2126          96 :             CPLDebug("MBTILES", "%s", pszSQL);
    2127          96 :             hSQLLyr = OGR_DS_ExecuteSQL(hDS, pszSQL, nullptr, nullptr);
    2128          96 :             if (hSQLLyr)
    2129             :             {
    2130          96 :                 hFeat = OGR_L_GetNextFeature(hSQLLyr);
    2131          96 :                 if (hFeat)
    2132             :                 {
    2133           3 :                     nMaxLevel = iLevel;
    2134           3 :                     bHasMinMaxLevel = TRUE;
    2135           3 :                     OGR_F_Destroy(hFeat);
    2136             :                 }
    2137          96 :                 OGR_DS_ReleaseResultSet(hDS, hSQLLyr);
    2138             :             }
    2139             :         }
    2140             : #else
    2141             :         pszSQL = "SELECT min(zoom_level), max(zoom_level) FROM tiles";
    2142             :         CPLDebug("MBTILES", "%s", pszSQL);
    2143             :         hSQLLyr = OGR_DS_ExecuteSQL(hDS, pszSQL, NULL, nullptr);
    2144             :         if (hSQLLyr == NULL)
    2145             :         {
    2146             :             return FALSE;
    2147             :         }
    2148             : 
    2149             :         hFeat = OGR_L_GetNextFeature(hSQLLyr);
    2150             :         if (hFeat == NULL)
    2151             :         {
    2152             :             OGR_DS_ReleaseResultSet(hDS, hSQLLyr);
    2153             :             return FALSE;
    2154             :         }
    2155             : 
    2156             :         if (OGR_F_IsFieldSetAndNotNull(hFeat, 0) &&
    2157             :             OGR_F_IsFieldSetAndNotNull(hFeat, 1))
    2158             :         {
    2159             :             nMinLevel = OGR_F_GetFieldAsInteger(hFeat, 0);
    2160             :             nMaxLevel = OGR_F_GetFieldAsInteger(hFeat, 1);
    2161             :             bHasMinMaxLevel = TRUE;
    2162             :         }
    2163             : 
    2164             :         OGR_F_Destroy(hFeat);
    2165             :         OGR_DS_ReleaseResultSet(hDS, hSQLLyr);
    2166             : #endif
    2167             :     }
    2168             : 
    2169          74 :     return bHasMinMaxLevel;
    2170             : }
    2171             : 
    2172             : /************************************************************************/
    2173             : /*                   MBTilesTileCoordToWorldCoord()                     */
    2174             : /************************************************************************/
    2175             : 
    2176          16 : static double MBTilesTileCoordToWorldCoord(double dfTileCoord, int nZoomLevel)
    2177             : {
    2178          16 :     return -MAX_GM + 2 * MAX_GM * (dfTileCoord / (1 << nZoomLevel));
    2179             : }
    2180             : 
    2181             : /************************************************************************/
    2182             : /*                   MBTilesWorldCoordToTileCoord()                     */
    2183             : /************************************************************************/
    2184             : 
    2185         232 : static double MBTilesWorldCoordToTileCoord(double dfWorldCoord, int nZoomLevel)
    2186             : {
    2187         232 :     return (dfWorldCoord + MAX_GM) / (2 * MAX_GM) * (1 << nZoomLevel);
    2188             : }
    2189             : 
    2190             : /************************************************************************/
    2191             : /*                           MBTilesGetBounds()                         */
    2192             : /************************************************************************/
    2193             : 
    2194          74 : static bool MBTilesGetBounds(OGRDataSourceH hDS, bool bUseBounds, int nMaxLevel,
    2195             :                              double &minX, double &minY, double &maxX,
    2196             :                              double &maxY)
    2197             : {
    2198          74 :     bool bHasBounds = false;
    2199             :     OGRLayerH hSQLLyr;
    2200             :     OGRFeatureH hFeat;
    2201             : 
    2202          74 :     if (bUseBounds)
    2203             :     {
    2204          73 :         const char *pszSQL = "SELECT value FROM metadata WHERE name = 'bounds'";
    2205          73 :         CPLDebug("MBTILES", "%s", pszSQL);
    2206          73 :         hSQLLyr = OGR_DS_ExecuteSQL(hDS, pszSQL, nullptr, nullptr);
    2207          73 :         if (hSQLLyr)
    2208             :         {
    2209          73 :             hFeat = OGR_L_GetNextFeature(hSQLLyr);
    2210          73 :             if (hFeat != nullptr)
    2211             :             {
    2212          71 :                 const char *pszBounds = OGR_F_GetFieldAsString(hFeat, 0);
    2213          71 :                 char **papszTokens = CSLTokenizeString2(pszBounds, ",", 0);
    2214          71 :                 if (CSLCount(papszTokens) != 4 ||
    2215          70 :                     fabs(CPLAtof(papszTokens[0])) > 180 ||
    2216          54 :                     fabs(CPLAtof(papszTokens[1])) >= 89.99 ||
    2217          54 :                     fabs(CPLAtof(papszTokens[2])) > 180 ||
    2218          54 :                     fabs(CPLAtof(papszTokens[3])) >= 89.99 ||
    2219         195 :                     CPLAtof(papszTokens[0]) > CPLAtof(papszTokens[2]) ||
    2220          54 :                     CPLAtof(papszTokens[1]) > CPLAtof(papszTokens[3]))
    2221             :                 {
    2222          17 :                     CPLError(CE_Warning, CPLE_AppDefined,
    2223             :                              "Invalid value for 'bounds' metadata. Ignoring it "
    2224             :                              "and fall back to present tile extent");
    2225             :                 }
    2226             :                 else
    2227             :                 {
    2228          54 :                     minX = CPLAtof(papszTokens[0]);
    2229          54 :                     minY = CPLAtof(papszTokens[1]);
    2230          54 :                     maxX = CPLAtof(papszTokens[2]);
    2231          54 :                     maxY = CPLAtof(papszTokens[3]);
    2232          54 :                     LongLatToSphericalMercator(&minX, &minY);
    2233          54 :                     LongLatToSphericalMercator(&maxX, &maxY);
    2234             : 
    2235             :                     // Clamp northings
    2236          54 :                     if (maxY > MAX_GM)
    2237           0 :                         maxY = MAX_GM;
    2238          54 :                     if (minY < -MAX_GM)
    2239           8 :                         minY = -MAX_GM;
    2240             : 
    2241          54 :                     bHasBounds = true;
    2242             :                 }
    2243             : 
    2244          71 :                 CSLDestroy(papszTokens);
    2245             : 
    2246          71 :                 OGR_F_Destroy(hFeat);
    2247             :             }
    2248          73 :             OGR_DS_ReleaseResultSet(hDS, hSQLLyr);
    2249             :         }
    2250             :     }
    2251             : 
    2252          74 :     if (!bHasBounds)
    2253             :     {
    2254             :         const char *pszSQL =
    2255          20 :             CPLSPrintf("SELECT min(tile_column), max(tile_column), "
    2256             :                        "min(tile_row), max(tile_row) FROM tiles "
    2257             :                        "WHERE zoom_level = %d",
    2258             :                        nMaxLevel);
    2259          20 :         CPLDebug("MBTILES", "%s", pszSQL);
    2260          20 :         hSQLLyr = OGR_DS_ExecuteSQL(hDS, pszSQL, nullptr, nullptr);
    2261          20 :         if (hSQLLyr == nullptr)
    2262             :         {
    2263           0 :             return false;
    2264             :         }
    2265             : 
    2266          20 :         hFeat = OGR_L_GetNextFeature(hSQLLyr);
    2267          20 :         if (hFeat == nullptr)
    2268             :         {
    2269           0 :             OGR_DS_ReleaseResultSet(hDS, hSQLLyr);
    2270           0 :             return false;
    2271             :         }
    2272             : 
    2273          20 :         if (OGR_F_IsFieldSetAndNotNull(hFeat, 0) &&
    2274           4 :             OGR_F_IsFieldSetAndNotNull(hFeat, 1) &&
    2275          28 :             OGR_F_IsFieldSetAndNotNull(hFeat, 2) &&
    2276           4 :             OGR_F_IsFieldSetAndNotNull(hFeat, 3))
    2277             :         {
    2278           4 :             int nMinTileCol = OGR_F_GetFieldAsInteger(hFeat, 0);
    2279           4 :             int nMaxTileCol = OGR_F_GetFieldAsInteger(hFeat, 1);
    2280           4 :             int nMinTileRow = OGR_F_GetFieldAsInteger(hFeat, 2);
    2281           4 :             int nMaxTileRow = OGR_F_GetFieldAsInteger(hFeat, 3);
    2282           4 :             if (nMaxTileCol < INT_MAX && nMaxTileRow < INT_MAX)
    2283             :             {
    2284           4 :                 minX = MBTilesTileCoordToWorldCoord(nMinTileCol, nMaxLevel);
    2285           4 :                 minY = MBTilesTileCoordToWorldCoord(nMinTileRow, nMaxLevel);
    2286           4 :                 maxX = MBTilesTileCoordToWorldCoord(nMaxTileCol + 1, nMaxLevel);
    2287           4 :                 maxY = MBTilesTileCoordToWorldCoord(nMaxTileRow + 1, nMaxLevel);
    2288           4 :                 bHasBounds = true;
    2289             :             }
    2290             :         }
    2291             : 
    2292          20 :         OGR_F_Destroy(hFeat);
    2293          20 :         OGR_DS_ReleaseResultSet(hDS, hSQLLyr);
    2294             :     }
    2295             : 
    2296          74 :     return bHasBounds;
    2297             : }
    2298             : 
    2299             : /************************************************************************/
    2300             : /*                        MBTilesCurlReadCbk()                          */
    2301             : /************************************************************************/
    2302             : 
    2303             : typedef struct
    2304             : {
    2305             :     int nBands;
    2306             :     int nSize;
    2307             : } TileProperties;
    2308             : 
    2309             : /* We spy the data received by CURL for the initial request where we try */
    2310             : /* to get a first tile to see its characteristics. We just need the header */
    2311             : /* to determine that, so let's make VSICurl stop reading after we have found it
    2312             :  */
    2313             : 
    2314           1 : static int MBTilesCurlReadCbk(CPL_UNUSED VSILFILE *fp, void *pabyBuffer,
    2315             :                               size_t nBufferSize, void *pfnUserData)
    2316             : {
    2317           1 :     TileProperties *psTP = static_cast<TileProperties *>(pfnUserData);
    2318             : 
    2319           1 :     const GByte abyPNGSig[] = {0x89, 0x50, 0x4E, 0x47,
    2320             :                                0x0D, 0x0A, 0x1A, 0x0A, /* PNG signature */
    2321             :                                0x00, 0x00, 0x00, 0x0D, /* IHDR length */
    2322             :                                0x49, 0x48, 0x44, 0x52 /* IHDR chunk */};
    2323             : 
    2324             :     /* JPEG SOF0 (Start Of Frame 0) marker */
    2325           1 :     const GByte abyJPEG1CompSig[] = {0xFF, 0xC0, /* marker */
    2326             :                                      0x00, 0x0B, /* data length = 8 + 1 * 3 */
    2327             :                                      0x08, /* depth : 8 bit */};
    2328           1 :     const GByte abyJPEG3CompSig[] = {0xFF, 0xC0, /* marker */
    2329             :                                      0x00, 0x11, /* data length = 8 + 3 * 3 */
    2330             :                                      0x08, /* depth : 8 bit */};
    2331             : 
    2332             :     int i;
    2333       16369 :     for (i = 0; i < (int)nBufferSize - (int)sizeof(abyPNGSig); i++)
    2334             :     {
    2335       16368 :         if (memcmp(((GByte *)pabyBuffer) + i, abyPNGSig, sizeof(abyPNGSig)) ==
    2336           0 :                 0 &&
    2337           0 :             i + sizeof(abyPNGSig) + 4 + 4 + 1 + 1 < nBufferSize)
    2338             :         {
    2339           0 :             GByte *ptr = ((GByte *)(pabyBuffer)) + i + (int)sizeof(abyPNGSig);
    2340             : 
    2341             :             int nWidth;
    2342           0 :             memcpy(&nWidth, ptr, 4);
    2343           0 :             CPL_MSBPTR32(&nWidth);
    2344           0 :             ptr += 4;
    2345             : 
    2346             :             int nHeight;
    2347           0 :             memcpy(&nHeight, ptr, 4);
    2348           0 :             CPL_MSBPTR32(&nHeight);
    2349           0 :             ptr += 4;
    2350             : 
    2351           0 :             GByte nDepth = *ptr;
    2352           0 :             ptr += 1;
    2353             : 
    2354           0 :             GByte nColorType = *ptr;
    2355           0 :             CPLDebug("MBTILES",
    2356             :                      "PNG: nWidth=%d nHeight=%d depth=%d nColorType=%d", nWidth,
    2357             :                      nHeight, nDepth, nColorType);
    2358             : 
    2359           0 :             psTP->nBands = -2;
    2360           0 :             psTP->nSize = nWidth;
    2361           0 :             if (nWidth == nHeight && nDepth == 8)
    2362             :             {
    2363           0 :                 if (nColorType == 0)
    2364           0 :                     psTP->nBands = 1; /* Gray */
    2365           0 :                 else if (nColorType == 2)
    2366           0 :                     psTP->nBands = 3; /* RGB */
    2367           0 :                 else if (nColorType == 3)
    2368             :                 {
    2369             :                     /* This might also be a color table with transparency */
    2370             :                     /* but we cannot tell ! */
    2371           0 :                     psTP->nBands = -1;
    2372           0 :                     return TRUE;
    2373             :                 }
    2374           0 :                 else if (nColorType == 4)
    2375           0 :                     psTP->nBands = 2; /* Gray + alpha */
    2376           0 :                 else if (nColorType == 6)
    2377           0 :                     psTP->nBands = 4; /* RGB + alpha */
    2378             :             }
    2379             : 
    2380           0 :             return FALSE;
    2381             :         }
    2382             :     }
    2383             : 
    2384       16375 :     for (i = 0; i < (int)nBufferSize - ((int)sizeof(abyJPEG1CompSig) + 5); i++)
    2385             :     {
    2386       16374 :         if (memcmp(((GByte *)pabyBuffer) + i, abyJPEG1CompSig,
    2387           0 :                    sizeof(abyJPEG1CompSig)) == 0 &&
    2388           0 :             ((GByte *)pabyBuffer)[sizeof(abyJPEG1CompSig) + 4] == 1)
    2389             :         {
    2390             :             GUInt16 nWidth;
    2391           0 :             memcpy(&nWidth, &(((GByte *)pabyBuffer)[sizeof(abyJPEG1CompSig)]),
    2392             :                    2);
    2393           0 :             CPL_MSBPTR16(&nWidth);
    2394             :             GUInt16 nHeight;
    2395           0 :             memcpy(&nHeight,
    2396           0 :                    &(((GByte *)pabyBuffer)[sizeof(abyJPEG1CompSig) + 2]), 2);
    2397           0 :             CPL_MSBPTR16(&nHeight);
    2398             : 
    2399           0 :             CPLDebug("MBTILES", "JPEG: nWidth=%d nHeight=%d depth=%d nBands=%d",
    2400             :                      nWidth, nHeight, 8, 1);
    2401             : 
    2402           0 :             psTP->nBands = -2;
    2403           0 :             if (nWidth == nHeight)
    2404             :             {
    2405           0 :                 psTP->nSize = nWidth;
    2406           0 :                 psTP->nBands = 1;
    2407             :             }
    2408             : 
    2409           0 :             return FALSE;
    2410             :         }
    2411       16374 :         else if (memcmp(((GByte *)pabyBuffer) + i, abyJPEG3CompSig,
    2412           3 :                         sizeof(abyJPEG3CompSig)) == 0 &&
    2413           3 :                  ((GByte *)pabyBuffer)[sizeof(abyJPEG3CompSig) + 4] == 3)
    2414             :         {
    2415             :             GUInt16 nWidth;
    2416           0 :             memcpy(&nWidth, &(((GByte *)pabyBuffer)[sizeof(abyJPEG3CompSig)]),
    2417             :                    2);
    2418           0 :             CPL_MSBPTR16(&nWidth);
    2419             :             GUInt16 nHeight;
    2420           0 :             memcpy(&nHeight,
    2421           0 :                    &(((GByte *)pabyBuffer)[sizeof(abyJPEG3CompSig) + 2]), 2);
    2422           0 :             CPL_MSBPTR16(&nHeight);
    2423             : 
    2424           0 :             CPLDebug("MBTILES", "JPEG: nWidth=%d nHeight=%d depth=%d nBands=%d",
    2425             :                      nWidth, nHeight, 8, 3);
    2426             : 
    2427           0 :             psTP->nBands = -2;
    2428           0 :             if (nWidth == nHeight)
    2429             :             {
    2430           0 :                 psTP->nSize = nWidth;
    2431           0 :                 psTP->nBands = 3;
    2432             :             }
    2433             : 
    2434           0 :             return FALSE;
    2435             :         }
    2436             :     }
    2437             : 
    2438           1 :     return TRUE;
    2439             : }
    2440             : 
    2441             : /************************************************************************/
    2442             : /*                  MBTilesGetBandCountAndTileSize()                    */
    2443             : /************************************************************************/
    2444             : 
    2445          58 : static int MBTilesGetBandCountAndTileSize(bool bIsVSICURL, OGRDataSourceH &hDS,
    2446             :                                           int nMaxLevel, int nMinTileRow,
    2447             :                                           int nMaxTileRow, int nMinTileCol,
    2448             :                                           int nMaxTileCol, int &nTileSize)
    2449             : {
    2450             :     OGRLayerH hSQLLyr;
    2451             :     OGRFeatureH hFeat;
    2452          58 :     VSILFILE *fpCURLOGR = nullptr;
    2453          58 :     int bFirstSelect = TRUE;
    2454             : 
    2455          58 :     int nBands = -1;
    2456          58 :     nTileSize = 0;
    2457             : 
    2458             :     /* Get the VSILFILE associated with the OGR SQLite DB */
    2459         116 :     CPLString osDSName(OGR_DS_GetName(hDS));
    2460          58 :     if (bIsVSICURL)
    2461             :     {
    2462           3 :         auto poDS = dynamic_cast<OGRSQLiteBaseDataSource *>(
    2463           6 :             GDALDataset::FromHandle(hDS));
    2464           3 :         CPLAssert(poDS);
    2465           3 :         if (poDS)
    2466             :         {
    2467           3 :             fpCURLOGR = poDS->GetVSILFILE();
    2468             :         }
    2469             :     }
    2470             : 
    2471             :     const char *pszSQL =
    2472         116 :         CPLSPrintf("SELECT tile_data FROM tiles WHERE "
    2473             :                    "tile_column = %d AND tile_row = %d AND zoom_level = %d",
    2474          58 :                    nMinTileCol / 2 + nMaxTileCol / 2,
    2475          58 :                    nMinTileRow / 2 + nMaxTileRow / 2, nMaxLevel);
    2476          58 :     CPLDebug("MBTILES", "%s", pszSQL);
    2477             : 
    2478          58 :     if (fpCURLOGR)
    2479             :     {
    2480             :         /* Install a spy on the file connection that will intercept */
    2481             :         /* PNG or JPEG headers, to interrupt their downloading */
    2482             :         /* once the header is found. Speeds up dataset opening. */
    2483           3 :         CPLErrorReset();
    2484             :         TileProperties tp;
    2485           3 :         tp.nBands = -1;
    2486           3 :         tp.nSize = 0;
    2487           3 :         VSICurlInstallReadCbk(fpCURLOGR, MBTilesCurlReadCbk, &tp, TRUE);
    2488           3 :         nBands = tp.nBands;
    2489           3 :         nTileSize = tp.nSize;
    2490             : 
    2491           3 :         CPLErrorReset();
    2492           3 :         CPLPushErrorHandler(CPLQuietErrorHandler);
    2493           3 :         hSQLLyr = OGR_DS_ExecuteSQL(hDS, pszSQL, nullptr, nullptr);
    2494           3 :         CPLPopErrorHandler();
    2495             : 
    2496           3 :         VSICurlUninstallReadCbk(fpCURLOGR);
    2497             : 
    2498             :         /* Did the spy intercept something interesting ? */
    2499             :         // cppcheck-suppress knownConditionTrueFalse
    2500           3 :         if (nBands != -1)
    2501             :         {
    2502           0 :             CPLErrorReset();
    2503             : 
    2504           0 :             OGR_DS_ReleaseResultSet(hDS, hSQLLyr);
    2505           0 :             hSQLLyr = nullptr;
    2506             : 
    2507             :             // Re-open OGR SQLite DB, because with our spy we have simulated an
    2508             :             // I/O error that SQLite will have difficulties to recover within
    2509             :             // the existing connection.  This will be fast because
    2510             :             // the /vsicurl/ cache has cached the already read blocks.
    2511           0 :             OGRReleaseDataSource(hDS);
    2512           0 :             hDS = MBTILESOpenSQLiteDB(osDSName.c_str(), GA_ReadOnly);
    2513           0 :             if (hDS == nullptr)
    2514           0 :                 return -1;
    2515             : 
    2516             :             /* Unrecognized form of PNG. Error out */
    2517           0 :             if (nBands <= 0)
    2518           0 :                 return -1;
    2519             : 
    2520           0 :             return nBands;
    2521             :         }
    2522           3 :         else if (CPLGetLastErrorType() == CE_Failure)
    2523             :         {
    2524           0 :             CPLError(CE_Failure, CPLGetLastErrorNo(), "%s",
    2525             :                      CPLGetLastErrorMsg());
    2526             :         }
    2527             :     }
    2528             :     else
    2529             :     {
    2530          55 :         hSQLLyr = OGR_DS_ExecuteSQL(hDS, pszSQL, nullptr, nullptr);
    2531             :     }
    2532             : 
    2533             :     while (true)
    2534             :     {
    2535          71 :         if (hSQLLyr == nullptr && bFirstSelect)
    2536             :         {
    2537          13 :             bFirstSelect = FALSE;
    2538          13 :             pszSQL = CPLSPrintf("SELECT tile_data FROM tiles WHERE "
    2539             :                                 "zoom_level = %d LIMIT 1",
    2540             :                                 nMaxLevel);
    2541          13 :             CPLDebug("MBTILES", "%s", pszSQL);
    2542          13 :             hSQLLyr = OGR_DS_ExecuteSQL(hDS, pszSQL, nullptr, nullptr);
    2543          13 :             if (hSQLLyr == nullptr)
    2544           0 :                 return -1;
    2545             :         }
    2546             : 
    2547          71 :         hFeat = OGR_L_GetNextFeature(hSQLLyr);
    2548          71 :         if (hFeat == nullptr)
    2549             :         {
    2550          18 :             OGR_DS_ReleaseResultSet(hDS, hSQLLyr);
    2551          18 :             hSQLLyr = nullptr;
    2552          18 :             if (!bFirstSelect)
    2553           5 :                 return -1;
    2554             :         }
    2555             :         else
    2556          53 :             break;
    2557             :     }
    2558             : 
    2559         106 :     CPLString osMemFileName;
    2560          53 :     osMemFileName.Printf("/vsimem/%p", hSQLLyr);
    2561             : 
    2562          53 :     int nDataSize = 0;
    2563          53 :     GByte *pabyData = OGR_F_GetFieldAsBinary(hFeat, 0, &nDataSize);
    2564             : 
    2565          53 :     VSIFCloseL(VSIFileFromMemBuffer(osMemFileName.c_str(), pabyData, nDataSize,
    2566             :                                     FALSE));
    2567             : 
    2568          53 :     GDALDatasetH hDSTile = GDALOpenEx(osMemFileName.c_str(), GDAL_OF_RASTER,
    2569             :                                       apszAllowedDrivers, nullptr, nullptr);
    2570          53 :     if (hDSTile == nullptr)
    2571             :     {
    2572          26 :         VSIUnlink(osMemFileName.c_str());
    2573          26 :         OGR_F_Destroy(hFeat);
    2574          26 :         OGR_DS_ReleaseResultSet(hDS, hSQLLyr);
    2575          26 :         return -1;
    2576             :     }
    2577             : 
    2578          27 :     nBands = GDALGetRasterCount(hDSTile);
    2579             : 
    2580          14 :     if ((nBands != 1 && nBands != 2 && nBands != 3 && nBands != 4) ||
    2581          68 :         GDALGetRasterXSize(hDSTile) != GDALGetRasterYSize(hDSTile) ||
    2582          27 :         GDALGetRasterDataType(GDALGetRasterBand(hDSTile, 1)) != GDT_Byte)
    2583             :     {
    2584           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    2585             :                  "Unsupported tile characteristics");
    2586           0 :         GDALClose(hDSTile);
    2587           0 :         VSIUnlink(osMemFileName.c_str());
    2588           0 :         OGR_F_Destroy(hFeat);
    2589           0 :         OGR_DS_ReleaseResultSet(hDS, hSQLLyr);
    2590           0 :         return -1;
    2591             :     }
    2592             : 
    2593          27 :     nTileSize = GDALGetRasterXSize(hDSTile);
    2594             :     GDALColorTableH hCT =
    2595          27 :         GDALGetRasterColorTable(GDALGetRasterBand(hDSTile, 1));
    2596          27 :     if (nBands == 1 && hCT != nullptr)
    2597             :     {
    2598           8 :         nBands = 3;
    2599           8 :         if (GDALGetColorEntryCount(hCT) > 0)
    2600             :         {
    2601             :             /* Typical of paletted PNG with transparency */
    2602           8 :             const GDALColorEntry *psEntry = GDALGetColorEntry(hCT, 0);
    2603           8 :             if (psEntry->c4 == 0)
    2604           0 :                 nBands = 4;
    2605             :         }
    2606             :     }
    2607             : 
    2608          27 :     GDALClose(hDSTile);
    2609          27 :     VSIUnlink(osMemFileName.c_str());
    2610          27 :     OGR_F_Destroy(hFeat);
    2611          27 :     OGR_DS_ReleaseResultSet(hDS, hSQLLyr);
    2612             : 
    2613          27 :     return nBands;
    2614             : }
    2615             : 
    2616             : /************************************************************************/
    2617             : /*                                Open()                                */
    2618             : /************************************************************************/
    2619             : 
    2620          75 : GDALDataset *MBTilesDataset::Open(GDALOpenInfo *poOpenInfo)
    2621             : {
    2622         150 :     CPLString osFileName;
    2623         150 :     CPLString osTableName;
    2624             : 
    2625          75 :     if (!Identify(poOpenInfo))
    2626           0 :         return nullptr;
    2627             : 
    2628          75 :     if ((poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) != 0 &&
    2629          50 :         (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 &&
    2630          13 :         (poOpenInfo->nOpenFlags & GDAL_OF_UPDATE) != 0)
    2631             :     {
    2632           0 :         return nullptr;
    2633             :     }
    2634             : 
    2635             :     /* -------------------------------------------------------------------- */
    2636             :     /*      Open underlying OGR DB                                          */
    2637             :     /* -------------------------------------------------------------------- */
    2638             : 
    2639             :     OGRDataSourceH hDS =
    2640          75 :         MBTILESOpenSQLiteDB(poOpenInfo->pszFilename, poOpenInfo->eAccess);
    2641             : 
    2642          75 :     MBTilesDataset *poDS = nullptr;
    2643             : 
    2644          75 :     if (hDS == nullptr)
    2645           0 :         goto end;
    2646             : 
    2647             :     /* -------------------------------------------------------------------- */
    2648             :     /*      Build dataset                                                   */
    2649             :     /* -------------------------------------------------------------------- */
    2650             :     {
    2651          75 :         CPLString osMetadataTableName, osRasterTableName;
    2652          75 :         CPLString osSQL;
    2653             :         OGRLayerH hMetadataLyr, hRasterLyr;
    2654             :         OGRFeatureH hFeat;
    2655             :         int nBands;
    2656          75 :         OGRLayerH hSQLLyr = nullptr;
    2657          75 :         int nMinLevel = -1;
    2658          75 :         int nMaxLevel = -1;
    2659          75 :         int bHasMinMaxLevel = FALSE;
    2660             :         int bHasMap;
    2661             : 
    2662          75 :         osMetadataTableName = "metadata";
    2663             : 
    2664          75 :         hMetadataLyr = OGR_DS_GetLayerByName(hDS, osMetadataTableName.c_str());
    2665          75 :         if (hMetadataLyr == nullptr)
    2666           0 :             goto end;
    2667             : 
    2668          75 :         osRasterTableName += "tiles";
    2669             : 
    2670          75 :         hRasterLyr = OGR_DS_GetLayerByName(hDS, osRasterTableName.c_str());
    2671          75 :         if (hRasterLyr == nullptr)
    2672           0 :             goto end;
    2673             : 
    2674          75 :         bHasMap = OGR_DS_GetLayerByName(hDS, "map") != nullptr;
    2675          75 :         if (bHasMap)
    2676             :         {
    2677           0 :             bHasMap = FALSE;
    2678             : 
    2679           0 :             hSQLLyr = OGR_DS_ExecuteSQL(
    2680             :                 hDS, "SELECT type FROM sqlite_master WHERE name = 'tiles'",
    2681             :                 nullptr, nullptr);
    2682           0 :             if (hSQLLyr != nullptr)
    2683             :             {
    2684           0 :                 hFeat = OGR_L_GetNextFeature(hSQLLyr);
    2685           0 :                 if (hFeat)
    2686             :                 {
    2687           0 :                     if (OGR_F_IsFieldSetAndNotNull(hFeat, 0))
    2688             :                     {
    2689           0 :                         bHasMap = strcmp(OGR_F_GetFieldAsString(hFeat, 0),
    2690           0 :                                          "view") == 0;
    2691           0 :                         if (!bHasMap)
    2692             :                         {
    2693           0 :                             CPLDebug("MBTILES", "Weird! 'tiles' is not a view, "
    2694             :                                                 "but 'map' exists");
    2695             :                         }
    2696             :                     }
    2697           0 :                     OGR_F_Destroy(hFeat);
    2698             :                 }
    2699           0 :                 OGR_DS_ReleaseResultSet(hDS, hSQLLyr);
    2700             :             }
    2701             :         }
    2702             : 
    2703             :         /* --------------------------------------------------------------------
    2704             :          */
    2705             :         /*      Get minimum and maximum zoom levels */
    2706             :         /* --------------------------------------------------------------------
    2707             :          */
    2708             : 
    2709             :         bHasMinMaxLevel =
    2710          75 :             MBTilesGetMinMaxZoomLevel(hDS, bHasMap, nMinLevel, nMaxLevel);
    2711             : 
    2712             :         const char *pszZoomLevel =
    2713          75 :             CSLFetchNameValue(poOpenInfo->papszOpenOptions, "ZOOM_LEVEL");
    2714          75 :         if (pszZoomLevel != nullptr)
    2715           0 :             nMaxLevel = atoi(pszZoomLevel);
    2716             : 
    2717          75 :         if (bHasMinMaxLevel && (nMinLevel < 0 || nMinLevel > nMaxLevel))
    2718             :         {
    2719           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2720             :                      "Inconsistent values : min(zoom_level) = %d, "
    2721             :                      "max(zoom_level) = %d",
    2722             :                      nMinLevel, nMaxLevel);
    2723           0 :             goto end;
    2724             :         }
    2725             : 
    2726          75 :         if (bHasMinMaxLevel && nMaxLevel > 22)
    2727             :         {
    2728           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    2729             :                      "zoom_level > 22 not supported");
    2730           0 :             goto end;
    2731             :         }
    2732             : 
    2733          75 :         if (!bHasMinMaxLevel)
    2734             :         {
    2735           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    2736             :                      "Cannot find min and max zoom_level");
    2737           1 :             goto end;
    2738             :         }
    2739             : 
    2740             :         /* --------------------------------------------------------------------
    2741             :          */
    2742             :         /*      Get bounds */
    2743             :         /* --------------------------------------------------------------------
    2744             :          */
    2745          74 :         double dfMinX = 0.0;
    2746          74 :         double dfMinY = 0.0;
    2747          74 :         double dfMaxX = 0.0;
    2748          74 :         double dfMaxY = 0.0;
    2749         148 :         bool bUseBounds = CPLFetchBool(
    2750          74 :             const_cast<const char **>(poOpenInfo->papszOpenOptions),
    2751             :             "USE_BOUNDS", true);
    2752             :         const char *pszMinX =
    2753          74 :             CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MINX");
    2754             :         const char *pszMinY =
    2755          74 :             CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MINY");
    2756             :         const char *pszMaxX =
    2757          74 :             CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MAXX");
    2758             :         const char *pszMaxY =
    2759          74 :             CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MAXY");
    2760             :         bool bHasBounds;
    2761          74 :         if (pszMinX != nullptr && pszMinY != nullptr && pszMaxX != nullptr &&
    2762             :             pszMaxY != nullptr)
    2763             :         {
    2764           0 :             bHasBounds = true;
    2765             :         }
    2766             :         else
    2767             :         {
    2768          74 :             bHasBounds = MBTilesGetBounds(hDS, bUseBounds, nMaxLevel, dfMinX,
    2769             :                                           dfMinY, dfMaxX, dfMaxY);
    2770             :         }
    2771          74 :         if (!bHasBounds)
    2772             :         {
    2773          16 :             CPLError(CE_Failure, CPLE_AppDefined,
    2774             :                      "Cannot find min and max tile numbers");
    2775          16 :             goto end;
    2776             :         }
    2777          58 :         if (pszMinX != nullptr)
    2778           0 :             dfMinX = CPLAtof(pszMinX);
    2779          58 :         if (pszMinY != nullptr)
    2780           0 :             dfMinY = CPLAtof(pszMinY);
    2781          58 :         if (pszMaxX != nullptr)
    2782           0 :             dfMaxX = CPLAtof(pszMaxX);
    2783          58 :         if (pszMaxY != nullptr)
    2784           0 :             dfMaxY = CPLAtof(pszMaxY);
    2785             : 
    2786             :         /* --------------------------------------------------------------------
    2787             :          */
    2788             :         /*      Get number of bands */
    2789             :         /* --------------------------------------------------------------------
    2790             :          */
    2791             :         int nMinTileCol =
    2792          58 :             static_cast<int>(MBTilesWorldCoordToTileCoord(dfMinX, nMaxLevel));
    2793             :         int nMinTileRow =
    2794          58 :             static_cast<int>(MBTilesWorldCoordToTileCoord(dfMinY, nMaxLevel));
    2795             :         int nMaxTileCol =
    2796          58 :             static_cast<int>(MBTilesWorldCoordToTileCoord(dfMaxX, nMaxLevel));
    2797             :         int nMaxTileRow =
    2798          58 :             static_cast<int>(MBTilesWorldCoordToTileCoord(dfMaxY, nMaxLevel));
    2799          58 :         int nTileSize = 0;
    2800         116 :         nBands = MBTilesGetBandCountAndTileSize(
    2801          58 :             STARTS_WITH_CI(poOpenInfo->pszFilename, "/vsicurl/"), hDS,
    2802             :             nMaxLevel, nMinTileRow, nMaxTileRow, nMinTileCol, nMaxTileCol,
    2803             :             nTileSize);
    2804          58 :         bool bFoundRasterTile = nBands > 0;
    2805          58 :         if (!bFoundRasterTile)
    2806          31 :             nTileSize = knDEFAULT_BLOCK_SIZE;
    2807             : 
    2808             :         // Force 4 bands by default (see #6119)
    2809          58 :         nBands = 4;
    2810             : 
    2811          58 :         const char *pszBandCount = CSLFetchNameValueDef(
    2812          58 :             poOpenInfo->papszOpenOptions, "BAND_COUNT",
    2813             :             CPLGetConfigOption("MBTILES_BAND_COUNT", nullptr));
    2814          58 :         if (pszBandCount)
    2815             :         {
    2816           1 :             int nTmpBands = atoi(pszBandCount);
    2817           1 :             if (nTmpBands >= 1 && nTmpBands <= 4)
    2818           1 :                 nBands = nTmpBands;
    2819             :         }
    2820             : 
    2821          58 :         if (poOpenInfo->eAccess == GA_Update)
    2822             :         {
    2823             :             // So that we can edit all potential overviews
    2824           4 :             nMinLevel = 0;
    2825             :         }
    2826             : 
    2827             :         /* --------------------------------------------------------------------
    2828             :          */
    2829             :         /*      Set dataset attributes */
    2830             :         /* --------------------------------------------------------------------
    2831             :          */
    2832             : 
    2833          58 :         poDS = new MBTilesDataset();
    2834          58 :         poDS->eAccess = poOpenInfo->eAccess;
    2835          58 :         poDS->hDS = hDS;
    2836          58 :         poDS->hDB = (sqlite3 *)GDALGetInternalHandle((GDALDatasetH)hDS,
    2837             :                                                      "SQLITE_HANDLE");
    2838          58 :         CPLAssert(poDS->hDB != nullptr);
    2839             : 
    2840             :         /* poDS will release it from now */
    2841          58 :         hDS = nullptr;
    2842             : 
    2843             :         poDS->m_osClip =
    2844          58 :             CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "CLIP", "");
    2845          58 :         poDS->m_nMinZoomLevel = nMinLevel;
    2846          58 :         bool bRasterOK = poDS->InitRaster(nullptr, nMaxLevel, nBands, nTileSize,
    2847             :                                           dfMinX, dfMinY, dfMaxX, dfMaxY);
    2848             : 
    2849          58 :         const char *pszFormat = poDS->GetMetadataItem("format");
    2850          58 :         if (pszFormat != nullptr && EQUAL(pszFormat, "pbf"))
    2851             :         {
    2852          31 :             if ((poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) == 0)
    2853             :             {
    2854           1 :                 CPLDebug("MBTiles", "This files contain vector tiles, "
    2855             :                                     "but driver open in raster-only mode");
    2856           1 :                 delete poDS;
    2857           1 :                 return nullptr;
    2858             :             }
    2859          60 :             poDS->InitVector(dfMinX, dfMinY, dfMaxX, dfMaxY,
    2860          30 :                              CPLFetchBool(poOpenInfo->papszOpenOptions,
    2861             :                                           "ZOOM_LEVEL_AUTO",
    2862          30 :                                           CPLTestBool(CPLGetConfigOption(
    2863             :                                               "MVT_ZOOM_LEVEL_AUTO", "NO"))),
    2864          30 :                              CPLFetchBool(poOpenInfo->papszOpenOptions,
    2865             :                                           "JSON_FIELD", false));
    2866             :         }
    2867          27 :         else if ((pszFormat != nullptr && !EQUAL(pszFormat, "pbf")) ||
    2868             :                  bFoundRasterTile)
    2869             :         {
    2870          27 :             if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0)
    2871             :             {
    2872           1 :                 CPLDebug("MBTiles", "This files contain raster tiles, "
    2873             :                                     "but driver open in vector-only mode");
    2874           1 :                 delete poDS;
    2875           1 :                 return nullptr;
    2876             :             }
    2877             :         }
    2878             : 
    2879          56 :         if ((pszFormat == nullptr || !EQUAL(pszFormat, "pbf")) && !bRasterOK)
    2880             :         {
    2881           0 :             delete poDS;
    2882           0 :             return nullptr;
    2883             :         }
    2884             : 
    2885          56 :         if (poDS->eAccess == GA_Update)
    2886             :         {
    2887           4 :             if (pszFormat != nullptr &&
    2888           4 :                 (EQUAL(pszFormat, "jpg") || EQUAL(pszFormat, "jpeg")))
    2889             :             {
    2890           0 :                 poDS->m_eTF = GPKG_TF_JPEG;
    2891             :             }
    2892             : 
    2893             :             const char *pszTF =
    2894           4 :                 CSLFetchNameValue(poOpenInfo->papszOpenOptions, "TILE_FORMAT");
    2895           4 :             if (pszTF)
    2896             :             {
    2897           0 :                 poDS->m_eTF = GDALGPKGMBTilesGetTileFormat(pszTF);
    2898           0 :                 if ((pszFormat != nullptr &&
    2899           0 :                      (EQUAL(pszFormat, "jpg") || EQUAL(pszFormat, "jpeg")) &&
    2900           0 :                      poDS->m_eTF != GPKG_TF_JPEG) ||
    2901           0 :                     (pszFormat != nullptr && EQUAL(pszFormat, "png") &&
    2902           0 :                      poDS->m_eTF == GPKG_TF_JPEG))
    2903             :                 {
    2904           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    2905             :                              "Format metadata = '%s', but TILE_FORMAT='%s'",
    2906             :                              pszFormat, pszTF);
    2907             :                 }
    2908             :             }
    2909             : 
    2910           4 :             poDS->ParseCompressionOptions(poOpenInfo->papszOpenOptions);
    2911             :         }
    2912             : 
    2913             :         /* --------------------------------------------------------------------
    2914             :          */
    2915             :         /*      Add overview levels as internal datasets */
    2916             :         /* --------------------------------------------------------------------
    2917             :          */
    2918          69 :         for (int iLevel = nMaxLevel - 1; iLevel >= nMinLevel; iLevel--)
    2919             :         {
    2920          42 :             MBTilesDataset *poOvrDS = new MBTilesDataset();
    2921          42 :             poOvrDS->ShareLockWithParentDataset(poDS);
    2922          42 :             poOvrDS->InitRaster(poDS, iLevel, nBands, nTileSize, dfMinX, dfMinY,
    2923             :                                 dfMaxX, dfMaxY);
    2924             : 
    2925          84 :             poDS->m_papoOverviewDS = (MBTilesDataset **)CPLRealloc(
    2926          42 :                 poDS->m_papoOverviewDS,
    2927          42 :                 sizeof(MBTilesDataset *) * (poDS->m_nOverviewCount + 1));
    2928          42 :             poDS->m_papoOverviewDS[poDS->m_nOverviewCount++] = poOvrDS;
    2929             : 
    2930          71 :             if (poOvrDS->GetRasterXSize() < 256 &&
    2931          29 :                 poOvrDS->GetRasterYSize() < 256)
    2932             :             {
    2933          29 :                 break;
    2934             :             }
    2935             :         }
    2936             : 
    2937             :         /* --------------------------------------------------------------------
    2938             :          */
    2939             :         /*      Initialize any PAM information. */
    2940             :         /* --------------------------------------------------------------------
    2941             :          */
    2942          56 :         poDS->SetDescription(poOpenInfo->pszFilename);
    2943             : 
    2944          56 :         if (!STARTS_WITH_CI(poOpenInfo->pszFilename, "/vsicurl/"))
    2945          53 :             poDS->TryLoadXML();
    2946             :         else
    2947             :         {
    2948           3 :             poDS->SetPamFlags(poDS->GetPamFlags() & ~GPF_DIRTY);
    2949             :         }
    2950             :     }
    2951             : 
    2952          73 : end:
    2953          73 :     if (hDS)
    2954          17 :         OGRReleaseDataSource(hDS);
    2955             : 
    2956          73 :     return poDS;
    2957             : }
    2958             : 
    2959             : /************************************************************************/
    2960             : /*                                Create()                              */
    2961             : /************************************************************************/
    2962             : 
    2963          81 : GDALDataset *MBTilesDataset::Create(const char *pszFilename, int nXSize,
    2964             :                                     int nYSize, int nBandsIn, GDALDataType eDT,
    2965             :                                     char **papszOptions)
    2966             : {
    2967             : #ifdef HAVE_MVT_WRITE_SUPPORT
    2968          81 :     if (nXSize == 0 && nYSize == 0 && nBandsIn == 0 && eDT == GDT_Unknown)
    2969             :     {
    2970          40 :         char **papszOptionsMod = CSLDuplicate(papszOptions);
    2971          40 :         papszOptionsMod = CSLSetNameValue(papszOptionsMod, "FORMAT", "MBTILES");
    2972          40 :         GDALDataset *poRet = OGRMVTWriterDatasetCreate(
    2973             :             pszFilename, nXSize, nYSize, nBandsIn, eDT, papszOptionsMod);
    2974          40 :         CSLDestroy(papszOptionsMod);
    2975          40 :         return poRet;
    2976             :     }
    2977             : #endif
    2978             : 
    2979          41 :     MBTilesDataset *poDS = new MBTilesDataset();
    2980          41 :     if (!poDS->CreateInternal(pszFilename, nXSize, nYSize, nBandsIn, eDT,
    2981             :                               papszOptions))
    2982             :     {
    2983          13 :         delete poDS;
    2984          13 :         poDS = nullptr;
    2985             :     }
    2986          41 :     return poDS;
    2987             : }
    2988             : 
    2989             : /************************************************************************/
    2990             : /*                            CreateInternal()                          */
    2991             : /************************************************************************/
    2992             : 
    2993          41 : bool MBTilesDataset::CreateInternal(const char *pszFilename, int nXSize,
    2994             :                                     int nYSize, int nBandsIn, GDALDataType eDT,
    2995             :                                     char **papszOptions)
    2996             : {
    2997          41 :     if (eDT != GDT_Byte)
    2998             :     {
    2999           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Only Byte supported");
    3000           0 :         return false;
    3001             :     }
    3002          41 :     if (nBandsIn != 1 && nBandsIn != 2 && nBandsIn != 3 && nBandsIn != 4)
    3003             :     {
    3004           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    3005             :                  "Only 1 (Grey/ColorTable), 2 (Grey+Alpha), 3 (RGB) or 4 "
    3006             :                  "(RGBA) band dataset supported");
    3007           0 :         return false;
    3008             :     }
    3009             : 
    3010             :     // for test/debug purposes only. true is the nominal value
    3011          41 :     m_bPNGSupports2Bands =
    3012          41 :         CPLTestBool(CPLGetConfigOption("MBTILES_PNG_SUPPORTS_2BANDS", "TRUE"));
    3013          41 :     m_bPNGSupportsCT =
    3014          41 :         CPLTestBool(CPLGetConfigOption("MBTILES_PNG_SUPPORTS_CT", "TRUE"));
    3015          41 :     m_bWriteBounds = CPLFetchBool(const_cast<const char **>(papszOptions),
    3016             :                                   "WRITE_BOUNDS", true);
    3017          41 :     m_bWriteMinMaxZoom = CPLFetchBool(const_cast<const char **>(papszOptions),
    3018             :                                       "WRITE_MINMAXZOOM", true);
    3019             :     int nBlockSize = std::max(
    3020          41 :         64, std::min(8192, atoi(CSLFetchNameValueDef(
    3021             :                                papszOptions, "BLOCKSIZE",
    3022          41 :                                CPLSPrintf("%d", knDEFAULT_BLOCK_SIZE)))));
    3023          41 :     m_osBounds = CSLFetchNameValueDef(papszOptions, "BOUNDS", "");
    3024          41 :     m_osCenter = CSLFetchNameValueDef(papszOptions, "CENTER", "");
    3025             : 
    3026          41 :     VSIUnlink(pszFilename);
    3027          41 :     SetDescription(pszFilename);
    3028             : 
    3029             :     int rc;
    3030          41 :     if (STARTS_WITH(pszFilename, "/vsi"))
    3031             :     {
    3032          23 :         pMyVFS = OGRSQLiteCreateVFS(nullptr, nullptr);
    3033          23 :         sqlite3_vfs_register(pMyVFS, 0);
    3034          23 :         rc = sqlite3_open_v2(pszFilename, &hDB,
    3035             :                              SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
    3036          23 :                              pMyVFS->zName);
    3037             :     }
    3038             :     else
    3039             :     {
    3040          18 :         rc = sqlite3_open(pszFilename, &hDB);
    3041             :     }
    3042             : 
    3043          41 :     if (rc != SQLITE_OK)
    3044             :     {
    3045           3 :         CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s", pszFilename);
    3046           3 :         return false;
    3047             :     }
    3048             : 
    3049          38 :     sqlite3_exec(hDB, "PRAGMA synchronous = OFF", nullptr, nullptr, nullptr);
    3050             : 
    3051          38 :     rc = sqlite3_exec(hDB,
    3052             :                       "CREATE TABLE tiles ("
    3053             :                       "zoom_level INTEGER NOT NULL,"
    3054             :                       "tile_column INTEGER NOT NULL,"
    3055             :                       "tile_row INTEGER NOT NULL,"
    3056             :                       "tile_data BLOB NOT NULL,"
    3057             :                       "UNIQUE (zoom_level, tile_column, tile_row) )",
    3058             :                       nullptr, nullptr, nullptr);
    3059          38 :     if (rc != SQLITE_OK)
    3060             :     {
    3061           8 :         CPLError(CE_Failure, CPLE_FileIO, "Cannot create tiles table");
    3062           8 :         return false;
    3063             :     }
    3064             : 
    3065          30 :     rc = sqlite3_exec(hDB, "CREATE TABLE metadata (name TEXT, value TEXT)",
    3066             :                       nullptr, nullptr, nullptr);
    3067          30 :     if (rc != SQLITE_OK)
    3068             :     {
    3069           2 :         CPLError(CE_Failure, CPLE_FileIO, "Cannot create metadata table");
    3070           2 :         return false;
    3071             :     }
    3072             : 
    3073             :     const char *pszName =
    3074          28 :         CSLFetchNameValueDef(papszOptions, "NAME", CPLGetBasename(pszFilename));
    3075          28 :     char *pszSQL = sqlite3_mprintf(
    3076             :         "INSERT INTO metadata (name, value) VALUES ('name', '%q')", pszName);
    3077          28 :     sqlite3_exec(hDB, pszSQL, nullptr, nullptr, nullptr);
    3078          28 :     sqlite3_free(pszSQL);
    3079             : 
    3080          28 :     const char *pszType = CSLFetchNameValueDef(papszOptions, "TYPE", "overlay");
    3081          28 :     pszSQL = sqlite3_mprintf(
    3082             :         "INSERT INTO metadata (name, value) VALUES ('type', '%q')", pszType);
    3083          28 :     sqlite3_exec(hDB, pszSQL, nullptr, nullptr, nullptr);
    3084          28 :     sqlite3_free(pszSQL);
    3085             : 
    3086          28 :     const char *pszDescription = CSLFetchNameValueDef(
    3087             :         papszOptions, "DESCRIPTION", CPLGetBasename(pszFilename));
    3088          28 :     pszSQL = sqlite3_mprintf(
    3089             :         "INSERT INTO metadata (name, value) VALUES ('description', '%q')",
    3090             :         pszDescription);
    3091          28 :     sqlite3_exec(hDB, pszSQL, nullptr, nullptr, nullptr);
    3092          28 :     sqlite3_free(pszSQL);
    3093             : 
    3094          28 :     const char *pszTF = CSLFetchNameValue(papszOptions, "TILE_FORMAT");
    3095          28 :     if (pszTF)
    3096           3 :         m_eTF = GDALGPKGMBTilesGetTileFormat(pszTF);
    3097             : 
    3098          28 :     const char *pszVersion = CSLFetchNameValueDef(
    3099          28 :         papszOptions, "VERSION", (m_eTF == GPKG_TF_WEBP) ? "1.3" : "1.1");
    3100          28 :     pszSQL = sqlite3_mprintf(
    3101             :         "INSERT INTO metadata (name, value) VALUES ('version', '%q')",
    3102             :         pszVersion);
    3103          28 :     sqlite3_exec(hDB, pszSQL, nullptr, nullptr, nullptr);
    3104          28 :     sqlite3_free(pszSQL);
    3105             : 
    3106          28 :     const char *pszFormat = CSLFetchNameValueDef(
    3107             :         papszOptions, "FORMAT", GDALMBTilesGetTileFormatName(m_eTF));
    3108          28 :     pszSQL = sqlite3_mprintf(
    3109             :         "INSERT INTO metadata (name, value) VALUES ('format', '%q')",
    3110             :         pszFormat);
    3111          28 :     sqlite3_exec(hDB, pszSQL, nullptr, nullptr, nullptr);
    3112          28 :     sqlite3_free(pszSQL);
    3113             : 
    3114          28 :     m_bNew = true;
    3115          28 :     eAccess = GA_Update;
    3116          28 :     nRasterXSize = nXSize;
    3117          28 :     nRasterYSize = nYSize;
    3118             : 
    3119          28 :     m_pabyCachedTiles =
    3120          28 :         (GByte *)VSI_MALLOC3_VERBOSE(4 * 4, nBlockSize, nBlockSize);
    3121          28 :     if (m_pabyCachedTiles == nullptr)
    3122             :     {
    3123           0 :         return false;
    3124             :     }
    3125             : 
    3126          83 :     for (int i = 1; i <= nBandsIn; i++)
    3127          55 :         SetBand(i, new MBTilesBand(this, nBlockSize));
    3128             : 
    3129          28 :     ParseCompressionOptions(papszOptions);
    3130             : 
    3131          28 :     return true;
    3132             : }
    3133             : 
    3134             : /************************************************************************/
    3135             : /*                            CreateCopy()                              */
    3136             : /************************************************************************/
    3137             : 
    3138             : typedef struct
    3139             : {
    3140             :     const char *pszName;
    3141             :     GDALResampleAlg eResampleAlg;
    3142             : } WarpResamplingAlg;
    3143             : 
    3144             : static const WarpResamplingAlg asResamplingAlg[] = {
    3145             :     {"NEAREST", GRA_NearestNeighbour},
    3146             :     {"BILINEAR", GRA_Bilinear},
    3147             :     {"CUBIC", GRA_Cubic},
    3148             :     {"CUBICSPLINE", GRA_CubicSpline},
    3149             :     {"LANCZOS", GRA_Lanczos},
    3150             :     {"MODE", GRA_Mode},
    3151             :     {"AVERAGE", GRA_Average},
    3152             :     {"RMS", GRA_RMS},
    3153             : };
    3154             : 
    3155          38 : GDALDataset *MBTilesDataset::CreateCopy(const char *pszFilename,
    3156             :                                         GDALDataset *poSrcDS, int /*bStrict*/,
    3157             :                                         char **papszOptions,
    3158             :                                         GDALProgressFunc pfnProgress,
    3159             :                                         void *pProgressData)
    3160             : {
    3161             : 
    3162          38 :     int nBands = poSrcDS->GetRasterCount();
    3163          38 :     if (nBands != 1 && nBands != 2 && nBands != 3 && nBands != 4)
    3164             :     {
    3165           2 :         CPLError(CE_Failure, CPLE_NotSupported,
    3166             :                  "Only 1 (Grey/ColorTable), 2 (Grey+Alpha), 3 (RGB) or 4 "
    3167             :                  "(RGBA) band dataset supported");
    3168           2 :         return nullptr;
    3169             :     }
    3170             : 
    3171          36 :     char **papszTO = CSLSetNameValue(nullptr, "DST_SRS", SRS_EPSG_3857);
    3172             : 
    3173          36 :     void *hTransformArg = nullptr;
    3174             : 
    3175             :     // Hack to compensate for GDALSuggestedWarpOutput2() failure (or not
    3176             :     // ideal suggestion with PROJ 8) when reprojecting latitude = +/- 90 to
    3177             :     // EPSG:3857.
    3178          36 :     double adfSrcGeoTransform[6] = {0, 0, 0, 0, 0, 0};
    3179          36 :     std::unique_ptr<GDALDataset> poTmpDS;
    3180          36 :     bool bModifiedMaxLat = false;
    3181          36 :     bool bModifiedMinLat = false;
    3182          36 :     const auto poSrcSRS = poSrcDS->GetSpatialRef();
    3183          36 :     if (poSrcDS->GetGeoTransform(adfSrcGeoTransform) == CE_None &&
    3184          72 :         adfSrcGeoTransform[2] == 0 && adfSrcGeoTransform[4] == 0 &&
    3185          36 :         adfSrcGeoTransform[5] < 0)
    3186             :     {
    3187          36 :         if (poSrcSRS && poSrcSRS->IsGeographic())
    3188             :         {
    3189          30 :             double maxLat = adfSrcGeoTransform[3];
    3190          30 :             double minLat = adfSrcGeoTransform[3] +
    3191          30 :                             poSrcDS->GetRasterYSize() * adfSrcGeoTransform[5];
    3192             :             // Corresponds to the latitude of MAX_GM
    3193          30 :             constexpr double MAX_LAT = 85.0511287798066;
    3194          30 :             if (maxLat > MAX_LAT)
    3195             :             {
    3196           3 :                 maxLat = MAX_LAT;
    3197           3 :                 bModifiedMaxLat = true;
    3198             :             }
    3199          30 :             if (minLat < -MAX_LAT)
    3200             :             {
    3201           3 :                 minLat = -MAX_LAT;
    3202           3 :                 bModifiedMinLat = true;
    3203             :             }
    3204          30 :             if (bModifiedMaxLat || bModifiedMinLat)
    3205             :             {
    3206           6 :                 CPLStringList aosOptions;
    3207           3 :                 aosOptions.AddString("-of");
    3208           3 :                 aosOptions.AddString("VRT");
    3209           3 :                 aosOptions.AddString("-projwin");
    3210             :                 aosOptions.AddString(
    3211           3 :                     CPLSPrintf("%.18g", adfSrcGeoTransform[0]));
    3212           3 :                 aosOptions.AddString(CPLSPrintf("%.18g", maxLat));
    3213             :                 aosOptions.AddString(
    3214           3 :                     CPLSPrintf("%.18g", adfSrcGeoTransform[0] +
    3215           3 :                                             poSrcDS->GetRasterXSize() *
    3216           3 :                                                 adfSrcGeoTransform[1]));
    3217           3 :                 aosOptions.AddString(CPLSPrintf("%.18g", minLat));
    3218             :                 auto psOptions =
    3219           3 :                     GDALTranslateOptionsNew(aosOptions.List(), nullptr);
    3220           3 :                 poTmpDS.reset(GDALDataset::FromHandle(GDALTranslate(
    3221             :                     "", GDALDataset::ToHandle(poSrcDS), psOptions, nullptr)));
    3222           3 :                 GDALTranslateOptionsFree(psOptions);
    3223           3 :                 if (poTmpDS)
    3224             :                 {
    3225           3 :                     hTransformArg = GDALCreateGenImgProjTransformer2(
    3226           3 :                         GDALDataset::FromHandle(poTmpDS.get()), nullptr,
    3227             :                         papszTO);
    3228             :                 }
    3229             :             }
    3230             :         }
    3231             :     }
    3232          36 :     if (hTransformArg == nullptr)
    3233             :     {
    3234             :         hTransformArg =
    3235          33 :             GDALCreateGenImgProjTransformer2(poSrcDS, nullptr, papszTO);
    3236             :     }
    3237          36 :     if (hTransformArg == nullptr)
    3238             :     {
    3239           0 :         CSLDestroy(papszTO);
    3240           0 :         return nullptr;
    3241             :     }
    3242             : 
    3243          36 :     GDALTransformerInfo *psInfo = (GDALTransformerInfo *)hTransformArg;
    3244             :     double adfGeoTransform[6];
    3245             :     double adfExtent[4];
    3246             :     int nXSize, nYSize;
    3247             : 
    3248          36 :     if (GDALSuggestedWarpOutput2(poSrcDS, psInfo->pfnTransform, hTransformArg,
    3249             :                                  adfGeoTransform, &nXSize, &nYSize, adfExtent,
    3250          36 :                                  0) != CE_None)
    3251             :     {
    3252           0 :         CSLDestroy(papszTO);
    3253           0 :         GDALDestroyGenImgProjTransformer(hTransformArg);
    3254           0 :         return nullptr;
    3255             :     }
    3256             : 
    3257          36 :     GDALDestroyGenImgProjTransformer(hTransformArg);
    3258          36 :     hTransformArg = nullptr;
    3259          36 :     poTmpDS.reset();
    3260             : 
    3261          36 :     if (bModifiedMaxLat || bModifiedMinLat)
    3262             :     {
    3263           3 :         if (bModifiedMaxLat)
    3264             :         {
    3265           3 :             const double maxNorthing = MAX_GM;
    3266           3 :             adfGeoTransform[3] = maxNorthing;
    3267           3 :             adfExtent[3] = maxNorthing;
    3268             :         }
    3269           3 :         if (bModifiedMinLat)
    3270             :         {
    3271           3 :             const double minNorthing = -MAX_GM;
    3272           3 :             adfExtent[1] = minNorthing;
    3273             :         }
    3274             : 
    3275           3 :         if (poSrcSRS && poSrcSRS->IsGeographic())
    3276             :         {
    3277           6 :             if (adfSrcGeoTransform[0] +
    3278           3 :                     poSrcDS->GetRasterXSize() * adfSrcGeoTransform[1] ==
    3279             :                 180)
    3280             :             {
    3281           3 :                 adfExtent[2] = MAX_GM;
    3282             :             }
    3283             :         }
    3284             :     }
    3285             : 
    3286             :     int nZoomLevel;
    3287          36 :     double dfComputedRes = adfGeoTransform[1];
    3288          36 :     double dfPrevRes = 0.0;
    3289          36 :     double dfRes = 0.0;
    3290             :     int nBlockSize = std::max(
    3291          36 :         64, std::min(8192, atoi(CSLFetchNameValueDef(
    3292             :                                papszOptions, "BLOCKSIZE",
    3293          36 :                                CPLSPrintf("%d", knDEFAULT_BLOCK_SIZE)))));
    3294          36 :     const double dfPixelXSizeZoomLevel0 = 2 * MAX_GM / nBlockSize;
    3295         242 :     for (nZoomLevel = 0; nZoomLevel < 25; nZoomLevel++)
    3296             :     {
    3297         242 :         dfRes = dfPixelXSizeZoomLevel0 / (1 << nZoomLevel);
    3298         242 :         if (dfComputedRes > dfRes)
    3299          36 :             break;
    3300         206 :         dfPrevRes = dfRes;
    3301             :     }
    3302          36 :     if (nZoomLevel == 25)
    3303             :     {
    3304           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3305             :                  "Could not find an appropriate zoom level");
    3306           0 :         CSLDestroy(papszTO);
    3307           0 :         return nullptr;
    3308             :     }
    3309             : 
    3310             :     const char *pszZoomLevelStrategy =
    3311          36 :         CSLFetchNameValueDef(papszOptions, "ZOOM_LEVEL_STRATEGY", "AUTO");
    3312          36 :     if (fabs(dfComputedRes - dfRes) / dfRes > 1e-8)
    3313             :     {
    3314          36 :         if (EQUAL(pszZoomLevelStrategy, "LOWER"))
    3315             :         {
    3316           0 :             if (nZoomLevel > 0)
    3317           0 :                 nZoomLevel--;
    3318             :         }
    3319          36 :         else if (EQUAL(pszZoomLevelStrategy, "UPPER"))
    3320             :         {
    3321             :             /* do nothing */
    3322             :         }
    3323          36 :         else if (nZoomLevel > 0)
    3324             :         {
    3325          35 :             if (dfPrevRes / dfComputedRes < dfComputedRes / dfRes)
    3326          33 :                 nZoomLevel--;
    3327             :         }
    3328             :     }
    3329             : 
    3330          36 :     dfRes = dfPixelXSizeZoomLevel0 / (1 << nZoomLevel);
    3331             : 
    3332          36 :     double dfMinX = adfExtent[0];
    3333          36 :     double dfMinY = adfExtent[1];
    3334          36 :     double dfMaxX = adfExtent[2];
    3335          36 :     double dfMaxY = adfExtent[3];
    3336             : 
    3337          36 :     nXSize = (int)(0.5 + (dfMaxX - dfMinX) / dfRes);
    3338          36 :     nYSize = (int)(0.5 + (dfMaxY - dfMinY) / dfRes);
    3339          36 :     adfGeoTransform[1] = dfRes;
    3340          36 :     adfGeoTransform[5] = -dfRes;
    3341             : 
    3342          36 :     int nTargetBands = nBands;
    3343             :     /* For grey level or RGB, if there's reprojection involved, add an alpha */
    3344             :     /* channel */
    3345          68 :     if ((nBands == 1 &&
    3346          36 :          poSrcDS->GetRasterBand(1)->GetColorTable() == nullptr) ||
    3347             :         nBands == 3)
    3348             :     {
    3349          64 :         OGRSpatialReference oSrcSRS;
    3350          32 :         oSrcSRS.SetFromUserInput(poSrcDS->GetProjectionRef());
    3351          32 :         oSrcSRS.AutoIdentifyEPSG();
    3352          39 :         if (oSrcSRS.GetAuthorityCode(nullptr) == nullptr ||
    3353           7 :             atoi(oSrcSRS.GetAuthorityCode(nullptr)) != 3857)
    3354             :         {
    3355          32 :             nTargetBands++;
    3356             :         }
    3357             :     }
    3358             : 
    3359          36 :     GDALResampleAlg eResampleAlg = GRA_Bilinear;
    3360          36 :     const char *pszResampling = CSLFetchNameValue(papszOptions, "RESAMPLING");
    3361          36 :     if (pszResampling)
    3362             :     {
    3363           4 :         for (size_t iAlg = 0;
    3364           4 :              iAlg < sizeof(asResamplingAlg) / sizeof(asResamplingAlg[0]);
    3365             :              iAlg++)
    3366             :         {
    3367           4 :             if (EQUAL(pszResampling, asResamplingAlg[iAlg].pszName))
    3368             :             {
    3369           4 :                 eResampleAlg = asResamplingAlg[iAlg].eResampleAlg;
    3370           4 :                 break;
    3371             :             }
    3372             :         }
    3373             :     }
    3374             : 
    3375          32 :     if (nBands == 1 && poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr &&
    3376          68 :         eResampleAlg != GRA_NearestNeighbour && eResampleAlg != GRA_Mode)
    3377             :     {
    3378           0 :         CPLError(
    3379             :             CE_Warning, CPLE_AppDefined,
    3380             :             "Input dataset has a color table, which will likely lead to "
    3381             :             "bad results when using a resampling method other than "
    3382             :             "nearest neighbour or mode. Converting the dataset to 24/32 bit "
    3383             :             "(e.g. with gdal_translate -expand rgb/rgba) is advised.");
    3384             :     }
    3385             : 
    3386          36 :     GDALDataset *poDS = Create(pszFilename, nXSize, nYSize, nTargetBands,
    3387             :                                GDT_Byte, papszOptions);
    3388          36 :     if (poDS == nullptr)
    3389             :     {
    3390          13 :         CSLDestroy(papszTO);
    3391          13 :         return nullptr;
    3392             :     }
    3393          23 :     poDS->SetGeoTransform(adfGeoTransform);
    3394          25 :     if (nTargetBands == 1 && nBands == 1 &&
    3395           2 :         poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr)
    3396             :     {
    3397           4 :         poDS->GetRasterBand(1)->SetColorTable(
    3398           2 :             poSrcDS->GetRasterBand(1)->GetColorTable());
    3399             :     }
    3400             : 
    3401          23 :     hTransformArg = GDALCreateGenImgProjTransformer2(poSrcDS, poDS, papszTO);
    3402          23 :     CSLDestroy(papszTO);
    3403          23 :     if (hTransformArg == nullptr)
    3404             :     {
    3405           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3406             :                  "GDALCreateGenImgProjTransformer2 failed");
    3407           0 :         delete poDS;
    3408           0 :         return nullptr;
    3409             :     }
    3410             : 
    3411             :     /* -------------------------------------------------------------------- */
    3412             :     /*      Warp the transformer with a linear approximator                 */
    3413             :     /* -------------------------------------------------------------------- */
    3414          23 :     hTransformArg = GDALCreateApproxTransformer(GDALGenImgProjTransform,
    3415             :                                                 hTransformArg, 0.125);
    3416          23 :     GDALApproxTransformerOwnsSubtransformer(hTransformArg, TRUE);
    3417             : 
    3418             :     /* -------------------------------------------------------------------- */
    3419             :     /*      Setup warp options.                                             */
    3420             :     /* -------------------------------------------------------------------- */
    3421          23 :     GDALWarpOptions *psWO = GDALCreateWarpOptions();
    3422             : 
    3423          23 :     psWO->papszWarpOptions = CSLSetNameValue(nullptr, "OPTIMIZE_SIZE", "YES");
    3424          23 :     psWO->eWorkingDataType = GDT_Byte;
    3425             : 
    3426          23 :     psWO->eResampleAlg = eResampleAlg;
    3427             : 
    3428          23 :     psWO->hSrcDS = poSrcDS;
    3429          23 :     psWO->hDstDS = poDS;
    3430             : 
    3431          23 :     psWO->pfnTransformer = GDALApproxTransform;
    3432          23 :     psWO->pTransformerArg = hTransformArg;
    3433             : 
    3434          23 :     psWO->pfnProgress = pfnProgress;
    3435          23 :     psWO->pProgressArg = pProgressData;
    3436             : 
    3437             :     /* -------------------------------------------------------------------- */
    3438             :     /*      Setup band mapping.                                             */
    3439             :     /* -------------------------------------------------------------------- */
    3440             : 
    3441          23 :     if (nBands == 2 || nBands == 4)
    3442           2 :         psWO->nBandCount = nBands - 1;
    3443             :     else
    3444          21 :         psWO->nBandCount = nBands;
    3445             : 
    3446          23 :     psWO->panSrcBands = (int *)CPLMalloc(psWO->nBandCount * sizeof(int));
    3447          23 :     psWO->panDstBands = (int *)CPLMalloc(psWO->nBandCount * sizeof(int));
    3448             : 
    3449          52 :     for (int i = 0; i < psWO->nBandCount; i++)
    3450             :     {
    3451          29 :         psWO->panSrcBands[i] = i + 1;
    3452          29 :         psWO->panDstBands[i] = i + 1;
    3453             :     }
    3454             : 
    3455          23 :     if (nBands == 2 || nBands == 4)
    3456             :     {
    3457           2 :         psWO->nSrcAlphaBand = nBands;
    3458             :     }
    3459          23 :     if (nTargetBands == 2 || nTargetBands == 4)
    3460             :     {
    3461          21 :         psWO->nDstAlphaBand = nTargetBands;
    3462             :     }
    3463             : 
    3464             :     /* -------------------------------------------------------------------- */
    3465             :     /*      Initialize and execute the warp.                                */
    3466             :     /* -------------------------------------------------------------------- */
    3467          23 :     GDALWarpOperation oWO;
    3468             : 
    3469          23 :     CPLErr eErr = oWO.Initialize(psWO);
    3470          23 :     if (eErr == CE_None)
    3471             :     {
    3472             :         /*if( bMulti )
    3473             :             eErr = oWO.ChunkAndWarpMulti( 0, 0, nXSize, nYSize );
    3474             :         else*/
    3475          23 :         eErr = oWO.ChunkAndWarpImage(0, 0, nXSize, nYSize);
    3476             :     }
    3477          23 :     if (eErr != CE_None)
    3478             :     {
    3479           0 :         delete poDS;
    3480           0 :         poDS = nullptr;
    3481             :     }
    3482             : 
    3483          23 :     GDALDestroyTransformer(hTransformArg);
    3484          23 :     GDALDestroyWarpOptions(psWO);
    3485             : 
    3486          23 :     return poDS;
    3487             : }
    3488             : 
    3489             : /************************************************************************/
    3490             : /*                        ParseCompressionOptions()                     */
    3491             : /************************************************************************/
    3492             : 
    3493          32 : void MBTilesDataset::ParseCompressionOptions(char **papszOptions)
    3494             : {
    3495          32 :     const char *pszZLevel = CSLFetchNameValue(papszOptions, "ZLEVEL");
    3496          32 :     if (pszZLevel)
    3497           0 :         m_nZLevel = atoi(pszZLevel);
    3498             : 
    3499          32 :     const char *pszQuality = CSLFetchNameValue(papszOptions, "QUALITY");
    3500          32 :     if (pszQuality)
    3501           2 :         m_nQuality = atoi(pszQuality);
    3502             : 
    3503          32 :     const char *pszDither = CSLFetchNameValue(papszOptions, "DITHER");
    3504          32 :     if (pszDither)
    3505           1 :         m_bDither = CPLTestBool(pszDither);
    3506          32 : }
    3507             : 
    3508             : /************************************************************************/
    3509             : /*                          IBuildOverviews()                           */
    3510             : /************************************************************************/
    3511             : 
    3512           4 : static int GetFloorPowerOfTwo(int n)
    3513             : {
    3514           4 :     int p2 = 1;
    3515          11 :     while ((n = n >> 1) > 0)
    3516             :     {
    3517           7 :         p2 <<= 1;
    3518             :     }
    3519           4 :     return p2;
    3520             : }
    3521             : 
    3522           4 : CPLErr MBTilesDataset::IBuildOverviews(
    3523             :     const char *pszResampling, int nOverviews, const int *panOverviewList,
    3524             :     int nBandsIn, const int * /*panBandList*/, GDALProgressFunc pfnProgress,
    3525             :     void *pProgressData, CSLConstList papszOptions)
    3526             : {
    3527           4 :     if (GetAccess() != GA_Update)
    3528             :     {
    3529           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    3530             :                  "Overview building not supported on a database opened in "
    3531             :                  "read-only mode");
    3532           0 :         return CE_Failure;
    3533             :     }
    3534           4 :     if (m_poParentDS != nullptr)
    3535             :     {
    3536           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    3537             :                  "Overview building not supported on overview dataset");
    3538           0 :         return CE_Failure;
    3539             :     }
    3540             : 
    3541           4 :     if (nOverviews == 0)
    3542             :     {
    3543           2 :         for (int i = 0; i < m_nOverviewCount; i++)
    3544           1 :             m_papoOverviewDS[i]->FlushCache(false);
    3545           1 :         char *pszSQL = sqlite3_mprintf(
    3546             :             "DELETE FROM 'tiles' WHERE zoom_level < %d", m_nZoomLevel);
    3547           1 :         char *pszErrMsg = nullptr;
    3548           1 :         int ret = sqlite3_exec(hDB, pszSQL, nullptr, nullptr, &pszErrMsg);
    3549           1 :         sqlite3_free(pszSQL);
    3550           1 :         if (ret != SQLITE_OK)
    3551             :         {
    3552           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Failure: %s",
    3553           0 :                      pszErrMsg ? pszErrMsg : "");
    3554           0 :             sqlite3_free(pszErrMsg);
    3555           0 :             return CE_Failure;
    3556             :         }
    3557             : 
    3558           1 :         int nRows = 0;
    3559           1 :         int nCols = 0;
    3560           1 :         char **papszResult = nullptr;
    3561           1 :         sqlite3_get_table(
    3562             :             hDB, "SELECT * FROM metadata WHERE name = 'minzoom' LIMIT 2",
    3563             :             &papszResult, &nRows, &nCols, nullptr);
    3564           1 :         sqlite3_free_table(papszResult);
    3565           1 :         if (nRows == 1)
    3566             :         {
    3567           1 :             pszSQL = sqlite3_mprintf(
    3568             :                 "UPDATE metadata SET value = %d WHERE name = 'minzoom'",
    3569             :                 m_nZoomLevel);
    3570           1 :             sqlite3_exec(hDB, pszSQL, nullptr, nullptr, nullptr);
    3571           1 :             sqlite3_free(pszSQL);
    3572             :         }
    3573             : 
    3574           1 :         return CE_None;
    3575             :     }
    3576             : 
    3577           3 :     if (nBandsIn != nBands)
    3578             :     {
    3579           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    3580             :                  "Generation of overviews only"
    3581             :                  "supported when operating on all bands.");
    3582           0 :         return CE_Failure;
    3583             :     }
    3584             : 
    3585           3 :     if (m_nOverviewCount == 0)
    3586             :     {
    3587           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3588             :                  "Image too small to support overviews");
    3589           0 :         return CE_Failure;
    3590             :     }
    3591             : 
    3592           3 :     FlushCache(false);
    3593             : 
    3594          24 :     const auto GetOverviewIndex = [](int nVal)
    3595             :     {
    3596          24 :         int iOvr = -1;
    3597          66 :         while (nVal > 1)
    3598             :         {
    3599          42 :             nVal >>= 1;
    3600          42 :             iOvr++;
    3601             :         }
    3602          24 :         return iOvr;
    3603             :     };
    3604             : 
    3605           7 :     for (int i = 0; i < nOverviews; i++)
    3606             :     {
    3607           4 :         if (panOverviewList[i] < 2)
    3608             :         {
    3609           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
    3610           0 :                      "Overview factor '%d' must be >= 2", panOverviewList[i]);
    3611           0 :             return CE_Failure;
    3612             :         }
    3613             : 
    3614           4 :         if (GetFloorPowerOfTwo(panOverviewList[i]) != panOverviewList[i])
    3615             :         {
    3616           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
    3617             :                      "Overview factor '%d' is not a power of 2",
    3618           0 :                      panOverviewList[i]);
    3619           0 :             return CE_Failure;
    3620             :         }
    3621           4 :         const int iOvr = GetOverviewIndex(panOverviewList[i]);
    3622           4 :         if (iOvr >= m_nOverviewCount)
    3623             :         {
    3624           1 :             CPLDebug("MBTILES",
    3625             :                      "Requested overview factor %d leads to too small overview "
    3626             :                      "and will be ignored",
    3627           1 :                      panOverviewList[i]);
    3628             :         }
    3629             :     }
    3630             : 
    3631             :     GDALRasterBand ***papapoOverviewBands =
    3632           3 :         (GDALRasterBand ***)CPLCalloc(sizeof(void *), nBands);
    3633           3 :     int iCurOverview = 0;
    3634          15 :     for (int iBand = 0; iBand < nBands; iBand++)
    3635             :     {
    3636          24 :         papapoOverviewBands[iBand] =
    3637          12 :             (GDALRasterBand **)CPLCalloc(sizeof(void *), nOverviews);
    3638          12 :         iCurOverview = 0;
    3639          28 :         for (int i = 0; i < nOverviews; i++)
    3640             :         {
    3641          16 :             const int iOvr = GetOverviewIndex(panOverviewList[i]);
    3642          16 :             if (iOvr < m_nOverviewCount)
    3643             :             {
    3644          12 :                 MBTilesDataset *poODS = m_papoOverviewDS[iOvr];
    3645          24 :                 papapoOverviewBands[iBand][iCurOverview] =
    3646          12 :                     poODS->GetRasterBand(iBand + 1);
    3647          12 :                 iCurOverview++;
    3648             :             }
    3649             :         }
    3650             :     }
    3651             : 
    3652           6 :     CPLErr eErr = GDALRegenerateOverviewsMultiBand(
    3653           3 :         nBands, papoBands, iCurOverview, papapoOverviewBands, pszResampling,
    3654             :         pfnProgress, pProgressData, papszOptions);
    3655             : 
    3656          15 :     for (int iBand = 0; iBand < nBands; iBand++)
    3657             :     {
    3658          12 :         CPLFree(papapoOverviewBands[iBand]);
    3659             :     }
    3660           3 :     CPLFree(papapoOverviewBands);
    3661             : 
    3662           3 :     if (eErr == CE_None)
    3663             :     {
    3664             :         // Determine new minzoom value from the existing one and the new
    3665             :         // requested overview levels
    3666           3 :         int nMinZoom = m_nZoomLevel;
    3667           3 :         bool bHasMinZoomMetadata = false;
    3668           3 :         int nRows = 0;
    3669           3 :         int nCols = 0;
    3670           3 :         char **papszResult = nullptr;
    3671           3 :         sqlite3_get_table(
    3672             :             hDB, "SELECT value FROM metadata WHERE name = 'minzoom' LIMIT 2",
    3673             :             &papszResult, &nRows, &nCols, nullptr);
    3674           3 :         if (nRows == 1 && nCols == 1 && papszResult[1])
    3675             :         {
    3676           3 :             bHasMinZoomMetadata = true;
    3677           3 :             nMinZoom = atoi(papszResult[1]);
    3678             :         }
    3679           3 :         sqlite3_free_table(papszResult);
    3680           3 :         if (bHasMinZoomMetadata)
    3681             :         {
    3682           7 :             for (int i = 0; i < nOverviews; i++)
    3683             :             {
    3684           4 :                 const int iOvr = GetOverviewIndex(panOverviewList[i]);
    3685           4 :                 if (iOvr < m_nOverviewCount)
    3686             :                 {
    3687           3 :                     const MBTilesDataset *poODS = m_papoOverviewDS[iOvr];
    3688           3 :                     nMinZoom = std::min(nMinZoom, poODS->m_nZoomLevel);
    3689             :                 }
    3690             :             }
    3691             : 
    3692           3 :             char *pszSQL = sqlite3_mprintf(
    3693             :                 "UPDATE metadata SET value = '%d' WHERE name = 'minzoom'",
    3694             :                 nMinZoom);
    3695           3 :             sqlite3_exec(hDB, pszSQL, nullptr, nullptr, nullptr);
    3696           3 :             sqlite3_free(pszSQL);
    3697             :         }
    3698             :     }
    3699             : 
    3700           3 :     return eErr;
    3701             : }
    3702             : 
    3703             : /************************************************************************/
    3704             : /*                       GDALRegister_MBTiles()                         */
    3705             : /************************************************************************/
    3706             : 
    3707        1509 : void GDALRegister_MBTiles()
    3708             : 
    3709             : {
    3710        1509 :     if (!GDAL_CHECK_VERSION("MBTiles driver"))
    3711           0 :         return;
    3712             : 
    3713        1509 :     if (GDALGetDriverByName("MBTiles") != nullptr)
    3714         295 :         return;
    3715             : 
    3716        1214 :     GDALDriver *poDriver = new GDALDriver();
    3717             : 
    3718        1214 :     poDriver->SetDescription("MBTiles");
    3719        1214 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    3720        1214 :     poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
    3721        1214 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "MBTiles");
    3722        1214 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
    3723        1214 :                               "drivers/raster/mbtiles.html");
    3724        1214 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "mbtiles");
    3725        1214 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Byte");
    3726             : 
    3727             : #define COMPRESSION_OPTIONS                                                    \
    3728             :     "  <Option name='TILE_FORMAT' scope='raster' type='string-select' "        \
    3729             :     "description='Format to use to create tiles' default='PNG'>"               \
    3730             :     "    <Value>PNG</Value>"                                                   \
    3731             :     "    <Value>PNG8</Value>"                                                  \
    3732             :     "    <Value>JPEG</Value>"                                                  \
    3733             :     "    <Value>WEBP</Value>"                                                  \
    3734             :     "  </Option>"                                                              \
    3735             :     "  <Option name='QUALITY' scope='raster' type='int' min='1' max='100' "    \
    3736             :     "description='Quality for JPEG and WEBP tiles' default='75'/>"             \
    3737             :     "  <Option name='ZLEVEL' scope='raster' type='int' min='1' max='9' "       \
    3738             :     "description='DEFLATE compression level for PNG tiles' default='6'/>"      \
    3739             :     "  <Option name='DITHER' scope='raster' type='boolean' "                   \
    3740             :     "description='Whether to apply Floyd-Steinberg dithering (for "            \
    3741             :     "TILE_FORMAT=PNG8)' default='NO'/>"
    3742             : 
    3743        1214 :     poDriver->SetMetadataItem(
    3744             :         GDAL_DMD_OPENOPTIONLIST,
    3745             :         "<OpenOptionList>"
    3746             :         "  <Option name='ZOOM_LEVEL' scope='raster,vector' type='integer' "
    3747             :         "description='Zoom level of full resolution. If not specified, maximum "
    3748             :         "non-empty zoom level'/>"
    3749             :         "  <Option name='BAND_COUNT' scope='raster' type='string-select' "
    3750             :         "description='Number of raster bands' default='AUTO'>"
    3751             :         "    <Value>AUTO</Value>"
    3752             :         "    <Value>1</Value>"
    3753             :         "    <Value>2</Value>"
    3754             :         "    <Value>3</Value>"
    3755             :         "    <Value>4</Value>"
    3756             :         "  </Option>"
    3757             :         "  <Option name='MINX' scope='raster,vector' type='float' "
    3758             :         "description='Minimum X of area of interest'/>"
    3759             :         "  <Option name='MINY' scope='raster,vector' type='float' "
    3760             :         "description='Minimum Y of area of interest'/>"
    3761             :         "  <Option name='MAXX' scope='raster,vector' type='float' "
    3762             :         "description='Maximum X of area of interest'/>"
    3763             :         "  <Option name='MAXY' scope='raster,vector' type='float' "
    3764             :         "description='Maximum Y of area of interest'/>"
    3765             :         "  <Option name='USE_BOUNDS' scope='raster,vector' type='boolean' "
    3766             :         "description='Whether to use the bounds metadata, when available, to "
    3767             :         "determine the AOI' default='YES'/>" COMPRESSION_OPTIONS
    3768             :         "  <Option name='CLIP' scope='vector' type='boolean' "
    3769             :         "description='Whether to clip geometries to tile extent' "
    3770             :         "default='YES'/>"
    3771             :         "  <Option name='ZOOM_LEVEL_AUTO' scope='vector' type='boolean' "
    3772             :         "description='Whether to auto-select the zoom level for vector layers "
    3773             :         "according to spatial filter extent. Only for display purpose' "
    3774             :         "default='NO'/>"
    3775             :         "  <Option name='JSON_FIELD' scope='vector' type='boolean' "
    3776             :         "description='For vector layers, "
    3777             :         "whether to put all attributes as a serialized JSon dictionary'/>"
    3778        1214 :         "</OpenOptionList>");
    3779             : 
    3780        1214 :     poDriver->SetMetadataItem(
    3781             :         GDAL_DMD_CREATIONOPTIONLIST,
    3782             :         "<CreationOptionList>"
    3783             :         "  <Option name='NAME' scope='raster,vector' type='string' "
    3784             :         "description='Tileset name'/>"
    3785             :         "  <Option name='DESCRIPTION' scope='raster,vector' type='string' "
    3786             :         "description='A description of the layer'/>"
    3787             :         "  <Option name='TYPE' scope='raster,vector' type='string-select' "
    3788             :         "description='Layer type' default='overlay'>"
    3789             :         "    <Value>overlay</Value>"
    3790             :         "    <Value>baselayer</Value>"
    3791             :         "  </Option>"
    3792             :         "  <Option name='VERSION' scope='raster' type='string' "
    3793             :         "description='The version of the tileset, as a plain number' "
    3794             :         "default='1.1'/>"
    3795             :         "  <Option name='BLOCKSIZE' scope='raster' type='int' "
    3796             :         "description='Block size in pixels' default='256' min='64' "
    3797             :         "max='8192'/>" COMPRESSION_OPTIONS
    3798             :         "  <Option name='ZOOM_LEVEL_STRATEGY' scope='raster' "
    3799             :         "type='string-select' description='Strategy to determine zoom level.' "
    3800             :         "default='AUTO'>"
    3801             :         "    <Value>AUTO</Value>"
    3802             :         "    <Value>LOWER</Value>"
    3803             :         "    <Value>UPPER</Value>"
    3804             :         "  </Option>"
    3805             :         "  <Option name='RESAMPLING' scope='raster' type='string-select' "
    3806             :         "description='Resampling algorithm.' default='BILINEAR'>"
    3807             :         "    <Value>NEAREST</Value>"
    3808             :         "    <Value>BILINEAR</Value>"
    3809             :         "    <Value>CUBIC</Value>"
    3810             :         "    <Value>CUBICSPLINE</Value>"
    3811             :         "    <Value>LANCZOS</Value>"
    3812             :         "    <Value>MODE</Value>"
    3813             :         "    <Value>AVERAGE</Value>"
    3814             :         "  </Option>"
    3815             :         "  <Option name='WRITE_BOUNDS' scope='raster' type='boolean' "
    3816             :         "description='Whether to write the bounds metadata' default='YES'/>"
    3817             :         "  <Option name='WRITE_MINMAXZOOM' scope='raster' type='boolean' "
    3818             :         "description='Whether to write the minzoom and maxzoom metadata' "
    3819             :         "default='YES'/>"
    3820             :         "  <Option name='BOUNDS' scope='raster,vector' type='string' "
    3821             :         "description='Override default value for bounds metadata item'/>"
    3822             :         "  <Option name='CENTER' scope='raster,vector' type='string' "
    3823             :         "description='Override default value for center metadata item'/>"
    3824             : #ifdef HAVE_MVT_WRITE_SUPPORT
    3825             :         MVT_MBTILES_COMMON_DSCO
    3826             : #endif
    3827        1214 :         "</CreationOptionList>");
    3828        1214 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
    3829             : 
    3830             : #ifdef HAVE_MVT_WRITE_SUPPORT
    3831        1214 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATE_FIELD, "YES");
    3832        1214 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATATYPES,
    3833        1214 :                               "Integer Integer64 Real String");
    3834        1214 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATASUBTYPES,
    3835        1214 :                               "Boolean Float32");
    3836             : 
    3837        1214 :     poDriver->SetMetadataItem(GDAL_DS_LAYER_CREATIONOPTIONLIST, MVT_LCO);
    3838             : #endif
    3839             : 
    3840             : #ifdef ENABLE_SQL_SQLITE_FORMAT
    3841        1214 :     poDriver->SetMetadataItem("ENABLE_SQL_SQLITE_FORMAT", "YES");
    3842             : #endif
    3843        1214 :     poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "SQLITE OGRSQL");
    3844             : 
    3845        1214 :     poDriver->pfnOpen = MBTilesDataset::Open;
    3846        1214 :     poDriver->pfnIdentify = MBTilesDataset::Identify;
    3847        1214 :     poDriver->pfnCreateCopy = MBTilesDataset::CreateCopy;
    3848        1214 :     poDriver->pfnCreate = MBTilesDataset::Create;
    3849             : 
    3850        1214 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    3851             : }

Generated by: LCOV version 1.14