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