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