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