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 235 : GDALRasterMosaicStackCommonAlgorithm::GetConstructorOptions(bool standaloneStep)
34 : {
35 235 : ConstructorOptions opts;
36 235 : opts.SetStandaloneStep(standaloneStep);
37 235 : opts.SetAutoOpenInputDatasets(false);
38 : opts.SetInputDatasetHelpMsg(
39 : _("Input raster datasets (or specify a @<filename> to point to a "
40 235 : "file containing filenames)"));
41 235 : opts.SetAddDefaultArguments(false);
42 235 : opts.SetInputDatasetMetaVar("INPUTS");
43 235 : opts.SetInputDatasetMaxCount(INT_MAX);
44 235 : return opts;
45 : }
46 :
47 : /************************************************************************/
48 : /* GDALRasterMosaicStackCommonAlgorithm() */
49 : /************************************************************************/
50 :
51 235 : GDALRasterMosaicStackCommonAlgorithm::GDALRasterMosaicStackCommonAlgorithm(
52 : const std::string &name, const std::string &description,
53 235 : const std::string &helpURL, bool bStandalone)
54 : : GDALRasterPipelineStepAlgorithm(name, description, helpURL,
55 235 : GetConstructorOptions(bStandalone))
56 : {
57 235 : AddRasterInputArgs(/* openForMixedRasterVector = */ false,
58 : /* hiddenForCLI = */ false);
59 235 : if (bStandalone)
60 : {
61 145 : AddProgressArg();
62 145 : AddRasterOutputArgs(false);
63 : }
64 :
65 235 : AddBandArg(&m_bands);
66 : AddAbsolutePathArg(
67 : &m_writeAbsolutePaths,
68 : _("Whether the path to the input datasets should be stored as an "
69 235 : "absolute path"));
70 :
71 : auto &resArg =
72 : AddArg("resolution", 0,
73 470 : _("Target resolution (in destination CRS units)"), &m_resolution)
74 235 : .SetDefault("same")
75 235 : .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 235 : });
99 :
100 : AddBBOXArg(&m_bbox, _("Target bounding box as xmin,ymin,xmax,ymax (in "
101 235 : "destination CRS units)"));
102 : auto &tapArg = AddArg("target-aligned-pixels", 0,
103 : _("Round target extent to target resolution"),
104 470 : &m_targetAlignedPixels)
105 235 : .AddHiddenAlias("tap");
106 : AddArg("input-nodata", 0, _("Set nodata values for input bands."),
107 470 : &m_srcNoData)
108 235 : .SetMinCount(1)
109 470 : .AddHiddenAlias("src-nodata")
110 235 : .SetRepeatedArgAllowed(false);
111 : AddArg("output-nodata", 0,
112 470 : _("Set nodata values at the destination band level."), &m_dstNoData)
113 235 : .SetMinCount(1)
114 470 : .AddHiddenAlias("dst-nodata")
115 235 : .SetRepeatedArgAllowed(false);
116 : AddArg("hide-nodata", 0,
117 : _("Makes the destination band not report the NoData."),
118 235 : &m_hideNoData);
119 :
120 235 : AddValidationAction(
121 134 : [this, &resArg, &tapArg]()
122 : {
123 130 : 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 129 : return true;
132 : });
133 235 : }
134 :
135 : /************************************************************************/
136 : /* GDALRasterMosaicStackCommonAlgorithm::GetInputDatasetNames() */
137 : /************************************************************************/
138 :
139 56 : bool GDALRasterMosaicStackCommonAlgorithm::GetInputDatasetNames(
140 : GDALPipelineStepRunContext &ctxt,
141 : std::vector<GDALDatasetH> &ahInputDatasets,
142 : CPLStringList &aosInputDatasetNames, bool &foundByName)
143 : {
144 56 : bool foundByRef = false;
145 134 : for (auto &ds : m_inputDataset)
146 : {
147 80 : if (ds.GetDatasetRef())
148 : {
149 49 : foundByRef = true;
150 49 : ahInputDatasets.push_back(
151 49 : GDALDataset::ToHandle(ds.GetDatasetRef()));
152 : }
153 31 : else if (!ds.GetName().empty())
154 : {
155 31 : foundByName = true;
156 31 : 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 28 : 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 54 : std::string osDatasetName = ds.GetName();
184 27 : if (!GetReferencePathForRelativePaths().empty())
185 : {
186 0 : osDatasetName = GDALDataset::BuildFilename(
187 : osDatasetName.c_str(),
188 0 : GetReferencePathForRelativePaths().c_str(), true);
189 : }
190 27 : aosInputDatasetNames.push_back(osDatasetName.c_str());
191 : }
192 : }
193 : }
194 54 : 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 54 : return true;
203 : }
204 :
205 : /************************************************************************/
206 : /* GDALRasterMosaicStackCommonAlgorithm::SetBuildVRTOptions() */
207 : /************************************************************************/
208 :
209 54 : void GDALRasterMosaicStackCommonAlgorithm::SetBuildVRTOptions(
210 : CPLStringList &aosOptions)
211 : {
212 : const auto aosTokens =
213 108 : CPLStringList(CSLTokenizeString2(m_resolution.c_str(), ",", 0));
214 54 : 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 52 : aosOptions.push_back("-resolution");
223 52 : aosOptions.push_back(m_resolution);
224 : }
225 :
226 54 : 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 54 : if (m_targetAlignedPixels)
235 : {
236 1 : aosOptions.push_back("-tap");
237 : }
238 54 : 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 54 : if (!m_dstNoData.empty())
251 : {
252 5 : aosOptions.push_back("-vrtnodata");
253 10 : std::string s;
254 10 : for (double v : m_dstNoData)
255 : {
256 5 : if (!s.empty())
257 0 : s += " ";
258 5 : s += CPLSPrintf("%.17g", v);
259 : }
260 5 : aosOptions.push_back(s);
261 : }
262 56 : for (const int b : m_bands)
263 : {
264 2 : aosOptions.push_back("-b");
265 2 : aosOptions.push_back(CPLSPrintf("%d", b));
266 : }
267 54 : if (m_hideNoData)
268 : {
269 1 : aosOptions.push_back("-hidenodata");
270 : }
271 54 : if (m_writeAbsolutePaths)
272 : {
273 2 : aosOptions.push_back("-write_absolute_path");
274 : }
275 54 : }
276 :
277 : /************************************************************************/
278 : /* GDALRasterMosaicStackCommonAlgorithm::RunImpl() */
279 : /************************************************************************/
280 :
281 80 : bool GDALRasterMosaicStackCommonAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
282 : void *pProgressData)
283 : {
284 80 : if (m_standaloneStep)
285 : {
286 80 : GDALRasterWriteAlgorithm writeAlg;
287 440 : for (auto &arg : writeAlg.GetArgs())
288 : {
289 400 : if (!arg->IsHidden())
290 : {
291 320 : auto stepArg = GetArg(arg->GetName());
292 320 : if (stepArg && stepArg->IsExplicitlySet())
293 : {
294 76 : arg->SetSkipIfAlreadySet(true);
295 76 : arg->SetFrom(*stepArg);
296 : }
297 : }
298 : }
299 :
300 : // Already checked by GDALAlgorithm::Run()
301 40 : CPLAssert(!m_executionForStreamOutput ||
302 : EQUAL(m_format.c_str(), "stream"));
303 :
304 40 : m_standaloneStep = false;
305 40 : m_alreadyRun = false;
306 40 : const bool ok = Run(pfnProgress, pProgressData);
307 40 : m_standaloneStep = true;
308 :
309 40 : if (!ok)
310 : {
311 10 : return false;
312 : }
313 :
314 30 : if (m_format == "stream")
315 : {
316 21 : return true;
317 : }
318 :
319 18 : std::vector<GDALArgDatasetValue> inputDataset(1);
320 9 : inputDataset[0].Set(m_outputDataset.GetDatasetRef());
321 9 : auto inputArg = writeAlg.GetArg(GDAL_ARG_NAME_INPUT);
322 9 : CPLAssert(inputArg);
323 9 : inputArg->Set(std::move(inputDataset));
324 9 : if (!writeAlg.Run(pfnProgress, pProgressData))
325 : {
326 1 : return false;
327 : }
328 :
329 8 : m_outputDataset.Set(writeAlg.m_outputDataset.GetDatasetRef());
330 8 : return true;
331 : }
332 : else
333 : {
334 40 : GDALPipelineStepRunContext stepCtxt;
335 40 : stepCtxt.m_pfnProgress = pfnProgress;
336 40 : stepCtxt.m_pProgressData = pProgressData;
337 40 : return RunStep(stepCtxt);
338 : }
339 : }
340 :
341 : //! @endcond
|