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 (int i = 0; i < poVRTBand->nSources; ++i)
243 : {
244 : auto poSource =
245 2 : dynamic_cast<VRTSimpleSource *>(poVRTBand->papoSources[i]);
246 2 : if (poSource)
247 : {
248 : VSIStatBufL sStatSource;
249 2 : if (VSIStatL(poSource->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 : poSource->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 1 : region.osFilename = poSource->GetSourceDatasetName();
290 1 : region.nDstXOff = nXOff;
291 1 : region.nDstYOff = nYOff;
292 1 : region.nDstXSize = nXSize;
293 1 : region.nDstYSize = nYSize;
294 1 : regions.push_back(std::move(region));
295 : }
296 : }
297 : }
298 : }
299 : }
300 : #ifdef GTI_DRIVER_DISABLED_OR_PLUGIN
301 : else if (poDS->GetDriver() &&
302 : EQUAL(poDS->GetDriver()->GetDescription(), "GTI"))
303 : {
304 : CPLError(CE_Failure, CPLE_NotSupported,
305 : "--partial-refresh-from-source-timestamp only works on a GTI "
306 : "dataset if the GTI driver is not built as a plugin, "
307 : "but in core library");
308 : return false;
309 : }
310 : #else
311 1 : else if (auto poGTIDS = GDALDatasetCastToGTIDataset(poDS))
312 : {
313 1 : regions = GTIGetSourcesMoreRecentThan(poGTIDS, sStatOvr.st_mtime);
314 2 : for (const auto ®ion : regions)
315 : {
316 1 : dfTotalPixels +=
317 1 : static_cast<double>(region.nDstXSize) * region.nDstYSize;
318 : }
319 : }
320 : #endif
321 : else
322 : {
323 0 : CPLError(CE_Failure, CPLE_AppDefined,
324 : "--partial-refresh-from-source-timestamp only works on a VRT "
325 : "or GTI dataset");
326 0 : return false;
327 : }
328 :
329 2 : if (!regions.empty())
330 : {
331 2 : double dfCurPixels = 0;
332 4 : for (const auto ®ion : regions)
333 : {
334 2 : if (pfnProgress == GDALDummyProgress)
335 : {
336 0 : CPLDebug("GDAL", "Refresh from source %s",
337 : region.osFilename.c_str());
338 : }
339 : else
340 : {
341 2 : printf("Refresh from source %s.\n", region.osFilename.c_str());
342 : }
343 2 : const double dfNextCurPixels =
344 : dfCurPixels +
345 2 : static_cast<double>(region.nDstXSize) * region.nDstYSize;
346 2 : void *pScaledProgress = GDALCreateScaledProgress(
347 : dfCurPixels / dfTotalPixels, dfNextCurPixels / dfTotalPixels,
348 : pfnProgress, pProgressArg);
349 4 : bool bRet = PartialRefresh(
350 : poDS, anOvrIndices, nBandCount, panBandList, pszResampling,
351 2 : region.nDstXOff, region.nDstYOff, region.nDstXSize,
352 2 : region.nDstYSize, GDALScaledProgress, pScaledProgress);
353 2 : GDALDestroyScaledProgress(pScaledProgress);
354 2 : if (!bRet)
355 0 : return false;
356 2 : dfCurPixels = dfNextCurPixels;
357 : }
358 : }
359 : else
360 : {
361 0 : if (pfnProgress == GDALDummyProgress)
362 : {
363 0 : CPLDebug("GDAL", "No source is more recent than the overviews");
364 : }
365 : else
366 : {
367 0 : printf("No source is more recent than the overviews.\n");
368 : }
369 : }
370 :
371 2 : return true;
372 : }
373 :
374 : /************************************************************************/
375 : /* PartialRefreshFromSourceExtent() */
376 : /************************************************************************/
377 :
378 1 : static bool PartialRefreshFromSourceExtent(
379 : GDALDataset *poDS, const CPLStringList &aosSources,
380 : const char *pszResampling, int nLevelCount, const int *panLevels,
381 : int nBandCount, const int *panBandList, bool bMinSizeSpecified,
382 : int nMinSize, GDALProgressFunc pfnProgress, void *pProgressArg)
383 : {
384 2 : std::vector<int> anOvrIndices;
385 1 : if (!GetOvrIndices(poDS, nLevelCount, panLevels, bMinSizeSpecified,
386 : nMinSize, anOvrIndices))
387 0 : return false;
388 :
389 1 : GDALGeoTransform gt;
390 1 : if (poDS->GetGeoTransform(gt) != CE_None)
391 : {
392 0 : CPLError(CE_Failure, CPLE_AppDefined, "Dataset has no geotransform");
393 0 : return false;
394 : }
395 1 : GDALGeoTransform invGT;
396 1 : if (!gt.GetInverse(invGT))
397 : {
398 0 : return false;
399 : }
400 :
401 : struct Region
402 : {
403 : std::string osFileName;
404 : int nXOff;
405 : int nYOff;
406 : int nXSize;
407 : int nYSize;
408 : };
409 :
410 2 : std::vector<Region> regions;
411 :
412 : // Smallest positive double to avoid Coverity Scan complaining about divide_by_zero
413 1 : double dfTotalPixels = std::numeric_limits<double>::min();
414 2 : for (int i = 0; i < aosSources.size(); ++i)
415 : {
416 : auto poSrcDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
417 1 : aosSources[i], GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
418 1 : if (!poSrcDS)
419 0 : return false;
420 :
421 1 : GDALGeoTransform srcGT;
422 1 : if (poSrcDS->GetGeoTransform(srcGT) != CE_None)
423 : {
424 0 : CPLError(CE_Failure, CPLE_AppDefined,
425 : "Source dataset has no geotransform");
426 0 : return false;
427 : }
428 :
429 1 : const double dfULX = srcGT[0];
430 1 : const double dfULY = srcGT[3];
431 1 : const double dfLRX = srcGT[0] + poSrcDS->GetRasterXSize() * srcGT[1] +
432 1 : poSrcDS->GetRasterYSize() * srcGT[2];
433 1 : const double dfLRY = srcGT[3] + poSrcDS->GetRasterXSize() * srcGT[4] +
434 1 : poSrcDS->GetRasterYSize() * srcGT[5];
435 1 : const double dfX1 = invGT[0] + invGT[1] * dfULX + invGT[2] * dfULY;
436 1 : const double dfY1 = invGT[3] + invGT[4] * dfULX + invGT[5] * dfULY;
437 1 : const double dfX2 = invGT[0] + invGT[1] * dfLRX + invGT[2] * dfLRY;
438 1 : const double dfY2 = invGT[3] + invGT[4] * dfLRX + invGT[5] * dfLRY;
439 1 : constexpr double EPS = 1e-8;
440 : const int nXOff =
441 1 : static_cast<int>(std::max(0.0, std::min(dfX1, dfX2)) + EPS);
442 : const int nYOff =
443 1 : static_cast<int>(std::max(0.0, std::min(dfY1, dfY2)) + EPS);
444 : const int nXSize =
445 1 : static_cast<int>(
446 1 : std::ceil(std::min(static_cast<double>(poDS->GetRasterXSize()),
447 2 : std::max(dfX1, dfX2)) -
448 : EPS)) -
449 1 : nXOff;
450 : const int nYSize =
451 1 : static_cast<int>(
452 1 : std::ceil(std::min(static_cast<double>(poDS->GetRasterYSize()),
453 2 : std::max(dfY1, dfY2)) -
454 : EPS)) -
455 1 : nYOff;
456 :
457 1 : dfTotalPixels += static_cast<double>(nXSize) * nYSize;
458 2 : Region region;
459 1 : region.osFileName = aosSources[i];
460 1 : region.nXOff = nXOff;
461 1 : region.nYOff = nYOff;
462 1 : region.nXSize = nXSize;
463 1 : region.nYSize = nYSize;
464 1 : regions.push_back(std::move(region));
465 : }
466 :
467 1 : double dfCurPixels = 0;
468 2 : for (const auto ®ion : regions)
469 : {
470 1 : if (pfnProgress == GDALDummyProgress)
471 : {
472 0 : CPLDebug("GDAL", "Refresh from source %s",
473 : region.osFileName.c_str());
474 : }
475 : else
476 : {
477 1 : printf("Refresh from source %s.\n", region.osFileName.c_str());
478 : }
479 1 : const double dfNextCurPixels =
480 1 : dfCurPixels + static_cast<double>(region.nXSize) * region.nYSize;
481 1 : void *pScaledProgress = GDALCreateScaledProgress(
482 : dfCurPixels / dfTotalPixels, dfNextCurPixels / dfTotalPixels,
483 : pfnProgress, pProgressArg);
484 2 : bool bRet = PartialRefresh(poDS, anOvrIndices, nBandCount, panBandList,
485 1 : pszResampling, region.nXOff, region.nYOff,
486 1 : region.nXSize, region.nYSize,
487 : GDALScaledProgress, pScaledProgress);
488 1 : GDALDestroyScaledProgress(pScaledProgress);
489 1 : if (!bRet)
490 0 : return false;
491 1 : dfCurPixels = dfNextCurPixels;
492 : }
493 :
494 1 : return true;
495 : }
496 :
497 : /************************************************************************/
498 : /* PartialRefreshFromProjWin() */
499 : /************************************************************************/
500 :
501 1 : static bool PartialRefreshFromProjWin(
502 : GDALDataset *poDS, double dfULX, double dfULY, double dfLRX, double dfLRY,
503 : const char *pszResampling, int nLevelCount, const int *panLevels,
504 : int nBandCount, const int *panBandList, bool bMinSizeSpecified,
505 : int nMinSize, GDALProgressFunc pfnProgress, void *pProgressArg)
506 : {
507 2 : std::vector<int> anOvrIndices;
508 1 : if (!GetOvrIndices(poDS, nLevelCount, panLevels, bMinSizeSpecified,
509 : nMinSize, anOvrIndices))
510 0 : return false;
511 :
512 1 : GDALGeoTransform gt;
513 1 : if (poDS->GetGeoTransform(gt) != CE_None)
514 : {
515 0 : CPLError(CE_Failure, CPLE_AppDefined, "Dataset has no geotransform");
516 0 : return false;
517 : }
518 1 : GDALGeoTransform invGT;
519 1 : if (!gt.GetInverse(invGT))
520 : {
521 0 : return false;
522 : }
523 1 : const double dfX1 = invGT[0] + invGT[1] * dfULX + invGT[2] * dfULY;
524 1 : const double dfY1 = invGT[3] + invGT[4] * dfULX + invGT[5] * dfULY;
525 1 : const double dfX2 = invGT[0] + invGT[1] * dfLRX + invGT[2] * dfLRY;
526 1 : const double dfY2 = invGT[3] + invGT[4] * dfLRX + invGT[5] * dfLRY;
527 1 : constexpr double EPS = 1e-8;
528 : const int nXOff =
529 1 : static_cast<int>(std::max(0.0, std::min(dfX1, dfX2)) + EPS);
530 : const int nYOff =
531 1 : static_cast<int>(std::max(0.0, std::min(dfY1, dfY2)) + EPS);
532 1 : const int nXSize = static_cast<int>(std::ceil(
533 1 : std::min(static_cast<double>(poDS->GetRasterXSize()),
534 2 : std::max(dfX1, dfX2)) -
535 : EPS)) -
536 1 : nXOff;
537 1 : const int nYSize = static_cast<int>(std::ceil(
538 1 : std::min(static_cast<double>(poDS->GetRasterYSize()),
539 2 : std::max(dfY1, dfY2)) -
540 : EPS)) -
541 1 : nYOff;
542 1 : return PartialRefresh(poDS, anOvrIndices, nBandCount, panBandList,
543 : pszResampling, nXOff, nYOff, nXSize, nYSize,
544 1 : pfnProgress, pProgressArg);
545 : }
546 :
547 : /************************************************************************/
548 : /* main() */
549 : /************************************************************************/
550 :
551 30 : MAIN_START(nArgc, papszArgv)
552 :
553 : {
554 30 : EarlySetConfigOptions(nArgc, papszArgv);
555 30 : GDALAllRegister();
556 :
557 30 : nArgc = GDALGeneralCmdLineProcessor(nArgc, &papszArgv, 0);
558 30 : if (nArgc < 1)
559 2 : exit(-nArgc);
560 52 : CPLStringList aosArgv;
561 28 : aosArgv.Assign(papszArgv, /* bAssign = */ true);
562 :
563 80 : GDALArgumentParser argParser(aosArgv[0], /* bForBinary=*/true);
564 :
565 28 : argParser.add_description(_("Builds or rebuilds overview images."));
566 :
567 28 : const char *pszEpilog = _(
568 : "Useful configuration variables :\n"
569 : " --config USE_RRD YES : Use Erdas Imagine format (.aux) as overview "
570 : "format.\n"
571 : "Below, only for external overviews in GeoTIFF format:\n"
572 : " --config COMPRESS_OVERVIEW {JPEG,LZW,PACKBITS,DEFLATE} : TIFF "
573 : "compression\n"
574 : " --config PHOTOMETRIC_OVERVIEW {RGB,YCBCR,...} : TIFF photometric "
575 : "interp.\n"
576 : " --config INTERLEAVE_OVERVIEW {PIXEL|BAND} : TIFF interleaving "
577 : "method\n"
578 : " --config BIGTIFF_OVERVIEW {IF_NEEDED|IF_SAFER|YES|NO} : is BigTIFF "
579 : "used\n"
580 : "\n"
581 : "Examples:\n"
582 : " %% gdaladdo -r average abc.tif\n"
583 : " %% gdaladdo --config COMPRESS_OVERVIEW JPEG\n"
584 : " --config PHOTOMETRIC_OVERVIEW YCBCR\n"
585 : " --config INTERLEAVE_OVERVIEW PIXEL -ro abc.tif\n"
586 : "\n"
587 : "For more details, consult https://gdal.org/programs/gdaladdo.html");
588 28 : argParser.add_epilog(pszEpilog);
589 :
590 52 : std::string osResampling;
591 28 : argParser.add_argument("-r")
592 28 : .store_into(osResampling)
593 : .metavar("nearest|average|rms|gauss|bilinear|cubic|cubicspline|lanczos|"
594 56 : "average_magphase|mode")
595 28 : .help(_("Select a resampling algorithm."));
596 :
597 28 : bool bReadOnly = false;
598 28 : argParser.add_argument("-ro").store_into(bReadOnly).help(
599 : _("Open the dataset in read-only mode, in order to generate external "
600 28 : "overview."));
601 :
602 28 : bool bQuiet = false;
603 28 : argParser.add_quiet_argument(&bQuiet);
604 :
605 52 : std::vector<int> anBandList;
606 28 : argParser.add_argument("-b")
607 28 : .append()
608 56 : .metavar("<band>")
609 : .action(
610 0 : [&anBandList](const std::string &s)
611 : {
612 0 : const int nBand = atoi(s.c_str());
613 0 : if (nBand < 1)
614 : {
615 : throw std::invalid_argument(CPLSPrintf(
616 0 : "Unrecognizable band number (%s).", s.c_str()));
617 : }
618 0 : anBandList.push_back(nBand);
619 28 : })
620 28 : .help(_("Select input band(s) for overview generation."));
621 :
622 52 : CPLStringList aosOpenOptions;
623 28 : argParser.add_argument("-oo")
624 28 : .append()
625 56 : .metavar("<NAME=VALUE>")
626 1 : .action([&aosOpenOptions](const std::string &s)
627 29 : { aosOpenOptions.AddString(s.c_str()); })
628 28 : .help(_("Dataset open option (format-specific)."));
629 :
630 28 : int nMinSize = 256;
631 28 : argParser.add_argument("-minsize")
632 28 : .default_value(nMinSize)
633 56 : .metavar("<val>")
634 28 : .store_into(nMinSize)
635 28 : .help(_("Maximum width or height of the smallest overview level."));
636 :
637 28 : bool bClean = false;
638 28 : bool bPartialRefreshFromSourceTimestamp = false;
639 52 : std::string osPartialRefreshFromSourceExtent;
640 :
641 : {
642 28 : auto &group = argParser.add_mutually_exclusive_group();
643 28 : group.add_argument("-clean").store_into(bClean).help(
644 28 : _("Remove all overviews."));
645 :
646 28 : group.add_argument("--partial-refresh-from-source-timestamp")
647 28 : .store_into(bPartialRefreshFromSourceTimestamp)
648 : .help(_("Performs a partial refresh of existing overviews, when "
649 28 : "<filename> is a VRT file with an external overview."));
650 :
651 28 : group.add_argument("--partial-refresh-from-projwin")
652 56 : .metavar("<ulx> <uly> <lrx> <lry>")
653 28 : .nargs(4)
654 28 : .scan<'g', double>()
655 : .help(
656 : _("Performs a partial refresh of existing overviews, in the "
657 28 : "region of interest specified by georeference coordinates."));
658 :
659 28 : group.add_argument("--partial-refresh-from-source-extent")
660 56 : .metavar("<filename1>[,<filenameN>]...")
661 28 : .store_into(osPartialRefreshFromSourceExtent)
662 : .help(
663 : _("Performs a partial refresh of existing overviews, in the "
664 28 : "region of interest specified by one or several filename."));
665 : }
666 :
667 52 : std::string osFilename;
668 28 : argParser.add_argument("filename")
669 28 : .store_into(osFilename)
670 : .help(_("The file to build overviews for (or whose overviews must be "
671 28 : "removed)."));
672 :
673 56 : argParser.add_argument("level").remaining().metavar("<level>").help(
674 28 : _("A list of integral overview levels to build."));
675 :
676 : try
677 : {
678 28 : argParser.parse_args(aosArgv);
679 : }
680 0 : catch (const std::exception &err)
681 : {
682 0 : argParser.display_error_and_usage(err);
683 0 : std::exit(1);
684 : }
685 :
686 52 : std::vector<int> anLevels;
687 52 : auto levels = argParser.present<std::vector<std::string>>("level");
688 28 : if (levels)
689 : {
690 29 : for (const auto &level : *levels)
691 : {
692 18 : if (CPLGetValueType(level.c_str()) != CPL_VALUE_INTEGER)
693 : {
694 1 : CPLError(
695 : CE_Failure, CPLE_IllegalArg,
696 : "Value '%s' is not a positive integer subsampling factor",
697 : level.c_str());
698 1 : std::exit(1);
699 : }
700 17 : anLevels.push_back(atoi(level.c_str()));
701 17 : if (anLevels.back() <= 0)
702 : {
703 2 : CPLError(
704 : CE_Failure, CPLE_IllegalArg,
705 : "Value '%s' is not a positive integer subsampling factor",
706 : level.c_str());
707 2 : std::exit(1);
708 : }
709 15 : if (anLevels.back() == 1)
710 : {
711 0 : printf(
712 : "Warning: Overview with subsampling factor of 1 requested. "
713 : "This will copy the full resolution dataset in the "
714 : "overview!\n");
715 : }
716 : }
717 : }
718 :
719 25 : GDALProgressFunc pfnProgress =
720 25 : bQuiet ? GDALDummyProgress : GDALTermProgress;
721 25 : const bool bMinSizeSpecified = argParser.is_used("-minsize");
722 :
723 25 : CPLStringList aosSources;
724 25 : if (!osPartialRefreshFromSourceExtent.empty())
725 : {
726 : aosSources = CSLTokenizeString2(
727 1 : osPartialRefreshFromSourceExtent.c_str(), ",", 0);
728 : }
729 :
730 25 : bool bPartialRefreshFromProjWin = false;
731 25 : double dfULX = 0;
732 25 : double dfULY = 0;
733 25 : double dfLRX = 0;
734 25 : double dfLRY = 0;
735 25 : if (auto oProjWin = argParser.present<std::vector<double>>(
736 50 : "--partial-refresh-from-projwin"))
737 : {
738 1 : bPartialRefreshFromProjWin = true;
739 1 : dfULX = (*oProjWin)[0];
740 1 : dfULY = (*oProjWin)[1];
741 1 : dfLRX = (*oProjWin)[2];
742 1 : dfLRY = (*oProjWin)[3];
743 : }
744 :
745 : /* -------------------------------------------------------------------- */
746 : /* Open data file. */
747 : /* -------------------------------------------------------------------- */
748 25 : GDALDatasetH hDataset = nullptr;
749 25 : if (!bReadOnly)
750 : {
751 20 : CPLPushErrorHandler(GDALAddoErrorHandler);
752 22 : if (bClean &&
753 2 : aosOpenOptions.FetchNameValue("IGNORE_COG_LAYOUT_BREAK") == nullptr)
754 : {
755 2 : GDALDriverH hDrv = GDALIdentifyDriver(osFilename.c_str(), nullptr);
756 2 : if (hDrv && EQUAL(GDALGetDescription(hDrv), "GTiff"))
757 : {
758 : // Cleaning does not break COG layout
759 2 : aosOpenOptions.SetNameValue("IGNORE_COG_LAYOUT_BREAK", "YES");
760 : }
761 : }
762 :
763 20 : CPLSetCurrentErrorHandlerCatchDebug(FALSE);
764 : hDataset =
765 20 : GDALOpenEx(osFilename.c_str(), GDAL_OF_RASTER | GDAL_OF_UPDATE,
766 20 : nullptr, aosOpenOptions.List(), nullptr);
767 20 : CPLPopErrorHandler();
768 : const bool bIsCOG =
769 21 : (aoErrors.size() == 1 &&
770 1 : aoErrors[0].m_osMsg.find("C(loud) O(ptimized) G(eoTIFF) layout") !=
771 21 : std::string::npos &&
772 1 : aosOpenOptions.FetchNameValue("IGNORE_COG_LAYOUT_BREAK") ==
773 20 : nullptr);
774 20 : if (hDataset != nullptr || bIsCOG)
775 : {
776 21 : for (size_t i = 0; i < aoErrors.size(); i++)
777 : {
778 1 : CPLError(aoErrors[i].m_eErr, aoErrors[i].m_errNum, "%s",
779 1 : aoErrors[i].m_osMsg.c_str());
780 : }
781 20 : if (bIsCOG)
782 1 : exit(1);
783 : }
784 : }
785 :
786 24 : if (hDataset == nullptr)
787 5 : hDataset = GDALOpenEx(osFilename.c_str(),
788 : GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR, nullptr,
789 5 : aosOpenOptions.List(), nullptr);
790 24 : if (hDataset == nullptr)
791 0 : exit(2);
792 :
793 24 : if (!bClean && osResampling.empty())
794 : {
795 9 : auto poDS = GDALDataset::FromHandle(hDataset);
796 9 : if (poDS->GetRasterCount() > 0)
797 : {
798 9 : auto poBand = poDS->GetRasterBand(1);
799 9 : if (poBand->GetOverviewCount() > 0)
800 : {
801 : const char *pszResampling =
802 2 : poBand->GetOverview(0)->GetMetadataItem("RESAMPLING");
803 2 : if (pszResampling)
804 : {
805 2 : osResampling = pszResampling;
806 2 : if (pfnProgress == GDALDummyProgress)
807 0 : CPLDebug("GDAL",
808 : "Reusing resampling method %s from existing "
809 : "overview",
810 : pszResampling);
811 : else
812 2 : printf("Info: reusing resampling method %s from "
813 : "existing overview.\n",
814 : pszResampling);
815 : }
816 : }
817 : }
818 9 : if (osResampling.empty())
819 7 : osResampling = "nearest";
820 : }
821 :
822 : /* -------------------------------------------------------------------- */
823 : /* Clean overviews. */
824 : /* -------------------------------------------------------------------- */
825 24 : int nResultStatus = 0;
826 24 : void *pProgressArg = nullptr;
827 24 : const int nBandCount = static_cast<int>(anBandList.size());
828 24 : if (bClean)
829 : {
830 2 : if (GDALBuildOverviews(hDataset, "NONE", 0, nullptr, 0, nullptr,
831 2 : pfnProgress, pProgressArg) != CE_None)
832 : {
833 0 : fprintf(stderr, "Cleaning overviews failed.\n");
834 0 : nResultStatus = 200;
835 : }
836 : }
837 22 : else if (bPartialRefreshFromSourceTimestamp)
838 : {
839 2 : if (!PartialRefreshFromSourceTimestamp(
840 : GDALDataset::FromHandle(hDataset), osResampling.c_str(),
841 2 : static_cast<int>(anLevels.size()), anLevels.data(), nBandCount,
842 2 : anBandList.data(), bMinSizeSpecified, nMinSize, pfnProgress,
843 : pProgressArg))
844 : {
845 0 : nResultStatus = 1;
846 : }
847 : }
848 20 : else if (bPartialRefreshFromProjWin)
849 : {
850 1 : if (!PartialRefreshFromProjWin(
851 : GDALDataset::FromHandle(hDataset), dfULX, dfULY, dfLRX, dfLRY,
852 1 : osResampling.c_str(), static_cast<int>(anLevels.size()),
853 1 : anLevels.data(), nBandCount, anBandList.data(),
854 : bMinSizeSpecified, nMinSize, pfnProgress, pProgressArg))
855 : {
856 0 : nResultStatus = 1;
857 : }
858 : }
859 19 : else if (!aosSources.empty())
860 : {
861 1 : if (!PartialRefreshFromSourceExtent(
862 : GDALDataset::FromHandle(hDataset), aosSources,
863 1 : osResampling.c_str(), static_cast<int>(anLevels.size()),
864 1 : anLevels.data(), nBandCount, anBandList.data(),
865 : bMinSizeSpecified, nMinSize, pfnProgress, pProgressArg))
866 : {
867 0 : nResultStatus = 1;
868 : }
869 : }
870 : else
871 : {
872 : /* --------------------------------------------------------------------
873 : */
874 : /* Generate overviews. */
875 : /* --------------------------------------------------------------------
876 : */
877 :
878 : // If no levels are specified, reuse the potentially existing ones.
879 18 : if (anLevels.empty())
880 : {
881 8 : auto poDS = GDALDataset::FromHandle(hDataset);
882 8 : if (poDS->GetRasterCount() > 0)
883 : {
884 8 : auto poBand = poDS->GetRasterBand(1);
885 8 : const int nExistingCount = poBand->GetOverviewCount();
886 8 : if (nExistingCount > 0)
887 : {
888 12 : for (int iOvr = 0; iOvr < nExistingCount; ++iOvr)
889 : {
890 8 : auto poOverview = poBand->GetOverview(iOvr);
891 8 : if (poOverview)
892 : {
893 8 : const int nOvFactor = GDALComputeOvFactor(
894 : poOverview->GetXSize(), poBand->GetXSize(),
895 8 : poOverview->GetYSize(), poBand->GetYSize());
896 8 : anLevels.push_back(nOvFactor);
897 : }
898 : }
899 : }
900 : }
901 : }
902 :
903 18 : if (anLevels.empty())
904 : {
905 4 : const int nXSize = GDALGetRasterXSize(hDataset);
906 4 : const int nYSize = GDALGetRasterYSize(hDataset);
907 4 : int nOvrFactor = 1;
908 12 : while (DIV_ROUND_UP(nXSize, nOvrFactor) > nMinSize ||
909 4 : DIV_ROUND_UP(nYSize, nOvrFactor) > nMinSize)
910 : {
911 8 : nOvrFactor *= 2;
912 8 : anLevels.push_back(nOvrFactor);
913 : }
914 : }
915 :
916 : // Only HFA supports selected layers
917 18 : if (nBandCount > 0)
918 0 : CPLSetConfigOption("USE_RRD", "YES");
919 :
920 35 : if (!anLevels.empty() &&
921 17 : GDALBuildOverviews(hDataset, osResampling.c_str(),
922 17 : static_cast<int>(anLevels.size()),
923 17 : anLevels.data(), nBandCount, anBandList.data(),
924 : pfnProgress, pProgressArg) != CE_None)
925 : {
926 0 : fprintf(stderr, "Overview building failed.\n");
927 0 : nResultStatus = 100;
928 : }
929 : }
930 :
931 : /* -------------------------------------------------------------------- */
932 : /* Cleanup */
933 : /* -------------------------------------------------------------------- */
934 24 : if (GDALClose(hDataset) != CE_None)
935 : {
936 0 : if (nResultStatus == 0)
937 0 : nResultStatus = 1;
938 : }
939 :
940 24 : GDALDestroyDriverManager();
941 :
942 24 : return nResultStatus;
943 : }
944 :
945 0 : MAIN_END
|