LCOV - code coverage report
Current view: top level - frmts/mbtiles - mbtilesdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1273 1714 74.3 %
Date: 2025-02-18 14:19:29 Functions: 62 67 92.5 %

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

Generated by: LCOV version 1.14