1 /*
2  *  keadataset.cpp
3  *
4  *  Created by Pete Bunting on 01/08/2012.
5  *  Copyright 2012 LibKEA. All rights reserved.
6  *
7  *  This file is part of LibKEA.
8  *
9  *  Permission is hereby granted, free of charge, to any person
10  *  obtaining a copy of this software and associated documentation
11  *  files (the "Software"), to deal in the Software without restriction,
12  *  including without limitation the rights to use, copy, modify,
13  *  merge, publish, distribute, sublicense, and/or sell copies of the
14  *  Software, and to permit persons to whom the Software is furnished
15  *  to do so, subject to the following conditions:
16  *
17  *  The above copyright notice and this permission notice shall be
18  *  included in all copies or substantial portions of the Software.
19  *
20  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21  *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22  *  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
23  *  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
24  *  ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
25  *  CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26  *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27  *
28  */
29 
30 #include "keadataset.h"
31 #include "keaband.h"
32 #include "keacopy.h"
33 #include "../frmts/hdf5/hdf5vfl.h"
34 
35 CPL_CVSID("$Id: keadataset.cpp fa752ad6eabafaf630a704e1892a9d837d683cb3 2021-03-06 17:04:38 +0100 Even Rouault $")
36 
37 /************************************************************************/
38 /*                     KEADatasetDriverUnload()                        */
39 /************************************************************************/
40 
KEADatasetDriverUnload(GDALDriver *)41 void KEADatasetDriverUnload(GDALDriver*)
42 {
43     HDF5VFLUnloadFileDriver();
44 }
45 
46 // Function for converting a libkea type into a GDAL type
KEA_to_GDAL_Type(kealib::KEADataType ekeaType)47 GDALDataType KEA_to_GDAL_Type( kealib::KEADataType ekeaType )
48 {
49     GDALDataType egdalType = GDT_Unknown;
50     switch( ekeaType )
51     {
52         case kealib::kea_8int:
53         case kealib::kea_8uint:
54             egdalType = GDT_Byte;
55             break;
56         case kealib::kea_16int:
57             egdalType = GDT_Int16;
58             break;
59         case kealib::kea_32int:
60             egdalType = GDT_Int32;
61             break;
62         case kealib::kea_16uint:
63             egdalType = GDT_UInt16;
64             break;
65         case kealib::kea_32uint:
66             egdalType = GDT_UInt32;
67             break;
68         case kealib::kea_32float:
69             egdalType = GDT_Float32;
70             break;
71         case kealib::kea_64float:
72             egdalType = GDT_Float64;
73             break;
74         default:
75             egdalType = GDT_Unknown;
76             break;
77     }
78     return egdalType;
79 }
80 
81 // function for converting a GDAL type to a kealib type
GDAL_to_KEA_Type(GDALDataType egdalType)82 kealib::KEADataType GDAL_to_KEA_Type( GDALDataType egdalType )
83 {
84     kealib::KEADataType ekeaType = kealib::kea_undefined;
85     switch( egdalType )
86     {
87         case GDT_Byte:
88             ekeaType = kealib::kea_8uint;
89             break;
90         case GDT_Int16:
91             ekeaType = kealib::kea_16int;
92             break;
93         case GDT_Int32:
94             ekeaType = kealib::kea_32int;
95             break;
96         case GDT_UInt16:
97             ekeaType = kealib::kea_16uint;
98             break;
99         case GDT_UInt32:
100             ekeaType = kealib::kea_32uint;
101             break;
102         case GDT_Float32:
103             ekeaType = kealib::kea_32float;
104             break;
105         case GDT_Float64:
106             ekeaType = kealib::kea_64float;
107             break;
108         default:
109             ekeaType = kealib::kea_undefined;
110             break;
111     }
112     return ekeaType;
113 }
114 
115 // static function - pointer set in driver
Open(GDALOpenInfo * poOpenInfo)116 GDALDataset *KEADataset::Open( GDALOpenInfo * poOpenInfo )
117 {
118     if( Identify( poOpenInfo ) )
119     {
120         try
121         {
122             // try and open it in the appropriate mode
123             H5::H5File *pH5File;
124             if( poOpenInfo->eAccess == GA_ReadOnly )
125             {
126                 // use the virtual driver so we can open files using
127                 // /vsicurl etc
128                 // do this same as libkea
129                 H5::FileAccPropList keaAccessPlist = H5::FileAccPropList(H5::FileAccPropList::DEFAULT);
130                 keaAccessPlist.setCache(kealib::KEA_MDC_NELMTS, kealib::KEA_RDCC_NELMTS,
131                             kealib::KEA_RDCC_NBYTES, kealib::KEA_RDCC_W0);
132                 keaAccessPlist.setSieveBufSize(kealib::KEA_SIEVE_BUF);
133                 hsize_t blockSize = kealib::KEA_META_BLOCKSIZE;
134                 keaAccessPlist.setMetaBlockSize(blockSize);
135                 // but set the driver
136                 keaAccessPlist.setDriver(HDF5VFLGetFileDriver(), nullptr);
137 
138                 const H5std_string keaImgFilePath(poOpenInfo->pszFilename);
139                 pH5File = new H5::H5File(keaImgFilePath, H5F_ACC_RDONLY, H5::FileCreatPropList::DEFAULT, keaAccessPlist);
140             }
141             else
142             {
143                 // Must be a local file
144                 pH5File = kealib::KEAImageIO::openKeaH5RW( poOpenInfo->pszFilename );
145             }
146             // create the KEADataset object
147             KEADataset *pDataset = new KEADataset( pH5File, poOpenInfo->eAccess );
148 
149             // set the description as the name
150             pDataset->SetDescription( poOpenInfo->pszFilename );
151 
152             return pDataset;
153         }
154         catch (const kealib::KEAIOException &)
155         {
156             // was a problem - can't be a valid file
157             return nullptr;
158         }
159     }
160     else
161     {
162         // not a KEA file
163         return nullptr;
164     }
165 }
166 
167 // static function- pointer set in driver
168 // this function is called in preference to Open
169 //
Identify(GDALOpenInfo * poOpenInfo)170 int KEADataset::Identify( GDALOpenInfo * poOpenInfo )
171 {
172 
173 /* -------------------------------------------------------------------- */
174 /*      Is it an HDF5 file?                                             */
175 /* -------------------------------------------------------------------- */
176     static const char achSignature[] = "\211HDF\r\n\032\n";
177 
178     if( poOpenInfo->pabyHeader == nullptr ||
179         memcmp(poOpenInfo->pabyHeader,achSignature,8) != 0 )
180     {
181         return 0;
182     }
183 
184     // avoid using kealib::KEAImageIO::isKEAImage as this is likely
185     // to be too slow over curl etc (and doesn't take a HDF5 file handle
186     // anyway).
187     // Just test the extension
188     CPLString osExt(CPLGetExtension(poOpenInfo->pszFilename));
189     if( EQUAL(osExt, "KEA") )
190         return 1;
191     else
192         return 0;
193 }
194 
195 // static function
CreateLL(const char * pszFilename,int nXSize,int nYSize,int nBands,GDALDataType eType,char ** papszParamList)196 H5::H5File *KEADataset::CreateLL( const char * pszFilename,
197                                   int nXSize, int nYSize, int nBands,
198                                   GDALDataType eType,
199                                   char ** papszParamList  )
200 {
201     GDALDriverH hDriver = GDALGetDriverByName( "KEA" );
202     if( ( hDriver == nullptr ) || !GDALValidateCreationOptions( hDriver, papszParamList ) )
203     {
204         CPLError( CE_Failure, CPLE_OpenFailed,
205                   "Attempt to create file `%s' failed. Invalid creation option(s)\n", pszFilename);
206         return nullptr;
207     }
208     // process any creation options in papszParamList
209     // default value
210     unsigned int nimageblockSize = kealib::KEA_IMAGE_CHUNK_SIZE;
211     // see if they have provided a different value
212     const char *pszValue = CSLFetchNameValue( papszParamList, "IMAGEBLOCKSIZE" );
213     if( pszValue != nullptr )
214         nimageblockSize = (unsigned int) atol( pszValue );
215 
216     unsigned int nattblockSize = kealib::KEA_ATT_CHUNK_SIZE;
217     pszValue = CSLFetchNameValue( papszParamList, "ATTBLOCKSIZE" );
218     if( pszValue != nullptr )
219         nattblockSize = (unsigned int) atol( pszValue );
220 
221     unsigned int nmdcElmts = kealib::KEA_MDC_NELMTS;
222     pszValue = CSLFetchNameValue( papszParamList, "MDC_NELMTS" );
223     if( pszValue != nullptr )
224         nmdcElmts = (unsigned int) atol( pszValue );
225 
226     hsize_t nrdccNElmts = kealib::KEA_RDCC_NELMTS;
227     pszValue = CSLFetchNameValue( papszParamList, "RDCC_NELMTS" );
228     if( pszValue != nullptr )
229         nrdccNElmts = (unsigned int) atol( pszValue );
230 
231     hsize_t nrdccNBytes = kealib::KEA_RDCC_NBYTES;
232     pszValue = CSLFetchNameValue( papszParamList, "RDCC_NBYTES" );
233     if( pszValue != nullptr )
234         nrdccNBytes = (unsigned int) atol( pszValue );
235 
236     double nrdccW0 = kealib::KEA_RDCC_W0;
237     pszValue = CSLFetchNameValue( papszParamList, "RDCC_W0" );
238     if( pszValue != nullptr )
239         nrdccW0 = CPLAtof( pszValue );
240 
241     hsize_t nsieveBuf = kealib::KEA_SIEVE_BUF;
242     pszValue = CSLFetchNameValue( papszParamList, "SIEVE_BUF" );
243     if( pszValue != nullptr )
244         nsieveBuf = (unsigned int) atol( pszValue );
245 
246     hsize_t nmetaBlockSize = kealib::KEA_META_BLOCKSIZE;
247     pszValue = CSLFetchNameValue( papszParamList, "META_BLOCKSIZE" );
248     if( pszValue != nullptr )
249         nmetaBlockSize = (unsigned int) atol( pszValue );
250 
251     unsigned int ndeflate = kealib::KEA_DEFLATE;
252     pszValue = CSLFetchNameValue( papszParamList, "DEFLATE" );
253     if( pszValue != nullptr )
254         ndeflate = (unsigned int) atol( pszValue );
255 
256     kealib::KEADataType keaDataType = GDAL_to_KEA_Type( eType );
257     if( nBands > 0 && keaDataType == kealib::kea_undefined )
258     {
259         CPLError( CE_Failure, CPLE_NotSupported,
260                   "Data type %s not supported in KEA",
261                   GDALGetDataTypeName(eType) );
262         return nullptr;
263     }
264 
265     try
266     {
267         // now create it
268         H5::H5File *keaImgH5File = kealib::KEAImageIO::createKEAImage( pszFilename,
269                                                     keaDataType,
270                                                     nXSize, nYSize, nBands,
271                                                     nullptr, nullptr, nimageblockSize,
272                                                     nattblockSize, nmdcElmts, nrdccNElmts,
273                                                     nrdccNBytes, nrdccW0, nsieveBuf,
274                                                     nmetaBlockSize, ndeflate );
275         return keaImgH5File;
276     }
277     catch (kealib::KEAIOException &e)
278     {
279         CPLError( CE_Failure, CPLE_OpenFailed,
280                   "Attempt to create file `%s' failed. Error: %s\n",
281                   pszFilename, e.what() );
282         return nullptr;
283     }
284 }
285 
286 // static function- pointer set in driver
Create(const char * pszFilename,int nXSize,int nYSize,int nBands,GDALDataType eType,char ** papszParamList)287 GDALDataset *KEADataset::Create( const char * pszFilename,
288                                   int nXSize, int nYSize, int nBands,
289                                   GDALDataType eType,
290                                   char ** papszParamList  )
291 {
292     H5::H5File *keaImgH5File = CreateLL( pszFilename, nXSize, nYSize, nBands,
293                                          eType, papszParamList  );
294     if( keaImgH5File == nullptr )
295         return nullptr;
296 
297     bool bThematic =
298         CPLTestBool(CSLFetchNameValueDef( papszParamList, "THEMATIC", "FALSE" ));
299 
300     try
301     {
302         // create our dataset object
303         KEADataset *pDataset = new KEADataset( keaImgH5File, GA_Update );
304 
305         pDataset->SetDescription( pszFilename );
306 
307         // set all to thematic if asked
308         if( bThematic )
309         {
310             for( int nCount = 0; nCount < nBands; nCount++ )
311             {
312                 GDALRasterBand *pBand = pDataset->GetRasterBand(nCount+1);
313                 pBand->SetMetadataItem("LAYER_TYPE", "thematic");
314             }
315         }
316 
317         return pDataset;
318     }
319     catch (kealib::KEAIOException &e)
320     {
321         CPLError( CE_Failure, CPLE_OpenFailed,
322                   "Attempt to create file `%s' failed. Error: %s\n",
323                   pszFilename, e.what() );
324         return nullptr;
325     }
326 }
327 
CreateCopy(const char * pszFilename,GDALDataset * pSrcDs,CPL_UNUSED int bStrict,char ** papszParamList,GDALProgressFunc pfnProgress,void * pProgressData)328 GDALDataset *KEADataset::CreateCopy( const char * pszFilename, GDALDataset *pSrcDs,
329                                 CPL_UNUSED int bStrict, char **  papszParamList,
330                                 GDALProgressFunc pfnProgress, void *pProgressData )
331 {
332     // get the data out of the input dataset
333     int nXSize = pSrcDs->GetRasterXSize();
334     int nYSize = pSrcDs->GetRasterYSize();
335     int nBands = pSrcDs->GetRasterCount();
336 
337     GDALDataType eType = (nBands == 0) ? GDT_Unknown : pSrcDs->GetRasterBand(1)->GetRasterDataType();
338     H5::H5File *keaImgH5File = CreateLL( pszFilename, nXSize, nYSize, nBands,
339                                          eType, papszParamList  );
340     if( keaImgH5File == nullptr )
341         return nullptr;
342 
343     bool bThematic =
344         CPLTestBool(CSLFetchNameValueDef( papszParamList, "THEMATIC", "FALSE" ));
345 
346     try
347     {
348         // create the imageio
349         kealib::KEAImageIO *pImageIO = new kealib::KEAImageIO();
350 
351         // open the file
352         pImageIO->openKEAImageHeader( keaImgH5File );
353 
354         // copy file
355         if( !KEACopyFile( pSrcDs, pImageIO, pfnProgress, pProgressData) )
356         {
357             delete pImageIO;
358             return nullptr;
359         }
360 
361         // close it
362         try
363         {
364             pImageIO->close();
365         }
366         catch (const kealib::KEAIOException &)
367         {
368         }
369         delete pImageIO;
370 
371         // now open it again - because the constructor loads all the info
372         // in we need to copy the data first....
373         keaImgH5File = kealib::KEAImageIO::openKeaH5RW( pszFilename );
374 
375         // and wrap it in a dataset
376         KEADataset *pDataset = new KEADataset( keaImgH5File, GA_Update );
377         pDataset->SetDescription( pszFilename );
378 
379         // set all to thematic if asked - overrides whatever set by CopyFile
380         if( bThematic )
381         {
382             for( int nCount = 0; nCount < nBands; nCount++ )
383             {
384                 GDALRasterBand *pBand = pDataset->GetRasterBand(nCount+1);
385                 pBand->SetMetadataItem("LAYER_TYPE", "thematic");
386             }
387         }
388 
389         for( int nCount = 0; nCount < nBands; nCount++ )
390         {
391             pDataset->GetRasterBand(nCount+1)->SetColorInterpretation(
392                 pSrcDs->GetRasterBand(nCount+1)->GetColorInterpretation());
393         }
394 
395         // KEA has no concept of per-dataset mask band for now.
396         for( int nCount = 0; nCount < nBands; nCount++ )
397         {
398             if( pSrcDs->GetRasterBand(nCount+1)->GetMaskFlags() == 0 ) // Per-band mask
399             {
400                 pDataset->GetRasterBand(nCount+1)->CreateMaskBand(0);
401                 if( GDALRasterBandCopyWholeRaster(
402                     (GDALRasterBandH)pSrcDs->GetRasterBand(nCount+1)->GetMaskBand(),
403                     (GDALRasterBandH)pDataset->GetRasterBand(nCount+1)->GetMaskBand(),
404                     nullptr, nullptr, nullptr) != CE_None )
405                 {
406                     delete pDataset;
407                     return nullptr;
408                 }
409             }
410         }
411 
412         return pDataset;
413     }
414     catch (kealib::KEAException &e)
415     {
416         CPLError( CE_Failure, CPLE_OpenFailed,
417                   "Attempt to create file `%s' failed. Error: %s\n",
418                   pszFilename, e.what() );
419         return nullptr;
420     }
421 }
422 
423 // constructor
KEADataset(H5::H5File * keaImgH5File,GDALAccess eAccessIn)424 KEADataset::KEADataset( H5::H5File *keaImgH5File, GDALAccess eAccessIn )
425 {
426     this->m_hMutex = CPLCreateMutex();
427     CPLReleaseMutex( this->m_hMutex );
428     try
429     {
430         // Create the image IO and initialize the refcount.
431         m_pImageIO = new kealib::KEAImageIO();
432         m_pRefcount = new LockedRefCount();
433 
434         // NULL until we read them in.
435         m_papszMetadataList = nullptr;
436         m_pGCPs = nullptr;
437         m_pszGCPProjection = nullptr;
438 
439         // open the file
440         m_pImageIO->openKEAImageHeader( keaImgH5File );
441         kealib::KEAImageSpatialInfo *pSpatialInfo = m_pImageIO->getSpatialInfo();
442 
443         // get the dimensions
444         this->nBands = m_pImageIO->getNumOfImageBands();
445         this->nRasterXSize = static_cast<int>(pSpatialInfo->xSize);
446         this->nRasterYSize = static_cast<int>(pSpatialInfo->ySize);
447         this->eAccess = eAccessIn;
448 
449         // create all the bands
450         for( int nCount = 0; nCount < nBands; nCount++ )
451         {
452             // Note: GDAL uses indices starting at 1 and so does kealib.
453             // Create band object.
454             KEARasterBand *pBand = new KEARasterBand(
455                 this, nCount + 1, eAccess, m_pImageIO, m_pRefcount );
456             // read in overviews
457             pBand->readExistingOverviews();
458             // set the band into this dataset
459             this->SetBand( nCount + 1, pBand );
460         }
461 
462         // read in the metadata
463         this->UpdateMetadataList();
464     }
465     catch (const kealib::KEAIOException &e)
466     {
467         // ignore?
468         CPLError( CE_Warning, CPLE_AppDefined,
469                 "Caught exception in KEADataset constructor %s", e.what() );
470     }
471 }
472 
~KEADataset()473 KEADataset::~KEADataset()
474 {
475     {
476         CPLMutexHolderD( &m_hMutex );
477         // destroy the metadata
478         CSLDestroy(m_papszMetadataList);
479         this->DestroyGCPs();
480         CPLFree( m_pszGCPProjection );
481     }
482     if( m_pRefcount->DecRef() )
483     {
484         try
485         {
486             m_pImageIO->close();
487         }
488         catch (const kealib::KEAIOException &)
489         {
490         }
491         delete m_pImageIO;
492         delete m_pRefcount;
493     }
494 
495     CPLDestroyMutex( m_hMutex );
496     m_hMutex = nullptr;
497 }
498 
499 // read in the metadata into our CSLStringList
UpdateMetadataList()500 void KEADataset::UpdateMetadataList()
501 {
502     CPLMutexHolderD( &m_hMutex );
503     std::vector< std::pair<std::string, std::string> > odata;
504     // get all the metadata
505     odata = this->m_pImageIO->getImageMetaData();
506     for(std::vector< std::pair<std::string, std::string> >::iterator iterMetaData = odata.begin(); iterMetaData != odata.end(); ++iterMetaData)
507     {
508         m_papszMetadataList = CSLSetNameValue(m_papszMetadataList, iterMetaData->first.c_str(), iterMetaData->second.c_str());
509     }
510 }
511 
512 // read in the geotransform
GetGeoTransform(double * padfTransform)513 CPLErr KEADataset::GetGeoTransform( double * padfTransform )
514 {
515     try
516     {
517         kealib::KEAImageSpatialInfo *pSpatialInfo = m_pImageIO->getSpatialInfo();
518         // GDAL uses an array format
519         padfTransform[0] = pSpatialInfo->tlX;
520         padfTransform[1] = pSpatialInfo->xRes;
521         padfTransform[2] = pSpatialInfo->xRot;
522         padfTransform[3] = pSpatialInfo->tlY;
523         padfTransform[4] = pSpatialInfo->yRot;
524         padfTransform[5] = pSpatialInfo->yRes;
525 
526         return CE_None;
527     }
528     catch (const kealib::KEAIOException &e)
529     {
530         CPLError( CE_Warning, CPLE_AppDefined,
531                 "Unable to read geotransform: %s", e.what() );
532         return CE_Failure;
533     }
534 }
535 
536 // read in the projection ref
_GetProjectionRef()537 const char *KEADataset::_GetProjectionRef()
538 {
539     try
540     {
541         kealib::KEAImageSpatialInfo *pSpatialInfo = m_pImageIO->getSpatialInfo();
542         // should be safe since pSpatialInfo should be around a while...
543         return pSpatialInfo->wktString.c_str();
544     }
545     catch (const kealib::KEAIOException &)
546     {
547         return nullptr;
548     }
549 }
550 
551 // set the geotransform
SetGeoTransform(double * padfTransform)552 CPLErr KEADataset::SetGeoTransform (double *padfTransform )
553 {
554     try
555     {
556         // get the spatial info and update it
557         kealib::KEAImageSpatialInfo *pSpatialInfo = m_pImageIO->getSpatialInfo();
558         // convert back from GDAL's array format
559         pSpatialInfo->tlX = padfTransform[0];
560         pSpatialInfo->xRes = padfTransform[1];
561         pSpatialInfo->xRot = padfTransform[2];
562         pSpatialInfo->tlY = padfTransform[3];
563         pSpatialInfo->yRot = padfTransform[4];
564         pSpatialInfo->yRes = padfTransform[5];
565 
566         m_pImageIO->setSpatialInfo( pSpatialInfo );
567         return CE_None;
568     }
569     catch (const kealib::KEAIOException &e)
570     {
571         CPLError( CE_Warning, CPLE_AppDefined,
572                 "Unable to write geotransform: %s", e.what() );
573         return CE_Failure;
574     }
575 }
576 
577 // set the projection
_SetProjection(const char * pszWKT)578 CPLErr KEADataset::_SetProjection( const char *pszWKT )
579 {
580     try
581     {
582         // get the spatial info and update it
583         kealib::KEAImageSpatialInfo *pSpatialInfo = m_pImageIO->getSpatialInfo();
584 
585         pSpatialInfo->wktString = pszWKT;
586 
587         m_pImageIO->setSpatialInfo( pSpatialInfo );
588         return CE_None;
589     }
590     catch (const kealib::KEAIOException &e)
591     {
592         CPLError( CE_Warning, CPLE_AppDefined,
593                 "Unable to write projection: %s", e.what() );
594         return CE_Failure;
595     }
596 }
597 
598 // Thought this might be handy to pass back to the application
GetInternalHandle(const char *)599 void * KEADataset::GetInternalHandle(const char *)
600 {
601     return m_pImageIO;
602 }
603 
604 // this is called by GDALDataset::BuildOverviews. we implement this function to support
605 // building of overviews
IBuildOverviews(const char * pszResampling,int nOverviews,int * panOverviewList,int nListBands,int * panBandList,GDALProgressFunc pfnProgress,void * pProgressData)606 CPLErr KEADataset::IBuildOverviews(const char *pszResampling, int nOverviews, int *panOverviewList,
607                                     int nListBands, int *panBandList, GDALProgressFunc pfnProgress,
608                                     void *pProgressData)
609 {
610     // go through the list of bands that have been passed in
611     int nCurrentBand, nOK = 1;
612     for( int nBandCount = 0; (nBandCount < nListBands) && nOK; nBandCount++ )
613     {
614         // get the band number
615         nCurrentBand = panBandList[nBandCount];
616         // get the band
617         KEARasterBand *pBand = (KEARasterBand*)this->GetRasterBand(nCurrentBand);
618         // create the overview object
619         pBand->CreateOverviews( nOverviews, panOverviewList );
620 
621         // get GDAL to do the hard work. It will calculate the overviews and write them
622         // back into the objects
623         if( GDALRegenerateOverviews( (GDALRasterBandH)pBand, nOverviews, (GDALRasterBandH*)pBand->GetOverviewList(),
624                                     pszResampling, pfnProgress, pProgressData ) != CE_None )
625         {
626             nOK = 0;
627         }
628     }
629     if( !nOK )
630     {
631         return CE_Failure;
632     }
633     else
634     {
635         return CE_None;
636     }
637 }
638 
639 // set a single metadata item
SetMetadataItem(const char * pszName,const char * pszValue,const char * pszDomain)640 CPLErr KEADataset::SetMetadataItem(const char *pszName, const char *pszValue, const char *pszDomain)
641 {
642     CPLMutexHolderD( &m_hMutex );
643     // only deal with 'default' domain - no geolocation etc
644     if( ( pszDomain != nullptr ) && ( *pszDomain != '\0' ) )
645         return CE_Failure;
646 
647     try
648     {
649         this->m_pImageIO->setImageMetaData(pszName, pszValue );
650         // CSLSetNameValue will update if already there
651         m_papszMetadataList = CSLSetNameValue( m_papszMetadataList, pszName, pszValue );
652         return CE_None;
653     }
654     catch (const kealib::KEAIOException &e)
655     {
656         CPLError( CE_Failure, CPLE_AppDefined,
657                 "Unable to write metadata: %s", e.what() );
658         return CE_Failure;
659     }
660 }
661 
662 // get a single metadata item
GetMetadataItem(const char * pszName,const char * pszDomain)663 const char *KEADataset::GetMetadataItem (const char *pszName, const char *pszDomain)
664 {
665     CPLMutexHolderD( &m_hMutex );
666     // only deal with 'default' domain - no geolocation etc
667     if( ( pszDomain != nullptr ) && ( *pszDomain != '\0' ) )
668         return nullptr;
669     // string returned from CSLFetchNameValue should be persistent
670     return CSLFetchNameValue(m_papszMetadataList, pszName);
671 }
672 
673 // get the whole metadata as CSLStringList - note may be thread safety issues
GetMetadata(const char * pszDomain)674 char **KEADataset::GetMetadata(const char *pszDomain)
675 {
676     // only deal with 'default' domain - no geolocation etc
677     if( ( pszDomain != nullptr ) && ( *pszDomain != '\0' ) )
678         return nullptr;
679     // this is what we store it as anyway
680     return m_papszMetadataList;
681 }
682 
683 // set the whole metadata as a CSLStringList
SetMetadata(char ** papszMetadata,const char * pszDomain)684 CPLErr KEADataset::SetMetadata(char **papszMetadata, const char *pszDomain)
685 {
686     CPLMutexHolderD( &m_hMutex );
687     // only deal with 'default' domain - no geolocation etc
688     if( ( pszDomain != nullptr ) && ( *pszDomain != '\0' ) )
689         return CE_Failure;
690 
691     int nIndex = 0;
692     try
693     {
694         // go through each item
695         while( papszMetadata[nIndex] != nullptr )
696         {
697             // get the value/name
698             char *pszName = nullptr;
699             const char *pszValue =
700                 CPLParseNameValue( papszMetadata[nIndex], &pszName );
701             if( pszValue == nullptr )
702                 pszValue = "";
703             if( pszName != nullptr )
704             {
705                 // set it with imageio
706                 this->m_pImageIO->setImageMetaData(pszName, pszValue );
707                 CPLFree(pszName);
708             }
709             nIndex++;
710         }
711     }
712     catch (const kealib::KEAIOException &e)
713     {
714         CPLError( CE_Failure, CPLE_AppDefined,
715                 "Unable to write metadata: %s", e.what() );
716         return CE_Failure;
717     }
718 
719     // destroy our one and replace it
720     CSLDestroy(m_papszMetadataList);
721     m_papszMetadataList = CSLDuplicate(papszMetadata);
722     return CE_None;
723 }
724 
AddBand(GDALDataType eType,char ** papszOptions)725 CPLErr KEADataset::AddBand(GDALDataType eType, char **papszOptions)
726 {
727     // process any creation options in papszOptions
728     unsigned int nimageBlockSize = kealib::KEA_IMAGE_CHUNK_SIZE;
729     unsigned int nattBlockSize = kealib::KEA_ATT_CHUNK_SIZE;
730     unsigned int ndeflate = kealib::KEA_DEFLATE;
731     if (papszOptions != nullptr) {
732         const char *pszValue = CSLFetchNameValue(papszOptions,"IMAGEBLOCKSIZE");
733         if ( pszValue != nullptr ) {
734             nimageBlockSize = atoi(pszValue);
735         }
736 
737         pszValue = CSLFetchNameValue(papszOptions, "ATTBLOCKSIZE");
738         if (pszValue != nullptr) {
739             nattBlockSize = atoi(pszValue);
740         }
741 
742         pszValue = CSLFetchNameValue(papszOptions, "DEFLATE");
743         if (pszValue != nullptr) {
744             ndeflate = atoi(pszValue);
745         }
746     }
747 
748     kealib::KEADataType keaDataType = GDAL_to_KEA_Type( eType );
749     if( keaDataType == kealib::kea_undefined )
750     {
751         CPLError( CE_Failure, CPLE_NotSupported,
752                   "Data type %s not supported in KEA",
753                   GDALGetDataTypeName(eType) );
754         return CE_Failure;
755     }
756 
757     try {
758         m_pImageIO->addImageBand(keaDataType, "", nimageBlockSize,
759                 nattBlockSize, ndeflate);
760     } catch (const kealib::KEAIOException &e) {
761         CPLError( CE_Failure, CPLE_AppDefined,
762                 "Unable to create band: %s", e.what() );
763         return CE_Failure;
764     }
765 
766     // create a new band and add it to the dataset
767     // note GDAL uses indices starting at 1 and so does kealib
768     KEARasterBand *pBand = new KEARasterBand(this, this->nBands+1, this->eAccess,
769             m_pImageIO, m_pRefcount);
770     this->SetBand(this->nBands+1, pBand);
771 
772     return CE_None;
773 }
774 
GetGCPCount()775 int KEADataset::GetGCPCount()
776 {
777     try
778     {
779         return m_pImageIO->getGCPCount();
780     }
781     catch (const kealib::KEAIOException &)
782     {
783         return 0;
784     }
785 }
786 
_GetGCPProjection()787 const char* KEADataset::_GetGCPProjection()
788 {
789     CPLMutexHolderD( &m_hMutex );
790     if( m_pszGCPProjection == nullptr )
791     {
792         try
793         {
794             std::string sProj = m_pImageIO->getGCPProjection();
795             m_pszGCPProjection = CPLStrdup( sProj.c_str() );
796         }
797         catch (const kealib::KEAIOException &)
798         {
799             return nullptr;
800         }
801     }
802     return m_pszGCPProjection;
803 }
804 
GetGCPs()805 const GDAL_GCP* KEADataset::GetGCPs()
806 {
807     CPLMutexHolderD( &m_hMutex );
808     if( m_pGCPs == nullptr )
809     {
810         // convert to GDAL data structures
811         try
812         {
813             unsigned int nCount = m_pImageIO->getGCPCount();
814             std::vector<kealib::KEAImageGCP*> *pKEAGCPs = m_pImageIO->getGCPs();
815 
816             m_pGCPs = (GDAL_GCP*)CPLCalloc(nCount, sizeof(GDAL_GCP));
817             for( unsigned int nIndex = 0; nIndex < nCount; nIndex++)
818             {
819                 GDAL_GCP *pGCP = &m_pGCPs[nIndex];
820                 kealib::KEAImageGCP *pKEAGCP = pKEAGCPs->at(nIndex);
821                 pGCP->pszId = CPLStrdup( pKEAGCP->pszId.c_str() );
822                 pGCP->pszInfo = CPLStrdup( pKEAGCP->pszInfo.c_str() );
823                 pGCP->dfGCPPixel = pKEAGCP->dfGCPPixel;
824                 pGCP->dfGCPLine = pKEAGCP->dfGCPLine;
825                 pGCP->dfGCPX = pKEAGCP->dfGCPX;
826                 pGCP->dfGCPY = pKEAGCP->dfGCPY;
827                 pGCP->dfGCPZ = pKEAGCP->dfGCPZ;
828 
829                 delete pKEAGCP;
830             }
831 
832             delete pKEAGCPs;
833         }
834         catch (const kealib::KEAIOException &)
835         {
836             return nullptr;
837         }
838     }
839     return m_pGCPs;
840 }
841 
_SetGCPs(int nGCPCount,const GDAL_GCP * pasGCPList,const char * pszGCPProjection)842 CPLErr KEADataset::_SetGCPs(int nGCPCount, const GDAL_GCP *pasGCPList, const char *pszGCPProjection)
843 {
844     CPLMutexHolderD( &m_hMutex );
845     this->DestroyGCPs();
846     CPLFree( m_pszGCPProjection );
847     m_pszGCPProjection = nullptr;
848     CPLErr result = CE_None;
849 
850     std::vector<kealib::KEAImageGCP*> *pKEAGCPs = new std::vector<kealib::KEAImageGCP*>(nGCPCount);
851     for( int nIndex = 0; nIndex < nGCPCount; nIndex++ )
852     {
853         const GDAL_GCP *pGCP = &pasGCPList[nIndex];
854         kealib::KEAImageGCP *pKEA = new kealib::KEAImageGCP;
855         pKEA->pszId = pGCP->pszId;
856         pKEA->pszInfo = pGCP->pszInfo;
857         pKEA->dfGCPPixel = pGCP->dfGCPPixel;
858         pKEA->dfGCPLine = pGCP->dfGCPLine;
859         pKEA->dfGCPX = pGCP->dfGCPX;
860         pKEA->dfGCPY = pGCP->dfGCPY;
861         pKEA->dfGCPZ = pGCP->dfGCPZ;
862         pKEAGCPs->at(nIndex) = pKEA;
863     }
864     try
865     {
866         m_pImageIO->setGCPs(pKEAGCPs, pszGCPProjection);
867     }
868     catch (const kealib::KEAIOException &e)
869     {
870         CPLError( CE_Warning, CPLE_AppDefined,
871                 "Unable to write GCPs: %s", e.what() );
872         result = CE_Failure;
873     }
874 
875     for( std::vector<kealib::KEAImageGCP*>::iterator itr = pKEAGCPs->begin(); itr != pKEAGCPs->end(); ++itr)
876     {
877         kealib::KEAImageGCP *pKEA = (*itr);
878         delete pKEA;
879     }
880     delete pKEAGCPs;
881 
882     return result;
883 }
884 
DestroyGCPs()885 void KEADataset::DestroyGCPs()
886 {
887     CPLMutexHolderD( &m_hMutex );
888     if( m_pGCPs != nullptr )
889     {
890         // we assume this is always the same as the internal list...
891         int nCount = this->GetGCPCount();
892         for( int nIndex = 0; nIndex < nCount; nIndex++ )
893         {
894             GDAL_GCP *pGCP = &m_pGCPs[nIndex];
895             CPLFree( pGCP->pszId );
896             CPLFree( pGCP->pszInfo );
897         }
898         CPLFree( m_pGCPs );
899         m_pGCPs = nullptr;
900     }
901 }
902