1 /*****************************************************************************
2  *
3  * Project:  Intergraph Raster Format support
4  * Purpose:  Read/Write Intergraph Raster Format, dataset support
5  * Author:   Ivan Lucena, [lucena_ivan at hotmail.com]
6  *
7  ******************************************************************************
8  * Copyright (c) 2007, Ivan Lucena
9  * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com>
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 "cpl_conv.h"
31 #include "cpl_csv.h"
32 #include "cpl_string.h"
33 #include "gdal_frmts.h"
34 #include "gdal_alg.h"
35 #include "gdal_pam.h"
36 #include "gdal_priv.h"
37 #include "ogr_spatialref.h"
38 
39 #include "IntergraphDataset.h"
40 #include "IntergraphBand.h"
41 #include "IngrTypes.h"
42 
43 CPL_CVSID("$Id: IntergraphDataset.cpp 452b07d9aa72be1d260abacca8a95367d32abc48 2021-03-08 17:45:34 +0100 Even Rouault $")
44 
45 //  ----------------------------------------------------------------------------
46 //                                        IntergraphDataset::IntergraphDataset()
47 //  ----------------------------------------------------------------------------
48 
IntergraphDataset()49 IntergraphDataset::IntergraphDataset() :
50     fp(nullptr),
51     pszFilename(nullptr)
52 {
53     adfGeoTransform[0] = 0.0;
54     adfGeoTransform[1] = 1.0;
55     adfGeoTransform[2] = 0.0;
56     adfGeoTransform[3] = 0.0;
57     adfGeoTransform[4] = 0.0;
58     adfGeoTransform[5] = 1.0;
59 
60     hVirtual.poDS = nullptr;
61     hVirtual.poBand = nullptr;
62     hVirtual.pszFileName = nullptr;
63 
64     memset(&hHeaderOne, 0, sizeof(hHeaderOne));
65     memset(&hHeaderTwo, 0, sizeof(hHeaderTwo));
66 }
67 
68 //  ----------------------------------------------------------------------------
69 //                                       IntergraphDataset::~IntergraphDataset()
70 //  ----------------------------------------------------------------------------
71 
~IntergraphDataset()72 IntergraphDataset::~IntergraphDataset()
73 {
74     FlushCache();
75 
76     CPLFree( pszFilename );
77 
78     if( fp != nullptr )
79     {
80         VSIFCloseL( fp );
81     }
82 }
83 
84 //  ----------------------------------------------------------------------------
85 //                                                     IntergraphDataset::Open()
86 //  ----------------------------------------------------------------------------
87 
Open(GDALOpenInfo * poOpenInfo)88 GDALDataset *IntergraphDataset::Open( GDALOpenInfo *poOpenInfo )
89 {
90     if( poOpenInfo->nHeaderBytes < 1024 || poOpenInfo->fpL == nullptr )
91     {
92         return nullptr;
93     }
94 
95     // --------------------------------------------------------------------
96     // Assign Header Information
97     // --------------------------------------------------------------------
98 
99     INGR_HeaderOne hHeaderOne;
100 
101     INGR_HeaderOneDiskToMem( &hHeaderOne, (GByte*) poOpenInfo->pabyHeader);
102 
103     // --------------------------------------------------------------------
104     // Check Header Type (HTC) Version
105     // --------------------------------------------------------------------
106 
107     if( hHeaderOne.HeaderType.Version != INGR_HEADER_VERSION )
108     {
109         return nullptr;
110     }
111 
112     // --------------------------------------------------------------------
113     // Check Header Type (HTC) 2D / 3D Flag
114     // --------------------------------------------------------------------
115 
116     if( ( hHeaderOne.HeaderType.Is2Dor3D != INGR_HEADER_2D ) &&
117         ( hHeaderOne.HeaderType.Is2Dor3D != INGR_HEADER_3D ) )
118     {
119         return nullptr;
120     }
121 
122     // --------------------------------------------------------------------
123     // Check Header Type (HTC) Type Flag
124     // --------------------------------------------------------------------
125 
126     if( hHeaderOne.HeaderType.Type != INGR_HEADER_TYPE )
127     {
128         return nullptr;
129     }
130 
131     // --------------------------------------------------------------------
132     // Check Grid File Version (VER)
133     // --------------------------------------------------------------------
134 
135     if( hHeaderOne.GridFileVersion != 1 &&
136         hHeaderOne.GridFileVersion != 2 &&
137         hHeaderOne.GridFileVersion != 3 )
138     {
139         return nullptr;
140     }
141 
142     // --------------------------------------------------------------------
143     // Check Words To Follow (WTC) Minimum Value
144     // --------------------------------------------------------------------
145 
146     if( hHeaderOne.WordsToFollow < 254 )
147     {
148         return nullptr;
149     }
150 
151     // --------------------------------------------------------------------
152     // Check Words To Follow (WTC) Integrity
153     // --------------------------------------------------------------------
154 
155     float fHeaderBlocks = (float) ( hHeaderOne.WordsToFollow + 2 ) / 256;
156 
157     if( ( fHeaderBlocks - (int) fHeaderBlocks ) != 0.0 )
158     {
159         return nullptr;
160     }
161 
162     if( !GDALIsDriverDeprecatedForGDAL35StillEnabled("INGR") )
163         return nullptr;
164 
165     // --------------------------------------------------------------------
166     // Get Data Type Code (DTC) => Format Type
167     // --------------------------------------------------------------------
168 
169     int eFormatUntyped = hHeaderOne.DataTypeCode;
170 
171     // --------------------------------------------------------------------
172     // We need to scan around the file, so we open it now.
173     // --------------------------------------------------------------------
174 
175     VSILFILE *fp = poOpenInfo->fpL;
176     poOpenInfo->fpL = nullptr;
177 
178     // --------------------------------------------------------------------
179     // Get Format Type from the tile directory
180     // --------------------------------------------------------------------
181 
182     if( eFormatUntyped == TiledRasterData )
183     {
184         INGR_TileHeader hTileDir;
185 
186         int nOffset = 2 + ( 2 * ( hHeaderOne.WordsToFollow + 1 ) );
187 
188         GByte abyBuffer[SIZEOF_TDIR];
189 
190         if( (VSIFSeekL( fp, nOffset, SEEK_SET ) == -1 )  ||
191             (VSIFReadL( abyBuffer, 1, SIZEOF_TDIR, fp ) == 0) )
192         {
193             VSIFCloseL( fp );
194             CPLError( CE_Failure, CPLE_AppDefined,
195                 "Error reading tiles header" );
196             return nullptr;
197         }
198 
199         INGR_TileHeaderDiskToMem( &hTileDir, abyBuffer );
200 
201         if( !
202           ( hTileDir.ApplicationType     == 1 &&
203             hTileDir.SubTypeCode         == 7 &&
204             //( hTileDir.WordsToFollow % 4 ) == 0 &&
205             hTileDir.PacketVersion       == 1 &&
206             hTileDir.Identifier          == 1 ) )
207         {
208             CPLError( CE_Failure, CPLE_AppDefined,
209                 "Cannot recognize tiles header info");
210             VSIFCloseL( fp );
211             return nullptr;
212         }
213 
214         eFormatUntyped = hTileDir.DataTypeCode;
215     }
216 
217     // --------------------------------------------------------------------
218     // Check Scannable Flag
219     // --------------------------------------------------------------------
220 /*
221     if (hHeaderOne.ScannableFlag == HasLineHeader)
222     {
223         CPLError( CE_Failure, CPLE_AppDefined,
224             "Intergraph Raster Scannable Line Header not supported yet" );
225         VSIFCloseL( fp );
226         return NULL;
227     }
228 */
229     // --------------------------------------------------------------------
230     // Check supported Format Type
231     // --------------------------------------------------------------------
232 
233     if( eFormatUntyped != ByteInteger &&
234         eFormatUntyped != WordIntegers &&
235         eFormatUntyped != Integers32Bit &&
236         eFormatUntyped != FloatingPoint32Bit &&
237         eFormatUntyped != FloatingPoint64Bit &&
238         eFormatUntyped != RunLengthEncoded &&
239         eFormatUntyped != RunLengthEncodedC &&
240         eFormatUntyped != CCITTGroup4 &&
241         eFormatUntyped != AdaptiveRGB &&
242         eFormatUntyped != Uncompressed24bit &&
243         eFormatUntyped != AdaptiveGrayScale &&
244         eFormatUntyped != ContinuousTone &&
245         eFormatUntyped != JPEGGRAY &&
246         eFormatUntyped != JPEGRGB &&
247         eFormatUntyped != JPEGCMYK )
248     {
249         CPLError( CE_Failure, CPLE_AppDefined,
250             "Intergraph Raster Format %d not supported",
251             eFormatUntyped );
252         VSIFCloseL( fp );
253         return nullptr;
254     }
255 
256     // -----------------------------------------------------------------
257     // Create a corresponding GDALDataset
258     // -----------------------------------------------------------------
259 
260     IntergraphDataset *poDS = new IntergraphDataset();
261     poDS->eAccess = poOpenInfo->eAccess;
262     poDS->pszFilename = CPLStrdup( poOpenInfo->pszFilename );
263     poDS->fp = fp;
264 
265     // --------------------------------------------------------------------
266     // Get X Size from Pixels Per Line (PPL)
267     // --------------------------------------------------------------------
268 
269     poDS->nRasterXSize = hHeaderOne.PixelsPerLine;
270 
271     // --------------------------------------------------------------------
272     // Get Y Size from Number of Lines (NOL)
273     // --------------------------------------------------------------------
274 
275     poDS->nRasterYSize = hHeaderOne.NumberOfLines;
276 
277     if (poDS->nRasterXSize <= 0 || poDS->nRasterYSize <= 0)
278     {
279         CPLError( CE_Failure, CPLE_AppDefined,
280                   "Invalid dimensions : %d x %d",
281                   poDS->nRasterXSize, poDS->nRasterYSize);
282         delete poDS;
283         return nullptr;
284     }
285 
286     // --------------------------------------------------------------------
287     // Get Geo Transformation from Homogeneous Transformation Matrix (TRN)
288     // --------------------------------------------------------------------
289 
290     INGR_GetTransMatrix( &hHeaderOne, poDS->adfGeoTransform );
291 
292     // --------------------------------------------------------------------
293     // Set Metadata Information
294     // --------------------------------------------------------------------
295 
296     poDS->SetMetadataItem( "VERSION",
297         CPLSPrintf ( "%d", hHeaderOne.GridFileVersion ), "IMAGE_STRUCTURE" );
298     poDS->SetMetadataItem( "RESOLUTION",
299         CPLSPrintf ( "%d", (hHeaderOne.DeviceResolution < 0)?-hHeaderOne.DeviceResolution:1) );
300 
301     // --------------------------------------------------------------------
302     // Create Band Information
303     // --------------------------------------------------------------------
304 
305     int nBands = 0;
306     int nBandOffset = 0;
307 
308     GByte abyBuf[MAX(SIZEOF_HDR1,SIZEOF_HDR2_A)];
309 
310     do
311     {
312         VSIFSeekL( poDS->fp, nBandOffset, SEEK_SET );
313 
314         if( VSIFReadL( abyBuf, 1, SIZEOF_HDR1, poDS->fp ) != SIZEOF_HDR1 )
315             break;
316 
317         INGR_HeaderOneDiskToMem( &poDS->hHeaderOne, abyBuf );
318         if( hHeaderOne.PixelsPerLine != poDS->hHeaderOne.PixelsPerLine ||
319             hHeaderOne.NumberOfLines != poDS->hHeaderOne.NumberOfLines )
320         {
321             CPLError(CE_Failure, CPLE_NotSupported,
322                      "Not all bands have same dimensions");
323             delete poDS;
324             return nullptr;
325         }
326 
327         if( VSIFReadL( abyBuf, 1, SIZEOF_HDR2_A, poDS->fp ) != SIZEOF_HDR2_A )
328             break;
329 
330         INGR_HeaderTwoADiskToMem( &poDS->hHeaderTwo, abyBuf );
331 
332         switch( static_cast<INGR_Format>(eFormatUntyped) )
333         {
334         case JPEGRGB:
335         case JPEGCMYK:
336         {
337             IntergraphBitmapBand* poBand = nullptr;
338             nBands++;
339             poDS->SetBand( nBands,
340                 poBand = new IntergraphBitmapBand( poDS, nBands, nBandOffset, 1 ));
341             if (poBand->pabyBMPBlock == nullptr)
342             {
343                 delete poDS;
344                 return nullptr;
345             }
346             nBands++;
347             poDS->SetBand( nBands,
348                 poBand = new IntergraphBitmapBand( poDS, nBands, nBandOffset, 2 ));
349             if (poBand->pabyBMPBlock == nullptr)
350             {
351                 delete poDS;
352                 return nullptr;
353             }
354             nBands++;
355             poDS->SetBand( nBands,
356                 poBand = new IntergraphBitmapBand( poDS, nBands, nBandOffset, 3 ));
357             if (poBand->pabyBMPBlock == nullptr)
358             {
359                 delete poDS;
360                 return nullptr;
361             }
362             break;
363         }
364         case JPEGGRAY:
365         case CCITTGroup4:
366         {
367             IntergraphBitmapBand* poBand = nullptr;
368             nBands++;
369             poDS->SetBand( nBands,
370                 poBand = new IntergraphBitmapBand( poDS, nBands, nBandOffset ));
371             if (poBand->pabyBMPBlock == nullptr)
372             {
373                 delete poDS;
374                 return nullptr;
375             }
376             break;
377         }
378         case RunLengthEncoded:
379         case RunLengthEncodedC:
380         case AdaptiveGrayScale:
381         {
382             IntergraphRLEBand* poBand = nullptr;
383             nBands++;
384             poDS->SetBand( nBands,
385                 poBand = new IntergraphRLEBand( poDS, nBands, nBandOffset ));
386             if (poBand->pabyBlockBuf == nullptr || poBand->pabyRLEBlock == nullptr)
387             {
388                 delete poDS;
389                 return nullptr;
390             }
391             break;
392         }
393         case AdaptiveRGB:
394         case ContinuousTone:
395         {
396             IntergraphRLEBand* poBand = nullptr;
397             nBands++;
398             poDS->SetBand( nBands,
399                 poBand = new IntergraphRLEBand( poDS, nBands, nBandOffset, 1 ));
400             if (poBand->pabyBlockBuf == nullptr || poBand->pabyRLEBlock == nullptr)
401             {
402                 delete poDS;
403                 return nullptr;
404             }
405             nBands++;
406             poDS->SetBand( nBands,
407                 poBand = new IntergraphRLEBand( poDS, nBands, nBandOffset, 2 ));
408             if (poBand->pabyBlockBuf == nullptr || poBand->pabyRLEBlock == nullptr)
409             {
410                 delete poDS;
411                 return nullptr;
412             }
413             nBands++;
414             poDS->SetBand( nBands,
415                 poBand = new IntergraphRLEBand( poDS, nBands, nBandOffset, 3 ));
416             if (poBand->pabyBlockBuf == nullptr || poBand->pabyRLEBlock == nullptr)
417             {
418                 delete poDS;
419                 return nullptr;
420             }
421             break;
422         }
423         case Uncompressed24bit:
424         {
425             IntergraphRGBBand* poBand = nullptr;
426             nBands++;
427             poDS->SetBand( nBands,
428                 poBand = new IntergraphRGBBand( poDS, nBands, nBandOffset, 1 ));
429             if (poBand->pabyBlockBuf == nullptr)
430             {
431                 delete poDS;
432                 return nullptr;
433             }
434             nBands++;
435             poDS->SetBand( nBands,
436                 poBand = new IntergraphRGBBand( poDS, nBands, nBandOffset, 2 ));
437             if (poBand->pabyBlockBuf == nullptr)
438             {
439                 delete poDS;
440                 return nullptr;
441             }
442             nBands++;
443             poDS->SetBand( nBands,
444                 poBand = new IntergraphRGBBand( poDS, nBands, nBandOffset, 3 ));
445             if (poBand->pabyBlockBuf == nullptr)
446             {
447                 delete poDS;
448                 return nullptr;
449             }
450             break;
451         }
452         default:
453         {
454             IntergraphRasterBand* poBand = nullptr;
455             nBands++;
456             poDS->SetBand( nBands,
457                 poBand = new IntergraphRasterBand( poDS, nBands, nBandOffset ));
458             if (poBand->pabyBlockBuf == nullptr)
459             {
460                 delete poDS;
461                 return nullptr;
462             }
463         }
464         }
465 
466         // ----------------------------------------------------------------
467         // Get next band offset from Catenated File Pointer (CFP)
468         // ----------------------------------------------------------------
469 
470         nBandOffset = poDS->hHeaderTwo.CatenatedFilePointer;
471     }
472     while( nBandOffset != 0 && GDALCheckBandCount(nBands, false) );
473 
474     poDS->nBands = nBands;
475 
476     // --------------------------------------------------------------------
477     // Initialize any PAM information
478     // --------------------------------------------------------------------
479 
480     poDS->SetDescription( poOpenInfo->pszFilename );
481     poDS->TryLoadXML();
482 
483     /* --------------------------------------------------------------------*/
484     /*      Check for external overviews.                                   */
485     /* --------------------------------------------------------------------*/
486 
487     poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
488 
489     return poDS;
490 }
491 
492 //  ----------------------------------------------------------------------------
493 //                                                   IntergraphDataset::Create()
494 //  ----------------------------------------------------------------------------
495 
Create(const char * pszFilename,int nXSize,int nYSize,int nBands,GDALDataType eType,char ** papszOptions)496 GDALDataset *IntergraphDataset::Create( const char *pszFilename,
497                                         int nXSize,
498                                         int nYSize,
499                                         int nBands,
500                                         GDALDataType eType,
501                                         char **papszOptions )
502 {
503     if( !GDALIsDriverDeprecatedForGDAL35StillEnabled("INGR") )
504         return nullptr;
505 
506     int nDeviceResolution = 1;
507 
508     const char *pszValue = CSLFetchNameValue(papszOptions, "RESOLUTION");
509     if( pszValue != nullptr )
510         nDeviceResolution = -atoi( pszValue );
511 
512     char *pszExtension = CPLStrlwr(CPLStrdup(CPLGetExtension(pszFilename)));
513     const char *pszCompression = nullptr;
514     if ( EQUAL( pszExtension, "rle" ) )
515         pszCompression = INGR_GetFormatName(RunLengthEncoded);
516     CPLFree(pszExtension);
517 
518     if( eType != GDT_Byte &&
519         eType != GDT_Int16 &&
520         eType != GDT_Int32 &&
521         eType != GDT_UInt16 &&
522         eType != GDT_UInt32 &&
523         eType != GDT_Float32&&
524         eType != GDT_Float64 )
525     {
526         CPLError( CE_Failure, CPLE_AppDefined, "Data type not supported (%s)",
527             GDALGetDataTypeName( eType ) );
528         return nullptr;
529     }
530 
531     // --------------------------------------------------------------------
532     //  Fill headers with minimum information
533     // --------------------------------------------------------------------
534 
535     INGR_HeaderOne  hHdr1;
536     INGR_HeaderTwoA hHdr2;
537     INGR_ColorTable256 hCTab;
538 
539     memset(&hHdr1, 0, sizeof(hHdr1));
540     memset(&hHdr2, 0, sizeof(hHdr2));
541     memset(&hCTab, 0, sizeof(hCTab));
542 
543     hHdr1.HeaderType.Version    = INGR_HEADER_VERSION;
544     hHdr1.HeaderType.Type       = INGR_HEADER_TYPE;
545     hHdr1.HeaderType.Is2Dor3D   = INGR_HEADER_2D;
546     hHdr1.DataTypeCode          = (uint16_t) INGR_GetFormat( eType, (pszCompression!=nullptr)?pszCompression:"None" );
547     hHdr1.WordsToFollow         = ( ( SIZEOF_HDR1 * 3 ) / 2 ) - 2;
548     hHdr1.ApplicationType       = GenericRasterImageFile;
549     hHdr1.XViewOrigin           = 0.0;
550     hHdr1.YViewOrigin           = 0.0;
551     hHdr1.ZViewOrigin           = 0.0;
552     hHdr1.XViewExtent           = 0.0;
553     hHdr1.YViewExtent           = 0.0;
554     hHdr1.ZViewExtent           = 0.0;
555     for( int i = 0; i < 15; i++ )
556         hHdr1.TransformationMatrix[i]   = 0.0;
557     hHdr1.TransformationMatrix[15]      = 1.0;
558     hHdr1.PixelsPerLine         = nXSize;
559     hHdr1.NumberOfLines         = nYSize;
560     hHdr1.DeviceResolution      = static_cast<int16_t>(nDeviceResolution);
561     hHdr1.ScanlineOrientation   = UpperLeftHorizontal;
562     hHdr1.ScannableFlag         = NoLineHeader;
563     hHdr1.RotationAngle         = 0.0;
564     hHdr1.SkewAngle             = 0.0;
565     hHdr1.DataTypeModifier      = 0;
566     hHdr1.DesignFileName[0]     = '\0';
567     hHdr1.DataBaseFileName[0]   = '\0';
568     hHdr1.ParentGridFileName[0] = '\0';
569     hHdr1.FileDescription[0]    = '\0';
570     hHdr1.Minimum               = INGR_SetMinMax( eType, 0.0 );
571     hHdr1.Maximum               = INGR_SetMinMax( eType, 0.0 );
572     hHdr1.GridFileVersion       = 3;
573     hHdr1.Reserved[0]           = 0;
574     hHdr1.Reserved[1]           = 0;
575     hHdr1.Reserved[2]           = 0;
576     hHdr2.Gain                  = 0;
577     hHdr2.OffsetThreshold       = 0;
578     hHdr2.View1                 = 0;
579     hHdr2.View2                 = 0;
580     hHdr2.ViewNumber            = 0;
581     hHdr2.Reserved2             = 0;
582     hHdr2.Reserved3             = 0;
583     hHdr2.AspectRatio           = nXSize / nYSize;
584     hHdr2.CatenatedFilePointer  = 0;
585     hHdr2.ColorTableType        = NoColorTable;
586     hHdr2.NumberOfCTEntries     = 0;
587     hHdr2.Reserved8             = 0;
588     for( int i = 0; i < 110; i++ )
589         hHdr2.Reserved[i]       = 0;
590     hHdr2.ApplicationPacketLength   = 0;
591     hHdr2.ApplicationPacketPointer  = 0;
592 
593     // --------------------------------------------------------------------
594     //  RGB Composite assumption
595     // --------------------------------------------------------------------
596 
597     if( eType  == GDT_Byte  &&
598         nBands == 3 )
599     {
600         hHdr1.DataTypeCode = Uncompressed24bit;
601     }
602 
603     // --------------------------------------------------------------------
604     //  Create output file with minimum header info
605     // --------------------------------------------------------------------
606 
607     VSILFILE *fp = VSIFOpenL( pszFilename, "wb+" );
608 
609     if( fp == nullptr )
610     {
611         CPLError( CE_Failure, CPLE_OpenFailed,
612             "Attempt to create file %s' failed.\n", pszFilename );
613         return nullptr;
614     }
615 
616     GByte abyBuf[MAX(SIZEOF_HDR1,SIZEOF_CTAB)];
617 
618     INGR_HeaderOneMemToDisk( &hHdr1, abyBuf );
619 
620     VSIFWriteL( abyBuf, 1, SIZEOF_HDR1, fp );
621 
622     INGR_HeaderTwoAMemToDisk( &hHdr2, abyBuf );
623 
624     VSIFWriteL( abyBuf, 1, SIZEOF_HDR2_A, fp );
625 
626     unsigned int n = 0;
627 
628     for( int i = 0; i < 256; i++ )
629     {
630         STRC2BUF( abyBuf, n, hCTab.Entry[i].v_red );
631         STRC2BUF( abyBuf, n, hCTab.Entry[i].v_green );
632         STRC2BUF( abyBuf, n, hCTab.Entry[i].v_blue );
633     }
634 
635     VSIFWriteL( abyBuf, 1, SIZEOF_CTAB, fp );
636 
637     VSIFCloseL( fp );
638 
639     // --------------------------------------------------------------------
640     //  Returns a new IntergraphDataset from the created file
641     // --------------------------------------------------------------------
642 
643     return ( IntergraphDataset * ) GDALOpen( pszFilename, GA_Update );
644 }
645 
646 //  ----------------------------------------------------------------------------
647 //                                               IntergraphDataset::CreateCopy()
648 //  ----------------------------------------------------------------------------
649 
CreateCopy(const char * pszFilename,GDALDataset * poSrcDS,int,char ** papszOptions,GDALProgressFunc pfnProgress,void * pProgressData)650 GDALDataset *IntergraphDataset::CreateCopy( const char *pszFilename,
651                                            GDALDataset *poSrcDS,
652                                             int /* bStrict  */ ,
653                                            char **papszOptions,
654                                            GDALProgressFunc pfnProgress,
655                                            void *pProgressData )
656 {
657     if( !GDALIsDriverDeprecatedForGDAL35StillEnabled("INGR") )
658         return nullptr;
659 
660     int nBands = poSrcDS->GetRasterCount();
661     if (nBands == 0)
662     {
663         CPLError( CE_Failure, CPLE_NotSupported,
664                   "Intergraph driver does not support source dataset with zero band.\n");
665         return nullptr;
666     }
667 
668     if( !pfnProgress( 0.0, nullptr, pProgressData ) )
669     {
670         return nullptr;
671     }
672 
673     // --------------------------------------------------------------------
674     // Query GDAL Data Type
675     // --------------------------------------------------------------------
676 
677     GDALDataType eType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
678 
679     // --------------------------------------------------------------------
680     // Copy metadata
681     // --------------------------------------------------------------------
682 
683     char **papszCreateOptions = CSLDuplicate( papszOptions );
684     const char  *pszValue = CSLFetchNameValue(papszCreateOptions, "RESOLUTION");
685     if( pszValue == nullptr )
686     {
687         const char *value = poSrcDS->GetMetadataItem("RESOLUTION");
688         if (value)
689         {
690             papszCreateOptions = CSLSetNameValue( papszCreateOptions, "RESOLUTION",
691                 value );
692         }
693     }
694 
695     // --------------------------------------------------------------------
696     // Create IntergraphDataset
697     // --------------------------------------------------------------------
698 
699     IntergraphDataset *poDstDS
700         = (IntergraphDataset*) IntergraphDataset::Create( pszFilename,
701         poSrcDS->GetRasterXSize(),
702         poSrcDS->GetRasterYSize(),
703         poSrcDS->GetRasterCount(),
704         eType,
705         papszCreateOptions );
706 
707     CSLDestroy( papszCreateOptions );
708 
709     if( poDstDS == nullptr )
710     {
711         return nullptr;
712     }
713 
714     // --------------------------------------------------------------------
715     // Copy Transformation Matrix to the dataset
716     // --------------------------------------------------------------------
717 
718     double adfGeoTransform[6];
719 
720     poDstDS->SetSpatialRef( poSrcDS->GetSpatialRef() );
721     poSrcDS->GetGeoTransform( adfGeoTransform );
722     poDstDS->SetGeoTransform( adfGeoTransform );
723 
724     // --------------------------------------------------------------------
725     // Copy information to the raster band
726     // --------------------------------------------------------------------
727 
728     double dfMin;
729     double dfMax;
730     double dfMean;
731     double dfStdDev = -1;
732 
733     for( int i = 1; i <= poDstDS->nBands; i++)
734     {
735         delete poDstDS->GetRasterBand(i);
736     }
737     poDstDS->nBands = 0;
738 
739     if( poDstDS->hHeaderOne.DataTypeCode == Uncompressed24bit )
740     {
741         poDstDS->SetBand( 1, new IntergraphRGBBand( poDstDS, 1, 0, 3 ) );
742         poDstDS->SetBand( 2, new IntergraphRGBBand( poDstDS, 2, 0, 2 ) );
743         poDstDS->SetBand( 3, new IntergraphRGBBand( poDstDS, 3, 0, 1 ) );
744         poDstDS->nBands = 3;
745     }
746     else
747     {
748         for( int i = 1; i <= poSrcDS->GetRasterCount(); i++ )
749         {
750             GDALRasterBand* poSrcBand = poSrcDS->GetRasterBand(i);
751             eType = poSrcDS->GetRasterBand(i)->GetRasterDataType();
752 
753             GDALRasterBand* poDstBand = new IntergraphRasterBand( poDstDS, i, 0, eType );
754             poDstDS->SetBand( i, poDstBand );
755 
756             poDstBand->SetCategoryNames( poSrcBand->GetCategoryNames() );
757             poDstBand->SetColorTable( poSrcBand->GetColorTable() );
758             poSrcBand->GetStatistics( false, true, &dfMin, &dfMax, &dfMean, &dfStdDev );
759             poDstBand->SetStatistics( dfMin, dfMax, dfMean, dfStdDev );
760         }
761     }
762 
763     // --------------------------------------------------------------------
764     // Copy image data
765     // --------------------------------------------------------------------
766 
767     int nXSize = poDstDS->GetRasterXSize();
768     int nYSize = poDstDS->GetRasterYSize();
769 
770     for( int iBand = 1; iBand <= poSrcDS->GetRasterCount(); iBand++ )
771     {
772         GDALRasterBand *poDstBand = poDstDS->GetRasterBand( iBand );
773         GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( iBand );
774 
775         // ------------------------------------------------------------
776         // Copy Untiled / Uncompressed
777         // ------------------------------------------------------------
778 
779         int nBlockXSize;
780         int nBlockYSize;
781 
782         poSrcBand->GetBlockSize( &nBlockXSize, &nBlockYSize );
783 
784         // TODO: Is this correct?  It appears to overwrite the block sizes.
785         nBlockXSize = nXSize;
786         nBlockYSize = 1;
787 
788         void *pData
789             = CPLMalloc( nBlockXSize * nBlockYSize
790                          * GDALGetDataTypeSize( eType ) / 8 );
791 
792         for( int iYOffset = 0; iYOffset < nYSize; iYOffset += nBlockYSize )
793         {
794             CPLErr eErr = CE_None;
795             for( int iXOffset = 0; iXOffset < nXSize; iXOffset += nBlockXSize )
796             {
797                 eErr = poSrcBand->RasterIO( GF_Read,
798                     iXOffset, iYOffset,
799                     nBlockXSize, nBlockYSize,
800                     pData, nBlockXSize, nBlockYSize,
801                     eType, 0, 0, nullptr );
802                 if( eErr != CE_None )
803                 {
804                     CPLFree( pData );
805                     delete poDstDS;
806                     return nullptr;
807                 }
808                 eErr = poDstBand->RasterIO( GF_Write,
809                     iXOffset, iYOffset,
810                     nBlockXSize, nBlockYSize,
811                     pData, nBlockXSize, nBlockYSize,
812                     eType, 0, 0, nullptr );
813                 if( eErr != CE_None )
814                 {
815                     CPLFree( pData );
816                     delete poDstDS;
817                     return nullptr;
818                 }
819             }
820             if( ( eErr == CE_None ) && ( ! pfnProgress(
821                 ( iYOffset + 1 ) / ( double ) nYSize, nullptr, pProgressData ) ) )
822             {
823                 CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated CreateCopy()" );
824                 CPLFree( pData );
825                 delete poDstDS;
826                 return nullptr;
827             }
828         }
829         CPLFree( pData );
830     }
831 
832     // --------------------------------------------------------------------
833     // Finalize
834     // --------------------------------------------------------------------
835 
836     poDstDS->FlushCache();
837 
838     return poDstDS;
839 }
840 
841 //  ----------------------------------------------------------------------------
842 //                                          IntergraphDataset::GetGeoTransform()
843 //  ----------------------------------------------------------------------------
844 
GetGeoTransform(double * padfTransform)845 CPLErr  IntergraphDataset::GetGeoTransform( double *padfTransform )
846 {
847     if( GDALPamDataset::GetGeoTransform( padfTransform ) != CE_None )
848     {
849         memcpy( padfTransform, adfGeoTransform, sizeof( double ) * 6 );
850     }
851 
852     return CE_None;
853 }
854 
855 //  ----------------------------------------------------------------------------
856 //                                          IntergraphDataset::SetGeoTransform()
857 //  ----------------------------------------------------------------------------
858 
SetGeoTransform(double * padfTransform)859 CPLErr IntergraphDataset::SetGeoTransform( double *padfTransform )
860 {
861     if( GDALPamDataset::SetGeoTransform( padfTransform ) != CE_None )
862     {
863         memcpy( adfGeoTransform, padfTransform, sizeof( double ) * 6 );
864     }
865 
866     INGR_SetTransMatrix( hHeaderOne.TransformationMatrix, padfTransform );
867 
868     return CE_None;
869 }
870 
871 //  ----------------------------------------------------------------------------
872 //                                            IntergraphDataset::_SetProjection()
873 //  ----------------------------------------------------------------------------
874 
_SetProjection(const char *)875 CPLErr IntergraphDataset::_SetProjection( const char * /* pszProjString */ )
876 {
877     return CE_None;
878 }
879 
880 //  ----------------------------------------------------------------------------
881 //                                                           GDALRegister_INGR()
882 //  ----------------------------------------------------------------------------
883 
GDALRegister_INGR()884 void GDALRegister_INGR()
885 {
886     if( GDALGetDriverByName( "INGR" ) != nullptr )
887         return;
888 
889     GDALDriver *poDriver = new GDALDriver();
890 
891     poDriver->SetDescription( "INGR" );
892     poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" );
893     poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, "Intergraph Raster" );
894     poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
895                                "drivers/raster/intergraphraster.html" );
896     poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
897     poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES,
898                                "Byte Int16 Int32 Float32 Float64" );
899 
900     poDriver->pfnOpen = IntergraphDataset::Open;
901     poDriver->pfnCreate    = IntergraphDataset::Create;
902     poDriver->pfnCreateCopy = IntergraphDataset::CreateCopy;
903 
904     GetGDALDriverManager()->RegisterDriver( poDriver );
905 }
906