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