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