LCOV - code coverage report
Current view: top level - apps - gdalalg_raster_update.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 130 132 98.5 %
Date: 2025-12-01 18:11:08 Functions: 3 3 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  gdal "raster update" subcommand
       5             :  * Author:   Even Rouault <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdalalg_raster_update.h"
      14             : 
      15             : #include "cpl_conv.h"
      16             : 
      17             : #include "gdal_priv.h"
      18             : #include "gdal_utils.h"
      19             : #include "gdalalg_raster_reproject.h"  // for GDALRasterReprojectUtils
      20             : #include "gdalalg_raster_overview_refresh.h"
      21             : #include "ogr_spatialref.h"
      22             : 
      23             : #include <algorithm>
      24             : #include <array>
      25             : #include <cmath>
      26             : #include <tuple>
      27             : 
      28             : //! @cond Doxygen_Suppress
      29             : 
      30             : #ifndef _
      31             : #define _(x) (x)
      32             : #endif
      33             : 
      34             : /************************************************************************/
      35             : /*        GDALRasterUpdateAlgorithm::GDALRasterUpdateAlgorithm()        */
      36             : /************************************************************************/
      37             : 
      38          54 : GDALRasterUpdateAlgorithm::GDALRasterUpdateAlgorithm(bool standaloneStep)
      39             :     : GDALRasterPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
      40           0 :                                       ConstructorOptions()
      41          54 :                                           .SetStandaloneStep(standaloneStep)
      42          54 :                                           .SetInputDatasetMaxCount(1)
      43          54 :                                           .SetAddDefaultArguments(false)
      44         108 :                                           .SetInputDatasetAlias("dataset"))
      45             : {
      46          54 :     AddProgressArg();
      47             : 
      48          54 :     if (standaloneStep)
      49             :     {
      50          18 :         AddRasterInputArgs(/* openForMixedRasterVector = */ false,
      51             :                            /* hiddenForCLI = */ false);
      52             :     }
      53             :     else
      54             :     {
      55          36 :         AddRasterHiddenInputDatasetArg();
      56             :     }
      57             : 
      58          54 :     AddOutputDatasetArg(&m_outputDataset, GDAL_OF_RASTER)
      59          54 :         .SetDatasetInputFlags(GADV_NAME | GADV_OBJECT);
      60             : 
      61          54 :     m_update = true;
      62          54 :     AddUpdateArg(&m_update).SetDefault(true).SetHidden();
      63             : 
      64         108 :     AddArg("geometry", 0, _("Clipping geometry (WKT or GeoJSON)"), &m_geometry)
      65          54 :         .SetMutualExclusionGroup("bbox-geometry-like");
      66         108 :     AddArg("geometry-crs", 0, _("CRS of clipping geometry"), &m_geometryCrs)
      67         108 :         .SetIsCRSArg()
      68          54 :         .AddHiddenAlias("geometry_srs");
      69             : 
      70          54 :     GDALRasterReprojectUtils::AddResamplingArg(this, m_resampling);
      71             : 
      72          54 :     GDALRasterReprojectUtils::AddWarpOptTransformOptErrorThresholdArg(
      73          54 :         this, m_warpOptions, m_transformOptions, m_errorThreshold);
      74             : 
      75             :     AddArg("no-update-overviews", 0, _("Do not update existing overviews"),
      76          54 :            &m_noUpdateOverviews);
      77          54 : }
      78             : 
      79             : /************************************************************************/
      80             : /*                GDALRasterUpdateAlgorithm::RunStep()                  */
      81             : /************************************************************************/
      82             : 
      83          11 : bool GDALRasterUpdateAlgorithm::RunStep(GDALPipelineStepRunContext &stepCtxt)
      84             : {
      85          11 :     auto poSrcDS = m_inputDataset[0].GetDatasetRef();
      86          11 :     CPLAssert(poSrcDS);
      87             : 
      88          11 :     auto poDstDS = m_outputDataset.GetDatasetRef();
      89          11 :     CPLAssert(poDstDS);
      90          11 :     CPLAssert(poDstDS->GetAccess() == GA_Update);
      91             : 
      92          11 :     std::unique_ptr<OGRGeometry> poClipGeom;
      93          22 :     std::string errMsg;
      94          11 :     if (!m_geometry.empty())
      95             :     {
      96           2 :         std::tie(poClipGeom, errMsg) = GetClipGeometry();
      97           2 :         if (!poClipGeom)
      98             :         {
      99           1 :             ReportError(CE_Failure, CPLE_AppDefined, "%s", errMsg.c_str());
     100           1 :             return false;
     101             :         }
     102             :     }
     103             : 
     104          10 :     auto poSrcDriver = poSrcDS->GetDriver();
     105          10 :     auto poDstDriver = poDstDS->GetDriver();
     106          19 :     if (poSrcDS == poDstDS ||
     107           9 :         (poSrcDriver && poDstDriver &&
     108           9 :          !EQUAL(poSrcDriver->GetDescription(), "MEM") &&
     109           3 :          !EQUAL(poDstDriver->GetDescription(), "MEM") &&
     110           3 :          strcmp(poSrcDS->GetDescription(), poDstDS->GetDescription()) == 0))
     111             :     {
     112           2 :         ReportError(CE_Failure, CPLE_NotSupported,
     113             :                     "Source and destination datasets must be different");
     114           2 :         return false;
     115             :     }
     116             : 
     117          16 :     CPLStringList aosOptions;
     118           8 :     if (!m_resampling.empty())
     119             :     {
     120           8 :         aosOptions.AddString("-r");
     121           8 :         aosOptions.AddString(m_resampling.c_str());
     122             :     }
     123           9 :     for (const std::string &opt : m_warpOptions)
     124             :     {
     125           1 :         aosOptions.AddString("-wo");
     126           1 :         aosOptions.AddString(opt.c_str());
     127             :     }
     128           9 :     for (const std::string &opt : m_transformOptions)
     129             :     {
     130           1 :         aosOptions.AddString("-to");
     131           1 :         aosOptions.AddString(opt.c_str());
     132             :     }
     133           8 :     if (std::isfinite(m_errorThreshold))
     134             :     {
     135           1 :         aosOptions.AddString("-et");
     136           1 :         aosOptions.AddString(CPLSPrintf("%.17g", m_errorThreshold));
     137             :     }
     138             : 
     139           8 :     if (poClipGeom)
     140             :     {
     141           1 :         aosOptions.AddString("-cutline");
     142           1 :         aosOptions.AddString(poClipGeom->exportToWkt());
     143             :     }
     144             : 
     145           8 :     bool bOvrCanBeUpdated = false;
     146           8 :     std::vector<double> overviewRefreshBBox;
     147          10 :     if (poDstDS->GetRasterBand(1)->GetOverviewCount() > 0 &&
     148           2 :         !m_noUpdateOverviews)
     149             :     {
     150           2 :         GDALGeoTransform gt;
     151           2 :         const auto poSrcCRS = poSrcDS->GetSpatialRef();
     152           2 :         const auto poDstCRS = poDstDS->GetSpatialRef();
     153           2 :         const bool bBothCRS = poSrcCRS && poDstCRS;
     154           2 :         const bool bBothNoCRS = !poSrcCRS && !poDstCRS;
     155           2 :         if ((bBothCRS || bBothNoCRS) && poSrcDS->GetGeoTransform(gt) == CE_None)
     156             :         {
     157             :             auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
     158           1 :                 bBothCRS ? OGRCreateCoordinateTransformation(poSrcCRS, poDstCRS)
     159           5 :                          : nullptr);
     160           2 :             if (bBothNoCRS || poCT)
     161             :             {
     162           2 :                 const double dfTLX = gt[0];
     163           2 :                 const double dfTLY = gt[3];
     164             : 
     165           2 :                 double dfTRX = 0;
     166           2 :                 double dfTRY = 0;
     167           2 :                 gt.Apply(poSrcDS->GetRasterXSize(), 0, &dfTRX, &dfTRY);
     168             : 
     169           2 :                 double dfBLX = 0;
     170           2 :                 double dfBLY = 0;
     171           2 :                 gt.Apply(0, poSrcDS->GetRasterYSize(), &dfBLX, &dfBLY);
     172             : 
     173           2 :                 double dfBRX = 0;
     174           2 :                 double dfBRY = 0;
     175           2 :                 gt.Apply(poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize(),
     176             :                          &dfBRX, &dfBRY);
     177             : 
     178             :                 const double dfXMin =
     179           2 :                     std::min(std::min(dfTLX, dfTRX), std::min(dfBLX, dfBRX));
     180             :                 const double dfYMin =
     181           2 :                     std::min(std::min(dfTLY, dfTRY), std::min(dfBLY, dfBRY));
     182             :                 const double dfXMax =
     183           2 :                     std::max(std::max(dfTLX, dfTRX), std::max(dfBLX, dfBRX));
     184             :                 const double dfYMax =
     185           2 :                     std::max(std::max(dfTLY, dfTRY), std::max(dfBLY, dfBRY));
     186           2 :                 double dfOutXMin = dfXMin;
     187           2 :                 double dfOutYMin = dfYMin;
     188           2 :                 double dfOutXMax = dfXMax;
     189           2 :                 double dfOutYMax = dfYMax;
     190           3 :                 if (!poCT || poCT->TransformBounds(
     191             :                                  dfXMin, dfYMin, dfXMax, dfYMax, &dfOutXMin,
     192           1 :                                  &dfOutYMin, &dfOutXMax, &dfOutYMax, 21))
     193             :                 {
     194           2 :                     bOvrCanBeUpdated = true;
     195           2 :                     CPLDebug("update",
     196             :                              "Refresh overviews from (%f,%f) to (%f,%f)",
     197             :                              dfOutXMin, dfOutYMin, dfOutXMax, dfOutYMax);
     198           4 :                     overviewRefreshBBox = std::vector<double>{
     199           2 :                         dfOutXMin, dfOutYMin, dfOutXMax, dfOutYMax};
     200             :                 }
     201             :             }
     202             :         }
     203           2 :         if (!bOvrCanBeUpdated)
     204             :         {
     205           0 :             ReportError(CE_Warning, CPLE_AppDefined,
     206             :                         "Overviews can not be updated");
     207             :         }
     208             :     }
     209             : 
     210           8 :     bool bOK = false;
     211             :     GDALWarpAppOptions *psOptions =
     212           8 :         GDALWarpAppOptionsNew(aosOptions.List(), nullptr);
     213           8 :     if (psOptions)
     214             :     {
     215             :         std::unique_ptr<void, decltype(&GDALDestroyScaledProgress)> pScaledData(
     216          16 :             nullptr, GDALDestroyScaledProgress);
     217           8 :         auto pfnProgress = stepCtxt.m_pfnProgress;
     218           8 :         void *pProgressData = stepCtxt.m_pProgressData;
     219           8 :         if (pfnProgress)
     220             :         {
     221           3 :             pScaledData.reset(
     222             :                 GDALCreateScaledProgress(0.0, bOvrCanBeUpdated ? 0.75 : 1.0,
     223             :                                          pfnProgress, pProgressData));
     224           3 :             GDALWarpAppOptionsSetProgress(psOptions, GDALScaledProgress,
     225             :                                           pScaledData.get());
     226             :         }
     227             : 
     228           8 :         GDALDatasetH hSrcDS = GDALDataset::ToHandle(poSrcDS);
     229           8 :         GDALDatasetH hDstDS = GDALDataset::ToHandle(poDstDS);
     230           8 :         auto poRetDS = GDALDataset::FromHandle(
     231             :             GDALWarp(nullptr, hDstDS, 1, &hSrcDS, psOptions, nullptr));
     232           8 :         GDALWarpAppOptionsFree(psOptions);
     233             : 
     234           8 :         bOK = poRetDS != nullptr;
     235           8 :         if (bOK && bOvrCanBeUpdated)
     236             :         {
     237           2 :             GDALRasterOverviewAlgorithmRefresh refresh;
     238           2 :             refresh.GetArg("dataset")->Set(poRetDS);
     239           2 :             if (!m_resampling.empty())
     240           2 :                 refresh.GetArg("resampling")->Set(m_resampling);
     241           2 :             refresh.GetArg("bbox")->Set(overviewRefreshBBox);
     242           2 :             pScaledData.reset(GDALCreateScaledProgress(0.75, 1.0, pfnProgress,
     243             :                                                        pProgressData));
     244           2 :             bOK = refresh.Run(pScaledData ? GDALScaledProgress : nullptr,
     245             :                               pScaledData.get());
     246             :         }
     247           8 :         if (bOK && pfnProgress)
     248           3 :             pfnProgress(1.0, "", pProgressData);
     249             :     }
     250             : 
     251           8 :     return bOK;
     252             : }
     253             : 
     254             : /************************************************************************/
     255             : /*                GDALRasterUpdateAlgorithm::RunImpl()                  */
     256             : /************************************************************************/
     257             : 
     258           9 : bool GDALRasterUpdateAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
     259             :                                         void *pProgressData)
     260             : {
     261           9 :     GDALPipelineStepRunContext stepCtxt;
     262           9 :     stepCtxt.m_pfnProgress = pfnProgress;
     263           9 :     stepCtxt.m_pProgressData = pProgressData;
     264          18 :     return RunStep(stepCtxt);
     265             : }
     266             : 
     267             : GDALRasterUpdateAlgorithmStandalone::~GDALRasterUpdateAlgorithmStandalone() =
     268             :     default;
     269             : 
     270             : //! @endcond

Generated by: LCOV version 1.14