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