Coverage Report

Created: 2026-02-14 06:52

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