1 : /******************************************************************************
2 : * $Id: gdalbuildvrt.cpp 18306 2009-12-15 18:57:11Z rouault $
3 : *
4 : * Project: GDAL Utilities
5 : * Purpose: Commandline application to build VRT datasets from raster products or content of SHP tile index
6 : * Author: Even Rouault, even.rouault at mines-paris.org
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2007, Even Rouault
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "gdal_proxy.h"
31 : #include "cpl_string.h"
32 : #include "vrt/gdal_vrt.h"
33 :
34 : #ifdef OGR_ENABLED
35 : #include "ogr_api.h"
36 : #endif
37 :
38 : CPL_CVSID("$Id: gdalbuildvrt.cpp 18306 2009-12-15 18:57:11Z rouault $");
39 :
40 : #define GEOTRSFRM_TOPLEFT_X 0
41 : #define GEOTRSFRM_WE_RES 1
42 : #define GEOTRSFRM_ROTATION_PARAM1 2
43 : #define GEOTRSFRM_TOPLEFT_Y 3
44 : #define GEOTRSFRM_ROTATION_PARAM2 4
45 : #define GEOTRSFRM_NS_RES 5
46 :
47 : typedef enum
48 : {
49 : LOWEST_RESOLUTION,
50 : HIGHEST_RESOLUTION,
51 : AVERAGE_RESOLUTION,
52 : USER_RESOLUTION
53 : } ResolutionStrategy;
54 :
55 : typedef struct
56 : {
57 : int isFileOK;
58 : int nRasterXSize;
59 : int nRasterYSize;
60 : double adfGeoTransform[6];
61 : int nBlockXSize;
62 : int nBlockYSize;
63 : GDALDataType firstBandType;
64 : int* panHasNoData;
65 : double* padfNoDataValues;
66 : } DatasetProperty;
67 :
68 : typedef struct
69 : {
70 : GDALColorInterp colorInterpretation;
71 : GDALDataType dataType;
72 : GDALColorTableH colorTable;
73 : int bHasNoData;
74 : double noDataValue;
75 : } BandProperty;
76 :
77 : /************************************************************************/
78 : /* Usage() */
79 : /************************************************************************/
80 :
81 0 : static void Usage()
82 :
83 : {
84 : fprintf(stdout, "%s",
85 : "Usage: gdalbuildvrt [-tileindex field_name] [-resolution {highest|lowest|average|user}]\n"
86 : " [-tr xres yres] [-separate] [-allow_projection_difference] [-q]\n"
87 : " [-te xmin ymin xmax ymax] [-addalpha] \n"
88 : " [-srcnodata \"value [value...]\"] [-vrtnodata \"value [value...]\"] \n"
89 : " [-input_file_list my_liste.txt] output.vrt [gdalfile]*\n"
90 : "\n"
91 : "eg.\n"
92 : " % gdalbuildvrt doq_index.vrt doq/*.tif\n"
93 : " % gdalbuildvrt -input_file_list my_liste.txt doq_index.vrt\n"
94 : "\n"
95 : "NOTES:\n"
96 : " o With -separate, each files goes into a separate band in the VRT band. Otherwise,\n"
97 : " the files are considered as tiles of a larger mosaic.\n"
98 : " o The default tile index field is 'location' unless otherwise specified by -tileindex.\n"
99 : " o In case the resolution of all input files is not the same, the -resolution flag.\n"
100 : " enable the user to control the way the output resolution is computed. average is the default.\n"
101 : " o Input files may be any valid GDAL dataset or a GDAL raster tile index.\n"
102 : " o For a GDAL raster tile index, all entries will be added to the VRT.\n"
103 : " o If one GDAL dataset is made of several subdatasets and has 0 raster bands, its\n"
104 : " datasets will be added to the VRT rather than the dataset itself.\n"
105 : " o By default, only datasets of same projection and band characteristics may be added to the VRT.\n"
106 0 : );
107 0 : exit( 1 );
108 : }
109 :
110 :
111 38 : int GetSrcDstWin(DatasetProperty* psDP,
112 : double we_res, double ns_res,
113 : double minX, double minY, double maxX, double maxY,
114 : int* pnSrcXOff, int* pnSrcYOff, int* pnSrcXSize, int* pnSrcYSize,
115 : int* pnDstXOff, int* pnDstYOff, int* pnDstXSize, int* pnDstYSize)
116 : {
117 : /* Check that the destination bounding box intersects the source bounding box */
118 76 : if ( psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_X] +
119 : psDP->nRasterXSize *
120 38 : psDP->adfGeoTransform[GEOTRSFRM_WE_RES] < minX )
121 0 : return FALSE;
122 38 : if ( psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_X] > maxX )
123 0 : return FALSE;
124 76 : if ( psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] +
125 : psDP->nRasterYSize *
126 38 : psDP->adfGeoTransform[GEOTRSFRM_NS_RES] > maxY )
127 0 : return FALSE;
128 38 : if ( psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] < minY )
129 0 : return FALSE;
130 :
131 38 : *pnSrcXSize = psDP->nRasterXSize;
132 38 : *pnSrcYSize = psDP->nRasterYSize;
133 38 : if ( psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_X] < minX )
134 : {
135 1 : *pnSrcXOff = (int)((minX - psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_X]) /
136 1 : psDP->adfGeoTransform[GEOTRSFRM_WE_RES] + 0.5);
137 1 : *pnDstXOff = 0;
138 : }
139 : else
140 : {
141 37 : *pnSrcXOff = 0;
142 : *pnDstXOff = (int)
143 37 : (0.5 + (psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_X] - minX) / we_res);
144 : }
145 38 : if ( maxY < psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_Y])
146 : {
147 1 : *pnSrcYOff = (int)((psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] - maxY) /
148 1 : -psDP->adfGeoTransform[GEOTRSFRM_NS_RES] + 0.5);
149 1 : *pnDstYOff = 0;
150 : }
151 : else
152 : {
153 37 : *pnSrcYOff = 0;
154 : *pnDstYOff = (int)
155 37 : (0.5 + (maxY - psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_Y]) / -ns_res);
156 : }
157 : *pnDstXSize = (int)
158 : (0.5 + psDP->nRasterXSize *
159 38 : psDP->adfGeoTransform[GEOTRSFRM_WE_RES] / we_res);
160 : *pnDstYSize = (int)
161 : (0.5 + psDP->nRasterYSize *
162 38 : psDP->adfGeoTransform[GEOTRSFRM_NS_RES] / ns_res);
163 :
164 38 : return TRUE;
165 : }
166 :
167 : /************************************************************************/
168 : /* GDALBuildVRT() */
169 : /************************************************************************/
170 :
171 12 : CPLErr GDALBuildVRT( const char* pszOutputFilename,
172 : int* pnInputFiles, char*** pppszInputFilenames,
173 : ResolutionStrategy resolutionStrategy,
174 : double we_res, double ns_res,
175 : double minX, double minY, double maxX, double maxY,
176 : int bSeparate, int bAllowProjectionDifference,
177 : int bAddAlpha,
178 : const char* pszSrcNoData, const char* pszVRTNoData,
179 : GDALProgressFunc pfnProgress, void * pProgressData)
180 : {
181 12 : char* projectionRef = NULL;
182 12 : int nBands = 0;
183 12 : BandProperty* bandProperties = NULL;
184 : int i,j;
185 : int rasterXSize;
186 : int rasterYSize;
187 12 : int nCount = 0;
188 12 : int bFirst = TRUE;
189 12 : VRTDatasetH hVRTDS = NULL;
190 12 : CPLErr eErr = CE_None;
191 :
192 12 : if( pfnProgress == NULL )
193 0 : pfnProgress = GDALDummyProgress;
194 :
195 12 : int bUserExtent = (minX != 0 || minY != 0 || maxX != 0 || maxY != 0);
196 12 : if (bUserExtent)
197 : {
198 2 : if (minX >= maxX || minY >= maxY )
199 : {
200 0 : CPLError(CE_Failure, CPLE_IllegalArg, "Invalid user extent");
201 0 : return CE_Failure;
202 : }
203 : }
204 :
205 12 : if (resolutionStrategy == USER_RESOLUTION)
206 : {
207 2 : if (we_res <= 0 || ns_res <= 0)
208 : {
209 0 : CPLError(CE_Failure, CPLE_IllegalArg, "Invalid user resolution");
210 0 : return CE_Failure;
211 : }
212 :
213 : /* We work with negative north-south resolution in all the following code */
214 2 : ns_res = -ns_res;
215 : }
216 : else
217 : {
218 10 : we_res = ns_res = 0;
219 : }
220 :
221 12 : char** ppszInputFilenames = *pppszInputFilenames;
222 12 : int nInputFiles = *pnInputFiles;
223 :
224 : DatasetProperty* psDatasetProperties =
225 12 : (DatasetProperty*) CPLCalloc(nInputFiles, sizeof(DatasetProperty));
226 :
227 :
228 12 : int bAllowSrcNoData = TRUE;
229 12 : double* padfSrcNoData = NULL;
230 12 : int nSrcNoDataCount = 0;
231 12 : if (pszSrcNoData != NULL)
232 : {
233 1 : if (EQUAL(pszSrcNoData, "none"))
234 : {
235 0 : bAllowSrcNoData = FALSE;
236 : }
237 : else
238 : {
239 1 : char **papszTokens = CSLTokenizeString( pszSrcNoData );
240 1 : nSrcNoDataCount = CSLCount(papszTokens);
241 1 : padfSrcNoData = (double *) CPLMalloc(sizeof(double) * nSrcNoDataCount);
242 2 : for(i=0;i<nSrcNoDataCount;i++)
243 1 : padfSrcNoData[i] = atof(papszTokens[i]);
244 1 : CSLDestroy(papszTokens);
245 : }
246 : }
247 :
248 :
249 12 : int bAllowVRTNoData = TRUE;
250 12 : double* padfVRTNoData = NULL;
251 12 : int nVRTNoDataCount = 0;
252 12 : if (pszVRTNoData != NULL)
253 : {
254 1 : if (EQUAL(pszVRTNoData, "none"))
255 : {
256 0 : bAllowVRTNoData = FALSE;
257 : }
258 : else
259 : {
260 1 : char **papszTokens = CSLTokenizeString( pszVRTNoData );
261 1 : nVRTNoDataCount = CSLCount(papszTokens);
262 1 : padfVRTNoData = (double *) CPLMalloc(sizeof(double) * nVRTNoDataCount);
263 2 : for(i=0;i<nVRTNoDataCount;i++)
264 1 : padfVRTNoData[i] = atof(papszTokens[i]);
265 1 : CSLDestroy(papszTokens);
266 : }
267 : }
268 :
269 52 : for(i=0;i<nInputFiles;i++)
270 : {
271 40 : const char* dsFileName = ppszInputFilenames[i];
272 :
273 40 : if (!pfnProgress( 1.0 * (i+1) / nInputFiles, NULL, pProgressData))
274 : {
275 0 : eErr = CE_Failure;
276 0 : goto end;
277 : }
278 :
279 40 : GDALDatasetH hDS = GDALOpen(ppszInputFilenames[i], GA_ReadOnly );
280 40 : psDatasetProperties[i].isFileOK = FALSE;
281 :
282 40 : if (hDS)
283 : {
284 40 : char** papszMetadata = GDALGetMetadata( hDS, "SUBDATASETS" );
285 40 : if( CSLCount(papszMetadata) > 0 && GDALGetRasterCount(hDS) == 0 )
286 : {
287 : psDatasetProperties =
288 0 : (DatasetProperty*) CPLMalloc((nInputFiles+CSLCount(papszMetadata))*sizeof(DatasetProperty));
289 :
290 : ppszInputFilenames = (char**)CPLRealloc(ppszInputFilenames,
291 0 : sizeof(char*) * (nInputFiles+CSLCount(papszMetadata)));
292 0 : int count = 1;
293 : char subdatasetNameKey[256];
294 0 : sprintf(subdatasetNameKey, "SUBDATASET_%d_NAME", count);
295 0 : while(*papszMetadata != NULL)
296 : {
297 0 : if (EQUALN(*papszMetadata, subdatasetNameKey, strlen(subdatasetNameKey)))
298 : {
299 0 : ppszInputFilenames[nInputFiles++] =
300 0 : CPLStrdup(*papszMetadata+strlen(subdatasetNameKey)+1);
301 0 : count++;
302 0 : sprintf(subdatasetNameKey, "SUBDATASET_%d_NAME", count);
303 : }
304 0 : papszMetadata++;
305 : }
306 0 : GDALClose(hDS);
307 0 : continue;
308 : }
309 :
310 40 : const char* proj = GDALGetProjectionRef(hDS);
311 40 : GDALGetGeoTransform(hDS, psDatasetProperties[i].adfGeoTransform);
312 80 : if (psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_ROTATION_PARAM1] != 0 ||
313 40 : psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_ROTATION_PARAM2] != 0)
314 : {
315 : CPLError(CE_Warning, CPLE_NotSupported,
316 : "gdalbuildvrt does not support rotated geo transforms. Skipping %s",
317 0 : dsFileName);
318 0 : GDALClose(hDS);
319 0 : continue;
320 : }
321 40 : if (psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_NS_RES] >= 0)
322 : {
323 : CPLError(CE_Warning, CPLE_NotSupported,
324 : "gdalbuildvrt does not support positive NS resolution. Skipping %s",
325 0 : dsFileName);
326 0 : GDALClose(hDS);
327 0 : continue;
328 : }
329 40 : psDatasetProperties[i].nRasterXSize = GDALGetRasterXSize(hDS);
330 40 : psDatasetProperties[i].nRasterYSize = GDALGetRasterYSize(hDS);
331 40 : double product_minX = psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_TOPLEFT_X];
332 40 : double product_maxY = psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_TOPLEFT_Y];
333 : double product_maxX = product_minX +
334 : GDALGetRasterXSize(hDS) *
335 40 : psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_WE_RES];
336 : double product_minY = product_maxY +
337 : GDALGetRasterYSize(hDS) *
338 40 : psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_NS_RES];
339 :
340 : GDALGetBlockSize(GDALGetRasterBand( hDS, 1 ),
341 40 : &psDatasetProperties[i].nBlockXSize,
342 80 : &psDatasetProperties[i].nBlockYSize);
343 :
344 40 : int _nBands = GDALGetRasterCount(hDS);
345 40 : if (_nBands == 0)
346 : {
347 : CPLError(CE_Warning, CPLE_AppDefined,
348 0 : "Skipping %s as it has no bands", dsFileName);
349 0 : GDALClose(hDS);
350 0 : continue;
351 : }
352 40 : else if (_nBands > 1 && bSeparate)
353 : {
354 : CPLError(CE_Warning, CPLE_AppDefined, "%s has %d bands. Only the first one will "
355 : "be taken into account in the -separate case",
356 0 : dsFileName, _nBands);
357 0 : _nBands = 1;
358 : }
359 :
360 : /* For the -separate case */
361 40 : psDatasetProperties[i].firstBandType = GDALGetRasterDataType(GDALGetRasterBand(hDS, 1));
362 :
363 40 : psDatasetProperties[i].padfNoDataValues = (double*)CPLMalloc(sizeof(double) * _nBands);
364 40 : psDatasetProperties[i].panHasNoData = (int*)CPLMalloc(sizeof(int) * _nBands);
365 85 : for(j=0;j<_nBands;j++)
366 : {
367 45 : if (nSrcNoDataCount > 0)
368 : {
369 2 : psDatasetProperties[i].panHasNoData[j] = TRUE;
370 2 : if (j < nSrcNoDataCount)
371 2 : psDatasetProperties[i].padfNoDataValues[j] = padfSrcNoData[j];
372 : else
373 0 : psDatasetProperties[i].padfNoDataValues[j] = padfSrcNoData[nSrcNoDataCount - 1];
374 : }
375 : else
376 : {
377 43 : psDatasetProperties[i].padfNoDataValues[j] =
378 : GDALGetRasterNoDataValue(GDALGetRasterBand(hDS, j+1),
379 43 : &psDatasetProperties[i].panHasNoData[j]);
380 : }
381 : }
382 :
383 40 : if (bFirst)
384 : {
385 12 : if (proj)
386 12 : projectionRef = CPLStrdup(proj);
387 12 : if (!bUserExtent)
388 : {
389 10 : minX = product_minX;
390 10 : minY = product_minY;
391 10 : maxX = product_maxX;
392 10 : maxY = product_maxY;
393 : }
394 12 : nBands = _nBands;
395 :
396 12 : if (!bSeparate)
397 : {
398 11 : bandProperties = (BandProperty*)CPLMalloc(nBands*sizeof(BandProperty));
399 24 : for(j=0;j<nBands;j++)
400 : {
401 13 : GDALRasterBandH hRasterBand = GDALGetRasterBand( hDS, j+1 );
402 13 : bandProperties[j].colorInterpretation =
403 13 : GDALGetRasterColorInterpretation(hRasterBand);
404 13 : bandProperties[j].dataType = GDALGetRasterDataType(hRasterBand);
405 13 : if (bandProperties[j].colorInterpretation == GCI_PaletteIndex)
406 : {
407 1 : bandProperties[j].colorTable =
408 1 : GDALGetRasterColorTable( hRasterBand );
409 1 : if (bandProperties[j].colorTable)
410 : {
411 1 : bandProperties[j].colorTable =
412 1 : GDALCloneColorTable(bandProperties[j].colorTable);
413 : }
414 : }
415 : else
416 12 : bandProperties[j].colorTable = 0;
417 :
418 13 : if (nVRTNoDataCount > 0)
419 : {
420 1 : bandProperties[j].bHasNoData = TRUE;
421 1 : if (j < nVRTNoDataCount)
422 1 : bandProperties[j].noDataValue = padfVRTNoData[j];
423 : else
424 0 : bandProperties[j].noDataValue = padfVRTNoData[nVRTNoDataCount - 1];
425 : }
426 : else
427 : {
428 12 : bandProperties[j].noDataValue =
429 12 : GDALGetRasterNoDataValue(hRasterBand, &bandProperties[j].bHasNoData);
430 : }
431 : }
432 : }
433 : }
434 : else
435 : {
436 28 : if ((proj != NULL && projectionRef == NULL) ||
437 : (proj == NULL && projectionRef != NULL) ||
438 : (proj != NULL && projectionRef != NULL && EQUAL(proj, projectionRef) == FALSE))
439 : {
440 1 : if (!bAllowProjectionDifference)
441 : {
442 : CPLError(CE_Warning, CPLE_NotSupported,
443 : "gdalbuildvrt does not support heterogenous projection. Skipping %s",
444 1 : dsFileName);
445 1 : GDALClose(hDS);
446 1 : continue;
447 : }
448 : }
449 27 : if (!bSeparate)
450 : {
451 24 : if (nBands != _nBands)
452 : {
453 : CPLError(CE_Warning, CPLE_NotSupported,
454 : "gdalbuildvrt does not support heterogenous band numbers. Skipping %s",
455 1 : dsFileName);
456 1 : GDALClose(hDS);
457 1 : continue;
458 : }
459 48 : for(j=0;j<nBands;j++)
460 : {
461 25 : GDALRasterBandH hRasterBand = GDALGetRasterBand( hDS, j+1 );
462 50 : if (bandProperties[j].colorInterpretation != GDALGetRasterColorInterpretation(hRasterBand) ||
463 25 : bandProperties[j].dataType != GDALGetRasterDataType(hRasterBand))
464 : {
465 : CPLError(CE_Warning, CPLE_NotSupported,
466 : "gdalbuildvrt does not support heterogenous band characteristics. Skipping %s",
467 0 : dsFileName);
468 0 : GDALClose(hDS);
469 : }
470 25 : if (bandProperties[j].colorTable)
471 : {
472 1 : GDALColorTableH colorTable = GDALGetRasterColorTable( hRasterBand );
473 2 : if (colorTable == NULL ||
474 1 : GDALGetColorEntryCount(colorTable) != GDALGetColorEntryCount(bandProperties[j].colorTable))
475 : {
476 : CPLError(CE_Warning, CPLE_NotSupported,
477 : "gdalbuildvrt does not support heterogenous band characteristics. Skipping %s",
478 0 : dsFileName);
479 0 : GDALClose(hDS);
480 0 : break;
481 : }
482 : /* We should check that the palette are the same too ! */
483 : }
484 : }
485 23 : if (j != nBands)
486 0 : continue;
487 : }
488 26 : if (!bUserExtent)
489 : {
490 23 : if (product_minX < minX) minX = product_minX;
491 23 : if (product_minY < minY) minY = product_minY;
492 23 : if (product_maxX > maxX) maxX = product_maxX;
493 23 : if (product_maxY > maxY) maxY = product_maxY;
494 : }
495 : }
496 :
497 38 : if (resolutionStrategy == AVERAGE_RESOLUTION)
498 : {
499 33 : we_res += psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_WE_RES];
500 33 : ns_res += psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_NS_RES];
501 : }
502 5 : else if (resolutionStrategy != USER_RESOLUTION)
503 : {
504 0 : if (bFirst)
505 : {
506 0 : we_res = psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_WE_RES];
507 0 : ns_res = psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_NS_RES];
508 : }
509 0 : else if (resolutionStrategy == HIGHEST_RESOLUTION)
510 : {
511 0 : we_res = MIN(we_res, psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_WE_RES]);
512 : /* Yes : as ns_res is negative, the highest resolution is the max value */
513 0 : ns_res = MAX(ns_res, psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_NS_RES]);
514 : }
515 : else
516 : {
517 0 : we_res = MAX(we_res, psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_WE_RES]);
518 : /* Yes : as ns_res is negative, the lowest resolution is the min value */
519 0 : ns_res = MIN(ns_res, psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_NS_RES]);
520 : }
521 : }
522 :
523 38 : psDatasetProperties[i].isFileOK = 1;
524 38 : nCount ++;
525 38 : bFirst = FALSE;
526 38 : GDALClose(hDS);
527 : }
528 : else
529 : {
530 : CPLError(CE_Warning, CPLE_AppDefined,
531 0 : "Warning : can't open %s. Skipping it", dsFileName);
532 : }
533 : }
534 :
535 12 : *pppszInputFilenames = ppszInputFilenames;
536 12 : *pnInputFiles = nInputFiles;
537 :
538 12 : if (nCount == 0)
539 0 : goto end;
540 :
541 12 : if (resolutionStrategy == AVERAGE_RESOLUTION)
542 : {
543 10 : we_res /= nCount;
544 10 : ns_res /= nCount;
545 : }
546 :
547 12 : rasterXSize = (int)(0.5 + (maxX - minX) / we_res);
548 12 : rasterYSize = (int)(0.5 + (maxY - minY) / -ns_res);
549 :
550 12 : if (rasterXSize == 0 || rasterYSize == 0)
551 : {
552 : CPLError(CE_Failure, CPLE_AppDefined,
553 0 : "Computed VRT dimension is invalid. You've probably specified unappropriate resolution.");
554 0 : goto end;
555 : }
556 :
557 12 : hVRTDS = VRTCreate(rasterXSize, rasterYSize);
558 12 : GDALSetDescription(hVRTDS, pszOutputFilename);
559 :
560 12 : if (projectionRef)
561 : {
562 12 : GDALSetProjection(hVRTDS, projectionRef);
563 : }
564 :
565 : double adfGeoTransform[6];
566 12 : adfGeoTransform[GEOTRSFRM_TOPLEFT_X] = minX;
567 12 : adfGeoTransform[GEOTRSFRM_WE_RES] = we_res;
568 12 : adfGeoTransform[GEOTRSFRM_ROTATION_PARAM1] = 0;
569 12 : adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] = maxY;
570 12 : adfGeoTransform[GEOTRSFRM_ROTATION_PARAM2] = 0;
571 12 : adfGeoTransform[GEOTRSFRM_NS_RES] = ns_res;
572 12 : GDALSetGeoTransform(hVRTDS, adfGeoTransform);
573 :
574 12 : if (bSeparate)
575 : {
576 1 : int iBand = 1;
577 5 : for(i=0;i<nInputFiles;i++)
578 : {
579 4 : if (psDatasetProperties[i].isFileOK == 0)
580 0 : continue;
581 :
582 : int nSrcXOff, nSrcYOff, nSrcXSize, nSrcYSize,
583 : nDstXOff, nDstYOff, nDstXSize, nDstYSize;
584 4 : if ( ! GetSrcDstWin(&psDatasetProperties[i],
585 : we_res, ns_res, minX, minY, maxX, maxY,
586 : &nSrcXOff, &nSrcYOff, &nSrcXSize, &nSrcYSize,
587 : &nDstXOff, &nDstYOff, &nDstXSize, &nDstYSize) )
588 0 : continue;
589 :
590 4 : const char* dsFileName = ppszInputFilenames[i];
591 :
592 4 : GDALAddBand(hVRTDS, psDatasetProperties[i].firstBandType, NULL);
593 :
594 : GDALProxyPoolDatasetH hProxyDS =
595 : GDALProxyPoolDatasetCreate(dsFileName,
596 4 : psDatasetProperties[i].nRasterXSize,
597 4 : psDatasetProperties[i].nRasterYSize,
598 : GA_ReadOnly, TRUE, projectionRef,
599 12 : psDatasetProperties[i].adfGeoTransform);
600 : GDALProxyPoolDatasetAddSrcBandDescription(hProxyDS,
601 4 : psDatasetProperties[i].firstBandType,
602 4 : psDatasetProperties[i].nBlockXSize,
603 12 : psDatasetProperties[i].nBlockYSize);
604 :
605 : VRTSourcedRasterBandH hVRTBand =
606 4 : (VRTSourcedRasterBandH)GDALGetRasterBand(hVRTDS, iBand);
607 4 : if (bAllowSrcNoData && psDatasetProperties[i].panHasNoData[0])
608 : {
609 0 : GDALSetRasterNoDataValue(hVRTBand, psDatasetProperties[i].padfNoDataValues[0]);
610 : VRTAddComplexSource(hVRTBand, GDALGetRasterBand((GDALDatasetH)hProxyDS, 1),
611 : nSrcXOff, nSrcYOff, nSrcXSize, nSrcYSize,
612 : nDstXOff, nDstYOff, nDstXSize, nDstYSize,
613 0 : 0, 1, psDatasetProperties[i].padfNoDataValues[0]);
614 : }
615 : else
616 : /* Place the raster band at the right position in the VRT */
617 : VRTAddSimpleSource(hVRTBand, GDALGetRasterBand((GDALDatasetH)hProxyDS, 1),
618 : nSrcXOff, nSrcYOff, nSrcXSize, nSrcYSize,
619 : nDstXOff, nDstYOff, nDstXSize, nDstYSize,
620 4 : "near", VRT_NODATA_UNSET);
621 :
622 4 : GDALDereferenceDataset(hProxyDS);
623 :
624 4 : iBand ++;
625 : }
626 : }
627 : else
628 : {
629 24 : for(j=0;j<nBands;j++)
630 : {
631 : GDALRasterBandH hBand;
632 13 : GDALAddBand(hVRTDS, bandProperties[j].dataType, NULL);
633 13 : hBand = GDALGetRasterBand(hVRTDS, j+1);
634 13 : GDALSetRasterColorInterpretation(hBand, bandProperties[j].colorInterpretation);
635 13 : if (bandProperties[j].colorInterpretation == GCI_PaletteIndex)
636 : {
637 1 : GDALSetRasterColorTable(hBand, bandProperties[j].colorTable);
638 : }
639 13 : if (bAllowVRTNoData && bandProperties[j].bHasNoData)
640 4 : GDALSetRasterNoDataValue(hBand, bandProperties[j].noDataValue);
641 : }
642 :
643 11 : if (bAddAlpha)
644 : {
645 : GDALRasterBandH hBand;
646 0 : GDALAddBand(hVRTDS, GDT_Byte, NULL);
647 0 : hBand = GDALGetRasterBand(hVRTDS, nBands + 1);
648 0 : GDALSetRasterColorInterpretation(hBand, GCI_AlphaBand);
649 : }
650 :
651 47 : for(i=0;i<nInputFiles;i++)
652 : {
653 36 : if (psDatasetProperties[i].isFileOK == 0)
654 2 : continue;
655 :
656 : int nSrcXOff, nSrcYOff, nSrcXSize, nSrcYSize,
657 : nDstXOff, nDstYOff, nDstXSize, nDstYSize;
658 34 : if ( ! GetSrcDstWin(&psDatasetProperties[i],
659 : we_res, ns_res, minX, minY, maxX, maxY,
660 : &nSrcXOff, &nSrcYOff, &nSrcXSize, &nSrcYSize,
661 : &nDstXOff, &nDstYOff, &nDstXSize, &nDstYSize) )
662 0 : continue;
663 :
664 34 : const char* dsFileName = ppszInputFilenames[i];
665 :
666 : GDALProxyPoolDatasetH hProxyDS =
667 : GDALProxyPoolDatasetCreate(dsFileName,
668 34 : psDatasetProperties[i].nRasterXSize,
669 34 : psDatasetProperties[i].nRasterYSize,
670 : GA_ReadOnly, TRUE, projectionRef,
671 102 : psDatasetProperties[i].adfGeoTransform);
672 :
673 72 : for(j=0;j<nBands;j++)
674 : {
675 : GDALProxyPoolDatasetAddSrcBandDescription(hProxyDS,
676 38 : bandProperties[j].dataType,
677 38 : psDatasetProperties[i].nBlockXSize,
678 114 : psDatasetProperties[i].nBlockYSize);
679 : }
680 :
681 72 : for(j=0;j<nBands;j++)
682 : {
683 : VRTSourcedRasterBandH hVRTBand =
684 38 : (VRTSourcedRasterBandH)GDALGetRasterBand(hVRTDS, j + 1);
685 :
686 : /* Place the raster band at the right position in the VRT */
687 46 : if (bAllowSrcNoData && psDatasetProperties[i].panHasNoData[j])
688 : VRTAddComplexSource(hVRTBand, GDALGetRasterBand((GDALDatasetH)hProxyDS, j + 1),
689 : nSrcXOff, nSrcYOff, nSrcXSize, nSrcYSize,
690 : nDstXOff, nDstYOff, nDstXSize, nDstYSize,
691 8 : 0, 1, psDatasetProperties[i].padfNoDataValues[j]);
692 : else
693 : VRTAddSimpleSource(hVRTBand, GDALGetRasterBand((GDALDatasetH)hProxyDS, j + 1),
694 : nSrcXOff, nSrcYOff, nSrcXSize, nSrcYSize,
695 : nDstXOff, nDstYOff, nDstXSize, nDstYSize,
696 30 : "near", VRT_NODATA_UNSET);
697 : }
698 :
699 34 : if (bAddAlpha)
700 : {
701 : VRTSourcedRasterBandH hVRTBand =
702 0 : (VRTSourcedRasterBandH)GDALGetRasterBand(hVRTDS, nBands + 1);
703 : /* Little trick : we use an offset of 255 and a scaling of 0, so that in areas covered */
704 : /* by the source, the value of the alpha band will be 255, otherwise it will be 0 */
705 : VRTAddComplexSource(hVRTBand, GDALGetRasterBand((GDALDatasetH)hProxyDS, 1),
706 : nSrcXOff, nSrcYOff, nSrcXSize, nSrcYSize,
707 : nDstXOff, nDstYOff, nDstXSize, nDstYSize,
708 0 : 255, 0, VRT_NODATA_UNSET);
709 : }
710 :
711 34 : GDALDereferenceDataset(hProxyDS);
712 : }
713 : }
714 12 : GDALClose(hVRTDS);
715 :
716 : end:
717 52 : for(i=0;i<nInputFiles;i++)
718 : {
719 40 : CPLFree(psDatasetProperties[i].padfNoDataValues);
720 40 : CPLFree(psDatasetProperties[i].panHasNoData);
721 : }
722 12 : CPLFree(psDatasetProperties);
723 12 : if (!bSeparate && bandProperties != NULL)
724 : {
725 24 : for(j=0;j<nBands;j++)
726 : {
727 13 : GDALDestroyColorTable(bandProperties[j].colorTable);
728 : }
729 : }
730 12 : CPLFree(bandProperties);
731 12 : CPLFree(projectionRef);
732 12 : CPLFree(padfSrcNoData);
733 12 : CPLFree(padfVRTNoData);
734 :
735 12 : return eErr;
736 : }
737 :
738 : /************************************************************************/
739 : /* add_file_to_list() */
740 : /************************************************************************/
741 :
742 37 : static void add_file_to_list(const char* filename, const char* tile_index,
743 : int* pnInputFiles, char*** pppszInputFilenames)
744 : {
745 : #ifndef OGR_ENABLED
746 : CPLError(CE_Failure, CPLE_AppDefined, "OGR support needed to read tileindex");
747 : *pnInputFiles = 0;
748 : *pppszInputFilenames = NULL;
749 : #else
750 : OGRDataSourceH hDS;
751 : OGRLayerH hLayer;
752 : OGRFeatureDefnH hFDefn;
753 : int j, ti_field;
754 :
755 37 : int nInputFiles = *pnInputFiles;
756 37 : char** ppszInputFilenames = *pppszInputFilenames;
757 :
758 37 : if (EQUAL(CPLGetExtension(filename), "SHP"))
759 : {
760 1 : OGRRegisterAll();
761 :
762 : /* Handle GDALTIndex Shapefile as a special case */
763 1 : hDS = OGROpen( filename, FALSE, NULL );
764 1 : if( hDS == NULL )
765 : {
766 : fprintf( stderr, "Unable to open shapefile `%s'.\n",
767 0 : filename );
768 0 : exit(2);
769 : }
770 :
771 1 : hLayer = OGR_DS_GetLayer(hDS, 0);
772 :
773 1 : hFDefn = OGR_L_GetLayerDefn(hLayer);
774 :
775 1 : for( ti_field = 0; ti_field < OGR_FD_GetFieldCount(hFDefn); ti_field++ )
776 : {
777 1 : OGRFieldDefnH hFieldDefn = OGR_FD_GetFieldDefn( hFDefn, ti_field );
778 1 : const char* pszName = OGR_Fld_GetNameRef(hFieldDefn);
779 :
780 1 : if (strcmp(pszName, "LOCATION") == 0 && strcmp("LOCATION", tile_index) != 0 )
781 : {
782 : fprintf( stderr, "This shapefile seems to be a tile index of "
783 0 : "OGR features and not GDAL products.\n");
784 : }
785 1 : if( strcmp(pszName, tile_index) == 0 )
786 1 : break;
787 : }
788 :
789 1 : if( ti_field == OGR_FD_GetFieldCount(hFDefn) )
790 : {
791 : fprintf( stderr, "Unable to find field `%s' in DBF file `%s'.\n",
792 0 : tile_index, filename );
793 0 : return;
794 : }
795 :
796 : /* Load in memory existing file names in SHP */
797 1 : int nTileIndexFiles = OGR_L_GetFeatureCount(hLayer, TRUE);
798 1 : if (nTileIndexFiles == 0)
799 : {
800 0 : fprintf( stderr, "Tile index %s is empty. Skipping it.\n", filename);
801 0 : return;
802 : }
803 :
804 : ppszInputFilenames = (char**)CPLRealloc(ppszInputFilenames,
805 1 : sizeof(char*) * (nInputFiles+nTileIndexFiles));
806 5 : for(j=0;j<nTileIndexFiles;j++)
807 : {
808 4 : OGRFeatureH hFeat = OGR_L_GetNextFeature(hLayer);
809 8 : ppszInputFilenames[nInputFiles++] =
810 4 : CPLStrdup(OGR_F_GetFieldAsString(hFeat, ti_field ));
811 4 : OGR_F_Destroy(hFeat);
812 : }
813 :
814 1 : OGR_DS_Destroy( hDS );
815 : }
816 : else
817 : {
818 : ppszInputFilenames = (char**)CPLRealloc(ppszInputFilenames,
819 36 : sizeof(char*) * (nInputFiles+1));
820 36 : ppszInputFilenames[nInputFiles++] = CPLStrdup(filename);
821 : }
822 :
823 37 : *pnInputFiles = nInputFiles;
824 37 : *pppszInputFilenames = ppszInputFilenames;
825 : #endif
826 : }
827 :
828 : /************************************************************************/
829 : /* main() */
830 : /************************************************************************/
831 :
832 13 : int main( int nArgc, char ** papszArgv )
833 :
834 : {
835 13 : const char *tile_index = "location";
836 13 : const char *resolution = NULL;
837 13 : int nInputFiles = 0;
838 13 : char ** ppszInputFilenames = NULL;
839 13 : const char * pszOutputFilename = NULL;
840 : int i, iArg;
841 13 : int bSeparate = FALSE;
842 13 : int bAllowProjectionDifference = FALSE;
843 13 : int bQuiet = FALSE;
844 13 : GDALProgressFunc pfnProgress = NULL;
845 13 : double we_res = 0, ns_res = 0;
846 13 : double xmin = 0, ymin = 0, xmax = 0, ymax = 0;
847 13 : int bAddAlpha = FALSE;
848 13 : int bForceOverwrite = FALSE;
849 13 : const char* pszSrcNoData = NULL;
850 13 : const char* pszVRTNoData = NULL;
851 :
852 13 : GDALAllRegister();
853 :
854 13 : nArgc = GDALGeneralCmdLineProcessor( nArgc, &papszArgv, 0 );
855 13 : if( nArgc < 1 )
856 0 : exit( -nArgc );
857 :
858 : /* -------------------------------------------------------------------- */
859 : /* Parse commandline. */
860 : /* -------------------------------------------------------------------- */
861 65 : for( iArg = 1; iArg < nArgc; iArg++ )
862 : {
863 53 : if( EQUAL(papszArgv[iArg], "--utility_version") )
864 : {
865 : printf("%s was compiled against GDAL %s and is running against GDAL %s\n",
866 1 : papszArgv[0], GDAL_RELEASE_NAME, GDALVersionInfo("RELEASE_NAME"));
867 1 : return 0;
868 : }
869 52 : else if( EQUAL(papszArgv[iArg],"-tileindex") &&
870 : iArg + 1 < nArgc)
871 : {
872 0 : tile_index = papszArgv[++iArg];
873 : }
874 52 : else if( EQUAL(papszArgv[iArg],"-resolution") &&
875 : iArg + 1 < nArgc)
876 : {
877 0 : resolution = papszArgv[++iArg];
878 : }
879 53 : else if( EQUAL(papszArgv[iArg],"-input_file_list") &&
880 : iArg + 1 < nArgc)
881 : {
882 1 : const char* input_file_list = papszArgv[++iArg];
883 1 : FILE* f = VSIFOpen(input_file_list, "r");
884 1 : if (f)
885 : {
886 4 : while(1)
887 : {
888 5 : const char* filename = CPLReadLine(f);
889 5 : if (filename == NULL)
890 : break;
891 : add_file_to_list(filename, tile_index,
892 4 : &nInputFiles, &ppszInputFilenames);
893 : }
894 1 : VSIFClose(f);
895 : }
896 : }
897 51 : else if ( EQUAL(papszArgv[iArg],"-separate") )
898 : {
899 1 : bSeparate = TRUE;
900 : }
901 50 : else if ( EQUAL(papszArgv[iArg],"-allow_projection_difference") )
902 : {
903 0 : bAllowProjectionDifference = TRUE;
904 : }
905 : /* Alternate syntax for output file */
906 50 : else if( EQUAL(papszArgv[iArg],"-o") &&
907 : iArg + 1 < nArgc)
908 : {
909 0 : pszOutputFilename = papszArgv[++iArg];
910 : }
911 50 : else if ( EQUAL(papszArgv[iArg],"-q") || EQUAL(papszArgv[iArg],"-quiet") )
912 : {
913 0 : bQuiet = TRUE;
914 : }
915 52 : else if ( EQUAL(papszArgv[iArg],"-tr") && iArg + 2 < nArgc)
916 : {
917 2 : we_res = atof(papszArgv[++iArg]);
918 2 : ns_res = atof(papszArgv[++iArg]);
919 : }
920 50 : else if ( EQUAL(papszArgv[iArg],"-te") && iArg + 4 < nArgc)
921 : {
922 2 : xmin = atof(papszArgv[++iArg]);
923 2 : ymin = atof(papszArgv[++iArg]);
924 2 : xmax = atof(papszArgv[++iArg]);
925 2 : ymax = atof(papszArgv[++iArg]);
926 : }
927 46 : else if ( EQUAL(papszArgv[iArg],"-addalpha") )
928 : {
929 0 : bAddAlpha = TRUE;
930 : }
931 46 : else if ( EQUAL(papszArgv[iArg],"-overwrite") )
932 : {
933 0 : bForceOverwrite = TRUE;
934 : }
935 47 : else if ( EQUAL(papszArgv[iArg],"-srcnodata") && iArg + 1 < nArgc)
936 : {
937 1 : pszSrcNoData = papszArgv[++iArg];
938 : }
939 45 : else if ( EQUAL(papszArgv[iArg],"-vrtnodata") && iArg + 1 < nArgc)
940 : {
941 0 : pszVRTNoData = papszArgv[++iArg];
942 : }
943 45 : else if ( papszArgv[iArg][0] == '-' )
944 : {
945 0 : printf("Unrecognized option : %s\n", papszArgv[iArg]);
946 0 : exit(-1);
947 : }
948 45 : else if( pszOutputFilename == NULL )
949 : {
950 12 : pszOutputFilename = papszArgv[iArg];
951 : }
952 : else
953 : {
954 33 : add_file_to_list(papszArgv[iArg], tile_index,
955 66 : &nInputFiles, &ppszInputFilenames);
956 : }
957 : }
958 :
959 12 : if( pszOutputFilename == NULL || nInputFiles == 0 )
960 0 : Usage();
961 :
962 12 : if (!bQuiet)
963 12 : pfnProgress = GDALTermProgress;
964 :
965 : /* Avoid overwriting a non VRT dataset if the user did not put the */
966 : /* filenames in the right order */
967 : VSIStatBuf sBuf;
968 12 : if (!bForceOverwrite)
969 : {
970 12 : int bExists = (VSIStat(pszOutputFilename, &sBuf) == 0);
971 12 : if (bExists)
972 : {
973 6 : GDALDriverH hDriver = GDALIdentifyDriver( pszOutputFilename, NULL );
974 6 : if (hDriver && !EQUAL(GDALGetDriverShortName(hDriver), "VRT"))
975 : {
976 : fprintf(stderr,
977 : "'%s' is an existing GDAL dataset managed by %s driver.\n"
978 : "There is an high chance you did not put filenames in the right order.\n"
979 : "If you want to overwrite %s, add -overwrite option to the command line.\n\n",
980 0 : pszOutputFilename, GDALGetDriverShortName(hDriver), pszOutputFilename);
981 0 : Usage();
982 : }
983 : }
984 : }
985 :
986 12 : if (we_res != 0 && ns_res != 0 &&
987 : resolution != NULL && !EQUAL(resolution, "user"))
988 : {
989 0 : fprintf(stderr, "-tr option is not compatible with -resolution %s\n", resolution);
990 0 : Usage();
991 : }
992 :
993 12 : if (bAddAlpha && bSeparate)
994 : {
995 0 : fprintf(stderr, "-addalpha option is not compatible with -separate\n");
996 0 : Usage();
997 : }
998 :
999 12 : ResolutionStrategy eStrategy = AVERAGE_RESOLUTION;
1000 24 : if ( resolution == NULL || EQUAL(resolution, "user") )
1001 : {
1002 14 : if ( we_res != 0 || ns_res != 0)
1003 2 : eStrategy = USER_RESOLUTION;
1004 10 : else if ( resolution != NULL && EQUAL(resolution, "user") )
1005 : {
1006 0 : fprintf(stderr, "-tr option must be used with -resolution user\n");
1007 0 : Usage();
1008 : }
1009 : }
1010 0 : else if ( EQUAL(resolution, "average") )
1011 0 : eStrategy = AVERAGE_RESOLUTION;
1012 0 : else if ( EQUAL(resolution, "highest") )
1013 0 : eStrategy = HIGHEST_RESOLUTION;
1014 0 : else if ( EQUAL(resolution, "lowest") )
1015 0 : eStrategy = LOWEST_RESOLUTION;
1016 : else
1017 : {
1018 0 : fprintf(stderr, "invalid value (%s) for -resolution\n", resolution);
1019 0 : Usage();
1020 : }
1021 :
1022 : /* If -srcnodata is specified, use it as the -vrtnodata if the latter is not */
1023 : /* specified */
1024 12 : if (pszSrcNoData != NULL && pszVRTNoData == NULL)
1025 1 : pszVRTNoData = pszSrcNoData;
1026 :
1027 : GDALBuildVRT(pszOutputFilename, &nInputFiles, &ppszInputFilenames,
1028 : eStrategy, we_res, ns_res, xmin, ymin, xmax, ymax,
1029 : bSeparate, bAllowProjectionDifference, bAddAlpha,
1030 12 : pszSrcNoData, pszVRTNoData, pfnProgress, NULL);
1031 :
1032 52 : for(i=0;i<nInputFiles;i++)
1033 : {
1034 40 : CPLFree(ppszInputFilenames[i]);
1035 : }
1036 12 : CPLFree(ppszInputFilenames);
1037 :
1038 :
1039 12 : CSLDestroy( papszArgv );
1040 12 : GDALDumpOpenDatasets( stderr );
1041 12 : GDALDestroyDriverManager();
1042 :
1043 12 : return 0;
1044 : }
|