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 :
18 : //! @cond Doxygen_Suppress
19 :
20 : #ifndef _
21 : #define _(x) (x)
22 : #endif
23 :
24 : /************************************************************************/
25 : /* GDALRasterEditAlgorithm::GDALRasterEditAlgorithm() */
26 : /************************************************************************/
27 :
28 35 : GDALRasterEditAlgorithm::GDALRasterEditAlgorithm(bool standaloneStep)
29 : : GDALRasterPipelineStepAlgorithm(
30 : NAME, DESCRIPTION, HELP_URL,
31 : // Avoid automatic addition of input/output arguments
32 35 : /*standaloneStep = */ false)
33 : {
34 35 : if (standaloneStep)
35 : {
36 21 : AddProgressArg();
37 :
38 : AddArg("dataset", 0,
39 : _("Dataset (to be updated in-place, unless --auxiliary)"),
40 42 : &m_dataset, GDAL_OF_RASTER | GDAL_OF_UPDATE)
41 21 : .SetPositional()
42 21 : .SetRequired();
43 : AddArg("auxiliary", 0,
44 : _("Ask for an auxiliary .aux.xml file to be edited"),
45 42 : &m_readOnly)
46 42 : .AddHiddenAlias("ro")
47 21 : .AddHiddenAlias(GDAL_ARG_NAME_READ_ONLY);
48 :
49 21 : m_standaloneStep = true;
50 : }
51 :
52 70 : AddArg("crs", 0, _("Override CRS (without reprojection)"), &m_overrideCrs)
53 70 : .AddHiddenAlias("a_srs")
54 35 : .SetIsCRSArg(/*noneAllowed=*/true);
55 :
56 35 : AddBBOXArg(&m_bbox);
57 :
58 35 : AddNodataDataTypeArg(&m_nodata, /* noneAllowed = */ true);
59 :
60 : {
61 : auto &arg = AddArg("metadata", 0, _("Add/update dataset metadata item"),
62 70 : &m_metadata)
63 70 : .SetMetaVar("<KEY>=<VALUE>")
64 35 : .SetPackedValuesAllowed(false);
65 3 : arg.AddValidationAction([this, &arg]()
66 38 : { return ParseAndValidateKeyValue(arg); });
67 35 : arg.AddHiddenAlias("mo");
68 : }
69 :
70 : AddArg("unset-metadata", 0, _("Remove dataset metadata item"),
71 70 : &m_unsetMetadata)
72 35 : .SetMetaVar("<KEY>");
73 :
74 35 : if (standaloneStep)
75 : {
76 42 : AddArg("stats", 0, _("Compute statistics, using all pixels"), &m_stats)
77 21 : .SetMutualExclusionGroup("stats");
78 : AddArg("approx-stats", 0,
79 : _("Compute statistics, using a subset of pixels"),
80 42 : &m_approxStats)
81 21 : .SetMutualExclusionGroup("stats");
82 21 : AddArg("hist", 0, _("Compute histogram"), &m_hist);
83 : }
84 35 : }
85 :
86 : /************************************************************************/
87 : /* GDALRasterEditAlgorithm::RunImpl() */
88 : /************************************************************************/
89 :
90 24 : bool GDALRasterEditAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
91 : void *pProgressData)
92 : {
93 24 : if (m_standaloneStep)
94 : {
95 17 : auto poDS = m_dataset.GetDatasetRef();
96 17 : CPLAssert(poDS);
97 17 : if (poDS->GetAccess() != GA_Update && !m_readOnly)
98 : {
99 1 : ReportError(CE_Failure, CPLE_AppDefined,
100 : "Dataset should be opened in update mode unless "
101 : "--auxiliary is set");
102 1 : return false;
103 : }
104 :
105 16 : if (m_overrideCrs == "null" || m_overrideCrs == "none")
106 : {
107 2 : if (poDS->SetSpatialRef(nullptr) != CE_None)
108 : {
109 1 : ReportError(CE_Failure, CPLE_AppDefined,
110 : "SetSpatialRef(%s) failed", m_overrideCrs.c_str());
111 1 : return false;
112 : }
113 : }
114 14 : else if (!m_overrideCrs.empty())
115 : {
116 2 : OGRSpatialReference oSRS;
117 2 : oSRS.SetFromUserInput(m_overrideCrs.c_str());
118 2 : oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
119 2 : if (poDS->SetSpatialRef(&oSRS) != CE_None)
120 : {
121 1 : ReportError(CE_Failure, CPLE_AppDefined,
122 : "SetSpatialRef(%s) failed", m_overrideCrs.c_str());
123 1 : return false;
124 : }
125 : }
126 :
127 14 : if (!m_bbox.empty())
128 : {
129 3 : if (poDS->GetRasterXSize() == 0 || poDS->GetRasterYSize() == 0)
130 : {
131 1 : ReportError(CE_Failure, CPLE_AppDefined,
132 : "Cannot set extent because one of dataset height "
133 : "or width is null");
134 2 : return false;
135 : }
136 : double adfGT[6];
137 2 : adfGT[0] = m_bbox[0];
138 2 : adfGT[1] = (m_bbox[2] - m_bbox[0]) / poDS->GetRasterXSize();
139 2 : adfGT[2] = 0;
140 2 : adfGT[3] = m_bbox[3];
141 2 : adfGT[4] = 0;
142 2 : adfGT[5] = -(m_bbox[3] - m_bbox[1]) / poDS->GetRasterYSize();
143 2 : if (poDS->SetGeoTransform(adfGT) != CE_None)
144 : {
145 1 : ReportError(CE_Failure, CPLE_AppDefined,
146 : "Setting extent failed");
147 1 : return false;
148 : }
149 : }
150 :
151 12 : if (!m_nodata.empty())
152 : {
153 4 : for (int i = 0; i < poDS->GetRasterCount(); ++i)
154 : {
155 2 : if (EQUAL(m_nodata.c_str(), "none"))
156 1 : poDS->GetRasterBand(i + 1)->DeleteNoDataValue();
157 : else
158 2 : poDS->GetRasterBand(i + 1)->SetNoDataValue(
159 1 : CPLAtof(m_nodata.c_str()));
160 : }
161 : }
162 :
163 24 : const CPLStringList aosMD(m_metadata);
164 14 : for (const auto &[key, value] : cpl::IterateNameValue(aosMD))
165 : {
166 3 : if (poDS->SetMetadataItem(key, value) != CE_None)
167 : {
168 1 : ReportError(CE_Failure, CPLE_AppDefined,
169 : "SetMetadataItem('%s', '%s') failed", key, value);
170 1 : return false;
171 : }
172 : }
173 :
174 12 : for (const std::string &key : m_unsetMetadata)
175 : {
176 2 : if (poDS->SetMetadataItem(key.c_str(), nullptr) != CE_None)
177 : {
178 1 : ReportError(CE_Failure, CPLE_AppDefined,
179 : "SetMetadataItem('%s', NULL) failed", key.c_str());
180 1 : return false;
181 : }
182 : }
183 :
184 10 : const int nBands = poDS->GetRasterCount();
185 10 : int nCurProgress = 0;
186 10 : const double dfTotalProgress =
187 10 : ((m_stats || m_approxStats) ? nBands : 0) + (m_hist ? nBands : 0);
188 10 : bool ret = true;
189 10 : if (m_stats || m_approxStats)
190 : {
191 4 : for (int i = 0; (i < nBands) && ret; ++i)
192 : {
193 4 : void *pScaledProgress = GDALCreateScaledProgress(
194 : nCurProgress / dfTotalProgress,
195 2 : (nCurProgress + 1) / dfTotalProgress, pfnProgress,
196 : pProgressData);
197 2 : ++nCurProgress;
198 2 : double dfMin = 0.0;
199 2 : double dfMax = 0.0;
200 2 : double dfMean = 0.0;
201 2 : double dfStdDev = 0.0;
202 2 : ret = poDS->GetRasterBand(i + 1)->ComputeStatistics(
203 2 : m_approxStats, &dfMin, &dfMax, &dfMean, &dfStdDev,
204 2 : GDALScaledProgress, pScaledProgress) == CE_None;
205 2 : GDALDestroyScaledProgress(pScaledProgress);
206 : }
207 : }
208 10 : if (m_hist)
209 : {
210 2 : for (int i = 0; (i < nBands) && ret; ++i)
211 : {
212 2 : void *pScaledProgress = GDALCreateScaledProgress(
213 : nCurProgress / dfTotalProgress,
214 1 : (nCurProgress + 1) / dfTotalProgress, pfnProgress,
215 : pProgressData);
216 1 : ++nCurProgress;
217 1 : double dfMin = 0.0;
218 1 : double dfMax = 0.0;
219 1 : int nBucketCount = 0;
220 1 : GUIntBig *panHistogram = nullptr;
221 1 : ret = poDS->GetRasterBand(i + 1)->GetDefaultHistogram(
222 : &dfMin, &dfMax, &nBucketCount, &panHistogram, TRUE,
223 1 : GDALScaledProgress, pScaledProgress) == CE_None;
224 1 : if (ret)
225 : {
226 2 : ret = poDS->GetRasterBand(i + 1)->SetDefaultHistogram(
227 1 : dfMin, dfMax, nBucketCount, panHistogram) ==
228 : CE_None;
229 : }
230 1 : CPLFree(panHistogram);
231 1 : GDALDestroyScaledProgress(pScaledProgress);
232 : }
233 : }
234 :
235 10 : return ret;
236 : }
237 : else
238 : {
239 7 : return RunStep(pfnProgress, pProgressData);
240 : }
241 : }
242 :
243 : /************************************************************************/
244 : /* GDALRasterEditAlgorithm::RunStep() */
245 : /************************************************************************/
246 :
247 7 : bool GDALRasterEditAlgorithm::RunStep(GDALProgressFunc, void *)
248 : {
249 7 : CPLAssert(m_inputDataset.GetDatasetRef());
250 7 : CPLAssert(m_outputDataset.GetName().empty());
251 7 : CPLAssert(!m_outputDataset.GetDatasetRef());
252 :
253 7 : CPLStringList aosOptions;
254 7 : aosOptions.AddString("-of");
255 7 : aosOptions.AddString("VRT");
256 7 : if (!m_overrideCrs.empty())
257 : {
258 2 : aosOptions.AddString("-a_srs");
259 2 : aosOptions.AddString(m_overrideCrs.c_str());
260 : }
261 7 : if (!m_bbox.empty())
262 : {
263 1 : aosOptions.AddString("-a_ullr");
264 1 : aosOptions.AddString(CPLSPrintf("%.17g", m_bbox[0])); // upper-left X
265 1 : aosOptions.AddString(CPLSPrintf("%.17g", m_bbox[3])); // upper-left Y
266 1 : aosOptions.AddString(CPLSPrintf("%.17g", m_bbox[2])); // lower-right X
267 1 : aosOptions.AddString(CPLSPrintf("%.17g", m_bbox[1])); // lower-right Y
268 : }
269 :
270 9 : for (const auto &val : m_metadata)
271 : {
272 2 : aosOptions.AddString("-mo");
273 2 : aosOptions.AddString(val.c_str());
274 : }
275 :
276 8 : for (const std::string &key : m_unsetMetadata)
277 : {
278 1 : aosOptions.AddString("-mo");
279 1 : aosOptions.AddString((key + "=").c_str());
280 : }
281 :
282 7 : if (!m_nodata.empty())
283 : {
284 2 : aosOptions.AddString("-a_nodata");
285 2 : aosOptions.AddString(m_nodata);
286 : }
287 :
288 : GDALTranslateOptions *psOptions =
289 7 : GDALTranslateOptionsNew(aosOptions.List(), nullptr);
290 :
291 7 : GDALDatasetH hSrcDS = GDALDataset::ToHandle(m_inputDataset.GetDatasetRef());
292 : auto poRetDS =
293 7 : GDALDataset::FromHandle(GDALTranslate("", hSrcDS, psOptions, nullptr));
294 7 : GDALTranslateOptionsFree(psOptions);
295 7 : const bool ok = poRetDS != nullptr;
296 7 : if (ok)
297 7 : m_outputDataset.Set(std::unique_ptr<GDALDataset>(poRetDS));
298 :
299 14 : return ok;
300 : }
301 :
302 : //! @endcond
|