Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: Common code of "raster mosaic" and "raster stack"
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_mosaic_stack_common.h"
14 : #include "gdalalg_raster_write.h"
15 :
16 : #include "cpl_conv.h"
17 : #include "cpl_vsi_virtual.h"
18 :
19 : #include "gdal_priv.h"
20 : #include "gdal_utils.h"
21 :
22 : //! @cond Doxygen_Suppress
23 :
24 : #ifndef _
25 : #define _(x) (x)
26 : #endif
27 :
28 : /************************************************************************/
29 : /* GetConstructorOptions() */
30 : /************************************************************************/
31 :
32 : /* static */ GDALRasterMosaicStackCommonAlgorithm::ConstructorOptions
33 149 : GDALRasterMosaicStackCommonAlgorithm::GetConstructorOptions(bool standaloneStep)
34 : {
35 149 : ConstructorOptions opts;
36 149 : opts.SetStandaloneStep(standaloneStep);
37 149 : opts.SetAutoOpenInputDatasets(false);
38 : opts.SetInputDatasetHelpMsg(
39 : _("Input raster datasets (or specify a @<filename> to point to a "
40 149 : "file containing filenames)"));
41 149 : opts.SetAddDefaultArguments(false);
42 149 : opts.SetInputDatasetMaxCount(INT_MAX);
43 149 : return opts;
44 : }
45 :
46 : /************************************************************************/
47 : /* GDALRasterMosaicStackCommonAlgorithm() */
48 : /************************************************************************/
49 :
50 149 : GDALRasterMosaicStackCommonAlgorithm::GDALRasterMosaicStackCommonAlgorithm(
51 : const std::string &name, const std::string &description,
52 149 : const std::string &helpURL, bool bStandalone)
53 : : GDALRasterPipelineStepAlgorithm(name, description, helpURL,
54 149 : GetConstructorOptions(bStandalone))
55 : {
56 149 : AddRasterInputArgs(/* openForMixedRasterVector = */ false,
57 : /* hiddenForCLI = */ false);
58 149 : if (bStandalone)
59 : {
60 62 : AddProgressArg();
61 62 : AddRasterOutputArgs(false);
62 : }
63 :
64 149 : AddBandArg(&m_bands);
65 : AddAbsolutePathArg(
66 : &m_writeAbsolutePaths,
67 : _("Whether the path to the input datasets should be stored as an "
68 149 : "absolute path"));
69 :
70 : auto &resArg =
71 : AddArg("resolution", 0,
72 298 : _("Target resolution (in destination CRS units)"), &m_resolution)
73 149 : .SetDefault("same")
74 149 : .SetMetaVar("<xres>,<yres>|same|average|common|highest|lowest");
75 : resArg.AddValidationAction(
76 27 : [this, &resArg]()
77 : {
78 48 : const std::string val = resArg.Get<std::string>();
79 45 : if (val != "average" && val != "highest" && val != "lowest" &&
80 45 : val != "same" && val != "common")
81 : {
82 : const auto aosTokens =
83 9 : CPLStringList(CSLTokenizeString2(val.c_str(), ",", 0));
84 9 : if (aosTokens.size() != 2 ||
85 7 : CPLGetValueType(aosTokens[0]) == CPL_VALUE_STRING ||
86 7 : CPLGetValueType(aosTokens[1]) == CPL_VALUE_STRING ||
87 16 : CPLAtof(aosTokens[0]) <= 0 || CPLAtof(aosTokens[1]) <= 0)
88 : {
89 3 : ReportError(CE_Failure, CPLE_AppDefined,
90 : "resolution: two comma separated positive "
91 : "values should be provided, or 'same', "
92 : "'average', 'common', 'highest' or 'lowest'");
93 3 : return false;
94 : }
95 : }
96 21 : return true;
97 149 : });
98 :
99 : AddBBOXArg(&m_bbox, _("Target bounding box as xmin,ymin,xmax,ymax (in "
100 149 : "destination CRS units)"));
101 : auto &tapArg = AddArg("target-aligned-pixels", 0,
102 : _("Round target extent to target resolution"),
103 298 : &m_targetAlignedPixels)
104 149 : .AddHiddenAlias("tap");
105 : AddArg("src-nodata", 0, _("Set nodata values for input bands."),
106 298 : &m_srcNoData)
107 149 : .SetMinCount(1)
108 149 : .SetRepeatedArgAllowed(false);
109 : AddArg("dst-nodata", 0,
110 298 : _("Set nodata values at the destination band level."), &m_dstNoData)
111 149 : .SetMinCount(1)
112 149 : .SetRepeatedArgAllowed(false);
113 : AddArg("hide-nodata", 0,
114 : _("Makes the destination band not report the NoData."),
115 149 : &m_hideNoData);
116 :
117 149 : AddValidationAction(
118 114 : [this, &resArg, &tapArg]()
119 : {
120 110 : if (tapArg.IsExplicitlySet() && !resArg.IsExplicitlySet())
121 : {
122 1 : ReportError(
123 : CE_Failure, CPLE_IllegalArg,
124 : "Argument 'target-aligned-pixels' can only be specified if "
125 : "argument 'resolution' is also specified.");
126 1 : return false;
127 : }
128 109 : return true;
129 : });
130 149 : }
131 :
132 : /************************************************************************/
133 : /* GDALRasterMosaicStackCommonAlgorithm::GetInputDatasetNames() */
134 : /************************************************************************/
135 :
136 46 : bool GDALRasterMosaicStackCommonAlgorithm::GetInputDatasetNames(
137 : GDALPipelineStepRunContext &ctxt,
138 : std::vector<GDALDatasetH> &ahInputDatasets,
139 : CPLStringList &aosInputDatasetNames, bool &foundByName)
140 : {
141 46 : bool foundByRef = false;
142 104 : for (auto &ds : m_inputDataset)
143 : {
144 60 : if (ds.GetDatasetRef())
145 : {
146 30 : foundByRef = true;
147 30 : ahInputDatasets.push_back(
148 30 : GDALDataset::ToHandle(ds.GetDatasetRef()));
149 : }
150 30 : else if (!ds.GetName().empty())
151 : {
152 30 : foundByName = true;
153 30 : if (ds.GetName()[0] == '@')
154 : {
155 : auto f = VSIVirtualHandleUniquePtr(
156 3 : VSIFOpenL(ds.GetName().c_str() + 1, "r"));
157 3 : if (!f)
158 : {
159 2 : ReportError(CE_Failure, CPLE_FileIO, "Cannot open %s",
160 2 : ds.GetName().c_str() + 1);
161 2 : return false;
162 : }
163 2 : while (const char *filename = CPLReadLineL(f.get()))
164 : {
165 1 : aosInputDatasetNames.push_back(filename);
166 1 : }
167 : }
168 27 : else if (ds.GetName().find_first_of("*?[") != std::string::npos)
169 : {
170 1 : CPLStringList aosMatches(VSIGlob(ds.GetName().c_str(), nullptr,
171 : ctxt.m_pfnProgress,
172 2 : ctxt.m_pProgressData));
173 2 : for (const char *pszStr : aosMatches)
174 : {
175 1 : aosInputDatasetNames.push_back(pszStr);
176 : }
177 : }
178 : else
179 : {
180 52 : std::string osDatasetName = ds.GetName();
181 26 : if (!GetReferencePathForRelativePaths().empty())
182 : {
183 0 : osDatasetName = GDALDataset::BuildFilename(
184 : osDatasetName.c_str(),
185 0 : GetReferencePathForRelativePaths().c_str(), true);
186 : }
187 26 : aosInputDatasetNames.push_back(osDatasetName.c_str());
188 : }
189 : }
190 : }
191 44 : if (foundByName && foundByRef)
192 : {
193 0 : ReportError(CE_Failure, CPLE_NotSupported,
194 : "Input datasets should be provided either all by reference "
195 : "or all by name");
196 0 : return false;
197 : }
198 :
199 44 : return true;
200 : }
201 :
202 : /************************************************************************/
203 : /* GDALRasterMosaicStackCommonAlgorithm::SetBuildVRTOptions() */
204 : /************************************************************************/
205 :
206 44 : void GDALRasterMosaicStackCommonAlgorithm::SetBuildVRTOptions(
207 : CPLStringList &aosOptions)
208 : {
209 : const auto aosTokens =
210 88 : CPLStringList(CSLTokenizeString2(m_resolution.c_str(), ",", 0));
211 44 : if (aosTokens.size() == 2)
212 : {
213 2 : aosOptions.push_back("-tr");
214 2 : aosOptions.push_back(aosTokens[0]);
215 2 : aosOptions.push_back(aosTokens[1]);
216 : }
217 : else
218 : {
219 42 : aosOptions.push_back("-resolution");
220 42 : aosOptions.push_back(m_resolution);
221 : }
222 :
223 44 : if (!m_bbox.empty())
224 : {
225 1 : aosOptions.push_back("-te");
226 1 : aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[0]));
227 1 : aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[1]));
228 1 : aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[2]));
229 1 : aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[3]));
230 : }
231 44 : if (m_targetAlignedPixels)
232 : {
233 1 : aosOptions.push_back("-tap");
234 : }
235 44 : if (!m_srcNoData.empty())
236 : {
237 2 : aosOptions.push_back("-srcnodata");
238 4 : std::string s;
239 4 : for (double v : m_srcNoData)
240 : {
241 2 : if (!s.empty())
242 0 : s += " ";
243 2 : s += CPLSPrintf("%.17g", v);
244 : }
245 2 : aosOptions.push_back(s);
246 : }
247 44 : if (!m_dstNoData.empty())
248 : {
249 4 : aosOptions.push_back("-vrtnodata");
250 8 : std::string s;
251 8 : for (double v : m_dstNoData)
252 : {
253 4 : if (!s.empty())
254 0 : s += " ";
255 4 : s += CPLSPrintf("%.17g", v);
256 : }
257 4 : aosOptions.push_back(s);
258 : }
259 46 : for (const int b : m_bands)
260 : {
261 2 : aosOptions.push_back("-b");
262 2 : aosOptions.push_back(CPLSPrintf("%d", b));
263 : }
264 44 : if (m_hideNoData)
265 : {
266 1 : aosOptions.push_back("-hidenodata");
267 : }
268 44 : if (m_writeAbsolutePaths)
269 : {
270 2 : aosOptions.push_back("-write_absolute_path");
271 : }
272 44 : }
273 :
274 : /************************************************************************/
275 : /* GDALRasterMosaicStackCommonAlgorithm::RunImpl() */
276 : /************************************************************************/
277 :
278 62 : bool GDALRasterMosaicStackCommonAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
279 : void *pProgressData)
280 : {
281 62 : if (m_standaloneStep)
282 : {
283 31 : GDALRasterWriteAlgorithm writeAlg;
284 341 : for (auto &arg : writeAlg.GetArgs())
285 : {
286 310 : if (!arg->IsHidden())
287 : {
288 248 : auto stepArg = GetArg(arg->GetName());
289 248 : if (stepArg && stepArg->IsExplicitlySet())
290 : {
291 59 : arg->SetSkipIfAlreadySet(true);
292 59 : arg->SetFrom(*stepArg);
293 : }
294 : }
295 : }
296 :
297 : // Already checked by GDALAlgorithm::Run()
298 31 : CPLAssert(!m_executionForStreamOutput ||
299 : EQUAL(m_format.c_str(), "stream"));
300 :
301 31 : m_standaloneStep = false;
302 31 : bool ret = Run(pfnProgress, pProgressData);
303 31 : m_standaloneStep = true;
304 31 : if (ret)
305 : {
306 27 : if (m_format == "stream")
307 : {
308 19 : ret = true;
309 : }
310 : else
311 : {
312 8 : writeAlg.m_inputDataset.clear();
313 8 : writeAlg.m_inputDataset.resize(1);
314 8 : writeAlg.m_inputDataset[0].Set(m_outputDataset.GetDatasetRef());
315 8 : if (writeAlg.Run(pfnProgress, pProgressData))
316 : {
317 8 : m_outputDataset.Set(
318 : writeAlg.m_outputDataset.GetDatasetRef());
319 8 : ret = true;
320 : }
321 : }
322 : }
323 :
324 31 : return ret;
325 : }
326 : else
327 : {
328 31 : GDALPipelineStepRunContext stepCtxt;
329 31 : stepCtxt.m_pfnProgress = pfnProgress;
330 31 : stepCtxt.m_pProgressData = pProgressData;
331 31 : return RunStep(stepCtxt);
332 : }
333 : }
334 :
335 : //! @endcond
|