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