Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL Utilities
4 : * Purpose: Command line application to build VRT datasets from raster products
5 : * or content of SHP tile index
6 : * Author: Even Rouault, <even dot rouault at spatialys dot com>
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2007-2016, Even Rouault <even dot rouault at spatialys dot com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "ogr_api.h"
15 : #include "ogr_srs_api.h"
16 :
17 : #include "cpl_port.h"
18 : #include "gdal_utils.h"
19 : #include "gdal_utils_priv.h"
20 : #include "gdalargumentparser.h"
21 :
22 : #include <cassert>
23 : #include <cmath>
24 : #include <cstdio>
25 : #include <cstdlib>
26 : #include <cstring>
27 :
28 : #include <algorithm>
29 : #include <memory>
30 : #include <set>
31 : #include <string>
32 : #include <vector>
33 :
34 : #include "commonutils.h"
35 : #include "cpl_conv.h"
36 : #include "cpl_error.h"
37 : #include "cpl_progress.h"
38 : #include "cpl_string.h"
39 : #include "cpl_vsi.h"
40 : #include "cpl_vsi_virtual.h"
41 : #include "gdal.h"
42 : #include "gdal_vrt.h"
43 : #include "gdal_priv.h"
44 : #include "gdal_proxy.h"
45 : #include "ogr_api.h"
46 : #include "ogr_core.h"
47 : #include "ogr_srs_api.h"
48 : #include "ogr_spatialref.h"
49 : #include "ogrsf_frmts.h"
50 : #include "vrtdataset.h"
51 :
52 : #define GEOTRSFRM_TOPLEFT_X 0
53 : #define GEOTRSFRM_WE_RES 1
54 : #define GEOTRSFRM_ROTATION_PARAM1 2
55 : #define GEOTRSFRM_TOPLEFT_Y 3
56 : #define GEOTRSFRM_ROTATION_PARAM2 4
57 : #define GEOTRSFRM_NS_RES 5
58 :
59 : namespace gdal::GDALBuildVRT
60 : {
61 : typedef enum
62 : {
63 : LOWEST_RESOLUTION,
64 : HIGHEST_RESOLUTION,
65 : AVERAGE_RESOLUTION,
66 : USER_RESOLUTION
67 : } ResolutionStrategy;
68 :
69 : struct DatasetProperty
70 : {
71 : int isFileOK = FALSE;
72 : int nRasterXSize = 0;
73 : int nRasterYSize = 0;
74 : double adfGeoTransform[6];
75 : int nBlockXSize = 0;
76 : int nBlockYSize = 0;
77 : std::vector<GDALDataType> aeBandType{};
78 : std::vector<bool> abHasNoData{};
79 : std::vector<double> adfNoDataValues{};
80 : std::vector<bool> abHasOffset{};
81 : std::vector<double> adfOffset{};
82 : std::vector<bool> abHasScale{};
83 : std::vector<bool> abHasMaskBand{};
84 : std::vector<double> adfScale{};
85 : int bHasDatasetMask = 0;
86 : bool bLastBandIsAlpha = false;
87 : int nMaskBlockXSize = 0;
88 : int nMaskBlockYSize = 0;
89 : std::vector<int> anOverviewFactors{};
90 :
91 2266 : DatasetProperty()
92 2266 : {
93 2266 : adfGeoTransform[0] = 0;
94 2266 : adfGeoTransform[1] = 0;
95 2266 : adfGeoTransform[2] = 0;
96 2266 : adfGeoTransform[3] = 0;
97 2266 : adfGeoTransform[4] = 0;
98 2266 : adfGeoTransform[5] = 0;
99 2266 : }
100 : };
101 :
102 : struct BandProperty
103 : {
104 : GDALColorInterp colorInterpretation = GCI_Undefined;
105 : GDALDataType dataType = GDT_Unknown;
106 : std::unique_ptr<GDALColorTable> colorTable{};
107 : bool bHasNoData = false;
108 : double noDataValue = 0;
109 : bool bHasOffset = false;
110 : double dfOffset = 0;
111 : bool bHasScale = false;
112 : double dfScale = 0;
113 : };
114 : } // namespace gdal::GDALBuildVRT
115 :
116 : using namespace gdal::GDALBuildVRT;
117 :
118 : /************************************************************************/
119 : /* GetSrcDstWin() */
120 : /************************************************************************/
121 :
122 2253 : static int GetSrcDstWin(DatasetProperty *psDP, double we_res, double ns_res,
123 : double minX, double minY, double maxX, double maxY,
124 : int nTargetXSize, int nTargetYSize, double *pdfSrcXOff,
125 : double *pdfSrcYOff, double *pdfSrcXSize,
126 : double *pdfSrcYSize, double *pdfDstXOff,
127 : double *pdfDstYOff, double *pdfDstXSize,
128 : double *pdfDstYSize)
129 : {
130 : /* Check that the destination bounding box intersects the source bounding
131 : * box */
132 2253 : if (psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_X] +
133 2253 : psDP->nRasterXSize * psDP->adfGeoTransform[GEOTRSFRM_WE_RES] <=
134 : minX)
135 0 : return FALSE;
136 2253 : if (psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_X] >= maxX)
137 1 : return FALSE;
138 2252 : if (psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] +
139 2252 : psDP->nRasterYSize * psDP->adfGeoTransform[GEOTRSFRM_NS_RES] >=
140 : maxY)
141 0 : return FALSE;
142 2252 : if (psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] <= minY)
143 0 : return FALSE;
144 :
145 2252 : if (psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_X] < minX)
146 : {
147 1 : *pdfSrcXOff = (minX - psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_X]) /
148 1 : psDP->adfGeoTransform[GEOTRSFRM_WE_RES];
149 1 : *pdfDstXOff = 0.0;
150 : }
151 : else
152 : {
153 2251 : *pdfSrcXOff = 0.0;
154 2251 : *pdfDstXOff =
155 2251 : ((psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_X] - minX) / we_res);
156 : }
157 2252 : if (maxY < psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_Y])
158 : {
159 2 : *pdfSrcYOff = (psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] - maxY) /
160 2 : -psDP->adfGeoTransform[GEOTRSFRM_NS_RES];
161 2 : *pdfDstYOff = 0.0;
162 : }
163 : else
164 : {
165 2250 : *pdfSrcYOff = 0.0;
166 2250 : *pdfDstYOff =
167 2250 : ((maxY - psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_Y]) / -ns_res);
168 : }
169 :
170 2252 : *pdfSrcXSize = psDP->nRasterXSize;
171 2252 : *pdfSrcYSize = psDP->nRasterYSize;
172 2252 : if (*pdfSrcXOff > 0)
173 1 : *pdfSrcXSize -= *pdfSrcXOff;
174 2252 : if (*pdfSrcYOff > 0)
175 2 : *pdfSrcYSize -= *pdfSrcYOff;
176 :
177 2252 : const double dfSrcToDstXSize =
178 2252 : psDP->adfGeoTransform[GEOTRSFRM_WE_RES] / we_res;
179 2252 : *pdfDstXSize = *pdfSrcXSize * dfSrcToDstXSize;
180 2252 : const double dfSrcToDstYSize =
181 2252 : psDP->adfGeoTransform[GEOTRSFRM_NS_RES] / ns_res;
182 2252 : *pdfDstYSize = *pdfSrcYSize * dfSrcToDstYSize;
183 :
184 2252 : if (*pdfDstXOff + *pdfDstXSize > nTargetXSize)
185 : {
186 4 : *pdfDstXSize = nTargetXSize - *pdfDstXOff;
187 4 : *pdfSrcXSize = *pdfDstXSize / dfSrcToDstXSize;
188 : }
189 :
190 2252 : if (*pdfDstYOff + *pdfDstYSize > nTargetYSize)
191 : {
192 1 : *pdfDstYSize = nTargetYSize - *pdfDstYOff;
193 1 : *pdfSrcYSize = *pdfDstYSize / dfSrcToDstYSize;
194 : }
195 :
196 4504 : return *pdfSrcXSize > 0 && *pdfDstXSize > 0 && *pdfSrcYSize > 0 &&
197 4504 : *pdfDstYSize > 0;
198 : }
199 :
200 : /************************************************************************/
201 : /* VRTBuilder */
202 : /************************************************************************/
203 :
204 : class VRTBuilder
205 : {
206 : /* Input parameters */
207 : bool bStrict = false;
208 : char *pszOutputFilename = nullptr;
209 : int nInputFiles = 0;
210 : char **ppszInputFilenames = nullptr;
211 : int nSrcDSCount = 0;
212 : GDALDatasetH *pahSrcDS = nullptr;
213 : int nTotalBands = 0;
214 : bool bLastBandIsAlpha = false;
215 : bool bExplicitBandList = false;
216 : int nMaxSelectedBandNo = 0;
217 : int nSelectedBands = 0;
218 : int *panSelectedBandList = nullptr;
219 : ResolutionStrategy resolutionStrategy = AVERAGE_RESOLUTION;
220 : int nCountValid = 0;
221 : double we_res = 0;
222 : double ns_res = 0;
223 : int bTargetAlignedPixels = 0;
224 : double minX = 0;
225 : double minY = 0;
226 : double maxX = 0;
227 : double maxY = 0;
228 : int bSeparate = 0;
229 : int bAllowProjectionDifference = 0;
230 : int bAddAlpha = 0;
231 : int bHideNoData = 0;
232 : int nSubdataset = 0;
233 : char *pszSrcNoData = nullptr;
234 : char *pszVRTNoData = nullptr;
235 : char *pszOutputSRS = nullptr;
236 : char *pszResampling = nullptr;
237 : char **papszOpenOptions = nullptr;
238 : bool bUseSrcMaskBand = true;
239 : bool bNoDataFromMask = false;
240 : double dfMaskValueThreshold = 0;
241 : CPLStringList aosCreateOptions{};
242 :
243 : /* Internal variables */
244 : char *pszProjectionRef = nullptr;
245 : std::vector<BandProperty> asBandProperties{};
246 : int bFirst = TRUE;
247 : int bHasGeoTransform = 0;
248 : int nRasterXSize = 0;
249 : int nRasterYSize = 0;
250 : std::vector<DatasetProperty> asDatasetProperties{};
251 : int bUserExtent = 0;
252 : int bAllowSrcNoData = TRUE;
253 : double *padfSrcNoData = nullptr;
254 : int nSrcNoDataCount = 0;
255 : int bAllowVRTNoData = TRUE;
256 : double *padfVRTNoData = nullptr;
257 : int nVRTNoDataCount = 0;
258 : int bHasRunBuild = 0;
259 : int bHasDatasetMask = 0;
260 :
261 : std::string AnalyseRaster(GDALDatasetH hDS,
262 : DatasetProperty *psDatasetProperties);
263 :
264 : void CreateVRTSeparate(VRTDatasetH hVRTDS);
265 : void CreateVRTNonSeparate(VRTDatasetH hVRTDS);
266 :
267 : CPL_DISALLOW_COPY_ASSIGN(VRTBuilder)
268 :
269 : public:
270 : VRTBuilder(bool bStrictIn, const char *pszOutputFilename, int nInputFiles,
271 : const char *const *ppszInputFilenames, GDALDatasetH *pahSrcDSIn,
272 : const int *panSelectedBandListIn, int nBandCount,
273 : ResolutionStrategy resolutionStrategy, double we_res,
274 : double ns_res, int bTargetAlignedPixels, double minX,
275 : double minY, double maxX, double maxY, int bSeparate,
276 : int bAllowProjectionDifference, int bAddAlpha, int bHideNoData,
277 : int nSubdataset, const char *pszSrcNoData,
278 : const char *pszVRTNoData, bool bUseSrcMaskBand,
279 : bool bNoDataFromMask, double dfMaskValueThreshold,
280 : const char *pszOutputSRS, const char *pszResampling,
281 : const char *const *papszOpenOptionsIn,
282 : const CPLStringList &aosCreateOptionsIn);
283 :
284 : ~VRTBuilder();
285 :
286 : GDALDataset *Build(GDALProgressFunc pfnProgress, void *pProgressData);
287 : };
288 :
289 : /************************************************************************/
290 : /* VRTBuilder() */
291 : /************************************************************************/
292 :
293 158 : VRTBuilder::VRTBuilder(
294 : bool bStrictIn, const char *pszOutputFilenameIn, int nInputFilesIn,
295 : const char *const *ppszInputFilenamesIn, GDALDatasetH *pahSrcDSIn,
296 : const int *panSelectedBandListIn, int nBandCount,
297 : ResolutionStrategy resolutionStrategyIn, double we_resIn, double ns_resIn,
298 : int bTargetAlignedPixelsIn, double minXIn, double minYIn, double maxXIn,
299 : double maxYIn, int bSeparateIn, int bAllowProjectionDifferenceIn,
300 : int bAddAlphaIn, int bHideNoDataIn, int nSubdatasetIn,
301 : const char *pszSrcNoDataIn, const char *pszVRTNoDataIn,
302 : bool bUseSrcMaskBandIn, bool bNoDataFromMaskIn,
303 : double dfMaskValueThresholdIn, const char *pszOutputSRSIn,
304 : const char *pszResamplingIn, const char *const *papszOpenOptionsIn,
305 158 : const CPLStringList &aosCreateOptionsIn)
306 158 : : bStrict(bStrictIn), aosCreateOptions(aosCreateOptionsIn)
307 : {
308 158 : pszOutputFilename = CPLStrdup(pszOutputFilenameIn);
309 158 : nInputFiles = nInputFilesIn;
310 158 : papszOpenOptions = CSLDuplicate(const_cast<char **>(papszOpenOptionsIn));
311 :
312 158 : if (ppszInputFilenamesIn)
313 : {
314 93 : ppszInputFilenames =
315 93 : static_cast<char **>(CPLMalloc(nInputFiles * sizeof(char *)));
316 1257 : for (int i = 0; i < nInputFiles; i++)
317 : {
318 1164 : ppszInputFilenames[i] = CPLStrdup(ppszInputFilenamesIn[i]);
319 : }
320 : }
321 65 : else if (pahSrcDSIn)
322 : {
323 65 : nSrcDSCount = nInputFiles;
324 65 : pahSrcDS = static_cast<GDALDatasetH *>(
325 65 : CPLMalloc(nInputFiles * sizeof(GDALDatasetH)));
326 65 : memcpy(pahSrcDS, pahSrcDSIn, nInputFiles * sizeof(GDALDatasetH));
327 65 : ppszInputFilenames =
328 65 : static_cast<char **>(CPLMalloc(nInputFiles * sizeof(char *)));
329 1167 : for (int i = 0; i < nInputFiles; i++)
330 : {
331 2204 : ppszInputFilenames[i] =
332 1102 : CPLStrdup(GDALGetDescription(pahSrcDSIn[i]));
333 : }
334 : }
335 :
336 158 : bExplicitBandList = nBandCount != 0;
337 158 : nSelectedBands = nBandCount;
338 158 : if (nBandCount)
339 : {
340 18 : panSelectedBandList =
341 18 : static_cast<int *>(CPLMalloc(nSelectedBands * sizeof(int)));
342 18 : memcpy(panSelectedBandList, panSelectedBandListIn,
343 18 : nSelectedBands * sizeof(int));
344 : }
345 :
346 158 : resolutionStrategy = resolutionStrategyIn;
347 158 : we_res = we_resIn;
348 158 : ns_res = ns_resIn;
349 158 : bTargetAlignedPixels = bTargetAlignedPixelsIn;
350 158 : minX = minXIn;
351 158 : minY = minYIn;
352 158 : maxX = maxXIn;
353 158 : maxY = maxYIn;
354 158 : bSeparate = bSeparateIn;
355 158 : bAllowProjectionDifference = bAllowProjectionDifferenceIn;
356 158 : bAddAlpha = bAddAlphaIn;
357 158 : bHideNoData = bHideNoDataIn;
358 158 : nSubdataset = nSubdatasetIn;
359 158 : pszSrcNoData = (pszSrcNoDataIn) ? CPLStrdup(pszSrcNoDataIn) : nullptr;
360 158 : pszVRTNoData = (pszVRTNoDataIn) ? CPLStrdup(pszVRTNoDataIn) : nullptr;
361 158 : pszOutputSRS = (pszOutputSRSIn) ? CPLStrdup(pszOutputSRSIn) : nullptr;
362 158 : pszResampling = (pszResamplingIn) ? CPLStrdup(pszResamplingIn) : nullptr;
363 158 : bUseSrcMaskBand = bUseSrcMaskBandIn;
364 158 : bNoDataFromMask = bNoDataFromMaskIn;
365 158 : dfMaskValueThreshold = dfMaskValueThresholdIn;
366 158 : }
367 :
368 : /************************************************************************/
369 : /* ~VRTBuilder() */
370 : /************************************************************************/
371 :
372 158 : VRTBuilder::~VRTBuilder()
373 : {
374 158 : CPLFree(pszOutputFilename);
375 158 : CPLFree(pszSrcNoData);
376 158 : CPLFree(pszVRTNoData);
377 158 : CPLFree(panSelectedBandList);
378 :
379 158 : if (ppszInputFilenames)
380 : {
381 2424 : for (int i = 0; i < nInputFiles; i++)
382 : {
383 2266 : CPLFree(ppszInputFilenames[i]);
384 : }
385 : }
386 158 : CPLFree(ppszInputFilenames);
387 158 : CPLFree(pahSrcDS);
388 :
389 158 : CPLFree(pszProjectionRef);
390 158 : CPLFree(padfSrcNoData);
391 158 : CPLFree(padfVRTNoData);
392 158 : CPLFree(pszOutputSRS);
393 158 : CPLFree(pszResampling);
394 158 : CSLDestroy(papszOpenOptions);
395 158 : }
396 :
397 : /************************************************************************/
398 : /* ProjAreEqual() */
399 : /************************************************************************/
400 :
401 2106 : static int ProjAreEqual(const char *pszWKT1, const char *pszWKT2)
402 : {
403 2106 : if (EQUAL(pszWKT1, pszWKT2))
404 2105 : return TRUE;
405 :
406 1 : OGRSpatialReferenceH hSRS1 = OSRNewSpatialReference(pszWKT1);
407 1 : OGRSpatialReferenceH hSRS2 = OSRNewSpatialReference(pszWKT2);
408 1 : int bRet = hSRS1 != nullptr && hSRS2 != nullptr && OSRIsSame(hSRS1, hSRS2);
409 1 : if (hSRS1)
410 1 : OSRDestroySpatialReference(hSRS1);
411 1 : if (hSRS2)
412 1 : OSRDestroySpatialReference(hSRS2);
413 1 : return bRet;
414 : }
415 :
416 : /************************************************************************/
417 : /* GetProjectionName() */
418 : /************************************************************************/
419 :
420 2 : static CPLString GetProjectionName(const char *pszProjection)
421 : {
422 2 : if (!pszProjection)
423 0 : return "(null)";
424 :
425 4 : OGRSpatialReference oSRS;
426 2 : oSRS.SetFromUserInput(pszProjection);
427 2 : const char *pszRet = nullptr;
428 2 : if (oSRS.IsProjected())
429 0 : pszRet = oSRS.GetAttrValue("PROJCS");
430 2 : else if (oSRS.IsGeographic())
431 2 : pszRet = oSRS.GetAttrValue("GEOGCS");
432 2 : return pszRet ? pszRet : "(null)";
433 : }
434 :
435 : /************************************************************************/
436 : /* AnalyseRaster() */
437 : /************************************************************************/
438 :
439 2264 : std::string VRTBuilder::AnalyseRaster(GDALDatasetH hDS,
440 : DatasetProperty *psDatasetProperties)
441 : {
442 2264 : GDALDataset *poDS = GDALDataset::FromHandle(hDS);
443 2264 : const char *dsFileName = poDS->GetDescription();
444 2264 : char **papszMetadata = poDS->GetMetadata("SUBDATASETS");
445 2264 : if (CSLCount(papszMetadata) > 0 && poDS->GetRasterCount() == 0)
446 : {
447 0 : ppszInputFilenames = static_cast<char **>(CPLRealloc(
448 0 : ppszInputFilenames,
449 0 : sizeof(char *) * (nInputFiles + CSLCount(papszMetadata))));
450 0 : if (nSubdataset < 0)
451 : {
452 0 : int count = 1;
453 : char subdatasetNameKey[80];
454 0 : snprintf(subdatasetNameKey, sizeof(subdatasetNameKey),
455 : "SUBDATASET_%d_NAME", count);
456 0 : while (*papszMetadata != nullptr)
457 : {
458 0 : if (EQUALN(*papszMetadata, subdatasetNameKey,
459 : strlen(subdatasetNameKey)))
460 : {
461 0 : asDatasetProperties.resize(nInputFiles + 1);
462 0 : ppszInputFilenames[nInputFiles] = CPLStrdup(
463 0 : *papszMetadata + strlen(subdatasetNameKey) + 1);
464 0 : nInputFiles++;
465 0 : count++;
466 0 : snprintf(subdatasetNameKey, sizeof(subdatasetNameKey),
467 : "SUBDATASET_%d_NAME", count);
468 : }
469 0 : papszMetadata++;
470 : }
471 : }
472 : else
473 : {
474 : char subdatasetNameKey[80];
475 : const char *pszSubdatasetName;
476 :
477 0 : snprintf(subdatasetNameKey, sizeof(subdatasetNameKey),
478 : "SUBDATASET_%d_NAME", nSubdataset);
479 : pszSubdatasetName =
480 0 : CSLFetchNameValue(papszMetadata, subdatasetNameKey);
481 0 : if (pszSubdatasetName)
482 : {
483 0 : asDatasetProperties.resize(nInputFiles + 1);
484 0 : ppszInputFilenames[nInputFiles] = CPLStrdup(pszSubdatasetName);
485 0 : nInputFiles++;
486 : }
487 : }
488 0 : return "SILENTLY_IGNORE";
489 : }
490 :
491 2264 : const char *proj = poDS->GetProjectionRef();
492 2264 : double *padfGeoTransform = psDatasetProperties->adfGeoTransform;
493 2264 : int bGotGeoTransform = poDS->GetGeoTransform(padfGeoTransform) == CE_None;
494 2264 : if (bSeparate)
495 : {
496 25 : if (bFirst)
497 : {
498 12 : bHasGeoTransform = bGotGeoTransform;
499 12 : if (!bHasGeoTransform)
500 : {
501 1 : if (bUserExtent)
502 : {
503 0 : CPLError(CE_Warning, CPLE_NotSupported,
504 : "User extent ignored by gdalbuildvrt -separate "
505 : "with ungeoreferenced images.");
506 : }
507 1 : if (resolutionStrategy == USER_RESOLUTION)
508 : {
509 0 : CPLError(CE_Warning, CPLE_NotSupported,
510 : "User resolution ignored by gdalbuildvrt "
511 : "-separate with ungeoreferenced images.");
512 : }
513 : }
514 : }
515 13 : else if (bHasGeoTransform != bGotGeoTransform)
516 : {
517 : return "gdalbuildvrt -separate cannot stack ungeoreferenced and "
518 0 : "georeferenced images.";
519 : }
520 14 : else if (!bHasGeoTransform && (nRasterXSize != poDS->GetRasterXSize() ||
521 1 : nRasterYSize != poDS->GetRasterYSize()))
522 : {
523 : return "gdalbuildvrt -separate cannot stack ungeoreferenced images "
524 0 : "that have not the same dimensions.";
525 : }
526 : }
527 : else
528 : {
529 2239 : if (!bGotGeoTransform)
530 : {
531 0 : return "gdalbuildvrt does not support ungeoreferenced image.";
532 : }
533 2239 : bHasGeoTransform = TRUE;
534 : }
535 :
536 2264 : if (bGotGeoTransform)
537 : {
538 2262 : if (padfGeoTransform[GEOTRSFRM_ROTATION_PARAM1] != 0 ||
539 2262 : padfGeoTransform[GEOTRSFRM_ROTATION_PARAM2] != 0)
540 : {
541 0 : return "gdalbuildvrt does not support rotated geo transforms.";
542 : }
543 2262 : if (padfGeoTransform[GEOTRSFRM_NS_RES] >= 0)
544 : {
545 0 : return "gdalbuildvrt does not support positive NS resolution.";
546 : }
547 : }
548 :
549 2264 : psDatasetProperties->nRasterXSize = poDS->GetRasterXSize();
550 2264 : psDatasetProperties->nRasterYSize = poDS->GetRasterYSize();
551 2264 : if (bFirst && bSeparate && !bGotGeoTransform)
552 : {
553 1 : nRasterXSize = poDS->GetRasterXSize();
554 1 : nRasterYSize = poDS->GetRasterYSize();
555 : }
556 :
557 2264 : double ds_minX = padfGeoTransform[GEOTRSFRM_TOPLEFT_X];
558 2264 : double ds_maxY = padfGeoTransform[GEOTRSFRM_TOPLEFT_Y];
559 : double ds_maxX =
560 2264 : ds_minX + GDALGetRasterXSize(hDS) * padfGeoTransform[GEOTRSFRM_WE_RES];
561 : double ds_minY =
562 2264 : ds_maxY + GDALGetRasterYSize(hDS) * padfGeoTransform[GEOTRSFRM_NS_RES];
563 :
564 2264 : int _nBands = GDALGetRasterCount(hDS);
565 2264 : if (_nBands == 0)
566 : {
567 0 : return "Dataset has no bands";
568 : }
569 2273 : if (bNoDataFromMask &&
570 9 : poDS->GetRasterBand(_nBands)->GetColorInterpretation() == GCI_AlphaBand)
571 3 : _nBands--;
572 :
573 2264 : GDALRasterBand *poFirstBand = poDS->GetRasterBand(1);
574 2264 : poFirstBand->GetBlockSize(&psDatasetProperties->nBlockXSize,
575 : &psDatasetProperties->nBlockYSize);
576 :
577 : /* For the -separate case */
578 2264 : psDatasetProperties->aeBandType.resize(_nBands);
579 :
580 2264 : psDatasetProperties->adfNoDataValues.resize(_nBands);
581 2264 : psDatasetProperties->abHasNoData.resize(_nBands);
582 :
583 2264 : psDatasetProperties->adfOffset.resize(_nBands);
584 2264 : psDatasetProperties->abHasOffset.resize(_nBands);
585 :
586 2264 : psDatasetProperties->adfScale.resize(_nBands);
587 2264 : psDatasetProperties->abHasScale.resize(_nBands);
588 :
589 2264 : psDatasetProperties->abHasMaskBand.resize(_nBands);
590 :
591 2264 : psDatasetProperties->bHasDatasetMask =
592 2264 : poFirstBand->GetMaskFlags() == GMF_PER_DATASET;
593 2264 : if (psDatasetProperties->bHasDatasetMask)
594 10 : bHasDatasetMask = TRUE;
595 2264 : poFirstBand->GetMaskBand()->GetBlockSize(
596 : &psDatasetProperties->nMaskBlockXSize,
597 : &psDatasetProperties->nMaskBlockYSize);
598 :
599 2264 : psDatasetProperties->bLastBandIsAlpha = false;
600 2264 : if (poDS->GetRasterBand(_nBands)->GetColorInterpretation() == GCI_AlphaBand)
601 10 : psDatasetProperties->bLastBandIsAlpha = true;
602 :
603 : // Collect overview factors. We only handle power-of-two situations for now
604 2264 : const int nOverviews = poFirstBand->GetOverviewCount();
605 2264 : int nExpectedOvFactor = 2;
606 2276 : for (int j = 0; j < nOverviews; j++)
607 : {
608 15 : GDALRasterBand *poOverview = poFirstBand->GetOverview(j);
609 15 : if (!poOverview)
610 0 : continue;
611 15 : if (poOverview->GetXSize() < 128 && poOverview->GetYSize() < 128)
612 : {
613 3 : break;
614 : }
615 :
616 12 : const int nOvFactor = GDALComputeOvFactor(
617 : poOverview->GetXSize(), poFirstBand->GetXSize(),
618 12 : poOverview->GetYSize(), poFirstBand->GetYSize());
619 :
620 12 : if (nOvFactor != nExpectedOvFactor)
621 0 : break;
622 :
623 12 : psDatasetProperties->anOverviewFactors.push_back(nOvFactor);
624 12 : nExpectedOvFactor *= 2;
625 : }
626 :
627 5689 : for (int j = 0; j < _nBands; j++)
628 : {
629 3425 : GDALRasterBand *poBand = poDS->GetRasterBand(j + 1);
630 :
631 3425 : psDatasetProperties->aeBandType[j] = poBand->GetRasterDataType();
632 :
633 3425 : if (!bSeparate && nSrcNoDataCount > 0)
634 : {
635 2 : psDatasetProperties->abHasNoData[j] = true;
636 2 : if (j < nSrcNoDataCount)
637 2 : psDatasetProperties->adfNoDataValues[j] = padfSrcNoData[j];
638 : else
639 0 : psDatasetProperties->adfNoDataValues[j] =
640 0 : padfSrcNoData[nSrcNoDataCount - 1];
641 : }
642 : else
643 : {
644 3423 : int bHasNoData = false;
645 6846 : psDatasetProperties->adfNoDataValues[j] =
646 3423 : poBand->GetNoDataValue(&bHasNoData);
647 3423 : psDatasetProperties->abHasNoData[j] = bHasNoData != 0;
648 : }
649 :
650 3425 : int bHasOffset = false;
651 3425 : psDatasetProperties->adfOffset[j] = poBand->GetOffset(&bHasOffset);
652 6850 : psDatasetProperties->abHasOffset[j] =
653 3425 : bHasOffset != 0 && psDatasetProperties->adfOffset[j] != 0.0;
654 :
655 3425 : int bHasScale = false;
656 3425 : psDatasetProperties->adfScale[j] = poBand->GetScale(&bHasScale);
657 6850 : psDatasetProperties->abHasScale[j] =
658 3425 : bHasScale != 0 && psDatasetProperties->adfScale[j] != 1.0;
659 :
660 3425 : const int nMaskFlags = poBand->GetMaskFlags();
661 3425 : psDatasetProperties->abHasMaskBand[j] =
662 6801 : (nMaskFlags != GMF_ALL_VALID && nMaskFlags != GMF_NODATA) ||
663 6801 : poBand->GetColorInterpretation() == GCI_AlphaBand;
664 : }
665 :
666 2264 : if (bSeparate)
667 : {
668 31 : for (int j = 0; j < nSelectedBands; j++)
669 : {
670 7 : if (panSelectedBandList[j] > _nBands)
671 : {
672 : return CPLSPrintf("%s has %d bands, but %d is requested",
673 1 : dsFileName, _nBands, panSelectedBandList[j]);
674 : }
675 : }
676 : }
677 :
678 2263 : if (bFirst)
679 : {
680 157 : nTotalBands = _nBands;
681 157 : if (bAddAlpha && psDatasetProperties->bLastBandIsAlpha)
682 : {
683 4 : bLastBandIsAlpha = true;
684 4 : nTotalBands--;
685 : }
686 :
687 157 : if (proj)
688 157 : pszProjectionRef = CPLStrdup(proj);
689 157 : if (!bUserExtent)
690 : {
691 152 : minX = ds_minX;
692 152 : minY = ds_minY;
693 152 : maxX = ds_maxX;
694 152 : maxY = ds_maxY;
695 : }
696 :
697 157 : if (!bSeparate)
698 : {
699 : // if not provided an explicit band list, take the one of the first
700 : // dataset
701 146 : if (nSelectedBands == 0)
702 : {
703 130 : nSelectedBands = nTotalBands;
704 130 : CPLFree(panSelectedBandList);
705 130 : panSelectedBandList =
706 130 : static_cast<int *>(CPLMalloc(nSelectedBands * sizeof(int)));
707 319 : for (int j = 0; j < nSelectedBands; j++)
708 : {
709 189 : panSelectedBandList[j] = j + 1;
710 : }
711 : }
712 562 : for (int j = 0; j < nSelectedBands; j++)
713 : {
714 416 : nMaxSelectedBandNo =
715 416 : std::max(nMaxSelectedBandNo, panSelectedBandList[j]);
716 : }
717 :
718 146 : asBandProperties.resize(nSelectedBands);
719 561 : for (int j = 0; j < nSelectedBands; j++)
720 : {
721 416 : const int nSelBand = panSelectedBandList[j];
722 416 : if (nSelBand <= 0 || nSelBand > nTotalBands)
723 : {
724 1 : return CPLSPrintf("Invalid band number: %d", nSelBand);
725 : }
726 415 : GDALRasterBand *poBand = poDS->GetRasterBand(nSelBand);
727 830 : asBandProperties[j].colorInterpretation =
728 415 : poBand->GetColorInterpretation();
729 415 : asBandProperties[j].dataType = poBand->GetRasterDataType();
730 415 : if (asBandProperties[j].colorInterpretation == GCI_PaletteIndex)
731 : {
732 2 : auto colorTable = poBand->GetColorTable();
733 2 : if (colorTable)
734 : {
735 2 : asBandProperties[j].colorTable.reset(
736 : colorTable->Clone());
737 : }
738 : }
739 : else
740 413 : asBandProperties[j].colorTable = nullptr;
741 :
742 415 : if (nVRTNoDataCount > 0)
743 : {
744 19 : asBandProperties[j].bHasNoData = true;
745 19 : if (j < nVRTNoDataCount)
746 13 : asBandProperties[j].noDataValue = padfVRTNoData[j];
747 : else
748 6 : asBandProperties[j].noDataValue =
749 6 : padfVRTNoData[nVRTNoDataCount - 1];
750 : }
751 : else
752 : {
753 396 : int bHasNoData = false;
754 792 : asBandProperties[j].noDataValue =
755 396 : poBand->GetNoDataValue(&bHasNoData);
756 396 : asBandProperties[j].bHasNoData = bHasNoData != 0;
757 : }
758 :
759 415 : int bHasOffset = false;
760 415 : asBandProperties[j].dfOffset = poBand->GetOffset(&bHasOffset);
761 415 : asBandProperties[j].bHasOffset =
762 415 : bHasOffset != 0 && asBandProperties[j].dfOffset != 0.0;
763 :
764 415 : int bHasScale = false;
765 415 : asBandProperties[j].dfScale = poBand->GetScale(&bHasScale);
766 415 : asBandProperties[j].bHasScale =
767 415 : bHasScale != 0 && asBandProperties[j].dfScale != 1.0;
768 : }
769 : }
770 : }
771 : else
772 : {
773 2106 : if ((proj != nullptr && pszProjectionRef == nullptr) ||
774 6318 : (proj == nullptr && pszProjectionRef != nullptr) ||
775 2106 : (proj != nullptr && pszProjectionRef != nullptr &&
776 2106 : ProjAreEqual(proj, pszProjectionRef) == FALSE))
777 : {
778 1 : if (!bAllowProjectionDifference)
779 : {
780 2 : CPLString osExpected = GetProjectionName(pszProjectionRef);
781 1 : CPLString osGot = GetProjectionName(proj);
782 : return CPLSPrintf("gdalbuildvrt does not support heterogeneous "
783 : "projection: expected %s, got %s.",
784 1 : osExpected.c_str(), osGot.c_str());
785 : }
786 : }
787 2105 : if (!bSeparate)
788 : {
789 2092 : if (!bExplicitBandList && _nBands != nTotalBands)
790 : {
791 9 : if (bAddAlpha && _nBands == nTotalBands + 1 &&
792 4 : psDatasetProperties->bLastBandIsAlpha)
793 : {
794 4 : bLastBandIsAlpha = true;
795 : }
796 : else
797 : {
798 : return CPLSPrintf(
799 : "gdalbuildvrt does not support heterogeneous band "
800 : "numbers: expected %d, got %d.",
801 5 : nTotalBands, _nBands);
802 : }
803 : }
804 2083 : else if (bExplicitBandList && _nBands < nMaxSelectedBandNo)
805 : {
806 : return CPLSPrintf(
807 : "gdalbuildvrt does not support heterogeneous band "
808 : "numbers: expected at least %d, got %d.",
809 0 : nMaxSelectedBandNo, _nBands);
810 : }
811 :
812 5225 : for (int j = 0; j < nSelectedBands; j++)
813 : {
814 3138 : const int nSelBand = panSelectedBandList[j];
815 3138 : CPLAssert(nSelBand >= 1 && nSelBand <= _nBands);
816 3138 : GDALRasterBand *poBand = poDS->GetRasterBand(nSelBand);
817 3138 : if (asBandProperties[j].colorInterpretation !=
818 3138 : poBand->GetColorInterpretation())
819 : {
820 : return CPLSPrintf(
821 : "gdalbuildvrt does not support heterogeneous "
822 : "band color interpretation: expected %s, got %s.",
823 : GDALGetColorInterpretationName(
824 0 : asBandProperties[j].colorInterpretation),
825 : GDALGetColorInterpretationName(
826 0 : poBand->GetColorInterpretation()));
827 : }
828 3138 : if (asBandProperties[j].dataType != poBand->GetRasterDataType())
829 : {
830 : return CPLSPrintf(
831 : "gdalbuildvrt does not support heterogeneous "
832 : "band data type: expected %s, got %s.",
833 0 : GDALGetDataTypeName(asBandProperties[j].dataType),
834 0 : GDALGetDataTypeName(poBand->GetRasterDataType()));
835 : }
836 3138 : if (asBandProperties[j].colorTable)
837 : {
838 2 : const GDALColorTable *colorTable = poBand->GetColorTable();
839 : int nRefColorEntryCount =
840 2 : asBandProperties[j].colorTable->GetColorEntryCount();
841 4 : if (colorTable == nullptr ||
842 2 : colorTable->GetColorEntryCount() != nRefColorEntryCount)
843 : {
844 : return "gdalbuildvrt does not support rasters with "
845 : "different color tables (different number of "
846 0 : "color table entries)";
847 : }
848 :
849 : /* Check that the palette are the same too */
850 : /* We just warn and still process the file. It is not a
851 : * technical no-go, but the user */
852 : /* should check that the end result is OK for him. */
853 260 : for (int i = 0; i < nRefColorEntryCount; i++)
854 : {
855 : const GDALColorEntry *psEntry =
856 258 : colorTable->GetColorEntry(i);
857 : const GDALColorEntry *psEntryRef =
858 258 : asBandProperties[j].colorTable->GetColorEntry(i);
859 258 : if (psEntry->c1 != psEntryRef->c1 ||
860 258 : psEntry->c2 != psEntryRef->c2 ||
861 258 : psEntry->c3 != psEntryRef->c3 ||
862 258 : psEntry->c4 != psEntryRef->c4)
863 : {
864 : static int bFirstWarningPCT = TRUE;
865 0 : if (bFirstWarningPCT)
866 0 : CPLError(
867 : CE_Warning, CPLE_NotSupported,
868 : "%s has different values than the first "
869 : "raster for some entries in the color "
870 : "table.\n"
871 : "The end result might produce weird "
872 : "colors.\n"
873 : "You're advised to pre-process your "
874 : "rasters with other tools, such as "
875 : "pct2rgb.py or gdal_translate -expand RGB\n"
876 : "to operate gdalbuildvrt on RGB rasters "
877 : "instead",
878 : dsFileName);
879 : else
880 0 : CPLError(CE_Warning, CPLE_NotSupported,
881 : "%s has different values than the "
882 : "first raster for some entries in the "
883 : "color table.",
884 : dsFileName);
885 0 : bFirstWarningPCT = FALSE;
886 0 : break;
887 : }
888 : }
889 : }
890 :
891 3138 : if (psDatasetProperties->abHasOffset[j] !=
892 6276 : asBandProperties[j].bHasOffset ||
893 3138 : (asBandProperties[j].bHasOffset &&
894 0 : psDatasetProperties->adfOffset[j] !=
895 0 : asBandProperties[j].dfOffset))
896 : {
897 : return CPLSPrintf(
898 : "gdalbuildvrt does not support heterogeneous "
899 : "band offset: expected (%d,%f), got (%d,%f).",
900 0 : static_cast<int>(asBandProperties[j].bHasOffset),
901 0 : asBandProperties[j].dfOffset,
902 0 : static_cast<int>(psDatasetProperties->abHasOffset[j]),
903 0 : psDatasetProperties->adfOffset[j]);
904 : }
905 :
906 3138 : if (psDatasetProperties->abHasScale[j] !=
907 6276 : asBandProperties[j].bHasScale ||
908 3138 : (asBandProperties[j].bHasScale &&
909 0 : psDatasetProperties->adfScale[j] !=
910 0 : asBandProperties[j].dfScale))
911 : {
912 : return CPLSPrintf(
913 : "gdalbuildvrt does not support heterogeneous "
914 : "band scale: expected (%d,%f), got (%d,%f).",
915 0 : static_cast<int>(asBandProperties[j].bHasScale),
916 0 : asBandProperties[j].dfScale,
917 0 : static_cast<int>(psDatasetProperties->abHasScale[j]),
918 0 : psDatasetProperties->adfScale[j]);
919 : }
920 : }
921 : }
922 2100 : if (!bUserExtent)
923 : {
924 2097 : if (ds_minX < minX)
925 5 : minX = ds_minX;
926 2097 : if (ds_minY < minY)
927 25 : minY = ds_minY;
928 2097 : if (ds_maxX > maxX)
929 684 : maxX = ds_maxX;
930 2097 : if (ds_maxY > maxY)
931 35 : maxY = ds_maxY;
932 : }
933 : }
934 :
935 2256 : if (resolutionStrategy == AVERAGE_RESOLUTION)
936 : {
937 2247 : ++nCountValid;
938 : {
939 2247 : const double dfDelta = padfGeoTransform[GEOTRSFRM_WE_RES] - we_res;
940 2247 : we_res += dfDelta / nCountValid;
941 : }
942 : {
943 2247 : const double dfDelta = padfGeoTransform[GEOTRSFRM_NS_RES] - ns_res;
944 2247 : ns_res += dfDelta / nCountValid;
945 : }
946 : }
947 9 : else if (resolutionStrategy != USER_RESOLUTION)
948 : {
949 0 : if (bFirst)
950 : {
951 0 : we_res = padfGeoTransform[GEOTRSFRM_WE_RES];
952 0 : ns_res = padfGeoTransform[GEOTRSFRM_NS_RES];
953 : }
954 0 : else if (resolutionStrategy == HIGHEST_RESOLUTION)
955 : {
956 0 : we_res = std::min(we_res, padfGeoTransform[GEOTRSFRM_WE_RES]);
957 : // ns_res is negative, the highest resolution is the max value.
958 0 : ns_res = std::max(ns_res, padfGeoTransform[GEOTRSFRM_NS_RES]);
959 : }
960 : else
961 : {
962 0 : we_res = std::max(we_res, padfGeoTransform[GEOTRSFRM_WE_RES]);
963 : // ns_res is negative, the lowest resolution is the min value.
964 0 : ns_res = std::min(ns_res, padfGeoTransform[GEOTRSFRM_NS_RES]);
965 : }
966 : }
967 :
968 2256 : return "";
969 : }
970 :
971 : /************************************************************************/
972 : /* CreateVRTSeparate() */
973 : /************************************************************************/
974 :
975 11 : void VRTBuilder::CreateVRTSeparate(VRTDatasetH hVRTDS)
976 : {
977 11 : int iBand = 1;
978 35 : for (int i = 0; ppszInputFilenames != nullptr && i < nInputFiles; i++)
979 : {
980 24 : DatasetProperty *psDatasetProperties = &asDatasetProperties[i];
981 :
982 24 : if (psDatasetProperties->isFileOK == FALSE)
983 0 : continue;
984 :
985 24 : const char *dsFileName = ppszInputFilenames[i];
986 :
987 : double dfSrcXOff, dfSrcYOff, dfSrcXSize, dfSrcYSize, dfDstXOff,
988 : dfDstYOff, dfDstXSize, dfDstYSize;
989 24 : if (bHasGeoTransform)
990 : {
991 22 : if (!GetSrcDstWin(psDatasetProperties, we_res, ns_res, minX, minY,
992 : maxX, maxY, nRasterXSize, nRasterYSize,
993 : &dfSrcXOff, &dfSrcYOff, &dfSrcXSize, &dfSrcYSize,
994 : &dfDstXOff, &dfDstYOff, &dfDstXSize, &dfDstYSize))
995 : {
996 0 : CPLDebug("BuildVRT",
997 : "Skipping %s as not intersecting area of interest",
998 : dsFileName);
999 0 : continue;
1000 : }
1001 : }
1002 : else
1003 : {
1004 2 : dfSrcXOff = dfSrcYOff = dfDstXOff = dfDstYOff = 0;
1005 2 : dfSrcXSize = dfDstXSize = nRasterXSize;
1006 2 : dfSrcYSize = dfDstYSize = nRasterYSize;
1007 : }
1008 :
1009 : GDALDatasetH hSourceDS;
1010 24 : bool bDropRef = false;
1011 64 : if (nSrcDSCount == nInputFiles &&
1012 40 : GDALGetDatasetDriver(pahSrcDS[i]) != nullptr &&
1013 16 : (dsFileName[0] == '\0' || // could be a unnamed VRT file
1014 2 : EQUAL(GDALGetDescription(GDALGetDatasetDriver(pahSrcDS[i])),
1015 : "MEM")))
1016 : {
1017 16 : hSourceDS = pahSrcDS[i];
1018 : }
1019 : else
1020 : {
1021 8 : bDropRef = true;
1022 16 : GDALProxyPoolDatasetH hProxyDS = GDALProxyPoolDatasetCreate(
1023 : dsFileName, psDatasetProperties->nRasterXSize,
1024 : psDatasetProperties->nRasterYSize, GA_ReadOnly, TRUE,
1025 8 : pszProjectionRef, psDatasetProperties->adfGeoTransform);
1026 8 : hSourceDS = static_cast<GDALDatasetH>(hProxyDS);
1027 8 : reinterpret_cast<GDALProxyPoolDataset *>(hProxyDS)->SetOpenOptions(
1028 8 : papszOpenOptions);
1029 :
1030 19 : for (int jBand = 0;
1031 19 : jBand <
1032 19 : static_cast<int>(psDatasetProperties->aeBandType.size());
1033 : ++jBand)
1034 : {
1035 11 : GDALProxyPoolDatasetAddSrcBandDescription(
1036 11 : hProxyDS, psDatasetProperties->aeBandType[jBand],
1037 : psDatasetProperties->nBlockXSize,
1038 : psDatasetProperties->nBlockYSize);
1039 : }
1040 : }
1041 :
1042 : const int nBandsToIter =
1043 24 : nSelectedBands > 0
1044 46 : ? nSelectedBands
1045 22 : : static_cast<int>(psDatasetProperties->aeBandType.size());
1046 56 : for (int iBandToIter = 0; iBandToIter < nBandsToIter; ++iBandToIter)
1047 : {
1048 : // 0-based
1049 64 : const int nSrcBandIdx = nSelectedBands > 0
1050 32 : ? panSelectedBandList[iBandToIter] - 1
1051 : : iBandToIter;
1052 32 : assert(nSrcBandIdx >= 0);
1053 32 : GDALAddBand(hVRTDS, psDatasetProperties->aeBandType[nSrcBandIdx],
1054 : nullptr);
1055 :
1056 : VRTSourcedRasterBandH hVRTBand = static_cast<VRTSourcedRasterBandH>(
1057 32 : GDALGetRasterBand(hVRTDS, iBand));
1058 :
1059 32 : if (bHideNoData)
1060 0 : GDALSetMetadataItem(hVRTBand, "HideNoDataValue", "1", nullptr);
1061 :
1062 32 : VRTSourcedRasterBand *poVRTBand =
1063 : static_cast<VRTSourcedRasterBand *>(hVRTBand);
1064 :
1065 32 : if (bAllowVRTNoData)
1066 : {
1067 30 : if (nVRTNoDataCount > 0)
1068 : {
1069 4 : if (iBand - 1 < nVRTNoDataCount)
1070 4 : GDALSetRasterNoDataValue(hVRTBand,
1071 4 : padfVRTNoData[iBand - 1]);
1072 : else
1073 0 : GDALSetRasterNoDataValue(
1074 0 : hVRTBand, padfVRTNoData[nVRTNoDataCount - 1]);
1075 : }
1076 26 : else if (psDatasetProperties->abHasNoData[nSrcBandIdx])
1077 : {
1078 2 : GDALSetRasterNoDataValue(
1079 : hVRTBand,
1080 2 : psDatasetProperties->adfNoDataValues[nSrcBandIdx]);
1081 : }
1082 : }
1083 :
1084 : VRTSimpleSource *poSimpleSource;
1085 62 : if (bAllowSrcNoData &&
1086 56 : (nSrcNoDataCount > 0 ||
1087 58 : psDatasetProperties->abHasNoData[nSrcBandIdx]))
1088 : {
1089 6 : auto poComplexSource = new VRTComplexSource();
1090 6 : poSimpleSource = poComplexSource;
1091 6 : if (nSrcNoDataCount > 0)
1092 : {
1093 4 : if (iBand - 1 < nSrcNoDataCount)
1094 4 : poComplexSource->SetNoDataValue(
1095 4 : padfSrcNoData[iBand - 1]);
1096 : else
1097 0 : poComplexSource->SetNoDataValue(
1098 0 : padfSrcNoData[nSrcNoDataCount - 1]);
1099 : }
1100 : else /* if (psDatasetProperties->abHasNoData[nSrcBandIdx]) */
1101 : {
1102 2 : poComplexSource->SetNoDataValue(
1103 2 : psDatasetProperties->adfNoDataValues[nSrcBandIdx]);
1104 : }
1105 : }
1106 52 : else if (bUseSrcMaskBand &&
1107 52 : psDatasetProperties->abHasMaskBand[nSrcBandIdx])
1108 : {
1109 0 : auto poSource = new VRTComplexSource();
1110 0 : poSource->SetUseMaskBand(true);
1111 0 : poSimpleSource = poSource;
1112 : }
1113 : else
1114 26 : poSimpleSource = new VRTSimpleSource();
1115 :
1116 32 : if (pszResampling)
1117 0 : poSimpleSource->SetResampling(pszResampling);
1118 64 : poVRTBand->ConfigureSource(
1119 : poSimpleSource,
1120 : static_cast<GDALRasterBand *>(
1121 32 : GDALGetRasterBand(hSourceDS, nSrcBandIdx + 1)),
1122 : FALSE, dfSrcXOff, dfSrcYOff, dfSrcXSize, dfSrcYSize, dfDstXOff,
1123 : dfDstYOff, dfDstXSize, dfDstYSize);
1124 :
1125 32 : if (psDatasetProperties->abHasOffset[nSrcBandIdx])
1126 0 : poVRTBand->SetOffset(
1127 0 : psDatasetProperties->adfOffset[nSrcBandIdx]);
1128 :
1129 32 : if (psDatasetProperties->abHasScale[nSrcBandIdx])
1130 0 : poVRTBand->SetScale(psDatasetProperties->adfScale[nSrcBandIdx]);
1131 :
1132 32 : poVRTBand->AddSource(poSimpleSource);
1133 :
1134 32 : iBand++;
1135 : }
1136 :
1137 24 : if (bDropRef)
1138 : {
1139 8 : GDALDereferenceDataset(hSourceDS);
1140 : }
1141 : }
1142 11 : }
1143 :
1144 : /************************************************************************/
1145 : /* CreateVRTNonSeparate() */
1146 : /************************************************************************/
1147 :
1148 144 : void VRTBuilder::CreateVRTNonSeparate(VRTDatasetH hVRTDS)
1149 : {
1150 144 : VRTDataset *poVRTDS = reinterpret_cast<VRTDataset *>(hVRTDS);
1151 558 : for (int j = 0; j < nSelectedBands; j++)
1152 : {
1153 414 : poVRTDS->AddBand(asBandProperties[j].dataType);
1154 414 : GDALRasterBand *poBand = poVRTDS->GetRasterBand(j + 1);
1155 414 : poBand->SetColorInterpretation(asBandProperties[j].colorInterpretation);
1156 414 : if (asBandProperties[j].colorInterpretation == GCI_PaletteIndex)
1157 : {
1158 2 : poBand->SetColorTable(asBandProperties[j].colorTable.get());
1159 : }
1160 414 : if (bAllowVRTNoData && asBandProperties[j].bHasNoData)
1161 32 : poBand->SetNoDataValue(asBandProperties[j].noDataValue);
1162 414 : if (bHideNoData)
1163 1 : poBand->SetMetadataItem("HideNoDataValue", "1");
1164 :
1165 414 : if (asBandProperties[j].bHasOffset)
1166 0 : poBand->SetOffset(asBandProperties[j].dfOffset);
1167 :
1168 414 : if (asBandProperties[j].bHasScale)
1169 0 : poBand->SetScale(asBandProperties[j].dfScale);
1170 : }
1171 :
1172 144 : VRTSourcedRasterBand *poMaskVRTBand = nullptr;
1173 144 : if (bAddAlpha)
1174 : {
1175 10 : poVRTDS->AddBand(GDT_Byte);
1176 10 : GDALRasterBand *poBand = poVRTDS->GetRasterBand(nSelectedBands + 1);
1177 10 : poBand->SetColorInterpretation(GCI_AlphaBand);
1178 : }
1179 134 : else if (bHasDatasetMask)
1180 : {
1181 9 : poVRTDS->CreateMaskBand(GMF_PER_DATASET);
1182 : poMaskVRTBand = static_cast<VRTSourcedRasterBand *>(
1183 9 : poVRTDS->GetRasterBand(1)->GetMaskBand());
1184 : }
1185 :
1186 144 : bool bCanCollectOverviewFactors = true;
1187 288 : std::set<int> anOverviewFactorsSet;
1188 288 : std::vector<int> anIdxValidDatasets;
1189 :
1190 2382 : for (int i = 0; ppszInputFilenames != nullptr && i < nInputFiles; i++)
1191 : {
1192 2238 : DatasetProperty *psDatasetProperties = &asDatasetProperties[i];
1193 :
1194 2238 : if (psDatasetProperties->isFileOK == FALSE)
1195 8 : continue;
1196 :
1197 2231 : const char *dsFileName = ppszInputFilenames[i];
1198 :
1199 : double dfSrcXOff;
1200 : double dfSrcYOff;
1201 : double dfSrcXSize;
1202 : double dfSrcYSize;
1203 : double dfDstXOff;
1204 : double dfDstYOff;
1205 : double dfDstXSize;
1206 : double dfDstYSize;
1207 2231 : if (!GetSrcDstWin(psDatasetProperties, we_res, ns_res, minX, minY, maxX,
1208 : maxY, nRasterXSize, nRasterYSize, &dfSrcXOff,
1209 : &dfSrcYOff, &dfSrcXSize, &dfSrcYSize, &dfDstXOff,
1210 : &dfDstYOff, &dfDstXSize, &dfDstYSize))
1211 : {
1212 1 : CPLDebug("BuildVRT",
1213 : "Skipping %s as not intersecting area of interest",
1214 : dsFileName);
1215 1 : continue;
1216 : }
1217 :
1218 2230 : anIdxValidDatasets.push_back(i);
1219 :
1220 2230 : if (bCanCollectOverviewFactors)
1221 : {
1222 2226 : if (std::abs(psDatasetProperties->adfGeoTransform[1] - we_res) >
1223 4446 : 1e-8 * std::abs(we_res) ||
1224 2220 : std::abs(psDatasetProperties->adfGeoTransform[5] - ns_res) >
1225 2220 : 1e-8 * std::abs(ns_res))
1226 : {
1227 6 : bCanCollectOverviewFactors = false;
1228 6 : anOverviewFactorsSet.clear();
1229 : }
1230 : }
1231 2230 : if (bCanCollectOverviewFactors)
1232 : {
1233 2229 : for (int nOvFactor : psDatasetProperties->anOverviewFactors)
1234 9 : anOverviewFactorsSet.insert(nOvFactor);
1235 : }
1236 :
1237 : GDALDatasetH hSourceDS;
1238 2230 : bool bDropRef = false;
1239 :
1240 5540 : if (nSrcDSCount == nInputFiles &&
1241 3310 : GDALGetDatasetDriver(pahSrcDS[i]) != nullptr &&
1242 1080 : (dsFileName[0] == '\0' || // could be a unnamed VRT file
1243 46 : EQUAL(GDALGetDescription(GDALGetDatasetDriver(pahSrcDS[i])),
1244 : "MEM")))
1245 : {
1246 1068 : hSourceDS = pahSrcDS[i];
1247 : }
1248 : else
1249 : {
1250 1162 : bDropRef = true;
1251 2324 : GDALProxyPoolDatasetH hProxyDS = GDALProxyPoolDatasetCreate(
1252 : dsFileName, psDatasetProperties->nRasterXSize,
1253 : psDatasetProperties->nRasterYSize, GA_ReadOnly, TRUE,
1254 1162 : pszProjectionRef, psDatasetProperties->adfGeoTransform);
1255 1162 : reinterpret_cast<GDALProxyPoolDataset *>(hProxyDS)->SetOpenOptions(
1256 1162 : papszOpenOptions);
1257 :
1258 3404 : for (int j = 0;
1259 3404 : j < nMaxSelectedBandNo +
1260 42 : (bAddAlpha && psDatasetProperties->bLastBandIsAlpha
1261 3446 : ? 1
1262 : : 0);
1263 : j++)
1264 : {
1265 2242 : GDALProxyPoolDatasetAddSrcBandDescription(
1266 : hProxyDS,
1267 2242 : j < static_cast<int>(asBandProperties.size())
1268 2238 : ? asBandProperties[j].dataType
1269 : : GDT_Byte,
1270 : psDatasetProperties->nBlockXSize,
1271 : psDatasetProperties->nBlockYSize);
1272 : }
1273 1162 : if (bHasDatasetMask && !bAddAlpha)
1274 : {
1275 : static_cast<GDALProxyPoolRasterBand *>(
1276 : reinterpret_cast<GDALProxyPoolDataset *>(hProxyDS)
1277 8 : ->GetRasterBand(1))
1278 8 : ->AddSrcMaskBandDescription(
1279 : GDT_Byte, psDatasetProperties->nMaskBlockXSize,
1280 : psDatasetProperties->nMaskBlockYSize);
1281 : }
1282 :
1283 1162 : hSourceDS = static_cast<GDALDatasetH>(hProxyDS);
1284 : }
1285 :
1286 5789 : for (int j = 0;
1287 5789 : j <
1288 5789 : nSelectedBands +
1289 5789 : (bAddAlpha && psDatasetProperties->bLastBandIsAlpha ? 1 : 0);
1290 : j++)
1291 : {
1292 : VRTSourcedRasterBandH hVRTBand = static_cast<VRTSourcedRasterBandH>(
1293 3559 : poVRTDS->GetRasterBand(j + 1));
1294 3559 : const int nSelBand = j == nSelectedBands ? nSelectedBands + 1
1295 3551 : : panSelectedBandList[j];
1296 :
1297 : /* Place the raster band at the right position in the VRT */
1298 3559 : VRTSourcedRasterBand *poVRTBand =
1299 : static_cast<VRTSourcedRasterBand *>(hVRTBand);
1300 :
1301 : VRTSimpleSource *poSimpleSource;
1302 3559 : if (bNoDataFromMask)
1303 : {
1304 15 : auto poNoDataFromMaskSource = new VRTNoDataFromMaskSource();
1305 15 : poSimpleSource = poNoDataFromMaskSource;
1306 15 : poNoDataFromMaskSource->SetParameters(
1307 15 : (nVRTNoDataCount > 0)
1308 15 : ? ((j < nVRTNoDataCount)
1309 15 : ? padfVRTNoData[j]
1310 6 : : padfVRTNoData[nVRTNoDataCount - 1])
1311 : : 0,
1312 : dfMaskValueThreshold);
1313 : }
1314 7088 : else if (bAllowSrcNoData &&
1315 7088 : psDatasetProperties->abHasNoData[nSelBand - 1])
1316 : {
1317 25 : auto poComplexSource = new VRTComplexSource();
1318 25 : poSimpleSource = poComplexSource;
1319 25 : poComplexSource->SetNoDataValue(
1320 25 : psDatasetProperties->adfNoDataValues[nSelBand - 1]);
1321 : }
1322 7038 : else if (bUseSrcMaskBand &&
1323 7038 : psDatasetProperties->abHasMaskBand[nSelBand - 1])
1324 : {
1325 44 : auto poSource = new VRTComplexSource();
1326 44 : poSource->SetUseMaskBand(true);
1327 44 : poSimpleSource = poSource;
1328 : }
1329 : else
1330 3475 : poSimpleSource = new VRTSimpleSource();
1331 3559 : if (pszResampling)
1332 11 : poSimpleSource->SetResampling(pszResampling);
1333 3559 : auto poSrcBand = GDALRasterBand::FromHandle(
1334 : GDALGetRasterBand(hSourceDS, nSelBand));
1335 3559 : poVRTBand->ConfigureSource(poSimpleSource, poSrcBand, FALSE,
1336 : dfSrcXOff, dfSrcYOff, dfSrcXSize,
1337 : dfSrcYSize, dfDstXOff, dfDstYOff,
1338 : dfDstXSize, dfDstYSize);
1339 :
1340 3559 : poVRTBand->AddSource(poSimpleSource);
1341 : }
1342 :
1343 2230 : if (bAddAlpha && !psDatasetProperties->bLastBandIsAlpha)
1344 : {
1345 : VRTSourcedRasterBandH hVRTBand = static_cast<VRTSourcedRasterBandH>(
1346 11 : GDALGetRasterBand(hVRTDS, nSelectedBands + 1));
1347 : /* Little trick : we use an offset of 255 and a scaling of 0, so
1348 : * that in areas covered */
1349 : /* by the source, the value of the alpha band will be 255, otherwise
1350 : * it will be 0 */
1351 22 : static_cast<VRTSourcedRasterBand *>(hVRTBand)->AddComplexSource(
1352 11 : static_cast<GDALRasterBand *>(GDALGetRasterBand(hSourceDS, 1)),
1353 : dfSrcXOff, dfSrcYOff, dfSrcXSize, dfSrcYSize, dfDstXOff,
1354 11 : dfDstYOff, dfDstXSize, dfDstYSize, 255, 0, VRT_NODATA_UNSET);
1355 : }
1356 2219 : else if (bHasDatasetMask)
1357 : {
1358 : VRTSimpleSource *poSource;
1359 10 : if (bUseSrcMaskBand)
1360 : {
1361 10 : auto poComplexSource = new VRTComplexSource();
1362 10 : poComplexSource->SetUseMaskBand(true);
1363 10 : poSource = poComplexSource;
1364 : }
1365 : else
1366 : {
1367 0 : poSource = new VRTSimpleSource();
1368 : }
1369 10 : if (pszResampling)
1370 0 : poSource->SetResampling(pszResampling);
1371 10 : assert(poMaskVRTBand);
1372 20 : poMaskVRTBand->ConfigureSource(
1373 : poSource,
1374 10 : static_cast<GDALRasterBand *>(GDALGetRasterBand(hSourceDS, 1)),
1375 : TRUE, dfSrcXOff, dfSrcYOff, dfSrcXSize, dfSrcYSize, dfDstXOff,
1376 : dfDstYOff, dfDstXSize, dfDstYSize);
1377 :
1378 10 : poMaskVRTBand->AddSource(poSource);
1379 : }
1380 :
1381 2230 : if (bDropRef)
1382 : {
1383 1162 : GDALDereferenceDataset(hSourceDS);
1384 : }
1385 : }
1386 :
1387 2374 : for (int i : anIdxValidDatasets)
1388 : {
1389 2230 : const DatasetProperty *psDatasetProperties = &asDatasetProperties[i];
1390 2230 : for (auto oIter = anOverviewFactorsSet.begin();
1391 2239 : oIter != anOverviewFactorsSet.end();)
1392 : {
1393 9 : const int nGlobalOvrFactor = *oIter;
1394 9 : auto oIterNext = oIter;
1395 9 : ++oIterNext;
1396 :
1397 9 : if (psDatasetProperties->nRasterXSize / nGlobalOvrFactor < 128 &&
1398 0 : psDatasetProperties->nRasterYSize / nGlobalOvrFactor < 128)
1399 : {
1400 0 : break;
1401 : }
1402 9 : if (std::find(psDatasetProperties->anOverviewFactors.begin(),
1403 : psDatasetProperties->anOverviewFactors.end(),
1404 9 : nGlobalOvrFactor) ==
1405 18 : psDatasetProperties->anOverviewFactors.end())
1406 : {
1407 0 : anOverviewFactorsSet.erase(oIter);
1408 : }
1409 :
1410 9 : oIter = oIterNext;
1411 : }
1412 : }
1413 147 : if (!anOverviewFactorsSet.empty() &&
1414 3 : CPLTestBool(CPLGetConfigOption("VRT_VIRTUAL_OVERVIEWS", "YES")))
1415 : {
1416 6 : std::vector<int> anOverviewFactors;
1417 3 : anOverviewFactors.insert(anOverviewFactors.end(),
1418 : anOverviewFactorsSet.begin(),
1419 6 : anOverviewFactorsSet.end());
1420 3 : const char *const apszOptions[] = {"VRT_VIRTUAL_OVERVIEWS=YES",
1421 : nullptr};
1422 3 : poVRTDS->BuildOverviews(pszResampling ? pszResampling : "nearest",
1423 3 : static_cast<int>(anOverviewFactors.size()),
1424 3 : &anOverviewFactors[0], 0, nullptr, nullptr,
1425 : nullptr, apszOptions);
1426 : }
1427 144 : }
1428 :
1429 : /************************************************************************/
1430 : /* Build() */
1431 : /************************************************************************/
1432 :
1433 158 : GDALDataset *VRTBuilder::Build(GDALProgressFunc pfnProgress,
1434 : void *pProgressData)
1435 : {
1436 158 : if (bHasRunBuild)
1437 0 : return nullptr;
1438 158 : bHasRunBuild = TRUE;
1439 :
1440 158 : if (pfnProgress == nullptr)
1441 0 : pfnProgress = GDALDummyProgress;
1442 :
1443 158 : bUserExtent = (minX != 0 || minY != 0 || maxX != 0 || maxY != 0);
1444 158 : if (bUserExtent)
1445 : {
1446 5 : if (minX >= maxX || minY >= maxY)
1447 : {
1448 0 : CPLError(CE_Failure, CPLE_IllegalArg, "Invalid user extent");
1449 0 : return nullptr;
1450 : }
1451 : }
1452 :
1453 158 : if (resolutionStrategy == USER_RESOLUTION)
1454 : {
1455 6 : if (we_res <= 0 || ns_res <= 0)
1456 : {
1457 0 : CPLError(CE_Failure, CPLE_IllegalArg, "Invalid user resolution");
1458 0 : return nullptr;
1459 : }
1460 :
1461 : /* We work with negative north-south resolution in all the following
1462 : * code */
1463 6 : ns_res = -ns_res;
1464 : }
1465 : else
1466 : {
1467 152 : we_res = ns_res = 0;
1468 : }
1469 :
1470 158 : asDatasetProperties.resize(nInputFiles);
1471 :
1472 158 : if (pszSrcNoData != nullptr)
1473 : {
1474 4 : if (EQUAL(pszSrcNoData, "none"))
1475 : {
1476 1 : bAllowSrcNoData = FALSE;
1477 : }
1478 : else
1479 : {
1480 3 : char **papszTokens = CSLTokenizeString(pszSrcNoData);
1481 3 : nSrcNoDataCount = CSLCount(papszTokens);
1482 3 : padfSrcNoData = static_cast<double *>(
1483 3 : CPLMalloc(sizeof(double) * nSrcNoDataCount));
1484 8 : for (int i = 0; i < nSrcNoDataCount; i++)
1485 : {
1486 5 : if (!ArgIsNumeric(papszTokens[i]) &&
1487 0 : !EQUAL(papszTokens[i], "nan") &&
1488 5 : !EQUAL(papszTokens[i], "-inf") &&
1489 0 : !EQUAL(papszTokens[i], "inf"))
1490 : {
1491 0 : CPLError(CE_Failure, CPLE_IllegalArg,
1492 : "Invalid -srcnodata value");
1493 0 : CSLDestroy(papszTokens);
1494 0 : return nullptr;
1495 : }
1496 5 : padfSrcNoData[i] = CPLAtofM(papszTokens[i]);
1497 : }
1498 3 : CSLDestroy(papszTokens);
1499 : }
1500 : }
1501 :
1502 158 : if (pszVRTNoData != nullptr)
1503 : {
1504 16 : if (EQUAL(pszVRTNoData, "none"))
1505 : {
1506 1 : bAllowVRTNoData = FALSE;
1507 : }
1508 : else
1509 : {
1510 15 : char **papszTokens = CSLTokenizeString(pszVRTNoData);
1511 15 : nVRTNoDataCount = CSLCount(papszTokens);
1512 15 : padfVRTNoData = static_cast<double *>(
1513 15 : CPLMalloc(sizeof(double) * nVRTNoDataCount));
1514 32 : for (int i = 0; i < nVRTNoDataCount; i++)
1515 : {
1516 17 : if (!ArgIsNumeric(papszTokens[i]) &&
1517 0 : !EQUAL(papszTokens[i], "nan") &&
1518 17 : !EQUAL(papszTokens[i], "-inf") &&
1519 0 : !EQUAL(papszTokens[i], "inf"))
1520 : {
1521 0 : CPLError(CE_Failure, CPLE_IllegalArg,
1522 : "Invalid -vrtnodata value");
1523 0 : CSLDestroy(papszTokens);
1524 0 : return nullptr;
1525 : }
1526 17 : padfVRTNoData[i] = CPLAtofM(papszTokens[i]);
1527 : }
1528 15 : CSLDestroy(papszTokens);
1529 : }
1530 : }
1531 :
1532 158 : bool bFoundValid = false;
1533 2423 : for (int i = 0; ppszInputFilenames != nullptr && i < nInputFiles; i++)
1534 : {
1535 2266 : const char *dsFileName = ppszInputFilenames[i];
1536 :
1537 2266 : if (!pfnProgress(1.0 * (i + 1) / nInputFiles, nullptr, pProgressData))
1538 : {
1539 0 : return nullptr;
1540 : }
1541 :
1542 2266 : GDALDatasetH hDS = (pahSrcDS)
1543 2266 : ? pahSrcDS[i]
1544 1164 : : GDALOpenEx(dsFileName, GDAL_OF_RASTER, nullptr,
1545 1164 : papszOpenOptions, nullptr);
1546 2266 : asDatasetProperties[i].isFileOK = FALSE;
1547 :
1548 2266 : if (hDS)
1549 : {
1550 2264 : const auto osErrorMsg = AnalyseRaster(hDS, &asDatasetProperties[i]);
1551 2264 : if (osErrorMsg.empty())
1552 : {
1553 2256 : asDatasetProperties[i].isFileOK = TRUE;
1554 2256 : bFoundValid = true;
1555 2256 : bFirst = FALSE;
1556 : }
1557 2264 : if (pahSrcDS == nullptr)
1558 1162 : GDALClose(hDS);
1559 2264 : if (!osErrorMsg.empty() && osErrorMsg != "SILENTLY_IGNORE")
1560 : {
1561 8 : if (bStrict)
1562 : {
1563 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
1564 : osErrorMsg.c_str());
1565 0 : return nullptr;
1566 : }
1567 : else
1568 : {
1569 8 : CPLError(CE_Warning, CPLE_AppDefined, "%s Skipping %s",
1570 : osErrorMsg.c_str(), dsFileName);
1571 : }
1572 : }
1573 : }
1574 : else
1575 : {
1576 2 : if (bStrict)
1577 : {
1578 1 : CPLError(CE_Failure, CPLE_AppDefined, "Can't open %s.",
1579 : dsFileName);
1580 1 : return nullptr;
1581 : }
1582 : else
1583 : {
1584 1 : CPLError(CE_Warning, CPLE_AppDefined,
1585 : "Can't open %s. Skipping it", dsFileName);
1586 : }
1587 : }
1588 : }
1589 :
1590 157 : if (!bFoundValid)
1591 2 : return nullptr;
1592 :
1593 155 : if (bHasGeoTransform)
1594 : {
1595 154 : if (bTargetAlignedPixels)
1596 : {
1597 1 : minX = floor(minX / we_res) * we_res;
1598 1 : maxX = ceil(maxX / we_res) * we_res;
1599 1 : minY = floor(minY / -ns_res) * -ns_res;
1600 1 : maxY = ceil(maxY / -ns_res) * -ns_res;
1601 : }
1602 :
1603 154 : nRasterXSize = static_cast<int>(0.5 + (maxX - minX) / we_res);
1604 154 : nRasterYSize = static_cast<int>(0.5 + (maxY - minY) / -ns_res);
1605 : }
1606 :
1607 155 : if (nRasterXSize == 0 || nRasterYSize == 0)
1608 : {
1609 0 : CPLError(CE_Failure, CPLE_AppDefined,
1610 : "Computed VRT dimension is invalid. You've probably "
1611 : "specified inappropriate resolution.");
1612 0 : return nullptr;
1613 : }
1614 :
1615 310 : VRTDatasetH hVRTDS = cpl::down_cast<VRTDataset *>(
1616 155 : VRTDataset::Create(pszOutputFilename, nRasterXSize, nRasterYSize, 0,
1617 : GDT_Unknown, aosCreateOptions.List()));
1618 155 : if (!hVRTDS)
1619 : {
1620 0 : return nullptr;
1621 : }
1622 :
1623 155 : if (pszOutputSRS)
1624 : {
1625 1 : GDALSetProjection(hVRTDS, pszOutputSRS);
1626 : }
1627 154 : else if (pszProjectionRef)
1628 : {
1629 154 : GDALSetProjection(hVRTDS, pszProjectionRef);
1630 : }
1631 :
1632 155 : if (bHasGeoTransform)
1633 : {
1634 : double adfGeoTransform[6];
1635 154 : adfGeoTransform[GEOTRSFRM_TOPLEFT_X] = minX;
1636 154 : adfGeoTransform[GEOTRSFRM_WE_RES] = we_res;
1637 154 : adfGeoTransform[GEOTRSFRM_ROTATION_PARAM1] = 0;
1638 154 : adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] = maxY;
1639 154 : adfGeoTransform[GEOTRSFRM_ROTATION_PARAM2] = 0;
1640 154 : adfGeoTransform[GEOTRSFRM_NS_RES] = ns_res;
1641 154 : GDALSetGeoTransform(hVRTDS, adfGeoTransform);
1642 : }
1643 :
1644 155 : if (bSeparate)
1645 : {
1646 11 : CreateVRTSeparate(hVRTDS);
1647 : }
1648 : else
1649 : {
1650 144 : CreateVRTNonSeparate(hVRTDS);
1651 : }
1652 :
1653 155 : return static_cast<GDALDataset *>(hVRTDS);
1654 : }
1655 :
1656 : /************************************************************************/
1657 : /* add_file_to_list() */
1658 : /************************************************************************/
1659 :
1660 45 : static bool add_file_to_list(const char *filename, const char *tile_index,
1661 : CPLStringList &aosList)
1662 : {
1663 :
1664 45 : if (EQUAL(CPLGetExtension(filename), "SHP"))
1665 : {
1666 : /* Handle gdaltindex Shapefile as a special case */
1667 1 : auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(filename));
1668 1 : if (poDS == nullptr)
1669 : {
1670 0 : CPLError(CE_Failure, CPLE_AppDefined,
1671 : "Unable to open shapefile `%s'.", filename);
1672 0 : return false;
1673 : }
1674 :
1675 1 : auto poLayer = poDS->GetLayer(0);
1676 1 : const auto poFDefn = poLayer->GetLayerDefn();
1677 :
1678 2 : if (poFDefn->GetFieldIndex("LOCATION") >= 0 &&
1679 1 : strcmp("LOCATION", tile_index) != 0)
1680 : {
1681 1 : CPLError(CE_Failure, CPLE_AppDefined,
1682 : "This shapefile seems to be a tile index of "
1683 : "OGR features and not GDAL products.");
1684 : }
1685 1 : const int ti_field = poFDefn->GetFieldIndex(tile_index);
1686 1 : if (ti_field < 0)
1687 : {
1688 0 : CPLError(CE_Failure, CPLE_AppDefined,
1689 : "Unable to find field `%s' in DBF file `%s'.", tile_index,
1690 : filename);
1691 0 : return false;
1692 : }
1693 :
1694 : /* Load in memory existing file names in SHP */
1695 1 : const auto nTileIndexFiles = poLayer->GetFeatureCount(TRUE);
1696 1 : if (nTileIndexFiles == 0)
1697 : {
1698 0 : CPLError(CE_Warning, CPLE_AppDefined,
1699 : "Tile index %s is empty. Skipping it.", filename);
1700 0 : return true;
1701 : }
1702 1 : if (nTileIndexFiles > 100 * 1024 * 1024)
1703 : {
1704 0 : CPLError(CE_Failure, CPLE_AppDefined,
1705 : "Too large feature count in tile index");
1706 0 : return false;
1707 : }
1708 :
1709 5 : for (auto &&poFeature : poLayer)
1710 : {
1711 4 : aosList.AddString(poFeature->GetFieldAsString(ti_field));
1712 : }
1713 : }
1714 : else
1715 : {
1716 44 : aosList.AddString(filename);
1717 : }
1718 :
1719 45 : return true;
1720 : }
1721 :
1722 : /************************************************************************/
1723 : /* GDALBuildVRTOptions */
1724 : /************************************************************************/
1725 :
1726 : /** Options for use with GDALBuildVRT(). GDALBuildVRTOptions* must be allocated
1727 : * and freed with GDALBuildVRTOptionsNew() and GDALBuildVRTOptionsFree()
1728 : * respectively.
1729 : */
1730 : struct GDALBuildVRTOptions
1731 : {
1732 : std::string osTileIndex = "location";
1733 : bool bStrict = false;
1734 : std::string osResolution{};
1735 : bool bSeparate = false;
1736 : bool bAllowProjectionDifference = false;
1737 : double we_res = 0;
1738 : double ns_res = 0;
1739 : bool bTargetAlignedPixels = false;
1740 : double xmin = 0;
1741 : double ymin = 0;
1742 : double xmax = 0;
1743 : double ymax = 0;
1744 : bool bAddAlpha = false;
1745 : bool bHideNoData = false;
1746 : int nSubdataset = -1;
1747 : std::string osSrcNoData{};
1748 : std::string osVRTNoData{};
1749 : std::string osOutputSRS{};
1750 : std::vector<int> anSelectedBandList{};
1751 : std::string osResampling{};
1752 : CPLStringList aosOpenOptions{};
1753 : CPLStringList aosCreateOptions{};
1754 : bool bUseSrcMaskBand = true;
1755 : bool bNoDataFromMask = false;
1756 : double dfMaskValueThreshold = 0;
1757 :
1758 : /*! allow or suppress progress monitor and other non-error output */
1759 : bool bQuiet = true;
1760 :
1761 : /*! the progress function to use */
1762 : GDALProgressFunc pfnProgress = GDALDummyProgress;
1763 :
1764 : /*! pointer to the progress data variable */
1765 : void *pProgressData = nullptr;
1766 : };
1767 :
1768 : /************************************************************************/
1769 : /* GDALBuildVRT() */
1770 : /************************************************************************/
1771 :
1772 : /* clang-format off */
1773 : /**
1774 : * Build a VRT from a list of datasets.
1775 : *
1776 : * This is the equivalent of the
1777 : * <a href="/programs/gdalbuildvrt.html">gdalbuildvrt</a> utility.
1778 : *
1779 : * GDALBuildVRTOptions* must be allocated and freed with
1780 : * GDALBuildVRTOptionsNew() and GDALBuildVRTOptionsFree() respectively. pahSrcDS
1781 : * and papszSrcDSNames cannot be used at the same time.
1782 : *
1783 : * @param pszDest the destination dataset path.
1784 : * @param nSrcCount the number of input datasets.
1785 : * @param pahSrcDS the list of input datasets (or NULL, exclusive with
1786 : * papszSrcDSNames). For practical purposes, the type
1787 : * of this argument should be considered as "const GDALDatasetH* const*", that
1788 : * is neither the array nor its values are mutated by this function.
1789 : * @param papszSrcDSNames the list of input dataset names (or NULL, exclusive
1790 : * with pahSrcDS)
1791 : * @param psOptionsIn the options struct returned by GDALBuildVRTOptionsNew() or
1792 : * NULL.
1793 : * @param pbUsageError pointer to a integer output variable to store if any
1794 : * usage error has occurred.
1795 : * @return the output dataset (new dataset that must be closed using
1796 : * GDALClose()) or NULL in case of error. If using pahSrcDS, the returned VRT
1797 : * dataset has a reference to each pahSrcDS[] element. Hence pahSrcDS[] elements
1798 : * should be closed after the returned dataset if using GDALClose().
1799 : * A safer alternative is to use GDALReleaseDataset() instead of using
1800 : * GDALClose(), in which case you can close datasets in any order.
1801 :
1802 : *
1803 : * @since GDAL 2.1
1804 : */
1805 : /* clang-format on */
1806 :
1807 159 : GDALDatasetH GDALBuildVRT(const char *pszDest, int nSrcCount,
1808 : GDALDatasetH *pahSrcDS,
1809 : const char *const *papszSrcDSNames,
1810 : const GDALBuildVRTOptions *psOptionsIn,
1811 : int *pbUsageError)
1812 : {
1813 159 : if (pszDest == nullptr)
1814 0 : pszDest = "";
1815 :
1816 159 : if (nSrcCount == 0)
1817 : {
1818 0 : CPLError(CE_Failure, CPLE_AppDefined, "No input dataset specified.");
1819 :
1820 0 : if (pbUsageError)
1821 0 : *pbUsageError = TRUE;
1822 0 : return nullptr;
1823 : }
1824 :
1825 : // cppcheck-suppress unreadVariable
1826 : GDALBuildVRTOptions sOptions(psOptionsIn ? *psOptionsIn
1827 318 : : GDALBuildVRTOptions());
1828 :
1829 6 : if (sOptions.we_res != 0 && sOptions.ns_res != 0 &&
1830 165 : !sOptions.osResolution.empty() &&
1831 0 : !EQUAL(sOptions.osResolution.c_str(), "user"))
1832 : {
1833 0 : CPLError(CE_Failure, CPLE_NotSupported,
1834 : "-tr option is not compatible with -resolution %s",
1835 : sOptions.osResolution.c_str());
1836 0 : if (pbUsageError)
1837 0 : *pbUsageError = TRUE;
1838 0 : return nullptr;
1839 : }
1840 :
1841 159 : if (sOptions.bTargetAlignedPixels && sOptions.we_res == 0 &&
1842 1 : sOptions.ns_res == 0)
1843 : {
1844 1 : CPLError(CE_Failure, CPLE_NotSupported,
1845 : "-tap option cannot be used without using -tr");
1846 1 : if (pbUsageError)
1847 1 : *pbUsageError = TRUE;
1848 1 : return nullptr;
1849 : }
1850 :
1851 158 : if (sOptions.bAddAlpha && sOptions.bSeparate)
1852 : {
1853 0 : CPLError(CE_Failure, CPLE_NotSupported,
1854 : "-addalpha option is not compatible with -separate.");
1855 0 : if (pbUsageError)
1856 0 : *pbUsageError = TRUE;
1857 0 : return nullptr;
1858 : }
1859 :
1860 158 : ResolutionStrategy eStrategy = AVERAGE_RESOLUTION;
1861 158 : if (sOptions.osResolution.empty() ||
1862 0 : EQUAL(sOptions.osResolution.c_str(), "user"))
1863 : {
1864 158 : if (sOptions.we_res != 0 || sOptions.ns_res != 0)
1865 6 : eStrategy = USER_RESOLUTION;
1866 152 : else if (EQUAL(sOptions.osResolution.c_str(), "user"))
1867 : {
1868 0 : CPLError(CE_Failure, CPLE_NotSupported,
1869 : "-tr option must be used with -resolution user.");
1870 0 : if (pbUsageError)
1871 0 : *pbUsageError = TRUE;
1872 0 : return nullptr;
1873 : }
1874 : }
1875 0 : else if (EQUAL(sOptions.osResolution.c_str(), "average"))
1876 0 : eStrategy = AVERAGE_RESOLUTION;
1877 0 : else if (EQUAL(sOptions.osResolution.c_str(), "highest"))
1878 0 : eStrategy = HIGHEST_RESOLUTION;
1879 0 : else if (EQUAL(sOptions.osResolution.c_str(), "lowest"))
1880 0 : eStrategy = LOWEST_RESOLUTION;
1881 :
1882 : /* If -srcnodata is specified, use it as the -vrtnodata if the latter is not
1883 : */
1884 : /* specified */
1885 158 : if (!sOptions.osSrcNoData.empty() && sOptions.osVRTNoData.empty())
1886 2 : sOptions.osVRTNoData = sOptions.osSrcNoData;
1887 :
1888 : VRTBuilder oBuilder(
1889 158 : sOptions.bStrict, pszDest, nSrcCount, papszSrcDSNames, pahSrcDS,
1890 158 : sOptions.anSelectedBandList.empty()
1891 : ? nullptr
1892 18 : : sOptions.anSelectedBandList.data(),
1893 158 : static_cast<int>(sOptions.anSelectedBandList.size()), eStrategy,
1894 158 : sOptions.we_res, sOptions.ns_res, sOptions.bTargetAlignedPixels,
1895 : sOptions.xmin, sOptions.ymin, sOptions.xmax, sOptions.ymax,
1896 158 : sOptions.bSeparate, sOptions.bAllowProjectionDifference,
1897 158 : sOptions.bAddAlpha, sOptions.bHideNoData, sOptions.nSubdataset,
1898 162 : sOptions.osSrcNoData.empty() ? nullptr : sOptions.osSrcNoData.c_str(),
1899 16 : sOptions.osVRTNoData.empty() ? nullptr : sOptions.osVRTNoData.c_str(),
1900 158 : sOptions.bUseSrcMaskBand, sOptions.bNoDataFromMask,
1901 : sOptions.dfMaskValueThreshold,
1902 159 : sOptions.osOutputSRS.empty() ? nullptr : sOptions.osOutputSRS.c_str(),
1903 169 : sOptions.osResampling.empty() ? nullptr : sOptions.osResampling.c_str(),
1904 813 : sOptions.aosOpenOptions.List(), sOptions.aosCreateOptions);
1905 :
1906 158 : return GDALDataset::ToHandle(
1907 158 : oBuilder.Build(sOptions.pfnProgress, sOptions.pProgressData));
1908 : }
1909 :
1910 : /************************************************************************/
1911 : /* SanitizeSRS */
1912 : /************************************************************************/
1913 :
1914 1 : static char *SanitizeSRS(const char *pszUserInput)
1915 :
1916 : {
1917 : OGRSpatialReferenceH hSRS;
1918 1 : char *pszResult = nullptr;
1919 :
1920 1 : CPLErrorReset();
1921 :
1922 1 : hSRS = OSRNewSpatialReference(nullptr);
1923 1 : if (OSRSetFromUserInput(hSRS, pszUserInput) == OGRERR_NONE)
1924 1 : OSRExportToWkt(hSRS, &pszResult);
1925 : else
1926 : {
1927 0 : CPLError(CE_Failure, CPLE_AppDefined, "Translating SRS failed:\n%s",
1928 : pszUserInput);
1929 : }
1930 :
1931 1 : OSRDestroySpatialReference(hSRS);
1932 :
1933 1 : return pszResult;
1934 : }
1935 :
1936 : /************************************************************************/
1937 : /* GDALBuildVRTOptionsGetParser() */
1938 : /************************************************************************/
1939 :
1940 : static std::unique_ptr<GDALArgumentParser>
1941 160 : GDALBuildVRTOptionsGetParser(GDALBuildVRTOptions *psOptions,
1942 : GDALBuildVRTOptionsForBinary *psOptionsForBinary)
1943 : {
1944 : auto argParser = std::make_unique<GDALArgumentParser>(
1945 160 : "gdalbuildvrt", /* bForBinary=*/psOptionsForBinary != nullptr);
1946 :
1947 160 : argParser->add_description(_("Builds a VRT from a list of datasets."));
1948 :
1949 160 : argParser->add_epilog(_(
1950 : "\n"
1951 : "e.g.\n"
1952 : " % gdalbuildvrt doq_index.vrt doq/*.tif\n"
1953 : " % gdalbuildvrt -input_file_list my_list.txt doq_index.vrt\n"
1954 : "\n"
1955 : "NOTES:\n"
1956 : " o With -separate, each files goes into a separate band in the VRT "
1957 : "band.\n"
1958 : " Otherwise, the files are considered as tiles of a larger mosaic.\n"
1959 : " o -b option selects a band to add into vrt. Multiple bands can be "
1960 : "listed.\n"
1961 : " By default all bands are queried.\n"
1962 : " o The default tile index field is 'location' unless otherwise "
1963 : "specified by\n"
1964 : " -tileindex.\n"
1965 : " o In case the resolution of all input files is not the same, the "
1966 : "-resolution\n"
1967 : " flag enable the user to control the way the output resolution is "
1968 : "computed.\n"
1969 : " Average is the default.\n"
1970 : " o Input files may be any valid GDAL dataset or a GDAL raster tile "
1971 : "index.\n"
1972 : " o For a GDAL raster tile index, all entries will be added to the "
1973 : "VRT.\n"
1974 : " o If one GDAL dataset is made of several subdatasets and has 0 "
1975 : "raster bands,\n"
1976 : " its datasets will be added to the VRT rather than the dataset "
1977 : "itself.\n"
1978 : " Single subdataset could be selected by its number using the -sd "
1979 : "option.\n"
1980 : " o By default, only datasets of same projection and band "
1981 : "characteristics\n"
1982 : " may be added to the VRT.\n"
1983 : "\n"
1984 : "For more details, consult "
1985 160 : "https://gdal.org/programs/gdalbuildvrt.html"));
1986 :
1987 : argParser->add_quiet_argument(
1988 160 : psOptionsForBinary ? &psOptionsForBinary->bQuiet : nullptr);
1989 :
1990 : {
1991 160 : auto &group = argParser->add_mutually_exclusive_group();
1992 :
1993 160 : group.add_argument("-strict")
1994 160 : .flag()
1995 160 : .store_into(psOptions->bStrict)
1996 160 : .help(_("Turn warnings as failures."));
1997 :
1998 160 : group.add_argument("-non_strict")
1999 160 : .flag()
2000 0 : .action([psOptions](const std::string &)
2001 160 : { psOptions->bStrict = false; })
2002 : .help(_("Skip source datasets that have issues with warnings, and "
2003 160 : "continue processing."));
2004 : }
2005 :
2006 160 : argParser->add_argument("-tile_index")
2007 320 : .metavar("<field_name>")
2008 160 : .store_into(psOptions->osTileIndex)
2009 : .help(_("Use the specified value as the tile index field, instead of "
2010 160 : "the default value which is 'location'."));
2011 :
2012 160 : argParser->add_argument("-resolution")
2013 320 : .metavar("user|average|highest|lowest")
2014 : .action(
2015 0 : [psOptions](const std::string &s)
2016 : {
2017 0 : psOptions->osResolution = s;
2018 0 : if (!EQUAL(psOptions->osResolution.c_str(), "user") &&
2019 0 : !EQUAL(psOptions->osResolution.c_str(), "average") &&
2020 0 : !EQUAL(psOptions->osResolution.c_str(), "highest") &&
2021 0 : !EQUAL(psOptions->osResolution.c_str(), "lowest"))
2022 : {
2023 : throw std::invalid_argument(
2024 : CPLSPrintf("Illegal resolution value (%s).",
2025 0 : psOptions->osResolution.c_str()));
2026 : }
2027 160 : })
2028 160 : .help(_("Control the way the output resolution is computed."));
2029 :
2030 160 : argParser->add_argument("-tr")
2031 320 : .metavar("<xres> <yes>")
2032 160 : .nargs(2)
2033 160 : .scan<'g', double>()
2034 160 : .help(_("Set target resolution."));
2035 :
2036 160 : if (psOptionsForBinary)
2037 : {
2038 21 : argParser->add_argument("-input_file_list")
2039 42 : .metavar("<filename>")
2040 : .action(
2041 5 : [psOptions, psOptionsForBinary](const std::string &s)
2042 : {
2043 1 : const char *input_file_list = s.c_str();
2044 : auto f = VSIVirtualHandleUniquePtr(
2045 2 : VSIFOpenL(input_file_list, "r"));
2046 1 : if (f)
2047 : {
2048 : while (1)
2049 : {
2050 5 : const char *filename = CPLReadLineL(f.get());
2051 5 : if (filename == nullptr)
2052 1 : break;
2053 4 : if (!add_file_to_list(
2054 : filename, psOptions->osTileIndex.c_str(),
2055 4 : psOptionsForBinary->aosSrcFiles))
2056 : {
2057 : throw std::invalid_argument(
2058 0 : std::string("Cannot add ")
2059 0 : .append(filename)
2060 0 : .append(" to input file list"));
2061 : }
2062 4 : }
2063 : }
2064 22 : })
2065 21 : .help(_("Text file with an input filename on each line"));
2066 : }
2067 :
2068 160 : argParser->add_argument("-separate")
2069 160 : .flag()
2070 160 : .store_into(psOptions->bSeparate)
2071 160 : .help(_("Place each input file into a separate band."));
2072 :
2073 160 : argParser->add_argument("-allow_projection_difference")
2074 160 : .flag()
2075 160 : .store_into(psOptions->bAllowProjectionDifference)
2076 : .help(_("Accept source files not in the same projection (but without "
2077 160 : "reprojecting them!)."));
2078 :
2079 160 : argParser->add_argument("-sd")
2080 320 : .metavar("<n>")
2081 160 : .store_into(psOptions->nSubdataset)
2082 : .help(_("Use subdataset of specified index (starting at 1), instead of "
2083 160 : "the source dataset itself."));
2084 :
2085 160 : argParser->add_argument("-tap")
2086 160 : .flag()
2087 160 : .store_into(psOptions->bTargetAlignedPixels)
2088 : .help(_("Align the coordinates of the extent of the output file to the "
2089 160 : "values of the resolution."));
2090 :
2091 160 : argParser->add_argument("-te")
2092 320 : .metavar("<xmin> <ymin> <xmax> <ymax>")
2093 160 : .nargs(4)
2094 160 : .scan<'g', double>()
2095 160 : .help(_("Set georeferenced extents of output file to be created."));
2096 :
2097 160 : argParser->add_argument("-addalpha")
2098 160 : .flag()
2099 160 : .store_into(psOptions->bAddAlpha)
2100 : .help(_("Adds an alpha mask band to the VRT when the source raster "
2101 160 : "have none."));
2102 :
2103 160 : argParser->add_argument("-b")
2104 320 : .metavar("<band>")
2105 160 : .append()
2106 160 : .store_into(psOptions->anSelectedBandList)
2107 160 : .help(_("Specify input band(s) number."));
2108 :
2109 160 : argParser->add_argument("-hidenodata")
2110 160 : .flag()
2111 160 : .store_into(psOptions->bHideNoData)
2112 160 : .help(_("Makes the VRT band not report the NoData."));
2113 :
2114 160 : if (psOptionsForBinary)
2115 : {
2116 21 : argParser->add_argument("-overwrite")
2117 21 : .flag()
2118 21 : .store_into(psOptionsForBinary->bOverwrite)
2119 21 : .help(_("Overwrite the VRT if it already exists."));
2120 : }
2121 :
2122 160 : argParser->add_argument("-srcnodata")
2123 320 : .metavar("\"<value>[ <value>]...\"")
2124 160 : .store_into(psOptions->osSrcNoData)
2125 160 : .help(_("Set nodata values for input bands."));
2126 :
2127 160 : argParser->add_argument("-vrtnodata")
2128 320 : .metavar("\"<value>[ <value>]...\"")
2129 160 : .store_into(psOptions->osVRTNoData)
2130 160 : .help(_("Set nodata values at the VRT band level."));
2131 :
2132 160 : argParser->add_argument("-a_srs")
2133 320 : .metavar("<srs_def>")
2134 : .action(
2135 2 : [psOptions](const std::string &s)
2136 : {
2137 1 : char *pszSRS = SanitizeSRS(s.c_str());
2138 1 : if (pszSRS == nullptr)
2139 : {
2140 0 : throw std::invalid_argument("Invalid value for -a_srs");
2141 : }
2142 1 : psOptions->osOutputSRS = pszSRS;
2143 1 : CPLFree(pszSRS);
2144 161 : })
2145 160 : .help(_("Override the projection for the output file.."));
2146 :
2147 160 : argParser->add_argument("-r")
2148 320 : .metavar("nearest|bilinear|cubic|cubicspline|lanczos|average|mode")
2149 160 : .store_into(psOptions->osResampling)
2150 160 : .help(_("Resampling algorithm."));
2151 :
2152 160 : argParser->add_open_options_argument(&psOptions->aosOpenOptions);
2153 :
2154 160 : argParser->add_creation_options_argument(psOptions->aosCreateOptions);
2155 :
2156 160 : argParser->add_argument("-ignore_srcmaskband")
2157 160 : .flag()
2158 0 : .action([psOptions](const std::string &)
2159 160 : { psOptions->bUseSrcMaskBand = false; })
2160 160 : .help(_("Cause mask band of sources will not be taken into account."));
2161 :
2162 160 : argParser->add_argument("-nodata_max_mask_threshold")
2163 320 : .metavar("<threshold>")
2164 160 : .scan<'g', double>()
2165 : .action(
2166 18 : [psOptions](const std::string &s)
2167 : {
2168 9 : psOptions->bNoDataFromMask = true;
2169 9 : psOptions->dfMaskValueThreshold = CPLAtofM(s.c_str());
2170 160 : })
2171 : .help(_("Replaces the value of the source with the value of -vrtnodata "
2172 : "when the value of the mask band of the source is less or "
2173 160 : "equal to the threshold."));
2174 :
2175 160 : if (psOptionsForBinary)
2176 : {
2177 21 : if (psOptionsForBinary->osDstFilename.empty())
2178 : {
2179 : // We normally go here, unless undocumented -o switch is used
2180 21 : argParser->add_argument("vrt_dataset_name")
2181 42 : .metavar("<vrt_dataset_name>")
2182 21 : .store_into(psOptionsForBinary->osDstFilename)
2183 21 : .help(_("Output VRT."));
2184 : }
2185 :
2186 21 : argParser->add_argument("src_dataset_name")
2187 42 : .metavar("<src_dataset_name>")
2188 21 : .nargs(argparse::nargs_pattern::any)
2189 : .action(
2190 41 : [psOptions, psOptionsForBinary](const std::string &s)
2191 : {
2192 41 : if (!add_file_to_list(s.c_str(),
2193 : psOptions->osTileIndex.c_str(),
2194 41 : psOptionsForBinary->aosSrcFiles))
2195 : {
2196 : throw std::invalid_argument(
2197 0 : std::string("Cannot add ")
2198 0 : .append(s)
2199 0 : .append(" to input file list"));
2200 : }
2201 62 : })
2202 21 : .help(_("Input dataset(s)."));
2203 : }
2204 :
2205 160 : return argParser;
2206 : }
2207 :
2208 : /************************************************************************/
2209 : /* GDALBuildVRTGetParserUsage() */
2210 : /************************************************************************/
2211 :
2212 1 : std::string GDALBuildVRTGetParserUsage()
2213 : {
2214 : try
2215 : {
2216 2 : GDALBuildVRTOptions sOptions;
2217 2 : GDALBuildVRTOptionsForBinary sOptionsForBinary;
2218 : auto argParser =
2219 2 : GDALBuildVRTOptionsGetParser(&sOptions, &sOptionsForBinary);
2220 1 : return argParser->usage();
2221 : }
2222 0 : catch (const std::exception &err)
2223 : {
2224 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
2225 0 : err.what());
2226 0 : return std::string();
2227 : }
2228 : }
2229 :
2230 : /************************************************************************/
2231 : /* GDALBuildVRTOptionsNew() */
2232 : /************************************************************************/
2233 :
2234 : /**
2235 : * Allocates a GDALBuildVRTOptions struct.
2236 : *
2237 : * @param papszArgv NULL terminated list of options (potentially including
2238 : * filename and open options too), or NULL. The accepted options are the ones of
2239 : * the <a href="/programs/gdalbuildvrt.html">gdalbuildvrt</a> utility.
2240 : * @param psOptionsForBinary (output) may be NULL (and should generally be
2241 : * NULL), otherwise (gdalbuildvrt_bin.cpp use case) must be allocated with
2242 : * GDALBuildVRTOptionsForBinaryNew() prior to this function. Will be filled
2243 : * with potentially present filename, open options,...
2244 : * @return pointer to the allocated GDALBuildVRTOptions struct. Must be freed
2245 : * with GDALBuildVRTOptionsFree().
2246 : *
2247 : * @since GDAL 2.1
2248 : */
2249 :
2250 : GDALBuildVRTOptions *
2251 159 : GDALBuildVRTOptionsNew(char **papszArgv,
2252 : GDALBuildVRTOptionsForBinary *psOptionsForBinary)
2253 : {
2254 317 : auto psOptions = std::make_unique<GDALBuildVRTOptions>();
2255 :
2256 317 : CPLStringList aosArgv;
2257 159 : const int nArgc = CSLCount(papszArgv);
2258 819 : for (int i = 0;
2259 819 : i < nArgc && papszArgv != nullptr && papszArgv[i] != nullptr; i++)
2260 : {
2261 660 : if (psOptionsForBinary && EQUAL(papszArgv[i], "-o") && i + 1 < nArgc &&
2262 0 : papszArgv[i + 1] != nullptr)
2263 : {
2264 : // Undocumented alternate way of specifying the destination file
2265 0 : psOptionsForBinary->osDstFilename = papszArgv[i + 1];
2266 0 : ++i;
2267 : }
2268 : // argparser will be confused if the value of a string argument
2269 : // starts with a negative sign.
2270 660 : else if (EQUAL(papszArgv[i], "-srcnodata") && i + 1 < nArgc)
2271 : {
2272 4 : ++i;
2273 4 : psOptions->osSrcNoData = papszArgv[i];
2274 : }
2275 : // argparser will be confused if the value of a string argument
2276 : // starts with a negative sign.
2277 656 : else if (EQUAL(papszArgv[i], "-vrtnodata") && i + 1 < nArgc)
2278 : {
2279 14 : ++i;
2280 14 : psOptions->osVRTNoData = papszArgv[i];
2281 : }
2282 :
2283 : else
2284 : {
2285 642 : aosArgv.AddString(papszArgv[i]);
2286 : }
2287 : }
2288 :
2289 : try
2290 : {
2291 : auto argParser =
2292 317 : GDALBuildVRTOptionsGetParser(psOptions.get(), psOptionsForBinary);
2293 :
2294 159 : argParser->parse_args_without_binary_name(aosArgv.List());
2295 :
2296 164 : if (auto adfTargetRes = argParser->present<std::vector<double>>("-tr"))
2297 : {
2298 6 : psOptions->we_res = (*adfTargetRes)[0];
2299 6 : psOptions->ns_res = (*adfTargetRes)[1];
2300 : }
2301 :
2302 163 : if (auto oTE = argParser->present<std::vector<double>>("-te"))
2303 : {
2304 5 : psOptions->xmin = (*oTE)[0];
2305 5 : psOptions->ymin = (*oTE)[1];
2306 5 : psOptions->xmax = (*oTE)[2];
2307 5 : psOptions->ymax = (*oTE)[3];
2308 : }
2309 :
2310 158 : return psOptions.release();
2311 : }
2312 0 : catch (const std::exception &err)
2313 : {
2314 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", err.what());
2315 0 : return nullptr;
2316 : }
2317 : }
2318 :
2319 : /************************************************************************/
2320 : /* GDALBuildVRTOptionsFree() */
2321 : /************************************************************************/
2322 :
2323 : /**
2324 : * Frees the GDALBuildVRTOptions struct.
2325 : *
2326 : * @param psOptions the options struct for GDALBuildVRT().
2327 : *
2328 : * @since GDAL 2.1
2329 : */
2330 :
2331 157 : void GDALBuildVRTOptionsFree(GDALBuildVRTOptions *psOptions)
2332 : {
2333 157 : delete psOptions;
2334 157 : }
2335 :
2336 : /************************************************************************/
2337 : /* GDALBuildVRTOptionsSetProgress() */
2338 : /************************************************************************/
2339 :
2340 : /**
2341 : * Set a progress function.
2342 : *
2343 : * @param psOptions the options struct for GDALBuildVRT().
2344 : * @param pfnProgress the progress callback.
2345 : * @param pProgressData the user data for the progress callback.
2346 : *
2347 : * @since GDAL 2.1
2348 : */
2349 :
2350 20 : void GDALBuildVRTOptionsSetProgress(GDALBuildVRTOptions *psOptions,
2351 : GDALProgressFunc pfnProgress,
2352 : void *pProgressData)
2353 : {
2354 20 : psOptions->pfnProgress = pfnProgress ? pfnProgress : GDALDummyProgress;
2355 20 : psOptions->pProgressData = pProgressData;
2356 20 : if (pfnProgress == GDALTermProgress)
2357 19 : psOptions->bQuiet = false;
2358 20 : }
|