Coverage Report

Created: 2026-07-25 06:50

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