Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: "edit" 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_edit.h"
14 :
15 : #include "gdal_priv.h"
16 : #include "gdal_utils.h"
17 : #include "ogrsf_frmts.h"
18 :
19 : #include <optional>
20 :
21 : //! @cond Doxygen_Suppress
22 :
23 : #ifndef _
24 : #define _(x) (x)
25 : #endif
26 :
27 : /************************************************************************/
28 : /* GetGCPFilename() */
29 : /************************************************************************/
30 :
31 29 : static std::string GetGCPFilename(const std::vector<std::string> &gcps)
32 : {
33 29 : if (gcps.size() == 1 && !gcps[0].empty() && gcps[0][0] == '@')
34 : {
35 12 : return gcps[0].substr(1);
36 : }
37 17 : return std::string();
38 : }
39 :
40 : /************************************************************************/
41 : /* GDALRasterEditAlgorithm::GDALRasterEditAlgorithm() */
42 : /************************************************************************/
43 :
44 208 : GDALRasterEditAlgorithm::GDALRasterEditAlgorithm(bool standaloneStep)
45 : : GDALRasterPipelineStepAlgorithm(
46 : NAME, DESCRIPTION, HELP_URL,
47 208 : ConstructorOptions().SetAddDefaultArguments(false))
48 : {
49 208 : if (standaloneStep)
50 : {
51 118 : AddProgressArg();
52 :
53 : AddArg("dataset", 0,
54 : _("Dataset (to be updated in-place, unless --auxiliary)"),
55 236 : &m_dataset, GDAL_OF_RASTER | GDAL_OF_UPDATE)
56 118 : .SetPositional()
57 118 : .SetRequired()
58 118 : .SetAvailableInPipelineStep(false);
59 118 : AddOpenOptionsArg(&m_openOptions).SetAvailableInPipelineStep(false);
60 : AddArg("auxiliary", 0,
61 : _("Ask for an auxiliary .aux.xml file to be edited"),
62 236 : &m_readOnly)
63 236 : .AddHiddenAlias("ro")
64 236 : .AddHiddenAlias(GDAL_ARG_NAME_READ_ONLY)
65 118 : .SetAvailableInPipelineStep(false);
66 : }
67 : else
68 : {
69 90 : AddRasterHiddenInputDatasetArg();
70 : }
71 :
72 416 : AddArg("crs", 0, _("Override CRS (without reprojection)"), &m_overrideCrs)
73 416 : .AddHiddenAlias("a_srs")
74 416 : .AddHiddenAlias("srs")
75 208 : .SetIsCRSArg(/*noneAllowed=*/true);
76 :
77 208 : AddBBOXArg(&m_bbox);
78 :
79 208 : AddNodataArg(&m_nodata, /* noneAllowed = */ true);
80 :
81 : AddArg("color-interpretation", 0, _("Set band color interpretation"),
82 416 : &m_colorInterpretation)
83 416 : .SetMetaVar("[all|<BAND>=]<COLOR-INTEPRETATION>")
84 : .SetAutoCompleteFunction(
85 5 : [this](const std::string &s)
86 : {
87 3 : std::vector<std::string> ret;
88 3 : int nValues = 0;
89 3 : const auto paeVals = GDALGetColorInterpretationList(&nValues);
90 3 : if (s.find('=') == std::string::npos)
91 : {
92 2 : ret.push_back("all=");
93 2 : if (auto poDS = m_dataset.GetDatasetRef())
94 : {
95 2 : for (int i = 0; i < poDS->GetRasterCount(); ++i)
96 1 : ret.push_back(std::to_string(i + 1).append("="));
97 : }
98 70 : for (int i = 0; i < nValues; ++i)
99 136 : ret.push_back(
100 68 : GDALGetColorInterpretationName(paeVals[i]));
101 : }
102 : else
103 : {
104 35 : for (int i = 0; i < nValues; ++i)
105 68 : ret.push_back(
106 34 : GDALGetColorInterpretationName(paeVals[i]));
107 : }
108 6 : return ret;
109 208 : });
110 :
111 : const auto ValidationActionScaleOffset =
112 44 : [this](const char *argName, const std::vector<std::string> &values)
113 : {
114 106 : for (const std::string &s : values)
115 : {
116 74 : bool valid = true;
117 74 : const auto nPos = s.find('=');
118 74 : if (nPos != std::string::npos)
119 : {
120 40 : if (CPLGetValueType(s.substr(0, nPos).c_str()) !=
121 58 : CPL_VALUE_INTEGER ||
122 38 : CPLGetValueType(s.substr(nPos + 1).c_str()) ==
123 : CPL_VALUE_STRING)
124 : {
125 4 : valid = false;
126 : }
127 : }
128 54 : else if (CPLGetValueType(s.c_str()) == CPL_VALUE_STRING)
129 : {
130 2 : valid = false;
131 : }
132 74 : if (!valid)
133 : {
134 6 : ReportError(CE_Failure, CPLE_IllegalArg,
135 : "Invalid value '%s' for '%s'", s.c_str(), argName);
136 6 : return false;
137 : }
138 : }
139 32 : return true;
140 208 : };
141 :
142 416 : AddArg("scale", 0, _("Override band scale factor"), &m_scale)
143 416 : .SetMetaVar("[<BAND>=]<SCALE>")
144 : .AddValidationAction(
145 19 : [this, ValidationActionScaleOffset]()
146 227 : { return ValidationActionScaleOffset("scale", m_scale); });
147 :
148 416 : AddArg("offset", 0, _("Override band offset constant"), &m_offset)
149 416 : .SetMetaVar("[<BAND>=]<OFFSET>")
150 : .AddValidationAction(
151 19 : [this, ValidationActionScaleOffset]()
152 227 : { return ValidationActionScaleOffset("offset", m_offset); });
153 :
154 : {
155 : auto &arg = AddArg("metadata", 0, _("Add/update dataset metadata item"),
156 416 : &m_metadata)
157 416 : .SetMetaVar("<KEY>=<VALUE>")
158 208 : .SetPackedValuesAllowed(false);
159 46 : arg.AddValidationAction([this, &arg]()
160 254 : { return ParseAndValidateKeyValue(arg); });
161 208 : arg.AddHiddenAlias("mo");
162 : }
163 :
164 : AddArg("unset-metadata", 0, _("Remove dataset metadata item(s)"),
165 416 : &m_unsetMetadata)
166 208 : .SetMetaVar("<KEY>");
167 :
168 : AddArg("unset-metadata-domain", 0, _("Remove dataset metadata domain(s)"),
169 416 : &m_unsetMetadataDomain)
170 208 : .SetMetaVar("<DOMAIN>");
171 :
172 : AddArg("gcp", 0,
173 : _("Add ground control point, formatted as "
174 : "pixel,line,easting,northing[,elevation], or @filename"),
175 416 : &m_gcps)
176 208 : .SetPackedValuesAllowed(false)
177 : .AddValidationAction(
178 36 : [this]()
179 : {
180 21 : if (GetGCPFilename(m_gcps).empty())
181 : {
182 28 : for (const std::string &gcp : m_gcps)
183 : {
184 : const CPLStringList aosTokens(
185 17 : CSLTokenizeString2(gcp.c_str(), ",", 0));
186 17 : if (aosTokens.size() != 4 && aosTokens.size() != 5)
187 : {
188 1 : ReportError(CE_Failure, CPLE_IllegalArg,
189 : "Bad format for %s", gcp.c_str());
190 1 : return false;
191 : }
192 85 : for (int i = 0; i < aosTokens.size(); ++i)
193 : {
194 70 : if (CPLGetValueType(aosTokens[i]) ==
195 : CPL_VALUE_STRING)
196 : {
197 1 : ReportError(CE_Failure, CPLE_IllegalArg,
198 : "Bad format for %s", gcp.c_str());
199 1 : return false;
200 : }
201 : }
202 : }
203 : }
204 19 : return true;
205 208 : });
206 :
207 208 : if (standaloneStep)
208 : {
209 236 : AddArg("stats", 0, _("Compute statistics, using all pixels"), &m_stats)
210 118 : .SetMutualExclusionGroup("stats");
211 : AddArg("approx-stats", 0,
212 : _("Compute statistics, using a subset of pixels"),
213 236 : &m_approxStats)
214 118 : .SetMutualExclusionGroup("stats");
215 118 : AddArg("hist", 0, _("Compute histogram"), &m_hist);
216 : }
217 208 : }
218 :
219 : /************************************************************************/
220 : /* GDALRasterEditAlgorithm::~GDALRasterEditAlgorithm() */
221 : /************************************************************************/
222 :
223 : GDALRasterEditAlgorithm::~GDALRasterEditAlgorithm() = default;
224 :
225 : /************************************************************************/
226 : /* ParseGCPs() */
227 : /************************************************************************/
228 :
229 8 : std::vector<gdal::GCP> GDALRasterEditAlgorithm::ParseGCPs() const
230 : {
231 8 : std::vector<gdal::GCP> ret;
232 16 : const std::string osGCPFilename = GetGCPFilename(m_gcps);
233 8 : if (!osGCPFilename.empty())
234 : {
235 : auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
236 4 : osGCPFilename.c_str(), GDAL_OF_VECTOR | GDAL_OF_VERBOSE_ERROR));
237 4 : if (!poDS)
238 1 : return ret;
239 3 : if (poDS->GetLayerCount() != 1)
240 : {
241 1 : ReportError(CE_Failure, CPLE_AppDefined,
242 : "GCPs can only be specified for single-layer datasets");
243 1 : return ret;
244 : }
245 2 : auto poLayer = poDS->GetLayer(0);
246 2 : const auto poLayerDefn = poLayer->GetLayerDefn();
247 2 : int nIdIdx = -1, nInfoIdx = -1, nColIdx = -1, nLineIdx = -1, nXIdx = -1,
248 2 : nYIdx = -1, nZIdx = -1;
249 :
250 : const struct
251 : {
252 : int &idx;
253 : const char *name;
254 : bool required;
255 2 : } aFields[] = {
256 : {nIdIdx, "id", false}, {nInfoIdx, "info", false},
257 : {nColIdx, "column", true}, {nLineIdx, "line", true},
258 : {nXIdx, "x", true}, {nYIdx, "y", true},
259 : {nZIdx, "z", false},
260 2 : };
261 :
262 11 : for (auto &field : aFields)
263 : {
264 10 : field.idx = poLayerDefn->GetFieldIndex(field.name);
265 10 : if (field.idx < 0 && field.required)
266 : {
267 3 : ReportError(CE_Failure, CPLE_AppDefined,
268 1 : "Field '%s' cannot be found in '%s'", field.name,
269 1 : poDS->GetDescription());
270 1 : return ret;
271 : }
272 : }
273 2 : for (auto &&poFeature : poLayer)
274 : {
275 2 : gdal::GCP gcp;
276 1 : if (nIdIdx >= 0)
277 1 : gcp.SetId(poFeature->GetFieldAsString(nIdIdx));
278 1 : if (nInfoIdx >= 0)
279 1 : gcp.SetInfo(poFeature->GetFieldAsString(nInfoIdx));
280 1 : gcp.Pixel() = poFeature->GetFieldAsDouble(nColIdx);
281 1 : gcp.Line() = poFeature->GetFieldAsDouble(nLineIdx);
282 1 : gcp.X() = poFeature->GetFieldAsDouble(nXIdx);
283 1 : gcp.Y() = poFeature->GetFieldAsDouble(nYIdx);
284 1 : if (nZIdx >= 0 && poFeature->IsFieldSetAndNotNull(nZIdx))
285 1 : gcp.Z() = poFeature->GetFieldAsDouble(nZIdx);
286 1 : ret.push_back(std::move(gcp));
287 : }
288 : }
289 : else
290 : {
291 10 : for (const std::string &gcpStr : m_gcps)
292 : {
293 : const CPLStringList aosTokens(
294 12 : CSLTokenizeString2(gcpStr.c_str(), ",", 0));
295 : // Verified by validation action
296 6 : CPLAssert(aosTokens.size() == 4 || aosTokens.size() == 5);
297 12 : gdal::GCP gcp;
298 6 : gcp.Pixel() = CPLAtof(aosTokens[0]);
299 6 : gcp.Line() = CPLAtof(aosTokens[1]);
300 6 : gcp.X() = CPLAtof(aosTokens[2]);
301 6 : gcp.Y() = CPLAtof(aosTokens[3]);
302 6 : if (aosTokens.size() == 5)
303 3 : gcp.Z() = CPLAtof(aosTokens[4]);
304 6 : ret.push_back(std::move(gcp));
305 : }
306 : }
307 5 : return ret;
308 : }
309 :
310 : /************************************************************************/
311 : /* GDALRasterEditAlgorithm::RunStep() */
312 : /************************************************************************/
313 :
314 79 : bool GDALRasterEditAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
315 : {
316 79 : GDALDataset *poDS = m_dataset.GetDatasetRef();
317 79 : if (poDS)
318 : {
319 : // Standalone mode
320 60 : if (poDS->GetAccess() != GA_Update && !m_readOnly)
321 : {
322 1 : ReportError(CE_Failure, CPLE_AppDefined,
323 : "Dataset should be opened in update mode unless "
324 : "--auxiliary is set");
325 1 : return false;
326 : }
327 : }
328 : else
329 : {
330 : // Pipeline mode
331 19 : const auto poSrcDS = m_inputDataset[0].GetDatasetRef();
332 19 : CPLAssert(poSrcDS);
333 19 : CPLAssert(m_outputDataset.GetName().empty());
334 19 : CPLAssert(!m_outputDataset.GetDatasetRef());
335 19 : auto poDriver = poSrcDS->GetDriver();
336 23 : if (poDriver && EQUAL(poDriver->GetDescription(), "VRT") &&
337 4 : poSrcDS->GetDescription()[0] == '\0')
338 : {
339 : // We can directly edit an anonymous input VRT file
340 : // and we actually need to do that since the generic code path
341 : // in the other branch will try to serialize and deserialize a XML
342 : // file pointing to an anonymous source.
343 4 : m_outputDataset.Set(poSrcDS);
344 : }
345 : else
346 : {
347 : // Create a in-memory VRT to avoid modifying the source dataset.
348 15 : CPLStringList aosOptions;
349 15 : aosOptions.push_back("-of");
350 15 : aosOptions.push_back("VRT");
351 : GDALTranslateOptions *psOptions =
352 15 : GDALTranslateOptionsNew(aosOptions.List(), nullptr);
353 15 : GDALDatasetH hSrcDS = GDALDataset::ToHandle(poSrcDS);
354 15 : auto poRetDS = GDALDataset::FromHandle(
355 : GDALTranslate("", hSrcDS, psOptions, nullptr));
356 15 : GDALTranslateOptionsFree(psOptions);
357 15 : m_outputDataset.Set(std::unique_ptr<GDALDataset>(poRetDS));
358 : }
359 19 : poDS = m_outputDataset.GetDatasetRef();
360 : }
361 :
362 78 : bool ret = poDS != nullptr;
363 :
364 78 : if (poDS)
365 : {
366 78 : if (m_overrideCrs == "null" || m_overrideCrs == "none")
367 : {
368 3 : if (poDS->SetSpatialRef(nullptr) != CE_None)
369 : {
370 1 : ReportError(CE_Failure, CPLE_AppDefined,
371 : "SetSpatialRef(%s) failed", m_overrideCrs.c_str());
372 32 : return false;
373 : }
374 : }
375 75 : else if (!m_overrideCrs.empty() && m_gcps.empty())
376 : {
377 4 : OGRSpatialReference oSRS;
378 4 : oSRS.SetFromUserInput(m_overrideCrs.c_str());
379 4 : oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
380 4 : if (poDS->SetSpatialRef(&oSRS) != CE_None)
381 : {
382 1 : ReportError(CE_Failure, CPLE_AppDefined,
383 : "SetSpatialRef(%s) failed", m_overrideCrs.c_str());
384 1 : return false;
385 : }
386 : }
387 :
388 76 : if (!m_bbox.empty())
389 : {
390 4 : if (poDS->GetRasterXSize() == 0 || poDS->GetRasterYSize() == 0)
391 : {
392 1 : ReportError(CE_Failure, CPLE_AppDefined,
393 : "Cannot set extent because one of dataset height "
394 : "or width is null");
395 2 : return false;
396 : }
397 3 : GDALGeoTransform gt;
398 3 : gt.xorig = m_bbox[0];
399 3 : gt.xscale = (m_bbox[2] - m_bbox[0]) / poDS->GetRasterXSize();
400 3 : gt.xrot = 0;
401 3 : gt.yorig = m_bbox[3];
402 3 : gt.yrot = 0;
403 3 : gt.yscale = -(m_bbox[3] - m_bbox[1]) / poDS->GetRasterYSize();
404 3 : if (poDS->SetGeoTransform(gt) != CE_None)
405 : {
406 1 : ReportError(CE_Failure, CPLE_AppDefined,
407 : "Setting extent failed");
408 1 : return false;
409 : }
410 : }
411 :
412 74 : if (!m_nodata.empty())
413 : {
414 18 : for (int i = 0; i < poDS->GetRasterCount(); ++i)
415 : {
416 10 : if (EQUAL(m_nodata.c_str(), "none"))
417 2 : poDS->GetRasterBand(i + 1)->DeleteNoDataValue();
418 : else
419 16 : poDS->GetRasterBand(i + 1)->SetNoDataValue(
420 8 : CPLAtof(m_nodata.c_str()));
421 : }
422 : }
423 :
424 74 : if (!m_colorInterpretation.empty())
425 : {
426 : const auto GetColorInterp =
427 28 : [this](const char *pszStr) -> std::optional<GDALColorInterp>
428 : {
429 25 : if (EQUAL(pszStr, "undefined"))
430 0 : return GCI_Undefined;
431 : const GDALColorInterp eInterp =
432 25 : GDALGetColorInterpretationByName(pszStr);
433 25 : if (eInterp != GCI_Undefined)
434 22 : return eInterp;
435 3 : ReportError(CE_Failure, CPLE_NotSupported,
436 : "Unsupported color interpretation: %s", pszStr);
437 3 : return {};
438 18 : };
439 :
440 18 : if (m_colorInterpretation.size() == 1 &&
441 20 : poDS->GetRasterCount() > 1 &&
442 2 : !cpl::starts_with(m_colorInterpretation[0], "all="))
443 : {
444 1 : ReportError(
445 : CE_Failure, CPLE_NotSupported,
446 : "With several bands, specify as many color interpretation "
447 : "as bands, one or many values of the form "
448 : "<band_number>=<color> or a single value all=<color>");
449 12 : return false;
450 : }
451 : else
452 : {
453 17 : int nBandIter = 0;
454 17 : bool bSyntaxAll = false;
455 17 : bool bSyntaxExplicitBand = false;
456 17 : bool bSyntaxImplicitBand = false;
457 39 : for (const std::string &token : m_colorInterpretation)
458 : {
459 : const CPLStringList aosTokens(
460 29 : CSLTokenizeString2(token.c_str(), "=", 0));
461 29 : if (aosTokens.size() == 2 && EQUAL(aosTokens[0], "all"))
462 : {
463 4 : bSyntaxAll = true;
464 4 : const auto eColorInterp = GetColorInterp(aosTokens[1]);
465 4 : if (!eColorInterp)
466 : {
467 1 : return false;
468 : }
469 : else
470 : {
471 10 : for (int i = 0; i < poDS->GetRasterCount(); ++i)
472 : {
473 7 : if (poDS->GetRasterBand(i + 1)
474 7 : ->SetColorInterpretation(
475 14 : *eColorInterp) != CE_None)
476 0 : return false;
477 : }
478 : }
479 : }
480 25 : else if (aosTokens.size() == 2)
481 : {
482 9 : bSyntaxExplicitBand = true;
483 9 : const int nBand = atoi(aosTokens[0]);
484 9 : if (nBand <= 0 || nBand > poDS->GetRasterCount())
485 : {
486 2 : ReportError(CE_Failure, CPLE_NotSupported,
487 : "Invalid band number '%s' in '%s'",
488 : aosTokens[0], token.c_str());
489 3 : return false;
490 : }
491 7 : const auto eColorInterp = GetColorInterp(aosTokens[1]);
492 7 : if (!eColorInterp)
493 : {
494 1 : return false;
495 : }
496 6 : else if (poDS->GetRasterBand(nBand)
497 6 : ->SetColorInterpretation(*eColorInterp) !=
498 : CE_None)
499 : {
500 0 : return false;
501 : }
502 : }
503 : else
504 : {
505 16 : bSyntaxImplicitBand = true;
506 16 : ++nBandIter;
507 16 : if (nBandIter > poDS->GetRasterCount())
508 : {
509 2 : ReportError(CE_Failure, CPLE_IllegalArg,
510 : "More color interpretation values "
511 : "specified than bands in the dataset");
512 3 : return false;
513 : }
514 14 : const auto eColorInterp = GetColorInterp(token.c_str());
515 14 : if (!eColorInterp)
516 : {
517 1 : return false;
518 : }
519 13 : else if (poDS->GetRasterBand(nBandIter)
520 13 : ->SetColorInterpretation(*eColorInterp) !=
521 : CE_None)
522 : {
523 0 : return false;
524 : }
525 : }
526 : }
527 20 : if ((bSyntaxAll ? 1 : 0) + (bSyntaxExplicitBand ? 1 : 0) +
528 10 : (bSyntaxImplicitBand ? 1 : 0) !=
529 : 1)
530 : {
531 3 : ReportError(CE_Failure, CPLE_IllegalArg,
532 : "Mix of different syntaxes to specify color "
533 : "interpretation");
534 3 : return false;
535 : }
536 7 : if (bSyntaxImplicitBand && nBandIter != poDS->GetRasterCount())
537 : {
538 1 : ReportError(CE_Failure, CPLE_IllegalArg,
539 : "Less color interpretation values specified "
540 : "than bands in the dataset");
541 1 : return false;
542 : }
543 : }
544 : }
545 :
546 : const auto ScaleOffsetSetterLambda =
547 16 : [this, poDS](const char *argName,
548 : const std::vector<std::string> &values,
549 84 : CPLErr (GDALRasterBand::*Setter)(double))
550 : {
551 16 : if (values.size() == 1 && values[0].find('=') == std::string::npos)
552 : {
553 2 : const double dfScale = CPLAtof(values[0].c_str());
554 8 : for (int i = 0; i < poDS->GetRasterCount(); ++i)
555 : {
556 6 : if ((poDS->GetRasterBand(i + 1)->*Setter)(dfScale) !=
557 : CE_None)
558 0 : return false;
559 : }
560 : }
561 : else
562 : {
563 14 : int nBandIter = 0;
564 14 : bool bSyntaxExplicitBand = false;
565 14 : bool bSyntaxImplicitBand = false;
566 40 : for (const std::string &token : values)
567 : {
568 : const CPLStringList aosTokens(
569 32 : CSLTokenizeString2(token.c_str(), "=", 0));
570 32 : if (aosTokens.size() == 2)
571 : {
572 8 : bSyntaxExplicitBand = true;
573 8 : const int nBand = atoi(aosTokens[0]);
574 8 : if (nBand <= 0 || nBand > poDS->GetRasterCount())
575 : {
576 4 : ReportError(CE_Failure, CPLE_NotSupported,
577 : "Invalid band number '%s' in '%s'",
578 : aosTokens[0], token.c_str());
579 4 : return false;
580 : }
581 4 : const double dfScale = CPLAtof(aosTokens[1]);
582 4 : if ((poDS->GetRasterBand(nBand)->*Setter)(dfScale) !=
583 : CE_None)
584 : {
585 0 : return false;
586 : }
587 : }
588 : else
589 : {
590 24 : bSyntaxImplicitBand = true;
591 24 : ++nBandIter;
592 24 : if (nBandIter > poDS->GetRasterCount())
593 : {
594 2 : ReportError(CE_Failure, CPLE_IllegalArg,
595 : "More %s values "
596 : "specified than bands in the dataset",
597 : argName);
598 2 : return false;
599 : }
600 22 : const double dfScale = CPLAtof(token.c_str());
601 22 : if ((poDS->GetRasterBand(nBandIter)->*Setter)(
602 22 : dfScale) != CE_None)
603 : {
604 0 : return false;
605 : }
606 : }
607 : }
608 16 : if (((bSyntaxExplicitBand ? 1 : 0) +
609 8 : (bSyntaxImplicitBand ? 1 : 0)) != 1)
610 : {
611 2 : ReportError(CE_Failure, CPLE_IllegalArg,
612 : "Mix of different syntaxes to specify %s",
613 : argName);
614 2 : return false;
615 : }
616 6 : if (bSyntaxImplicitBand && nBandIter != poDS->GetRasterCount())
617 : {
618 2 : ReportError(CE_Failure, CPLE_IllegalArg,
619 : "Less %s values specified "
620 : "than bands in the dataset",
621 : argName);
622 2 : return false;
623 : }
624 : }
625 :
626 6 : return true;
627 62 : };
628 :
629 62 : if (!m_scale.empty())
630 : {
631 8 : if (!ScaleOffsetSetterLambda("scale", m_scale,
632 : &GDALRasterBand::SetScale))
633 5 : return false;
634 : }
635 :
636 57 : if (!m_offset.empty())
637 : {
638 8 : if (!ScaleOffsetSetterLambda("offset", m_offset,
639 : &GDALRasterBand::SetOffset))
640 5 : return false;
641 : }
642 :
643 52 : const CPLStringList aosMD(m_metadata);
644 64 : for (const auto &[key, value] : cpl::IterateNameValue(aosMD))
645 : {
646 13 : if (poDS->SetMetadataItem(key, value) != CE_None)
647 : {
648 1 : ReportError(CE_Failure, CPLE_AppDefined,
649 : "SetMetadataItem('%s', '%s') failed", key, value);
650 1 : return false;
651 : }
652 : }
653 :
654 53 : for (const std::string &key : m_unsetMetadata)
655 : {
656 3 : if (poDS->SetMetadataItem(key.c_str(), nullptr) != CE_None)
657 : {
658 1 : ReportError(CE_Failure, CPLE_AppDefined,
659 : "SetMetadataItem('%s', NULL) failed", key.c_str());
660 1 : return false;
661 : }
662 : }
663 :
664 51 : for (const std::string &domain : m_unsetMetadataDomain)
665 : {
666 1 : if (poDS->SetMetadata(nullptr, domain.c_str()) != CE_None)
667 : {
668 0 : ReportError(CE_Failure, CPLE_AppDefined,
669 : "SetMetadata(NULL, '%s') failed", domain.c_str());
670 0 : return false;
671 : }
672 : }
673 :
674 50 : if (!m_gcps.empty())
675 : {
676 8 : const auto gcps = ParseGCPs();
677 8 : if (gcps.empty())
678 3 : return false; // error already emitted by ParseGCPs()
679 :
680 5 : OGRSpatialReference oSRS;
681 5 : if (!m_overrideCrs.empty())
682 : {
683 1 : oSRS.SetFromUserInput(m_overrideCrs.c_str());
684 1 : oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
685 : }
686 :
687 5 : if (poDS->SetGCPs(static_cast<int>(gcps.size()), gcps[0].c_ptr(),
688 10 : oSRS.IsEmpty() ? nullptr : &oSRS) != CE_None)
689 : {
690 1 : ReportError(CE_Failure, CPLE_AppDefined, "Setting GCPs failed");
691 1 : return false;
692 : }
693 : }
694 :
695 46 : const int nBands = poDS->GetRasterCount();
696 46 : int nCurProgress = 0;
697 46 : const double dfTotalProgress =
698 46 : ((m_stats || m_approxStats) ? nBands : 0) + (m_hist ? nBands : 0);
699 46 : if (m_stats || m_approxStats)
700 : {
701 4 : for (int i = 0; (i < nBands) && ret; ++i)
702 : {
703 4 : void *pScaledProgress = GDALCreateScaledProgress(
704 : nCurProgress / dfTotalProgress,
705 2 : (nCurProgress + 1) / dfTotalProgress, ctxt.m_pfnProgress,
706 : ctxt.m_pProgressData);
707 2 : ++nCurProgress;
708 2 : double dfMin = 0.0;
709 2 : double dfMax = 0.0;
710 2 : double dfMean = 0.0;
711 2 : double dfStdDev = 0.0;
712 4 : ret = poDS->GetRasterBand(i + 1)->ComputeStatistics(
713 2 : m_approxStats, &dfMin, &dfMax, &dfMean, &dfStdDev,
714 : pScaledProgress ? GDALScaledProgress : nullptr,
715 2 : pScaledProgress) == CE_None;
716 2 : GDALDestroyScaledProgress(pScaledProgress);
717 : }
718 : }
719 :
720 46 : if (m_hist)
721 : {
722 2 : for (int i = 0; (i < nBands) && ret; ++i)
723 : {
724 2 : void *pScaledProgress = GDALCreateScaledProgress(
725 : nCurProgress / dfTotalProgress,
726 1 : (nCurProgress + 1) / dfTotalProgress, ctxt.m_pfnProgress,
727 : ctxt.m_pProgressData);
728 1 : ++nCurProgress;
729 1 : double dfMin = 0.0;
730 1 : double dfMax = 0.0;
731 1 : int nBucketCount = 0;
732 1 : GUIntBig *panHistogram = nullptr;
733 2 : ret = poDS->GetRasterBand(i + 1)->GetDefaultHistogram(
734 : &dfMin, &dfMax, &nBucketCount, &panHistogram, TRUE,
735 : pScaledProgress ? GDALScaledProgress : nullptr,
736 1 : pScaledProgress) == CE_None;
737 1 : if (ret)
738 : {
739 2 : ret = poDS->GetRasterBand(i + 1)->SetDefaultHistogram(
740 1 : dfMin, dfMax, nBucketCount, panHistogram) ==
741 : CE_None;
742 : }
743 1 : CPLFree(panHistogram);
744 1 : GDALDestroyScaledProgress(pScaledProgress);
745 : }
746 : }
747 : }
748 :
749 46 : return poDS != nullptr;
750 : }
751 :
752 : GDALRasterEditAlgorithmStandalone::~GDALRasterEditAlgorithmStandalone() =
753 : default;
754 :
755 : //! @endcond
|