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