Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL Utilities
4 : * Purpose: Command line application to build overviews.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2000, Frank Warmerdam
9 : * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_string.h"
15 : #include "gdal_version.h"
16 : #include "gdal_priv.h"
17 : #include "commonutils.h"
18 : #include "vrtdataset.h"
19 : #include "vrt_priv.h"
20 : #include "gdalargumentparser.h"
21 :
22 : #include <algorithm>
23 : #include <limits>
24 :
25 : /************************************************************************/
26 : /* GDALAddoErrorHandler() */
27 : /************************************************************************/
28 :
29 : class GDALError
30 : {
31 : public:
32 : CPLErr m_eErr;
33 : CPLErrorNum m_errNum;
34 : CPLString m_osMsg;
35 :
36 1 : explicit GDALError(CPLErr eErr = CE_None, CPLErrorNum errNum = CPLE_None,
37 : const char *pszMsg = "")
38 1 : : m_eErr(eErr), m_errNum(errNum), m_osMsg(pszMsg ? pszMsg : "")
39 : {
40 1 : }
41 : };
42 :
43 : std::vector<GDALError> aoErrors;
44 :
45 1 : static void CPL_STDCALL GDALAddoErrorHandler(CPLErr eErr, CPLErrorNum errNum,
46 : const char *pszMsg)
47 : {
48 1 : aoErrors.push_back(GDALError(eErr, errNum, pszMsg));
49 1 : }
50 :
51 : /************************************************************************/
52 : /* PartialRefresh() */
53 : /************************************************************************/
54 :
55 4 : static bool PartialRefresh(GDALDataset *poDS,
56 : const std::vector<int> &anOvrIndices, int nBandCount,
57 : const int *panBandList, const char *pszResampling,
58 : int nXOff, int nYOff, int nXSize, int nYSize,
59 : GDALProgressFunc pfnProgress, void *pProgressArg)
60 : {
61 8 : std::vector<int> anBandList;
62 4 : if (nBandCount == 0)
63 : {
64 8 : for (int i = 0; i < poDS->GetRasterCount(); ++i)
65 4 : anBandList.push_back(i + 1);
66 4 : nBandCount = poDS->GetRasterCount();
67 4 : panBandList = anBandList.data();
68 : }
69 :
70 4 : int nOvCount = 0;
71 8 : for (int i = 0; i < nBandCount; ++i)
72 : {
73 4 : auto poSrcBand = poDS->GetRasterBand(panBandList[i]);
74 4 : if (i == 0)
75 4 : nOvCount = poSrcBand->GetOverviewCount();
76 0 : else if (nOvCount != poSrcBand->GetOverviewCount())
77 : {
78 0 : CPLError(CE_Failure, CPLE_AppDefined,
79 : "Not same number of overviews on all bands");
80 0 : return false;
81 : }
82 : }
83 :
84 8 : std::vector<GDALRasterBand *> apoSrcBands;
85 8 : std::vector<GDALRasterBand **> apapoOverviewBands;
86 8 : for (int i = 0; i < nBandCount; ++i)
87 : {
88 4 : auto poSrcBand = poDS->GetRasterBand(panBandList[i]);
89 4 : apoSrcBands.push_back(poSrcBand);
90 0 : apapoOverviewBands.push_back(static_cast<GDALRasterBand **>(
91 4 : CPLMalloc(sizeof(GDALRasterBand *) * anOvrIndices.size())));
92 4 : int j = 0;
93 8 : for (int nOvrIdx : anOvrIndices)
94 : {
95 4 : apapoOverviewBands[i][j] = poSrcBand->GetOverview(nOvrIdx);
96 4 : ++j;
97 : }
98 : }
99 :
100 4 : CPLStringList aosOptions;
101 4 : aosOptions.SetNameValue("XOFF", CPLSPrintf("%d", nXOff));
102 4 : aosOptions.SetNameValue("YOFF", CPLSPrintf("%d", nYOff));
103 4 : aosOptions.SetNameValue("XSIZE", CPLSPrintf("%d", nXSize));
104 4 : aosOptions.SetNameValue("YSIZE", CPLSPrintf("%d", nYSize));
105 4 : bool bOK = GDALRegenerateOverviewsMultiBand(
106 4 : nBandCount, apoSrcBands.data(),
107 4 : static_cast<int>(anOvrIndices.size()),
108 4 : apapoOverviewBands.data(), pszResampling, pfnProgress,
109 4 : pProgressArg, aosOptions.List()) == CE_None;
110 8 : for (auto papoOverviewBands : apapoOverviewBands)
111 4 : CPLFree(papoOverviewBands);
112 4 : return bOK;
113 : }
114 :
115 : /************************************************************************/
116 : /* GetOvrIndices() */
117 : /************************************************************************/
118 :
119 4 : static bool GetOvrIndices(GDALDataset *poDS, int nLevelCount,
120 : const int *panLevels, bool bMinSizeSpecified,
121 : int nMinSize, std::vector<int> &anOvrIndices)
122 : {
123 4 : auto poBand = poDS->GetRasterBand(1);
124 4 : if (!poBand)
125 : {
126 0 : CPLError(CE_Failure, CPLE_AppDefined, "Dataset has no bands");
127 0 : return false;
128 : }
129 4 : const int nOvCount = poBand->GetOverviewCount();
130 4 : if (nOvCount == 0)
131 : {
132 0 : CPLError(CE_Failure, CPLE_AppDefined, "Dataset has no overviews");
133 0 : return false;
134 : }
135 :
136 4 : if (nLevelCount == 0)
137 : {
138 3 : if (!bMinSizeSpecified)
139 : {
140 6 : for (int i = 0; i < nOvCount; ++i)
141 3 : anOvrIndices.push_back(i);
142 : }
143 : else
144 : {
145 0 : for (int i = 0; i < nOvCount; i++)
146 : {
147 0 : GDALRasterBand *poOverview = poBand->GetOverview(i);
148 0 : if (poOverview == nullptr)
149 0 : continue;
150 0 : if (poOverview->GetXSize() >= nMinSize ||
151 0 : poOverview->GetYSize() >= nMinSize)
152 : {
153 0 : anOvrIndices.push_back(i);
154 : }
155 : }
156 : }
157 : }
158 : else
159 : {
160 2 : for (int i = 0; i < nLevelCount; ++i)
161 : {
162 1 : const int nLevel = panLevels[i];
163 1 : int nIdx = -1;
164 1 : for (int j = 0; j < nOvCount; j++)
165 : {
166 1 : GDALRasterBand *poOverview = poBand->GetOverview(j);
167 1 : if (poOverview == nullptr)
168 0 : continue;
169 :
170 1 : int nOvFactor = GDALComputeOvFactor(
171 : poOverview->GetXSize(), poBand->GetXSize(),
172 : poOverview->GetYSize(), poBand->GetYSize());
173 :
174 1 : if (nOvFactor == nLevel ||
175 0 : nOvFactor == GDALOvLevelAdjust2(nLevel, poBand->GetXSize(),
176 : poBand->GetYSize()))
177 : {
178 1 : nIdx = j;
179 1 : break;
180 : }
181 : }
182 1 : if (nIdx < 0)
183 : {
184 0 : CPLError(
185 : CE_Failure, CPLE_AppDefined,
186 : "Cannot find overview level with subsampling factor of %d",
187 : nLevel);
188 0 : return false;
189 : }
190 1 : anOvrIndices.push_back(nIdx);
191 : }
192 : }
193 4 : return true;
194 : }
195 :
196 : /************************************************************************/
197 : /* PartialRefreshFromSourceTimestamp() */
198 : /************************************************************************/
199 :
200 2 : static bool PartialRefreshFromSourceTimestamp(
201 : GDALDataset *poDS, const char *pszResampling, int nLevelCount,
202 : const int *panLevels, int nBandCount, const int *panBandList,
203 : bool bMinSizeSpecified, int nMinSize, GDALProgressFunc pfnProgress,
204 : void *pProgressArg)
205 : {
206 4 : std::vector<int> anOvrIndices;
207 2 : if (!GetOvrIndices(poDS, nLevelCount, panLevels, bMinSizeSpecified,
208 : nMinSize, anOvrIndices))
209 0 : return false;
210 :
211 : VSIStatBufL sStatOvr;
212 6 : std::string osVRTOvr(std::string(poDS->GetDescription()) + ".ovr");
213 2 : if (VSIStatL(osVRTOvr.c_str(), &sStatOvr) != 0)
214 : {
215 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s\n",
216 : osVRTOvr.c_str());
217 0 : return false;
218 : }
219 2 : if (sStatOvr.st_mtime == 0)
220 : {
221 0 : CPLError(CE_Failure, CPLE_AppDefined,
222 : "Cannot get modification time of %s\n", osVRTOvr.c_str());
223 0 : return false;
224 : }
225 :
226 4 : std::vector<GTISourceDesc> regions;
227 :
228 : // Smallest positive double to avoid Coverity Scan complaining about divide_by_zero
229 2 : double dfTotalPixels = std::numeric_limits<double>::min();
230 :
231 2 : if (dynamic_cast<VRTDataset *>(poDS))
232 : {
233 : auto poVRTBand =
234 1 : dynamic_cast<VRTSourcedRasterBand *>(poDS->GetRasterBand(1));
235 1 : if (!poVRTBand)
236 : {
237 0 : CPLError(CE_Failure, CPLE_AppDefined,
238 : "Band is not a VRTSourcedRasterBand");
239 0 : return false;
240 : }
241 :
242 3 : for (auto &poSource : poVRTBand->m_papoSources)
243 : {
244 : auto poSimpleSource =
245 2 : dynamic_cast<VRTSimpleSource *>(poSource.get());
246 2 : if (poSimpleSource)
247 : {
248 : VSIStatBufL sStatSource;
249 2 : if (VSIStatL(poSimpleSource->GetSourceDatasetName().c_str(),
250 2 : &sStatSource) == 0)
251 : {
252 2 : if (sStatSource.st_mtime > sStatOvr.st_mtime)
253 : {
254 : double dfXOff, dfYOff, dfXSize, dfYSize;
255 1 : poSimpleSource->GetDstWindow(dfXOff, dfYOff, dfXSize,
256 : dfYSize);
257 1 : constexpr double EPS = 1e-8;
258 1 : int nXOff = static_cast<int>(dfXOff + EPS);
259 1 : int nYOff = static_cast<int>(dfYOff + EPS);
260 1 : int nXSize = static_cast<int>(dfXSize + 0.5);
261 1 : int nYSize = static_cast<int>(dfYSize + 0.5);
262 1 : if (nXOff > poDS->GetRasterXSize() ||
263 1 : nYOff > poDS->GetRasterYSize() || nXSize <= 0 ||
264 : nYSize <= 0)
265 : {
266 0 : continue;
267 : }
268 1 : if (nXOff < 0)
269 : {
270 0 : nXSize += nXOff;
271 0 : nXOff = 0;
272 : }
273 1 : if (nXOff > poDS->GetRasterXSize() - nXSize)
274 : {
275 0 : nXSize = poDS->GetRasterXSize() - nXOff;
276 : }
277 1 : if (nYOff < 0)
278 : {
279 0 : nYSize += nYOff;
280 0 : nYOff = 0;
281 : }
282 1 : if (nYOff > poDS->GetRasterYSize() - nYSize)
283 : {
284 0 : nYSize = poDS->GetRasterYSize() - nYOff;
285 : }
286 :
287 1 : dfTotalPixels += static_cast<double>(nXSize) * nYSize;
288 2 : GTISourceDesc region;
289 : region.osFilename =
290 1 : poSimpleSource->GetSourceDatasetName();
291 1 : region.nDstXOff = nXOff;
292 1 : region.nDstYOff = nYOff;
293 1 : region.nDstXSize = nXSize;
294 1 : region.nDstYSize = nYSize;
295 1 : regions.push_back(std::move(region));
296 : }
297 : }
298 : }
299 : }
300 : }
301 : #ifdef GTI_DRIVER_DISABLED_OR_PLUGIN
302 : else if (poDS->GetDriver() &&
303 : EQUAL(poDS->GetDriver()->GetDescription(), "GTI"))
304 : {
305 : CPLError(CE_Failure, CPLE_NotSupported,
306 : "--partial-refresh-from-source-timestamp only works on a GTI "
307 : "dataset if the GTI driver is not built as a plugin, "
308 : "but in core library");
309 : return false;
310 : }
311 : #else
312 1 : else if (auto poGTIDS = GDALDatasetCastToGTIDataset(poDS))
313 : {
314 1 : regions = GTIGetSourcesMoreRecentThan(poGTIDS, sStatOvr.st_mtime);
315 2 : for (const auto ®ion : regions)
316 : {
317 1 : dfTotalPixels +=
318 1 : static_cast<double>(region.nDstXSize) * region.nDstYSize;
319 : }
320 : }
321 : #endif
322 : else
323 : {
324 0 : CPLError(CE_Failure, CPLE_AppDefined,
325 : "--partial-refresh-from-source-timestamp only works on a VRT "
326 : "or GTI dataset");
327 0 : return false;
328 : }
329 :
330 2 : if (!regions.empty())
331 : {
332 2 : double dfCurPixels = 0;
333 4 : for (const auto ®ion : regions)
334 : {
335 2 : if (pfnProgress == GDALDummyProgress)
336 : {
337 0 : CPLDebug("GDAL", "Refresh from source %s",
338 : region.osFilename.c_str());
339 : }
340 : else
341 : {
342 2 : printf("Refresh from source %s.\n", region.osFilename.c_str());
343 : }
344 2 : const double dfNextCurPixels =
345 : dfCurPixels +
346 2 : static_cast<double>(region.nDstXSize) * region.nDstYSize;
347 2 : void *pScaledProgress = GDALCreateScaledProgress(
348 : dfCurPixels / dfTotalPixels, dfNextCurPixels / dfTotalPixels,
349 : pfnProgress, pProgressArg);
350 4 : bool bRet = PartialRefresh(
351 : poDS, anOvrIndices, nBandCount, panBandList, pszResampling,
352 2 : region.nDstXOff, region.nDstYOff, region.nDstXSize,
353 2 : region.nDstYSize, GDALScaledProgress, pScaledProgress);
354 2 : GDALDestroyScaledProgress(pScaledProgress);
355 2 : if (!bRet)
356 0 : return false;
357 2 : dfCurPixels = dfNextCurPixels;
358 : }
359 : }
360 : else
361 : {
362 0 : if (pfnProgress == GDALDummyProgress)
363 : {
364 0 : CPLDebug("GDAL", "No source is more recent than the overviews");
365 : }
366 : else
367 : {
368 0 : printf("No source is more recent than the overviews.\n");
369 : }
370 : }
371 :
372 2 : return true;
373 : }
374 :
375 : /************************************************************************/
376 : /* PartialRefreshFromSourceExtent() */
377 : /************************************************************************/
378 :
379 1 : static bool PartialRefreshFromSourceExtent(
380 : GDALDataset *poDS, const CPLStringList &aosSources,
381 : const char *pszResampling, int nLevelCount, const int *panLevels,
382 : int nBandCount, const int *panBandList, bool bMinSizeSpecified,
383 : int nMinSize, GDALProgressFunc pfnProgress, void *pProgressArg)
384 : {
385 2 : std::vector<int> anOvrIndices;
386 1 : if (!GetOvrIndices(poDS, nLevelCount, panLevels, bMinSizeSpecified,
387 : nMinSize, anOvrIndices))
388 0 : return false;
389 :
390 1 : GDALGeoTransform gt;
391 1 : if (poDS->GetGeoTransform(gt) != CE_None)
392 : {
393 0 : CPLError(CE_Failure, CPLE_AppDefined, "Dataset has no geotransform");
394 0 : return false;
395 : }
396 1 : GDALGeoTransform invGT;
397 1 : if (!gt.GetInverse(invGT))
398 : {
399 0 : return false;
400 : }
401 :
402 : struct Region
403 : {
404 : std::string osFileName;
405 : int nXOff;
406 : int nYOff;
407 : int nXSize;
408 : int nYSize;
409 : };
410 :
411 2 : std::vector<Region> regions;
412 :
413 : // Smallest positive double to avoid Coverity Scan complaining about divide_by_zero
414 1 : double dfTotalPixels = std::numeric_limits<double>::min();
415 2 : for (int i = 0; i < aosSources.size(); ++i)
416 : {
417 : auto poSrcDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
418 1 : aosSources[i], GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
419 1 : if (!poSrcDS)
420 0 : return false;
421 :
422 1 : GDALGeoTransform srcGT;
423 1 : if (poSrcDS->GetGeoTransform(srcGT) != CE_None)
424 : {
425 0 : CPLError(CE_Failure, CPLE_AppDefined,
426 : "Source dataset has no geotransform");
427 0 : return false;
428 : }
429 :
430 1 : const double dfULX = srcGT[0];
431 1 : const double dfULY = srcGT[3];
432 1 : const double dfLRX = srcGT[0] + poSrcDS->GetRasterXSize() * srcGT[1] +
433 1 : poSrcDS->GetRasterYSize() * srcGT[2];
434 1 : const double dfLRY = srcGT[3] + poSrcDS->GetRasterXSize() * srcGT[4] +
435 1 : poSrcDS->GetRasterYSize() * srcGT[5];
436 1 : const double dfX1 = invGT[0] + invGT[1] * dfULX + invGT[2] * dfULY;
437 1 : const double dfY1 = invGT[3] + invGT[4] * dfULX + invGT[5] * dfULY;
438 1 : const double dfX2 = invGT[0] + invGT[1] * dfLRX + invGT[2] * dfLRY;
439 1 : const double dfY2 = invGT[3] + invGT[4] * dfLRX + invGT[5] * dfLRY;
440 1 : constexpr double EPS = 1e-8;
441 : const int nXOff =
442 1 : static_cast<int>(std::max(0.0, std::min(dfX1, dfX2)) + EPS);
443 : const int nYOff =
444 1 : static_cast<int>(std::max(0.0, std::min(dfY1, dfY2)) + EPS);
445 : const int nXSize =
446 1 : static_cast<int>(
447 1 : std::ceil(std::min(static_cast<double>(poDS->GetRasterXSize()),
448 2 : std::max(dfX1, dfX2)) -
449 : EPS)) -
450 1 : nXOff;
451 : const int nYSize =
452 1 : static_cast<int>(
453 1 : std::ceil(std::min(static_cast<double>(poDS->GetRasterYSize()),
454 2 : std::max(dfY1, dfY2)) -
455 : EPS)) -
456 1 : nYOff;
457 :
458 1 : dfTotalPixels += static_cast<double>(nXSize) * nYSize;
459 2 : Region region;
460 1 : region.osFileName = aosSources[i];
461 1 : region.nXOff = nXOff;
462 1 : region.nYOff = nYOff;
463 1 : region.nXSize = nXSize;
464 1 : region.nYSize = nYSize;
465 1 : regions.push_back(std::move(region));
466 : }
467 :
468 1 : double dfCurPixels = 0;
469 2 : for (const auto ®ion : regions)
470 : {
471 1 : if (pfnProgress == GDALDummyProgress)
472 : {
473 0 : CPLDebug("GDAL", "Refresh from source %s",
474 : region.osFileName.c_str());
475 : }
476 : else
477 : {
478 1 : printf("Refresh from source %s.\n", region.osFileName.c_str());
479 : }
480 1 : const double dfNextCurPixels =
481 1 : dfCurPixels + static_cast<double>(region.nXSize) * region.nYSize;
482 1 : void *pScaledProgress = GDALCreateScaledProgress(
483 : dfCurPixels / dfTotalPixels, dfNextCurPixels / dfTotalPixels,
484 : pfnProgress, pProgressArg);
485 2 : bool bRet = PartialRefresh(poDS, anOvrIndices, nBandCount, panBandList,
486 1 : pszResampling, region.nXOff, region.nYOff,
487 1 : region.nXSize, region.nYSize,
488 : GDALScaledProgress, pScaledProgress);
489 1 : GDALDestroyScaledProgress(pScaledProgress);
490 1 : if (!bRet)
491 0 : return false;
492 1 : dfCurPixels = dfNextCurPixels;
493 : }
494 :
495 1 : return true;
496 : }
497 :
498 : /************************************************************************/
499 : /* PartialRefreshFromProjWin() */
500 : /************************************************************************/
501 :
502 1 : static bool PartialRefreshFromProjWin(
503 : GDALDataset *poDS, double dfULX, double dfULY, double dfLRX, double dfLRY,
504 : const char *pszResampling, int nLevelCount, const int *panLevels,
505 : int nBandCount, const int *panBandList, bool bMinSizeSpecified,
506 : int nMinSize, GDALProgressFunc pfnProgress, void *pProgressArg)
507 : {
508 2 : std::vector<int> anOvrIndices;
509 1 : if (!GetOvrIndices(poDS, nLevelCount, panLevels, bMinSizeSpecified,
510 : nMinSize, anOvrIndices))
511 0 : return false;
512 :
513 1 : GDALGeoTransform gt;
514 1 : if (poDS->GetGeoTransform(gt) != CE_None)
515 : {
516 0 : CPLError(CE_Failure, CPLE_AppDefined, "Dataset has no geotransform");
517 0 : return false;
518 : }
519 1 : GDALGeoTransform invGT;
520 1 : if (!gt.GetInverse(invGT))
521 : {
522 0 : return false;
523 : }
524 1 : const double dfX1 = invGT[0] + invGT[1] * dfULX + invGT[2] * dfULY;
525 1 : const double dfY1 = invGT[3] + invGT[4] * dfULX + invGT[5] * dfULY;
526 1 : const double dfX2 = invGT[0] + invGT[1] * dfLRX + invGT[2] * dfLRY;
527 1 : const double dfY2 = invGT[3] + invGT[4] * dfLRX + invGT[5] * dfLRY;
528 1 : constexpr double EPS = 1e-8;
529 : const int nXOff =
530 1 : static_cast<int>(std::max(0.0, std::min(dfX1, dfX2)) + EPS);
531 : const int nYOff =
532 1 : static_cast<int>(std::max(0.0, std::min(dfY1, dfY2)) + EPS);
533 1 : const int nXSize = static_cast<int>(std::ceil(
534 1 : std::min(static_cast<double>(poDS->GetRasterXSize()),
535 2 : std::max(dfX1, dfX2)) -
536 : EPS)) -
537 1 : nXOff;
538 1 : const int nYSize = static_cast<int>(std::ceil(
539 1 : std::min(static_cast<double>(poDS->GetRasterYSize()),
540 2 : std::max(dfY1, dfY2)) -
541 : EPS)) -
542 1 : nYOff;
543 1 : return PartialRefresh(poDS, anOvrIndices, nBandCount, panBandList,
544 : pszResampling, nXOff, nYOff, nXSize, nYSize,
545 1 : pfnProgress, pProgressArg);
546 : }
547 :
548 : /************************************************************************/
549 : /* main() */
550 : /************************************************************************/
551 :
552 30 : MAIN_START(nArgc, papszArgv)
553 :
554 : {
555 30 : EarlySetConfigOptions(nArgc, papszArgv);
556 30 : GDALAllRegister();
557 :
558 30 : nArgc = GDALGeneralCmdLineProcessor(nArgc, &papszArgv, 0);
559 30 : if (nArgc < 1)
560 2 : exit(-nArgc);
561 52 : CPLStringList aosArgv;
562 28 : aosArgv.Assign(papszArgv, /* bAssign = */ true);
563 :
564 80 : GDALArgumentParser argParser(aosArgv[0], /* bForBinary=*/true);
565 :
566 28 : argParser.add_description(_("Builds or rebuilds overview images."));
567 :
568 28 : const char *pszEpilog = _(
569 : "Useful configuration variables :\n"
570 : " --config USE_RRD YES : Use Erdas Imagine format (.aux) as overview "
571 : "format.\n"
572 : "Below, only for external overviews in GeoTIFF format:\n"
573 : " --config COMPRESS_OVERVIEW {JPEG,LZW,PACKBITS,DEFLATE} : TIFF "
574 : "compression\n"
575 : " --config PHOTOMETRIC_OVERVIEW {RGB,YCBCR,...} : TIFF photometric "
576 : "interp.\n"
577 : " --config INTERLEAVE_OVERVIEW {PIXEL|BAND} : TIFF interleaving "
578 : "method\n"
579 : " --config BIGTIFF_OVERVIEW {IF_NEEDED|IF_SAFER|YES|NO} : is BigTIFF "
580 : "used\n"
581 : "\n"
582 : "Examples:\n"
583 : " %% gdaladdo -r average abc.tif\n"
584 : " %% gdaladdo --config COMPRESS_OVERVIEW JPEG\n"
585 : " --config PHOTOMETRIC_OVERVIEW YCBCR\n"
586 : " --config INTERLEAVE_OVERVIEW PIXEL -ro abc.tif\n"
587 : "\n"
588 : "For more details, consult https://gdal.org/programs/gdaladdo.html");
589 28 : argParser.add_epilog(pszEpilog);
590 :
591 52 : std::string osResampling;
592 28 : argParser.add_argument("-r")
593 28 : .store_into(osResampling)
594 : .metavar("nearest|average|rms|gauss|bilinear|cubic|cubicspline|lanczos|"
595 56 : "average_magphase|mode")
596 28 : .help(_("Select a resampling algorithm."));
597 :
598 28 : bool bReadOnly = false;
599 28 : argParser.add_argument("-ro").store_into(bReadOnly).help(
600 : _("Open the dataset in read-only mode, in order to generate external "
601 28 : "overview."));
602 :
603 28 : bool bQuiet = false;
604 28 : argParser.add_quiet_argument(&bQuiet);
605 :
606 52 : std::vector<int> anBandList;
607 28 : argParser.add_argument("-b")
608 28 : .append()
609 56 : .metavar("<band>")
610 : .action(
611 0 : [&anBandList](const std::string &s)
612 : {
613 0 : const int nBand = atoi(s.c_str());
614 0 : if (nBand < 1)
615 : {
616 : throw std::invalid_argument(CPLSPrintf(
617 0 : "Unrecognizable band number (%s).", s.c_str()));
618 : }
619 0 : anBandList.push_back(nBand);
620 28 : })
621 28 : .help(_("Select input band(s) for overview generation."));
622 :
623 52 : CPLStringList aosOpenOptions;
624 28 : argParser.add_argument("-oo")
625 28 : .append()
626 56 : .metavar("<NAME=VALUE>")
627 1 : .action([&aosOpenOptions](const std::string &s)
628 29 : { aosOpenOptions.AddString(s.c_str()); })
629 28 : .help(_("Dataset open option (format-specific)."));
630 :
631 28 : int nMinSize = 256;
632 28 : argParser.add_argument("-minsize")
633 28 : .default_value(nMinSize)
634 56 : .metavar("<val>")
635 28 : .store_into(nMinSize)
636 28 : .help(_("Maximum width or height of the smallest overview level."));
637 :
638 28 : bool bClean = false;
639 28 : bool bPartialRefreshFromSourceTimestamp = false;
640 52 : std::string osPartialRefreshFromSourceExtent;
641 :
642 : {
643 28 : auto &group = argParser.add_mutually_exclusive_group();
644 28 : group.add_argument("-clean").store_into(bClean).help(
645 28 : _("Remove all overviews."));
646 :
647 28 : group.add_argument("--partial-refresh-from-source-timestamp")
648 28 : .store_into(bPartialRefreshFromSourceTimestamp)
649 : .help(_("Performs a partial refresh of existing overviews, when "
650 28 : "<filename> is a VRT file with an external overview."));
651 :
652 28 : group.add_argument("--partial-refresh-from-projwin")
653 56 : .metavar("<ulx> <uly> <lrx> <lry>")
654 28 : .nargs(4)
655 28 : .scan<'g', double>()
656 : .help(
657 : _("Performs a partial refresh of existing overviews, in the "
658 28 : "region of interest specified by georeference coordinates."));
659 :
660 28 : group.add_argument("--partial-refresh-from-source-extent")
661 56 : .metavar("<filename1>[,<filenameN>]...")
662 28 : .store_into(osPartialRefreshFromSourceExtent)
663 : .help(
664 : _("Performs a partial refresh of existing overviews, in the "
665 28 : "region of interest specified by one or several filename."));
666 : }
667 :
668 52 : std::string osFilename;
669 28 : argParser.add_argument("filename")
670 28 : .store_into(osFilename)
671 : .help(_("The file to build overviews for (or whose overviews must be "
672 28 : "removed)."));
673 :
674 56 : argParser.add_argument("level").remaining().metavar("<level>").help(
675 28 : _("A list of integral overview levels to build."));
676 :
677 : try
678 : {
679 28 : argParser.parse_args(aosArgv);
680 : }
681 0 : catch (const std::exception &err)
682 : {
683 0 : argParser.display_error_and_usage(err);
684 0 : std::exit(1);
685 : }
686 :
687 52 : std::vector<int> anLevels;
688 52 : auto levels = argParser.present<std::vector<std::string>>("level");
689 28 : if (levels)
690 : {
691 29 : for (const auto &level : *levels)
692 : {
693 18 : if (CPLGetValueType(level.c_str()) != CPL_VALUE_INTEGER)
694 : {
695 1 : CPLError(
696 : CE_Failure, CPLE_IllegalArg,
697 : "Value '%s' is not a positive integer subsampling factor",
698 : level.c_str());
699 1 : std::exit(1);
700 : }
701 17 : anLevels.push_back(atoi(level.c_str()));
702 17 : if (anLevels.back() <= 0)
703 : {
704 2 : CPLError(
705 : CE_Failure, CPLE_IllegalArg,
706 : "Value '%s' is not a positive integer subsampling factor",
707 : level.c_str());
708 2 : std::exit(1);
709 : }
710 15 : if (anLevels.back() == 1)
711 : {
712 0 : printf(
713 : "Warning: Overview with subsampling factor of 1 requested. "
714 : "This will copy the full resolution dataset in the "
715 : "overview!\n");
716 : }
717 : }
718 : }
719 :
720 25 : GDALProgressFunc pfnProgress =
721 25 : bQuiet ? GDALDummyProgress : GDALTermProgress;
722 25 : const bool bMinSizeSpecified = argParser.is_used("-minsize");
723 :
724 25 : CPLStringList aosSources;
725 25 : if (!osPartialRefreshFromSourceExtent.empty())
726 : {
727 : aosSources = CSLTokenizeString2(
728 1 : osPartialRefreshFromSourceExtent.c_str(), ",", 0);
729 : }
730 :
731 25 : bool bPartialRefreshFromProjWin = false;
732 25 : double dfULX = 0;
733 25 : double dfULY = 0;
734 25 : double dfLRX = 0;
735 25 : double dfLRY = 0;
736 25 : if (auto oProjWin = argParser.present<std::vector<double>>(
737 50 : "--partial-refresh-from-projwin"))
738 : {
739 1 : bPartialRefreshFromProjWin = true;
740 1 : dfULX = (*oProjWin)[0];
741 1 : dfULY = (*oProjWin)[1];
742 1 : dfLRX = (*oProjWin)[2];
743 1 : dfLRY = (*oProjWin)[3];
744 : }
745 :
746 : /* -------------------------------------------------------------------- */
747 : /* Open data file. */
748 : /* -------------------------------------------------------------------- */
749 25 : GDALDatasetH hDataset = nullptr;
750 25 : if (!bReadOnly)
751 : {
752 20 : CPLPushErrorHandler(GDALAddoErrorHandler);
753 22 : if (bClean &&
754 2 : aosOpenOptions.FetchNameValue("IGNORE_COG_LAYOUT_BREAK") == nullptr)
755 : {
756 2 : GDALDriverH hDrv = GDALIdentifyDriver(osFilename.c_str(), nullptr);
757 2 : if (hDrv && EQUAL(GDALGetDescription(hDrv), "GTiff"))
758 : {
759 : // Cleaning does not break COG layout
760 2 : aosOpenOptions.SetNameValue("IGNORE_COG_LAYOUT_BREAK", "YES");
761 : }
762 : }
763 :
764 20 : CPLSetCurrentErrorHandlerCatchDebug(FALSE);
765 : hDataset =
766 20 : GDALOpenEx(osFilename.c_str(), GDAL_OF_RASTER | GDAL_OF_UPDATE,
767 20 : nullptr, aosOpenOptions.List(), nullptr);
768 20 : CPLPopErrorHandler();
769 : const bool bIsCOG =
770 21 : (aoErrors.size() == 1 &&
771 1 : aoErrors[0].m_osMsg.find("C(loud) O(ptimized) G(eoTIFF) layout") !=
772 21 : std::string::npos &&
773 1 : aosOpenOptions.FetchNameValue("IGNORE_COG_LAYOUT_BREAK") ==
774 20 : nullptr);
775 20 : if (hDataset != nullptr || bIsCOG)
776 : {
777 21 : for (size_t i = 0; i < aoErrors.size(); i++)
778 : {
779 1 : CPLError(aoErrors[i].m_eErr, aoErrors[i].m_errNum, "%s",
780 1 : aoErrors[i].m_osMsg.c_str());
781 : }
782 20 : if (bIsCOG)
783 1 : exit(1);
784 : }
785 : }
786 :
787 24 : if (hDataset == nullptr)
788 5 : hDataset = GDALOpenEx(osFilename.c_str(),
789 : GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR, nullptr,
790 5 : aosOpenOptions.List(), nullptr);
791 24 : if (hDataset == nullptr)
792 0 : exit(2);
793 :
794 24 : if (!bClean && osResampling.empty())
795 : {
796 9 : auto poDS = GDALDataset::FromHandle(hDataset);
797 9 : if (poDS->GetRasterCount() > 0)
798 : {
799 9 : auto poBand = poDS->GetRasterBand(1);
800 9 : if (poBand->GetOverviewCount() > 0)
801 : {
802 : const char *pszResampling =
803 2 : poBand->GetOverview(0)->GetMetadataItem("RESAMPLING");
804 2 : if (pszResampling)
805 : {
806 2 : osResampling = pszResampling;
807 2 : if (pfnProgress == GDALDummyProgress)
808 0 : CPLDebug("GDAL",
809 : "Reusing resampling method %s from existing "
810 : "overview",
811 : pszResampling);
812 : else
813 2 : printf("Info: reusing resampling method %s from "
814 : "existing overview.\n",
815 : pszResampling);
816 : }
817 : }
818 : }
819 9 : if (osResampling.empty())
820 7 : osResampling = "nearest";
821 : }
822 :
823 : /* -------------------------------------------------------------------- */
824 : /* Clean overviews. */
825 : /* -------------------------------------------------------------------- */
826 24 : int nResultStatus = 0;
827 24 : void *pProgressArg = nullptr;
828 24 : const int nBandCount = static_cast<int>(anBandList.size());
829 24 : if (bClean)
830 : {
831 2 : if (GDALBuildOverviews(hDataset, "NONE", 0, nullptr, 0, nullptr,
832 2 : pfnProgress, pProgressArg) != CE_None)
833 : {
834 0 : fprintf(stderr, "Cleaning overviews failed.\n");
835 0 : nResultStatus = 200;
836 : }
837 : }
838 22 : else if (bPartialRefreshFromSourceTimestamp)
839 : {
840 2 : if (!PartialRefreshFromSourceTimestamp(
841 : GDALDataset::FromHandle(hDataset), osResampling.c_str(),
842 2 : static_cast<int>(anLevels.size()), anLevels.data(), nBandCount,
843 2 : anBandList.data(), bMinSizeSpecified, nMinSize, pfnProgress,
844 : pProgressArg))
845 : {
846 0 : nResultStatus = 1;
847 : }
848 : }
849 20 : else if (bPartialRefreshFromProjWin)
850 : {
851 1 : if (!PartialRefreshFromProjWin(
852 : GDALDataset::FromHandle(hDataset), dfULX, dfULY, dfLRX, dfLRY,
853 1 : osResampling.c_str(), static_cast<int>(anLevels.size()),
854 1 : anLevels.data(), nBandCount, anBandList.data(),
855 : bMinSizeSpecified, nMinSize, pfnProgress, pProgressArg))
856 : {
857 0 : nResultStatus = 1;
858 : }
859 : }
860 19 : else if (!aosSources.empty())
861 : {
862 1 : if (!PartialRefreshFromSourceExtent(
863 : GDALDataset::FromHandle(hDataset), aosSources,
864 1 : osResampling.c_str(), static_cast<int>(anLevels.size()),
865 1 : anLevels.data(), nBandCount, anBandList.data(),
866 : bMinSizeSpecified, nMinSize, pfnProgress, pProgressArg))
867 : {
868 0 : nResultStatus = 1;
869 : }
870 : }
871 : else
872 : {
873 : /* --------------------------------------------------------------------
874 : */
875 : /* Generate overviews. */
876 : /* --------------------------------------------------------------------
877 : */
878 :
879 : // If no levels are specified, reuse the potentially existing ones.
880 18 : if (anLevels.empty())
881 : {
882 8 : auto poDS = GDALDataset::FromHandle(hDataset);
883 8 : if (poDS->GetRasterCount() > 0)
884 : {
885 8 : auto poBand = poDS->GetRasterBand(1);
886 8 : const int nExistingCount = poBand->GetOverviewCount();
887 8 : if (nExistingCount > 0)
888 : {
889 12 : for (int iOvr = 0; iOvr < nExistingCount; ++iOvr)
890 : {
891 8 : auto poOverview = poBand->GetOverview(iOvr);
892 8 : if (poOverview)
893 : {
894 8 : const int nOvFactor = GDALComputeOvFactor(
895 : poOverview->GetXSize(), poBand->GetXSize(),
896 8 : poOverview->GetYSize(), poBand->GetYSize());
897 8 : anLevels.push_back(nOvFactor);
898 : }
899 : }
900 : }
901 : }
902 : }
903 :
904 18 : if (anLevels.empty())
905 : {
906 4 : const int nXSize = GDALGetRasterXSize(hDataset);
907 4 : const int nYSize = GDALGetRasterYSize(hDataset);
908 4 : int nOvrFactor = 1;
909 12 : while (DIV_ROUND_UP(nXSize, nOvrFactor) > nMinSize ||
910 4 : DIV_ROUND_UP(nYSize, nOvrFactor) > nMinSize)
911 : {
912 8 : nOvrFactor *= 2;
913 8 : anLevels.push_back(nOvrFactor);
914 : }
915 : }
916 :
917 : // Only HFA supports selected layers
918 18 : if (nBandCount > 0)
919 0 : CPLSetConfigOption("USE_RRD", "YES");
920 :
921 35 : if (!anLevels.empty() &&
922 17 : GDALBuildOverviews(hDataset, osResampling.c_str(),
923 17 : static_cast<int>(anLevels.size()),
924 17 : anLevels.data(), nBandCount, anBandList.data(),
925 : pfnProgress, pProgressArg) != CE_None)
926 : {
927 0 : fprintf(stderr, "Overview building failed.\n");
928 0 : nResultStatus = 100;
929 : }
930 : }
931 :
932 : /* -------------------------------------------------------------------- */
933 : /* Cleanup */
934 : /* -------------------------------------------------------------------- */
935 24 : if (GDALClose(hDataset) != CE_None)
936 : {
937 0 : if (nResultStatus == 0)
938 0 : nResultStatus = 1;
939 : }
940 :
941 24 : GDALDestroyDriverManager();
942 :
943 24 : return nResultStatus;
944 : }
945 :
946 0 : MAIN_END
|