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