Coverage Report

Created: 2026-04-10 07:04

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