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_overview_add.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  gdal "raster overview add" 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_overview.h"
14
#include "gdalalg_raster_overview_add.h"
15
16
#include "cpl_string.h"
17
#include "gdal_priv.h"
18
19
//! @cond Doxygen_Suppress
20
21
#ifndef _
22
0
#define _(x) (x)
23
#endif
24
25
bool GDALRasterOverviewAlgorithm::RunStep(GDALPipelineStepRunContext &)
26
0
{
27
0
    CPLError(CE_Failure, CPLE_AppDefined,
28
0
             "The Run() method should not be called directly on the \"gdal "
29
0
             "raster overview\" program.");
30
0
    return false;
31
0
}
32
33
GDALRasterOverviewAlgorithmStandalone::
34
0
    ~GDALRasterOverviewAlgorithmStandalone() = default;
35
36
/************************************************************************/
37
/*                   GDALRasterOverviewAlgorithmAdd()                   */
38
/************************************************************************/
39
40
GDALRasterOverviewAlgorithmAdd::GDALRasterOverviewAlgorithmAdd(
41
    bool standaloneStep)
42
0
    : GDALRasterPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
43
0
                                      ConstructorOptions()
44
0
                                          .SetStandaloneStep(standaloneStep)
45
0
                                          .SetAddDefaultArguments(false))
46
0
{
47
0
    if (standaloneStep)
48
0
        AddProgressArg();
49
50
0
    AddOpenOptionsArg(&m_openOptions);
51
0
    auto &datasetArg =
52
0
        AddInputDatasetArg(
53
0
            &m_inputDataset, GDAL_OF_RASTER | GDAL_OF_UPDATE,
54
0
            /* positionalAndRequired = */ standaloneStep,
55
0
            _("Dataset (to be updated in-place, unless --external)"))
56
0
            .AddAlias("dataset")
57
0
            .SetMaxCount(1);
58
0
    if (!standaloneStep)
59
0
    {
60
0
        datasetArg.SetPositional();
61
0
        datasetArg.SetHidden();
62
0
    }
63
64
0
    constexpr const char *OVERVIEW_SRC_LEVELS_MUTEX = "overview-src-levels";
65
66
0
    auto &overviewSrcArg =
67
0
        AddArg("overview-src", 0, _("Source overview dataset"),
68
0
               &m_overviewSources, GDAL_OF_RASTER)
69
0
            .SetMutualExclusionGroup(OVERVIEW_SRC_LEVELS_MUTEX);
70
0
    SetAutoCompleteFunctionForFilename(overviewSrcArg, GDAL_OF_RASTER);
71
72
0
    if (standaloneStep)
73
0
    {
74
0
        AddArg("external", 0, _("Add external overviews"), &m_readOnly)
75
0
            .AddHiddenAlias("ro")
76
0
            .AddHiddenAlias(GDAL_ARG_NAME_READ_ONLY);
77
0
    }
78
79
0
    AddArg("resampling", 'r', _("Resampling method"), &m_resampling)
80
0
        .SetChoices("nearest", "average", "cubic", "cubicspline", "lanczos",
81
0
                    "bilinear", "gauss", "average_magphase", "rms", "mode")
82
0
        .SetHiddenChoices("near", "none");
83
84
0
    AddArg("levels", 0, _("Levels / decimation factors"), &m_levels)
85
0
        .SetMinValueIncluded(2)
86
0
        .SetMutualExclusionGroup(OVERVIEW_SRC_LEVELS_MUTEX);
87
0
    AddArg("min-size", 0,
88
0
           _("Maximum width or height of the smallest overview level."),
89
0
           &m_minSize)
90
0
        .SetMinValueIncluded(1);
91
92
0
    if (standaloneStep)
93
0
    {
94
0
        auto &ovrCreationOptionArg =
95
0
            AddArg(GDAL_ARG_NAME_CREATION_OPTION, 0,
96
0
                   _("Overview creation option"), &m_creationOptions)
97
0
                .AddAlias("co")
98
0
                .SetMetaVar("<KEY>=<VALUE>")
99
0
                .SetPackedValuesAllowed(false);
100
0
        ovrCreationOptionArg.AddValidationAction(
101
0
            [this, &ovrCreationOptionArg]()
102
0
            { return ParseAndValidateKeyValue(ovrCreationOptionArg); });
103
104
0
        ovrCreationOptionArg.SetAutoCompleteFunction(
105
0
            [this](const std::string &currentValue)
106
0
            {
107
0
                std::vector<std::string> oRet;
108
109
0
                const std::string osDSName = m_inputDataset.size() == 1
110
0
                                                 ? m_inputDataset[0].GetName()
111
0
                                                 : std::string();
112
0
                const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
113
0
                if (!osExt.empty())
114
0
                {
115
0
                    std::set<std::string> oVisitedExtensions;
116
0
                    auto poDM = GetGDALDriverManager();
117
0
                    for (int i = 0; i < poDM->GetDriverCount(); ++i)
118
0
                    {
119
0
                        auto poDriver = poDM->GetDriver(i);
120
0
                        if (poDriver->GetMetadataItem(GDAL_DCAP_RASTER))
121
0
                        {
122
0
                            const char *pszExtensions =
123
0
                                poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
124
0
                            if (pszExtensions)
125
0
                            {
126
0
                                const CPLStringList aosExts(
127
0
                                    CSLTokenizeString2(pszExtensions, " ", 0));
128
0
                                for (const char *pszExt : cpl::Iterate(aosExts))
129
0
                                {
130
0
                                    if (EQUAL(pszExt, osExt.c_str()) &&
131
0
                                        !cpl::contains(oVisitedExtensions,
132
0
                                                       pszExt))
133
0
                                    {
134
0
                                        oVisitedExtensions.insert(pszExt);
135
0
                                        if (AddOptionsSuggestions(
136
0
                                                poDriver->GetMetadataItem(
137
0
                                                    GDAL_DMD_OVERVIEW_CREATIONOPTIONLIST),
138
0
                                                GDAL_OF_RASTER, currentValue,
139
0
                                                oRet))
140
0
                                        {
141
0
                                            return oRet;
142
0
                                        }
143
0
                                        break;
144
0
                                    }
145
0
                                }
146
0
                            }
147
0
                        }
148
0
                    }
149
0
                }
150
151
0
                return oRet;
152
0
            });
153
0
    }
154
0
}
155
156
/************************************************************************/
157
/*              GDALRasterOverviewAlgorithmAdd::RunStep()               */
158
/************************************************************************/
159
160
bool GDALRasterOverviewAlgorithmAdd::RunStep(GDALPipelineStepRunContext &ctxt)
161
0
{
162
0
    GDALProgressFunc pfnProgress = ctxt.m_pfnProgress;
163
0
    void *pProgressData = ctxt.m_pProgressData;
164
0
    auto poDS = m_inputDataset[0].GetDatasetRef();
165
0
    CPLAssert(poDS);
166
167
0
    CPLStringList aosOptions(m_creationOptions);
168
0
    if (m_readOnly)
169
0
    {
170
0
        auto poDriver = poDS->GetDriver();
171
0
        if (poDriver)
172
0
        {
173
0
            const char *pszOptionList =
174
0
                poDriver->GetMetadataItem(GDAL_DMD_OVERVIEW_CREATIONOPTIONLIST);
175
0
            if (pszOptionList)
176
0
            {
177
0
                if (strstr(pszOptionList, "<Value>EXTERNAL</Value>") == nullptr)
178
0
                {
179
0
                    ReportError(CE_Failure, CPLE_NotSupported,
180
0
                                "Driver %s does not support external overviews",
181
0
                                poDriver->GetDescription());
182
0
                    return false;
183
0
                }
184
0
                else if (aosOptions.FetchNameValue("LOCATION") == nullptr)
185
0
                {
186
0
                    aosOptions.SetNameValue("LOCATION", "EXTERNAL");
187
0
                }
188
0
            }
189
0
        }
190
0
    }
191
192
0
    std::string resampling = m_resampling;
193
0
    if (resampling.empty() && poDS->GetRasterCount() > 0)
194
0
    {
195
0
        auto poBand = poDS->GetRasterBand(1);
196
0
        if (poBand->GetOverviewCount() > 0)
197
0
        {
198
0
            const char *pszResampling =
199
0
                poBand->GetOverview(0)->GetMetadataItem("RESAMPLING");
200
0
            if (pszResampling)
201
0
            {
202
0
                resampling = pszResampling;
203
0
                CPLDebug("GDAL",
204
0
                         "Reusing resampling method %s from existing "
205
0
                         "overview",
206
0
                         pszResampling);
207
0
            }
208
0
        }
209
0
    }
210
0
    if (resampling.empty())
211
0
        resampling = "nearest";
212
213
0
    if (!m_overviewSources.empty())
214
0
    {
215
0
        std::vector<GDALDataset *> apoDS;
216
0
        for (auto &val : m_overviewSources)
217
0
        {
218
0
            CPLAssert(val.GetDatasetRef());
219
0
            apoDS.push_back(val.GetDatasetRef());
220
0
        }
221
0
        return poDS->AddOverviews(apoDS, pfnProgress, pProgressData, nullptr) ==
222
0
               CE_None;
223
0
    }
224
225
0
    std::vector<int> levels = m_levels;
226
227
    // If no levels are specified, reuse the potentially existing ones.
228
0
    if (levels.empty() && poDS->GetRasterCount() > 0)
229
0
    {
230
0
        auto poBand = poDS->GetRasterBand(1);
231
0
        const int nExistingCount = poBand->GetOverviewCount();
232
0
        if (nExistingCount > 0)
233
0
        {
234
0
            for (int iOvr = 0; iOvr < nExistingCount; ++iOvr)
235
0
            {
236
0
                auto poOverview = poBand->GetOverview(iOvr);
237
0
                if (poOverview)
238
0
                {
239
0
                    const int nOvFactor = GDALComputeOvFactor(
240
0
                        poOverview->GetXSize(), poBand->GetXSize(),
241
0
                        poOverview->GetYSize(), poBand->GetYSize());
242
0
                    levels.push_back(nOvFactor);
243
0
                }
244
0
            }
245
0
        }
246
0
    }
247
248
0
    if (levels.empty())
249
0
    {
250
0
        const int nXSize = poDS->GetRasterXSize();
251
0
        const int nYSize = poDS->GetRasterYSize();
252
0
        int nOvrFactor = 1;
253
0
        while (DIV_ROUND_UP(nXSize, nOvrFactor) > m_minSize ||
254
0
               DIV_ROUND_UP(nYSize, nOvrFactor) > m_minSize)
255
0
        {
256
0
            nOvrFactor *= 2;
257
0
            levels.push_back(nOvrFactor);
258
0
        }
259
0
    }
260
261
0
    if (!m_standaloneStep && !levels.empty())
262
0
    {
263
0
        auto poVRTDriver = GetGDALDriverManager()->GetDriverByName("VRT");
264
0
        if (!poVRTDriver)
265
0
        {
266
0
            ReportError(CE_Failure, CPLE_AppDefined,
267
0
                        "VRT driver not available");
268
0
            return false;
269
0
        }
270
0
        auto poVRTDS = std::unique_ptr<GDALDataset>(poVRTDriver->CreateCopy(
271
0
            "", poDS, false, nullptr, nullptr, nullptr));
272
0
        bool bRet = poVRTDS != nullptr;
273
0
        if (bRet)
274
0
        {
275
0
            aosOptions.SetNameValue("VIRTUAL", "YES");
276
0
            bRet = GDALBuildOverviewsEx(
277
0
                       GDALDataset::ToHandle(poVRTDS.get()), resampling.c_str(),
278
0
                       static_cast<int>(levels.size()), levels.data(), 0,
279
0
                       nullptr, nullptr, nullptr, aosOptions.List()) == CE_None;
280
0
            if (bRet)
281
0
                m_outputDataset.Set(std::move(poVRTDS));
282
0
        }
283
0
        return bRet;
284
0
    }
285
0
    else
286
0
    {
287
0
        const auto ret =
288
0
            levels.empty() ||
289
0
            GDALBuildOverviewsEx(
290
0
                GDALDataset::ToHandle(poDS), resampling.c_str(),
291
0
                static_cast<int>(levels.size()), levels.data(), 0, nullptr,
292
0
                pfnProgress, pProgressData, aosOptions.List()) == CE_None;
293
0
        if (ret)
294
0
            m_outputDataset.Set(poDS);
295
0
        return ret;
296
0
    }
297
0
}
298
299
/************************************************************************/
300
/*              GDALRasterOverviewAlgorithmAdd::RunImpl()               */
301
/************************************************************************/
302
303
bool GDALRasterOverviewAlgorithmAdd::RunImpl(GDALProgressFunc pfnProgress,
304
                                             void *pProgressData)
305
0
{
306
0
    GDALPipelineStepRunContext stepCtxt;
307
0
    stepCtxt.m_pfnProgress = pfnProgress;
308
0
    stepCtxt.m_pProgressData = pProgressData;
309
0
    return RunStep(stepCtxt);
310
0
}
311
312
GDALRasterOverviewAlgorithmAddStandalone::
313
0
    ~GDALRasterOverviewAlgorithmAddStandalone() = default;
314
315
//! @endcond