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