diff --git a/autotest/gdrivers/mbtiles.py b/autotest/gdrivers/mbtiles.py index 53130b1bea61..54c49f7ed1c0 100755 --- a/autotest/gdrivers/mbtiles.py +++ b/autotest/gdrivers/mbtiles.py @@ -435,6 +435,41 @@ def test_mbtiles_7(): gdal.Unlink("/vsimem/mbtiles_7.mbtiles") +############################################################################### +# Test building overview + + +@pytest.mark.require_driver("PNG") +def test_mbtiles_overview_minzoom(): + + filename = "/vsimem/test_mbtiles_overview_minzoom.mbtiles" + + gdal.Translate(filename, "data/byte.tif", options="-outsize 1024 0") + ds = gdal.Open(filename, gdal.GA_Update) + assert ds.GetMetadataItem("minzoom") == "17" + assert ds.GetMetadataItem("maxzoom") == "17" + ds.BuildOverviews("NEAR", [2]) + ds = None + + ds = gdal.Open(filename) + assert ds.GetRasterBand(1).GetOverviewCount() == 1 + assert ds.GetMetadataItem("minzoom") == "16" + assert ds.GetMetadataItem("maxzoom") == "17" + ds = None + + ds = gdal.Open(filename, gdal.GA_Update) + ds.BuildOverviews("NEAR", [8]) + ds = None + + ds = gdal.Open(filename) + assert ds.GetRasterBand(1).GetOverviewCount() == 3 + assert ds.GetMetadataItem("minzoom") == "14" + assert ds.GetMetadataItem("maxzoom") == "17" + ds = None + + gdal.Unlink(filename) + + ############################################################################### # Single band with 24 bit color table, PNG diff --git a/frmts/mbtiles/mbtilesdataset.cpp b/frmts/mbtiles/mbtilesdataset.cpp index 5ea4defd85cf..d4e774df055e 100644 --- a/frmts/mbtiles/mbtilesdataset.cpp +++ b/frmts/mbtiles/mbtilesdataset.cpp @@ -3539,15 +3539,14 @@ CPLErr MBTilesDataset::IBuildOverviews( int nRows = 0; int nCols = 0; char **papszResult = nullptr; - sqlite3_get_table(hDB, "SELECT * FROM metadata WHERE name = 'minzoom'", - &papszResult, &nRows, &nCols, nullptr); + sqlite3_get_table( + hDB, "SELECT * FROM metadata WHERE name = 'minzoom' LIMIT 2", + &papszResult, &nRows, &nCols, nullptr); sqlite3_free_table(papszResult); if (nRows == 1) { - sqlite3_exec(hDB, "DELETE FROM metadata WHERE name = 'minzoom'", - nullptr, nullptr, nullptr); pszSQL = sqlite3_mprintf( - "INSERT INTO metadata (name, value) VALUES ('minzoom', '%d')", + "UPDATE metadata SET value = %d WHERE name = 'minzoom'", m_nZoomLevel); sqlite3_exec(hDB, pszSQL, nullptr, nullptr, nullptr); sqlite3_free(pszSQL); @@ -3572,6 +3571,18 @@ CPLErr MBTilesDataset::IBuildOverviews( } FlushCache(false); + + const auto GetOverviewIndex = [](int nVal) + { + int iOvr = -1; + while (nVal > 1) + { + nVal >>= 1; + iOvr++; + } + return iOvr; + }; + for (int i = 0; i < nOverviews; i++) { if (panOverviewList[i] < 2) @@ -3588,18 +3599,19 @@ CPLErr MBTilesDataset::IBuildOverviews( panOverviewList[i]); return CE_Failure; } + const int iOvr = GetOverviewIndex(panOverviewList[i]); + if (iOvr >= m_nOverviewCount) + { + CPLDebug("MBTILES", + "Requested overview factor %d leads to too small overview " + "and will be ignored", + panOverviewList[i]); + } } GDALRasterBand ***papapoOverviewBands = (GDALRasterBand ***)CPLCalloc(sizeof(void *), nBands); int iCurOverview = 0; - int nMinZoom = m_nZoomLevel; - for (int i = 0; i < m_nOverviewCount; i++) - { - MBTilesDataset *poODS = m_papoOverviewDS[i]; - if (poODS->m_nZoomLevel < nMinZoom) - nMinZoom = poODS->m_nZoomLevel; - } for (int iBand = 0; iBand < nBands; iBand++) { papapoOverviewBands[iBand] = @@ -3607,21 +3619,14 @@ CPLErr MBTilesDataset::IBuildOverviews( iCurOverview = 0; for (int i = 0; i < nOverviews; i++) { - int nVal = panOverviewList[i]; - int iOvr = -1; - while (nVal > 1) - { - nVal >>= 1; - iOvr++; - } - if (iOvr >= m_nOverviewCount) + const int iOvr = GetOverviewIndex(panOverviewList[i]); + if (iOvr < m_nOverviewCount) { - continue; + MBTilesDataset *poODS = m_papoOverviewDS[iOvr]; + papapoOverviewBands[iBand][iCurOverview] = + poODS->GetRasterBand(iBand + 1); + iCurOverview++; } - MBTilesDataset *poODS = m_papoOverviewDS[iOvr]; - papapoOverviewBands[iBand][iCurOverview] = - poODS->GetRasterBand(iBand + 1); - iCurOverview++; } } @@ -3637,19 +3642,36 @@ CPLErr MBTilesDataset::IBuildOverviews( if (eErr == CE_None) { + // Determine new minzoom value from the existing one and the new + // requested overview levels + int nMinZoom = m_nZoomLevel; + bool bHasMinZoomMetadata = false; int nRows = 0; int nCols = 0; char **papszResult = nullptr; sqlite3_get_table( - hDB, "SELECT * FROM metadata WHERE name = 'minzoom' LIMIT 2", + hDB, "SELECT value FROM metadata WHERE name = 'minzoom' LIMIT 2", &papszResult, &nRows, &nCols, nullptr); + if (nRows == 1 && nCols == 1 && papszResult[1]) + { + bHasMinZoomMetadata = true; + nMinZoom = atoi(papszResult[1]); + } sqlite3_free_table(papszResult); - if (nRows == 1) + if (bHasMinZoomMetadata) { - sqlite3_exec(hDB, "DELETE FROM metadata WHERE name = 'minzoom'", - nullptr, nullptr, nullptr); + for (int i = 0; i < nOverviews; i++) + { + const int iOvr = GetOverviewIndex(panOverviewList[i]); + if (iOvr < m_nOverviewCount) + { + const MBTilesDataset *poODS = m_papoOverviewDS[iOvr]; + nMinZoom = std::min(nMinZoom, poODS->m_nZoomLevel); + } + } + char *pszSQL = sqlite3_mprintf( - "INSERT INTO metadata (name, value) VALUES ('minzoom', '%d')", + "UPDATE metadata SET value = '%d' WHERE name = 'minzoom'", nMinZoom); sqlite3_exec(hDB, pszSQL, nullptr, nullptr, nullptr); sqlite3_free(pszSQL);