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