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