/src/gdal/apps/gdalalg_raster_update.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: GDAL |
4 | | * Purpose: gdal "raster update" subcommand |
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_update.h" |
14 | | |
15 | | #include "cpl_conv.h" |
16 | | |
17 | | #include "gdal_priv.h" |
18 | | #include "gdal_utils.h" |
19 | | #include "gdalalg_raster_reproject.h" // for GDALRasterReprojectUtils |
20 | | #include "gdalalg_raster_overview_refresh.h" |
21 | | #include "ogr_spatialref.h" |
22 | | |
23 | | #include <algorithm> |
24 | | #include <array> |
25 | | #include <cmath> |
26 | | #include <tuple> |
27 | | |
28 | | //! @cond Doxygen_Suppress |
29 | | |
30 | | #ifndef _ |
31 | 0 | #define _(x) (x) |
32 | | #endif |
33 | | |
34 | | /************************************************************************/ |
35 | | /* GDALRasterUpdateAlgorithm::GDALRasterUpdateAlgorithm() */ |
36 | | /************************************************************************/ |
37 | | |
38 | | GDALRasterUpdateAlgorithm::GDALRasterUpdateAlgorithm(bool standaloneStep) |
39 | 0 | : GDALRasterPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL, |
40 | 0 | ConstructorOptions() |
41 | 0 | .SetStandaloneStep(standaloneStep) |
42 | 0 | .SetInputDatasetMaxCount(1) |
43 | 0 | .SetAddDefaultArguments(false)) |
44 | 0 | { |
45 | 0 | AddProgressArg(); |
46 | |
|
47 | 0 | if (standaloneStep) |
48 | 0 | { |
49 | 0 | AddRasterInputArgs(/* openForMixedRasterVector = */ false, |
50 | 0 | /* hiddenForCLI = */ false); |
51 | 0 | } |
52 | 0 | else |
53 | 0 | { |
54 | 0 | AddRasterHiddenInputDatasetArg(); |
55 | 0 | } |
56 | |
|
57 | 0 | AddOutputDatasetArg(&m_outputDataset, GDAL_OF_RASTER) |
58 | 0 | .SetDatasetInputFlags(GADV_NAME | GADV_OBJECT); |
59 | |
|
60 | 0 | m_update = true; |
61 | 0 | AddUpdateArg(&m_update).SetDefault(true).SetHidden(); |
62 | |
|
63 | 0 | AddArg("geometry", 0, _("Clipping geometry (WKT or GeoJSON)"), &m_geometry) |
64 | 0 | .SetMutualExclusionGroup("bbox-geometry-like"); |
65 | 0 | AddArg("geometry-crs", 0, _("CRS of clipping geometry"), &m_geometryCrs) |
66 | 0 | .SetIsCRSArg() |
67 | 0 | .AddHiddenAlias("geometry_srs"); |
68 | |
|
69 | 0 | GDALRasterReprojectUtils::AddResamplingArg(this, m_resampling); |
70 | |
|
71 | 0 | GDALRasterReprojectUtils::AddWarpOptTransformOptErrorThresholdArg( |
72 | 0 | this, m_warpOptions, m_transformOptions, m_errorThreshold); |
73 | |
|
74 | 0 | AddArg("no-update-overviews", 0, _("Do not update existing overviews"), |
75 | 0 | &m_noUpdateOverviews); |
76 | 0 | } |
77 | | |
78 | | /************************************************************************/ |
79 | | /* GDALRasterUpdateAlgorithm::RunStep() */ |
80 | | /************************************************************************/ |
81 | | |
82 | | bool GDALRasterUpdateAlgorithm::RunStep(GDALPipelineStepRunContext &stepCtxt) |
83 | 0 | { |
84 | 0 | auto poSrcDS = m_inputDataset[0].GetDatasetRef(); |
85 | 0 | CPLAssert(poSrcDS); |
86 | | |
87 | 0 | auto poDstDS = m_outputDataset.GetDatasetRef(); |
88 | 0 | CPLAssert(poDstDS); |
89 | 0 | CPLAssert(poDstDS->GetAccess() == GA_Update); |
90 | | |
91 | 0 | std::unique_ptr<OGRGeometry> poClipGeom; |
92 | 0 | std::string errMsg; |
93 | 0 | if (!m_geometry.empty()) |
94 | 0 | { |
95 | 0 | std::tie(poClipGeom, errMsg) = GetClipGeometry(); |
96 | 0 | if (!poClipGeom) |
97 | 0 | { |
98 | 0 | ReportError(CE_Failure, CPLE_AppDefined, "%s", errMsg.c_str()); |
99 | 0 | return false; |
100 | 0 | } |
101 | 0 | } |
102 | | |
103 | 0 | auto poSrcDriver = poSrcDS->GetDriver(); |
104 | 0 | auto poDstDriver = poDstDS->GetDriver(); |
105 | 0 | if (poSrcDS == poDstDS || |
106 | 0 | (poSrcDriver && poDstDriver && |
107 | 0 | !EQUAL(poSrcDriver->GetDescription(), "MEM") && |
108 | 0 | !EQUAL(poDstDriver->GetDescription(), "MEM") && |
109 | 0 | strcmp(poSrcDS->GetDescription(), poDstDS->GetDescription()) == 0)) |
110 | 0 | { |
111 | 0 | ReportError(CE_Failure, CPLE_NotSupported, |
112 | 0 | "Source and destination datasets must be different"); |
113 | 0 | return false; |
114 | 0 | } |
115 | | |
116 | 0 | CPLStringList aosOptions; |
117 | 0 | if (!m_resampling.empty()) |
118 | 0 | { |
119 | 0 | aosOptions.AddString("-r"); |
120 | 0 | aosOptions.AddString(m_resampling.c_str()); |
121 | 0 | } |
122 | 0 | for (const std::string &opt : m_warpOptions) |
123 | 0 | { |
124 | 0 | aosOptions.AddString("-wo"); |
125 | 0 | aosOptions.AddString(opt.c_str()); |
126 | 0 | } |
127 | 0 | for (const std::string &opt : m_transformOptions) |
128 | 0 | { |
129 | 0 | aosOptions.AddString("-to"); |
130 | 0 | aosOptions.AddString(opt.c_str()); |
131 | 0 | } |
132 | 0 | if (std::isfinite(m_errorThreshold)) |
133 | 0 | { |
134 | 0 | aosOptions.AddString("-et"); |
135 | 0 | aosOptions.AddString(CPLSPrintf("%.17g", m_errorThreshold)); |
136 | 0 | } |
137 | |
|
138 | 0 | if (poClipGeom) |
139 | 0 | { |
140 | 0 | aosOptions.AddString("-cutline"); |
141 | 0 | aosOptions.AddString(poClipGeom->exportToWkt()); |
142 | 0 | } |
143 | |
|
144 | 0 | bool bOvrCanBeUpdated = false; |
145 | 0 | std::vector<double> overviewRefreshBBox; |
146 | 0 | if (poDstDS->GetRasterBand(1)->GetOverviewCount() > 0 && |
147 | 0 | !m_noUpdateOverviews) |
148 | 0 | { |
149 | 0 | GDALGeoTransform gt; |
150 | 0 | const auto poSrcCRS = poSrcDS->GetSpatialRef(); |
151 | 0 | const auto poDstCRS = poDstDS->GetSpatialRef(); |
152 | 0 | const bool bBothCRS = poSrcCRS && poDstCRS; |
153 | 0 | const bool bBothNoCRS = !poSrcCRS && !poDstCRS; |
154 | 0 | if ((bBothCRS || bBothNoCRS) && poSrcDS->GetGeoTransform(gt) == CE_None) |
155 | 0 | { |
156 | 0 | auto poCT = std::unique_ptr<OGRCoordinateTransformation>( |
157 | 0 | bBothCRS ? OGRCreateCoordinateTransformation(poSrcCRS, poDstCRS) |
158 | 0 | : nullptr); |
159 | 0 | if (bBothNoCRS || poCT) |
160 | 0 | { |
161 | 0 | const double dfTLX = gt.xorig; |
162 | 0 | const double dfTLY = gt.yorig; |
163 | |
|
164 | 0 | double dfTRX = 0; |
165 | 0 | double dfTRY = 0; |
166 | 0 | gt.Apply(poSrcDS->GetRasterXSize(), 0, &dfTRX, &dfTRY); |
167 | |
|
168 | 0 | double dfBLX = 0; |
169 | 0 | double dfBLY = 0; |
170 | 0 | gt.Apply(0, poSrcDS->GetRasterYSize(), &dfBLX, &dfBLY); |
171 | |
|
172 | 0 | double dfBRX = 0; |
173 | 0 | double dfBRY = 0; |
174 | 0 | gt.Apply(poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize(), |
175 | 0 | &dfBRX, &dfBRY); |
176 | |
|
177 | 0 | const double dfXMin = |
178 | 0 | std::min(std::min(dfTLX, dfTRX), std::min(dfBLX, dfBRX)); |
179 | 0 | const double dfYMin = |
180 | 0 | std::min(std::min(dfTLY, dfTRY), std::min(dfBLY, dfBRY)); |
181 | 0 | const double dfXMax = |
182 | 0 | std::max(std::max(dfTLX, dfTRX), std::max(dfBLX, dfBRX)); |
183 | 0 | const double dfYMax = |
184 | 0 | std::max(std::max(dfTLY, dfTRY), std::max(dfBLY, dfBRY)); |
185 | 0 | double dfOutXMin = dfXMin; |
186 | 0 | double dfOutYMin = dfYMin; |
187 | 0 | double dfOutXMax = dfXMax; |
188 | 0 | double dfOutYMax = dfYMax; |
189 | 0 | if (!poCT || poCT->TransformBounds( |
190 | 0 | dfXMin, dfYMin, dfXMax, dfYMax, &dfOutXMin, |
191 | 0 | &dfOutYMin, &dfOutXMax, &dfOutYMax, 21)) |
192 | 0 | { |
193 | 0 | bOvrCanBeUpdated = true; |
194 | 0 | CPLDebug("update", |
195 | 0 | "Refresh overviews from (%f,%f) to (%f,%f)", |
196 | 0 | dfOutXMin, dfOutYMin, dfOutXMax, dfOutYMax); |
197 | 0 | overviewRefreshBBox = std::vector<double>{ |
198 | 0 | dfOutXMin, dfOutYMin, dfOutXMax, dfOutYMax}; |
199 | 0 | } |
200 | 0 | } |
201 | 0 | } |
202 | 0 | if (!bOvrCanBeUpdated) |
203 | 0 | { |
204 | 0 | ReportError(CE_Warning, CPLE_AppDefined, |
205 | 0 | "Overviews can not be updated"); |
206 | 0 | } |
207 | 0 | } |
208 | |
|
209 | 0 | bool bOK = false; |
210 | 0 | GDALWarpAppOptions *psOptions = |
211 | 0 | GDALWarpAppOptionsNew(aosOptions.List(), nullptr); |
212 | 0 | if (psOptions) |
213 | 0 | { |
214 | 0 | std::unique_ptr<void, decltype(&GDALDestroyScaledProgress)> pScaledData( |
215 | 0 | nullptr, GDALDestroyScaledProgress); |
216 | 0 | auto pfnProgress = stepCtxt.m_pfnProgress; |
217 | 0 | void *pProgressData = stepCtxt.m_pProgressData; |
218 | 0 | if (pfnProgress) |
219 | 0 | { |
220 | 0 | pScaledData.reset( |
221 | 0 | GDALCreateScaledProgress(0.0, bOvrCanBeUpdated ? 0.75 : 1.0, |
222 | 0 | pfnProgress, pProgressData)); |
223 | 0 | GDALWarpAppOptionsSetProgress(psOptions, GDALScaledProgress, |
224 | 0 | pScaledData.get()); |
225 | 0 | } |
226 | |
|
227 | 0 | GDALDatasetH hSrcDS = GDALDataset::ToHandle(poSrcDS); |
228 | 0 | GDALDatasetH hDstDS = GDALDataset::ToHandle(poDstDS); |
229 | 0 | auto poRetDS = GDALDataset::FromHandle( |
230 | 0 | GDALWarp(nullptr, hDstDS, 1, &hSrcDS, psOptions, nullptr)); |
231 | 0 | GDALWarpAppOptionsFree(psOptions); |
232 | |
|
233 | 0 | bOK = poRetDS != nullptr; |
234 | 0 | if (bOK && bOvrCanBeUpdated) |
235 | 0 | { |
236 | 0 | GDALRasterOverviewAlgorithmRefresh refresh; |
237 | 0 | refresh.GetArg("dataset")->Set(poRetDS); |
238 | 0 | if (!m_resampling.empty()) |
239 | 0 | refresh.GetArg("resampling")->Set(m_resampling); |
240 | 0 | refresh.GetArg("bbox")->Set(overviewRefreshBBox); |
241 | 0 | pScaledData.reset(GDALCreateScaledProgress(0.75, 1.0, pfnProgress, |
242 | 0 | pProgressData)); |
243 | 0 | bOK = refresh.Run(pScaledData ? GDALScaledProgress : nullptr, |
244 | 0 | pScaledData.get()); |
245 | 0 | } |
246 | 0 | if (bOK && pfnProgress) |
247 | 0 | pfnProgress(1.0, "", pProgressData); |
248 | 0 | } |
249 | |
|
250 | 0 | return bOK; |
251 | 0 | } |
252 | | |
253 | | /************************************************************************/ |
254 | | /* GDALRasterUpdateAlgorithm::RunImpl() */ |
255 | | /************************************************************************/ |
256 | | |
257 | | bool GDALRasterUpdateAlgorithm::RunImpl(GDALProgressFunc pfnProgress, |
258 | | void *pProgressData) |
259 | 0 | { |
260 | 0 | GDALPipelineStepRunContext stepCtxt; |
261 | 0 | stepCtxt.m_pfnProgress = pfnProgress; |
262 | 0 | stepCtxt.m_pProgressData = pProgressData; |
263 | 0 | return RunStep(stepCtxt); |
264 | 0 | } |
265 | | |
266 | 0 | GDALRasterUpdateAlgorithmStandalone::~GDALRasterUpdateAlgorithmStandalone() = |
267 | | default; |
268 | | |
269 | | //! @endcond |