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