1 /******************************************************************************
2 * $Id: tildataset.cpp 29198 2015-05-15 08:45:00Z rouault $
3 *
4 * Project: EarthWatch .TIL Driver
5 * Purpose: Implementation of the TILDataset class.
6 * Author: Frank Warmerdam, warmerdam@pobox.com
7 *
8 ******************************************************************************
9 * Copyright (c) 2009, Frank Warmerdam
10 * Copyright (c) 2009-2011, Even Rouault <even dot rouault at mines-paris dot org>
11 *
12 * Permission is hereby granted, free of charge, to any person obtaining a
13 * copy of this software and associated documentation files (the "Software"),
14 * to deal in the Software without restriction, including without limitation
15 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 * and/or sell copies of the Software, and to permit persons to whom the
17 * Software is furnished to do so, subject to the following conditions:
18 *
19 * The above copyright notice and this permission notice shall be included
20 * in all copies or substantial portions of the Software.
21 *
22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 * DEALINGS IN THE SOFTWARE.
29 ****************************************************************************/
30
31 #include "gdal_pam.h"
32 #include "gdal_proxy.h"
33 #include "ogr_spatialref.h"
34 #include "cpl_string.h"
35 #include "vrtdataset.h"
36 #include "cpl_multiproc.h"
37 #include "cplkeywordparser.h"
38 #include "gdal_mdreader.h"
39
40 CPL_CVSID("$Id: tildataset.cpp 29198 2015-05-15 08:45:00Z rouault $");
41
42 /************************************************************************/
43 /* ==================================================================== */
44 /* TILDataset */
45 /* ==================================================================== */
46 /************************************************************************/
47
48 class CPL_DLL TILDataset : public GDALPamDataset
49 {
50 VRTDataset *poVRTDS;
51 std::vector<GDALDataset *> apoTileDS;
52
53 char **papszMetadataFiles;
54
55 protected:
56 virtual int CloseDependentDatasets();
57
58 public:
59 TILDataset();
60 ~TILDataset();
61
62 virtual char **GetFileList(void);
63
64 static GDALDataset *Open( GDALOpenInfo * );
65 static int Identify( GDALOpenInfo *poOpenInfo );
66 };
67
68 /************************************************************************/
69 /* ==================================================================== */
70 /* TILRasterBand */
71 /* ==================================================================== */
72 /************************************************************************/
73
74 class TILRasterBand : public GDALPamRasterBand
75 {
76 friend class TILDataset;
77
78 VRTSourcedRasterBand *poVRTBand;
79
80 public:
81 TILRasterBand( TILDataset *, int, VRTSourcedRasterBand * );
~TILRasterBand()82 virtual ~TILRasterBand() {};
83
84 virtual CPLErr IReadBlock( int, int, void * );
85 virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int,
86 void *, int, int, GDALDataType,
87 GSpacing nPixelSpace, GSpacing nLineSpace,
88 GDALRasterIOExtraArg* psExtraArg );
89 };
90
91 /************************************************************************/
92 /* TILRasterBand() */
93 /************************************************************************/
94
TILRasterBand(TILDataset * poTILDS,int nBand,VRTSourcedRasterBand * poVRTBand)95 TILRasterBand::TILRasterBand( TILDataset *poTILDS, int nBand,
96 VRTSourcedRasterBand *poVRTBand )
97
98 {
99 this->poDS = poTILDS;
100 this->poVRTBand = poVRTBand;
101 this->nBand = nBand;
102 this->eDataType = poVRTBand->GetRasterDataType();
103
104 poVRTBand->GetBlockSize( &nBlockXSize, &nBlockYSize );
105 }
106
107 /************************************************************************/
108 /* IReadBlock() */
109 /************************************************************************/
110
IReadBlock(int iBlockX,int iBlockY,void * pBuffer)111 CPLErr TILRasterBand::IReadBlock( int iBlockX, int iBlockY, void *pBuffer )
112
113 {
114 return poVRTBand->ReadBlock( iBlockX, iBlockY, pBuffer );
115 }
116
117 /************************************************************************/
118 /* IRasterIO() */
119 /************************************************************************/
120
IRasterIO(GDALRWFlag eRWFlag,int nXOff,int nYOff,int nXSize,int nYSize,void * pData,int nBufXSize,int nBufYSize,GDALDataType eBufType,GSpacing nPixelSpace,GSpacing nLineSpace,GDALRasterIOExtraArg * psExtraArg)121 CPLErr TILRasterBand::IRasterIO( GDALRWFlag eRWFlag,
122 int nXOff, int nYOff, int nXSize, int nYSize,
123 void * pData, int nBufXSize, int nBufYSize,
124 GDALDataType eBufType,
125 GSpacing nPixelSpace, GSpacing nLineSpace,
126 GDALRasterIOExtraArg* psExtraArg )
127
128 {
129 if(GetOverviewCount() > 0)
130 {
131 return GDALPamRasterBand::IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize,
132 pData, nBufXSize, nBufYSize, eBufType,
133 nPixelSpace, nLineSpace, psExtraArg );
134 }
135 else //if not exist TIL overviews, try to use band source overviews
136 {
137 return poVRTBand->IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize,
138 pData, nBufXSize, nBufYSize, eBufType,
139 nPixelSpace, nLineSpace, psExtraArg );
140 }
141 }
142
143 /************************************************************************/
144 /* ==================================================================== */
145 /* TILDataset */
146 /* ==================================================================== */
147 /************************************************************************/
148
149 /************************************************************************/
150 /* TILDataset() */
151 /************************************************************************/
152
TILDataset()153 TILDataset::TILDataset()
154
155 {
156 poVRTDS = NULL;
157 papszMetadataFiles = NULL;
158 }
159
160 /************************************************************************/
161 /* ~TILDataset() */
162 /************************************************************************/
163
~TILDataset()164 TILDataset::~TILDataset()
165
166 {
167 CloseDependentDatasets();
168 CSLDestroy(papszMetadataFiles);
169 }
170
171 /************************************************************************/
172 /* CloseDependentDatasets() */
173 /************************************************************************/
174
CloseDependentDatasets()175 int TILDataset::CloseDependentDatasets()
176 {
177 int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets();
178
179 if( poVRTDS )
180 {
181 bHasDroppedRef = TRUE;
182 delete poVRTDS;
183 poVRTDS = NULL;
184 }
185
186 while( !apoTileDS.empty() )
187 {
188 GDALClose( (GDALDatasetH) apoTileDS.back() );
189 apoTileDS.pop_back();
190 }
191
192 return bHasDroppedRef;
193 }
194
195 /************************************************************************/
196 /* Identify() */
197 /************************************************************************/
198
Identify(GDALOpenInfo * poOpenInfo)199 int TILDataset::Identify( GDALOpenInfo *poOpenInfo )
200
201 {
202 if( poOpenInfo->nHeaderBytes < 200
203 || !EQUAL(CPLGetExtension(poOpenInfo->pszFilename),"TIL") )
204 return FALSE;
205
206 if( strstr((const char *) poOpenInfo->pabyHeader,"numTiles") == NULL )
207 return FALSE;
208 else
209 return TRUE;
210 }
211
212 /************************************************************************/
213 /* Open() */
214 /************************************************************************/
215
Open(GDALOpenInfo * poOpenInfo)216 GDALDataset *TILDataset::Open( GDALOpenInfo * poOpenInfo )
217
218 {
219 if( !Identify( poOpenInfo ) )
220 return NULL;
221
222 /* -------------------------------------------------------------------- */
223 /* Confirm the requested access is supported. */
224 /* -------------------------------------------------------------------- */
225 if( poOpenInfo->eAccess == GA_Update )
226 {
227 CPLError( CE_Failure, CPLE_NotSupported,
228 "The TIL driver does not support update access to existing"
229 " datasets.\n" );
230 return NULL;
231 }
232
233 CPLString osDirname = CPLGetDirname(poOpenInfo->pszFilename);
234
235 // get metadata reader
236
237 GDALMDReaderManager mdreadermanager;
238 GDALMDReaderBase* mdreader = mdreadermanager.GetReader(poOpenInfo->pszFilename,
239 poOpenInfo->GetSiblingFiles(), MDR_DG);
240
241 if(NULL == mdreader)
242 {
243 CPLError( CE_Failure, CPLE_OpenFailed,
244 "Unable to open .TIL dataset due to missing metadata file." );
245 return NULL;
246 }
247 /* -------------------------------------------------------------------- */
248 /* Try to find the corresponding .IMD file. */
249 /* -------------------------------------------------------------------- */
250 char **papszIMD = mdreader->GetMetadataDomain(MD_DOMAIN_IMD);
251
252 if( papszIMD == NULL )
253 {
254 CPLError( CE_Failure, CPLE_OpenFailed,
255 "Unable to open .TIL dataset due to missing .IMD file." );
256 return NULL;
257 }
258
259 if( CSLFetchNameValue( papszIMD, "numRows" ) == NULL
260 || CSLFetchNameValue( papszIMD, "numColumns" ) == NULL
261 || CSLFetchNameValue( papszIMD, "bitsPerPixel" ) == NULL )
262 {
263 CPLError( CE_Failure, CPLE_OpenFailed,
264 "Missing a required field in the .IMD file." );
265 return NULL;
266 }
267
268 /* -------------------------------------------------------------------- */
269 /* Try to load and parse the .TIL file. */
270 /* -------------------------------------------------------------------- */
271 VSILFILE *fp = VSIFOpenL( poOpenInfo->pszFilename, "r" );
272
273 if( fp == NULL )
274 {
275 return NULL;
276 }
277
278 CPLKeywordParser oParser;
279
280 if( !oParser.Ingest( fp ) )
281 {
282 VSIFCloseL( fp );
283 return NULL;
284 }
285
286 VSIFCloseL( fp );
287
288 char **papszTIL = oParser.GetAllKeywords();
289
290 /* -------------------------------------------------------------------- */
291 /* Create a corresponding GDALDataset. */
292 /* -------------------------------------------------------------------- */
293 TILDataset *poDS;
294
295 poDS = new TILDataset();
296 poDS->papszMetadataFiles = mdreader->GetMetadataFiles();
297 mdreader->FillMetadata(&poDS->oMDMD);
298 poDS->nRasterXSize = atoi(CSLFetchNameValueDef(papszIMD,"numColumns","0"));
299 poDS->nRasterYSize = atoi(CSLFetchNameValueDef(papszIMD,"numRows","0"));
300 if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
301 {
302 delete poDS;
303 return NULL;
304 }
305
306 /* -------------------------------------------------------------------- */
307 /* We need to open one of the images in order to establish */
308 /* details like the band count and types. */
309 /* -------------------------------------------------------------------- */
310 GDALDataset *poTemplateDS;
311 const char *pszFilename = CSLFetchNameValue( papszTIL, "TILE_1.filename" );
312 if( pszFilename == NULL )
313 {
314 CPLError( CE_Failure, CPLE_AppDefined,
315 "Missing TILE_1.filename in .TIL file." );
316 delete poDS;
317 return NULL;
318 }
319
320 // trim double quotes.
321 if( pszFilename[0] == '"' )
322 pszFilename++;
323 if( pszFilename[strlen(pszFilename)-1] == '"' )
324 ((char *) pszFilename)[strlen(pszFilename)-1] = '\0';
325
326 CPLString osFilename = CPLFormFilename(osDirname, pszFilename, NULL);
327 poTemplateDS = (GDALDataset *) GDALOpen( osFilename, GA_ReadOnly );
328 if( poTemplateDS == NULL || poTemplateDS->GetRasterCount() == 0)
329 {
330 delete poDS;
331 if (poTemplateDS != NULL)
332 GDALClose( poTemplateDS );
333 return NULL;
334 }
335
336 GDALRasterBand *poTemplateBand = poTemplateDS->GetRasterBand(1);
337 GDALDataType eDT = poTemplateBand->GetRasterDataType();
338 int nBandCount = poTemplateDS->GetRasterCount();
339
340 //we suppose the first tile have the same projection as others (usually so)
341 CPLString pszProjection(poTemplateDS->GetProjectionRef());
342 if(!pszProjection.empty())
343 poDS->SetProjection(pszProjection);
344
345 //we suppose the first tile have the same GeoTransform as others (usually so)
346 double adfGeoTransform[6];
347 if( poTemplateDS->GetGeoTransform( adfGeoTransform ) == CE_None )
348 {
349 // According to https://www.digitalglobe.com/sites/default/files/ISD_External.pdf, ulx=originX and
350 // is "Easting of the center of the upper left pixel of the image."
351 adfGeoTransform[0] = CPLAtof(CSLFetchNameValueDef(papszIMD,"MAP_PROJECTED_PRODUCT.ULX","0")) - adfGeoTransform[1] / 2;
352 adfGeoTransform[3] = CPLAtof(CSLFetchNameValueDef(papszIMD,"MAP_PROJECTED_PRODUCT.ULY","0")) - adfGeoTransform[5] / 2;
353 poDS->SetGeoTransform(adfGeoTransform);
354 }
355
356 poTemplateBand = NULL;
357 GDALClose( poTemplateDS );
358
359 /* -------------------------------------------------------------------- */
360 /* Create and initialize the corresponding VRT dataset used to */
361 /* manage the tiled data access. */
362 /* -------------------------------------------------------------------- */
363 int iBand;
364
365 poDS->poVRTDS = new VRTDataset(poDS->nRasterXSize,poDS->nRasterYSize);
366
367 for( iBand = 0; iBand < nBandCount; iBand++ )
368 poDS->poVRTDS->AddBand( eDT, NULL );
369
370 /* Don't try to write a VRT file */
371 poDS->poVRTDS->SetWritable(FALSE);
372
373 /* -------------------------------------------------------------------- */
374 /* Create band information objects. */
375 /* -------------------------------------------------------------------- */
376 for( iBand = 1; iBand <= nBandCount; iBand++ )
377 poDS->SetBand( iBand,
378 new TILRasterBand( poDS, iBand,
379 (VRTSourcedRasterBand *) poDS->poVRTDS->GetRasterBand(iBand)));
380
381 /* -------------------------------------------------------------------- */
382 /* Add tiles as sources for each band. */
383 /* -------------------------------------------------------------------- */
384 int nTileCount = atoi(CSLFetchNameValueDef(papszTIL,"numTiles","0"));
385 int iTile = 0;
386
387 for( iTile = 1; iTile <= nTileCount; iTile++ )
388 {
389 CPLString osKey;
390
391 osKey.Printf( "TILE_%d.filename", iTile );
392 pszFilename = CSLFetchNameValue( papszTIL, osKey );
393 if( pszFilename == NULL )
394 {
395 CPLError( CE_Failure, CPLE_AppDefined,
396 "Missing TILE_%d.filename in .TIL file.", iTile );
397 delete poDS;
398 return NULL;
399 }
400
401 // trim double quotes.
402 if( pszFilename[0] == '"' )
403 pszFilename++;
404 if( pszFilename[strlen(pszFilename)-1] == '"' )
405 ((char *) pszFilename)[strlen(pszFilename)-1] = '\0';
406 osFilename = CPLFormFilename(osDirname, pszFilename, NULL);
407
408 osKey.Printf( "TILE_%d.ULColOffset", iTile );
409 int nULX = atoi(CSLFetchNameValueDef(papszTIL, osKey, "0"));
410
411 osKey.Printf( "TILE_%d.ULRowOffset", iTile );
412 int nULY = atoi(CSLFetchNameValueDef(papszTIL, osKey, "0"));
413
414 osKey.Printf( "TILE_%d.LRColOffset", iTile );
415 int nLRX = atoi(CSLFetchNameValueDef(papszTIL, osKey, "0"));
416
417 osKey.Printf( "TILE_%d.LRRowOffset", iTile );
418 int nLRY = atoi(CSLFetchNameValueDef(papszTIL, osKey, "0"));
419
420 GDALDataset *poTileDS =
421 new GDALProxyPoolDataset( osFilename,
422 nLRX - nULX + 1, nLRY - nULY + 1 );
423 if( poTileDS == NULL )
424 continue;
425
426 poDS->apoTileDS.push_back( poTileDS );
427
428 for( iBand = 1; iBand <= nBandCount; iBand++ )
429 {
430 ((GDALProxyPoolDataset *) poTileDS)->
431 AddSrcBandDescription( eDT, nLRX - nULX + 1, 1 );
432
433 GDALRasterBand *poSrcBand = poTileDS->GetRasterBand(iBand);
434
435 VRTSourcedRasterBand *poVRTBand =
436 (VRTSourcedRasterBand *) poDS->poVRTDS->GetRasterBand(iBand);
437
438 poVRTBand->AddSimpleSource( poSrcBand,
439 0, 0,
440 nLRX - nULX + 1, nLRY - nULY + 1,
441 nULX, nULY,
442 nLRX - nULX + 1, nLRY - nULY + 1 );
443 }
444 }
445
446 /* -------------------------------------------------------------------- */
447 /* Initialize any PAM information. */
448 /* -------------------------------------------------------------------- */
449 poDS->SetDescription( poOpenInfo->pszFilename );
450 poDS->TryLoadXML();
451
452 /* -------------------------------------------------------------------- */
453 /* Check for overviews. */
454 /* -------------------------------------------------------------------- */
455 poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
456
457 return( poDS );
458 }
459
460 /************************************************************************/
461 /* GetFileList() */
462 /************************************************************************/
463
GetFileList()464 char **TILDataset::GetFileList()
465
466 {
467 unsigned int i;
468 char **papszFileList = GDALPamDataset::GetFileList();
469
470 for( i = 0; i < apoTileDS.size(); i++ )
471 papszFileList = CSLAddString( papszFileList,
472 apoTileDS[i]->GetDescription() );
473
474 if(NULL != papszMetadataFiles)
475 {
476 for( int i = 0; papszMetadataFiles[i] != NULL; i++ )
477 {
478 papszFileList = CSLAddString( papszFileList, papszMetadataFiles[i] );
479 }
480 }
481
482 return papszFileList;
483 }
484
485 /************************************************************************/
486 /* GDALRegister_TIL() */
487 /************************************************************************/
488
GDALRegister_TIL()489 void GDALRegister_TIL()
490
491 {
492 GDALDriver *poDriver;
493
494 if( GDALGetDriverByName( "TIL" ) == NULL )
495 {
496 poDriver = new GDALDriver();
497
498 poDriver->SetDescription( "TIL" );
499 poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" );
500 poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
501 "EarthWatch .TIL" );
502 poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
503 "frmt_til.html" );
504
505 poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
506
507 poDriver->pfnOpen = TILDataset::Open;
508 poDriver->pfnIdentify = TILDataset::Identify;
509
510 GetGDALDriverManager()->RegisterDriver( poDriver );
511 }
512 }
513
514
515