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