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