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