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