/src/gdal/apps/gdalalg_raster_overview_refresh.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: GDAL |
4 | | * Purpose: gdal "raster overview refresh" 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_overview_refresh.h" |
14 | | |
15 | | #include "cpl_string.h" |
16 | | #include "gdal_priv.h" |
17 | | #include "vrtdataset.h" |
18 | | #include "vrt_priv.h" |
19 | | |
20 | | #include <algorithm> |
21 | | #include <limits> |
22 | | |
23 | | //! @cond Doxygen_Suppress |
24 | | |
25 | | #ifndef _ |
26 | 0 | #define _(x) (x) |
27 | | #endif |
28 | | |
29 | | /************************************************************************/ |
30 | | /* GDALRasterOverviewAlgorithmRefresh() */ |
31 | | /************************************************************************/ |
32 | | |
33 | | GDALRasterOverviewAlgorithmRefresh::GDALRasterOverviewAlgorithmRefresh() |
34 | 0 | : GDALAlgorithm(NAME, DESCRIPTION, HELP_URL) |
35 | 0 | { |
36 | 0 | AddProgressArg(); |
37 | 0 | AddOpenOptionsArg(&m_openOptions); |
38 | 0 | AddArg("dataset", 0, |
39 | 0 | _("Dataset (to be updated in-place, unless --external)"), &m_dataset, |
40 | 0 | GDAL_OF_RASTER | GDAL_OF_UPDATE) |
41 | 0 | .SetPositional() |
42 | 0 | .SetRequired(); |
43 | 0 | AddArg("external", 0, _("Refresh external overviews"), &m_readOnly) |
44 | 0 | .AddHiddenAlias("ro") |
45 | 0 | .AddHiddenAlias(GDAL_ARG_NAME_READ_ONLY); |
46 | |
|
47 | 0 | AddArg("resampling", 'r', _("Resampling method"), &m_resampling) |
48 | 0 | .SetChoices("nearest", "average", "cubic", "cubicspline", "lanczos", |
49 | 0 | "bilinear", "gauss", "average_magphase", "rms", "mode") |
50 | 0 | .SetHiddenChoices("near", "none"); |
51 | |
|
52 | 0 | AddArg("levels", 0, _("Levels / decimation factors"), &m_levels) |
53 | 0 | .SetMinValueIncluded(2); |
54 | |
|
55 | 0 | AddBBOXArg(&m_refreshBbox, _("Bounding box to refresh")) |
56 | 0 | .SetMutualExclusionGroup("refresh"); |
57 | 0 | AddArg("like", 0, _("Use extent of dataset(s)"), &m_like) |
58 | 0 | .SetMutualExclusionGroup("refresh"); |
59 | 0 | AddArg("use-source-timestamp", 0, |
60 | 0 | _("Use timestamp of VRT or GTI sources as refresh criterion"), |
61 | 0 | &m_refreshFromSourceTimestamp) |
62 | 0 | .SetMutualExclusionGroup("refresh"); |
63 | 0 | } |
64 | | |
65 | | /************************************************************************/ |
66 | | /* PartialRefresh() */ |
67 | | /************************************************************************/ |
68 | | |
69 | | static bool PartialRefresh(GDALDataset *poDS, |
70 | | const std::vector<int> &anOvrIndices, |
71 | | const char *pszResampling, int nXOff, int nYOff, |
72 | | int nXSize, int nYSize, GDALProgressFunc pfnProgress, |
73 | | void *pProgressArg) |
74 | 0 | { |
75 | 0 | int nOvCount = 0; |
76 | 0 | const int nBandCount = poDS->GetRasterCount(); |
77 | 0 | for (int i = 0; i < nBandCount; ++i) |
78 | 0 | { |
79 | 0 | auto poSrcBand = poDS->GetRasterBand(i + 1); |
80 | 0 | if (i == 0) |
81 | 0 | nOvCount = poSrcBand->GetOverviewCount(); |
82 | 0 | else if (nOvCount != poSrcBand->GetOverviewCount()) |
83 | 0 | { |
84 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
85 | 0 | "Not same number of overviews on all bands"); |
86 | 0 | return false; |
87 | 0 | } |
88 | 0 | } |
89 | | |
90 | 0 | std::vector<GDALRasterBand *> apoSrcBands; |
91 | 0 | std::vector<std::vector<GDALRasterBand *>> aapoOverviewBands; |
92 | 0 | for (int i = 0; i < nBandCount; ++i) |
93 | 0 | { |
94 | 0 | auto poSrcBand = poDS->GetRasterBand(i + 1); |
95 | 0 | apoSrcBands.push_back(poSrcBand); |
96 | 0 | std::vector<GDALRasterBand *> apoOverviewBands; |
97 | 0 | for (int nOvrIdx : anOvrIndices) |
98 | 0 | { |
99 | 0 | apoOverviewBands.push_back(poSrcBand->GetOverview(nOvrIdx)); |
100 | 0 | } |
101 | 0 | aapoOverviewBands.push_back(std::move(apoOverviewBands)); |
102 | 0 | } |
103 | |
|
104 | 0 | CPLStringList aosOptions; |
105 | 0 | aosOptions.SetNameValue("XOFF", CPLSPrintf("%d", nXOff)); |
106 | 0 | aosOptions.SetNameValue("YOFF", CPLSPrintf("%d", nYOff)); |
107 | 0 | aosOptions.SetNameValue("XSIZE", CPLSPrintf("%d", nXSize)); |
108 | 0 | aosOptions.SetNameValue("YSIZE", CPLSPrintf("%d", nYSize)); |
109 | 0 | return GDALRegenerateOverviewsMultiBand( |
110 | 0 | apoSrcBands, aapoOverviewBands, pszResampling, pfnProgress, |
111 | 0 | pProgressArg, aosOptions.List()) == CE_None; |
112 | 0 | } |
113 | | |
114 | | /************************************************************************/ |
115 | | /* PartialRefreshFromSourceTimestamp() */ |
116 | | /************************************************************************/ |
117 | | |
118 | | static bool |
119 | | PartialRefreshFromSourceTimestamp(GDALDataset *poDS, const char *pszResampling, |
120 | | const std::vector<int> &anOvrIndices, |
121 | | GDALProgressFunc pfnProgress, |
122 | | void *pProgressArg) |
123 | 0 | { |
124 | 0 | VSIStatBufL sStatOvr; |
125 | 0 | std::string osOvr(std::string(poDS->GetDescription()) + ".ovr"); |
126 | 0 | if (VSIStatL(osOvr.c_str(), &sStatOvr) != 0) |
127 | 0 | { |
128 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s", osOvr.c_str()); |
129 | 0 | return false; |
130 | 0 | } |
131 | 0 | if (sStatOvr.st_mtime == 0) |
132 | 0 | { |
133 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
134 | 0 | "Cannot get modification time of %s", osOvr.c_str()); |
135 | 0 | return false; |
136 | 0 | } |
137 | | |
138 | 0 | std::vector<GTISourceDesc> regions; |
139 | | |
140 | | // init slightly above zero to please Coverity Scan |
141 | 0 | double dfTotalPixels = std::numeric_limits<double>::min(); |
142 | |
|
143 | 0 | if (dynamic_cast<VRTDataset *>(poDS)) |
144 | 0 | { |
145 | 0 | auto poVRTBand = |
146 | 0 | dynamic_cast<VRTSourcedRasterBand *>(poDS->GetRasterBand(1)); |
147 | 0 | if (!poVRTBand) |
148 | 0 | { |
149 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
150 | 0 | "Band is not a VRTSourcedRasterBand"); |
151 | 0 | return false; |
152 | 0 | } |
153 | | |
154 | 0 | for (auto &poSource : poVRTBand->m_papoSources) |
155 | 0 | { |
156 | 0 | auto poSimpleSource = |
157 | 0 | dynamic_cast<VRTSimpleSource *>(poSource.get()); |
158 | 0 | if (poSimpleSource) |
159 | 0 | { |
160 | 0 | VSIStatBufL sStatSource; |
161 | 0 | if (VSIStatL(poSimpleSource->GetSourceDatasetName().c_str(), |
162 | 0 | &sStatSource) == 0) |
163 | 0 | { |
164 | 0 | if (sStatSource.st_mtime > sStatOvr.st_mtime) |
165 | 0 | { |
166 | 0 | double dfXOff, dfYOff, dfXSize, dfYSize; |
167 | 0 | poSimpleSource->GetDstWindow(dfXOff, dfYOff, dfXSize, |
168 | 0 | dfYSize); |
169 | 0 | constexpr double EPS = 1e-8; |
170 | 0 | int nXOff = static_cast<int>(dfXOff + EPS); |
171 | 0 | int nYOff = static_cast<int>(dfYOff + EPS); |
172 | 0 | int nXSize = static_cast<int>(dfXSize + 0.5); |
173 | 0 | int nYSize = static_cast<int>(dfYSize + 0.5); |
174 | 0 | if (!(nXOff > poDS->GetRasterXSize() || |
175 | 0 | nYOff > poDS->GetRasterYSize() || nXSize <= 0 || |
176 | 0 | nYSize <= 0)) |
177 | 0 | { |
178 | 0 | if (nXOff < 0) |
179 | 0 | { |
180 | 0 | nXSize += nXOff; |
181 | 0 | nXOff = 0; |
182 | 0 | } |
183 | 0 | if (nXOff > poDS->GetRasterXSize() - nXSize) |
184 | 0 | { |
185 | 0 | nXSize = poDS->GetRasterXSize() - nXOff; |
186 | 0 | } |
187 | 0 | if (nYOff < 0) |
188 | 0 | { |
189 | 0 | nYSize += nYOff; |
190 | 0 | nYOff = 0; |
191 | 0 | } |
192 | 0 | if (nYOff > poDS->GetRasterYSize() - nYSize) |
193 | 0 | { |
194 | 0 | nYSize = poDS->GetRasterYSize() - nYOff; |
195 | 0 | } |
196 | |
|
197 | 0 | dfTotalPixels += |
198 | 0 | static_cast<double>(nXSize) * nYSize; |
199 | 0 | GTISourceDesc region; |
200 | 0 | region.osFilename = |
201 | 0 | poSimpleSource->GetSourceDatasetName(); |
202 | 0 | region.nDstXOff = nXOff; |
203 | 0 | region.nDstYOff = nYOff; |
204 | 0 | region.nDstXSize = nXSize; |
205 | 0 | region.nDstYSize = nYSize; |
206 | 0 | regions.push_back(std::move(region)); |
207 | 0 | } |
208 | 0 | } |
209 | 0 | } |
210 | 0 | } |
211 | 0 | } |
212 | 0 | } |
213 | 0 | #ifdef GTI_DRIVER_DISABLED_OR_PLUGIN |
214 | 0 | else if (poDS->GetDriver() && |
215 | 0 | EQUAL(poDS->GetDriver()->GetDescription(), "GTI")) |
216 | 0 | { |
217 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
218 | 0 | "--use-source-timestamp only works on a GTI " |
219 | 0 | "dataset if the GTI driver is not built as a plugin, " |
220 | 0 | "but in core library"); |
221 | 0 | return false; |
222 | 0 | } |
223 | | #else |
224 | | else if (auto poGTIDS = GDALDatasetCastToGTIDataset(poDS)) |
225 | | { |
226 | | regions = GTIGetSourcesMoreRecentThan(poGTIDS, sStatOvr.st_mtime); |
227 | | for (const auto ®ion : regions) |
228 | | { |
229 | | dfTotalPixels += |
230 | | static_cast<double>(region.nDstXSize) * region.nDstYSize; |
231 | | } |
232 | | } |
233 | | #endif |
234 | 0 | else |
235 | 0 | { |
236 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
237 | 0 | "--use-source-timestamp only works on a VRT or GTI " |
238 | 0 | "dataset"); |
239 | 0 | return false; |
240 | 0 | } |
241 | | |
242 | 0 | bool bRet = true; |
243 | 0 | if (!regions.empty()) |
244 | 0 | { |
245 | 0 | double dfCurPixels = 0; |
246 | 0 | for (const auto ®ion : regions) |
247 | 0 | { |
248 | 0 | if (bRet) |
249 | 0 | { |
250 | 0 | CPLDebug("GDAL", "Refresh from source %s", |
251 | 0 | region.osFilename.c_str()); |
252 | 0 | double dfNextCurPixels = |
253 | 0 | dfCurPixels + |
254 | 0 | static_cast<double>(region.nDstXSize) * region.nDstYSize; |
255 | 0 | void *pScaledProgress = GDALCreateScaledProgress( |
256 | 0 | dfCurPixels / dfTotalPixels, |
257 | 0 | dfNextCurPixels / dfTotalPixels, pfnProgress, pProgressArg); |
258 | 0 | bRet = PartialRefresh( |
259 | 0 | poDS, anOvrIndices, pszResampling, region.nDstXOff, |
260 | 0 | region.nDstYOff, region.nDstXSize, region.nDstYSize, |
261 | 0 | pScaledProgress ? GDALScaledProgress : nullptr, |
262 | 0 | pScaledProgress); |
263 | 0 | GDALDestroyScaledProgress(pScaledProgress); |
264 | 0 | dfCurPixels = dfNextCurPixels; |
265 | 0 | } |
266 | 0 | } |
267 | 0 | } |
268 | 0 | else |
269 | 0 | { |
270 | 0 | CPLDebug("GDAL", "No source is more recent than the overviews"); |
271 | 0 | } |
272 | |
|
273 | 0 | return bRet; |
274 | 0 | } |
275 | | |
276 | | /************************************************************************/ |
277 | | /* PartialRefreshFromSourceExtent() */ |
278 | | /************************************************************************/ |
279 | | |
280 | | static bool PartialRefreshFromSourceExtent( |
281 | | GDALDataset *poDS, const std::vector<std::string> &sources, |
282 | | const char *pszResampling, const std::vector<int> &anOvrIndices, |
283 | | GDALProgressFunc pfnProgress, void *pProgressArg) |
284 | 0 | { |
285 | 0 | GDALGeoTransform gt; |
286 | 0 | if (poDS->GetGeoTransform(gt) != CE_None) |
287 | 0 | { |
288 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Dataset has no geotransform"); |
289 | 0 | return false; |
290 | 0 | } |
291 | 0 | GDALGeoTransform invGT; |
292 | 0 | if (!gt.GetInverse(invGT)) |
293 | 0 | { |
294 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot invert geotransform"); |
295 | 0 | return false; |
296 | 0 | } |
297 | | |
298 | 0 | struct Region |
299 | 0 | { |
300 | 0 | std::string osFileName{}; |
301 | 0 | int nXOff = 0; |
302 | 0 | int nYOff = 0; |
303 | 0 | int nXSize = 0; |
304 | 0 | int nYSize = 0; |
305 | 0 | }; |
306 | |
|
307 | 0 | std::vector<Region> regions; |
308 | | |
309 | | // init slightly above zero to please Coverity Scan |
310 | 0 | double dfTotalPixels = std::numeric_limits<double>::min(); |
311 | 0 | for (const std::string &filename : sources) |
312 | 0 | { |
313 | 0 | auto poSrcDS = std::unique_ptr<GDALDataset>(GDALDataset::Open( |
314 | 0 | filename.c_str(), GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR)); |
315 | 0 | if (!poSrcDS) |
316 | 0 | return false; |
317 | | |
318 | 0 | GDALGeoTransform srcGT; |
319 | 0 | if (poSrcDS->GetGeoTransform(srcGT) != CE_None) |
320 | 0 | { |
321 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
322 | 0 | "Source dataset has no geotransform"); |
323 | 0 | return false; |
324 | 0 | } |
325 | | |
326 | 0 | const double dfULX = srcGT[0]; |
327 | 0 | const double dfULY = srcGT[3]; |
328 | 0 | const double dfLRX = srcGT[0] + poSrcDS->GetRasterXSize() * srcGT[1] + |
329 | 0 | poSrcDS->GetRasterYSize() * srcGT[2]; |
330 | 0 | const double dfLRY = srcGT[3] + poSrcDS->GetRasterXSize() * srcGT[4] + |
331 | 0 | poSrcDS->GetRasterYSize() * srcGT[5]; |
332 | 0 | const double dfX1 = invGT[0] + invGT[1] * dfULX + invGT[2] * dfULY; |
333 | 0 | const double dfY1 = invGT[3] + invGT[4] * dfULX + invGT[5] * dfULY; |
334 | 0 | const double dfX2 = invGT[0] + invGT[1] * dfLRX + invGT[2] * dfLRY; |
335 | 0 | const double dfY2 = invGT[3] + invGT[4] * dfLRX + invGT[5] * dfLRY; |
336 | 0 | constexpr double EPS = 1e-8; |
337 | 0 | const int nXOff = |
338 | 0 | static_cast<int>(std::max(0.0, std::min(dfX1, dfX2)) + EPS); |
339 | 0 | const int nYOff = |
340 | 0 | static_cast<int>(std::max(0.0, std::min(dfY1, dfY2)) + EPS); |
341 | 0 | const int nXSize = |
342 | 0 | static_cast<int>( |
343 | 0 | std::ceil(std::min(static_cast<double>(poDS->GetRasterXSize()), |
344 | 0 | std::max(dfX1, dfX2)) - |
345 | 0 | EPS)) - |
346 | 0 | nXOff; |
347 | 0 | const int nYSize = |
348 | 0 | static_cast<int>( |
349 | 0 | std::ceil(std::min(static_cast<double>(poDS->GetRasterYSize()), |
350 | 0 | std::max(dfY1, dfY2)) - |
351 | 0 | EPS)) - |
352 | 0 | nYOff; |
353 | |
|
354 | 0 | dfTotalPixels += static_cast<double>(nXSize) * nYSize; |
355 | 0 | Region region; |
356 | 0 | region.osFileName = filename; |
357 | 0 | region.nXOff = nXOff; |
358 | 0 | region.nYOff = nYOff; |
359 | 0 | region.nXSize = nXSize; |
360 | 0 | region.nYSize = nYSize; |
361 | 0 | regions.push_back(std::move(region)); |
362 | 0 | } |
363 | | |
364 | 0 | bool bRet = true; |
365 | 0 | double dfCurPixels = 0; |
366 | 0 | for (const auto ®ion : regions) |
367 | 0 | { |
368 | 0 | if (bRet) |
369 | 0 | { |
370 | 0 | CPLDebug("GDAL", "Refresh from source %s", |
371 | 0 | region.osFileName.c_str()); |
372 | 0 | double dfNextCurPixels = |
373 | 0 | dfCurPixels + |
374 | 0 | static_cast<double>(region.nXSize) * region.nYSize; |
375 | | // coverity[divide_by_zero] |
376 | 0 | void *pScaledProgress = GDALCreateScaledProgress( |
377 | 0 | dfCurPixels / dfTotalPixels, dfNextCurPixels / dfTotalPixels, |
378 | 0 | pfnProgress, pProgressArg); |
379 | 0 | bRet = |
380 | 0 | PartialRefresh(poDS, anOvrIndices, pszResampling, region.nXOff, |
381 | 0 | region.nYOff, region.nXSize, region.nYSize, |
382 | 0 | pScaledProgress ? GDALScaledProgress : nullptr, |
383 | 0 | pScaledProgress); |
384 | 0 | GDALDestroyScaledProgress(pScaledProgress); |
385 | 0 | dfCurPixels = dfNextCurPixels; |
386 | 0 | } |
387 | 0 | } |
388 | |
|
389 | 0 | return bRet; |
390 | 0 | } |
391 | | |
392 | | /************************************************************************/ |
393 | | /* PartialRefreshFromBBOX() */ |
394 | | /************************************************************************/ |
395 | | |
396 | | static bool PartialRefreshFromBBOX(GDALDataset *poDS, |
397 | | const std::vector<double> &bbox, |
398 | | const char *pszResampling, |
399 | | const std::vector<int> &anOvrIndices, |
400 | | GDALProgressFunc pfnProgress, |
401 | | void *pProgressArg) |
402 | 0 | { |
403 | 0 | const double dfULX = bbox[0]; |
404 | 0 | const double dfLRY = bbox[1]; |
405 | 0 | const double dfLRX = bbox[2]; |
406 | 0 | const double dfULY = bbox[3]; |
407 | |
|
408 | 0 | GDALGeoTransform gt; |
409 | 0 | if (poDS->GetGeoTransform(gt) != CE_None) |
410 | 0 | { |
411 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Dataset has no geotransform"); |
412 | 0 | return false; |
413 | 0 | } |
414 | 0 | GDALGeoTransform invGT; |
415 | 0 | if (!gt.GetInverse(invGT)) |
416 | 0 | { |
417 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot invert geotransform"); |
418 | 0 | return false; |
419 | 0 | } |
420 | 0 | const double dfX1 = invGT[0] + invGT[1] * dfULX + invGT[2] * dfULY; |
421 | 0 | const double dfY1 = invGT[3] + invGT[4] * dfULX + invGT[5] * dfULY; |
422 | 0 | const double dfX2 = invGT[0] + invGT[1] * dfLRX + invGT[2] * dfLRY; |
423 | 0 | const double dfY2 = invGT[3] + invGT[4] * dfLRX + invGT[5] * dfLRY; |
424 | 0 | constexpr double EPS = 1e-8; |
425 | 0 | const int nXOff = |
426 | 0 | static_cast<int>(std::max(0.0, std::min(dfX1, dfX2)) + EPS); |
427 | 0 | const int nYOff = |
428 | 0 | static_cast<int>(std::max(0.0, std::min(dfY1, dfY2)) + EPS); |
429 | 0 | const int nXSize = static_cast<int>(std::ceil( |
430 | 0 | std::min(static_cast<double>(poDS->GetRasterXSize()), |
431 | 0 | std::max(dfX1, dfX2)) - |
432 | 0 | EPS)) - |
433 | 0 | nXOff; |
434 | 0 | const int nYSize = static_cast<int>(std::ceil( |
435 | 0 | std::min(static_cast<double>(poDS->GetRasterYSize()), |
436 | 0 | std::max(dfY1, dfY2)) - |
437 | 0 | EPS)) - |
438 | 0 | nYOff; |
439 | 0 | return PartialRefresh(poDS, anOvrIndices, pszResampling, nXOff, nYOff, |
440 | 0 | nXSize, nYSize, pfnProgress, pProgressArg); |
441 | 0 | } |
442 | | |
443 | | /************************************************************************/ |
444 | | /* GDALRasterOverviewAlgorithmRefresh::RunImpl() */ |
445 | | /************************************************************************/ |
446 | | |
447 | | bool GDALRasterOverviewAlgorithmRefresh::RunImpl(GDALProgressFunc pfnProgress, |
448 | | void *pProgressData) |
449 | 0 | { |
450 | 0 | auto poDS = m_dataset.GetDatasetRef(); |
451 | 0 | CPLAssert(poDS); |
452 | 0 | if (poDS->GetRasterCount() == 0) |
453 | 0 | { |
454 | 0 | ReportError(CE_Failure, CPLE_AppDefined, "Dataset has no raster band"); |
455 | 0 | return false; |
456 | 0 | } |
457 | | |
458 | 0 | auto poBand = poDS->GetRasterBand(1); |
459 | 0 | const int nOvCount = poBand->GetOverviewCount(); |
460 | |
|
461 | 0 | std::vector<int> levels = m_levels; |
462 | | |
463 | | // If no levels are specified, reuse the potentially existing ones. |
464 | 0 | if (levels.empty()) |
465 | 0 | { |
466 | 0 | for (int iOvr = 0; iOvr < nOvCount; ++iOvr) |
467 | 0 | { |
468 | 0 | auto poOverview = poBand->GetOverview(iOvr); |
469 | 0 | if (poOverview) |
470 | 0 | { |
471 | 0 | const int nOvFactor = GDALComputeOvFactor( |
472 | 0 | poOverview->GetXSize(), poBand->GetXSize(), |
473 | 0 | poOverview->GetYSize(), poBand->GetYSize()); |
474 | 0 | levels.push_back(nOvFactor); |
475 | 0 | } |
476 | 0 | } |
477 | 0 | } |
478 | 0 | if (levels.empty()) |
479 | 0 | { |
480 | 0 | ReportError(CE_Failure, CPLE_AppDefined, "No overviews to refresh"); |
481 | 0 | return false; |
482 | 0 | } |
483 | | |
484 | 0 | std::vector<int> anOvrIndices; |
485 | 0 | for (int nLevel : levels) |
486 | 0 | { |
487 | 0 | int nIdx = -1; |
488 | 0 | for (int iOvr = 0; iOvr < nOvCount; iOvr++) |
489 | 0 | { |
490 | 0 | auto poOverview = poBand->GetOverview(iOvr); |
491 | 0 | if (poOverview) |
492 | 0 | { |
493 | 0 | const int nOvFactor = GDALComputeOvFactor( |
494 | 0 | poOverview->GetXSize(), poBand->GetXSize(), |
495 | 0 | poOverview->GetYSize(), poBand->GetYSize()); |
496 | 0 | if (nOvFactor == nLevel || |
497 | 0 | nOvFactor == GDALOvLevelAdjust2(nLevel, poBand->GetXSize(), |
498 | 0 | poBand->GetYSize())) |
499 | 0 | { |
500 | 0 | nIdx = iOvr; |
501 | 0 | break; |
502 | 0 | } |
503 | 0 | } |
504 | 0 | } |
505 | 0 | if (nIdx < 0) |
506 | 0 | { |
507 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
508 | 0 | "Cannot find overview level with subsampling factor of %d", |
509 | 0 | nLevel); |
510 | 0 | return false; |
511 | 0 | } |
512 | 0 | CPLDebug("GDAL", "Refreshing overview idx %d", nIdx); |
513 | 0 | anOvrIndices.push_back(nIdx); |
514 | 0 | } |
515 | | |
516 | 0 | std::string resampling = m_resampling; |
517 | 0 | if (resampling.empty()) |
518 | 0 | { |
519 | 0 | const char *pszResampling = |
520 | 0 | poBand->GetOverview(0)->GetMetadataItem("RESAMPLING"); |
521 | 0 | if (pszResampling) |
522 | 0 | { |
523 | 0 | resampling = pszResampling; |
524 | 0 | CPLDebug("GDAL", |
525 | 0 | "Reusing resampling method %s from existing " |
526 | 0 | "overview", |
527 | 0 | pszResampling); |
528 | 0 | } |
529 | 0 | } |
530 | 0 | if (resampling.empty()) |
531 | 0 | resampling = "nearest"; |
532 | |
|
533 | 0 | if (m_refreshFromSourceTimestamp) |
534 | 0 | { |
535 | 0 | return PartialRefreshFromSourceTimestamp( |
536 | 0 | poDS, resampling.c_str(), anOvrIndices, pfnProgress, pProgressData); |
537 | 0 | } |
538 | 0 | else if (!m_refreshBbox.empty()) |
539 | 0 | { |
540 | 0 | return PartialRefreshFromBBOX(poDS, m_refreshBbox, resampling.c_str(), |
541 | 0 | anOvrIndices, pfnProgress, pProgressData); |
542 | 0 | } |
543 | 0 | else if (!m_like.empty()) |
544 | 0 | { |
545 | 0 | return PartialRefreshFromSourceExtent(poDS, m_like, resampling.c_str(), |
546 | 0 | anOvrIndices, pfnProgress, |
547 | 0 | pProgressData); |
548 | 0 | } |
549 | 0 | else |
550 | 0 | { |
551 | 0 | return GDALBuildOverviews( |
552 | 0 | GDALDataset::ToHandle(poDS), resampling.c_str(), |
553 | 0 | static_cast<int>(levels.size()), levels.data(), 0, nullptr, |
554 | 0 | pfnProgress, pProgressData) == CE_None; |
555 | 0 | } |
556 | 0 | } |
557 | | |
558 | | //! @endcond |