LCOV - code coverage report
Current view: top level - apps - gdaladdo.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 382 457 83.6 %
Date: 2025-09-10 17:48:50 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 (auto &poSource : poVRTBand->m_papoSources)
     243             :         {
     244             :             auto poSimpleSource =
     245           2 :                 dynamic_cast<VRTSimpleSource *>(poSource.get());
     246           2 :             if (poSimpleSource)
     247             :             {
     248             :                 VSIStatBufL sStatSource;
     249           2 :                 if (VSIStatL(poSimpleSource->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 :                         poSimpleSource->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             :                         region.osFilename =
     290           1 :                             poSimpleSource->GetSourceDatasetName();
     291           1 :                         region.nDstXOff = nXOff;
     292           1 :                         region.nDstYOff = nYOff;
     293           1 :                         region.nDstXSize = nXSize;
     294           1 :                         region.nDstYSize = nYSize;
     295           1 :                         regions.push_back(std::move(region));
     296             :                     }
     297             :                 }
     298             :             }
     299             :         }
     300             :     }
     301             : #ifdef GTI_DRIVER_DISABLED_OR_PLUGIN
     302             :     else if (poDS->GetDriver() &&
     303             :              EQUAL(poDS->GetDriver()->GetDescription(), "GTI"))
     304             :     {
     305             :         CPLError(CE_Failure, CPLE_NotSupported,
     306             :                  "--partial-refresh-from-source-timestamp only works on a GTI "
     307             :                  "dataset if the GTI driver is not built as a plugin, "
     308             :                  "but in core library");
     309             :         return false;
     310             :     }
     311             : #else
     312           1 :     else if (auto poGTIDS = GDALDatasetCastToGTIDataset(poDS))
     313             :     {
     314           1 :         regions = GTIGetSourcesMoreRecentThan(poGTIDS, sStatOvr.st_mtime);
     315           2 :         for (const auto &region : regions)
     316             :         {
     317           1 :             dfTotalPixels +=
     318           1 :                 static_cast<double>(region.nDstXSize) * region.nDstYSize;
     319             :         }
     320             :     }
     321             : #endif
     322             :     else
     323             :     {
     324           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     325             :                  "--partial-refresh-from-source-timestamp only works on a VRT "
     326             :                  "or GTI dataset");
     327           0 :         return false;
     328             :     }
     329             : 
     330           2 :     if (!regions.empty())
     331             :     {
     332           2 :         double dfCurPixels = 0;
     333           4 :         for (const auto &region : regions)
     334             :         {
     335           2 :             if (pfnProgress == GDALDummyProgress)
     336             :             {
     337           0 :                 CPLDebug("GDAL", "Refresh from source %s",
     338             :                          region.osFilename.c_str());
     339             :             }
     340             :             else
     341             :             {
     342           2 :                 printf("Refresh from source %s.\n", region.osFilename.c_str());
     343             :             }
     344           2 :             const double dfNextCurPixels =
     345             :                 dfCurPixels +
     346           2 :                 static_cast<double>(region.nDstXSize) * region.nDstYSize;
     347           2 :             void *pScaledProgress = GDALCreateScaledProgress(
     348             :                 dfCurPixels / dfTotalPixels, dfNextCurPixels / dfTotalPixels,
     349             :                 pfnProgress, pProgressArg);
     350           4 :             bool bRet = PartialRefresh(
     351             :                 poDS, anOvrIndices, nBandCount, panBandList, pszResampling,
     352           2 :                 region.nDstXOff, region.nDstYOff, region.nDstXSize,
     353           2 :                 region.nDstYSize, GDALScaledProgress, pScaledProgress);
     354           2 :             GDALDestroyScaledProgress(pScaledProgress);
     355           2 :             if (!bRet)
     356           0 :                 return false;
     357           2 :             dfCurPixels = dfNextCurPixels;
     358             :         }
     359             :     }
     360             :     else
     361             :     {
     362           0 :         if (pfnProgress == GDALDummyProgress)
     363             :         {
     364           0 :             CPLDebug("GDAL", "No source is more recent than the overviews");
     365             :         }
     366             :         else
     367             :         {
     368           0 :             printf("No source is more recent than the overviews.\n");
     369             :         }
     370             :     }
     371             : 
     372           2 :     return true;
     373             : }
     374             : 
     375             : /************************************************************************/
     376             : /*                   PartialRefreshFromSourceExtent()                   */
     377             : /************************************************************************/
     378             : 
     379           1 : static bool PartialRefreshFromSourceExtent(
     380             :     GDALDataset *poDS, const CPLStringList &aosSources,
     381             :     const char *pszResampling, int nLevelCount, const int *panLevels,
     382             :     int nBandCount, const int *panBandList, bool bMinSizeSpecified,
     383             :     int nMinSize, GDALProgressFunc pfnProgress, void *pProgressArg)
     384             : {
     385           2 :     std::vector<int> anOvrIndices;
     386           1 :     if (!GetOvrIndices(poDS, nLevelCount, panLevels, bMinSizeSpecified,
     387             :                        nMinSize, anOvrIndices))
     388           0 :         return false;
     389             : 
     390           1 :     GDALGeoTransform gt;
     391           1 :     if (poDS->GetGeoTransform(gt) != CE_None)
     392             :     {
     393           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Dataset has no geotransform");
     394           0 :         return false;
     395             :     }
     396           1 :     GDALGeoTransform invGT;
     397           1 :     if (!gt.GetInverse(invGT))
     398             :     {
     399           0 :         return false;
     400             :     }
     401             : 
     402             :     struct Region
     403             :     {
     404             :         std::string osFileName;
     405             :         int nXOff;
     406             :         int nYOff;
     407             :         int nXSize;
     408             :         int nYSize;
     409             :     };
     410             : 
     411           2 :     std::vector<Region> regions;
     412             : 
     413             :     // Smallest positive double to avoid Coverity Scan complaining about divide_by_zero
     414           1 :     double dfTotalPixels = std::numeric_limits<double>::min();
     415           2 :     for (int i = 0; i < aosSources.size(); ++i)
     416             :     {
     417             :         auto poSrcDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
     418           1 :             aosSources[i], GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
     419           1 :         if (!poSrcDS)
     420           0 :             return false;
     421             : 
     422           1 :         GDALGeoTransform srcGT;
     423           1 :         if (poSrcDS->GetGeoTransform(srcGT) != CE_None)
     424             :         {
     425           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     426             :                      "Source dataset has no geotransform");
     427           0 :             return false;
     428             :         }
     429             : 
     430           1 :         const double dfULX = srcGT[0];
     431           1 :         const double dfULY = srcGT[3];
     432           1 :         const double dfLRX = srcGT[0] + poSrcDS->GetRasterXSize() * srcGT[1] +
     433           1 :                              poSrcDS->GetRasterYSize() * srcGT[2];
     434           1 :         const double dfLRY = srcGT[3] + poSrcDS->GetRasterXSize() * srcGT[4] +
     435           1 :                              poSrcDS->GetRasterYSize() * srcGT[5];
     436           1 :         const double dfX1 = invGT[0] + invGT[1] * dfULX + invGT[2] * dfULY;
     437           1 :         const double dfY1 = invGT[3] + invGT[4] * dfULX + invGT[5] * dfULY;
     438           1 :         const double dfX2 = invGT[0] + invGT[1] * dfLRX + invGT[2] * dfLRY;
     439           1 :         const double dfY2 = invGT[3] + invGT[4] * dfLRX + invGT[5] * dfLRY;
     440           1 :         constexpr double EPS = 1e-8;
     441             :         const int nXOff =
     442           1 :             static_cast<int>(std::max(0.0, std::min(dfX1, dfX2)) + EPS);
     443             :         const int nYOff =
     444           1 :             static_cast<int>(std::max(0.0, std::min(dfY1, dfY2)) + EPS);
     445             :         const int nXSize =
     446           1 :             static_cast<int>(
     447           1 :                 std::ceil(std::min(static_cast<double>(poDS->GetRasterXSize()),
     448           2 :                                    std::max(dfX1, dfX2)) -
     449             :                           EPS)) -
     450           1 :             nXOff;
     451             :         const int nYSize =
     452           1 :             static_cast<int>(
     453           1 :                 std::ceil(std::min(static_cast<double>(poDS->GetRasterYSize()),
     454           2 :                                    std::max(dfY1, dfY2)) -
     455             :                           EPS)) -
     456           1 :             nYOff;
     457             : 
     458           1 :         dfTotalPixels += static_cast<double>(nXSize) * nYSize;
     459           2 :         Region region;
     460           1 :         region.osFileName = aosSources[i];
     461           1 :         region.nXOff = nXOff;
     462           1 :         region.nYOff = nYOff;
     463           1 :         region.nXSize = nXSize;
     464           1 :         region.nYSize = nYSize;
     465           1 :         regions.push_back(std::move(region));
     466             :     }
     467             : 
     468           1 :     double dfCurPixels = 0;
     469           2 :     for (const auto &region : regions)
     470             :     {
     471           1 :         if (pfnProgress == GDALDummyProgress)
     472             :         {
     473           0 :             CPLDebug("GDAL", "Refresh from source %s",
     474             :                      region.osFileName.c_str());
     475             :         }
     476             :         else
     477             :         {
     478           1 :             printf("Refresh from source %s.\n", region.osFileName.c_str());
     479             :         }
     480           1 :         const double dfNextCurPixels =
     481           1 :             dfCurPixels + static_cast<double>(region.nXSize) * region.nYSize;
     482           1 :         void *pScaledProgress = GDALCreateScaledProgress(
     483             :             dfCurPixels / dfTotalPixels, dfNextCurPixels / dfTotalPixels,
     484             :             pfnProgress, pProgressArg);
     485           2 :         bool bRet = PartialRefresh(poDS, anOvrIndices, nBandCount, panBandList,
     486           1 :                                    pszResampling, region.nXOff, region.nYOff,
     487           1 :                                    region.nXSize, region.nYSize,
     488             :                                    GDALScaledProgress, pScaledProgress);
     489           1 :         GDALDestroyScaledProgress(pScaledProgress);
     490           1 :         if (!bRet)
     491           0 :             return false;
     492           1 :         dfCurPixels = dfNextCurPixels;
     493             :     }
     494             : 
     495           1 :     return true;
     496             : }
     497             : 
     498             : /************************************************************************/
     499             : /*                     PartialRefreshFromProjWin()                      */
     500             : /************************************************************************/
     501             : 
     502           1 : static bool PartialRefreshFromProjWin(
     503             :     GDALDataset *poDS, double dfULX, double dfULY, double dfLRX, double dfLRY,
     504             :     const char *pszResampling, int nLevelCount, const int *panLevels,
     505             :     int nBandCount, const int *panBandList, bool bMinSizeSpecified,
     506             :     int nMinSize, GDALProgressFunc pfnProgress, void *pProgressArg)
     507             : {
     508           2 :     std::vector<int> anOvrIndices;
     509           1 :     if (!GetOvrIndices(poDS, nLevelCount, panLevels, bMinSizeSpecified,
     510             :                        nMinSize, anOvrIndices))
     511           0 :         return false;
     512             : 
     513           1 :     GDALGeoTransform gt;
     514           1 :     if (poDS->GetGeoTransform(gt) != CE_None)
     515             :     {
     516           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Dataset has no geotransform");
     517           0 :         return false;
     518             :     }
     519           1 :     GDALGeoTransform invGT;
     520           1 :     if (!gt.GetInverse(invGT))
     521             :     {
     522           0 :         return false;
     523             :     }
     524           1 :     const double dfX1 = invGT[0] + invGT[1] * dfULX + invGT[2] * dfULY;
     525           1 :     const double dfY1 = invGT[3] + invGT[4] * dfULX + invGT[5] * dfULY;
     526           1 :     const double dfX2 = invGT[0] + invGT[1] * dfLRX + invGT[2] * dfLRY;
     527           1 :     const double dfY2 = invGT[3] + invGT[4] * dfLRX + invGT[5] * dfLRY;
     528           1 :     constexpr double EPS = 1e-8;
     529             :     const int nXOff =
     530           1 :         static_cast<int>(std::max(0.0, std::min(dfX1, dfX2)) + EPS);
     531             :     const int nYOff =
     532           1 :         static_cast<int>(std::max(0.0, std::min(dfY1, dfY2)) + EPS);
     533           1 :     const int nXSize = static_cast<int>(std::ceil(
     534           1 :                            std::min(static_cast<double>(poDS->GetRasterXSize()),
     535           2 :                                     std::max(dfX1, dfX2)) -
     536             :                            EPS)) -
     537           1 :                        nXOff;
     538           1 :     const int nYSize = static_cast<int>(std::ceil(
     539           1 :                            std::min(static_cast<double>(poDS->GetRasterYSize()),
     540           2 :                                     std::max(dfY1, dfY2)) -
     541             :                            EPS)) -
     542           1 :                        nYOff;
     543           1 :     return PartialRefresh(poDS, anOvrIndices, nBandCount, panBandList,
     544             :                           pszResampling, nXOff, nYOff, nXSize, nYSize,
     545           1 :                           pfnProgress, pProgressArg);
     546             : }
     547             : 
     548             : /************************************************************************/
     549             : /*                                main()                                */
     550             : /************************************************************************/
     551             : 
     552          30 : MAIN_START(nArgc, papszArgv)
     553             : 
     554             : {
     555          30 :     EarlySetConfigOptions(nArgc, papszArgv);
     556          30 :     GDALAllRegister();
     557             : 
     558          30 :     nArgc = GDALGeneralCmdLineProcessor(nArgc, &papszArgv, 0);
     559          30 :     if (nArgc < 1)
     560           2 :         exit(-nArgc);
     561          52 :     CPLStringList aosArgv;
     562          28 :     aosArgv.Assign(papszArgv, /* bAssign = */ true);
     563             : 
     564          80 :     GDALArgumentParser argParser(aosArgv[0], /* bForBinary=*/true);
     565             : 
     566          28 :     argParser.add_description(_("Builds or rebuilds overview images."));
     567             : 
     568          28 :     const char *pszEpilog = _(
     569             :         "Useful configuration variables :\n"
     570             :         "  --config USE_RRD YES : Use Erdas Imagine format (.aux) as overview "
     571             :         "format.\n"
     572             :         "Below, only for external overviews in GeoTIFF format:\n"
     573             :         "  --config COMPRESS_OVERVIEW {JPEG,LZW,PACKBITS,DEFLATE} : TIFF "
     574             :         "compression\n"
     575             :         "  --config PHOTOMETRIC_OVERVIEW {RGB,YCBCR,...} : TIFF photometric "
     576             :         "interp.\n"
     577             :         "  --config INTERLEAVE_OVERVIEW {PIXEL|BAND} : TIFF interleaving "
     578             :         "method\n"
     579             :         "  --config BIGTIFF_OVERVIEW {IF_NEEDED|IF_SAFER|YES|NO} : is BigTIFF "
     580             :         "used\n"
     581             :         "\n"
     582             :         "Examples:\n"
     583             :         " %% gdaladdo -r average abc.tif\n"
     584             :         " %% gdaladdo --config COMPRESS_OVERVIEW JPEG\n"
     585             :         "            --config PHOTOMETRIC_OVERVIEW YCBCR\n"
     586             :         "            --config INTERLEAVE_OVERVIEW PIXEL -ro abc.tif\n"
     587             :         "\n"
     588             :         "For more details, consult https://gdal.org/programs/gdaladdo.html");
     589          28 :     argParser.add_epilog(pszEpilog);
     590             : 
     591          52 :     std::string osResampling;
     592          28 :     argParser.add_argument("-r")
     593          28 :         .store_into(osResampling)
     594             :         .metavar("nearest|average|rms|gauss|bilinear|cubic|cubicspline|lanczos|"
     595          56 :                  "average_magphase|mode")
     596          28 :         .help(_("Select a resampling algorithm."));
     597             : 
     598          28 :     bool bReadOnly = false;
     599          28 :     argParser.add_argument("-ro").store_into(bReadOnly).help(
     600             :         _("Open the dataset in read-only mode, in order to generate external "
     601          28 :           "overview."));
     602             : 
     603          28 :     bool bQuiet = false;
     604          28 :     argParser.add_quiet_argument(&bQuiet);
     605             : 
     606          52 :     std::vector<int> anBandList;
     607          28 :     argParser.add_argument("-b")
     608          28 :         .append()
     609          56 :         .metavar("<band>")
     610             :         .action(
     611           0 :             [&anBandList](const std::string &s)
     612             :             {
     613           0 :                 const int nBand = atoi(s.c_str());
     614           0 :                 if (nBand < 1)
     615             :                 {
     616             :                     throw std::invalid_argument(CPLSPrintf(
     617           0 :                         "Unrecognizable band number (%s).", s.c_str()));
     618             :                 }
     619           0 :                 anBandList.push_back(nBand);
     620          28 :             })
     621          28 :         .help(_("Select input band(s) for overview generation."));
     622             : 
     623          52 :     CPLStringList aosOpenOptions;
     624          28 :     argParser.add_argument("-oo")
     625          28 :         .append()
     626          56 :         .metavar("<NAME=VALUE>")
     627           1 :         .action([&aosOpenOptions](const std::string &s)
     628          29 :                 { aosOpenOptions.AddString(s.c_str()); })
     629          28 :         .help(_("Dataset open option (format-specific)."));
     630             : 
     631          28 :     int nMinSize = 256;
     632          28 :     argParser.add_argument("-minsize")
     633          28 :         .default_value(nMinSize)
     634          56 :         .metavar("<val>")
     635          28 :         .store_into(nMinSize)
     636          28 :         .help(_("Maximum width or height of the smallest overview level."));
     637             : 
     638          28 :     bool bClean = false;
     639          28 :     bool bPartialRefreshFromSourceTimestamp = false;
     640          52 :     std::string osPartialRefreshFromSourceExtent;
     641             : 
     642             :     {
     643          28 :         auto &group = argParser.add_mutually_exclusive_group();
     644          28 :         group.add_argument("-clean").store_into(bClean).help(
     645          28 :             _("Remove all overviews."));
     646             : 
     647          28 :         group.add_argument("--partial-refresh-from-source-timestamp")
     648          28 :             .store_into(bPartialRefreshFromSourceTimestamp)
     649             :             .help(_("Performs a partial refresh of existing overviews, when "
     650          28 :                     "<filename> is a VRT file with an external overview."));
     651             : 
     652          28 :         group.add_argument("--partial-refresh-from-projwin")
     653          56 :             .metavar("<ulx> <uly> <lrx> <lry>")
     654          28 :             .nargs(4)
     655          28 :             .scan<'g', double>()
     656             :             .help(
     657             :                 _("Performs a partial refresh of existing overviews, in the "
     658          28 :                   "region of interest specified by georeference coordinates."));
     659             : 
     660          28 :         group.add_argument("--partial-refresh-from-source-extent")
     661          56 :             .metavar("<filename1>[,<filenameN>]...")
     662          28 :             .store_into(osPartialRefreshFromSourceExtent)
     663             :             .help(
     664             :                 _("Performs a partial refresh of existing overviews, in the "
     665          28 :                   "region of interest specified by one or several filename."));
     666             :     }
     667             : 
     668          52 :     std::string osFilename;
     669          28 :     argParser.add_argument("filename")
     670          28 :         .store_into(osFilename)
     671             :         .help(_("The file to build overviews for (or whose overviews must be "
     672          28 :                 "removed)."));
     673             : 
     674          56 :     argParser.add_argument("level").remaining().metavar("<level>").help(
     675          28 :         _("A list of integral overview levels to build."));
     676             : 
     677             :     try
     678             :     {
     679          28 :         argParser.parse_args(aosArgv);
     680             :     }
     681           0 :     catch (const std::exception &err)
     682             :     {
     683           0 :         argParser.display_error_and_usage(err);
     684           0 :         std::exit(1);
     685             :     }
     686             : 
     687          52 :     std::vector<int> anLevels;
     688          52 :     auto levels = argParser.present<std::vector<std::string>>("level");
     689          28 :     if (levels)
     690             :     {
     691          29 :         for (const auto &level : *levels)
     692             :         {
     693          18 :             if (CPLGetValueType(level.c_str()) != CPL_VALUE_INTEGER)
     694             :             {
     695           1 :                 CPLError(
     696             :                     CE_Failure, CPLE_IllegalArg,
     697             :                     "Value '%s' is not a positive integer subsampling factor",
     698             :                     level.c_str());
     699           1 :                 std::exit(1);
     700             :             }
     701          17 :             anLevels.push_back(atoi(level.c_str()));
     702          17 :             if (anLevels.back() <= 0)
     703             :             {
     704           2 :                 CPLError(
     705             :                     CE_Failure, CPLE_IllegalArg,
     706             :                     "Value '%s' is not a positive integer subsampling factor",
     707             :                     level.c_str());
     708           2 :                 std::exit(1);
     709             :             }
     710          15 :             if (anLevels.back() == 1)
     711             :             {
     712           0 :                 printf(
     713             :                     "Warning: Overview with subsampling factor of 1 requested. "
     714             :                     "This will copy the full resolution dataset in the "
     715             :                     "overview!\n");
     716             :             }
     717             :         }
     718             :     }
     719             : 
     720          25 :     GDALProgressFunc pfnProgress =
     721          25 :         bQuiet ? GDALDummyProgress : GDALTermProgress;
     722          25 :     const bool bMinSizeSpecified = argParser.is_used("-minsize");
     723             : 
     724          25 :     CPLStringList aosSources;
     725          25 :     if (!osPartialRefreshFromSourceExtent.empty())
     726             :     {
     727             :         aosSources = CSLTokenizeString2(
     728           1 :             osPartialRefreshFromSourceExtent.c_str(), ",", 0);
     729             :     }
     730             : 
     731          25 :     bool bPartialRefreshFromProjWin = false;
     732          25 :     double dfULX = 0;
     733          25 :     double dfULY = 0;
     734          25 :     double dfLRX = 0;
     735          25 :     double dfLRY = 0;
     736          25 :     if (auto oProjWin = argParser.present<std::vector<double>>(
     737          50 :             "--partial-refresh-from-projwin"))
     738             :     {
     739           1 :         bPartialRefreshFromProjWin = true;
     740           1 :         dfULX = (*oProjWin)[0];
     741           1 :         dfULY = (*oProjWin)[1];
     742           1 :         dfLRX = (*oProjWin)[2];
     743           1 :         dfLRY = (*oProjWin)[3];
     744             :     }
     745             : 
     746             :     /* -------------------------------------------------------------------- */
     747             :     /*      Open data file.                                                 */
     748             :     /* -------------------------------------------------------------------- */
     749          25 :     GDALDatasetH hDataset = nullptr;
     750          25 :     if (!bReadOnly)
     751             :     {
     752          20 :         CPLPushErrorHandler(GDALAddoErrorHandler);
     753          22 :         if (bClean &&
     754           2 :             aosOpenOptions.FetchNameValue("IGNORE_COG_LAYOUT_BREAK") == nullptr)
     755             :         {
     756           2 :             GDALDriverH hDrv = GDALIdentifyDriver(osFilename.c_str(), nullptr);
     757           2 :             if (hDrv && EQUAL(GDALGetDescription(hDrv), "GTiff"))
     758             :             {
     759             :                 // Cleaning does not break COG layout
     760           2 :                 aosOpenOptions.SetNameValue("IGNORE_COG_LAYOUT_BREAK", "YES");
     761             :             }
     762             :         }
     763             : 
     764          20 :         CPLSetCurrentErrorHandlerCatchDebug(FALSE);
     765             :         hDataset =
     766          20 :             GDALOpenEx(osFilename.c_str(), GDAL_OF_RASTER | GDAL_OF_UPDATE,
     767          20 :                        nullptr, aosOpenOptions.List(), nullptr);
     768          20 :         CPLPopErrorHandler();
     769             :         const bool bIsCOG =
     770          21 :             (aoErrors.size() == 1 &&
     771           1 :              aoErrors[0].m_osMsg.find("C(loud) O(ptimized) G(eoTIFF) layout") !=
     772          21 :                  std::string::npos &&
     773           1 :              aosOpenOptions.FetchNameValue("IGNORE_COG_LAYOUT_BREAK") ==
     774          20 :                  nullptr);
     775          20 :         if (hDataset != nullptr || bIsCOG)
     776             :         {
     777          21 :             for (size_t i = 0; i < aoErrors.size(); i++)
     778             :             {
     779           1 :                 CPLError(aoErrors[i].m_eErr, aoErrors[i].m_errNum, "%s",
     780           1 :                          aoErrors[i].m_osMsg.c_str());
     781             :             }
     782          20 :             if (bIsCOG)
     783           1 :                 exit(1);
     784             :         }
     785             :     }
     786             : 
     787          24 :     if (hDataset == nullptr)
     788           5 :         hDataset = GDALOpenEx(osFilename.c_str(),
     789             :                               GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR, nullptr,
     790           5 :                               aosOpenOptions.List(), nullptr);
     791          24 :     if (hDataset == nullptr)
     792           0 :         exit(2);
     793             : 
     794          24 :     if (!bClean && osResampling.empty())
     795             :     {
     796           9 :         auto poDS = GDALDataset::FromHandle(hDataset);
     797           9 :         if (poDS->GetRasterCount() > 0)
     798             :         {
     799           9 :             auto poBand = poDS->GetRasterBand(1);
     800           9 :             if (poBand->GetOverviewCount() > 0)
     801             :             {
     802             :                 const char *pszResampling =
     803           2 :                     poBand->GetOverview(0)->GetMetadataItem("RESAMPLING");
     804           2 :                 if (pszResampling)
     805             :                 {
     806           2 :                     osResampling = pszResampling;
     807           2 :                     if (pfnProgress == GDALDummyProgress)
     808           0 :                         CPLDebug("GDAL",
     809             :                                  "Reusing resampling method %s from existing "
     810             :                                  "overview",
     811             :                                  pszResampling);
     812             :                     else
     813           2 :                         printf("Info: reusing resampling method %s from "
     814             :                                "existing overview.\n",
     815             :                                pszResampling);
     816             :                 }
     817             :             }
     818             :         }
     819           9 :         if (osResampling.empty())
     820           7 :             osResampling = "nearest";
     821             :     }
     822             : 
     823             :     /* -------------------------------------------------------------------- */
     824             :     /*      Clean overviews.                                                */
     825             :     /* -------------------------------------------------------------------- */
     826          24 :     int nResultStatus = 0;
     827          24 :     void *pProgressArg = nullptr;
     828          24 :     const int nBandCount = static_cast<int>(anBandList.size());
     829          24 :     if (bClean)
     830             :     {
     831           2 :         if (GDALBuildOverviews(hDataset, "NONE", 0, nullptr, 0, nullptr,
     832           2 :                                pfnProgress, pProgressArg) != CE_None)
     833             :         {
     834           0 :             fprintf(stderr, "Cleaning overviews failed.\n");
     835           0 :             nResultStatus = 200;
     836             :         }
     837             :     }
     838          22 :     else if (bPartialRefreshFromSourceTimestamp)
     839             :     {
     840           2 :         if (!PartialRefreshFromSourceTimestamp(
     841             :                 GDALDataset::FromHandle(hDataset), osResampling.c_str(),
     842           2 :                 static_cast<int>(anLevels.size()), anLevels.data(), nBandCount,
     843           2 :                 anBandList.data(), bMinSizeSpecified, nMinSize, pfnProgress,
     844             :                 pProgressArg))
     845             :         {
     846           0 :             nResultStatus = 1;
     847             :         }
     848             :     }
     849          20 :     else if (bPartialRefreshFromProjWin)
     850             :     {
     851           1 :         if (!PartialRefreshFromProjWin(
     852             :                 GDALDataset::FromHandle(hDataset), dfULX, dfULY, dfLRX, dfLRY,
     853           1 :                 osResampling.c_str(), static_cast<int>(anLevels.size()),
     854           1 :                 anLevels.data(), nBandCount, anBandList.data(),
     855             :                 bMinSizeSpecified, nMinSize, pfnProgress, pProgressArg))
     856             :         {
     857           0 :             nResultStatus = 1;
     858             :         }
     859             :     }
     860          19 :     else if (!aosSources.empty())
     861             :     {
     862           1 :         if (!PartialRefreshFromSourceExtent(
     863             :                 GDALDataset::FromHandle(hDataset), aosSources,
     864           1 :                 osResampling.c_str(), static_cast<int>(anLevels.size()),
     865           1 :                 anLevels.data(), nBandCount, anBandList.data(),
     866             :                 bMinSizeSpecified, nMinSize, pfnProgress, pProgressArg))
     867             :         {
     868           0 :             nResultStatus = 1;
     869             :         }
     870             :     }
     871             :     else
     872             :     {
     873             :         /* --------------------------------------------------------------------
     874             :          */
     875             :         /*      Generate overviews. */
     876             :         /* --------------------------------------------------------------------
     877             :          */
     878             : 
     879             :         // If no levels are specified, reuse the potentially existing ones.
     880          18 :         if (anLevels.empty())
     881             :         {
     882           8 :             auto poDS = GDALDataset::FromHandle(hDataset);
     883           8 :             if (poDS->GetRasterCount() > 0)
     884             :             {
     885           8 :                 auto poBand = poDS->GetRasterBand(1);
     886           8 :                 const int nExistingCount = poBand->GetOverviewCount();
     887           8 :                 if (nExistingCount > 0)
     888             :                 {
     889          12 :                     for (int iOvr = 0; iOvr < nExistingCount; ++iOvr)
     890             :                     {
     891           8 :                         auto poOverview = poBand->GetOverview(iOvr);
     892           8 :                         if (poOverview)
     893             :                         {
     894           8 :                             const int nOvFactor = GDALComputeOvFactor(
     895             :                                 poOverview->GetXSize(), poBand->GetXSize(),
     896           8 :                                 poOverview->GetYSize(), poBand->GetYSize());
     897           8 :                             anLevels.push_back(nOvFactor);
     898             :                         }
     899             :                     }
     900             :                 }
     901             :             }
     902             :         }
     903             : 
     904          18 :         if (anLevels.empty())
     905             :         {
     906           4 :             const int nXSize = GDALGetRasterXSize(hDataset);
     907           4 :             const int nYSize = GDALGetRasterYSize(hDataset);
     908           4 :             int nOvrFactor = 1;
     909          12 :             while (DIV_ROUND_UP(nXSize, nOvrFactor) > nMinSize ||
     910           4 :                    DIV_ROUND_UP(nYSize, nOvrFactor) > nMinSize)
     911             :             {
     912           8 :                 nOvrFactor *= 2;
     913           8 :                 anLevels.push_back(nOvrFactor);
     914             :             }
     915             :         }
     916             : 
     917             :         // Only HFA supports selected layers
     918          18 :         if (nBandCount > 0)
     919           0 :             CPLSetConfigOption("USE_RRD", "YES");
     920             : 
     921          35 :         if (!anLevels.empty() &&
     922          17 :             GDALBuildOverviews(hDataset, osResampling.c_str(),
     923          17 :                                static_cast<int>(anLevels.size()),
     924          17 :                                anLevels.data(), nBandCount, anBandList.data(),
     925             :                                pfnProgress, pProgressArg) != CE_None)
     926             :         {
     927           0 :             fprintf(stderr, "Overview building failed.\n");
     928           0 :             nResultStatus = 100;
     929             :         }
     930             :     }
     931             : 
     932             :     /* -------------------------------------------------------------------- */
     933             :     /*      Cleanup                                                         */
     934             :     /* -------------------------------------------------------------------- */
     935          24 :     if (GDALClose(hDataset) != CE_None)
     936             :     {
     937           0 :         if (nResultStatus == 0)
     938           0 :             nResultStatus = 1;
     939             :     }
     940             : 
     941          24 :     GDALDestroyDriverManager();
     942             : 
     943          24 :     return nResultStatus;
     944             : }
     945             : 
     946           0 : MAIN_END

Generated by: LCOV version 1.14