LCOV - code coverage report
Current view: top level - apps - gdaladdo.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 383 458 83.6 %
Date: 2025-06-19 12:30:01 Functions: 9 10 90.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL Utilities
       4             :  * Purpose:  Command line application to build overviews.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2000, Frank Warmerdam
       9             :  * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_string.h"
      15             : #include "gdal_version.h"
      16             : #include "gdal_priv.h"
      17             : #include "commonutils.h"
      18             : #include "vrtdataset.h"
      19             : #include "vrt_priv.h"
      20             : #include "gdalargumentparser.h"
      21             : 
      22             : #include <algorithm>
      23             : #include <limits>
      24             : 
      25             : /************************************************************************/
      26             : /*                        GDALAddoErrorHandler()                        */
      27             : /************************************************************************/
      28             : 
      29             : class GDALError
      30             : {
      31             :   public:
      32             :     CPLErr m_eErr;
      33             :     CPLErrorNum m_errNum;
      34             :     CPLString m_osMsg;
      35             : 
      36           1 :     explicit GDALError(CPLErr eErr = CE_None, CPLErrorNum errNum = CPLE_None,
      37             :                        const char *pszMsg = "")
      38           1 :         : m_eErr(eErr), m_errNum(errNum), m_osMsg(pszMsg ? pszMsg : "")
      39             :     {
      40           1 :     }
      41             : };
      42             : 
      43             : std::vector<GDALError> aoErrors;
      44             : 
      45           1 : static void CPL_STDCALL GDALAddoErrorHandler(CPLErr eErr, CPLErrorNum errNum,
      46             :                                              const char *pszMsg)
      47             : {
      48           1 :     aoErrors.push_back(GDALError(eErr, errNum, pszMsg));
      49           1 : }
      50             : 
      51             : /************************************************************************/
      52             : /*                              PartialRefresh()                        */
      53             : /************************************************************************/
      54             : 
      55           4 : static bool PartialRefresh(GDALDataset *poDS,
      56             :                            const std::vector<int> &anOvrIndices, int nBandCount,
      57             :                            const int *panBandList, const char *pszResampling,
      58             :                            int nXOff, int nYOff, int nXSize, int nYSize,
      59             :                            GDALProgressFunc pfnProgress, void *pProgressArg)
      60             : {
      61           8 :     std::vector<int> anBandList;
      62           4 :     if (nBandCount == 0)
      63             :     {
      64           8 :         for (int i = 0; i < poDS->GetRasterCount(); ++i)
      65           4 :             anBandList.push_back(i + 1);
      66           4 :         nBandCount = poDS->GetRasterCount();
      67           4 :         panBandList = anBandList.data();
      68             :     }
      69             : 
      70           4 :     int nOvCount = 0;
      71           8 :     for (int i = 0; i < nBandCount; ++i)
      72             :     {
      73           4 :         auto poSrcBand = poDS->GetRasterBand(panBandList[i]);
      74           4 :         if (i == 0)
      75           4 :             nOvCount = poSrcBand->GetOverviewCount();
      76           0 :         else if (nOvCount != poSrcBand->GetOverviewCount())
      77             :         {
      78           0 :             CPLError(CE_Failure, CPLE_AppDefined,
      79             :                      "Not same number of overviews on all bands");
      80           0 :             return false;
      81             :         }
      82             :     }
      83             : 
      84           8 :     std::vector<GDALRasterBand *> apoSrcBands;
      85           8 :     std::vector<GDALRasterBand **> apapoOverviewBands;
      86           8 :     for (int i = 0; i < nBandCount; ++i)
      87             :     {
      88           4 :         auto poSrcBand = poDS->GetRasterBand(panBandList[i]);
      89           4 :         apoSrcBands.push_back(poSrcBand);
      90           0 :         apapoOverviewBands.push_back(static_cast<GDALRasterBand **>(
      91           4 :             CPLMalloc(sizeof(GDALRasterBand *) * anOvrIndices.size())));
      92           4 :         int j = 0;
      93           8 :         for (int nOvrIdx : anOvrIndices)
      94             :         {
      95           4 :             apapoOverviewBands[i][j] = poSrcBand->GetOverview(nOvrIdx);
      96           4 :             ++j;
      97             :         }
      98             :     }
      99             : 
     100           4 :     CPLStringList aosOptions;
     101           4 :     aosOptions.SetNameValue("XOFF", CPLSPrintf("%d", nXOff));
     102           4 :     aosOptions.SetNameValue("YOFF", CPLSPrintf("%d", nYOff));
     103           4 :     aosOptions.SetNameValue("XSIZE", CPLSPrintf("%d", nXSize));
     104           4 :     aosOptions.SetNameValue("YSIZE", CPLSPrintf("%d", nYSize));
     105           4 :     bool bOK = GDALRegenerateOverviewsMultiBand(
     106           4 :                    nBandCount, apoSrcBands.data(),
     107           4 :                    static_cast<int>(anOvrIndices.size()),
     108           4 :                    apapoOverviewBands.data(), pszResampling, pfnProgress,
     109           4 :                    pProgressArg, aosOptions.List()) == CE_None;
     110           8 :     for (auto papoOverviewBands : apapoOverviewBands)
     111           4 :         CPLFree(papoOverviewBands);
     112           4 :     return bOK;
     113             : }
     114             : 
     115             : /************************************************************************/
     116             : /*                           GetOvrIndices()                            */
     117             : /************************************************************************/
     118             : 
     119           4 : static bool GetOvrIndices(GDALDataset *poDS, int nLevelCount,
     120             :                           const int *panLevels, bool bMinSizeSpecified,
     121             :                           int nMinSize, std::vector<int> &anOvrIndices)
     122             : {
     123           4 :     auto poBand = poDS->GetRasterBand(1);
     124           4 :     if (!poBand)
     125             :     {
     126           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Dataset has no bands");
     127           0 :         return false;
     128             :     }
     129           4 :     const int nOvCount = poBand->GetOverviewCount();
     130           4 :     if (nOvCount == 0)
     131             :     {
     132           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Dataset has no overviews");
     133           0 :         return false;
     134             :     }
     135             : 
     136           4 :     if (nLevelCount == 0)
     137             :     {
     138           3 :         if (!bMinSizeSpecified)
     139             :         {
     140           6 :             for (int i = 0; i < nOvCount; ++i)
     141           3 :                 anOvrIndices.push_back(i);
     142             :         }
     143             :         else
     144             :         {
     145           0 :             for (int i = 0; i < nOvCount; i++)
     146             :             {
     147           0 :                 GDALRasterBand *poOverview = poBand->GetOverview(i);
     148           0 :                 if (poOverview == nullptr)
     149           0 :                     continue;
     150           0 :                 if (poOverview->GetXSize() >= nMinSize ||
     151           0 :                     poOverview->GetYSize() >= nMinSize)
     152             :                 {
     153           0 :                     anOvrIndices.push_back(i);
     154             :                 }
     155             :             }
     156             :         }
     157             :     }
     158             :     else
     159             :     {
     160           2 :         for (int i = 0; i < nLevelCount; ++i)
     161             :         {
     162           1 :             const int nLevel = panLevels[i];
     163           1 :             int nIdx = -1;
     164           1 :             for (int j = 0; j < nOvCount; j++)
     165             :             {
     166           1 :                 GDALRasterBand *poOverview = poBand->GetOverview(j);
     167           1 :                 if (poOverview == nullptr)
     168           0 :                     continue;
     169             : 
     170           1 :                 int nOvFactor = GDALComputeOvFactor(
     171             :                     poOverview->GetXSize(), poBand->GetXSize(),
     172             :                     poOverview->GetYSize(), poBand->GetYSize());
     173             : 
     174           1 :                 if (nOvFactor == nLevel ||
     175           0 :                     nOvFactor == GDALOvLevelAdjust2(nLevel, poBand->GetXSize(),
     176             :                                                     poBand->GetYSize()))
     177             :                 {
     178           1 :                     nIdx = j;
     179           1 :                     break;
     180             :                 }
     181             :             }
     182           1 :             if (nIdx < 0)
     183             :             {
     184           0 :                 CPLError(
     185             :                     CE_Failure, CPLE_AppDefined,
     186             :                     "Cannot find overview level with subsampling factor of %d",
     187             :                     nLevel);
     188           0 :                 return false;
     189             :             }
     190           1 :             anOvrIndices.push_back(nIdx);
     191             :         }
     192             :     }
     193           4 :     return true;
     194             : }
     195             : 
     196             : /************************************************************************/
     197             : /*                   PartialRefreshFromSourceTimestamp()                */
     198             : /************************************************************************/
     199             : 
     200           2 : static bool PartialRefreshFromSourceTimestamp(
     201             :     GDALDataset *poDS, const char *pszResampling, int nLevelCount,
     202             :     const int *panLevels, int nBandCount, const int *panBandList,
     203             :     bool bMinSizeSpecified, int nMinSize, GDALProgressFunc pfnProgress,
     204             :     void *pProgressArg)
     205             : {
     206           4 :     std::vector<int> anOvrIndices;
     207           2 :     if (!GetOvrIndices(poDS, nLevelCount, panLevels, bMinSizeSpecified,
     208             :                        nMinSize, anOvrIndices))
     209           0 :         return false;
     210             : 
     211             :     VSIStatBufL sStatOvr;
     212           6 :     std::string osVRTOvr(std::string(poDS->GetDescription()) + ".ovr");
     213           2 :     if (VSIStatL(osVRTOvr.c_str(), &sStatOvr) != 0)
     214             :     {
     215           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s\n",
     216             :                  osVRTOvr.c_str());
     217           0 :         return false;
     218             :     }
     219           2 :     if (sStatOvr.st_mtime == 0)
     220             :     {
     221           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     222             :                  "Cannot get modification time of %s\n", osVRTOvr.c_str());
     223           0 :         return false;
     224             :     }
     225             : 
     226           4 :     std::vector<GTISourceDesc> regions;
     227             : 
     228             :     // Smallest positive double to avoid Coverity Scan complaining about divide_by_zero
     229           2 :     double dfTotalPixels = std::numeric_limits<double>::min();
     230             : 
     231           2 :     if (dynamic_cast<VRTDataset *>(poDS))
     232             :     {
     233             :         auto poVRTBand =
     234           1 :             dynamic_cast<VRTSourcedRasterBand *>(poDS->GetRasterBand(1));
     235           1 :         if (!poVRTBand)
     236             :         {
     237           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     238             :                      "Band is not a VRTSourcedRasterBand");
     239           0 :             return false;
     240             :         }
     241             : 
     242           3 :         for (int i = 0; i < poVRTBand->nSources; ++i)
     243             :         {
     244             :             auto poSource =
     245           2 :                 dynamic_cast<VRTSimpleSource *>(poVRTBand->papoSources[i]);
     246           2 :             if (poSource)
     247             :             {
     248             :                 VSIStatBufL sStatSource;
     249           2 :                 if (VSIStatL(poSource->GetSourceDatasetName().c_str(),
     250           2 :                              &sStatSource) == 0)
     251             :                 {
     252           2 :                     if (sStatSource.st_mtime > sStatOvr.st_mtime)
     253             :                     {
     254             :                         double dfXOff, dfYOff, dfXSize, dfYSize;
     255           1 :                         poSource->GetDstWindow(dfXOff, dfYOff, dfXSize,
     256             :                                                dfYSize);
     257           1 :                         constexpr double EPS = 1e-8;
     258           1 :                         int nXOff = static_cast<int>(dfXOff + EPS);
     259           1 :                         int nYOff = static_cast<int>(dfYOff + EPS);
     260           1 :                         int nXSize = static_cast<int>(dfXSize + 0.5);
     261           1 :                         int nYSize = static_cast<int>(dfYSize + 0.5);
     262           1 :                         if (nXOff > poDS->GetRasterXSize() ||
     263           1 :                             nYOff > poDS->GetRasterYSize() || nXSize <= 0 ||
     264             :                             nYSize <= 0)
     265             :                         {
     266           0 :                             continue;
     267             :                         }
     268           1 :                         if (nXOff < 0)
     269             :                         {
     270           0 :                             nXSize += nXOff;
     271           0 :                             nXOff = 0;
     272             :                         }
     273           1 :                         if (nXOff > poDS->GetRasterXSize() - nXSize)
     274             :                         {
     275           0 :                             nXSize = poDS->GetRasterXSize() - nXOff;
     276             :                         }
     277           1 :                         if (nYOff < 0)
     278             :                         {
     279           0 :                             nYSize += nYOff;
     280           0 :                             nYOff = 0;
     281             :                         }
     282           1 :                         if (nYOff > poDS->GetRasterYSize() - nYSize)
     283             :                         {
     284           0 :                             nYSize = poDS->GetRasterYSize() - nYOff;
     285             :                         }
     286             : 
     287           1 :                         dfTotalPixels += static_cast<double>(nXSize) * nYSize;
     288           2 :                         GTISourceDesc region;
     289           1 :                         region.osFilename = poSource->GetSourceDatasetName();
     290           1 :                         region.nDstXOff = nXOff;
     291           1 :                         region.nDstYOff = nYOff;
     292           1 :                         region.nDstXSize = nXSize;
     293           1 :                         region.nDstYSize = nYSize;
     294           1 :                         regions.push_back(std::move(region));
     295             :                     }
     296             :                 }
     297             :             }
     298             :         }
     299             :     }
     300             : #ifdef GTI_DRIVER_DISABLED_OR_PLUGIN
     301             :     else if (poDS->GetDriver() &&
     302             :              EQUAL(poDS->GetDriver()->GetDescription(), "GTI"))
     303             :     {
     304             :         CPLError(CE_Failure, CPLE_NotSupported,
     305             :                  "--partial-refresh-from-source-timestamp only works on a GTI "
     306             :                  "dataset if the GTI driver is not built as a plugin, "
     307             :                  "but in core library");
     308             :         return false;
     309             :     }
     310             : #else
     311           1 :     else if (auto poGTIDS = GDALDatasetCastToGTIDataset(poDS))
     312             :     {
     313           1 :         regions = GTIGetSourcesMoreRecentThan(poGTIDS, sStatOvr.st_mtime);
     314           2 :         for (const auto &region : regions)
     315             :         {
     316           1 :             dfTotalPixels +=
     317           1 :                 static_cast<double>(region.nDstXSize) * region.nDstYSize;
     318             :         }
     319             :     }
     320             : #endif
     321             :     else
     322             :     {
     323           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     324             :                  "--partial-refresh-from-source-timestamp only works on a VRT "
     325             :                  "or GTI dataset");
     326           0 :         return false;
     327             :     }
     328             : 
     329           2 :     if (!regions.empty())
     330             :     {
     331           2 :         double dfCurPixels = 0;
     332           4 :         for (const auto &region : regions)
     333             :         {
     334           2 :             if (pfnProgress == GDALDummyProgress)
     335             :             {
     336           0 :                 CPLDebug("GDAL", "Refresh from source %s",
     337             :                          region.osFilename.c_str());
     338             :             }
     339             :             else
     340             :             {
     341           2 :                 printf("Refresh from source %s.\n", region.osFilename.c_str());
     342             :             }
     343           2 :             const double dfNextCurPixels =
     344             :                 dfCurPixels +
     345           2 :                 static_cast<double>(region.nDstXSize) * region.nDstYSize;
     346           2 :             void *pScaledProgress = GDALCreateScaledProgress(
     347             :                 dfCurPixels / dfTotalPixels, dfNextCurPixels / dfTotalPixels,
     348             :                 pfnProgress, pProgressArg);
     349           4 :             bool bRet = PartialRefresh(
     350             :                 poDS, anOvrIndices, nBandCount, panBandList, pszResampling,
     351           2 :                 region.nDstXOff, region.nDstYOff, region.nDstXSize,
     352           2 :                 region.nDstYSize, GDALScaledProgress, pScaledProgress);
     353           2 :             GDALDestroyScaledProgress(pScaledProgress);
     354           2 :             if (!bRet)
     355           0 :                 return false;
     356           2 :             dfCurPixels = dfNextCurPixels;
     357             :         }
     358             :     }
     359             :     else
     360             :     {
     361           0 :         if (pfnProgress == GDALDummyProgress)
     362             :         {
     363           0 :             CPLDebug("GDAL", "No source is more recent than the overviews");
     364             :         }
     365             :         else
     366             :         {
     367           0 :             printf("No source is more recent than the overviews.\n");
     368             :         }
     369             :     }
     370             : 
     371           2 :     return true;
     372             : }
     373             : 
     374             : /************************************************************************/
     375             : /*                   PartialRefreshFromSourceExtent()                   */
     376             : /************************************************************************/
     377             : 
     378           1 : static bool PartialRefreshFromSourceExtent(
     379             :     GDALDataset *poDS, const CPLStringList &aosSources,
     380             :     const char *pszResampling, int nLevelCount, const int *panLevels,
     381             :     int nBandCount, const int *panBandList, bool bMinSizeSpecified,
     382             :     int nMinSize, GDALProgressFunc pfnProgress, void *pProgressArg)
     383             : {
     384           2 :     std::vector<int> anOvrIndices;
     385           1 :     if (!GetOvrIndices(poDS, nLevelCount, panLevels, bMinSizeSpecified,
     386             :                        nMinSize, anOvrIndices))
     387           0 :         return false;
     388             : 
     389             :     double adfGeoTransform[6];
     390           1 :     if (poDS->GetGeoTransform(adfGeoTransform) != CE_None)
     391             :     {
     392           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Dataset has no geotransform");
     393           0 :         return false;
     394             :     }
     395             :     double adfInvGT[6];
     396           1 :     if (!GDALInvGeoTransform(adfGeoTransform, adfInvGT))
     397             :     {
     398           0 :         return false;
     399             :     }
     400             : 
     401             :     struct Region
     402             :     {
     403             :         std::string osFileName;
     404             :         int nXOff;
     405             :         int nYOff;
     406             :         int nXSize;
     407             :         int nYSize;
     408             :     };
     409             : 
     410           2 :     std::vector<Region> regions;
     411             : 
     412             :     // Smallest positive double to avoid Coverity Scan complaining about divide_by_zero
     413           1 :     double dfTotalPixels = std::numeric_limits<double>::min();
     414           2 :     for (int i = 0; i < aosSources.size(); ++i)
     415             :     {
     416             :         auto poSrcDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
     417           1 :             aosSources[i], GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
     418           1 :         if (!poSrcDS)
     419           0 :             return false;
     420             : 
     421             :         double adfSrcGT[6];
     422           1 :         if (poSrcDS->GetGeoTransform(adfSrcGT) != CE_None)
     423             :         {
     424           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     425             :                      "Source dataset has no geotransform");
     426           0 :             return false;
     427             :         }
     428             : 
     429           1 :         const double dfULX = adfSrcGT[0];
     430           1 :         const double dfULY = adfSrcGT[3];
     431           2 :         const double dfLRX = adfSrcGT[0] +
     432           1 :                              poSrcDS->GetRasterXSize() * adfSrcGT[1] +
     433           1 :                              poSrcDS->GetRasterYSize() * adfSrcGT[2];
     434           2 :         const double dfLRY = adfSrcGT[3] +
     435           1 :                              poSrcDS->GetRasterXSize() * adfSrcGT[4] +
     436           1 :                              poSrcDS->GetRasterYSize() * adfSrcGT[5];
     437           1 :         const double dfX1 =
     438           1 :             adfInvGT[0] + adfInvGT[1] * dfULX + adfInvGT[2] * dfULY;
     439           1 :         const double dfY1 =
     440           1 :             adfInvGT[3] + adfInvGT[4] * dfULX + adfInvGT[5] * dfULY;
     441           1 :         const double dfX2 =
     442           1 :             adfInvGT[0] + adfInvGT[1] * dfLRX + adfInvGT[2] * dfLRY;
     443           1 :         const double dfY2 =
     444           1 :             adfInvGT[3] + adfInvGT[4] * dfLRX + adfInvGT[5] * dfLRY;
     445           1 :         constexpr double EPS = 1e-8;
     446             :         const int nXOff =
     447           1 :             static_cast<int>(std::max(0.0, std::min(dfX1, dfX2)) + EPS);
     448             :         const int nYOff =
     449           1 :             static_cast<int>(std::max(0.0, std::min(dfY1, dfY2)) + EPS);
     450             :         const int nXSize =
     451           1 :             static_cast<int>(
     452           1 :                 std::ceil(std::min(static_cast<double>(poDS->GetRasterXSize()),
     453           2 :                                    std::max(dfX1, dfX2)) -
     454             :                           EPS)) -
     455           1 :             nXOff;
     456             :         const int nYSize =
     457           1 :             static_cast<int>(
     458           1 :                 std::ceil(std::min(static_cast<double>(poDS->GetRasterYSize()),
     459           2 :                                    std::max(dfY1, dfY2)) -
     460             :                           EPS)) -
     461           1 :             nYOff;
     462             : 
     463           1 :         dfTotalPixels += static_cast<double>(nXSize) * nYSize;
     464           2 :         Region region;
     465           1 :         region.osFileName = aosSources[i];
     466           1 :         region.nXOff = nXOff;
     467           1 :         region.nYOff = nYOff;
     468           1 :         region.nXSize = nXSize;
     469           1 :         region.nYSize = nYSize;
     470           1 :         regions.push_back(std::move(region));
     471             :     }
     472             : 
     473           1 :     double dfCurPixels = 0;
     474           2 :     for (const auto &region : regions)
     475             :     {
     476           1 :         if (pfnProgress == GDALDummyProgress)
     477             :         {
     478           0 :             CPLDebug("GDAL", "Refresh from source %s",
     479             :                      region.osFileName.c_str());
     480             :         }
     481             :         else
     482             :         {
     483           1 :             printf("Refresh from source %s.\n", region.osFileName.c_str());
     484             :         }
     485           1 :         const double dfNextCurPixels =
     486           1 :             dfCurPixels + static_cast<double>(region.nXSize) * region.nYSize;
     487           1 :         void *pScaledProgress = GDALCreateScaledProgress(
     488             :             dfCurPixels / dfTotalPixels, dfNextCurPixels / dfTotalPixels,
     489             :             pfnProgress, pProgressArg);
     490           2 :         bool bRet = PartialRefresh(poDS, anOvrIndices, nBandCount, panBandList,
     491           1 :                                    pszResampling, region.nXOff, region.nYOff,
     492           1 :                                    region.nXSize, region.nYSize,
     493             :                                    GDALScaledProgress, pScaledProgress);
     494           1 :         GDALDestroyScaledProgress(pScaledProgress);
     495           1 :         if (!bRet)
     496           0 :             return false;
     497           1 :         dfCurPixels = dfNextCurPixels;
     498             :     }
     499             : 
     500           1 :     return true;
     501             : }
     502             : 
     503             : /************************************************************************/
     504             : /*                     PartialRefreshFromProjWin()                      */
     505             : /************************************************************************/
     506             : 
     507           1 : static bool PartialRefreshFromProjWin(
     508             :     GDALDataset *poDS, double dfULX, double dfULY, double dfLRX, double dfLRY,
     509             :     const char *pszResampling, int nLevelCount, const int *panLevels,
     510             :     int nBandCount, const int *panBandList, bool bMinSizeSpecified,
     511             :     int nMinSize, GDALProgressFunc pfnProgress, void *pProgressArg)
     512             : {
     513           2 :     std::vector<int> anOvrIndices;
     514           1 :     if (!GetOvrIndices(poDS, nLevelCount, panLevels, bMinSizeSpecified,
     515             :                        nMinSize, anOvrIndices))
     516           0 :         return false;
     517             : 
     518             :     double adfGeoTransform[6];
     519           1 :     if (poDS->GetGeoTransform(adfGeoTransform) != CE_None)
     520             :     {
     521           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Dataset has no geotransform");
     522           0 :         return false;
     523             :     }
     524             :     double adfInvGT[6];
     525           1 :     if (!GDALInvGeoTransform(adfGeoTransform, adfInvGT))
     526             :     {
     527           0 :         return false;
     528             :     }
     529           1 :     const double dfX1 = adfInvGT[0] + adfInvGT[1] * dfULX + adfInvGT[2] * dfULY;
     530           1 :     const double dfY1 = adfInvGT[3] + adfInvGT[4] * dfULX + adfInvGT[5] * dfULY;
     531           1 :     const double dfX2 = adfInvGT[0] + adfInvGT[1] * dfLRX + adfInvGT[2] * dfLRY;
     532           1 :     const double dfY2 = adfInvGT[3] + adfInvGT[4] * dfLRX + adfInvGT[5] * dfLRY;
     533           1 :     constexpr double EPS = 1e-8;
     534             :     const int nXOff =
     535           1 :         static_cast<int>(std::max(0.0, std::min(dfX1, dfX2)) + EPS);
     536             :     const int nYOff =
     537           1 :         static_cast<int>(std::max(0.0, std::min(dfY1, dfY2)) + EPS);
     538           1 :     const int nXSize = static_cast<int>(std::ceil(
     539           1 :                            std::min(static_cast<double>(poDS->GetRasterXSize()),
     540           2 :                                     std::max(dfX1, dfX2)) -
     541             :                            EPS)) -
     542           1 :                        nXOff;
     543           1 :     const int nYSize = static_cast<int>(std::ceil(
     544           1 :                            std::min(static_cast<double>(poDS->GetRasterYSize()),
     545           2 :                                     std::max(dfY1, dfY2)) -
     546             :                            EPS)) -
     547           1 :                        nYOff;
     548           1 :     return PartialRefresh(poDS, anOvrIndices, nBandCount, panBandList,
     549             :                           pszResampling, nXOff, nYOff, nXSize, nYSize,
     550           1 :                           pfnProgress, pProgressArg);
     551             : }
     552             : 
     553             : /************************************************************************/
     554             : /*                                main()                                */
     555             : /************************************************************************/
     556             : 
     557          30 : MAIN_START(nArgc, papszArgv)
     558             : 
     559             : {
     560          30 :     EarlySetConfigOptions(nArgc, papszArgv);
     561          30 :     GDALAllRegister();
     562             : 
     563          30 :     nArgc = GDALGeneralCmdLineProcessor(nArgc, &papszArgv, 0);
     564          30 :     if (nArgc < 1)
     565           2 :         exit(-nArgc);
     566          52 :     CPLStringList aosArgv;
     567          28 :     aosArgv.Assign(papszArgv, /* bAssign = */ true);
     568             : 
     569          80 :     GDALArgumentParser argParser(aosArgv[0], /* bForBinary=*/true);
     570             : 
     571          28 :     argParser.add_description(_("Builds or rebuilds overview images."));
     572             : 
     573          28 :     const char *pszEpilog = _(
     574             :         "Useful configuration variables :\n"
     575             :         "  --config USE_RRD YES : Use Erdas Imagine format (.aux) as overview "
     576             :         "format.\n"
     577             :         "Below, only for external overviews in GeoTIFF format:\n"
     578             :         "  --config COMPRESS_OVERVIEW {JPEG,LZW,PACKBITS,DEFLATE} : TIFF "
     579             :         "compression\n"
     580             :         "  --config PHOTOMETRIC_OVERVIEW {RGB,YCBCR,...} : TIFF photometric "
     581             :         "interp.\n"
     582             :         "  --config INTERLEAVE_OVERVIEW {PIXEL|BAND} : TIFF interleaving "
     583             :         "method\n"
     584             :         "  --config BIGTIFF_OVERVIEW {IF_NEEDED|IF_SAFER|YES|NO} : is BigTIFF "
     585             :         "used\n"
     586             :         "\n"
     587             :         "Examples:\n"
     588             :         " %% gdaladdo -r average abc.tif\n"
     589             :         " %% gdaladdo --config COMPRESS_OVERVIEW JPEG\n"
     590             :         "            --config PHOTOMETRIC_OVERVIEW YCBCR\n"
     591             :         "            --config INTERLEAVE_OVERVIEW PIXEL -ro abc.tif\n"
     592             :         "\n"
     593             :         "For more details, consult https://gdal.org/programs/gdaladdo.html");
     594          28 :     argParser.add_epilog(pszEpilog);
     595             : 
     596          52 :     std::string osResampling;
     597          28 :     argParser.add_argument("-r")
     598          28 :         .store_into(osResampling)
     599             :         .metavar("nearest|average|rms|gauss|bilinear|cubic|cubicspline|lanczos|"
     600          56 :                  "average_magphase|mode")
     601          28 :         .help(_("Select a resampling algorithm."));
     602             : 
     603          28 :     bool bReadOnly = false;
     604          28 :     argParser.add_argument("-ro").store_into(bReadOnly).help(
     605             :         _("Open the dataset in read-only mode, in order to generate external "
     606          28 :           "overview."));
     607             : 
     608          28 :     bool bQuiet = false;
     609          28 :     argParser.add_quiet_argument(&bQuiet);
     610             : 
     611          52 :     std::vector<int> anBandList;
     612          28 :     argParser.add_argument("-b")
     613          28 :         .append()
     614          56 :         .metavar("<band>")
     615             :         .action(
     616           0 :             [&anBandList](const std::string &s)
     617             :             {
     618           0 :                 const int nBand = atoi(s.c_str());
     619           0 :                 if (nBand < 1)
     620             :                 {
     621             :                     throw std::invalid_argument(CPLSPrintf(
     622           0 :                         "Unrecognizable band number (%s).", s.c_str()));
     623             :                 }
     624           0 :                 anBandList.push_back(nBand);
     625          28 :             })
     626          28 :         .help(_("Select input band(s) for overview generation."));
     627             : 
     628          52 :     CPLStringList aosOpenOptions;
     629          28 :     argParser.add_argument("-oo")
     630          28 :         .append()
     631          56 :         .metavar("<NAME=VALUE>")
     632           1 :         .action([&aosOpenOptions](const std::string &s)
     633          29 :                 { aosOpenOptions.AddString(s.c_str()); })
     634          28 :         .help(_("Dataset open option (format-specific)."));
     635             : 
     636          28 :     int nMinSize = 256;
     637          28 :     argParser.add_argument("-minsize")
     638          28 :         .default_value(nMinSize)
     639          56 :         .metavar("<val>")
     640          28 :         .store_into(nMinSize)
     641          28 :         .help(_("Maximum width or height of the smallest overview level."));
     642             : 
     643          28 :     bool bClean = false;
     644          28 :     bool bPartialRefreshFromSourceTimestamp = false;
     645          52 :     std::string osPartialRefreshFromSourceExtent;
     646             : 
     647             :     {
     648          28 :         auto &group = argParser.add_mutually_exclusive_group();
     649          28 :         group.add_argument("-clean").store_into(bClean).help(
     650          28 :             _("Remove all overviews."));
     651             : 
     652          28 :         group.add_argument("--partial-refresh-from-source-timestamp")
     653          28 :             .store_into(bPartialRefreshFromSourceTimestamp)
     654             :             .help(_("Performs a partial refresh of existing overviews, when "
     655          28 :                     "<filename> is a VRT file with an external overview."));
     656             : 
     657          28 :         group.add_argument("--partial-refresh-from-projwin")
     658          56 :             .metavar("<ulx> <uly> <lrx> <lry>")
     659          28 :             .nargs(4)
     660          28 :             .scan<'g', double>()
     661             :             .help(
     662             :                 _("Performs a partial refresh of existing overviews, in the "
     663          28 :                   "region of interest specified by georeference coordinates."));
     664             : 
     665          28 :         group.add_argument("--partial-refresh-from-source-extent")
     666          56 :             .metavar("<filename1>[,<filenameN>]...")
     667          28 :             .store_into(osPartialRefreshFromSourceExtent)
     668             :             .help(
     669             :                 _("Performs a partial refresh of existing overviews, in the "
     670          28 :                   "region of interest specified by one or several filename."));
     671             :     }
     672             : 
     673          52 :     std::string osFilename;
     674          28 :     argParser.add_argument("filename")
     675          28 :         .store_into(osFilename)
     676             :         .help(_("The file to build overviews for (or whose overviews must be "
     677          28 :                 "removed)."));
     678             : 
     679          56 :     argParser.add_argument("level").remaining().metavar("<level>").help(
     680          28 :         _("A list of integral overview levels to build."));
     681             : 
     682             :     try
     683             :     {
     684          28 :         argParser.parse_args(aosArgv);
     685             :     }
     686           0 :     catch (const std::exception &err)
     687             :     {
     688           0 :         argParser.display_error_and_usage(err);
     689           0 :         std::exit(1);
     690             :     }
     691             : 
     692          52 :     std::vector<int> anLevels;
     693          52 :     auto levels = argParser.present<std::vector<std::string>>("level");
     694          28 :     if (levels)
     695             :     {
     696          29 :         for (const auto &level : *levels)
     697             :         {
     698          18 :             if (CPLGetValueType(level.c_str()) != CPL_VALUE_INTEGER)
     699             :             {
     700           1 :                 CPLError(
     701             :                     CE_Failure, CPLE_IllegalArg,
     702             :                     "Value '%s' is not a positive integer subsampling factor",
     703             :                     level.c_str());
     704           1 :                 std::exit(1);
     705             :             }
     706          17 :             anLevels.push_back(atoi(level.c_str()));
     707          17 :             if (anLevels.back() <= 0)
     708             :             {
     709           2 :                 CPLError(
     710             :                     CE_Failure, CPLE_IllegalArg,
     711             :                     "Value '%s' is not a positive integer subsampling factor",
     712             :                     level.c_str());
     713           2 :                 std::exit(1);
     714             :             }
     715          15 :             if (anLevels.back() == 1)
     716             :             {
     717           0 :                 printf(
     718             :                     "Warning: Overview with subsampling factor of 1 requested. "
     719             :                     "This will copy the full resolution dataset in the "
     720             :                     "overview!\n");
     721             :             }
     722             :         }
     723             :     }
     724             : 
     725          25 :     GDALProgressFunc pfnProgress =
     726          25 :         bQuiet ? GDALDummyProgress : GDALTermProgress;
     727          25 :     const bool bMinSizeSpecified = argParser.is_used("-minsize");
     728             : 
     729          25 :     CPLStringList aosSources;
     730          25 :     if (!osPartialRefreshFromSourceExtent.empty())
     731             :     {
     732             :         aosSources = CSLTokenizeString2(
     733           1 :             osPartialRefreshFromSourceExtent.c_str(), ",", 0);
     734             :     }
     735             : 
     736          25 :     bool bPartialRefreshFromProjWin = false;
     737          25 :     double dfULX = 0;
     738          25 :     double dfULY = 0;
     739          25 :     double dfLRX = 0;
     740          25 :     double dfLRY = 0;
     741          25 :     if (auto oProjWin = argParser.present<std::vector<double>>(
     742          50 :             "--partial-refresh-from-projwin"))
     743             :     {
     744           1 :         bPartialRefreshFromProjWin = true;
     745           1 :         dfULX = (*oProjWin)[0];
     746           1 :         dfULY = (*oProjWin)[1];
     747           1 :         dfLRX = (*oProjWin)[2];
     748           1 :         dfLRY = (*oProjWin)[3];
     749             :     }
     750             : 
     751             :     /* -------------------------------------------------------------------- */
     752             :     /*      Open data file.                                                 */
     753             :     /* -------------------------------------------------------------------- */
     754          25 :     GDALDatasetH hDataset = nullptr;
     755          25 :     if (!bReadOnly)
     756             :     {
     757          20 :         CPLPushErrorHandler(GDALAddoErrorHandler);
     758          22 :         if (bClean &&
     759           2 :             aosOpenOptions.FetchNameValue("IGNORE_COG_LAYOUT_BREAK") == nullptr)
     760             :         {
     761           2 :             GDALDriverH hDrv = GDALIdentifyDriver(osFilename.c_str(), nullptr);
     762           2 :             if (hDrv && EQUAL(GDALGetDescription(hDrv), "GTiff"))
     763             :             {
     764             :                 // Cleaning does not break COG layout
     765           2 :                 aosOpenOptions.SetNameValue("IGNORE_COG_LAYOUT_BREAK", "YES");
     766             :             }
     767             :         }
     768             : 
     769          20 :         CPLSetCurrentErrorHandlerCatchDebug(FALSE);
     770             :         hDataset =
     771          20 :             GDALOpenEx(osFilename.c_str(), GDAL_OF_RASTER | GDAL_OF_UPDATE,
     772          20 :                        nullptr, aosOpenOptions.List(), nullptr);
     773          20 :         CPLPopErrorHandler();
     774             :         const bool bIsCOG =
     775          21 :             (aoErrors.size() == 1 &&
     776           1 :              aoErrors[0].m_osMsg.find("C(loud) O(ptimized) G(eoTIFF) layout") !=
     777          21 :                  std::string::npos &&
     778           1 :              aosOpenOptions.FetchNameValue("IGNORE_COG_LAYOUT_BREAK") ==
     779          20 :                  nullptr);
     780          20 :         if (hDataset != nullptr || bIsCOG)
     781             :         {
     782          21 :             for (size_t i = 0; i < aoErrors.size(); i++)
     783             :             {
     784           1 :                 CPLError(aoErrors[i].m_eErr, aoErrors[i].m_errNum, "%s",
     785           1 :                          aoErrors[i].m_osMsg.c_str());
     786             :             }
     787          20 :             if (bIsCOG)
     788           1 :                 exit(1);
     789             :         }
     790             :     }
     791             : 
     792          24 :     if (hDataset == nullptr)
     793           5 :         hDataset = GDALOpenEx(osFilename.c_str(),
     794             :                               GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR, nullptr,
     795           5 :                               aosOpenOptions.List(), nullptr);
     796          24 :     if (hDataset == nullptr)
     797           0 :         exit(2);
     798             : 
     799          24 :     if (!bClean && osResampling.empty())
     800             :     {
     801           9 :         auto poDS = GDALDataset::FromHandle(hDataset);
     802           9 :         if (poDS->GetRasterCount() > 0)
     803             :         {
     804           9 :             auto poBand = poDS->GetRasterBand(1);
     805           9 :             if (poBand->GetOverviewCount() > 0)
     806             :             {
     807             :                 const char *pszResampling =
     808           2 :                     poBand->GetOverview(0)->GetMetadataItem("RESAMPLING");
     809           2 :                 if (pszResampling)
     810             :                 {
     811           2 :                     osResampling = pszResampling;
     812           2 :                     if (pfnProgress == GDALDummyProgress)
     813           0 :                         CPLDebug("GDAL",
     814             :                                  "Reusing resampling method %s from existing "
     815             :                                  "overview",
     816             :                                  pszResampling);
     817             :                     else
     818           2 :                         printf("Info: reusing resampling method %s from "
     819             :                                "existing overview.\n",
     820             :                                pszResampling);
     821             :                 }
     822             :             }
     823             :         }
     824           9 :         if (osResampling.empty())
     825           7 :             osResampling = "nearest";
     826             :     }
     827             : 
     828             :     /* -------------------------------------------------------------------- */
     829             :     /*      Clean overviews.                                                */
     830             :     /* -------------------------------------------------------------------- */
     831          24 :     int nResultStatus = 0;
     832          24 :     void *pProgressArg = nullptr;
     833          24 :     const int nBandCount = static_cast<int>(anBandList.size());
     834          24 :     if (bClean)
     835             :     {
     836           2 :         if (GDALBuildOverviews(hDataset, "NONE", 0, nullptr, 0, nullptr,
     837           2 :                                pfnProgress, pProgressArg) != CE_None)
     838             :         {
     839           0 :             fprintf(stderr, "Cleaning overviews failed.\n");
     840           0 :             nResultStatus = 200;
     841             :         }
     842             :     }
     843          22 :     else if (bPartialRefreshFromSourceTimestamp)
     844             :     {
     845           2 :         if (!PartialRefreshFromSourceTimestamp(
     846             :                 GDALDataset::FromHandle(hDataset), osResampling.c_str(),
     847           2 :                 static_cast<int>(anLevels.size()), anLevels.data(), nBandCount,
     848           2 :                 anBandList.data(), bMinSizeSpecified, nMinSize, pfnProgress,
     849             :                 pProgressArg))
     850             :         {
     851           0 :             nResultStatus = 1;
     852             :         }
     853             :     }
     854          20 :     else if (bPartialRefreshFromProjWin)
     855             :     {
     856           1 :         if (!PartialRefreshFromProjWin(
     857             :                 GDALDataset::FromHandle(hDataset), dfULX, dfULY, dfLRX, dfLRY,
     858           1 :                 osResampling.c_str(), static_cast<int>(anLevels.size()),
     859           1 :                 anLevels.data(), nBandCount, anBandList.data(),
     860             :                 bMinSizeSpecified, nMinSize, pfnProgress, pProgressArg))
     861             :         {
     862           0 :             nResultStatus = 1;
     863             :         }
     864             :     }
     865          19 :     else if (!aosSources.empty())
     866             :     {
     867           1 :         if (!PartialRefreshFromSourceExtent(
     868             :                 GDALDataset::FromHandle(hDataset), aosSources,
     869           1 :                 osResampling.c_str(), static_cast<int>(anLevels.size()),
     870           1 :                 anLevels.data(), nBandCount, anBandList.data(),
     871             :                 bMinSizeSpecified, nMinSize, pfnProgress, pProgressArg))
     872             :         {
     873           0 :             nResultStatus = 1;
     874             :         }
     875             :     }
     876             :     else
     877             :     {
     878             :         /* --------------------------------------------------------------------
     879             :          */
     880             :         /*      Generate overviews. */
     881             :         /* --------------------------------------------------------------------
     882             :          */
     883             : 
     884             :         // If no levels are specified, reuse the potentially existing ones.
     885          18 :         if (anLevels.empty())
     886             :         {
     887           8 :             auto poDS = GDALDataset::FromHandle(hDataset);
     888           8 :             if (poDS->GetRasterCount() > 0)
     889             :             {
     890           8 :                 auto poBand = poDS->GetRasterBand(1);
     891           8 :                 const int nExistingCount = poBand->GetOverviewCount();
     892           8 :                 if (nExistingCount > 0)
     893             :                 {
     894          12 :                     for (int iOvr = 0; iOvr < nExistingCount; ++iOvr)
     895             :                     {
     896           8 :                         auto poOverview = poBand->GetOverview(iOvr);
     897           8 :                         if (poOverview)
     898             :                         {
     899           8 :                             const int nOvFactor = GDALComputeOvFactor(
     900             :                                 poOverview->GetXSize(), poBand->GetXSize(),
     901           8 :                                 poOverview->GetYSize(), poBand->GetYSize());
     902           8 :                             anLevels.push_back(nOvFactor);
     903             :                         }
     904             :                     }
     905             :                 }
     906             :             }
     907             :         }
     908             : 
     909          18 :         if (anLevels.empty())
     910             :         {
     911           4 :             const int nXSize = GDALGetRasterXSize(hDataset);
     912           4 :             const int nYSize = GDALGetRasterYSize(hDataset);
     913           4 :             int nOvrFactor = 1;
     914          12 :             while (DIV_ROUND_UP(nXSize, nOvrFactor) > nMinSize ||
     915           4 :                    DIV_ROUND_UP(nYSize, nOvrFactor) > nMinSize)
     916             :             {
     917           8 :                 nOvrFactor *= 2;
     918           8 :                 anLevels.push_back(nOvrFactor);
     919             :             }
     920             :         }
     921             : 
     922             :         // Only HFA supports selected layers
     923          18 :         if (nBandCount > 0)
     924           0 :             CPLSetConfigOption("USE_RRD", "YES");
     925             : 
     926          35 :         if (!anLevels.empty() &&
     927          17 :             GDALBuildOverviews(hDataset, osResampling.c_str(),
     928          17 :                                static_cast<int>(anLevels.size()),
     929          17 :                                anLevels.data(), nBandCount, anBandList.data(),
     930             :                                pfnProgress, pProgressArg) != CE_None)
     931             :         {
     932           0 :             fprintf(stderr, "Overview building failed.\n");
     933           0 :             nResultStatus = 100;
     934             :         }
     935             :     }
     936             : 
     937             :     /* -------------------------------------------------------------------- */
     938             :     /*      Cleanup                                                         */
     939             :     /* -------------------------------------------------------------------- */
     940          24 :     if (GDALClose(hDataset) != CE_None)
     941             :     {
     942           0 :         if (nResultStatus == 0)
     943           0 :             nResultStatus = 1;
     944             :     }
     945             : 
     946          24 :     GDALDestroyDriverManager();
     947             : 
     948          24 :     return nResultStatus;
     949             : }
     950             : 
     951           0 : MAIN_END

Generated by: LCOV version 1.14