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