Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: "reproject" step of "raster pipeline"
5 : * Author: Even Rouault <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "gdalalg_raster_reproject.h"
14 : #include "gdalalg_raster_write.h"
15 :
16 : #include "gdal_alg.h"
17 : #include "gdal_priv.h"
18 : #include "gdal_utils.h"
19 : #include "gdalwarper.h"
20 :
21 : #include <cmath>
22 :
23 : //! @cond Doxygen_Suppress
24 :
25 : #ifndef _
26 : #define _(x) (x)
27 : #endif
28 :
29 : /************************************************************************/
30 : /* GDALRasterReprojectAlgorithm::GDALRasterReprojectAlgorithm() */
31 : /************************************************************************/
32 :
33 70 : GDALRasterReprojectAlgorithm::GDALRasterReprojectAlgorithm(bool standaloneStep)
34 : : GDALRasterPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
35 70 : standaloneStep)
36 : {
37 140 : AddArg("src-crs", 's', _("Source CRS"), &m_srsCrs)
38 140 : .SetIsCRSArg()
39 70 : .AddHiddenAlias("s_srs");
40 140 : AddArg("dst-crs", 'd', _("Destination CRS"), &m_dstCrs)
41 140 : .SetIsCRSArg()
42 70 : .AddHiddenAlias("t_srs");
43 :
44 70 : GDALRasterReprojectUtils::AddResamplingArg(this, m_resampling);
45 :
46 : AddArg("resolution", 0, _("Target resolution (in destination CRS units)"),
47 140 : &m_resolution)
48 70 : .SetMinCount(2)
49 70 : .SetMaxCount(2)
50 70 : .SetMinValueExcluded(0)
51 70 : .SetRepeatedArgAllowed(false)
52 70 : .SetDisplayHintAboutRepetition(false)
53 140 : .SetMetaVar("<xres>,<yres>")
54 70 : .SetMutualExclusionGroup("resolution-size");
55 :
56 140 : AddArg("size", 0, _("Target size in pixels"), &m_size)
57 70 : .SetMinCount(2)
58 70 : .SetMaxCount(2)
59 70 : .SetMinValueIncluded(0)
60 70 : .SetRepeatedArgAllowed(false)
61 70 : .SetDisplayHintAboutRepetition(false)
62 140 : .SetMetaVar("<width>,<height>")
63 70 : .SetMutualExclusionGroup("resolution-size");
64 :
65 70 : AddBBOXArg(&m_bbox, _("Target bounding box (in destination CRS units)"));
66 140 : AddArg("bbox-crs", 0, _("CRS of target bounding box"), &m_bboxCrs)
67 140 : .SetIsCRSArg()
68 70 : .AddHiddenAlias("bbox_srs");
69 :
70 : AddArg("target-aligned-pixels", 0,
71 : _("Round target extent to target resolution"),
72 140 : &m_targetAlignedPixels)
73 140 : .AddHiddenAlias("tap")
74 70 : .SetCategory(GAAC_ADVANCED);
75 : AddArg("src-nodata", 0,
76 : _("Set nodata values for input bands ('None' to unset)."),
77 140 : &m_srcNoData)
78 70 : .SetMinCount(1)
79 70 : .SetRepeatedArgAllowed(false)
80 70 : .SetCategory(GAAC_ADVANCED);
81 : AddArg("dst-nodata", 0,
82 : _("Set nodata values for output bands ('None' to unset)."),
83 140 : &m_dstNoData)
84 70 : .SetMinCount(1)
85 70 : .SetRepeatedArgAllowed(false)
86 70 : .SetCategory(GAAC_ADVANCED);
87 : AddArg("add-alpha", 0,
88 : _("Adds an alpha mask band to the destination when the source "
89 : "raster have none."),
90 140 : &m_addAlpha)
91 70 : .SetCategory(GAAC_ADVANCED);
92 :
93 70 : GDALRasterReprojectUtils::AddWarpOptTransformOptErrorThresholdArg(
94 70 : this, m_warpOptions, m_transformOptions, m_errorThreshold);
95 :
96 70 : AddNumThreadsArg(&m_numThreads, &m_numThreadsStr);
97 70 : }
98 :
99 : /************************************************************************/
100 : /* GDALRasterReprojectUtils::AddResamplingArg() */
101 : /************************************************************************/
102 :
103 : /*static */ void
104 82 : GDALRasterReprojectUtils::AddResamplingArg(GDALAlgorithm *alg,
105 : std::string &resampling)
106 : {
107 164 : alg->AddArg("resampling", 'r', _("Resampling method"), &resampling)
108 : .SetChoices("nearest", "bilinear", "cubic", "cubicspline", "lanczos",
109 : "average", "rms", "mode", "min", "max", "med", "q1", "q3",
110 82 : "sum")
111 82 : .SetDefault("nearest")
112 82 : .SetHiddenChoices("near");
113 82 : }
114 :
115 : /************************************************************************/
116 : /* AddWarpOptTransformOptErrorThresholdArg() */
117 : /************************************************************************/
118 :
119 : /* static */
120 82 : void GDALRasterReprojectUtils::AddWarpOptTransformOptErrorThresholdArg(
121 : GDALAlgorithm *alg, std::vector<std::string> &warpOptions,
122 : std::vector<std::string> &transformOptions, double &errorThreshold)
123 : {
124 : {
125 : auto &arg =
126 164 : alg->AddArg("warp-option", 0, _("Warping option(s)"), &warpOptions)
127 164 : .AddAlias("wo")
128 164 : .SetMetaVar("<NAME>=<VALUE>")
129 164 : .SetCategory(GAAC_ADVANCED)
130 82 : .SetPackedValuesAllowed(false);
131 4 : arg.AddValidationAction([alg, &arg]()
132 86 : { return alg->ParseAndValidateKeyValue(arg); });
133 : arg.SetAutoCompleteFunction(
134 0 : [](const std::string ¤tValue)
135 : {
136 0 : std::vector<std::string> ret;
137 0 : GDALAlgorithm::AddOptionsSuggestions(GDALWarpGetOptionList(), 0,
138 : currentValue, ret);
139 0 : return ret;
140 82 : });
141 : }
142 : {
143 : auto &arg = alg->AddArg("transform-option", 0, _("Transform option(s)"),
144 164 : &transformOptions)
145 164 : .AddAlias("to")
146 164 : .SetMetaVar("<NAME>=<VALUE>")
147 164 : .SetCategory(GAAC_ADVANCED)
148 82 : .SetPackedValuesAllowed(false);
149 2 : arg.AddValidationAction([alg, &arg]()
150 84 : { return alg->ParseAndValidateKeyValue(arg); });
151 : arg.SetAutoCompleteFunction(
152 0 : [](const std::string ¤tValue)
153 : {
154 0 : std::vector<std::string> ret;
155 0 : GDALAlgorithm::AddOptionsSuggestions(
156 : GDALGetGenImgProjTranformerOptionList(), 0, currentValue,
157 : ret);
158 0 : return ret;
159 82 : });
160 : }
161 164 : alg->AddArg("error-threshold", 0, _("Error threshold"), &errorThreshold)
162 164 : .AddAlias("et")
163 82 : .SetMinValueIncluded(0)
164 82 : .SetCategory(GAAC_ADVANCED);
165 82 : }
166 :
167 : /************************************************************************/
168 : /* GDALRasterReprojectAlgorithm::CanHandleNextStep() */
169 : /************************************************************************/
170 :
171 41 : bool GDALRasterReprojectAlgorithm::CanHandleNextStep(
172 : GDALPipelineStepAlgorithm *poNextStep) const
173 : {
174 80 : return poNextStep->GetName() == GDALRasterWriteAlgorithm::NAME &&
175 80 : poNextStep->GetOutputFormat() != "stream";
176 : }
177 :
178 : /************************************************************************/
179 : /* GDALRasterReprojectAlgorithm::RunStep() */
180 : /************************************************************************/
181 :
182 19 : bool GDALRasterReprojectAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
183 : {
184 19 : auto poSrcDS = m_inputDataset[0].GetDatasetRef();
185 19 : CPLAssert(poSrcDS);
186 19 : CPLAssert(m_outputDataset.GetName().empty());
187 19 : CPLAssert(!m_outputDataset.GetDatasetRef());
188 :
189 38 : CPLStringList aosOptions;
190 38 : std::string outputFilename;
191 19 : if (ctxt.m_poNextUsableStep)
192 : {
193 17 : CPLAssert(CanHandleNextStep(ctxt.m_poNextUsableStep));
194 17 : outputFilename = ctxt.m_poNextUsableStep->GetOutputDataset().GetName();
195 17 : const auto &format = ctxt.m_poNextUsableStep->GetOutputFormat();
196 17 : if (!format.empty())
197 : {
198 6 : aosOptions.AddString("-of");
199 6 : aosOptions.AddString(format.c_str());
200 : }
201 :
202 17 : bool bFoundNumThreads = false;
203 2 : for (const std::string &co :
204 21 : ctxt.m_poNextUsableStep->GetCreationOptions())
205 : {
206 2 : aosOptions.AddString("-co");
207 2 : if (STARTS_WITH_CI(co.c_str(), "NUM_THREADS="))
208 0 : bFoundNumThreads = true;
209 2 : aosOptions.AddString(co.c_str());
210 : }
211 :
212 : // Forward m_numThreads to GeoTIFF driver if --co NUM_THREADS not
213 : // specified
214 34 : if (!bFoundNumThreads && m_numThreads > 1 &&
215 34 : (EQUAL(format.c_str(), "GTIFF") || EQUAL(format.c_str(), "COG") ||
216 17 : (format.empty() &&
217 28 : EQUAL(CPLGetExtensionSafe(outputFilename.c_str()).c_str(),
218 : "tif"))))
219 : {
220 11 : aosOptions.AddString("-co");
221 11 : aosOptions.AddString(CPLSPrintf("NUM_THREADS=%d", m_numThreads));
222 : }
223 : }
224 : else
225 : {
226 2 : aosOptions.AddString("-of");
227 2 : aosOptions.AddString("VRT");
228 : }
229 19 : if (!m_srsCrs.empty())
230 : {
231 9 : aosOptions.AddString("-s_srs");
232 9 : aosOptions.AddString(m_srsCrs.c_str());
233 : }
234 19 : if (!m_dstCrs.empty())
235 : {
236 12 : aosOptions.AddString("-t_srs");
237 12 : aosOptions.AddString(m_dstCrs.c_str());
238 : }
239 19 : if (!m_resampling.empty())
240 : {
241 19 : aosOptions.AddString("-r");
242 19 : aosOptions.AddString(m_resampling.c_str());
243 : }
244 19 : if (!m_resolution.empty())
245 : {
246 1 : aosOptions.AddString("-tr");
247 1 : aosOptions.AddString(CPLSPrintf("%.17g", m_resolution[0]));
248 1 : aosOptions.AddString(CPLSPrintf("%.17g", m_resolution[1]));
249 : }
250 19 : if (!m_size.empty())
251 : {
252 1 : aosOptions.AddString("-ts");
253 1 : aosOptions.AddString(CPLSPrintf("%d", m_size[0]));
254 1 : aosOptions.AddString(CPLSPrintf("%d", m_size[1]));
255 : }
256 19 : if (!m_bbox.empty())
257 : {
258 2 : aosOptions.AddString("-te");
259 2 : aosOptions.AddString(CPLSPrintf("%.17g", m_bbox[0]));
260 2 : aosOptions.AddString(CPLSPrintf("%.17g", m_bbox[1]));
261 2 : aosOptions.AddString(CPLSPrintf("%.17g", m_bbox[2]));
262 2 : aosOptions.AddString(CPLSPrintf("%.17g", m_bbox[3]));
263 : }
264 19 : if (!m_bboxCrs.empty())
265 : {
266 1 : aosOptions.AddString("-te_srs");
267 1 : aosOptions.AddString(m_bboxCrs.c_str());
268 : }
269 19 : if (m_targetAlignedPixels)
270 : {
271 1 : aosOptions.AddString("-tap");
272 : }
273 19 : if (!m_srcNoData.empty())
274 : {
275 1 : aosOptions.push_back("-srcnodata");
276 2 : std::string s;
277 2 : for (const std::string &v : m_srcNoData)
278 : {
279 1 : if (!s.empty())
280 0 : s += " ";
281 1 : s += v;
282 : }
283 1 : aosOptions.push_back(s);
284 : }
285 19 : if (!m_dstNoData.empty())
286 : {
287 2 : aosOptions.push_back("-dstnodata");
288 4 : std::string s;
289 5 : for (const std::string &v : m_dstNoData)
290 : {
291 3 : if (!s.empty())
292 1 : s += " ";
293 3 : s += v;
294 : }
295 2 : aosOptions.push_back(s);
296 : }
297 19 : if (m_addAlpha)
298 : {
299 1 : aosOptions.AddString("-dstalpha");
300 : }
301 :
302 19 : bool bFoundNumThreads = false;
303 22 : for (const std::string &opt : m_warpOptions)
304 : {
305 3 : aosOptions.AddString("-wo");
306 3 : if (STARTS_WITH_CI(opt.c_str(), "NUM_THREADS="))
307 2 : bFoundNumThreads = true;
308 3 : aosOptions.AddString(opt.c_str());
309 : }
310 19 : if (bFoundNumThreads)
311 : {
312 2 : if (GetArg("num-threads")->IsExplicitlySet())
313 : {
314 1 : ReportError(CE_Failure, CPLE_AppDefined,
315 : "--num-threads argument and NUM_THREADS warp options "
316 : "are mutually exclusive.");
317 1 : return false;
318 : }
319 : }
320 : else
321 : {
322 17 : aosOptions.AddString("-wo");
323 17 : aosOptions.AddString(CPLSPrintf("NUM_THREADS=%d", m_numThreads));
324 : }
325 :
326 19 : for (const std::string &opt : m_transformOptions)
327 : {
328 1 : aosOptions.AddString("-to");
329 1 : aosOptions.AddString(opt.c_str());
330 : }
331 18 : if (std::isfinite(m_errorThreshold))
332 : {
333 0 : aosOptions.AddString("-et");
334 0 : aosOptions.AddString(CPLSPrintf("%.17g", m_errorThreshold));
335 : }
336 :
337 18 : bool bOK = false;
338 : GDALWarpAppOptions *psOptions =
339 18 : GDALWarpAppOptionsNew(aosOptions.List(), nullptr);
340 18 : if (psOptions)
341 : {
342 18 : if (ctxt.m_poNextUsableStep)
343 : {
344 16 : GDALWarpAppOptionsSetProgress(psOptions, ctxt.m_pfnProgress,
345 : ctxt.m_pProgressData);
346 : }
347 18 : GDALDatasetH hSrcDS = GDALDataset::ToHandle(poSrcDS);
348 18 : auto poRetDS = GDALDataset::FromHandle(GDALWarp(
349 : outputFilename.c_str(), nullptr, 1, &hSrcDS, psOptions, nullptr));
350 18 : GDALWarpAppOptionsFree(psOptions);
351 18 : bOK = poRetDS != nullptr;
352 18 : if (bOK)
353 : {
354 16 : m_outputDataset.Set(std::unique_ptr<GDALDataset>(poRetDS));
355 : }
356 : }
357 18 : return bOK;
358 : }
359 :
360 : GDALRasterReprojectAlgorithmStandalone::
361 : ~GDALRasterReprojectAlgorithmStandalone() = default;
362 :
363 : //! @endcond
|