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

Generated by: LCOV version 1.14