LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gpkg - gdalgeopackagerasterband.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1793 1936 92.6 %
Date: 2024-05-02 00:41:30 Functions: 45 46 97.8 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GeoPackage Translator
       4             :  * Purpose:  Implements GDALGeoPackageRasterBand class
       5             :  * Author:   Even Rouault <even dot rouault at spatialys dot com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2014, Even Rouault <even dot rouault at spatialys dot com>
       9             :  *
      10             :  * Permission is hereby granted, free of charge, to any person obtaining a
      11             :  * copy of this software and associated documentation files (the "Software"),
      12             :  * to deal in the Software without restriction, including without limitation
      13             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      14             :  * and/or sell copies of the Software, and to permit persons to whom the
      15             :  * Software is furnished to do so, subject to the following conditions:
      16             :  *
      17             :  * The above copyright notice and this permission notice shall be included
      18             :  * in all copies or substantial portions of the Software.
      19             :  *
      20             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      21             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      22             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      23             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      24             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      25             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      26             :  * DEALINGS IN THE SOFTWARE.
      27             :  ****************************************************************************/
      28             : 
      29             : #include "ogr_geopackage.h"
      30             : #include "memdataset.h"
      31             : #include "gdal_alg_priv.h"
      32             : #include "ogrsqlitevfs.h"
      33             : #include "cpl_error.h"
      34             : 
      35             : #include <algorithm>
      36             : #include <limits>
      37             : 
      38             : #if !defined(DEBUG_VERBOSE) && defined(DEBUG_VERBOSE_GPKG)
      39             : #define DEBUG_VERBOSE
      40             : #endif
      41             : 
      42             : /************************************************************************/
      43             : /*                    GDALGPKGMBTilesLikePseudoDataset()                */
      44             : /************************************************************************/
      45             : 
      46        2290 : GDALGPKGMBTilesLikePseudoDataset::GDALGPKGMBTilesLikePseudoDataset()
      47             :     : m_bForceTempDBCompaction(
      48        2290 :           CPLTestBool(CPLGetConfigOption("GPKG_FORCE_TEMPDB_COMPACTION", "NO")))
      49             : {
      50       11450 :     for (int i = 0; i < 4; i++)
      51             :     {
      52        9160 :         m_asCachedTilesDesc[i].nRow = -1;
      53        9160 :         m_asCachedTilesDesc[i].nCol = -1;
      54        9160 :         m_asCachedTilesDesc[i].nIdxWithinTileData = -1;
      55        9160 :         m_asCachedTilesDesc[i].abBandDirty[0] = FALSE;
      56        9160 :         m_asCachedTilesDesc[i].abBandDirty[1] = FALSE;
      57        9160 :         m_asCachedTilesDesc[i].abBandDirty[2] = FALSE;
      58        9160 :         m_asCachedTilesDesc[i].abBandDirty[3] = FALSE;
      59             :     }
      60        2290 : }
      61             : 
      62             : /************************************************************************/
      63             : /*                 ~GDALGPKGMBTilesLikePseudoDataset()                  */
      64             : /************************************************************************/
      65             : 
      66        2290 : GDALGPKGMBTilesLikePseudoDataset::~GDALGPKGMBTilesLikePseudoDataset()
      67             : {
      68        2290 :     if (m_poParentDS == nullptr && m_hTempDB != nullptr)
      69             :     {
      70          48 :         sqlite3_close(m_hTempDB);
      71          48 :         m_hTempDB = nullptr;
      72          48 :         VSIUnlink(m_osTempDBFilename);
      73          48 :         if (m_pMyVFS)
      74             :         {
      75          29 :             sqlite3_vfs_unregister(m_pMyVFS);
      76          29 :             CPLFree(m_pMyVFS->pAppData);
      77          29 :             CPLFree(m_pMyVFS);
      78             :         }
      79             :     }
      80        2290 :     CPLFree(m_pabyCachedTiles);
      81        2290 :     delete m_poCT;
      82        2290 :     CPLFree(m_pabyHugeColorArray);
      83        2290 : }
      84             : 
      85             : /************************************************************************/
      86             : /*                            SetDataType()                             */
      87             : /************************************************************************/
      88             : 
      89         291 : void GDALGPKGMBTilesLikePseudoDataset::SetDataType(GDALDataType eDT)
      90             : {
      91         291 :     CPLAssert(eDT == GDT_Byte || eDT == GDT_Int16 || eDT == GDT_UInt16 ||
      92             :               eDT == GDT_Float32);
      93         291 :     m_eDT = eDT;
      94         291 :     m_nDTSize = GDALGetDataTypeSizeBytes(m_eDT);
      95         291 : }
      96             : 
      97             : /************************************************************************/
      98             : /*                        SetGlobalOffsetScale()                        */
      99             : /************************************************************************/
     100             : 
     101          72 : void GDALGPKGMBTilesLikePseudoDataset::SetGlobalOffsetScale(double dfOffset,
     102             :                                                             double dfScale)
     103             : {
     104          72 :     m_dfOffset = dfOffset;
     105          72 :     m_dfScale = dfScale;
     106          72 : }
     107             : 
     108             : /************************************************************************/
     109             : /*                      GDALGPKGMBTilesLikeRasterBand()                 */
     110             : /************************************************************************/
     111             : 
     112             : // Recent GCC versions complain about null dereference of m_poTPD in
     113             : // the constructor body
     114             : #ifdef __GNUC__
     115             : #pragma GCC diagnostic push
     116             : #pragma GCC diagnostic ignored "-Wnull-dereference"
     117             : #endif
     118             : 
     119        2469 : GDALGPKGMBTilesLikeRasterBand::GDALGPKGMBTilesLikeRasterBand(
     120        2469 :     GDALGPKGMBTilesLikePseudoDataset *poTPD, int nTileWidth, int nTileHeight)
     121        2469 :     : m_poTPD(poTPD)
     122             : {
     123        2469 :     eDataType = m_poTPD->m_eDT;
     124        2469 :     m_nDTSize = m_poTPD->m_nDTSize;
     125        2469 :     nBlockXSize = nTileWidth;
     126        2469 :     nBlockYSize = nTileHeight;
     127        2469 : }
     128             : 
     129             : #ifdef __GNUC__
     130             : #pragma GCC diagnostic pop
     131             : #endif
     132             : 
     133             : /************************************************************************/
     134             : /*                              FlushCache()                            */
     135             : /************************************************************************/
     136             : 
     137        5323 : CPLErr GDALGPKGMBTilesLikeRasterBand::FlushCache(bool bAtClosing)
     138             : {
     139        5323 :     m_poTPD->m_nLastSpaceCheckTimestamp = -1;  // disable partial flushes
     140        5323 :     CPLErr eErr = GDALPamRasterBand::FlushCache(bAtClosing);
     141        5323 :     if (eErr == CE_None)
     142        5319 :         eErr = m_poTPD->IFlushCacheWithErrCode(bAtClosing);
     143        5323 :     m_poTPD->m_nLastSpaceCheckTimestamp = 0;
     144        5323 :     return eErr;
     145             : }
     146             : 
     147             : /************************************************************************/
     148             : /*                              FlushTiles()                            */
     149             : /************************************************************************/
     150             : 
     151        2968 : CPLErr GDALGPKGMBTilesLikePseudoDataset::FlushTiles()
     152             : {
     153        2968 :     CPLErr eErr = CE_None;
     154        2968 :     GDALGPKGMBTilesLikePseudoDataset *poMainDS =
     155        2968 :         m_poParentDS ? m_poParentDS : this;
     156        2968 :     if (poMainDS->m_nTileInsertionCount < 0)
     157           7 :         return CE_Failure;
     158             : 
     159        2961 :     if (IGetUpdate())
     160             :     {
     161        1802 :         if (m_nShiftXPixelsMod || m_nShiftYPixelsMod)
     162             :         {
     163         607 :             eErr = FlushRemainingShiftedTiles(false /* total flush*/);
     164             :         }
     165             :         else
     166             :         {
     167        1195 :             eErr = WriteTile();
     168             :         }
     169             :     }
     170             : 
     171        2961 :     if (poMainDS->m_nTileInsertionCount > 0)
     172             :     {
     173         174 :         if (poMainDS->ICommitTransaction() != OGRERR_NONE)
     174             :         {
     175           0 :             poMainDS->m_nTileInsertionCount = -1;
     176           0 :             eErr = CE_Failure;
     177             :         }
     178             :         else
     179             :         {
     180         174 :             poMainDS->m_nTileInsertionCount = 0;
     181             :         }
     182             :     }
     183        2961 :     return eErr;
     184             : }
     185             : 
     186             : /************************************************************************/
     187             : /*                             GetColorTable()                          */
     188             : /************************************************************************/
     189             : 
     190         317 : GDALColorTable *GDALGPKGMBTilesLikeRasterBand::GetColorTable()
     191             : {
     192         317 :     if (poDS->GetRasterCount() != 1)
     193         127 :         return nullptr;
     194             : 
     195         190 :     if (!m_poTPD->m_bTriedEstablishingCT)
     196             :     {
     197          51 :         m_poTPD->m_bTriedEstablishingCT = true;
     198          51 :         if (m_poTPD->m_poParentDS != nullptr)
     199             :         {
     200          16 :             m_poTPD->m_poCT =
     201           8 :                 m_poTPD->m_poParentDS->IGetRasterBand(1)->GetColorTable();
     202           8 :             if (m_poTPD->m_poCT)
     203           4 :                 m_poTPD->m_poCT = m_poTPD->m_poCT->Clone();
     204           8 :             return m_poTPD->m_poCT;
     205             :         }
     206             : 
     207          55 :         for (int i = 0; i < 2; i++)
     208             :         {
     209          49 :             bool bRetry = false;
     210          49 :             char *pszSQL = nullptr;
     211          49 :             if (i == 0)
     212             :             {
     213          43 :                 pszSQL = sqlite3_mprintf("SELECT tile_data FROM \"%w\" "
     214             :                                          "WHERE zoom_level = %d LIMIT 1",
     215          43 :                                          m_poTPD->m_osRasterTable.c_str(),
     216          43 :                                          m_poTPD->m_nZoomLevel);
     217             :             }
     218             :             else
     219             :             {
     220             :                 // Try a tile in the middle of the raster
     221           6 :                 pszSQL = sqlite3_mprintf(
     222             :                     "SELECT tile_data FROM \"%w\" "
     223             :                     "WHERE zoom_level = %d AND tile_column = %d AND tile_row = "
     224             :                     "%d",
     225           6 :                     m_poTPD->m_osRasterTable.c_str(), m_poTPD->m_nZoomLevel,
     226           6 :                     m_poTPD->m_nShiftXTiles + nRasterXSize / 2 / nBlockXSize,
     227           6 :                     m_poTPD->GetRowFromIntoTopConvention(
     228           6 :                         m_poTPD->m_nShiftYTiles +
     229           6 :                         nRasterYSize / 2 / nBlockYSize));
     230             :             }
     231          49 :             sqlite3_stmt *hStmt = nullptr;
     232          49 :             int rc = sqlite3_prepare_v2(m_poTPD->IGetDB(), pszSQL, -1, &hStmt,
     233             :                                         nullptr);
     234          49 :             if (rc == SQLITE_OK)
     235             :             {
     236          49 :                 rc = sqlite3_step(hStmt);
     237          65 :                 if (rc == SQLITE_ROW &&
     238          16 :                     sqlite3_column_type(hStmt, 0) == SQLITE_BLOB)
     239             :                 {
     240          16 :                     const int nBytes = sqlite3_column_bytes(hStmt, 0);
     241             :                     GByte *pabyRawData = reinterpret_cast<GByte *>(
     242          16 :                         const_cast<void *>(sqlite3_column_blob(hStmt, 0)));
     243          32 :                     CPLString osMemFileName;
     244          16 :                     osMemFileName.Printf("/vsimem/gpkg_read_tile_%p", this);
     245          16 :                     VSILFILE *fp = VSIFileFromMemBuffer(
     246             :                         osMemFileName.c_str(), pabyRawData, nBytes, FALSE);
     247          16 :                     VSIFCloseL(fp);
     248             : 
     249             :                     // Only PNG can have color table.
     250          16 :                     const char *const apszDrivers[] = {"PNG", nullptr};
     251             :                     auto poDSTile = std::unique_ptr<GDALDataset>(
     252             :                         GDALDataset::Open(osMemFileName.c_str(),
     253             :                                           GDAL_OF_RASTER | GDAL_OF_INTERNAL,
     254          32 :                                           apszDrivers, nullptr, nullptr));
     255          16 :                     if (poDSTile != nullptr)
     256             :                     {
     257          16 :                         if (poDSTile->GetRasterCount() == 1)
     258             :                         {
     259           8 :                             m_poTPD->m_poCT =
     260           4 :                                 poDSTile->GetRasterBand(1)->GetColorTable();
     261           4 :                             if (m_poTPD->m_poCT != nullptr)
     262           1 :                                 m_poTPD->m_poCT = m_poTPD->m_poCT->Clone();
     263             :                         }
     264             :                         else
     265             :                         {
     266          12 :                             bRetry = true;
     267             :                         }
     268             :                     }
     269             :                     else
     270           0 :                         bRetry = true;
     271             : 
     272          16 :                     VSIUnlink(osMemFileName);
     273             :                 }
     274             :             }
     275          49 :             sqlite3_free(pszSQL);
     276          49 :             sqlite3_finalize(hStmt);
     277          49 :             if (!bRetry)
     278          37 :                 break;
     279             :         }
     280             :     }
     281             : 
     282         182 :     return m_poTPD->m_poCT;
     283             : }
     284             : 
     285             : /************************************************************************/
     286             : /*                             SetColorTable()                          */
     287             : /************************************************************************/
     288             : 
     289          15 : CPLErr GDALGPKGMBTilesLikeRasterBand::SetColorTable(GDALColorTable *poCT)
     290             : {
     291          15 :     if (m_poTPD->m_eDT != GDT_Byte)
     292           0 :         return CE_Failure;
     293          15 :     if (poDS->GetRasterCount() != 1)
     294             :     {
     295           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     296             :                  "SetColorTable() only supported for a single band dataset");
     297           1 :         return CE_Failure;
     298             :     }
     299          14 :     if (!m_poTPD->m_bNew || m_poTPD->m_bTriedEstablishingCT)
     300             :     {
     301           2 :         CPLError(CE_Failure, CPLE_NotSupported,
     302             :                  "SetColorTable() only supported on a newly created dataset");
     303           2 :         return CE_Failure;
     304             :     }
     305             : 
     306          12 :     AssignColorTable(poCT);
     307          12 :     return CE_None;
     308             : }
     309             : 
     310             : /************************************************************************/
     311             : /*                          AssignColorTable()                          */
     312             : /************************************************************************/
     313             : 
     314          15 : void GDALGPKGMBTilesLikeRasterBand::AssignColorTable(const GDALColorTable *poCT)
     315             : {
     316          15 :     m_poTPD->m_bTriedEstablishingCT = true;
     317          15 :     delete m_poTPD->m_poCT;
     318          15 :     if (poCT != nullptr)
     319          14 :         m_poTPD->m_poCT = poCT->Clone();
     320             :     else
     321           1 :         m_poTPD->m_poCT = nullptr;
     322          15 : }
     323             : 
     324             : /************************************************************************/
     325             : /*                        GetColorInterpretation()                      */
     326             : /************************************************************************/
     327             : 
     328         196 : GDALColorInterp GDALGPKGMBTilesLikeRasterBand::GetColorInterpretation()
     329             : {
     330         196 :     if (m_poTPD->m_eDT != GDT_Byte)
     331          21 :         return GCI_Undefined;
     332         175 :     if (poDS->GetRasterCount() == 1)
     333          40 :         return GetColorTable() ? GCI_PaletteIndex : GCI_GrayIndex;
     334         135 :     else if (poDS->GetRasterCount() == 2)
     335           5 :         return (nBand == 1) ? GCI_GrayIndex : GCI_AlphaBand;
     336             :     else
     337         130 :         return static_cast<GDALColorInterp>(GCI_RedBand + (nBand - 1));
     338             : }
     339             : 
     340             : /************************************************************************/
     341             : /*                        SetColorInterpretation()                      */
     342             : /************************************************************************/
     343             : 
     344             : CPLErr
     345          30 : GDALGPKGMBTilesLikeRasterBand::SetColorInterpretation(GDALColorInterp eInterp)
     346             : {
     347          30 :     if (eInterp == GCI_Undefined)
     348           1 :         return CE_None;
     349          31 :     if (poDS->GetRasterCount() == 1 &&
     350           2 :         (eInterp == GCI_GrayIndex || eInterp == GCI_PaletteIndex))
     351          22 :         return CE_None;
     352          11 :     if (poDS->GetRasterCount() == 2 &&
     353           4 :         ((nBand == 1 && eInterp == GCI_GrayIndex) ||
     354           3 :          (nBand == 2 && eInterp == GCI_AlphaBand)))
     355           2 :         return CE_None;
     356           5 :     if (poDS->GetRasterCount() >= 3 && eInterp == GCI_RedBand + nBand - 1)
     357           1 :         return CE_None;
     358           4 :     CPLError(CE_Warning, CPLE_NotSupported,
     359             :              "%s color interpretation not supported. Will be ignored",
     360             :              GDALGetColorInterpretationName(eInterp));
     361           4 :     return CE_Warning;
     362             : }
     363             : 
     364             : /************************************************************************/
     365             : /*                        GPKGFindBestEntry()                           */
     366             : /************************************************************************/
     367             : 
     368           1 : static int GPKGFindBestEntry(GDALColorTable *poCT, GByte c1, GByte c2, GByte c3,
     369             :                              GByte c4, int nTileBandCount)
     370             : {
     371           1 :     const int nEntries = std::min(256, poCT->GetColorEntryCount());
     372           1 :     int iBestIdx = 0;
     373           1 :     int nBestDistance = 4 * 256 * 256;
     374         257 :     for (int i = 0; i < nEntries; i++)
     375             :     {
     376         256 :         const GDALColorEntry *psEntry = poCT->GetColorEntry(i);
     377         256 :         int nDistance = (psEntry->c1 - c1) * (psEntry->c1 - c1) +
     378         256 :                         (psEntry->c2 - c2) * (psEntry->c2 - c2) +
     379         256 :                         (psEntry->c3 - c3) * (psEntry->c3 - c3);
     380         256 :         if (nTileBandCount == 4)
     381         256 :             nDistance += (psEntry->c4 - c4) * (psEntry->c4 - c4);
     382         256 :         if (nDistance < nBestDistance)
     383             :         {
     384          10 :             iBestIdx = i;
     385          10 :             nBestDistance = nDistance;
     386             :         }
     387             :     }
     388           1 :     return iBestIdx;
     389             : }
     390             : 
     391             : /************************************************************************/
     392             : /*                             FillBuffer()                             */
     393             : /************************************************************************/
     394             : 
     395       12732 : void GDALGPKGMBTilesLikePseudoDataset::FillBuffer(GByte *pabyData,
     396             :                                                   size_t nPixels)
     397             : {
     398       12732 :     int bHasNoData = FALSE;
     399       12732 :     const double dfNoDataValue = IGetRasterBand(1)->GetNoDataValue(&bHasNoData);
     400       12732 :     if (!bHasNoData || dfNoDataValue == 0.0)
     401             :     {
     402             :         // cppcheck-suppress nullPointer
     403       12430 :         memset(pabyData, 0, nPixels * m_nDTSize);
     404             :     }
     405             :     else
     406             :     {
     407         302 :         GDALCopyWords64(&dfNoDataValue, GDT_Float64, 0, pabyData, m_eDT,
     408             :                         m_nDTSize, nPixels);
     409             :     }
     410       12732 : }
     411             : 
     412             : /************************************************************************/
     413             : /*                           FillEmptyTile()                            */
     414             : /************************************************************************/
     415             : 
     416        2938 : void GDALGPKGMBTilesLikePseudoDataset::FillEmptyTile(GByte *pabyData)
     417             : {
     418             :     int nBlockXSize, nBlockYSize;
     419        2938 :     IGetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
     420        2938 :     const int nBands = IGetRasterCount();
     421        2938 :     const size_t nPixels =
     422        2938 :         static_cast<size_t>(nBands) * nBlockXSize * nBlockYSize;
     423        2938 :     FillBuffer(pabyData, nPixels);
     424        2938 : }
     425             : 
     426             : /************************************************************************/
     427             : /*                    FillEmptyTileSingleBand()                         */
     428             : /************************************************************************/
     429             : 
     430        1481 : void GDALGPKGMBTilesLikePseudoDataset::FillEmptyTileSingleBand(GByte *pabyData)
     431             : {
     432             :     int nBlockXSize, nBlockYSize;
     433        1481 :     IGetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
     434        1481 :     const size_t nPixels = static_cast<size_t>(nBlockXSize) * nBlockYSize;
     435        1481 :     FillBuffer(pabyData, nPixels);
     436        1481 : }
     437             : 
     438             : /************************************************************************/
     439             : /*                           ReadTile()                                 */
     440             : /************************************************************************/
     441             : 
     442        2579 : CPLErr GDALGPKGMBTilesLikePseudoDataset::ReadTile(
     443             :     const CPLString &osMemFileName, GByte *pabyTileData, double dfTileOffset,
     444             :     double dfTileScale, bool *pbIsLossyFormat)
     445             : {
     446        2579 :     const char *const apszDriversByte[] = {"JPEG", "PNG", "WEBP", nullptr};
     447        2579 :     const char *const apszDriversInt[] = {"PNG", nullptr};
     448        2579 :     const char *const apszDriversFloat[] = {"GTiff", nullptr};
     449             :     int nBlockXSize, nBlockYSize;
     450        2579 :     IGetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
     451        2579 :     const int nBands = IGetRasterCount();
     452             :     auto poDSTile = std::unique_ptr<GDALDataset>(GDALDataset::Open(
     453             :         osMemFileName.c_str(), GDAL_OF_RASTER | GDAL_OF_INTERNAL,
     454        2579 :         (m_eDT == GDT_Byte)                   ? apszDriversByte
     455          41 :         : (m_eTF == GPKG_TF_TIFF_32BIT_FLOAT) ? apszDriversFloat
     456             :                                               : apszDriversInt,
     457        5158 :         nullptr, nullptr));
     458        2579 :     if (poDSTile == nullptr)
     459             :     {
     460           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot parse tile data");
     461           2 :         FillEmptyTile(pabyTileData);
     462           2 :         return CE_Failure;
     463             :     }
     464             : 
     465        2577 :     const int nTileBandCount = poDSTile->GetRasterCount();
     466             : 
     467        5154 :     if (!(poDSTile->GetRasterXSize() == nBlockXSize &&
     468        2577 :           poDSTile->GetRasterYSize() == nBlockYSize &&
     469        7731 :           (nTileBandCount >= 1 && nTileBandCount <= 4)) ||
     470        2577 :         (m_eDT != GDT_Byte && nTileBandCount != 1))
     471             :     {
     472           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     473             :                  "Inconsistent tiles characteristics");
     474           0 :         FillEmptyTile(pabyTileData);
     475           0 :         return CE_Failure;
     476             :     }
     477             : 
     478        2577 :     GDALDataType eRequestDT = GDT_Byte;
     479        2577 :     if (m_eTF == GPKG_TF_PNG_16BIT)
     480             :     {
     481          38 :         CPLAssert(m_eDT == GDT_Int16 || m_eDT == GDT_UInt16 ||
     482             :                   m_eDT == GDT_Float32);
     483          38 :         eRequestDT = GDT_UInt16;
     484             :     }
     485        2539 :     else if (m_eTF == GPKG_TF_TIFF_32BIT_FLOAT)
     486             :     {
     487           3 :         CPLAssert(m_eDT == GDT_Float32);
     488           3 :         eRequestDT = GDT_Float32;
     489             :     }
     490             : 
     491        2577 :     if (poDSTile->RasterIO(GF_Read, 0, 0, nBlockXSize, nBlockYSize,
     492             :                            pabyTileData, nBlockXSize, nBlockYSize, eRequestDT,
     493             :                            poDSTile->GetRasterCount(), nullptr, 0, 0, 0,
     494        2577 :                            nullptr) != CE_None)
     495             :     {
     496           0 :         FillEmptyTile(pabyTileData);
     497           0 :         return CE_Failure;
     498             :     }
     499             : 
     500        2577 :     if (m_eDT != GDT_Byte)
     501             :     {
     502          41 :         int bHasNoData = FALSE;
     503             :         const double dfNoDataValue =
     504          41 :             IGetRasterBand(1)->GetNoDataValue(&bHasNoData);
     505          41 :         if (m_eDT == GDT_Int16)
     506             :         {
     507          27 :             CPLAssert(eRequestDT == GDT_UInt16);
     508          27 :             for (size_t i = 0;
     509     1769500 :                  i < static_cast<size_t>(nBlockXSize) * nBlockYSize; i++)
     510             :             {
     511             :                 GUInt16 usVal;
     512     1769470 :                 memcpy(&usVal, pabyTileData + i * sizeof(GUInt16),
     513             :                        sizeof(usVal));
     514             :                 double dfVal =
     515     1769470 :                     floor((usVal * dfTileScale + dfTileOffset) * m_dfScale +
     516     1769470 :                           m_dfOffset + 0.5);
     517     1769470 :                 if (bHasNoData && usVal == m_usGPKGNull)
     518      195804 :                     dfVal = dfNoDataValue;
     519     1769470 :                 if (dfVal > 32767)
     520           0 :                     dfVal = 32767;
     521     1769470 :                 else if (dfVal < -32768)
     522           0 :                     dfVal = -32768;
     523     1769470 :                 GInt16 sVal = static_cast<GInt16>(dfVal);
     524     1769470 :                 memcpy(pabyTileData + i * sizeof(GUInt16), &sVal, sizeof(sVal));
     525             :             }
     526             :         }
     527          14 :         else if (m_eDT == GDT_UInt16 &&
     528           6 :                  (m_dfOffset != 0.0 || m_dfScale != 1.0 ||
     529           6 :                   dfTileOffset != 0.0 || dfTileScale != 1.0))
     530             :         {
     531           0 :             CPLAssert(eRequestDT == GDT_UInt16);
     532           0 :             GUInt16 *psVal = reinterpret_cast<GUInt16 *>(pabyTileData);
     533           0 :             for (size_t i = 0;
     534           0 :                  i < static_cast<size_t>(nBlockXSize) * nBlockYSize; i++)
     535             :             {
     536           0 :                 const GUInt16 nVal = psVal[i];
     537             :                 double dfVal =
     538           0 :                     floor((nVal * dfTileScale + dfTileOffset) * m_dfScale +
     539           0 :                           m_dfOffset + 0.5);
     540           0 :                 if (bHasNoData && nVal == m_usGPKGNull)
     541           0 :                     dfVal = dfNoDataValue;
     542           0 :                 if (dfVal > 65535)
     543           0 :                     dfVal = 65535;
     544           0 :                 else if (dfVal < 0)
     545           0 :                     dfVal = 0;
     546           0 :                 psVal[i] = static_cast<GUInt16>(dfVal);
     547           0 :             }
     548             :         }
     549          14 :         else if (m_eDT == GDT_Float32 && eRequestDT == GDT_UInt16)
     550             :         {
     551             :             // Due to non identical data type size, we need to start from the
     552             :             // end of the buffer.
     553      327685 :             for (GPtrDiff_t i =
     554           5 :                      static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize - 1;
     555      327685 :                  i >= 0; i--)
     556             :             {
     557             :                 // Use memcpy() and not reinterpret_cast<GUInt16*> and
     558             :                 // reinterpret_cast<float*>, otherwise compilers such as ICC
     559             :                 // may (ab)use rules about aliasing to generate wrong code!
     560             :                 GUInt16 usVal;
     561      327680 :                 memcpy(&usVal, pabyTileData + i * sizeof(GUInt16),
     562             :                        sizeof(usVal));
     563      327680 :                 double dfVal =
     564      327680 :                     (usVal * dfTileScale + dfTileOffset) * m_dfScale +
     565      327680 :                     m_dfOffset;
     566      327680 :                 if (m_dfPrecision == 1.0)
     567      327680 :                     dfVal = floor(dfVal + 0.5);
     568      327680 :                 if (bHasNoData && usVal == m_usGPKGNull)
     569      196206 :                     dfVal = dfNoDataValue;
     570      327680 :                 const float fVal = static_cast<float>(dfVal);
     571      327680 :                 memcpy(pabyTileData + i * sizeof(float), &fVal, sizeof(fVal));
     572             :             }
     573             :         }
     574             : 
     575          41 :         return CE_None;
     576             :     }
     577             : 
     578        2536 :     GDALColorTable *poCT = nullptr;
     579        2536 :     if (nBands == 1 || nTileBandCount == 1)
     580             :     {
     581         150 :         poCT = poDSTile->GetRasterBand(1)->GetColorTable();
     582         150 :         IGetRasterBand(1)->GetColorTable();
     583             :     }
     584             : 
     585        2536 :     if (pbIsLossyFormat)
     586           9 :         *pbIsLossyFormat =
     587           9 :             !EQUAL(poDSTile->GetDriver()->GetDescription(), "PNG") ||
     588           0 :             (poCT != nullptr && poCT->GetColorEntryCount() == 256) /* PNG8 */;
     589             : 
     590             :     /* Map RGB(A) tile to single-band color indexed */
     591        2536 :     const GPtrDiff_t nBlockPixels =
     592        2536 :         static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize;
     593        2536 :     if (nBands == 1 && m_poCT != nullptr && nTileBandCount != 1)
     594             :     {
     595           1 :         std::map<GUInt32, int> oMapEntryToIndex;
     596           1 :         const int nEntries = std::min(256, m_poCT->GetColorEntryCount());
     597         257 :         for (int i = 0; i < nEntries; i++)
     598             :         {
     599         256 :             const GDALColorEntry *psEntry = m_poCT->GetColorEntry(i);
     600         256 :             GByte c1 = static_cast<GByte>(psEntry->c1);
     601         256 :             GByte c2 = static_cast<GByte>(psEntry->c2);
     602         256 :             GByte c3 = static_cast<GByte>(psEntry->c3);
     603         256 :             GUInt32 nVal = c1 + (c2 << 8) + (c3 << 16);
     604         256 :             if (nTileBandCount == 4)
     605         256 :                 nVal += (static_cast<GUInt32>(psEntry->c4) << 24);
     606         256 :             oMapEntryToIndex[nVal] = i;
     607             :         }
     608             :         int iBestEntryFor0 =
     609           1 :             GPKGFindBestEntry(m_poCT, 0, 0, 0, 0, nTileBandCount);
     610       10001 :         for (GPtrDiff_t i = 0; i < nBlockPixels; i++)
     611             :         {
     612       10000 :             const GByte c1 = pabyTileData[i];
     613       10000 :             const GByte c2 = pabyTileData[i + nBlockPixels];
     614       10000 :             const GByte c3 = pabyTileData[i + 2 * nBlockPixels];
     615       10000 :             const GByte c4 = pabyTileData[i + 3 * nBlockPixels];
     616       10000 :             GUInt32 nVal = c1 + (c2 << 8) + (c3 << 16);
     617       10000 :             if (nTileBandCount == 4)
     618       10000 :                 nVal += (c4 << 24);
     619       10000 :             if (nVal == 0)
     620             :                 // In most cases we will reach that point at partial tiles.
     621        5000 :                 pabyTileData[i] = static_cast<GByte>(iBestEntryFor0);
     622             :             else
     623             :             {
     624             :                 std::map<GUInt32, int>::iterator oMapEntryToIndexIter =
     625        5000 :                     oMapEntryToIndex.find(nVal);
     626        5000 :                 if (oMapEntryToIndexIter == oMapEntryToIndex.end())
     627             :                     /* Could happen with JPEG tiles */
     628           0 :                     pabyTileData[i] = static_cast<GByte>(GPKGFindBestEntry(
     629             :                         m_poCT, c1, c2, c3, c4, nTileBandCount));
     630             :                 else
     631        5000 :                     pabyTileData[i] =
     632        5000 :                         static_cast<GByte>(oMapEntryToIndexIter->second);
     633             :             }
     634             :         }
     635           1 :         return CE_None;
     636             :     }
     637             : 
     638          28 :     if (nBands == 1 && nTileBandCount == 1 && poCT != nullptr &&
     639        2563 :         m_poCT != nullptr && !poCT->IsSame(m_poCT))
     640             :     {
     641           0 :         CPLError(CE_Warning, CPLE_NotSupported,
     642             :                  "Different color tables. Unhandled for now");
     643             :     }
     644        2535 :     else if ((nBands == 1 && nTileBandCount >= 3) ||
     645          28 :              (nBands == 1 && nTileBandCount == 1 && m_poCT != nullptr &&
     646        2535 :               poCT == nullptr) ||
     647        2535 :              ((nBands == 1 || nBands == 2) && nTileBandCount == 1 &&
     648          34 :               m_poCT == nullptr && poCT != nullptr))
     649             :     {
     650           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     651             :                  "Inconsistent dataset and tiles band characteristics");
     652             :     }
     653             : 
     654        2535 :     if (nBands == 2)
     655             :     {
     656             :         // assuming that the RGB is Grey,Grey,Grey
     657         307 :         if (nTileBandCount == 1 || nTileBandCount == 3)
     658             :         {
     659             :             /* Create fully opaque alpha */
     660          97 :             memset(pabyTileData + 1 * nBlockPixels, 255, nBlockPixels);
     661             :         }
     662         210 :         else if (nTileBandCount == 4)
     663             :         {
     664             :             /* Transfer alpha band */
     665          67 :             memcpy(pabyTileData + 1 * nBlockPixels,
     666          67 :                    pabyTileData + 3 * nBlockPixels, nBlockPixels);
     667             :         }
     668             :     }
     669        2228 :     else if (nTileBandCount == 2)
     670             :     {
     671             :         /* Do Grey+Alpha -> RGBA */
     672         366 :         memcpy(pabyTileData + 3 * nBlockPixels, pabyTileData + 1 * nBlockPixels,
     673             :                nBlockPixels);
     674         366 :         memcpy(pabyTileData + 1 * nBlockPixels, pabyTileData, nBlockPixels);
     675         366 :         memcpy(pabyTileData + 2 * nBlockPixels, pabyTileData, nBlockPixels);
     676             :     }
     677        1862 :     else if (nTileBandCount == 1 && !(nBands == 1 && m_poCT != nullptr))
     678             :     {
     679             :         /* Expand color indexed to RGB(A) */
     680         121 :         if (poCT != nullptr)
     681             :         {
     682             :             GByte abyCT[4 * 256];
     683          15 :             const int nEntries = std::min(256, poCT->GetColorEntryCount());
     684        3600 :             for (int i = 0; i < nEntries; i++)
     685             :             {
     686        3585 :                 const GDALColorEntry *psEntry = poCT->GetColorEntry(i);
     687        3585 :                 abyCT[4 * i] = static_cast<GByte>(psEntry->c1);
     688        3585 :                 abyCT[4 * i + 1] = static_cast<GByte>(psEntry->c2);
     689        3585 :                 abyCT[4 * i + 2] = static_cast<GByte>(psEntry->c3);
     690        3585 :                 abyCT[4 * i + 3] = static_cast<GByte>(psEntry->c4);
     691             :             }
     692         270 :             for (int i = nEntries; i < 256; i++)
     693             :             {
     694         255 :                 abyCT[4 * i] = 0;
     695         255 :                 abyCT[4 * i + 1] = 0;
     696         255 :                 abyCT[4 * i + 2] = 0;
     697         255 :                 abyCT[4 * i + 3] = 0;
     698             :             }
     699      967175 :             for (GPtrDiff_t i = 0; i < nBlockPixels; i++)
     700             :             {
     701      967160 :                 const GByte byVal = pabyTileData[i];
     702      967160 :                 pabyTileData[i] = abyCT[4 * byVal];
     703      967160 :                 pabyTileData[i + 1 * nBlockPixels] = abyCT[4 * byVal + 1];
     704      967160 :                 pabyTileData[i + 2 * nBlockPixels] = abyCT[4 * byVal + 2];
     705      967160 :                 pabyTileData[i + 3 * nBlockPixels] = abyCT[4 * byVal + 3];
     706             :             }
     707             :         }
     708             :         else
     709             :         {
     710         106 :             memcpy(pabyTileData + 1 * nBlockPixels, pabyTileData, nBlockPixels);
     711         106 :             memcpy(pabyTileData + 2 * nBlockPixels, pabyTileData, nBlockPixels);
     712         106 :             if (nBands == 4)
     713             :             {
     714          96 :                 memset(pabyTileData + 3 * nBlockPixels, 255, nBlockPixels);
     715             :             }
     716         121 :         }
     717             :     }
     718        1741 :     else if (nTileBandCount == 3 && nBands == 4)
     719             :     {
     720             :         /* Create fully opaque alpha */
     721         224 :         memset(pabyTileData + 3 * nBlockPixels, 255, nBlockPixels);
     722             :     }
     723             : 
     724        2535 :     return CE_None;
     725             : }
     726             : 
     727             : /************************************************************************/
     728             : /*                           ReadTile()                                 */
     729             : /************************************************************************/
     730             : 
     731        7522 : GByte *GDALGPKGMBTilesLikePseudoDataset::ReadTile(int nRow, int nCol)
     732             : {
     733             :     int nBlockXSize, nBlockYSize;
     734        7522 :     IGetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
     735        7522 :     const int nBands = IGetRasterCount();
     736        7522 :     const size_t nBandBlockSize =
     737        7522 :         static_cast<size_t>(nBlockXSize) * nBlockYSize * m_nDTSize;
     738        7522 :     const int nTileBands = m_eDT == GDT_Byte ? 4 : 1;
     739        7522 :     if (m_nShiftXPixelsMod || m_nShiftYPixelsMod)
     740             :     {
     741        4803 :         GByte *pabyData = nullptr;
     742        4803 :         int i = 0;
     743       11981 :         for (; i < 4; i++)
     744             :         {
     745       11981 :             if (m_asCachedTilesDesc[i].nRow == nRow &&
     746        7181 :                 m_asCachedTilesDesc[i].nCol == nCol)
     747             :             {
     748        4803 :                 if (m_asCachedTilesDesc[i].nIdxWithinTileData >= 0)
     749             :                 {
     750        2040 :                     return m_pabyCachedTiles +
     751        2040 :                            nBandBlockSize *
     752        2040 :                                m_asCachedTilesDesc[i].nIdxWithinTileData *
     753        2040 :                                nTileBands;
     754             :                 }
     755             :                 else
     756             :                 {
     757        2763 :                     if (i == 0)
     758         194 :                         m_asCachedTilesDesc[i].nIdxWithinTileData =
     759         194 :                             (m_asCachedTilesDesc[1].nIdxWithinTileData == 0)
     760         194 :                                 ? 1
     761             :                                 : 0;
     762        2569 :                     else if (i == 1)
     763        1189 :                         m_asCachedTilesDesc[i].nIdxWithinTileData =
     764        1189 :                             (m_asCachedTilesDesc[0].nIdxWithinTileData == 0)
     765        1189 :                                 ? 1
     766             :                                 : 0;
     767        1380 :                     else if (i == 2)
     768         191 :                         m_asCachedTilesDesc[i].nIdxWithinTileData =
     769         191 :                             (m_asCachedTilesDesc[3].nIdxWithinTileData == 2)
     770         191 :                                 ? 3
     771             :                                 : 2;
     772             :                     else
     773        1189 :                         m_asCachedTilesDesc[i].nIdxWithinTileData =
     774        1189 :                             (m_asCachedTilesDesc[2].nIdxWithinTileData == 2)
     775        1189 :                                 ? 3
     776             :                                 : 2;
     777        2763 :                     pabyData = m_pabyCachedTiles +
     778        2763 :                                nBandBlockSize *
     779        2763 :                                    m_asCachedTilesDesc[i].nIdxWithinTileData *
     780        2763 :                                    nTileBands;
     781        2763 :                     break;
     782             :                 }
     783             :             }
     784             :         }
     785        2763 :         CPLAssert(i < 4);
     786        2763 :         return ReadTile(nRow, nCol, pabyData);
     787             :     }
     788             :     else
     789             :     {
     790        2719 :         GByte *pabyDest = m_pabyCachedTiles + 2 * nTileBands * nBandBlockSize;
     791        2719 :         bool bAllNonDirty = true;
     792       11179 :         for (int i = 0; i < nBands; i++)
     793             :         {
     794        8460 :             if (m_asCachedTilesDesc[0].abBandDirty[i])
     795           2 :                 bAllNonDirty = false;
     796             :         }
     797        2719 :         if (bAllNonDirty)
     798             :         {
     799        2717 :             return ReadTile(nRow, nCol, pabyDest);
     800             :         }
     801             : 
     802             :         /* If some bands of the blocks are dirty/written we need to fetch */
     803             :         /* the tile in a temporary buffer in order not to override dirty bands*/
     804           2 :         GByte *pabyTemp = m_pabyCachedTiles + 3 * nTileBands * nBandBlockSize;
     805           2 :         if (ReadTile(nRow, nCol, pabyTemp) != nullptr)
     806             :         {
     807           8 :             for (int i = 0; i < nBands; i++)
     808             :             {
     809           6 :                 if (!m_asCachedTilesDesc[0].abBandDirty[i])
     810             :                 {
     811           4 :                     memcpy(pabyDest + i * nBandBlockSize,
     812           4 :                            pabyTemp + i * nBandBlockSize, nBandBlockSize);
     813             :                 }
     814             :             }
     815             :         }
     816           2 :         return pabyDest;
     817             :     }
     818             : }
     819             : 
     820             : /************************************************************************/
     821             : /*                         GetTileOffsetAndScale()                      */
     822             : /************************************************************************/
     823             : 
     824        2579 : void GDALGPKGMBTilesLikePseudoDataset::GetTileOffsetAndScale(
     825             :     GIntBig nTileId, double &dfTileOffset, double &dfTileScale)
     826             : {
     827        2579 :     dfTileOffset = 0.0;
     828        2579 :     dfTileScale = 1.0;
     829             : 
     830        2579 :     if (m_eTF == GPKG_TF_PNG_16BIT)
     831             :     {
     832          38 :         char *pszSQL = sqlite3_mprintf(
     833             :             "SELECT offset, scale FROM gpkg_2d_gridded_tile_ancillary WHERE "
     834             :             "tpudt_name = '%q' AND tpudt_id = ?",
     835             :             m_osRasterTable.c_str());
     836          38 :         sqlite3_stmt *hStmt = nullptr;
     837          38 :         int rc = sqlite3_prepare_v2(IGetDB(), pszSQL, -1, &hStmt, nullptr);
     838          38 :         if (rc == SQLITE_OK)
     839             :         {
     840          38 :             sqlite3_bind_int64(hStmt, 1, nTileId);
     841          38 :             rc = sqlite3_step(hStmt);
     842          38 :             if (rc == SQLITE_ROW)
     843             :             {
     844          38 :                 if (sqlite3_column_type(hStmt, 0) == SQLITE_FLOAT)
     845          38 :                     dfTileOffset = sqlite3_column_double(hStmt, 0);
     846          38 :                 if (sqlite3_column_type(hStmt, 1) == SQLITE_FLOAT)
     847          38 :                     dfTileScale = sqlite3_column_double(hStmt, 1);
     848             :             }
     849          38 :             sqlite3_finalize(hStmt);
     850             :         }
     851          38 :         sqlite3_free(pszSQL);
     852             :     }
     853        2579 : }
     854             : 
     855             : /************************************************************************/
     856             : /*                           ReadTile()                                 */
     857             : /************************************************************************/
     858             : 
     859        5500 : GByte *GDALGPKGMBTilesLikePseudoDataset::ReadTile(int nRow, int nCol,
     860             :                                                   GByte *pabyData,
     861             :                                                   bool *pbIsLossyFormat)
     862             : {
     863        5500 :     int nBlockXSize = 0;
     864        5500 :     int nBlockYSize = 0;
     865        5500 :     IGetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
     866        5500 :     const int nBands = IGetRasterCount();
     867             : 
     868        5500 :     if (pbIsLossyFormat)
     869          18 :         *pbIsLossyFormat = false;
     870             : 
     871        5500 :     const size_t nBandBlockSize =
     872        5500 :         static_cast<size_t>(nBlockXSize) * nBlockYSize * m_nDTSize;
     873        5500 :     if (nRow < 0 || nCol < 0 || nRow >= m_nTileMatrixHeight ||
     874        5443 :         nCol >= m_nTileMatrixWidth)
     875             :     {
     876          73 :         FillEmptyTile(pabyData);
     877          73 :         return pabyData;
     878             :     }
     879             : 
     880             : #ifdef DEBUG_VERBOSE
     881             :     CPLDebug("GPKG", "ReadTile(row=%d, col=%d)", nRow, nCol);
     882             : #endif
     883             : 
     884       16281 :     char *pszSQL = sqlite3_mprintf(
     885             :         "SELECT tile_data%s FROM \"%w\" "
     886             :         "WHERE zoom_level = %d AND tile_row = %d AND tile_column = %d%s",
     887        5427 :         m_eDT != GDT_Byte ? ", id" : "",  // MBTiles do not have an id
     888             :         m_osRasterTable.c_str(), m_nZoomLevel,
     889        5427 :         GetRowFromIntoTopConvention(nRow), nCol,
     890        5427 :         !m_osWHERE.empty() ? CPLSPrintf(" AND (%s)", m_osWHERE.c_str()) : "");
     891             : 
     892             : #ifdef DEBUG_VERBOSE
     893             :     CPLDebug("GPKG", "%s", pszSQL);
     894             : #endif
     895             : 
     896        5427 :     sqlite3_stmt *hStmt = nullptr;
     897        5427 :     int rc = sqlite3_prepare_v2(IGetDB(), pszSQL, -1, &hStmt, nullptr);
     898        5427 :     if (rc != SQLITE_OK)
     899             :     {
     900           0 :         CPLError(CE_Failure, CPLE_AppDefined, "failed to prepare SQL %s: %s",
     901           0 :                  pszSQL, sqlite3_errmsg(IGetDB()));
     902           0 :         sqlite3_free(pszSQL);
     903           0 :         return nullptr;
     904             :     }
     905        5427 :     sqlite3_free(pszSQL);
     906        5427 :     rc = sqlite3_step(hStmt);
     907             : 
     908        5427 :     if (rc == SQLITE_ROW && sqlite3_column_type(hStmt, 0) == SQLITE_BLOB)
     909             :     {
     910        2562 :         const int nBytes = sqlite3_column_bytes(hStmt, 0);
     911             :         GIntBig nTileId =
     912        2562 :             (m_eDT == GDT_Byte) ? 0 : sqlite3_column_int64(hStmt, 1);
     913             :         GByte *pabyRawData = static_cast<GByte *>(
     914        2562 :             const_cast<void *>(sqlite3_column_blob(hStmt, 0)));
     915        5124 :         CPLString osMemFileName;
     916        2562 :         osMemFileName.Printf("/vsimem/gpkg_read_tile_%p", this);
     917        2562 :         VSILFILE *fp = VSIFileFromMemBuffer(osMemFileName.c_str(), pabyRawData,
     918             :                                             nBytes, FALSE);
     919        2562 :         VSIFCloseL(fp);
     920             : 
     921        2562 :         double dfTileOffset = 0.0;
     922        2562 :         double dfTileScale = 1.0;
     923        2562 :         GetTileOffsetAndScale(nTileId, dfTileOffset, dfTileScale);
     924        2562 :         ReadTile(osMemFileName, pabyData, dfTileOffset, dfTileScale,
     925             :                  pbIsLossyFormat);
     926        2562 :         VSIUnlink(osMemFileName);
     927        2562 :         sqlite3_finalize(hStmt);
     928             :     }
     929        2865 :     else if (rc == SQLITE_BUSY)
     930             :     {
     931           0 :         FillEmptyTile(pabyData);
     932           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     933             :                  "sqlite3_step(%s) failed (SQLITE_BUSY): %s",
     934           0 :                  sqlite3_sql(hStmt), sqlite3_errmsg(IGetDB()));
     935           0 :         sqlite3_finalize(hStmt);
     936           0 :         return pabyData;
     937             :     }
     938             :     else
     939             :     {
     940        2865 :         sqlite3_finalize(hStmt);
     941        2865 :         hStmt = nullptr;
     942             : 
     943        2865 :         if (m_hTempDB && (m_nShiftXPixelsMod || m_nShiftYPixelsMod))
     944             :         {
     945         602 :             const char *pszSQLNew = CPLSPrintf(
     946             :                 "SELECT partial_flag, tile_data_band_1, tile_data_band_2, "
     947             :                 "tile_data_band_3, tile_data_band_4 FROM partial_tiles WHERE "
     948             :                 "zoom_level = %d AND tile_row = %d AND tile_column = %d",
     949             :                 m_nZoomLevel, nRow, nCol);
     950             : 
     951             : #ifdef DEBUG_VERBOSE
     952             :             CPLDebug("GPKG", "%s", pszSQLNew);
     953             : #endif
     954             : 
     955         602 :             rc = sqlite3_prepare_v2(m_hTempDB, pszSQLNew, -1, &hStmt, nullptr);
     956         602 :             if (rc != SQLITE_OK)
     957             :             {
     958           0 :                 FillEmptyTile(pabyData);
     959           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     960             :                          "sqlite3_prepare_v2(%s) failed: %s", pszSQLNew,
     961             :                          sqlite3_errmsg(m_hTempDB));
     962           0 :                 return pabyData;
     963             :             }
     964             : 
     965         602 :             rc = sqlite3_step(hStmt);
     966         602 :             if (rc == SQLITE_ROW)
     967             :             {
     968           2 :                 const int nPartialFlag = sqlite3_column_int(hStmt, 0);
     969          10 :                 for (int iBand = 1; iBand <= nBands; iBand++)
     970             :                 {
     971           8 :                     GByte *pabyDestBand =
     972           8 :                         pabyData + (iBand - 1) * nBandBlockSize;
     973           8 :                     if (nPartialFlag & (((1 << 4) - 1) << (4 * (iBand - 1))))
     974             :                     {
     975           2 :                         CPLAssert(sqlite3_column_bytes(hStmt, iBand) ==
     976             :                                   static_cast<int>(nBandBlockSize));
     977           2 :                         memcpy(pabyDestBand, sqlite3_column_blob(hStmt, iBand),
     978             :                                nBandBlockSize);
     979             :                     }
     980             :                     else
     981             :                     {
     982           6 :                         FillEmptyTileSingleBand(pabyDestBand);
     983             :                     }
     984             :                 }
     985             :             }
     986             :             else
     987             :             {
     988         600 :                 FillEmptyTile(pabyData);
     989             :             }
     990         602 :             sqlite3_finalize(hStmt);
     991             :         }
     992             :         else
     993             :         {
     994        2263 :             FillEmptyTile(pabyData);
     995             :         }
     996             :     }
     997             : 
     998        5427 :     return pabyData;
     999             : }
    1000             : 
    1001             : /************************************************************************/
    1002             : /*                         IReadBlock()                                 */
    1003             : /************************************************************************/
    1004             : 
    1005        3930 : CPLErr GDALGPKGMBTilesLikeRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
    1006             :                                                  void *pData)
    1007             : {
    1008             : #ifdef DEBUG_VERBOSE
    1009             :     CPLDebug("GPKG",
    1010             :              "IReadBlock(nBand=%d,nBlockXOff=%d,nBlockYOff=%d,m_nZoomLevel=%d)",
    1011             :              nBand, nBlockXOff, nBlockYOff, m_poTPD->m_nZoomLevel);
    1012             : #endif
    1013             : 
    1014        3930 :     if (m_poTPD->m_pabyCachedTiles == nullptr)
    1015           0 :         return CE_Failure;
    1016             : 
    1017        3930 :     const int nRowMin = nBlockYOff + m_poTPD->m_nShiftYTiles;
    1018        3930 :     int nRowMax = nRowMin;
    1019        3930 :     if (m_poTPD->m_nShiftYPixelsMod)
    1020        1211 :         nRowMax++;
    1021             : 
    1022        3930 :     const int nColMin = nBlockXOff + m_poTPD->m_nShiftXTiles;
    1023        3930 :     int nColMax = nColMin;
    1024        3930 :     if (m_poTPD->m_nShiftXPixelsMod)
    1025        1189 :         nColMax++;
    1026             : 
    1027        2741 : retry:
    1028             :     /* Optimize for left to right reading at constant row */
    1029        3933 :     if (m_poTPD->m_nShiftXPixelsMod || m_poTPD->m_nShiftYPixelsMod)
    1030             :     {
    1031        1214 :         if (nRowMin == m_poTPD->m_asCachedTilesDesc[0].nRow &&
    1032        1036 :             nColMin == m_poTPD->m_asCachedTilesDesc[0].nCol + 1 &&
    1033        1030 :             m_poTPD->m_asCachedTilesDesc[0].nIdxWithinTileData >= 0)
    1034             :         {
    1035        1030 :             CPLAssert(nRowMin == m_poTPD->m_asCachedTilesDesc[1].nRow);
    1036        1030 :             CPLAssert(nColMin == m_poTPD->m_asCachedTilesDesc[1].nCol);
    1037        1030 :             CPLAssert(m_poTPD->m_asCachedTilesDesc[0].nIdxWithinTileData == 0 ||
    1038             :                       m_poTPD->m_asCachedTilesDesc[0].nIdxWithinTileData == 1);
    1039             : 
    1040             :             /* 0 1  --> 1 -1 */
    1041             :             /* 2 3      3 -1 */
    1042             :             /* or */
    1043             :             /* 1 0  --> 0 -1 */
    1044             :             /* 3 2      2 -1 */
    1045        1030 :             m_poTPD->m_asCachedTilesDesc[0].nIdxWithinTileData =
    1046        1030 :                 m_poTPD->m_asCachedTilesDesc[1].nIdxWithinTileData;
    1047        1030 :             m_poTPD->m_asCachedTilesDesc[2].nIdxWithinTileData =
    1048        1030 :                 m_poTPD->m_asCachedTilesDesc[3].nIdxWithinTileData;
    1049             :         }
    1050             :         else
    1051             :         {
    1052         184 :             m_poTPD->m_asCachedTilesDesc[0].nIdxWithinTileData = -1;
    1053         184 :             m_poTPD->m_asCachedTilesDesc[2].nIdxWithinTileData = -1;
    1054             :         }
    1055        1214 :         m_poTPD->m_asCachedTilesDesc[0].nRow = nRowMin;
    1056        1214 :         m_poTPD->m_asCachedTilesDesc[0].nCol = nColMin;
    1057        1214 :         m_poTPD->m_asCachedTilesDesc[1].nRow = nRowMin;
    1058        1214 :         m_poTPD->m_asCachedTilesDesc[1].nCol = nColMin + 1;
    1059        1214 :         m_poTPD->m_asCachedTilesDesc[2].nRow = nRowMin + 1;
    1060        1214 :         m_poTPD->m_asCachedTilesDesc[2].nCol = nColMin;
    1061        1214 :         m_poTPD->m_asCachedTilesDesc[3].nRow = nRowMin + 1;
    1062        1214 :         m_poTPD->m_asCachedTilesDesc[3].nCol = nColMin + 1;
    1063        1214 :         m_poTPD->m_asCachedTilesDesc[1].nIdxWithinTileData = -1;
    1064        1214 :         m_poTPD->m_asCachedTilesDesc[3].nIdxWithinTileData = -1;
    1065             :     }
    1066             : 
    1067        9074 :     for (int nRow = nRowMin; nRow <= nRowMax; nRow++)
    1068             :     {
    1069       12663 :         for (int nCol = nColMin; nCol <= nColMax; nCol++)
    1070             :         {
    1071        7522 :             if (m_poTPD->m_nShiftXPixelsMod == 0 &&
    1072        2766 :                 m_poTPD->m_nShiftYPixelsMod == 0)
    1073             :             {
    1074        2719 :                 if (!(nRow == m_poTPD->m_asCachedTilesDesc[0].nRow &&
    1075           7 :                       nCol == m_poTPD->m_asCachedTilesDesc[0].nCol &&
    1076           3 :                       m_poTPD->m_asCachedTilesDesc[0].nIdxWithinTileData == 0))
    1077             :                 {
    1078        2717 :                     if (m_poTPD->WriteTile() != CE_None)
    1079           0 :                         return CE_Failure;
    1080             :                 }
    1081             :             }
    1082             : 
    1083        7522 :             GByte *pabyTileData = m_poTPD->ReadTile(nRow, nCol);
    1084        7522 :             if (pabyTileData == nullptr)
    1085           0 :                 return CE_Failure;
    1086             : 
    1087       34552 :             for (int iBand = 1; iBand <= poDS->GetRasterCount(); iBand++)
    1088             :             {
    1089       27033 :                 GDALRasterBlock *poBlock = nullptr;
    1090       27033 :                 GByte *pabyDest = nullptr;
    1091       27033 :                 if (iBand == nBand)
    1092             :                 {
    1093        7522 :                     pabyDest = static_cast<GByte *>(pData);
    1094             :                 }
    1095             :                 else
    1096             :                 {
    1097       19511 :                     poBlock = poDS->GetRasterBand(iBand)->GetLockedBlockRef(
    1098             :                         nBlockXOff, nBlockYOff, TRUE);
    1099       19511 :                     if (poBlock == nullptr)
    1100           0 :                         continue;
    1101       19511 :                     if (poBlock->GetDirty())
    1102             :                     {
    1103           2 :                         poBlock->DropLock();
    1104           2 :                         continue;
    1105             :                     }
    1106             :                     /* if we are short of GDAL cache max and there are dirty
    1107             :                      * blocks */
    1108             :                     /* of our dataset, the above GetLockedBlockRef() might have
    1109             :                      * reset */
    1110             :                     /* (at least part of) the 4 tiles we want to cache and have
    1111             :                      */
    1112             :                     /* already read */
    1113             :                     // FIXME this is way too fragile.
    1114       19509 :                     if ((m_poTPD->m_nShiftXPixelsMod != 0 ||
    1115        5849 :                          m_poTPD->m_nShiftYPixelsMod != 0) &&
    1116       13770 :                         (m_poTPD->m_asCachedTilesDesc[0].nRow != nRowMin ||
    1117       13767 :                          m_poTPD->m_asCachedTilesDesc[0].nCol != nColMin))
    1118             :                     {
    1119           3 :                         poBlock->DropLock();
    1120           3 :                         goto retry;
    1121             :                     }
    1122       19506 :                     pabyDest = static_cast<GByte *>(poBlock->GetDataRef());
    1123             :                 }
    1124             : 
    1125             :                 // Composite tile data into block data
    1126       27028 :                 if (m_poTPD->m_nShiftXPixelsMod == 0 &&
    1127        8612 :                     m_poTPD->m_nShiftYPixelsMod == 0)
    1128             :                 {
    1129        8458 :                     const size_t nBandBlockSize =
    1130        8458 :                         static_cast<size_t>(nBlockXSize) * nBlockYSize *
    1131        8458 :                         m_nDTSize;
    1132        8458 :                     memcpy(pabyDest,
    1133        8458 :                            pabyTileData + (iBand - 1) * nBandBlockSize,
    1134        8458 :                            nBandBlockSize);
    1135             : #ifdef DEBUG_VERBOSE
    1136             :                     if (eDataType == GDT_Byte &&
    1137             :                         (nBlockXOff + 1) * nBlockXSize <= nRasterXSize &&
    1138             :                         (nBlockYOff + 1) * nBlockYSize > nRasterYSize)
    1139             :                     {
    1140             :                         bool bFoundNonZero = false;
    1141             :                         for (int y = nRasterYSize - nBlockYOff * nBlockYSize;
    1142             :                              y < nBlockYSize; y++)
    1143             :                         {
    1144             :                             for (int x = 0; x < nBlockXSize; x++)
    1145             :                             {
    1146             :                                 if (pabyDest[static_cast<GPtrDiff_t>(y) *
    1147             :                                                  nBlockXSize +
    1148             :                                              x] != 0 &&
    1149             :                                     !bFoundNonZero)
    1150             :                                 {
    1151             :                                     CPLDebug("GPKG",
    1152             :                                              "IReadBlock(): Found non-zero "
    1153             :                                              "content in ghost part of "
    1154             :                                              "tile(nBand=%d,nBlockXOff=%d,"
    1155             :                                              "nBlockYOff=%d,m_nZoomLevel=%d)\n",
    1156             :                                              iBand, nBlockXOff, nBlockYOff,
    1157             :                                              m_poTPD->m_nZoomLevel);
    1158             :                                     bFoundNonZero = true;
    1159             :                                 }
    1160             :                             }
    1161             :                         }
    1162             :                     }
    1163             : #endif
    1164             :                 }
    1165             :                 else
    1166             :                 {
    1167       18570 :                     int nSrcXOffset = 0;
    1168       18570 :                     int nSrcXSize = 0;
    1169       18570 :                     int nDstXOffset = 0;
    1170       18570 :                     if (nCol == nColMin)
    1171             :                     {
    1172        9362 :                         nSrcXOffset = m_poTPD->m_nShiftXPixelsMod;
    1173        9362 :                         nSrcXSize = nBlockXSize - m_poTPD->m_nShiftXPixelsMod;
    1174        9362 :                         nDstXOffset = 0;
    1175             :                     }
    1176             :                     else
    1177             :                     {
    1178        9208 :                         nSrcXOffset = 0;
    1179        9208 :                         nSrcXSize = m_poTPD->m_nShiftXPixelsMod;
    1180        9208 :                         nDstXOffset = nBlockXSize - m_poTPD->m_nShiftXPixelsMod;
    1181             :                     }
    1182       18570 :                     int nSrcYOffset = 0;
    1183       18570 :                     int nSrcYSize = 0;
    1184       18570 :                     int nDstYOffset = 0;
    1185       18570 :                     if (nRow == nRowMin)
    1186             :                     {
    1187        9288 :                         nSrcYOffset = m_poTPD->m_nShiftYPixelsMod;
    1188        9288 :                         nSrcYSize = nBlockYSize - m_poTPD->m_nShiftYPixelsMod;
    1189        9288 :                         nDstYOffset = 0;
    1190             :                     }
    1191             :                     else
    1192             :                     {
    1193        9282 :                         nSrcYOffset = 0;
    1194        9282 :                         nSrcYSize = m_poTPD->m_nShiftYPixelsMod;
    1195        9282 :                         nDstYOffset = nBlockYSize - m_poTPD->m_nShiftYPixelsMod;
    1196             :                     }
    1197             : 
    1198             : #ifdef DEBUG_VERBOSE
    1199             :                     CPLDebug("GPKG",
    1200             :                              "Copy source tile x=%d,w=%d,y=%d,h=%d into "
    1201             :                              "buffer at x=%d,y=%d",
    1202             :                              nSrcXOffset, nSrcXSize, nSrcYOffset, nSrcYSize,
    1203             :                              nDstXOffset, nDstYOffset);
    1204             : #endif
    1205             : 
    1206     2368610 :                     for (GPtrDiff_t y = 0; y < nSrcYSize; y++)
    1207             :                     {
    1208     2350040 :                         GByte *pSrc =
    1209             :                             pabyTileData +
    1210     2350040 :                             (static_cast<GPtrDiff_t>(iBand - 1) * nBlockXSize *
    1211     2350040 :                                  nBlockYSize +
    1212     2350040 :                              (y + nSrcYOffset) * nBlockXSize + nSrcXOffset) *
    1213     2350040 :                                 m_nDTSize;
    1214     2350040 :                         GByte *pDst =
    1215             :                             pabyDest +
    1216     2350040 :                             ((y + nDstYOffset) * nBlockXSize + nDstXOffset) *
    1217     2350040 :                                 m_nDTSize;
    1218     2350040 :                         GDALCopyWords(pSrc, eDataType, m_nDTSize, pDst,
    1219             :                                       eDataType, m_nDTSize, nSrcXSize);
    1220             :                     }
    1221             :                 }
    1222             : 
    1223       27028 :                 if (poBlock)
    1224       19506 :                     poBlock->DropLock();
    1225             :             }
    1226             :         }
    1227             :     }
    1228             : 
    1229        3930 :     return CE_None;
    1230             : }
    1231             : 
    1232             : /************************************************************************/
    1233             : /*                       WEBPSupports4Bands()                           */
    1234             : /************************************************************************/
    1235             : 
    1236          83 : static bool WEBPSupports4Bands()
    1237             : {
    1238             :     static int bRes = -1;
    1239          83 :     if (bRes < 0)
    1240             :     {
    1241           1 :         GDALDriver *poDrv = GDALDriver::FromHandle(GDALGetDriverByName("WEBP"));
    1242           2 :         if (poDrv == nullptr ||
    1243           1 :             CPLTestBool(CPLGetConfigOption("GPKG_SIMUL_WEBP_3BAND", "FALSE")))
    1244           0 :             bRes = false;
    1245             :         else
    1246             :         {
    1247             :             // LOSSLESS and RGBA support appeared in the same version
    1248           1 :             bRes = strstr(poDrv->GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST),
    1249           1 :                           "LOSSLESS") != nullptr;
    1250             :         }
    1251           1 :         if (poDrv != nullptr && !bRes)
    1252             :         {
    1253           0 :             CPLError(
    1254             :                 CE_Warning, CPLE_AppDefined,
    1255             :                 "The version of WEBP available does not support 4-band RGBA");
    1256             :         }
    1257             :     }
    1258          83 :     return CPL_TO_BOOL(bRes);
    1259             : }
    1260             : 
    1261             : /************************************************************************/
    1262             : /*                         GetTileId()                                  */
    1263             : /************************************************************************/
    1264             : 
    1265          54 : GIntBig GDALGPKGMBTilesLikePseudoDataset::GetTileId(int nRow, int nCol)
    1266             : {
    1267             :     char *pszSQL =
    1268          54 :         sqlite3_mprintf("SELECT id FROM \"%w\" WHERE zoom_level = %d AND "
    1269             :                         "tile_row = %d AND tile_column = %d",
    1270             :                         m_osRasterTable.c_str(), m_nZoomLevel,
    1271          54 :                         GetRowFromIntoTopConvention(nRow), nCol);
    1272          54 :     GIntBig nRes = SQLGetInteger64(IGetDB(), pszSQL, nullptr);
    1273          54 :     sqlite3_free(pszSQL);
    1274          54 :     return nRes;
    1275             : }
    1276             : 
    1277             : /************************************************************************/
    1278             : /*                           DeleteTile()                               */
    1279             : /************************************************************************/
    1280             : 
    1281         440 : bool GDALGPKGMBTilesLikePseudoDataset::DeleteTile(int nRow, int nCol)
    1282             : {
    1283             :     char *pszSQL =
    1284         440 :         sqlite3_mprintf("DELETE FROM \"%w\" "
    1285             :                         "WHERE zoom_level = %d AND tile_row = %d AND "
    1286             :                         "tile_column = %d",
    1287             :                         m_osRasterTable.c_str(), m_nZoomLevel,
    1288         440 :                         GetRowFromIntoTopConvention(nRow), nCol);
    1289             : #ifdef DEBUG_VERBOSE
    1290             :     CPLDebug("GPKG", "%s", pszSQL);
    1291             : #endif
    1292         440 :     char *pszErrMsg = nullptr;
    1293         440 :     int rc = sqlite3_exec(IGetDB(), pszSQL, nullptr, nullptr, &pszErrMsg);
    1294         440 :     if (rc != SQLITE_OK)
    1295             :     {
    1296           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1297             :                  "Failure when deleting tile (row=%d,col=%d) "
    1298             :                  "at zoom_level=%d : %s",
    1299           0 :                  GetRowFromIntoTopConvention(nRow), nCol, m_nZoomLevel,
    1300           0 :                  pszErrMsg ? pszErrMsg : "");
    1301             :     }
    1302         440 :     sqlite3_free(pszSQL);
    1303         440 :     sqlite3_free(pszErrMsg);
    1304         440 :     return rc == SQLITE_OK;
    1305             : }
    1306             : 
    1307             : /************************************************************************/
    1308             : /*                   DeleteFromGriddedTileAncillary()                   */
    1309             : /************************************************************************/
    1310             : 
    1311          54 : bool GDALGPKGMBTilesLikePseudoDataset::DeleteFromGriddedTileAncillary(
    1312             :     GIntBig nTileId)
    1313             : {
    1314             :     char *pszSQL =
    1315          54 :         sqlite3_mprintf("DELETE FROM gpkg_2d_gridded_tile_ancillary WHERE "
    1316             :                         "tpudt_name = '%q' AND tpudt_id = ?",
    1317             :                         m_osRasterTable.c_str());
    1318          54 :     sqlite3_stmt *hStmt = nullptr;
    1319          54 :     int rc = sqlite3_prepare_v2(IGetDB(), pszSQL, -1, &hStmt, nullptr);
    1320          54 :     if (rc == SQLITE_OK)
    1321             :     {
    1322          54 :         sqlite3_bind_int64(hStmt, 1, nTileId);
    1323          54 :         rc = sqlite3_step(hStmt);
    1324          54 :         sqlite3_finalize(hStmt);
    1325             :     }
    1326          54 :     sqlite3_free(pszSQL);
    1327          54 :     return rc == SQLITE_OK;
    1328             : }
    1329             : 
    1330             : /************************************************************************/
    1331             : /*                      ProcessInt16UInt16Tile()                        */
    1332             : /************************************************************************/
    1333             : 
    1334             : template <class T>
    1335          45 : static void ProcessInt16UInt16Tile(
    1336             :     const void *pabyData, GPtrDiff_t nPixels, bool bIsInt16, bool bHasNoData,
    1337             :     double dfNoDataValue, GUInt16 usGPKGNull, double m_dfOffset,
    1338             :     double m_dfScale, GUInt16 *pTempTileBuffer, double &dfTileOffset,
    1339             :     double &dfTileScale, double &dfTileMin, double &dfTileMax,
    1340             :     double &dfTileMean, double &dfTileStdDev, GPtrDiff_t &nValidPixels)
    1341             : {
    1342          45 :     const T *pSrc = reinterpret_cast<const T *>(pabyData);
    1343          45 :     T nMin = 0;
    1344          45 :     T nMax = 0;
    1345          45 :     double dfM2 = 0.0;
    1346     2949170 :     for (int i = 0; i < nPixels; i++)
    1347             :     {
    1348     2949120 :         const T nVal = pSrc[i];
    1349     2949120 :         if (bHasNoData && nVal == dfNoDataValue)
    1350      718161 :             continue;
    1351             : 
    1352     2230959 :         if (nValidPixels == 0)
    1353             :         {
    1354          44 :             nMin = nVal;
    1355          44 :             nMax = nVal;
    1356             :         }
    1357             :         else
    1358             :         {
    1359     2230920 :             nMin = std::min(nMin, nVal);
    1360     2230920 :             nMax = std::max(nMax, nVal);
    1361             :         }
    1362     2230959 :         nValidPixels++;
    1363     2230959 :         const double dfDelta = nVal - dfTileMean;
    1364     2230959 :         dfTileMean += dfDelta / nValidPixels;
    1365     2230959 :         dfM2 += dfDelta * (nVal - dfTileMean);
    1366             :     }
    1367          45 :     dfTileMin = nMin;
    1368          45 :     dfTileMax = nMax;
    1369          45 :     if (nValidPixels)
    1370          44 :         dfTileStdDev = sqrt(dfM2 / nValidPixels);
    1371             : 
    1372          45 :     double dfGlobalMin = (nMin - m_dfOffset) / m_dfScale;
    1373          45 :     double dfGlobalMax = (nMax - m_dfOffset) / m_dfScale;
    1374          45 :     double dfRange = 65535.0;
    1375          45 :     if (bHasNoData && usGPKGNull == 65535 &&
    1376           8 :         dfGlobalMax - dfGlobalMin >= dfRange)
    1377             :     {
    1378           0 :         dfRange = 65534.0;
    1379             :     }
    1380             : 
    1381          45 :     if (dfGlobalMax - dfGlobalMin > dfRange)
    1382             :     {
    1383           0 :         dfTileScale = (dfGlobalMax - dfGlobalMin) / dfRange;
    1384             :     }
    1385          45 :     if (dfGlobalMin < 0.0)
    1386             :     {
    1387           0 :         dfTileOffset = -dfGlobalMin;
    1388             :     }
    1389          45 :     else if (dfGlobalMax / dfTileScale > dfRange)
    1390             :     {
    1391           0 :         dfTileOffset = dfGlobalMax - dfRange * dfTileScale;
    1392             :     }
    1393             : 
    1394          45 :     if (bHasNoData && std::numeric_limits<T>::min() == 0 && m_dfOffset == 0.0 &&
    1395             :         m_dfScale == 1.0)
    1396             :     {
    1397           7 :         dfTileOffset = 0.0;
    1398           7 :         dfTileScale = 1.0;
    1399             :     }
    1400          38 :     else if (bHasNoData && bIsInt16 && dfNoDataValue == -32768.0 &&
    1401           1 :              usGPKGNull == 65535 && m_dfOffset == -32768.0 && m_dfScale == 1.0)
    1402             :     {
    1403           1 :         dfTileOffset = 1.0;
    1404           1 :         dfTileScale = 1.0;
    1405             :     }
    1406             : 
    1407     2949170 :     for (GPtrDiff_t i = 0; i < nPixels; i++)
    1408             :     {
    1409     2949120 :         const T nVal = pSrc[i];
    1410     2949120 :         if (bHasNoData && nVal == dfNoDataValue)
    1411      718161 :             pTempTileBuffer[i] = usGPKGNull;
    1412             :         else
    1413             :         {
    1414     2230959 :             double dfVal =
    1415     2230959 :                 ((nVal - m_dfOffset) / m_dfScale - dfTileOffset) / dfTileScale;
    1416     2230959 :             CPLAssert(dfVal >= 0.0 && dfVal < 65535.5);
    1417     2230959 :             pTempTileBuffer[i] = static_cast<GUInt16>(dfVal + 0.5);
    1418     2230959 :             if (bHasNoData && pTempTileBuffer[i] == usGPKGNull)
    1419             :             {
    1420           0 :                 if (usGPKGNull > 0)
    1421           0 :                     pTempTileBuffer[i]--;
    1422             :                 else
    1423           0 :                     pTempTileBuffer[i]++;
    1424             :                 ;
    1425             :             }
    1426             :         }
    1427             :     }
    1428          45 : }
    1429             : 
    1430             : /************************************************************************/
    1431             : /*                         WriteTile()                                  */
    1432             : /************************************************************************/
    1433             : 
    1434       13161 : CPLErr GDALGPKGMBTilesLikePseudoDataset::WriteTile()
    1435             : {
    1436       13161 :     GDALGPKGMBTilesLikePseudoDataset *poMainDS =
    1437       13161 :         m_poParentDS ? m_poParentDS : this;
    1438       13161 :     if (poMainDS->m_nTileInsertionCount < 0)
    1439         501 :         return CE_Failure;
    1440             : 
    1441       12660 :     if (m_bInWriteTile)
    1442             :     {
    1443             :         // Shouldn't happen in practice, but #7022 shows that the unexpected
    1444             :         // can happen sometimes.
    1445           0 :         CPLError(
    1446             :             CE_Failure, CPLE_AppDefined,
    1447             :             "Recursive call to GDALGPKGMBTilesLikePseudoDataset::WriteTile()");
    1448           0 :         return CE_Failure;
    1449             :     }
    1450       12660 :     GDALRasterBlock::EnterDisableDirtyBlockFlush();
    1451       12660 :     m_bInWriteTile = true;
    1452       12660 :     CPLErr eErr = WriteTileInternal();
    1453       12660 :     m_bInWriteTile = false;
    1454       12660 :     GDALRasterBlock::LeaveDisableDirtyBlockFlush();
    1455       12660 :     return eErr;
    1456             : }
    1457             : 
    1458             : /* should only be called by WriteTile() */
    1459       12660 : CPLErr GDALGPKGMBTilesLikePseudoDataset::WriteTileInternal()
    1460             : {
    1461       17254 :     if (!(IGetUpdate() && m_asCachedTilesDesc[0].nRow >= 0 &&
    1462        4594 :           m_asCachedTilesDesc[0].nCol >= 0 &&
    1463        4594 :           m_asCachedTilesDesc[0].nIdxWithinTileData == 0))
    1464        8066 :         return CE_None;
    1465             : 
    1466        4594 :     int nRow = m_asCachedTilesDesc[0].nRow;
    1467        4594 :     int nCol = m_asCachedTilesDesc[0].nCol;
    1468             : 
    1469        4594 :     bool bAllDirty = true;
    1470        4594 :     bool bAllNonDirty = true;
    1471        4594 :     const int nBands = IGetRasterCount();
    1472       17936 :     for (int i = 0; i < nBands; i++)
    1473             :     {
    1474       13342 :         if (m_asCachedTilesDesc[0].abBandDirty[i])
    1475       13309 :             bAllNonDirty = false;
    1476             :         else
    1477          33 :             bAllDirty = false;
    1478             :     }
    1479        4594 :     if (bAllNonDirty)
    1480           0 :         return CE_None;
    1481             : 
    1482             :     int nBlockXSize, nBlockYSize;
    1483        4594 :     IGetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
    1484             : 
    1485             :     /* If all bands for that block are not dirty/written, we need to */
    1486             :     /* fetch the missing ones if the tile exists */
    1487        4594 :     bool bIsLossyFormat = false;
    1488        4594 :     const size_t nBandBlockSize =
    1489        4594 :         static_cast<size_t>(nBlockXSize) * nBlockYSize * m_nDTSize;
    1490        4594 :     if (!bAllDirty)
    1491             :     {
    1492          72 :         for (int i = 1; i <= 3; i++)
    1493             :         {
    1494          54 :             m_asCachedTilesDesc[i].nRow = -1;
    1495          54 :             m_asCachedTilesDesc[i].nCol = -1;
    1496          54 :             m_asCachedTilesDesc[i].nIdxWithinTileData = -1;
    1497             :         }
    1498          18 :         const int nTileBands = m_eDT == GDT_Byte ? 4 : 1;
    1499          18 :         GByte *pabyTemp = m_pabyCachedTiles + nTileBands * nBandBlockSize;
    1500          18 :         ReadTile(nRow, nCol, pabyTemp, &bIsLossyFormat);
    1501          82 :         for (int i = 0; i < nBands; i++)
    1502             :         {
    1503          64 :             if (!m_asCachedTilesDesc[0].abBandDirty[i])
    1504             :             {
    1505          33 :                 memcpy(m_pabyCachedTiles + i * nBandBlockSize,
    1506          33 :                        pabyTemp + i * nBandBlockSize, nBandBlockSize);
    1507             :             }
    1508             :         }
    1509             :     }
    1510             : 
    1511             :     /* Compute origin of tile in GDAL raster space */
    1512        4594 :     int nXOff = (nCol - m_nShiftXTiles) * nBlockXSize - m_nShiftXPixelsMod;
    1513        4594 :     int nYOff = (nRow - m_nShiftYTiles) * nBlockYSize - m_nShiftYPixelsMod;
    1514             : 
    1515             :     /* Assert that the tile at least intersects some of the GDAL raster space */
    1516        4594 :     CPLAssert(nXOff > -nBlockXSize);
    1517        4594 :     CPLAssert(nYOff > -nBlockYSize);
    1518             :     /* Can happen if the tile of the raster is less than the block size */
    1519        4594 :     const int nRasterXSize = IGetRasterBand(1)->GetXSize();
    1520        4594 :     const int nRasterYSize = IGetRasterBand(1)->GetYSize();
    1521        4594 :     if (nXOff >= nRasterXSize || nYOff >= nRasterYSize)
    1522           0 :         return CE_None;
    1523             : 
    1524             : #ifdef DEBUG_VERBOSE
    1525             :     if (m_nShiftXPixelsMod == 0 && m_nShiftYPixelsMod == 0 && m_eDT == GDT_Byte)
    1526             :     {
    1527             :         int nBlockXOff = nCol;
    1528             :         int nBlockYOff = nRow;
    1529             :         if (nBlockXOff * nBlockXSize <= nRasterXSize - nBlockXSize &&
    1530             :             nBlockYOff * nBlockYSize > nRasterYSize - nBlockYSize)
    1531             :         {
    1532             :             for (int i = 0; i < nBands; i++)
    1533             :             {
    1534             :                 bool bFoundNonZero = false;
    1535             :                 for (GPtrDiff_t y = nRasterYSize - nBlockYOff * nBlockYSize;
    1536             :                      y < nBlockYSize; y++)
    1537             :                 {
    1538             :                     for (int x = 0; x < nBlockXSize; x++)
    1539             :                     {
    1540             :                         if (m_pabyCachedTiles[y * nBlockXSize + x +
    1541             :                                               i * nBandBlockSize] != 0 &&
    1542             :                             !bFoundNonZero)
    1543             :                         {
    1544             :                             CPLDebug("GPKG",
    1545             :                                      "WriteTileInternal(): Found non-zero "
    1546             :                                      "content in ghost part of "
    1547             :                                      "tile(band=%d,nBlockXOff=%d,nBlockYOff=%d,"
    1548             :                                      "m_nZoomLevel=%d)\n",
    1549             :                                      i + 1, nBlockXOff, nBlockYOff,
    1550             :                                      m_nZoomLevel);
    1551             :                             bFoundNonZero = true;
    1552             :                         }
    1553             :                     }
    1554             :                 }
    1555             :             }
    1556             :         }
    1557             :     }
    1558             : #endif
    1559             : 
    1560             :     /* Validity area of tile data in intra-tile coordinate space */
    1561        4594 :     int iXOff = 0;
    1562        4594 :     GPtrDiff_t iYOff = 0;
    1563        4594 :     int iXCount = nBlockXSize;
    1564        4594 :     int iYCount = nBlockYSize;
    1565             : 
    1566        4594 :     bool bPartialTile = false;
    1567        4594 :     int nAlphaBand = (nBands == 2) ? 2 : (nBands == 4) ? 4 : 0;
    1568        4594 :     if (nAlphaBand == 0)
    1569             :     {
    1570        3714 :         if (nXOff < 0)
    1571             :         {
    1572           4 :             bPartialTile = true;
    1573           4 :             iXOff = -nXOff;
    1574           4 :             iXCount += nXOff;
    1575             :         }
    1576        3714 :         if (nXOff > nRasterXSize - nBlockXSize)
    1577             :         {
    1578          77 :             bPartialTile = true;
    1579          77 :             iXCount -= static_cast<int>(static_cast<GIntBig>(nXOff) +
    1580          77 :                                         nBlockXSize - nRasterXSize);
    1581             :         }
    1582        3714 :         if (nYOff < 0)
    1583             :         {
    1584           8 :             bPartialTile = true;
    1585           8 :             iYOff = -nYOff;
    1586           8 :             iYCount += nYOff;
    1587             :         }
    1588        3714 :         if (nYOff > nRasterYSize - nBlockYSize)
    1589             :         {
    1590          89 :             bPartialTile = true;
    1591          89 :             iYCount -= static_cast<int>(static_cast<GIntBig>(nYOff) +
    1592          89 :                                         nBlockYSize - nRasterYSize);
    1593             :         }
    1594        3714 :         CPLAssert(iXOff >= 0);
    1595        3714 :         CPLAssert(iYOff >= 0);
    1596        3714 :         CPLAssert(iXCount > 0);
    1597        3714 :         CPLAssert(iYCount > 0);
    1598        3714 :         CPLAssert(iXOff + iXCount <= nBlockXSize);
    1599        3714 :         CPLAssert(iYOff + iYCount <= nBlockYSize);
    1600             :     }
    1601             : 
    1602        4594 :     m_asCachedTilesDesc[0].nRow = -1;
    1603        4594 :     m_asCachedTilesDesc[0].nCol = -1;
    1604        4594 :     m_asCachedTilesDesc[0].nIdxWithinTileData = -1;
    1605        4594 :     m_asCachedTilesDesc[0].abBandDirty[0] = false;
    1606        4594 :     m_asCachedTilesDesc[0].abBandDirty[1] = false;
    1607        4594 :     m_asCachedTilesDesc[0].abBandDirty[2] = false;
    1608        4594 :     m_asCachedTilesDesc[0].abBandDirty[3] = false;
    1609             : 
    1610        4594 :     CPLErr eErr = CE_Failure;
    1611             : 
    1612        4594 :     int bHasNoData = FALSE;
    1613        4594 :     double dfNoDataValue = IGetRasterBand(1)->GetNoDataValue(&bHasNoData);
    1614        4594 :     const bool bHasNanNoData = bHasNoData && CPLIsNan(dfNoDataValue);
    1615             : 
    1616        4594 :     bool bAllOpaque = true;
    1617             :     // Detect fully transparent tiles, but only if all bands are dirty (that is
    1618             :     // the user wrote content into them) !
    1619        4594 :     if (bAllDirty && m_eDT == GDT_Byte && m_poCT == nullptr && nAlphaBand != 0)
    1620             :     {
    1621         870 :         GByte byFirstAlphaVal =
    1622         870 :             m_pabyCachedTiles[(nAlphaBand - 1) * nBlockXSize * nBlockYSize];
    1623         870 :         GPtrDiff_t i = 1;
    1624    23915400 :         for (; i < static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize; i++)
    1625             :         {
    1626    23914900 :             if (m_pabyCachedTiles[static_cast<GPtrDiff_t>(nAlphaBand - 1) *
    1627    23914900 :                                       nBlockXSize * nBlockYSize +
    1628    23914900 :                                   i] != byFirstAlphaVal)
    1629         368 :                 break;
    1630             :         }
    1631         870 :         if (i == static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize)
    1632             :         {
    1633             :             // If tile is fully transparent, don't serialize it and remove it if
    1634             :             // it exists
    1635         502 :             if (byFirstAlphaVal == 0)
    1636             :             {
    1637         437 :                 DeleteTile(nRow, nCol);
    1638             : 
    1639         437 :                 return CE_None;
    1640             :             }
    1641          65 :             bAllOpaque = (byFirstAlphaVal == 255);
    1642             :         }
    1643             :         else
    1644         433 :             bAllOpaque = false;
    1645             :     }
    1646        3724 :     else if (bAllDirty && m_eDT == GDT_Float32)
    1647             :     {
    1648          11 :         const float *pSrc = reinterpret_cast<float *>(m_pabyCachedTiles);
    1649             :         GPtrDiff_t i;
    1650          11 :         const float fNoDataValueOrZero =
    1651          11 :             bHasNoData ? static_cast<float>(dfNoDataValue) : 0.0f;
    1652      131083 :         for (i = 0; i < static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize; i++)
    1653             :         {
    1654      131081 :             const float fVal = pSrc[i];
    1655      131081 :             if (bHasNanNoData)
    1656             :             {
    1657           0 :                 if (CPLIsNan(fVal))
    1658           0 :                     continue;
    1659             :             }
    1660      131081 :             else if (fVal == fNoDataValueOrZero)
    1661             :             {
    1662      131072 :                 continue;
    1663             :             }
    1664           9 :             break;
    1665             :         }
    1666          11 :         if (i == static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize)
    1667             :         {
    1668             :             // If tile is fully transparent, don't serialize it and remove it if
    1669             :             // it exists
    1670           2 :             DeleteTile(nRow, nCol);
    1671             : 
    1672           2 :             return CE_None;
    1673             :         }
    1674             :     }
    1675             : 
    1676        4155 :     if (bIsLossyFormat)
    1677             :     {
    1678           1 :         CPLDebug("GPKG",
    1679             :                  "Had to read tile (row=%d,col=%d) at zoom_level=%d, "
    1680             :                  "stored in a lossy format, before rewriting it, causing "
    1681             :                  "potential extra quality loss",
    1682             :                  nRow, nCol, m_nZoomLevel);
    1683             :     }
    1684             : 
    1685        8310 :     CPLString osMemFileName;
    1686        4155 :     osMemFileName.Printf("/vsimem/gpkg_write_tile_%p", this);
    1687        4155 :     const char *pszDriverName = "PNG";
    1688        4155 :     bool bTileDriverSupports1Band = false;
    1689        4155 :     bool bTileDriverSupports2Bands = false;
    1690        4155 :     bool bTileDriverSupports4Bands = false;
    1691        4155 :     bool bTileDriverSupportsCT = false;
    1692             : 
    1693        4155 :     if (nBands == 1 && m_eDT == GDT_Byte)
    1694          72 :         IGetRasterBand(1)->GetColorTable();
    1695             : 
    1696        4155 :     GDALDataType eTileDT = GDT_Byte;
    1697             :     // If not all bands are dirty, then (temporarily) use a lossless format
    1698        4155 :     if (m_eTF == GPKG_TF_PNG_JPEG || (!bAllDirty && m_eTF == GPKG_TF_JPEG))
    1699             :     {
    1700          97 :         bTileDriverSupports1Band = true;
    1701          97 :         if (bPartialTile || !bAllDirty || (nBands == 2 && !bAllOpaque) ||
    1702          20 :             (nBands == 4 && !bAllOpaque) || m_poCT != nullptr)
    1703             :         {
    1704          89 :             pszDriverName = "PNG";
    1705          89 :             bTileDriverSupports2Bands = m_bPNGSupports2Bands;
    1706          89 :             bTileDriverSupports4Bands = true;
    1707          89 :             bTileDriverSupportsCT = m_bPNGSupportsCT;
    1708             :         }
    1709             :         else
    1710           8 :             pszDriverName = "JPEG";
    1711             :     }
    1712        4058 :     else if (m_eTF == GPKG_TF_PNG || m_eTF == GPKG_TF_PNG8)
    1713             :     {
    1714        3839 :         pszDriverName = "PNG";
    1715        3839 :         bTileDriverSupports1Band = true;
    1716        3839 :         bTileDriverSupports2Bands = m_bPNGSupports2Bands;
    1717        3839 :         bTileDriverSupports4Bands = true;
    1718        3839 :         bTileDriverSupportsCT = m_bPNGSupportsCT;
    1719             :     }
    1720         219 :     else if (m_eTF == GPKG_TF_JPEG)
    1721             :     {
    1722          82 :         pszDriverName = "JPEG";
    1723          82 :         bTileDriverSupports1Band = true;
    1724             :     }
    1725         137 :     else if (m_eTF == GPKG_TF_WEBP)
    1726             :     {
    1727          83 :         pszDriverName = "WEBP";
    1728          83 :         bTileDriverSupports4Bands = WEBPSupports4Bands();
    1729             :     }
    1730          54 :     else if (m_eTF == GPKG_TF_PNG_16BIT)
    1731             :     {
    1732          50 :         pszDriverName = "PNG";
    1733          50 :         eTileDT = GDT_UInt16;
    1734          50 :         bTileDriverSupports1Band = true;
    1735             :     }
    1736           4 :     else if (m_eTF == GPKG_TF_TIFF_32BIT_FLOAT)
    1737             :     {
    1738           4 :         pszDriverName = "GTiff";
    1739           4 :         eTileDT = GDT_Float32;
    1740           4 :         bTileDriverSupports1Band = true;
    1741             :     }
    1742             :     else
    1743             :     {
    1744           0 :         CPLAssert(false);
    1745             :     }
    1746             : 
    1747             :     GDALDriver *l_poDriver =
    1748        4155 :         GDALDriver::FromHandle(GDALGetDriverByName(pszDriverName));
    1749        4155 :     if (l_poDriver != nullptr)
    1750             :     {
    1751        4154 :         auto poMEMDS = MEMDataset::Create("", nBlockXSize, nBlockYSize, 0,
    1752             :                                           eTileDT, nullptr);
    1753        4154 :         int nTileBands = nBands;
    1754        4154 :         if (bPartialTile && nBands == 1 && m_poCT == nullptr &&
    1755             :             bTileDriverSupports2Bands)
    1756          27 :             nTileBands = 2;
    1757        4127 :         else if (bPartialTile && bTileDriverSupports4Bands)
    1758          37 :             nTileBands = 4;
    1759             :         // only use (somewhat lossy) PNG8 if all bands are dirty
    1760        4090 :         else if (bAllDirty && m_eTF == GPKG_TF_PNG8 && nBands >= 3 &&
    1761          10 :                  bAllOpaque && !bPartialTile)
    1762          10 :             nTileBands = 1;
    1763        4080 :         else if (nBands == 2)
    1764             :         {
    1765         383 :             if (bAllOpaque)
    1766             :             {
    1767          51 :                 if (bTileDriverSupports2Bands)
    1768          30 :                     nTileBands = 1;
    1769             :                 else
    1770          21 :                     nTileBands = 3;
    1771             :             }
    1772         332 :             else if (!bTileDriverSupports2Bands)
    1773             :             {
    1774         135 :                 if (bTileDriverSupports4Bands)
    1775          68 :                     nTileBands = 4;
    1776             :                 else
    1777          67 :                     nTileBands = 3;
    1778             :             }
    1779             :         }
    1780        3697 :         else if (nBands == 4 && (bAllOpaque || !bTileDriverSupports4Bands))
    1781          21 :             nTileBands = 3;
    1782        3676 :         else if (nBands == 1 && m_poCT != nullptr && !bTileDriverSupportsCT)
    1783             :         {
    1784           1 :             nTileBands = 3;
    1785           1 :             if (bTileDriverSupports4Bands)
    1786             :             {
    1787           0 :                 for (int i = 0; i < m_poCT->GetColorEntryCount(); i++)
    1788             :                 {
    1789           0 :                     const GDALColorEntry *psEntry = m_poCT->GetColorEntry(i);
    1790           0 :                     if (psEntry->c4 == 0)
    1791             :                     {
    1792           0 :                         nTileBands = 4;
    1793           0 :                         break;
    1794             :                     }
    1795             :                 }
    1796           1 :             }
    1797             :         }
    1798        3675 :         else if (nBands == 1 && m_poCT == nullptr && !bTileDriverSupports1Band)
    1799           1 :             nTileBands = 3;
    1800             : 
    1801        4154 :         if (bPartialTile && (nTileBands == 2 || nTileBands == 4))
    1802             :         {
    1803          64 :             int nTargetAlphaBand = nTileBands;
    1804          64 :             memset(m_pabyCachedTiles + (nTargetAlphaBand - 1) * nBandBlockSize,
    1805             :                    0, nBandBlockSize);
    1806        4072 :             for (GPtrDiff_t iY = iYOff; iY < iYOff + iYCount; iY++)
    1807             :             {
    1808        4008 :                 memset(m_pabyCachedTiles +
    1809        4008 :                            (static_cast<size_t>(nTargetAlphaBand - 1) *
    1810        4008 :                                 nBlockYSize +
    1811        4008 :                             iY) *
    1812        4008 :                                nBlockXSize +
    1813        4008 :                            iXOff,
    1814             :                        255, iXCount);
    1815             :             }
    1816             :         }
    1817             : 
    1818        4154 :         GUInt16 *pTempTileBuffer = nullptr;
    1819        4154 :         GPtrDiff_t nValidPixels = 0;
    1820        4154 :         double dfTileMin = 0.0;
    1821        4154 :         double dfTileMax = 0.0;
    1822        4154 :         double dfTileMean = 0.0;
    1823        4154 :         double dfTileStdDev = 0.0;
    1824        4154 :         double dfTileOffset = 0.0;
    1825        4154 :         double dfTileScale = 1.0;
    1826        4154 :         if (m_eTF == GPKG_TF_PNG_16BIT)
    1827             :         {
    1828             :             pTempTileBuffer = static_cast<GUInt16 *>(
    1829          50 :                 VSI_MALLOC3_VERBOSE(2, nBlockXSize, nBlockYSize));
    1830             : 
    1831          50 :             if (m_eDT == GDT_Int16)
    1832             :             {
    1833          70 :                 ProcessInt16UInt16Tile<GInt16>(
    1834          35 :                     m_pabyCachedTiles,
    1835          35 :                     static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize, true,
    1836          35 :                     CPL_TO_BOOL(bHasNoData), dfNoDataValue, m_usGPKGNull,
    1837             :                     m_dfOffset, m_dfScale, pTempTileBuffer, dfTileOffset,
    1838             :                     dfTileScale, dfTileMin, dfTileMax, dfTileMean, dfTileStdDev,
    1839             :                     nValidPixels);
    1840             :             }
    1841          15 :             else if (m_eDT == GDT_UInt16)
    1842             :             {
    1843          20 :                 ProcessInt16UInt16Tile<GUInt16>(
    1844          10 :                     m_pabyCachedTiles,
    1845          10 :                     static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize, false,
    1846          10 :                     CPL_TO_BOOL(bHasNoData), dfNoDataValue, m_usGPKGNull,
    1847             :                     m_dfOffset, m_dfScale, pTempTileBuffer, dfTileOffset,
    1848             :                     dfTileScale, dfTileMin, dfTileMax, dfTileMean, dfTileStdDev,
    1849             :                     nValidPixels);
    1850             :             }
    1851           5 :             else if (m_eDT == GDT_Float32)
    1852             :             {
    1853           5 :                 const float *pSrc =
    1854             :                     reinterpret_cast<float *>(m_pabyCachedTiles);
    1855           5 :                 float fMin = 0.0f;
    1856           5 :                 float fMax = 0.0f;
    1857           5 :                 double dfM2 = 0.0;
    1858      327685 :                 for (GPtrDiff_t i = 0;
    1859      327685 :                      i < static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize;
    1860             :                      i++)
    1861             :                 {
    1862      327680 :                     const float fVal = pSrc[i];
    1863      327680 :                     if (bHasNanNoData)
    1864             :                     {
    1865           0 :                         if (CPLIsNan(fVal))
    1866      196206 :                             continue;
    1867             :                     }
    1868      720494 :                     else if (bHasNoData &&
    1869      327680 :                              fVal == static_cast<float>(dfNoDataValue))
    1870             :                     {
    1871      196206 :                         continue;
    1872             :                     }
    1873      131474 :                     if (CPLIsInf(fVal))
    1874           0 :                         continue;
    1875             : 
    1876      131474 :                     if (nValidPixels == 0)
    1877             :                     {
    1878           5 :                         fMin = fVal;
    1879           5 :                         fMax = fVal;
    1880             :                     }
    1881             :                     else
    1882             :                     {
    1883      131469 :                         fMin = std::min(fMin, fVal);
    1884      131469 :                         fMax = std::max(fMax, fVal);
    1885             :                     }
    1886      131474 :                     nValidPixels++;
    1887      131474 :                     const double dfDelta = fVal - dfTileMean;
    1888      131474 :                     dfTileMean += dfDelta / nValidPixels;
    1889      131474 :                     dfM2 += dfDelta * (fVal - dfTileMean);
    1890             :                 }
    1891           5 :                 dfTileMin = fMin;
    1892           5 :                 dfTileMax = fMax;
    1893           5 :                 if (nValidPixels)
    1894           5 :                     dfTileStdDev = sqrt(dfM2 / nValidPixels);
    1895             : 
    1896           5 :                 double dfGlobalMin = (fMin - m_dfOffset) / m_dfScale;
    1897           5 :                 double dfGlobalMax = (fMax - m_dfOffset) / m_dfScale;
    1898           5 :                 if (dfGlobalMax > dfGlobalMin)
    1899             :                 {
    1900           4 :                     if (bHasNoData && m_usGPKGNull == 65535 &&
    1901           2 :                         dfGlobalMax - dfGlobalMin >= 65534.0)
    1902             :                     {
    1903           1 :                         dfTileOffset = dfGlobalMin;
    1904           1 :                         dfTileScale = (dfGlobalMax - dfGlobalMin) / 65534.0;
    1905             :                     }
    1906           3 :                     else if (bHasNoData && m_usGPKGNull == 0 &&
    1907           0 :                              (dfNoDataValue - m_dfOffset) / m_dfScale != 0)
    1908             :                     {
    1909           0 :                         dfTileOffset =
    1910           0 :                             (65535.0 * dfGlobalMin - dfGlobalMax) / 65534.0;
    1911           0 :                         dfTileScale = dfGlobalMin - dfTileOffset;
    1912             :                     }
    1913             :                     else
    1914             :                     {
    1915           3 :                         dfTileOffset = dfGlobalMin;
    1916           3 :                         dfTileScale = (dfGlobalMax - dfGlobalMin) / 65535.0;
    1917             :                     }
    1918             :                 }
    1919             :                 else
    1920             :                 {
    1921           1 :                     dfTileOffset = dfGlobalMin;
    1922             :                 }
    1923             : 
    1924      327685 :                 for (GPtrDiff_t i = 0;
    1925      327685 :                      i < static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize;
    1926             :                      i++)
    1927             :                 {
    1928      327680 :                     const float fVal = pSrc[i];
    1929      327680 :                     if (bHasNanNoData)
    1930             :                     {
    1931           0 :                         if (CPLIsNan(fVal))
    1932             :                         {
    1933           0 :                             pTempTileBuffer[i] = m_usGPKGNull;
    1934           0 :                             continue;
    1935             :                         }
    1936             :                     }
    1937      327680 :                     else if (bHasNoData)
    1938             :                     {
    1939      196608 :                         if (fVal == static_cast<float>(dfNoDataValue))
    1940             :                         {
    1941      196206 :                             pTempTileBuffer[i] = m_usGPKGNull;
    1942      196206 :                             continue;
    1943             :                         }
    1944             :                     }
    1945      131474 :                     double dfVal =
    1946      131474 :                         CPLIsFinite(fVal)
    1947      131474 :                             ? ((fVal - m_dfOffset) / m_dfScale - dfTileOffset) /
    1948             :                                   dfTileScale
    1949             :                         : (fVal > 0) ? 65535
    1950      131474 :                                      : 0;
    1951      131474 :                     CPLAssert(dfVal >= 0.0 && dfVal < 65535.5);
    1952      131474 :                     pTempTileBuffer[i] = static_cast<GUInt16>(dfVal + 0.5);
    1953      131474 :                     if (bHasNoData && pTempTileBuffer[i] == m_usGPKGNull)
    1954             :                     {
    1955           1 :                         if (m_usGPKGNull > 0)
    1956           1 :                             pTempTileBuffer[i]--;
    1957             :                         else
    1958           0 :                             pTempTileBuffer[i]++;
    1959             :                     }
    1960             :                 }
    1961             :             }
    1962             : 
    1963          50 :             auto hBand = MEMCreateRasterBandEx(
    1964             :                 poMEMDS, 1, reinterpret_cast<GByte *>(pTempTileBuffer),
    1965             :                 GDT_UInt16, 0, 0, false);
    1966          50 :             poMEMDS->AddMEMBand(hBand);
    1967             :         }
    1968        4104 :         else if (m_eTF == GPKG_TF_TIFF_32BIT_FLOAT)
    1969             :         {
    1970           4 :             const float *pSrc = reinterpret_cast<float *>(m_pabyCachedTiles);
    1971           4 :             float fMin = 0.0f;
    1972           4 :             float fMax = 0.0f;
    1973           4 :             double dfM2 = 0.0;
    1974      262148 :             for (GPtrDiff_t i = 0;
    1975      262148 :                  i < static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize; i++)
    1976             :             {
    1977      262144 :                 const float fVal = pSrc[i];
    1978      262144 :                 if (bHasNanNoData)
    1979             :                 {
    1980           0 :                     if (CPLIsNan(fVal))
    1981       65136 :                         continue;
    1982             :                 }
    1983      392816 :                 else if (bHasNoData &&
    1984      262144 :                          fVal == static_cast<float>(dfNoDataValue))
    1985             :                 {
    1986       65136 :                     continue;
    1987             :                 }
    1988             : 
    1989      197008 :                 if (nValidPixels == 0)
    1990             :                 {
    1991           4 :                     fMin = fVal;
    1992           4 :                     fMax = fVal;
    1993             :                 }
    1994             :                 else
    1995             :                 {
    1996      197004 :                     fMin = std::min(fMin, fVal);
    1997      197004 :                     fMax = std::max(fMax, fVal);
    1998             :                 }
    1999      197008 :                 nValidPixels++;
    2000      197008 :                 const double dfDelta = fVal - dfTileMean;
    2001      197008 :                 dfTileMean += dfDelta / nValidPixels;
    2002      197008 :                 dfM2 += dfDelta * (fVal - dfTileMean);
    2003             :             }
    2004           4 :             dfTileMin = fMin;
    2005           4 :             dfTileMax = fMax;
    2006           4 :             if (nValidPixels)
    2007           4 :                 dfTileStdDev = sqrt(dfM2 / nValidPixels);
    2008             : 
    2009           4 :             auto hBand = MEMCreateRasterBandEx(poMEMDS, 1, m_pabyCachedTiles,
    2010             :                                                GDT_Float32, 0, 0, false);
    2011           4 :             poMEMDS->AddMEMBand(hBand);
    2012             :         }
    2013             :         else
    2014             :         {
    2015        4100 :             CPLAssert(m_eDT == GDT_Byte);
    2016       16168 :             for (int i = 0; i < nTileBands; i++)
    2017             :             {
    2018       12068 :                 int iSrc = i;
    2019       12068 :                 if (nBands == 1 && m_poCT == nullptr && nTileBands == 3)
    2020           3 :                     iSrc = 0;
    2021       12065 :                 else if (nBands == 1 && m_poCT == nullptr && bPartialTile &&
    2022             :                          nTileBands == 4)
    2023           8 :                     iSrc = (i < 3) ? 0 : 3;
    2024       12057 :                 else if (nBands == 2 && nTileBands >= 3)
    2025         536 :                     iSrc = (i < 3) ? 0 : 1;
    2026             : 
    2027       24136 :                 auto hBand = MEMCreateRasterBandEx(
    2028             :                     poMEMDS, i + 1,
    2029       12068 :                     m_pabyCachedTiles + iSrc * nBlockXSize * nBlockYSize,
    2030             :                     GDT_Byte, 0, 0, false);
    2031       12068 :                 poMEMDS->AddMEMBand(hBand);
    2032             : 
    2033       12068 :                 if (i == 0 && nTileBands == 1 && m_poCT != nullptr)
    2034          15 :                     poMEMDS->GetRasterBand(1)->SetColorTable(m_poCT);
    2035             :             }
    2036             :         }
    2037             : 
    2038        4154 :         if ((m_eTF == GPKG_TF_PNG_16BIT || m_eTF == GPKG_TF_TIFF_32BIT_FLOAT) &&
    2039          54 :             nValidPixels == 0)
    2040             :         {
    2041             :             // If tile is fully transparent, don't serialize it and remove
    2042             :             // it if it exists.
    2043           1 :             GIntBig nId = GetTileId(nRow, nCol);
    2044           1 :             if (nId > 0)
    2045             :             {
    2046           1 :                 DeleteTile(nRow, nCol);
    2047             : 
    2048           1 :                 DeleteFromGriddedTileAncillary(nId);
    2049             :             }
    2050             : 
    2051           1 :             CPLFree(pTempTileBuffer);
    2052           1 :             delete poMEMDS;
    2053           2 :             return CE_None;
    2054             :         }
    2055             : 
    2056        4153 :         if (m_eTF == GPKG_TF_PNG8 && nTileBands == 1 && nBands >= 3)
    2057             :         {
    2058          10 :             CPLAssert(bAllDirty);
    2059             : 
    2060          10 :             auto poMEM_RGB_DS = MEMDataset::Create("", nBlockXSize, nBlockYSize,
    2061             :                                                    0, GDT_Byte, nullptr);
    2062          40 :             for (int i = 0; i < 3; i++)
    2063             :             {
    2064          60 :                 auto hBand = MEMCreateRasterBandEx(
    2065          30 :                     poMEMDS, i + 1, m_pabyCachedTiles + i * nBandBlockSize,
    2066             :                     GDT_Byte, 0, 0, false);
    2067          30 :                 poMEM_RGB_DS->AddMEMBand(hBand);
    2068             :             }
    2069             : 
    2070          10 :             if (m_pabyHugeColorArray == nullptr)
    2071             :             {
    2072           5 :                 if (nBlockXSize <= 65536 / nBlockYSize)
    2073           4 :                     m_pabyHugeColorArray =
    2074           4 :                         VSIMalloc(MEDIAN_CUT_AND_DITHER_BUFFER_SIZE_65536);
    2075             :                 else
    2076           1 :                     m_pabyHugeColorArray =
    2077           1 :                         VSIMalloc2(256 * 256 * 256, sizeof(GUInt32));
    2078             :             }
    2079             : 
    2080          10 :             GDALColorTable *poCT = new GDALColorTable();
    2081          20 :             GDALComputeMedianCutPCTInternal(
    2082          10 :                 poMEM_RGB_DS->GetRasterBand(1), poMEM_RGB_DS->GetRasterBand(2),
    2083          10 :                 poMEM_RGB_DS->GetRasterBand(3),
    2084             :                 /*NULL, NULL, NULL,*/
    2085          10 :                 m_pabyCachedTiles, m_pabyCachedTiles + nBandBlockSize,
    2086          10 :                 m_pabyCachedTiles + 2 * nBandBlockSize, nullptr,
    2087             :                 256, /* max colors */
    2088             :                 8,   /* bit depth */
    2089             :                 static_cast<GUInt32 *>(
    2090          10 :                     m_pabyHugeColorArray), /* preallocated histogram */
    2091             :                 poCT, nullptr, nullptr);
    2092             : 
    2093          20 :             GDALDitherRGB2PCTInternal(
    2094          10 :                 poMEM_RGB_DS->GetRasterBand(1), poMEM_RGB_DS->GetRasterBand(2),
    2095          10 :                 poMEM_RGB_DS->GetRasterBand(3), poMEMDS->GetRasterBand(1), poCT,
    2096             :                 8, /* bit depth */
    2097             :                 static_cast<GInt16 *>(
    2098          10 :                     m_pabyHugeColorArray), /* pasDynamicColorMap */
    2099          10 :                 m_bDither, nullptr, nullptr);
    2100          10 :             poMEMDS->GetRasterBand(1)->SetColorTable(poCT);
    2101          10 :             delete poCT;
    2102          10 :             GDALClose(poMEM_RGB_DS);
    2103             :         }
    2104        4143 :         else if (nBands == 1 && m_poCT != nullptr && nTileBands > 1)
    2105             :         {
    2106             :             GByte abyCT[4 * 256];
    2107           7 :             const int nEntries = std::min(256, m_poCT->GetColorEntryCount());
    2108        1799 :             for (int i = 0; i < nEntries; i++)
    2109             :             {
    2110        1792 :                 const GDALColorEntry *psEntry = m_poCT->GetColorEntry(i);
    2111        1792 :                 abyCT[4 * i] = static_cast<GByte>(psEntry->c1);
    2112        1792 :                 abyCT[4 * i + 1] = static_cast<GByte>(psEntry->c2);
    2113        1792 :                 abyCT[4 * i + 2] = static_cast<GByte>(psEntry->c3);
    2114        1792 :                 abyCT[4 * i + 3] = static_cast<GByte>(psEntry->c4);
    2115             :             }
    2116           7 :             for (int i = nEntries; i < 256; i++)
    2117             :             {
    2118           0 :                 abyCT[4 * i] = 0;
    2119           0 :                 abyCT[4 * i + 1] = 0;
    2120           0 :                 abyCT[4 * i + 2] = 0;
    2121           0 :                 abyCT[4 * i + 3] = 0;
    2122             :             }
    2123           7 :             if (iYOff > 0)
    2124             :             {
    2125           0 :                 memset(m_pabyCachedTiles + 0 * nBandBlockSize, 0,
    2126           0 :                        nBlockXSize * iYOff);
    2127           0 :                 memset(m_pabyCachedTiles + 1 * nBandBlockSize, 0,
    2128           0 :                        nBlockXSize * iYOff);
    2129           0 :                 memset(m_pabyCachedTiles + 2 * nBandBlockSize, 0,
    2130           0 :                        nBlockXSize * iYOff);
    2131           0 :                 memset(m_pabyCachedTiles + 3 * nBandBlockSize, 0,
    2132           0 :                        nBlockXSize * iYOff);
    2133             :             }
    2134        1057 :             for (GPtrDiff_t iY = iYOff; iY < iYOff + iYCount; iY++)
    2135             :             {
    2136        1050 :                 if (iXOff > 0)
    2137             :                 {
    2138           0 :                     const GPtrDiff_t i = iY * nBlockXSize;
    2139           0 :                     memset(m_pabyCachedTiles + 0 * nBandBlockSize + i, 0,
    2140             :                            iXOff);
    2141           0 :                     memset(m_pabyCachedTiles + 1 * nBandBlockSize + i, 0,
    2142             :                            iXOff);
    2143           0 :                     memset(m_pabyCachedTiles + 2 * nBandBlockSize + i, 0,
    2144             :                            iXOff);
    2145           0 :                     memset(m_pabyCachedTiles + 3 * nBandBlockSize + i, 0,
    2146             :                            iXOff);
    2147             :                 }
    2148      183250 :                 for (int iX = iXOff; iX < iXOff + iXCount; iX++)
    2149             :                 {
    2150      182200 :                     const GPtrDiff_t i = iY * nBlockXSize + iX;
    2151      182200 :                     GByte byVal = m_pabyCachedTiles[i];
    2152      182200 :                     m_pabyCachedTiles[i] = abyCT[4 * byVal];
    2153      182200 :                     m_pabyCachedTiles[i + 1 * nBandBlockSize] =
    2154      182200 :                         abyCT[4 * byVal + 1];
    2155      182200 :                     m_pabyCachedTiles[i + 2 * nBandBlockSize] =
    2156      182200 :                         abyCT[4 * byVal + 2];
    2157      182200 :                     m_pabyCachedTiles[i + 3 * nBandBlockSize] =
    2158      182200 :                         abyCT[4 * byVal + 3];
    2159             :                 }
    2160        1050 :                 if (iXOff + iXCount < nBlockXSize)
    2161             :                 {
    2162         800 :                     const GPtrDiff_t i = iY * nBlockXSize + iXOff + iXCount;
    2163         800 :                     memset(m_pabyCachedTiles + 0 * nBandBlockSize + i, 0,
    2164         800 :                            nBlockXSize - (iXOff + iXCount));
    2165         800 :                     memset(m_pabyCachedTiles + 1 * nBandBlockSize + i, 0,
    2166         800 :                            nBlockXSize - (iXOff + iXCount));
    2167         800 :                     memset(m_pabyCachedTiles + 2 * nBandBlockSize + i, 0,
    2168         800 :                            nBlockXSize - (iXOff + iXCount));
    2169         800 :                     memset(m_pabyCachedTiles + 3 * nBandBlockSize + i, 0,
    2170         800 :                            nBlockXSize - (iXOff + iXCount));
    2171             :                 }
    2172             :             }
    2173           7 :             if (iYOff + iYCount < nBlockYSize)
    2174             :             {
    2175           7 :                 const GPtrDiff_t i = (iYOff + iYCount) * nBlockXSize;
    2176           7 :                 memset(m_pabyCachedTiles + 0 * nBandBlockSize + i, 0,
    2177           7 :                        nBlockXSize * (nBlockYSize - (iYOff + iYCount)));
    2178           7 :                 memset(m_pabyCachedTiles + 1 * nBandBlockSize + i, 0,
    2179           7 :                        nBlockXSize * (nBlockYSize - (iYOff + iYCount)));
    2180           7 :                 memset(m_pabyCachedTiles + 2 * nBandBlockSize + i, 0,
    2181           7 :                        nBlockXSize * (nBlockYSize - (iYOff + iYCount)));
    2182           7 :                 memset(m_pabyCachedTiles + 3 * nBandBlockSize + i, 0,
    2183           7 :                        nBlockXSize * (nBlockYSize - (iYOff + iYCount)));
    2184             :             }
    2185             :         }
    2186             : 
    2187             :         char **papszDriverOptions =
    2188        4153 :             CSLSetNameValue(nullptr, "_INTERNAL_DATASET", "YES");
    2189        4153 :         if (EQUAL(pszDriverName, "JPEG") || EQUAL(pszDriverName, "WEBP"))
    2190             :         {
    2191             :             // If not all bands are dirty, then use lossless WEBP
    2192         172 :             if (!bAllDirty && EQUAL(pszDriverName, "WEBP"))
    2193             :             {
    2194           2 :                 papszDriverOptions =
    2195           2 :                     CSLSetNameValue(papszDriverOptions, "LOSSLESS", "YES");
    2196             :             }
    2197             :             else
    2198             :             {
    2199             :                 papszDriverOptions =
    2200         170 :                     CSLSetNameValue(papszDriverOptions, "QUALITY",
    2201             :                                     CPLSPrintf("%d", m_nQuality));
    2202             :             }
    2203             :         }
    2204        3981 :         else if (EQUAL(pszDriverName, "PNG"))
    2205             :         {
    2206        3977 :             papszDriverOptions = CSLSetNameValue(papszDriverOptions, "ZLEVEL",
    2207             :                                                  CPLSPrintf("%d", m_nZLevel));
    2208             :         }
    2209           4 :         else if (EQUAL(pszDriverName, "GTiff"))
    2210             :         {
    2211             :             papszDriverOptions =
    2212           4 :                 CSLSetNameValue(papszDriverOptions, "COMPRESS", "LZW");
    2213           4 :             if (nBlockXSize * nBlockYSize <= 512 * 512)
    2214             :             {
    2215             :                 // If tile is not too big, create it as single-strip TIFF
    2216             :                 papszDriverOptions =
    2217           4 :                     CSLSetNameValue(papszDriverOptions, "BLOCKYSIZE",
    2218             :                                     CPLSPrintf("%d", nBlockYSize));
    2219             :             }
    2220             :         }
    2221             : #ifdef DEBUG
    2222             :         VSIStatBufL sStat;
    2223        4153 :         CPLAssert(VSIStatL(osMemFileName, &sStat) != 0);
    2224             : #endif
    2225             :         GDALDataset *poOutDS =
    2226        4153 :             l_poDriver->CreateCopy(osMemFileName, poMEMDS, FALSE,
    2227             :                                    papszDriverOptions, nullptr, nullptr);
    2228        4153 :         CSLDestroy(papszDriverOptions);
    2229        4153 :         CPLFree(pTempTileBuffer);
    2230             : 
    2231        4153 :         if (poOutDS)
    2232             :         {
    2233        4151 :             GDALClose(poOutDS);
    2234        4151 :             vsi_l_offset nBlobSize = 0;
    2235             :             GByte *pabyBlob =
    2236        4151 :                 VSIGetMemFileBuffer(osMemFileName, &nBlobSize, TRUE);
    2237             : 
    2238             :             /* Create or commit and recreate transaction */
    2239        4151 :             GDALGPKGMBTilesLikePseudoDataset *poMainDS =
    2240        4151 :                 m_poParentDS ? m_poParentDS : this;
    2241        4151 :             if (poMainDS->m_nTileInsertionCount == 0)
    2242             :             {
    2243         175 :                 poMainDS->IStartTransaction();
    2244             :             }
    2245        3976 :             else if (poMainDS->m_nTileInsertionCount == 1000)
    2246             :             {
    2247           3 :                 if (poMainDS->ICommitTransaction() != OGRERR_NONE)
    2248             :                 {
    2249           1 :                     poMainDS->m_nTileInsertionCount = -1;
    2250           1 :                     CPLFree(pabyBlob);
    2251           1 :                     VSIUnlink(osMemFileName);
    2252           1 :                     delete poMEMDS;
    2253           1 :                     return CE_Failure;
    2254             :                 }
    2255           2 :                 poMainDS->IStartTransaction();
    2256           2 :                 poMainDS->m_nTileInsertionCount = 0;
    2257             :             }
    2258        4150 :             poMainDS->m_nTileInsertionCount++;
    2259             : 
    2260             :             char *pszSQL =
    2261        4150 :                 sqlite3_mprintf("INSERT OR REPLACE INTO \"%w\" "
    2262             :                                 "(zoom_level, tile_row, tile_column, "
    2263             :                                 "tile_data) VALUES (%d, %d, %d, ?)",
    2264             :                                 m_osRasterTable.c_str(), m_nZoomLevel,
    2265        4150 :                                 GetRowFromIntoTopConvention(nRow), nCol);
    2266             : #ifdef DEBUG_VERBOSE
    2267             :             CPLDebug("GPKG", "%s", pszSQL);
    2268             : #endif
    2269        4150 :             sqlite3_stmt *hStmt = nullptr;
    2270        4150 :             int rc = sqlite3_prepare_v2(IGetDB(), pszSQL, -1, &hStmt, nullptr);
    2271        4150 :             if (rc != SQLITE_OK)
    2272             :             {
    2273           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2274             :                          "failed to prepare SQL %s: %s", pszSQL,
    2275           0 :                          sqlite3_errmsg(IGetDB()));
    2276           0 :                 CPLFree(pabyBlob);
    2277             :             }
    2278             :             else
    2279             :             {
    2280        4150 :                 sqlite3_bind_blob(hStmt, 1, pabyBlob,
    2281             :                                   static_cast<int>(nBlobSize), CPLFree);
    2282        4150 :                 rc = sqlite3_step(hStmt);
    2283        4150 :                 if (rc == SQLITE_DONE)
    2284        4150 :                     eErr = CE_None;
    2285             :                 else
    2286             :                 {
    2287           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    2288             :                              "Failure when inserting tile (row=%d,col=%d) at "
    2289             :                              "zoom_level=%d : %s",
    2290           0 :                              GetRowFromIntoTopConvention(nRow), nCol,
    2291           0 :                              m_nZoomLevel, sqlite3_errmsg(IGetDB()));
    2292             :                 }
    2293             :             }
    2294        4150 :             sqlite3_finalize(hStmt);
    2295        4150 :             sqlite3_free(pszSQL);
    2296             : 
    2297        4150 :             if (m_eTF == GPKG_TF_PNG_16BIT || m_eTF == GPKG_TF_TIFF_32BIT_FLOAT)
    2298             :             {
    2299          53 :                 GIntBig nTileId = GetTileId(nRow, nCol);
    2300          53 :                 if (nTileId == 0)
    2301           0 :                     eErr = CE_Failure;
    2302             :                 else
    2303             :                 {
    2304          53 :                     DeleteFromGriddedTileAncillary(nTileId);
    2305             : 
    2306          53 :                     pszSQL = sqlite3_mprintf(
    2307             :                         "INSERT INTO gpkg_2d_gridded_tile_ancillary "
    2308             :                         "(tpudt_name, tpudt_id, scale, offset, min, max, "
    2309             :                         "mean, std_dev) VALUES "
    2310             :                         "('%q', ?, %.18g, %.18g, ?, ?, ?, ?)",
    2311             :                         m_osRasterTable.c_str(), dfTileScale, dfTileOffset);
    2312             : #ifdef DEBUG_VERBOSE
    2313             :                     CPLDebug("GPKG", "%s", pszSQL);
    2314             : #endif
    2315          53 :                     hStmt = nullptr;
    2316          53 :                     rc = sqlite3_prepare_v2(IGetDB(), pszSQL, -1, &hStmt,
    2317             :                                             nullptr);
    2318          53 :                     if (rc != SQLITE_OK)
    2319             :                     {
    2320           0 :                         eErr = CE_Failure;
    2321           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    2322             :                                  "failed to prepare SQL %s: %s", pszSQL,
    2323           0 :                                  sqlite3_errmsg(IGetDB()));
    2324             :                     }
    2325             :                     else
    2326             :                     {
    2327          53 :                         sqlite3_bind_int64(hStmt, 1, nTileId);
    2328          53 :                         sqlite3_bind_double(hStmt, 2, dfTileMin);
    2329          53 :                         sqlite3_bind_double(hStmt, 3, dfTileMax);
    2330          53 :                         sqlite3_bind_double(hStmt, 4, dfTileMean);
    2331          53 :                         sqlite3_bind_double(hStmt, 5, dfTileStdDev);
    2332          53 :                         rc = sqlite3_step(hStmt);
    2333          53 :                         if (rc == SQLITE_DONE)
    2334             :                         {
    2335          53 :                             eErr = CE_None;
    2336             :                         }
    2337             :                         else
    2338             :                         {
    2339           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
    2340             :                                      "Cannot insert into "
    2341             :                                      "gpkg_2d_gridded_tile_ancillary");
    2342           0 :                             eErr = CE_Failure;
    2343             :                         }
    2344             :                     }
    2345          53 :                     sqlite3_finalize(hStmt);
    2346          53 :                     sqlite3_free(pszSQL);
    2347             :                 }
    2348             :             }
    2349             :         }
    2350             : 
    2351        4152 :         VSIUnlink(osMemFileName);
    2352        4152 :         delete poMEMDS;
    2353             :     }
    2354             :     else
    2355             :     {
    2356           1 :         CPLError(CE_Failure, CPLE_NotSupported, "Cannot find driver %s",
    2357             :                  pszDriverName);
    2358             :     }
    2359             : 
    2360        4153 :     return eErr;
    2361             : }
    2362             : 
    2363             : /************************************************************************/
    2364             : /*                     FlushRemainingShiftedTiles()                     */
    2365             : /************************************************************************/
    2366             : 
    2367             : CPLErr
    2368         609 : GDALGPKGMBTilesLikePseudoDataset::FlushRemainingShiftedTiles(bool bPartialFlush)
    2369             : {
    2370         609 :     if (m_hTempDB == nullptr)
    2371         460 :         return CE_None;
    2372             : 
    2373         745 :     for (int i = 0; i <= 3; i++)
    2374             :     {
    2375         596 :         m_asCachedTilesDesc[i].nRow = -1;
    2376         596 :         m_asCachedTilesDesc[i].nCol = -1;
    2377         596 :         m_asCachedTilesDesc[i].nIdxWithinTileData = -1;
    2378             :     }
    2379             : 
    2380             :     int nBlockXSize, nBlockYSize;
    2381         149 :     IGetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
    2382         149 :     const int nBands = IGetRasterCount();
    2383         149 :     const int nRasterXSize = IGetRasterBand(1)->GetXSize();
    2384         149 :     const int nRasterYSize = IGetRasterBand(1)->GetYSize();
    2385         149 :     const int nXBlocks = DIV_ROUND_UP(nRasterXSize, nBlockXSize);
    2386         149 :     const int nYBlocks = DIV_ROUND_UP(nRasterYSize, nBlockYSize);
    2387             : 
    2388         149 :     int nPartialActiveTiles = 0;
    2389         149 :     if (bPartialFlush)
    2390             :     {
    2391           2 :         sqlite3_stmt *hStmt = nullptr;
    2392           4 :         CPLString osSQL;
    2393             :         osSQL.Printf("SELECT COUNT(*) FROM partial_tiles WHERE zoom_level = %d "
    2394             :                      "AND partial_flag != 0",
    2395           2 :                      m_nZoomLevel);
    2396           2 :         if (sqlite3_prepare_v2(m_hTempDB, osSQL.c_str(), -1, &hStmt, nullptr) ==
    2397             :             SQLITE_OK)
    2398             :         {
    2399           2 :             if (sqlite3_step(hStmt) == SQLITE_ROW)
    2400             :             {
    2401           2 :                 nPartialActiveTiles = sqlite3_column_int(hStmt, 0);
    2402           2 :                 CPLDebug("GPKG", "Active partial tiles before flush: %d",
    2403             :                          nPartialActiveTiles);
    2404             :             }
    2405           2 :             sqlite3_finalize(hStmt);
    2406             :         }
    2407             :     }
    2408             : 
    2409         298 :     CPLString osSQL = "SELECT tile_row, tile_column, partial_flag";
    2410         616 :     for (int nBand = 1; nBand <= nBands; nBand++)
    2411             :     {
    2412         467 :         osSQL += CPLSPrintf(", tile_data_band_%d", nBand);
    2413             :     }
    2414             :     osSQL += CPLSPrintf(" FROM partial_tiles WHERE "
    2415             :                         "zoom_level = %d AND partial_flag != 0",
    2416         149 :                         m_nZoomLevel);
    2417         149 :     if (bPartialFlush)
    2418             :     {
    2419           2 :         osSQL += " ORDER BY age";
    2420             :     }
    2421         149 :     const char *pszSQL = osSQL.c_str();
    2422             : 
    2423             : #ifdef DEBUG_VERBOSE
    2424             :     CPLDebug("GPKG", "%s", pszSQL);
    2425             : #endif
    2426         149 :     sqlite3_stmt *hStmt = nullptr;
    2427         149 :     int rc = sqlite3_prepare_v2(m_hTempDB, pszSQL, -1, &hStmt, nullptr);
    2428         149 :     if (rc != SQLITE_OK)
    2429             :     {
    2430           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2431             :                  "sqlite3_prepare_v2(%s) failed: %s", pszSQL,
    2432             :                  sqlite3_errmsg(m_hTempDB));
    2433           0 :         return CE_Failure;
    2434             :     }
    2435             : 
    2436         149 :     CPLErr eErr = CE_None;
    2437         149 :     bool bGotPartialTiles = false;
    2438         149 :     int nCountFlushedTiles = 0;
    2439         149 :     const size_t nBandBlockSize =
    2440         149 :         static_cast<size_t>(nBlockXSize) * nBlockYSize * m_nDTSize;
    2441         175 :     do
    2442             :     {
    2443         324 :         rc = sqlite3_step(hStmt);
    2444         324 :         if (rc == SQLITE_ROW)
    2445             :         {
    2446         177 :             bGotPartialTiles = true;
    2447             : 
    2448         177 :             int nRow = sqlite3_column_int(hStmt, 0);
    2449         177 :             int nCol = sqlite3_column_int(hStmt, 1);
    2450         177 :             int nPartialFlags = sqlite3_column_int(hStmt, 2);
    2451             : 
    2452         177 :             if (bPartialFlush)
    2453             :             {
    2454             :                 // This method assumes that there are no dirty blocks alive
    2455             :                 // so check this assumption.
    2456             :                 // When called with bPartialFlush = false, FlushCache() has
    2457             :                 // already been called, so no need to check.
    2458          16 :                 bool bFoundDirtyBlock = false;
    2459          16 :                 int nBlockXOff = nCol - m_nShiftXTiles;
    2460          16 :                 int nBlockYOff = nRow - m_nShiftYTiles;
    2461          96 :                 for (int iX = 0; !bFoundDirtyBlock &&
    2462          48 :                                  iX < ((m_nShiftXPixelsMod != 0) ? 2 : 1);
    2463             :                      iX++)
    2464             :                 {
    2465          32 :                     if (nBlockXOff + iX < 0 || nBlockXOff + iX >= nXBlocks)
    2466          12 :                         continue;
    2467         120 :                     for (int iY = 0; !bFoundDirtyBlock &&
    2468          60 :                                      iY < ((m_nShiftYPixelsMod != 0) ? 2 : 1);
    2469             :                          iY++)
    2470             :                     {
    2471          40 :                         if (nBlockYOff + iY < 0 || nBlockYOff + iY >= nYBlocks)
    2472          17 :                             continue;
    2473          69 :                         for (int iBand = 1;
    2474          69 :                              !bFoundDirtyBlock && iBand <= nBands; iBand++)
    2475             :                         {
    2476             :                             GDALRasterBlock *poBlock =
    2477             :                                 cpl::down_cast<GDALGPKGMBTilesLikeRasterBand *>(
    2478          46 :                                     IGetRasterBand(iBand))
    2479          46 :                                     ->AccessibleTryGetLockedBlockRef(
    2480             :                                         nBlockXOff + iX, nBlockYOff + iY);
    2481          46 :                             if (poBlock)
    2482             :                             {
    2483           0 :                                 if (poBlock->GetDirty())
    2484           0 :                                     bFoundDirtyBlock = true;
    2485           0 :                                 poBlock->DropLock();
    2486             :                             }
    2487             :                         }
    2488             :                     }
    2489             :                 }
    2490          16 :                 if (bFoundDirtyBlock)
    2491             :                 {
    2492             : #ifdef DEBUG_VERBOSE
    2493             :                     CPLDebug("GPKG",
    2494             :                              "Skipped flushing tile row = %d, column = %d "
    2495             :                              "because it has dirty block(s) in GDAL cache",
    2496             :                              nRow, nCol);
    2497             : #endif
    2498           0 :                     continue;
    2499             :                 }
    2500             :             }
    2501             : 
    2502         177 :             nCountFlushedTiles++;
    2503         177 :             if (bPartialFlush && nCountFlushedTiles >= nPartialActiveTiles / 2)
    2504             :             {
    2505           2 :                 CPLDebug("GPKG", "Flushed %d tiles", nCountFlushedTiles);
    2506           2 :                 break;
    2507             :             }
    2508             : 
    2509         705 :             for (int nBand = 1; nBand <= nBands; nBand++)
    2510             :             {
    2511         530 :                 if (nPartialFlags & (((1 << 4) - 1) << (4 * (nBand - 1))))
    2512             :                 {
    2513         498 :                     CPLAssert(sqlite3_column_bytes(hStmt, 2 + nBand) ==
    2514             :                               static_cast<int>(nBandBlockSize));
    2515         498 :                     memcpy(m_pabyCachedTiles + (nBand - 1) * nBandBlockSize,
    2516             :                            sqlite3_column_blob(hStmt, 2 + nBand),
    2517             :                            nBandBlockSize);
    2518             :                 }
    2519             :                 else
    2520             :                 {
    2521          32 :                     FillEmptyTileSingleBand(m_pabyCachedTiles +
    2522          32 :                                             (nBand - 1) * nBandBlockSize);
    2523             :                 }
    2524             :             }
    2525             : 
    2526         175 :             int nFullFlags = (1 << (4 * nBands)) - 1;
    2527             : 
    2528             :             // In case the partial flags indicate that there's some quadrant
    2529             :             // missing, check in the main database if there is already a tile
    2530             :             // In which case, use the parts of that tile that aren't in the
    2531             :             // temporary database
    2532         175 :             if (nPartialFlags != nFullFlags)
    2533             :             {
    2534         525 :                 char *pszNewSQL = sqlite3_mprintf(
    2535             :                     "SELECT tile_data%s FROM \"%w\" "
    2536             :                     "WHERE zoom_level = %d AND tile_row = %d AND tile_column = "
    2537             :                     "%d%s",
    2538         175 :                     m_eDT != GDT_Byte ? ", id"
    2539             :                                       : "",  // MBTiles do not have an id
    2540             :                     m_osRasterTable.c_str(), m_nZoomLevel,
    2541         175 :                     GetRowFromIntoTopConvention(nRow), nCol,
    2542         175 :                     !m_osWHERE.empty()
    2543           0 :                         ? CPLSPrintf(" AND (%s)", m_osWHERE.c_str())
    2544             :                         : "");
    2545             : #ifdef DEBUG_VERBOSE
    2546             :                 CPLDebug("GPKG", "%s", pszNewSQL);
    2547             : #endif
    2548         175 :                 sqlite3_stmt *hNewStmt = nullptr;
    2549         175 :                 rc = sqlite3_prepare_v2(IGetDB(), pszNewSQL, -1, &hNewStmt,
    2550             :                                         nullptr);
    2551         175 :                 if (rc == SQLITE_OK)
    2552             :                 {
    2553         175 :                     rc = sqlite3_step(hNewStmt);
    2554         192 :                     if (rc == SQLITE_ROW &&
    2555          17 :                         sqlite3_column_type(hNewStmt, 0) == SQLITE_BLOB)
    2556             :                     {
    2557          17 :                         const int nBytes = sqlite3_column_bytes(hNewStmt, 0);
    2558             :                         GIntBig nTileId =
    2559          17 :                             (m_eDT == GDT_Byte)
    2560          17 :                                 ? 0
    2561           0 :                                 : sqlite3_column_int64(hNewStmt, 1);
    2562             :                         GByte *pabyRawData =
    2563             :                             const_cast<GByte *>(static_cast<const GByte *>(
    2564          17 :                                 sqlite3_column_blob(hNewStmt, 0)));
    2565          34 :                         CPLString osMemFileName;
    2566          17 :                         osMemFileName.Printf("/vsimem/gpkg_read_tile_%p", this);
    2567          17 :                         VSILFILE *fp = VSIFileFromMemBuffer(
    2568             :                             osMemFileName.c_str(), pabyRawData, nBytes, FALSE);
    2569          17 :                         VSIFCloseL(fp);
    2570             : 
    2571          17 :                         double dfTileOffset = 0.0;
    2572          17 :                         double dfTileScale = 1.0;
    2573          17 :                         GetTileOffsetAndScale(nTileId, dfTileOffset,
    2574             :                                               dfTileScale);
    2575          17 :                         const int nTileBands = m_eDT == GDT_Byte ? 4 : 1;
    2576          17 :                         GByte *pabyTemp =
    2577          17 :                             m_pabyCachedTiles + nTileBands * nBandBlockSize;
    2578          17 :                         ReadTile(osMemFileName, pabyTemp, dfTileOffset,
    2579             :                                  dfTileScale);
    2580          17 :                         VSIUnlink(osMemFileName);
    2581             : 
    2582          17 :                         int iYQuadrantMax = (m_nShiftYPixelsMod) ? 1 : 0;
    2583          17 :                         int iXQuadrantMax = (m_nShiftXPixelsMod) ? 1 : 0;
    2584          51 :                         for (int iYQuadrant = 0; iYQuadrant <= iYQuadrantMax;
    2585             :                              iYQuadrant++)
    2586             :                         {
    2587         100 :                             for (int iXQuadrant = 0;
    2588         100 :                                  iXQuadrant <= iXQuadrantMax; iXQuadrant++)
    2589             :                             {
    2590         226 :                                 for (int nBand = 1; nBand <= nBands; nBand++)
    2591             :                                 {
    2592         160 :                                     int iQuadrantFlag = 0;
    2593         160 :                                     if (iXQuadrant == 0 && iYQuadrant == 0)
    2594          42 :                                         iQuadrantFlag |= (1 << 0);
    2595         160 :                                     if (iXQuadrant == iXQuadrantMax &&
    2596             :                                         iYQuadrant == 0)
    2597          42 :                                         iQuadrantFlag |= (1 << 1);
    2598         160 :                                     if (iXQuadrant == 0 &&
    2599             :                                         iYQuadrant == iYQuadrantMax)
    2600          42 :                                         iQuadrantFlag |= (1 << 2);
    2601         160 :                                     if (iXQuadrant == iXQuadrantMax &&
    2602             :                                         iYQuadrant == iYQuadrantMax)
    2603          42 :                                         iQuadrantFlag |= (1 << 3);
    2604         160 :                                     int nLocalFlag = iQuadrantFlag
    2605         160 :                                                      << (4 * (nBand - 1));
    2606         160 :                                     if (!(nPartialFlags & nLocalFlag))
    2607             :                                     {
    2608             :                                         int nXOff, nYOff, nXSize, nYSize;
    2609         118 :                                         if (iXQuadrant == 0 &&
    2610          60 :                                             m_nShiftXPixelsMod != 0)
    2611             :                                         {
    2612          56 :                                             nXOff = 0;
    2613          56 :                                             nXSize = m_nShiftXPixelsMod;
    2614             :                                         }
    2615             :                                         else
    2616             :                                         {
    2617          62 :                                             nXOff = m_nShiftXPixelsMod;
    2618          62 :                                             nXSize = nBlockXSize -
    2619          62 :                                                      m_nShiftXPixelsMod;
    2620             :                                         }
    2621         118 :                                         if (iYQuadrant == 0 &&
    2622          63 :                                             m_nShiftYPixelsMod != 0)
    2623             :                                         {
    2624          63 :                                             nYOff = 0;
    2625          63 :                                             nYSize = m_nShiftYPixelsMod;
    2626             :                                         }
    2627             :                                         else
    2628             :                                         {
    2629          55 :                                             nYOff = m_nShiftYPixelsMod;
    2630          55 :                                             nYSize = nBlockYSize -
    2631          55 :                                                      m_nShiftYPixelsMod;
    2632             :                                         }
    2633         118 :                                         for (int iY = nYOff;
    2634       13888 :                                              iY < nYOff + nYSize; iY++)
    2635             :                                         {
    2636       13770 :                                             memcpy(m_pabyCachedTiles +
    2637       13770 :                                                        ((static_cast<size_t>(
    2638       13770 :                                                              nBand - 1) *
    2639       13770 :                                                              nBlockYSize +
    2640       13770 :                                                          iY) *
    2641       13770 :                                                             nBlockXSize +
    2642       13770 :                                                         nXOff) *
    2643       13770 :                                                            m_nDTSize,
    2644       13770 :                                                    pabyTemp +
    2645       13770 :                                                        ((static_cast<size_t>(
    2646       13770 :                                                              nBand - 1) *
    2647       13770 :                                                              nBlockYSize +
    2648       13770 :                                                          iY) *
    2649       13770 :                                                             nBlockXSize +
    2650       13770 :                                                         nXOff) *
    2651       13770 :                                                            m_nDTSize,
    2652       13770 :                                                    static_cast<size_t>(nXSize) *
    2653       13770 :                                                        m_nDTSize);
    2654             :                                         }
    2655             :                                     }
    2656             :                                 }
    2657             :                             }
    2658             :                         }
    2659             :                     }
    2660         158 :                     else if (rc != SQLITE_DONE)
    2661             :                     {
    2662           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    2663             :                                  "sqlite3_step(%s) failed: %s", pszNewSQL,
    2664             :                                  sqlite3_errmsg(m_hTempDB));
    2665             :                     }
    2666         175 :                     sqlite3_finalize(hNewStmt);
    2667             :                 }
    2668             :                 else
    2669             :                 {
    2670           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    2671             :                              "sqlite3_prepare_v2(%s) failed: %s", pszNewSQL,
    2672             :                              sqlite3_errmsg(m_hTempDB));
    2673             :                 }
    2674         175 :                 sqlite3_free(pszNewSQL);
    2675             :             }
    2676             : 
    2677         175 :             m_asCachedTilesDesc[0].nRow = nRow;
    2678         175 :             m_asCachedTilesDesc[0].nCol = nCol;
    2679         175 :             m_asCachedTilesDesc[0].nIdxWithinTileData = 0;
    2680         175 :             m_asCachedTilesDesc[0].abBandDirty[0] = true;
    2681         175 :             m_asCachedTilesDesc[0].abBandDirty[1] = true;
    2682         175 :             m_asCachedTilesDesc[0].abBandDirty[2] = true;
    2683         175 :             m_asCachedTilesDesc[0].abBandDirty[3] = true;
    2684             : 
    2685         175 :             eErr = WriteTile();
    2686             : 
    2687         175 :             if (eErr == CE_None && bPartialFlush)
    2688             :             {
    2689             :                 pszSQL =
    2690          14 :                     CPLSPrintf("DELETE FROM partial_tiles WHERE zoom_level = "
    2691             :                                "%d AND tile_row = %d AND tile_column = %d",
    2692             :                                m_nZoomLevel, nRow, nCol);
    2693             : #ifdef DEBUG_VERBOSE
    2694             :                 CPLDebug("GPKG", "%s", pszSQL);
    2695             : #endif
    2696          14 :                 if (SQLCommand(m_hTempDB, pszSQL) != OGRERR_NONE)
    2697           0 :                     eErr = CE_None;
    2698             :             }
    2699             :         }
    2700             :         else
    2701             :         {
    2702         147 :             if (rc != SQLITE_DONE)
    2703             :             {
    2704           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2705             :                          "sqlite3_step(%s) failed: %s", pszSQL,
    2706             :                          sqlite3_errmsg(m_hTempDB));
    2707             :             }
    2708         147 :             break;
    2709             :         }
    2710         175 :     } while (eErr == CE_None);
    2711             : 
    2712         149 :     sqlite3_finalize(hStmt);
    2713             : 
    2714         149 :     if (bPartialFlush && nCountFlushedTiles < nPartialActiveTiles / 2)
    2715             :     {
    2716           0 :         CPLDebug("GPKG", "Flushed %d tiles. Target was %d", nCountFlushedTiles,
    2717             :                  nPartialActiveTiles / 2);
    2718             :     }
    2719             : 
    2720         149 :     if (bGotPartialTiles && !bPartialFlush)
    2721             :     {
    2722             : #ifdef DEBUG_VERBOSE
    2723             :         pszSQL = CPLSPrintf("SELECT p1.id, p1.tile_row, p1.tile_column FROM "
    2724             :                             "partial_tiles p1, partial_tiles p2 "
    2725             :                             "WHERE p1.zoom_level = %d AND p2.zoom_level = %d "
    2726             :                             "AND p1.tile_row = p2.tile_row AND p1.tile_column "
    2727             :                             "= p2.tile_column AND p2.partial_flag != 0",
    2728             :                             -1 - m_nZoomLevel, m_nZoomLevel);
    2729             :         rc = sqlite3_prepare_v2(m_hTempDB, pszSQL, -1, &hStmt, nullptr);
    2730             :         CPLAssert(rc == SQLITE_OK);
    2731             :         while ((rc = sqlite3_step(hStmt)) == SQLITE_ROW)
    2732             :         {
    2733             :             CPLDebug("GPKG",
    2734             :                      "Conflict: existing id = %d, tile_row = %d, tile_column = "
    2735             :                      "%d, zoom_level = %d",
    2736             :                      sqlite3_column_int(hStmt, 0), sqlite3_column_int(hStmt, 1),
    2737             :                      sqlite3_column_int(hStmt, 2), m_nZoomLevel);
    2738             :         }
    2739             :         sqlite3_finalize(hStmt);
    2740             : #endif
    2741             : 
    2742         110 :         pszSQL = CPLSPrintf("UPDATE partial_tiles SET zoom_level = %d, "
    2743             :                             "partial_flag = 0, age = -1 WHERE zoom_level = %d "
    2744             :                             "AND partial_flag != 0",
    2745          55 :                             -1 - m_nZoomLevel, m_nZoomLevel);
    2746             : #ifdef DEBUG_VERBOSE
    2747             :         CPLDebug("GPKG", "%s", pszSQL);
    2748             : #endif
    2749          55 :         SQLCommand(m_hTempDB, pszSQL);
    2750             :     }
    2751             : 
    2752         149 :     return eErr;
    2753             : }
    2754             : 
    2755             : /************************************************************************/
    2756             : /*                DoPartialFlushOfPartialTilesIfNecessary()             */
    2757             : /************************************************************************/
    2758             : 
    2759             : CPLErr
    2760        4556 : GDALGPKGMBTilesLikePseudoDataset::DoPartialFlushOfPartialTilesIfNecessary()
    2761             : {
    2762        4556 :     time_t nCurTimeStamp = time(nullptr);
    2763        4556 :     if (m_nLastSpaceCheckTimestamp == 0)
    2764           3 :         m_nLastSpaceCheckTimestamp = nCurTimeStamp;
    2765        4556 :     if (m_nLastSpaceCheckTimestamp > 0 &&
    2766          81 :         (m_bForceTempDBCompaction ||
    2767          13 :          nCurTimeStamp - m_nLastSpaceCheckTimestamp > 10))
    2768             :     {
    2769          68 :         m_nLastSpaceCheckTimestamp = nCurTimeStamp;
    2770             :         GIntBig nFreeSpace =
    2771          68 :             VSIGetDiskFreeSpace(CPLGetDirname(m_osTempDBFilename));
    2772          68 :         bool bTryFreeing = false;
    2773          68 :         if (nFreeSpace >= 0 && nFreeSpace < 1024 * 1024 * 1024)
    2774             :         {
    2775           0 :             CPLDebug("GPKG",
    2776             :                      "Free space below 1GB. Flushing part of partial tiles");
    2777           0 :             bTryFreeing = true;
    2778             :         }
    2779             :         else
    2780             :         {
    2781             :             VSIStatBufL sStat;
    2782          68 :             if (VSIStatL(m_osTempDBFilename, &sStat) == 0)
    2783             :             {
    2784          68 :                 GIntBig nTempSpace = sStat.st_size;
    2785         136 :                 if (VSIStatL((m_osTempDBFilename + "-journal").c_str(),
    2786          68 :                              &sStat) == 0)
    2787           0 :                     nTempSpace += sStat.st_size;
    2788         136 :                 else if (VSIStatL((m_osTempDBFilename + "-wal").c_str(),
    2789          68 :                                   &sStat) == 0)
    2790           0 :                     nTempSpace += sStat.st_size;
    2791             : 
    2792             :                 int nBlockXSize, nBlockYSize;
    2793          68 :                 IGetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
    2794          68 :                 const int nBands = IGetRasterCount();
    2795             : 
    2796          68 :                 if (nTempSpace >
    2797          68 :                     4 * static_cast<GIntBig>(IGetRasterBand(1)->GetXSize()) *
    2798          68 :                         nBlockYSize * nBands * m_nDTSize)
    2799             :                 {
    2800           2 :                     CPLDebug("GPKG",
    2801             :                              "Partial tiles DB is " CPL_FRMT_GIB
    2802             :                              " bytes. Flushing part of partial tiles",
    2803             :                              nTempSpace);
    2804           2 :                     bTryFreeing = true;
    2805             :                 }
    2806             :             }
    2807             :         }
    2808          68 :         if (bTryFreeing)
    2809             :         {
    2810           2 :             if (FlushRemainingShiftedTiles(true /* partial flush*/) != CE_None)
    2811             :             {
    2812           0 :                 return CE_Failure;
    2813             :             }
    2814           2 :             SQLCommand(m_hTempDB,
    2815             :                        "DELETE FROM partial_tiles WHERE zoom_level < 0");
    2816           2 :             SQLCommand(m_hTempDB, "VACUUM");
    2817             :         }
    2818             :     }
    2819        4556 :     return CE_None;
    2820             : }
    2821             : 
    2822             : /************************************************************************/
    2823             : /*                         WriteShiftedTile()                           */
    2824             : /************************************************************************/
    2825             : 
    2826        4556 : CPLErr GDALGPKGMBTilesLikePseudoDataset::WriteShiftedTile(
    2827             :     int nRow, int nCol, int nBand, int nDstXOffset, int nDstYOffset,
    2828             :     int nDstXSize, int nDstYSize)
    2829             : {
    2830        4556 :     CPLAssert(m_nShiftXPixelsMod || m_nShiftYPixelsMod);
    2831        4556 :     CPLAssert(nRow >= 0);
    2832        4556 :     CPLAssert(nCol >= 0);
    2833        4556 :     CPLAssert(nRow < m_nTileMatrixHeight);
    2834        4556 :     CPLAssert(nCol < m_nTileMatrixWidth);
    2835             : 
    2836        4556 :     if (m_hTempDB == nullptr &&
    2837          55 :         (m_poParentDS == nullptr || m_poParentDS->m_hTempDB == nullptr))
    2838             :     {
    2839             :         const char *pszBaseFilename =
    2840          48 :             m_poParentDS ? m_poParentDS->IGetFilename() : IGetFilename();
    2841             :         m_osTempDBFilename =
    2842          48 :             CPLResetExtension(pszBaseFilename, "partial_tiles.db");
    2843          48 :         CPLPushErrorHandler(CPLQuietErrorHandler);
    2844          48 :         VSIUnlink(m_osTempDBFilename);
    2845          48 :         CPLPopErrorHandler();
    2846          48 :         m_hTempDB = nullptr;
    2847          48 :         int rc = 0;
    2848          48 :         if (STARTS_WITH(m_osTempDBFilename, "/vsi"))
    2849             :         {
    2850          33 :             m_pMyVFS = OGRSQLiteCreateVFS(nullptr, nullptr);
    2851          33 :             sqlite3_vfs_register(m_pMyVFS, 0);
    2852          33 :             rc = sqlite3_open_v2(m_osTempDBFilename, &m_hTempDB,
    2853             :                                  SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |
    2854             :                                      SQLITE_OPEN_NOMUTEX,
    2855          33 :                                  m_pMyVFS->zName);
    2856             :         }
    2857             :         else
    2858             :         {
    2859          15 :             rc = sqlite3_open(m_osTempDBFilename, &m_hTempDB);
    2860             :         }
    2861          48 :         if (rc != SQLITE_OK || m_hTempDB == nullptr)
    2862             :         {
    2863           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2864             :                      "Cannot create temporary database %s",
    2865             :                      m_osTempDBFilename.c_str());
    2866           0 :             return CE_Failure;
    2867             :         }
    2868          48 :         SQLCommand(m_hTempDB, "PRAGMA synchronous = OFF");
    2869          48 :         SQLCommand(m_hTempDB, "PRAGMA journal_mode = OFF");
    2870          48 :         SQLCommand(m_hTempDB, "CREATE TABLE partial_tiles("
    2871             :                               "id INTEGER PRIMARY KEY AUTOINCREMENT,"
    2872             :                               "zoom_level INTEGER NOT NULL,"
    2873             :                               "tile_column INTEGER NOT NULL,"
    2874             :                               "tile_row INTEGER NOT NULL,"
    2875             :                               "tile_data_band_1 BLOB,"
    2876             :                               "tile_data_band_2 BLOB,"
    2877             :                               "tile_data_band_3 BLOB,"
    2878             :                               "tile_data_band_4 BLOB,"
    2879             :                               "partial_flag INTEGER NOT NULL,"
    2880             :                               "age INTEGER NOT NULL,"
    2881             :                               "UNIQUE (zoom_level, tile_column, tile_row))");
    2882          48 :         SQLCommand(m_hTempDB, "CREATE INDEX partial_tiles_partial_flag_idx "
    2883             :                               "ON partial_tiles(partial_flag)");
    2884          48 :         SQLCommand(m_hTempDB, "CREATE INDEX partial_tiles_age_idx "
    2885             :                               "ON partial_tiles(age)");
    2886             : 
    2887          48 :         if (m_poParentDS != nullptr)
    2888             :         {
    2889           4 :             m_poParentDS->m_osTempDBFilename = m_osTempDBFilename;
    2890           4 :             m_poParentDS->m_hTempDB = m_hTempDB;
    2891             :         }
    2892             :     }
    2893             : 
    2894        4556 :     if (m_poParentDS != nullptr)
    2895        4020 :         m_hTempDB = m_poParentDS->m_hTempDB;
    2896             : 
    2897             :     int nBlockXSize, nBlockYSize;
    2898        4556 :     IGetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
    2899        4556 :     const int nBands = IGetRasterCount();
    2900        4556 :     const size_t nBandBlockSize =
    2901        4556 :         static_cast<size_t>(nBlockXSize) * nBlockYSize * m_nDTSize;
    2902             : 
    2903        4556 :     int iQuadrantFlag = 0;
    2904        4556 :     if (nDstXOffset == 0 && nDstYOffset == 0)
    2905        1031 :         iQuadrantFlag |= (1 << 0);
    2906        4556 :     if ((nDstXOffset != 0 || nDstXOffset + nDstXSize == nBlockXSize) &&
    2907             :         nDstYOffset == 0)
    2908        1125 :         iQuadrantFlag |= (1 << 1);
    2909        4556 :     if (nDstXOffset == 0 &&
    2910        1031 :         (nDstYOffset != 0 || nDstYOffset + nDstYSize == nBlockYSize))
    2911        1135 :         iQuadrantFlag |= (1 << 2);
    2912        4556 :     if ((nDstXOffset != 0 || nDstXOffset + nDstXSize == nBlockXSize) &&
    2913        1125 :         (nDstYOffset != 0 || nDstYOffset + nDstYSize == nBlockYSize))
    2914        1323 :         iQuadrantFlag |= (1 << 3);
    2915        4556 :     int l_nFlags = iQuadrantFlag << (4 * (nBand - 1));
    2916        4556 :     int nFullFlags = (1 << (4 * nBands)) - 1;
    2917        4556 :     int nOldFlags = 0;
    2918             : 
    2919       18224 :     for (int i = 1; i <= 3; i++)
    2920             :     {
    2921       13668 :         m_asCachedTilesDesc[i].nRow = -1;
    2922       13668 :         m_asCachedTilesDesc[i].nCol = -1;
    2923       13668 :         m_asCachedTilesDesc[i].nIdxWithinTileData = -1;
    2924             :     }
    2925             : 
    2926        4556 :     int nExistingId = 0;
    2927        4556 :     const char *pszSQL = CPLSPrintf(
    2928             :         "SELECT id, partial_flag, tile_data_band_%d FROM partial_tiles WHERE "
    2929             :         "zoom_level = %d AND tile_row = %d AND tile_column = %d",
    2930             :         nBand, m_nZoomLevel, nRow, nCol);
    2931             : #ifdef DEBUG_VERBOSE
    2932             :     CPLDebug("GPKG", "%s", pszSQL);
    2933             : #endif
    2934        4556 :     sqlite3_stmt *hStmt = nullptr;
    2935        4556 :     int rc = sqlite3_prepare_v2(m_hTempDB, pszSQL, -1, &hStmt, nullptr);
    2936        4556 :     if (rc != SQLITE_OK)
    2937             :     {
    2938           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2939             :                  "sqlite3_prepare_v2(%s) failed: %s", pszSQL,
    2940             :                  sqlite3_errmsg(m_hTempDB));
    2941           0 :         return CE_Failure;
    2942             :     }
    2943             : 
    2944        4556 :     rc = sqlite3_step(hStmt);
    2945        4556 :     const int nTileBands = m_eDT == GDT_Byte ? 4 : 1;
    2946        4556 :     GByte *pabyTemp = m_pabyCachedTiles + nTileBands * nBandBlockSize;
    2947        4556 :     if (rc == SQLITE_ROW)
    2948             :     {
    2949        4134 :         nExistingId = sqlite3_column_int(hStmt, 0);
    2950             : #ifdef DEBUG_VERBOSE
    2951             :         CPLDebug("GPKG", "Using partial_tile id=%d", nExistingId);
    2952             : #endif
    2953        4134 :         nOldFlags = sqlite3_column_int(hStmt, 1);
    2954        4134 :         CPLAssert(nOldFlags != 0);
    2955        4134 :         if ((nOldFlags & (((1 << 4) - 1) << (4 * (nBand - 1)))) == 0)
    2956             :         {
    2957        1021 :             FillEmptyTileSingleBand(pabyTemp + (nBand - 1) * nBandBlockSize);
    2958             :         }
    2959             :         else
    2960             :         {
    2961        3113 :             CPLAssert(sqlite3_column_bytes(hStmt, 2) ==
    2962             :                       static_cast<int>(nBandBlockSize));
    2963        3113 :             memcpy(pabyTemp + (nBand - 1) * nBandBlockSize,
    2964             :                    sqlite3_column_blob(hStmt, 2), nBandBlockSize);
    2965             :         }
    2966             :     }
    2967             :     else
    2968             :     {
    2969         422 :         FillEmptyTileSingleBand(pabyTemp + (nBand - 1) * nBandBlockSize);
    2970             :     }
    2971        4556 :     sqlite3_finalize(hStmt);
    2972        4556 :     hStmt = nullptr;
    2973             : 
    2974             :     /* Copy the updated rectangle into the full tile */
    2975      556108 :     for (int iY = nDstYOffset; iY < nDstYOffset + nDstYSize; iY++)
    2976             :     {
    2977      551552 :         memcpy(pabyTemp +
    2978      551552 :                    (static_cast<size_t>(nBand - 1) * nBlockXSize * nBlockYSize +
    2979      551552 :                     static_cast<size_t>(iY) * nBlockXSize + nDstXOffset) *
    2980      551552 :                        m_nDTSize,
    2981      551552 :                m_pabyCachedTiles +
    2982      551552 :                    (static_cast<size_t>(nBand - 1) * nBlockXSize * nBlockYSize +
    2983      551552 :                     static_cast<size_t>(iY) * nBlockXSize + nDstXOffset) *
    2984      551552 :                        m_nDTSize,
    2985      551552 :                static_cast<size_t>(nDstXSize) * m_nDTSize);
    2986             :     }
    2987             : 
    2988             : #ifdef notdef
    2989             :     static int nCounter = 1;
    2990             :     GDALDataset *poLogDS =
    2991             :         ((GDALDriver *)GDALGetDriverByName("GTiff"))
    2992             :             ->Create(CPLSPrintf("/tmp/partial_band_%d_%d.tif", 1, nCounter++),
    2993             :                      nBlockXSize, nBlockYSize, nBands, m_eDT, NULL);
    2994             :     poLogDS->RasterIO(GF_Write, 0, 0, nBlockXSize, nBlockYSize,
    2995             :                       pabyTemp + (nBand - 1) * nBandBlockSize, nBlockXSize,
    2996             :                       nBlockYSize, m_eDT, 1, NULL, 0, 0, 0, NULL);
    2997             :     GDALClose(poLogDS);
    2998             : #endif
    2999             : 
    3000        4556 :     if ((nOldFlags & l_nFlags) != 0)
    3001             :     {
    3002           0 :         CPLDebug("GPKG",
    3003             :                  "Rewriting quadrant %d of band %d of tile (row=%d,col=%d)",
    3004             :                  iQuadrantFlag, nBand, nRow, nCol);
    3005             :     }
    3006             : 
    3007        4556 :     l_nFlags |= nOldFlags;
    3008        4556 :     if (l_nFlags == nFullFlags)
    3009             :     {
    3010             : #ifdef DEBUG_VERBOSE
    3011             :         CPLDebug("GPKG", "Got all quadrants for that tile");
    3012             : #endif
    3013        1192 :         for (int iBand = 1; iBand <= nBands; iBand++)
    3014             :         {
    3015         945 :             if (iBand != nBand)
    3016             :             {
    3017         698 :                 pszSQL = CPLSPrintf(
    3018             :                     "SELECT tile_data_band_%d FROM partial_tiles WHERE "
    3019             :                     "id = %d",
    3020             :                     iBand, nExistingId);
    3021             : #ifdef DEBUG_VERBOSE
    3022             :                 CPLDebug("GPKG", "%s", pszSQL);
    3023             : #endif
    3024         698 :                 hStmt = nullptr;
    3025         698 :                 rc = sqlite3_prepare_v2(m_hTempDB, pszSQL, -1, &hStmt, nullptr);
    3026         698 :                 if (rc != SQLITE_OK)
    3027             :                 {
    3028           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    3029             :                              "sqlite3_prepare_v2(%s) failed: %s", pszSQL,
    3030             :                              sqlite3_errmsg(m_hTempDB));
    3031           0 :                     return CE_Failure;
    3032             :                 }
    3033             : 
    3034         698 :                 rc = sqlite3_step(hStmt);
    3035         698 :                 if (rc == SQLITE_ROW)
    3036             :                 {
    3037         698 :                     CPLAssert(sqlite3_column_bytes(hStmt, 0) ==
    3038             :                               static_cast<int>(nBandBlockSize));
    3039         698 :                     memcpy(m_pabyCachedTiles + (iBand - 1) * nBandBlockSize,
    3040             :                            sqlite3_column_blob(hStmt, 0), nBandBlockSize);
    3041             :                 }
    3042         698 :                 sqlite3_finalize(hStmt);
    3043         698 :                 hStmt = nullptr;
    3044             :             }
    3045             :             else
    3046             :             {
    3047         247 :                 memcpy(m_pabyCachedTiles + (iBand - 1) * nBandBlockSize,
    3048         247 :                        pabyTemp + (iBand - 1) * nBandBlockSize, nBandBlockSize);
    3049             :             }
    3050             :         }
    3051             : 
    3052         247 :         m_asCachedTilesDesc[0].nRow = nRow;
    3053         247 :         m_asCachedTilesDesc[0].nCol = nCol;
    3054         247 :         m_asCachedTilesDesc[0].nIdxWithinTileData = 0;
    3055         247 :         m_asCachedTilesDesc[0].abBandDirty[0] = true;
    3056         247 :         m_asCachedTilesDesc[0].abBandDirty[1] = true;
    3057         247 :         m_asCachedTilesDesc[0].abBandDirty[2] = true;
    3058         247 :         m_asCachedTilesDesc[0].abBandDirty[3] = true;
    3059             : 
    3060         494 :         pszSQL = CPLSPrintf("UPDATE partial_tiles SET zoom_level = %d, "
    3061             :                             "partial_flag = 0, age = -1 WHERE id = %d",
    3062         247 :                             -1 - m_nZoomLevel, nExistingId);
    3063         247 :         SQLCommand(m_hTempDB, pszSQL);
    3064             : #ifdef DEBUG_VERBOSE
    3065             :         CPLDebug("GPKG", "%s", pszSQL);
    3066             : #endif
    3067         247 :         CPLErr eErr = WriteTile();
    3068             : 
    3069             :         // Call DoPartialFlushOfPartialTilesIfNecessary() after using
    3070             :         // m_pabyCachedTiles as it is going to mess with it.
    3071         247 :         if (DoPartialFlushOfPartialTilesIfNecessary() != CE_None)
    3072           0 :             eErr = CE_None;
    3073             : 
    3074         247 :         return eErr;
    3075             :     }
    3076             : 
    3077        4309 :     if (nExistingId == 0)
    3078             :     {
    3079             :         OGRErr err;
    3080         844 :         pszSQL = CPLSPrintf("SELECT id FROM partial_tiles WHERE "
    3081             :                             "partial_flag = 0 AND zoom_level = %d "
    3082             :                             "AND tile_row = %d AND tile_column = %d",
    3083         422 :                             -1 - m_nZoomLevel, nRow, nCol);
    3084             : #ifdef DEBUG_VERBOSE
    3085             :         CPLDebug("GPKG", "%s", pszSQL);
    3086             : #endif
    3087         422 :         nExistingId = SQLGetInteger(m_hTempDB, pszSQL, &err);
    3088         422 :         if (nExistingId == 0)
    3089             :         {
    3090         418 :             pszSQL =
    3091             :                 "SELECT id FROM partial_tiles WHERE partial_flag = 0 LIMIT 1";
    3092             : #ifdef DEBUG_VERBOSE
    3093             :             CPLDebug("GPKG", "%s", pszSQL);
    3094             : #endif
    3095         418 :             nExistingId = SQLGetInteger(m_hTempDB, pszSQL, &err);
    3096             :         }
    3097             :     }
    3098             : 
    3099        4309 :     const GIntBig nAge = (m_poParentDS) ? m_poParentDS->m_nAge : m_nAge;
    3100        4309 :     if (nExistingId == 0)
    3101             :     {
    3102         338 :         pszSQL = CPLSPrintf(
    3103             :             "INSERT INTO partial_tiles "
    3104             :             "(zoom_level, tile_row, tile_column, tile_data_band_%d, "
    3105             :             "partial_flag, age) VALUES (%d, %d, %d, ?, %d, " CPL_FRMT_GIB ")",
    3106             :             nBand, m_nZoomLevel, nRow, nCol, l_nFlags, nAge);
    3107             :     }
    3108             :     else
    3109             :     {
    3110        3971 :         pszSQL = CPLSPrintf(
    3111             :             "UPDATE partial_tiles SET zoom_level = %d, "
    3112             :             "tile_row = %d, tile_column = %d, "
    3113             :             "tile_data_band_%d = ?, partial_flag = %d, age = " CPL_FRMT_GIB
    3114             :             " WHERE id = %d",
    3115             :             m_nZoomLevel, nRow, nCol, nBand, l_nFlags, nAge, nExistingId);
    3116             :     }
    3117        4309 :     if (m_poParentDS)
    3118        3801 :         m_poParentDS->m_nAge++;
    3119             :     else
    3120         508 :         m_nAge++;
    3121             : 
    3122             : #ifdef DEBUG_VERBOSE
    3123             :     CPLDebug("GPKG", "%s", pszSQL);
    3124             : #endif
    3125             : 
    3126        4309 :     hStmt = nullptr;
    3127        4309 :     rc = sqlite3_prepare_v2(m_hTempDB, pszSQL, -1, &hStmt, nullptr);
    3128        4309 :     if (rc != SQLITE_OK)
    3129             :     {
    3130           0 :         CPLError(CE_Failure, CPLE_AppDefined, "failed to prepare SQL %s: %s",
    3131             :                  pszSQL, sqlite3_errmsg(m_hTempDB));
    3132           0 :         return CE_Failure;
    3133             :     }
    3134             : 
    3135        4309 :     sqlite3_bind_blob(hStmt, 1, pabyTemp + (nBand - 1) * nBandBlockSize,
    3136             :                       static_cast<int>(nBandBlockSize), SQLITE_TRANSIENT);
    3137        4309 :     rc = sqlite3_step(hStmt);
    3138        4309 :     CPLErr eErr = CE_Failure;
    3139        4309 :     if (rc == SQLITE_DONE)
    3140        4309 :         eErr = CE_None;
    3141             :     else
    3142             :     {
    3143           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3144             :                  "Failure when inserting partial tile (row=%d,col=%d) at "
    3145             :                  "zoom_level=%d : %s",
    3146             :                  nRow, nCol, m_nZoomLevel, sqlite3_errmsg(m_hTempDB));
    3147             :     }
    3148             : 
    3149        4309 :     sqlite3_finalize(hStmt);
    3150             : 
    3151             :     // Call DoPartialFlushOfPartialTilesIfNecessary() after using
    3152             :     // m_pabyCachedTiles as it is going to mess with it.
    3153        4309 :     if (DoPartialFlushOfPartialTilesIfNecessary() != CE_None)
    3154           0 :         eErr = CE_None;
    3155             : 
    3156        4309 :     return eErr;
    3157             : }
    3158             : 
    3159             : /************************************************************************/
    3160             : /*                         IWriteBlock()                                */
    3161             : /************************************************************************/
    3162             : 
    3163        5819 : CPLErr GDALGPKGMBTilesLikeRasterBand::IWriteBlock(int nBlockXOff,
    3164             :                                                   int nBlockYOff, void *pData)
    3165             : {
    3166             : #ifdef DEBUG_VERBOSE
    3167             :     CPLDebug(
    3168             :         "GPKG",
    3169             :         "IWriteBlock(nBand=%d,nBlockXOff=%d,nBlockYOff=%d,m_nZoomLevel=%d)",
    3170             :         nBand, nBlockXOff, nBlockYOff, m_poTPD->m_nZoomLevel);
    3171             : #endif
    3172        5819 :     if (!m_poTPD->ICanIWriteBlock())
    3173             :     {
    3174           0 :         return CE_Failure;
    3175             :     }
    3176        5819 :     if (m_poTPD->m_poParentDS)
    3177        1139 :         m_poTPD->m_poParentDS->m_bHasModifiedTiles = true;
    3178             :     else
    3179        4680 :         m_poTPD->m_bHasModifiedTiles = true;
    3180             : 
    3181        5819 :     int nRow = nBlockYOff + m_poTPD->m_nShiftYTiles;
    3182        5819 :     int nCol = nBlockXOff + m_poTPD->m_nShiftXTiles;
    3183             : 
    3184        5819 :     int nRowMin = nRow;
    3185        5819 :     int nRowMax = nRowMin;
    3186        5819 :     if (m_poTPD->m_nShiftYPixelsMod)
    3187        1360 :         nRowMax++;
    3188             : 
    3189        5819 :     int nColMin = nCol;
    3190        5819 :     int nColMax = nColMin;
    3191        5819 :     if (m_poTPD->m_nShiftXPixelsMod)
    3192        1322 :         nColMax++;
    3193             : 
    3194        5819 :     CPLErr eErr = CE_None;
    3195             : 
    3196       12998 :     for (nRow = nRowMin; eErr == CE_None && nRow <= nRowMax; nRow++)
    3197             :     {
    3198       17002 :         for (nCol = nColMin; eErr == CE_None && nCol <= nColMax; nCol++)
    3199             :         {
    3200        9823 :             if (nRow < 0 || nCol < 0 || nRow >= m_poTPD->m_nTileMatrixHeight ||
    3201        9683 :                 nCol >= m_poTPD->m_nTileMatrixWidth)
    3202             :             {
    3203         167 :                 continue;
    3204             :             }
    3205             : 
    3206        9656 :             if (m_poTPD->m_nShiftXPixelsMod == 0 &&
    3207        4485 :                 m_poTPD->m_nShiftYPixelsMod == 0)
    3208             :             {
    3209        4427 :                 if (!(nRow == m_poTPD->m_asCachedTilesDesc[0].nRow &&
    3210         250 :                       nCol == m_poTPD->m_asCachedTilesDesc[0].nCol &&
    3211           5 :                       m_poTPD->m_asCachedTilesDesc[0].nIdxWithinTileData == 0))
    3212             :                 {
    3213        4422 :                     eErr = m_poTPD->WriteTile();
    3214             : 
    3215        4422 :                     m_poTPD->m_asCachedTilesDesc[0].nRow = nRow;
    3216        4422 :                     m_poTPD->m_asCachedTilesDesc[0].nCol = nCol;
    3217        4422 :                     m_poTPD->m_asCachedTilesDesc[0].nIdxWithinTileData = 0;
    3218             :                 }
    3219             :             }
    3220             : 
    3221             :             // Composite block data into tile, and check if all bands for this
    3222             :             // block are dirty, and if so write the tile
    3223        9656 :             bool bAllDirty = true;
    3224       42163 :             for (int iBand = 1; iBand <= poDS->GetRasterCount(); iBand++)
    3225             :             {
    3226       32507 :                 GDALRasterBlock *poBlock = nullptr;
    3227       32507 :                 GByte *pabySrc = nullptr;
    3228       32507 :                 if (iBand == nBand)
    3229             :                 {
    3230        9656 :                     pabySrc = static_cast<GByte *>(pData);
    3231             :                 }
    3232             :                 else
    3233             :                 {
    3234       22851 :                     if (!(m_poTPD->m_nShiftXPixelsMod == 0 &&
    3235        8349 :                           m_poTPD->m_nShiftYPixelsMod == 0))
    3236       14646 :                         continue;
    3237             : 
    3238             :                     // If the block for this band is not dirty, it might be
    3239             :                     // dirty in cache
    3240        8205 :                     if (m_poTPD->m_asCachedTilesDesc[0].abBandDirty[iBand - 1])
    3241         505 :                         continue;
    3242             :                     else
    3243             :                     {
    3244             :                         poBlock =
    3245        7700 :                             cpl::down_cast<GDALGPKGMBTilesLikeRasterBand *>(
    3246        7700 :                                 poDS->GetRasterBand(iBand))
    3247        7700 :                                 ->TryGetLockedBlockRef(nBlockXOff, nBlockYOff);
    3248        7700 :                         if (poBlock && poBlock->GetDirty())
    3249             :                         {
    3250             :                             pabySrc =
    3251        7660 :                                 static_cast<GByte *>(poBlock->GetDataRef());
    3252        7660 :                             poBlock->MarkClean();
    3253             :                         }
    3254             :                         else
    3255             :                         {
    3256          40 :                             if (poBlock)
    3257           2 :                                 poBlock->DropLock();
    3258          40 :                             bAllDirty = false;
    3259          40 :                             continue;
    3260             :                         }
    3261             :                     }
    3262             :                 }
    3263             : 
    3264       17316 :                 if (m_poTPD->m_nShiftXPixelsMod == 0 &&
    3265       12145 :                     m_poTPD->m_nShiftYPixelsMod == 0)
    3266       12087 :                     m_poTPD->m_asCachedTilesDesc[0].abBandDirty[iBand - 1] =
    3267             :                         true;
    3268             : 
    3269       17316 :                 int nDstXOffset = 0;
    3270       17316 :                 int nDstXSize = nBlockXSize;
    3271       17316 :                 int nDstYOffset = 0;
    3272       17316 :                 int nDstYSize = nBlockYSize;
    3273             :                 // Composite block data into tile data
    3274       17316 :                 if (m_poTPD->m_nShiftXPixelsMod == 0 &&
    3275       12145 :                     m_poTPD->m_nShiftYPixelsMod == 0)
    3276             :                 {
    3277             : 
    3278             : #ifdef DEBUG_VERBOSE
    3279             :                     if (eDataType == GDT_Byte &&
    3280             :                         nBlockXOff * nBlockXSize <=
    3281             :                             nRasterXSize - nBlockXSize &&
    3282             :                         nBlockYOff * nBlockYSize > nRasterYSize - nBlockYSize)
    3283             :                     {
    3284             :                         bool bFoundNonZero = false;
    3285             :                         for (int y = nRasterYSize - nBlockYOff * nBlockYSize;
    3286             :                              y < nBlockYSize; y++)
    3287             :                         {
    3288             :                             for (int x = 0; x < nBlockXSize; x++)
    3289             :                             {
    3290             :                                 if (pabySrc[static_cast<GPtrDiff_t>(y) *
    3291             :                                                 nBlockXSize +
    3292             :                                             x] != 0 &&
    3293             :                                     !bFoundNonZero)
    3294             :                                 {
    3295             :                                     CPLDebug("GPKG",
    3296             :                                              "IWriteBlock(): Found non-zero "
    3297             :                                              "content in ghost part of "
    3298             :                                              "tile(nBand=%d,nBlockXOff=%d,"
    3299             :                                              "nBlockYOff=%d,m_nZoomLevel=%d)\n",
    3300             :                                              iBand, nBlockXOff, nBlockYOff,
    3301             :                                              m_poTPD->m_nZoomLevel);
    3302             :                                     bFoundNonZero = true;
    3303             :                                 }
    3304             :                             }
    3305             :                         }
    3306             :                     }
    3307             : #endif
    3308             : 
    3309       12087 :                     const size_t nBandBlockSize =
    3310       12087 :                         static_cast<size_t>(nBlockXSize) * nBlockYSize *
    3311       12087 :                         m_nDTSize;
    3312       12087 :                     memcpy(m_poTPD->m_pabyCachedTiles +
    3313       12087 :                                (iBand - 1) * nBandBlockSize,
    3314             :                            pabySrc, nBandBlockSize);
    3315             : 
    3316             :                     // Make sure partial blocks are zero'ed outside of the
    3317             :                     // validity area but do that only when know that JPEG will
    3318             :                     // not be used so as to avoid edge effects (although we
    3319             :                     // should probably repeat last pixels if we really want to
    3320             :                     // do that, but that only makes sense if readers only clip
    3321             :                     // to the gpkg_contents extent). Well, ere on the safe side
    3322             :                     // for now
    3323       12087 :                     if (m_poTPD->m_eTF != GPKG_TF_JPEG &&
    3324       11839 :                         (nBlockXOff * nBlockXSize >=
    3325       11839 :                              nRasterXSize - nBlockXSize ||
    3326       11327 :                          nBlockYOff * nBlockYSize >=
    3327       11327 :                              nRasterYSize - nBlockYSize))
    3328             :                     {
    3329        1003 :                         int nXEndValidity =
    3330        1003 :                             nRasterXSize - nBlockXOff * nBlockXSize;
    3331        1003 :                         if (nXEndValidity > nBlockXSize)
    3332         491 :                             nXEndValidity = nBlockXSize;
    3333        1003 :                         int nYEndValidity =
    3334        1003 :                             nRasterYSize - nBlockYOff * nBlockYSize;
    3335        1003 :                         if (nYEndValidity > nBlockYSize)
    3336         292 :                             nYEndValidity = nBlockYSize;
    3337        1003 :                         if (nXEndValidity < nBlockXSize)
    3338             :                         {
    3339        8277 :                             for (int iY = 0; iY < nYEndValidity; iY++)
    3340             :                             {
    3341        8108 :                                 m_poTPD->FillBuffer(
    3342        8108 :                                     m_poTPD->m_pabyCachedTiles +
    3343        8108 :                                         ((static_cast<size_t>(iBand - 1) *
    3344        8108 :                                               nBlockYSize +
    3345        8108 :                                           iY) *
    3346        8108 :                                              nBlockXSize +
    3347        8108 :                                          nXEndValidity) *
    3348        8108 :                                             m_nDTSize,
    3349        8108 :                                     nBlockXSize - nXEndValidity);
    3350             :                             }
    3351             :                         }
    3352        1003 :                         if (nYEndValidity < nBlockYSize)
    3353             :                         {
    3354         205 :                             m_poTPD->FillBuffer(
    3355         205 :                                 m_poTPD->m_pabyCachedTiles +
    3356         205 :                                     (static_cast<size_t>(iBand - 1) *
    3357         205 :                                          nBlockYSize +
    3358         205 :                                      nYEndValidity) *
    3359         205 :                                         nBlockXSize * m_nDTSize,
    3360         205 :                                 static_cast<size_t>(nBlockYSize -
    3361             :                                                     nYEndValidity) *
    3362         205 :                                     nBlockXSize);
    3363             :                         }
    3364       12087 :                     }
    3365             :                 }
    3366             :                 else
    3367             :                 {
    3368        5229 :                     const int nXValid =
    3369        5229 :                         (nBlockXOff * nBlockXSize > nRasterXSize - nBlockXSize)
    3370        5229 :                             ? (nRasterXSize - nBlockXOff * nBlockXSize)
    3371             :                             : nBlockXSize;
    3372        5229 :                     const int nYValid =
    3373        5229 :                         (nBlockYOff * nBlockYSize > nRasterYSize - nBlockYSize)
    3374        5229 :                             ? (nRasterYSize - nBlockYOff * nBlockYSize)
    3375             :                             : nBlockYSize;
    3376             : 
    3377        5229 :                     int nSrcXOffset = 0;
    3378        5229 :                     if (nCol == nColMin)
    3379             :                     {
    3380        2643 :                         nDstXOffset = m_poTPD->m_nShiftXPixelsMod;
    3381        2643 :                         nDstXSize = std::min(
    3382        2643 :                             nXValid, nBlockXSize - m_poTPD->m_nShiftXPixelsMod);
    3383             :                     }
    3384             :                     else
    3385             :                     {
    3386        2586 :                         nDstXOffset = 0;
    3387        2586 :                         if (nXValid > nBlockXSize - m_poTPD->m_nShiftXPixelsMod)
    3388             :                         {
    3389        2212 :                             nDstXSize = nXValid - (nBlockXSize -
    3390        2212 :                                                    m_poTPD->m_nShiftXPixelsMod);
    3391             :                         }
    3392             :                         else
    3393         374 :                             nDstXSize = 0;
    3394        2586 :                         nSrcXOffset = nBlockXSize - m_poTPD->m_nShiftXPixelsMod;
    3395             :                     }
    3396             : 
    3397        5229 :                     int nSrcYOffset = 0;
    3398        5229 :                     if (nRow == nRowMin)
    3399             :                     {
    3400        2607 :                         nDstYOffset = m_poTPD->m_nShiftYPixelsMod;
    3401        2607 :                         nDstYSize = std::min(
    3402        2607 :                             nYValid, nBlockYSize - m_poTPD->m_nShiftYPixelsMod);
    3403             :                     }
    3404             :                     else
    3405             :                     {
    3406        2622 :                         nDstYOffset = 0;
    3407        2622 :                         if (nYValid > nBlockYSize - m_poTPD->m_nShiftYPixelsMod)
    3408             :                         {
    3409        2232 :                             nDstYSize = nYValid - (nBlockYSize -
    3410        2232 :                                                    m_poTPD->m_nShiftYPixelsMod);
    3411             :                         }
    3412             :                         else
    3413         390 :                             nDstYSize = 0;
    3414        2622 :                         nSrcYOffset = nBlockYSize - m_poTPD->m_nShiftYPixelsMod;
    3415             :                     }
    3416             : 
    3417             : #ifdef DEBUG_VERBOSE
    3418             :                     CPLDebug("GPKG",
    3419             :                              "Copy source tile x=%d,w=%d,y=%d,h=%d into "
    3420             :                              "buffer at x=%d,y=%d",
    3421             :                              nDstXOffset, nDstXSize, nDstYOffset, nDstYSize,
    3422             :                              nSrcXOffset, nSrcYOffset);
    3423             : #endif
    3424             : 
    3425        5229 :                     if (nDstXSize > 0 && nDstYSize > 0)
    3426             :                     {
    3427      556108 :                         for (GPtrDiff_t y = 0; y < nDstYSize; y++)
    3428             :                         {
    3429      551552 :                             GByte *pDst = m_poTPD->m_pabyCachedTiles +
    3430      551552 :                                           (static_cast<size_t>(iBand - 1) *
    3431      551552 :                                                nBlockXSize * nBlockYSize +
    3432      551552 :                                            (y + nDstYOffset) * nBlockXSize +
    3433      551552 :                                            nDstXOffset) *
    3434      551552 :                                               m_nDTSize;
    3435      551552 :                             GByte *pSrc =
    3436      551552 :                                 pabySrc + ((y + nSrcYOffset) * nBlockXSize +
    3437      551552 :                                            nSrcXOffset) *
    3438      551552 :                                               m_nDTSize;
    3439      551552 :                             GDALCopyWords(pSrc, eDataType, m_nDTSize, pDst,
    3440             :                                           eDataType, m_nDTSize, nDstXSize);
    3441             :                         }
    3442             :                     }
    3443             :                 }
    3444             : 
    3445       17316 :                 if (poBlock)
    3446        7660 :                     poBlock->DropLock();
    3447             : 
    3448       17316 :                 if (!(m_poTPD->m_nShiftXPixelsMod == 0 &&
    3449       12145 :                       m_poTPD->m_nShiftYPixelsMod == 0))
    3450             :                 {
    3451        5229 :                     m_poTPD->m_asCachedTilesDesc[0].nRow = -1;
    3452        5229 :                     m_poTPD->m_asCachedTilesDesc[0].nCol = -1;
    3453        5229 :                     m_poTPD->m_asCachedTilesDesc[0].nIdxWithinTileData = -1;
    3454        5229 :                     if (nDstXSize > 0 && nDstYSize > 0)
    3455             :                     {
    3456        4556 :                         eErr = m_poTPD->WriteShiftedTile(
    3457             :                             nRow, nCol, iBand, nDstXOffset, nDstYOffset,
    3458             :                             nDstXSize, nDstYSize);
    3459             :                     }
    3460             :                 }
    3461             :             }
    3462             : 
    3463        9656 :             if (m_poTPD->m_nShiftXPixelsMod == 0 &&
    3464        4485 :                 m_poTPD->m_nShiftYPixelsMod == 0)
    3465             :             {
    3466        4427 :                 if (bAllDirty)
    3467             :                 {
    3468        4405 :                     eErr = m_poTPD->WriteTile();
    3469             :                 }
    3470             :             }
    3471             :         }
    3472             :     }
    3473             : 
    3474        5819 :     return eErr;
    3475             : }
    3476             : 
    3477             : /************************************************************************/
    3478             : /*                           GetNoDataValue()                           */
    3479             : /************************************************************************/
    3480             : 
    3481       18320 : double GDALGPKGMBTilesLikeRasterBand::GetNoDataValue(int *pbSuccess)
    3482             : {
    3483       18320 :     if (m_bHasNoData)
    3484             :     {
    3485         404 :         if (pbSuccess)
    3486         403 :             *pbSuccess = TRUE;
    3487         404 :         return m_dfNoDataValue;
    3488             :     }
    3489       17916 :     return GDALPamRasterBand::GetNoDataValue(pbSuccess);
    3490             : }
    3491             : 
    3492             : /************************************************************************/
    3493             : /*                        SetNoDataValueInternal()                      */
    3494             : /************************************************************************/
    3495             : 
    3496          75 : void GDALGPKGMBTilesLikeRasterBand::SetNoDataValueInternal(double dfNoDataValue)
    3497             : {
    3498          75 :     m_bHasNoData = true;
    3499          75 :     m_dfNoDataValue = dfNoDataValue;
    3500          75 : }
    3501             : 
    3502             : /************************************************************************/
    3503             : /*                      GDALGeoPackageRasterBand()                      */
    3504             : /************************************************************************/
    3505             : 
    3506        1751 : GDALGeoPackageRasterBand::GDALGeoPackageRasterBand(
    3507        1751 :     GDALGeoPackageDataset *poDSIn, int nTileWidth, int nTileHeight)
    3508        1751 :     : GDALGPKGMBTilesLikeRasterBand(poDSIn, nTileWidth, nTileHeight)
    3509             : {
    3510        1751 :     poDS = poDSIn;
    3511        1751 : }
    3512             : 
    3513             : /************************************************************************/
    3514             : /*                         GetOverviewCount()                           */
    3515             : /************************************************************************/
    3516             : 
    3517           8 : int GDALGeoPackageRasterBand::GetOverviewCount()
    3518             : {
    3519             :     GDALGeoPackageDataset *poGDS =
    3520           8 :         cpl::down_cast<GDALGeoPackageDataset *>(poDS);
    3521           8 :     return poGDS->m_nOverviewCount;
    3522             : }
    3523             : 
    3524             : /************************************************************************/
    3525             : /*                         GetOverviewCount()                           */
    3526             : /************************************************************************/
    3527             : 
    3528          41 : GDALRasterBand *GDALGeoPackageRasterBand::GetOverview(int nIdx)
    3529             : {
    3530          41 :     GDALGeoPackageDataset *poGDS =
    3531             :         reinterpret_cast<GDALGeoPackageDataset *>(poDS);
    3532          41 :     if (nIdx < 0 || nIdx >= poGDS->m_nOverviewCount)
    3533           1 :         return nullptr;
    3534          40 :     return poGDS->m_papoOverviewDS[nIdx]->GetRasterBand(nBand);
    3535             : }
    3536             : 
    3537             : /************************************************************************/
    3538             : /*                           SetNoDataValue()                           */
    3539             : /************************************************************************/
    3540             : 
    3541          23 : CPLErr GDALGeoPackageRasterBand::SetNoDataValue(double dfNoDataValue)
    3542             : {
    3543             :     GDALGeoPackageDataset *poGDS =
    3544          23 :         cpl::down_cast<GDALGeoPackageDataset *>(poDS);
    3545             : 
    3546          23 :     if (eDataType == GDT_Byte)
    3547             :     {
    3548           6 :         if (!(dfNoDataValue >= 0 && dfNoDataValue <= 255 &&
    3549           4 :               static_cast<int>(dfNoDataValue) == dfNoDataValue))
    3550             :         {
    3551           2 :             CPLError(CE_Failure, CPLE_NotSupported,
    3552             :                      "Invalid nodata value for a Byte band: %.18g",
    3553             :                      dfNoDataValue);
    3554           2 :             return CE_Failure;
    3555             :         }
    3556             : 
    3557           8 :         for (int i = 1; i <= poGDS->nBands; ++i)
    3558             :         {
    3559           5 :             if (i != nBand)
    3560             :             {
    3561           2 :                 int bHasNoData = FALSE;
    3562             :                 double dfOtherNoDataValue =
    3563           2 :                     poGDS->GetRasterBand(i)->GetNoDataValue(&bHasNoData);
    3564           2 :                 if (bHasNoData && dfOtherNoDataValue != dfNoDataValue)
    3565             :                 {
    3566           1 :                     CPLError(
    3567             :                         CE_Failure, CPLE_NotSupported,
    3568             :                         "Only the same nodata value can be set on all bands");
    3569           1 :                     return CE_Failure;
    3570             :                 }
    3571             :             }
    3572             :         }
    3573             : 
    3574           3 :         SetNoDataValueInternal(dfNoDataValue);
    3575           3 :         poGDS->m_bMetadataDirty = true;
    3576           3 :         return CE_None;
    3577             :     }
    3578             : 
    3579          17 :     if (CPLIsNan(dfNoDataValue))
    3580             :     {
    3581           0 :         CPLError(CE_Warning, CPLE_NotSupported,
    3582             :                  "A NaN nodata value cannot be recorded in "
    3583             :                  "gpkg_2d_gridded_coverage_ancillary table");
    3584             :     }
    3585             : 
    3586          17 :     SetNoDataValueInternal(dfNoDataValue);
    3587             : 
    3588          17 :     char *pszSQL = sqlite3_mprintf(
    3589             :         "UPDATE gpkg_2d_gridded_coverage_ancillary SET data_null = ? "
    3590             :         "WHERE tile_matrix_set_name = '%q'",
    3591             :         poGDS->m_osRasterTable.c_str());
    3592          17 :     sqlite3_stmt *hStmt = nullptr;
    3593          17 :     int rc = sqlite3_prepare_v2(poGDS->IGetDB(), pszSQL, -1, &hStmt, nullptr);
    3594          17 :     if (rc == SQLITE_OK)
    3595             :     {
    3596          17 :         if (poGDS->m_eTF == GPKG_TF_PNG_16BIT)
    3597             :         {
    3598          15 :             if (eDataType == GDT_UInt16 && poGDS->m_dfOffset == 0.0 &&
    3599           6 :                 poGDS->m_dfScale == 1.0 && dfNoDataValue >= 0 &&
    3600           5 :                 dfNoDataValue <= 65535 &&
    3601           5 :                 static_cast<GUInt16>(dfNoDataValue) == dfNoDataValue)
    3602             :             {
    3603           5 :                 poGDS->m_usGPKGNull = static_cast<GUInt16>(dfNoDataValue);
    3604             :             }
    3605             :             else
    3606             :             {
    3607          10 :                 poGDS->m_usGPKGNull = 65535;
    3608             :             }
    3609          15 :             sqlite3_bind_double(hStmt, 1, poGDS->m_usGPKGNull);
    3610             :         }
    3611             :         else
    3612             :         {
    3613           2 :             sqlite3_bind_double(hStmt, 1, static_cast<float>(dfNoDataValue));
    3614             :         }
    3615          17 :         rc = sqlite3_step(hStmt);
    3616          17 :         sqlite3_finalize(hStmt);
    3617             :     }
    3618          17 :     sqlite3_free(pszSQL);
    3619             : 
    3620          17 :     return (rc == SQLITE_OK) ? CE_None : CE_Failure;
    3621             : }
    3622             : 
    3623             : /************************************************************************/
    3624             : /*                         LoadBandMetadata()                           */
    3625             : /************************************************************************/
    3626             : 
    3627         972 : void GDALGeoPackageRasterBand::LoadBandMetadata()
    3628             : {
    3629             :     GDALGeoPackageDataset *poGDS =
    3630         972 :         cpl::down_cast<GDALGeoPackageDataset *>(poDS);
    3631             : 
    3632         972 :     if (m_bHasReadMetadataFromStorage)
    3633         882 :         return;
    3634             : 
    3635         397 :     m_bHasReadMetadataFromStorage = true;
    3636             : 
    3637         397 :     poGDS->TryLoadXML();
    3638             : 
    3639         397 :     if (!poGDS->HasMetadataTables())
    3640         307 :         return;
    3641             : 
    3642          90 :     char *pszSQL = sqlite3_mprintf(
    3643             :         "SELECT md.metadata, md.md_standard_uri, md.mime_type "
    3644             :         "FROM gpkg_metadata md "
    3645             :         "JOIN gpkg_metadata_reference mdr ON (md.id = mdr.md_file_id ) "
    3646             :         "WHERE "
    3647             :         "mdr.reference_scope = 'table' AND lower(mdr.table_name) = "
    3648             :         "lower('%q') ORDER BY md.id "
    3649             :         "LIMIT 1000",  // to avoid denial of service
    3650             :         poGDS->m_osRasterTable.c_str());
    3651             : 
    3652          90 :     auto oResult = SQLQuery(poGDS->hDB, pszSQL);
    3653          90 :     sqlite3_free(pszSQL);
    3654          90 :     if (!oResult)
    3655             :     {
    3656           0 :         return;
    3657             :     }
    3658             : 
    3659             :     /* GDAL metadata */
    3660         177 :     for (int i = 0; i < oResult->RowCount(); i++)
    3661             :     {
    3662          87 :         const char *pszMetadata = oResult->GetValue(0, i);
    3663          87 :         const char *pszMDStandardURI = oResult->GetValue(1, i);
    3664          87 :         const char *pszMimeType = oResult->GetValue(2, i);
    3665          87 :         if (pszMetadata && pszMDStandardURI && pszMimeType &&
    3666          87 :             EQUAL(pszMDStandardURI, "http://gdal.org") &&
    3667          63 :             EQUAL(pszMimeType, "text/xml"))
    3668             :         {
    3669          63 :             CPLXMLNode *psXMLNode = CPLParseXMLString(pszMetadata);
    3670          63 :             if (psXMLNode)
    3671             :             {
    3672         126 :                 GDALMultiDomainMetadata oLocalMDMD;
    3673          63 :                 oLocalMDMD.XMLInit(psXMLNode, FALSE);
    3674             : 
    3675          63 :                 CSLConstList papszDomainList = oLocalMDMD.GetDomainList();
    3676         176 :                 for (CSLConstList papszIter = papszDomainList;
    3677         176 :                      papszIter && *papszIter; ++papszIter)
    3678             :                 {
    3679         113 :                     if (STARTS_WITH(*papszIter, "BAND_"))
    3680             :                     {
    3681           5 :                         int l_nBand = atoi(*papszIter + strlen("BAND_"));
    3682           5 :                         if (l_nBand >= 1 && l_nBand <= poGDS->GetRasterCount())
    3683             :                         {
    3684             :                             auto l_poBand =
    3685           5 :                                 cpl::down_cast<GDALGeoPackageRasterBand *>(
    3686             :                                     poGDS->GetRasterBand(l_nBand));
    3687           5 :                             l_poBand->m_bHasReadMetadataFromStorage = true;
    3688             : 
    3689          10 :                             char **papszMD = CSLDuplicate(
    3690           5 :                                 oLocalMDMD.GetMetadata(*papszIter));
    3691          10 :                             papszMD = CSLMerge(
    3692             :                                 papszMD,
    3693           5 :                                 GDALGPKGMBTilesLikeRasterBand::GetMetadata(""));
    3694           5 :                             l_poBand->GDALPamRasterBand::SetMetadata(papszMD);
    3695           5 :                             CSLDestroy(papszMD);
    3696             :                         }
    3697             :                     }
    3698             :                 }
    3699             : 
    3700          63 :                 CPLDestroyXMLNode(psXMLNode);
    3701             :             }
    3702             :         }
    3703             :     }
    3704             : }
    3705             : 
    3706             : /************************************************************************/
    3707             : /*                            GetMetadata()                             */
    3708             : /************************************************************************/
    3709             : 
    3710         895 : char **GDALGeoPackageRasterBand::GetMetadata(const char *pszDomain)
    3711             : {
    3712         895 :     GDALGeoPackageDataset *poGDS =
    3713             :         reinterpret_cast<GDALGeoPackageDataset *>(poDS);
    3714         895 :     LoadBandMetadata(); /* force loading from storage if needed */
    3715             : 
    3716          15 :     if (poGDS->eAccess == GA_ReadOnly && eDataType != GDT_Byte &&
    3717          11 :         (pszDomain == nullptr || EQUAL(pszDomain, "")) &&
    3718          11 :         !m_bMinMaxComputedFromTileAncillary &&
    3719         918 :         !GDALGPKGMBTilesLikeRasterBand::GetMetadataItem("STATISTICS_MINIMUM") &&
    3720           8 :         !GDALGPKGMBTilesLikeRasterBand::GetMetadataItem("STATISTICS_MAXIMUM"))
    3721             :     {
    3722           8 :         m_bMinMaxComputedFromTileAncillary = true;
    3723             : 
    3724           8 :         const int nColMin = poGDS->m_nShiftXTiles;
    3725           8 :         const int nColMax =
    3726           8 :             (nRasterXSize - 1 + poGDS->m_nShiftXPixelsMod) / nBlockXSize +
    3727           8 :             poGDS->m_nShiftXTiles;
    3728           8 :         const int nRowMin = poGDS->m_nShiftYTiles;
    3729           8 :         const int nRowMax =
    3730           8 :             (nRasterYSize - 1 + poGDS->m_nShiftYPixelsMod) / nBlockYSize +
    3731           8 :             poGDS->m_nShiftYTiles;
    3732             : 
    3733           8 :         bool bOK = false;
    3734           8 :         if (poGDS->m_nShiftXPixelsMod == 0 && poGDS->m_nShiftYPixelsMod == 0 &&
    3735           8 :             (nRasterXSize % nBlockXSize) == 0 &&
    3736           2 :             (nRasterYSize % nBlockYSize) == 0)
    3737             :         {
    3738             :             // If the area of interest matches entire tiles, then we can
    3739             :             // use tile statistics
    3740           2 :             bOK = true;
    3741             :         }
    3742           6 :         else if (m_bHasNoData)
    3743             :         {
    3744             :             // Otherwise, in the case where we have nodata, we assume that
    3745             :             // if the area of interest is at least larger than the existing
    3746             :             // tiles, the tile statistics will be reliable.
    3747           2 :             char *pszSQL = sqlite3_mprintf(
    3748             :                 "SELECT MIN(tile_column), MAX(tile_column), "
    3749             :                 "MIN(tile_row), MAX(tile_row) FROM \"%w\" "
    3750             :                 "WHERE zoom_level = %d",
    3751             :                 poGDS->m_osRasterTable.c_str(), poGDS->m_nZoomLevel);
    3752           4 :             auto sResult = SQLQuery(poGDS->IGetDB(), pszSQL);
    3753           2 :             if (sResult && sResult->RowCount() == 1)
    3754             :             {
    3755           2 :                 const char *pszMinX = sResult->GetValue(0, 0);
    3756           2 :                 const char *pszMaxX = sResult->GetValue(1, 0);
    3757           2 :                 const char *pszMinY = sResult->GetValue(2, 0);
    3758           2 :                 const char *pszMaxY = sResult->GetValue(3, 0);
    3759           2 :                 if (pszMinX && pszMaxX && pszMinY && pszMaxY)
    3760             :                 {
    3761           2 :                     bOK = atoi(pszMinX) >= nColMin &&
    3762           2 :                           atoi(pszMaxX) <= nColMax &&
    3763           4 :                           atoi(pszMinY) >= nRowMin && atoi(pszMaxY) <= nRowMax;
    3764             :                 }
    3765             :             }
    3766           2 :             sqlite3_free(pszSQL);
    3767             :         }
    3768             : 
    3769           8 :         if (bOK)
    3770             :         {
    3771           4 :             char *pszSQL = sqlite3_mprintf(
    3772             :                 "SELECT MIN(min), MAX(max) FROM "
    3773             :                 "gpkg_2d_gridded_tile_ancillary WHERE tpudt_id "
    3774             :                 "IN (SELECT id FROM \"%w\" WHERE "
    3775             :                 "zoom_level = %d AND "
    3776             :                 "tile_column >= %d AND tile_column <= %d AND "
    3777             :                 "tile_row >= %d AND tile_row <= %d)",
    3778             :                 poGDS->m_osRasterTable.c_str(), poGDS->m_nZoomLevel, nColMin,
    3779             :                 nColMax, nRowMin, nRowMax);
    3780           8 :             auto sResult = SQLQuery(poGDS->IGetDB(), pszSQL);
    3781           4 :             CPLDebug("GPKG", "%s", pszSQL);
    3782           4 :             if (sResult && sResult->RowCount() == 1)
    3783             :             {
    3784           4 :                 const char *pszMin = sResult->GetValue(0, 0);
    3785           4 :                 const char *pszMax = sResult->GetValue(1, 0);
    3786           4 :                 if (pszMin)
    3787             :                 {
    3788           4 :                     m_dfStatsMinFromTileAncillary = CPLAtof(pszMin);
    3789             :                 }
    3790           4 :                 if (pszMax)
    3791             :                 {
    3792           4 :                     m_dfStatsMaxFromTileAncillary = CPLAtof(pszMax);
    3793             :                 }
    3794             :             }
    3795           4 :             sqlite3_free(pszSQL);
    3796             :         }
    3797             :     }
    3798             : 
    3799         671 :     if (m_bAddImplicitStatistics && m_bMinMaxComputedFromTileAncillary &&
    3800           8 :         (pszDomain == nullptr || EQUAL(pszDomain, "")) &&
    3801        1574 :         !GDALGPKGMBTilesLikeRasterBand::GetMetadataItem("STATISTICS_MINIMUM") &&
    3802           8 :         !GDALGPKGMBTilesLikeRasterBand::GetMetadataItem("STATISTICS_MAXIMUM"))
    3803             :     {
    3804             :         m_aosMD.Assign(CSLDuplicate(
    3805           8 :             GDALGPKGMBTilesLikeRasterBand::GetMetadata(pszDomain)));
    3806           8 :         if (!std::isnan(m_dfStatsMinFromTileAncillary))
    3807             :         {
    3808             :             m_aosMD.SetNameValue(
    3809             :                 "STATISTICS_MINIMUM",
    3810           4 :                 CPLSPrintf("%.14g", m_dfStatsMinFromTileAncillary));
    3811             :         }
    3812           8 :         if (!std::isnan(m_dfStatsMaxFromTileAncillary))
    3813             :         {
    3814             :             m_aosMD.SetNameValue(
    3815             :                 "STATISTICS_MAXIMUM",
    3816           4 :                 CPLSPrintf("%.14g", m_dfStatsMaxFromTileAncillary));
    3817             :         }
    3818           8 :         return m_aosMD.List();
    3819             :     }
    3820             : 
    3821         887 :     return GDALGPKGMBTilesLikeRasterBand::GetMetadata(pszDomain);
    3822             : }
    3823             : 
    3824             : /************************************************************************/
    3825             : /*                          GetMetadataItem()                           */
    3826             : /************************************************************************/
    3827             : 
    3828          47 : const char *GDALGeoPackageRasterBand::GetMetadataItem(const char *pszName,
    3829             :                                                       const char *pszDomain)
    3830             : {
    3831          47 :     LoadBandMetadata(); /* force loading from storage if needed */
    3832             : 
    3833          47 :     if (m_bAddImplicitStatistics && eDataType != GDT_Byte &&
    3834           4 :         (pszDomain == nullptr || EQUAL(pszDomain, "")) &&
    3835           4 :         (EQUAL(pszName, "STATISTICS_MINIMUM") ||
    3836           2 :          EQUAL(pszName, "STATISTICS_MAXIMUM")))
    3837             :     {
    3838           2 :         return CSLFetchNameValue(GetMetadata(), pszName);
    3839             :     }
    3840             : 
    3841          45 :     return GDALGPKGMBTilesLikeRasterBand::GetMetadataItem(pszName, pszDomain);
    3842             : }
    3843             : 
    3844             : /************************************************************************/
    3845             : /*                            SetMetadata()                             */
    3846             : /************************************************************************/
    3847             : 
    3848          10 : CPLErr GDALGeoPackageRasterBand::SetMetadata(char **papszMetadata,
    3849             :                                              const char *pszDomain)
    3850             : {
    3851             :     GDALGeoPackageDataset *poGDS =
    3852          10 :         cpl::down_cast<GDALGeoPackageDataset *>(poDS);
    3853          10 :     LoadBandMetadata(); /* force loading from storage if needed */
    3854          10 :     poGDS->m_bMetadataDirty = true;
    3855          40 :     for (CSLConstList papszIter = papszMetadata; papszIter && *papszIter;
    3856             :          ++papszIter)
    3857             :     {
    3858          30 :         if (STARTS_WITH(*papszIter, "STATISTICS_"))
    3859          30 :             m_bStatsMetadataSetInThisSession = true;
    3860             :     }
    3861          10 :     return GDALPamRasterBand::SetMetadata(papszMetadata, pszDomain);
    3862             : }
    3863             : 
    3864             : /************************************************************************/
    3865             : /*                          SetMetadataItem()                           */
    3866             : /************************************************************************/
    3867             : 
    3868          20 : CPLErr GDALGeoPackageRasterBand::SetMetadataItem(const char *pszName,
    3869             :                                                  const char *pszValue,
    3870             :                                                  const char *pszDomain)
    3871             : {
    3872             :     GDALGeoPackageDataset *poGDS =
    3873          20 :         cpl::down_cast<GDALGeoPackageDataset *>(poDS);
    3874          20 :     LoadBandMetadata(); /* force loading from storage if needed */
    3875          20 :     poGDS->m_bMetadataDirty = true;
    3876          20 :     if (STARTS_WITH(pszName, "STATISTICS_"))
    3877          20 :         m_bStatsMetadataSetInThisSession = true;
    3878          20 :     return GDALPamRasterBand::SetMetadataItem(pszName, pszValue, pszDomain);
    3879             : }
    3880             : 
    3881             : /************************************************************************/
    3882             : /*                         InvalidateStatistics()                       */
    3883             : /************************************************************************/
    3884             : 
    3885         327 : void GDALGeoPackageRasterBand::InvalidateStatistics()
    3886             : {
    3887         327 :     bool bModified = false;
    3888         654 :     CPLStringList aosMD(CSLDuplicate(GetMetadata()));
    3889         347 :     for (CSLConstList papszIter = GetMetadata(); papszIter && *papszIter;
    3890             :          ++papszIter)
    3891             :     {
    3892          20 :         if (STARTS_WITH(*papszIter, "STATISTICS_"))
    3893             :         {
    3894          20 :             char *pszKey = nullptr;
    3895          20 :             CPLParseNameValue(*papszIter, &pszKey);
    3896          20 :             CPLAssert(pszKey);
    3897          20 :             aosMD.SetNameValue(pszKey, nullptr);
    3898          20 :             CPLFree(pszKey);
    3899          20 :             bModified = true;
    3900             :         }
    3901             :     }
    3902         327 :     if (bModified)
    3903           4 :         SetMetadata(aosMD.List());
    3904         327 : }

Generated by: LCOV version 1.14