1 /******************************************************************************
2  *
3  * Project:  GRC Reader
4  * Purpose:  GDAL driver for Northwood Classified Format
5  * Author:   Perry Casson
6  *
7  ******************************************************************************
8  * Copyright (c) 2007, Waypoint Information Technology
9  * Copyright (c) 2009-2012, 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 "gdal_frmts.h"
31 #include "gdal_pam.h"
32 #include "northwood.h"
33 
34 #ifdef MSVC
35 #include "..\..\ogr\ogrsf_frmts\mitab\mitab.h"
36 #else
37 #include "../../ogr/ogrsf_frmts/mitab/mitab.h"
38 #endif
39 
40 CPL_CVSID("$Id: grcdataset.cpp edff0f6ff69efb760c7e123f7ca2e2b2038d3a7e 2021-03-27 10:49:34 +0100 Even Rouault $")
41 
42 /************************************************************************/
43 /* ==================================================================== */
44 /*                             NWT_GRCDataset                           */
45 /* ==================================================================== */
46 /************************************************************************/
47 class NWT_GRCRasterBand;
48 
49 class NWT_GRCDataset final : public GDALPamDataset
50 {
51   friend class NWT_GRCRasterBand;
52 
53   private:
54     VSILFILE * fp;
55     GByte abyHeader[1024];
56     NWT_GRID *pGrd;
57     char **papszCategories;
58     char *pszProjection;
59 
60     NWT_GRCDataset(const NWT_GRCDataset&) = delete;
61     NWT_GRCDataset& operator= (const NWT_GRCDataset&) = delete;
62 
63   protected:
64     GDALColorTable * poColorTable;
65 
66   public:
67     NWT_GRCDataset();
68     ~NWT_GRCDataset();
69 
70     static GDALDataset *Open( GDALOpenInfo * );
71     static int Identify( GDALOpenInfo * poOpenInfo );
72 
73     CPLErr GetGeoTransform( double *padfTransform ) override;
74     const char *_GetProjectionRef() override;
GetSpatialRef() const75     const OGRSpatialReference* GetSpatialRef() const override {
76         return GetSpatialRefFromOldGetProjectionRef();
77     }
78 };
79 
80 /************************************************************************/
81 /* ==================================================================== */
82 /*                            NWT_GRCRasterBand                         */
83 /* ==================================================================== */
84 /************************************************************************/
85 
86 class NWT_GRCRasterBand final : public GDALPamRasterBand
87 {
88   friend class NWT_GRCDataset;
89 
90   public:
91 
92     NWT_GRCRasterBand( NWT_GRCDataset *, int );
93     virtual ~NWT_GRCRasterBand();
94 
95     virtual CPLErr IReadBlock( int, int, void * ) override;
96     virtual double GetNoDataValue( int *pbSuccess ) override;
97 
98     virtual GDALColorInterp GetColorInterpretation() override;
99     virtual char **GetCategoryNames() override;
100     virtual GDALColorTable *GetColorTable() override;
101 };
102 
103 /************************************************************************/
104 /*                           NWT_GRCRasterBand()                        */
105 /************************************************************************/
106 
NWT_GRCRasterBand(NWT_GRCDataset * poDSIn,int nBandIn)107 NWT_GRCRasterBand::NWT_GRCRasterBand( NWT_GRCDataset * poDSIn, int nBandIn )
108 {
109     poDS = poDSIn;
110     nBand = nBandIn;
111     NWT_GRCDataset *poGDS = cpl::down_cast<NWT_GRCDataset *>( poDS );
112 
113     if( poGDS->pGrd->nBitsPerPixel == 8 )
114         eDataType = GDT_Byte;
115     else if( poGDS->pGrd->nBitsPerPixel == 16 )
116         eDataType = GDT_UInt16;
117     else /* if( poGDS->pGrd->nBitsPerPixel == 32 ) */
118         eDataType = GDT_UInt32;        // this would be funny
119 
120     nBlockXSize = poDS->GetRasterXSize();
121     nBlockYSize = 1;
122 
123     // load the color table and might as well to the ClassNames
124     poGDS->poColorTable = new GDALColorTable();
125 
126     GDALColorEntry oEntry = { 255, 255, 255, 0 };
127     // null value = 0 is transparent
128     // alpha 0 = transparent
129 
130     poGDS->poColorTable->SetColorEntry( 0, &oEntry );
131 
132     for( int i = 0;
133          i < static_cast<int>( poGDS->pGrd->stClassDict->nNumClassifiedItems );
134          i++ )
135     {
136         oEntry.c1 = poGDS->pGrd->stClassDict->stClassifiedItem[i]->r;
137         oEntry.c2 = poGDS->pGrd->stClassDict->stClassifiedItem[i]->g;
138         oEntry.c3 = poGDS->pGrd->stClassDict->stClassifiedItem[i]->b;
139         oEntry.c4 = 255;            // alpha 255 = solid
140 
141         poGDS->poColorTable->SetColorEntry( poGDS->pGrd->
142                                           stClassDict->stClassifiedItem[i]->
143                                           usPixVal, &oEntry );
144     }
145 
146     // find the max value used in the grc
147     int maxValue = 0;
148     for( int i=0; i < static_cast<int>( poGDS->pGrd->stClassDict->nNumClassifiedItems ); i++ )
149     {
150         if( poGDS->pGrd->stClassDict->stClassifiedItem[i]->usPixVal > maxValue )
151             maxValue = poGDS->pGrd->stClassDict->stClassifiedItem[i]->usPixVal;
152     }
153 
154     // load a value for the null value
155     poGDS->papszCategories = CSLAddString( poGDS->papszCategories, "No Data" );
156 
157     // for the class names we need to load nulls string for all classes that
158     // are not defined
159     for( int val = 1; val <= maxValue; val++ )
160     {
161         int i = 0;
162         // Loop through the GRC dictionary to see if the value is defined.
163         for( ;
164              i < static_cast<int>( poGDS->pGrd->stClassDict->nNumClassifiedItems );
165              i++ )
166         {
167             if( static_cast<int>( poGDS->pGrd->stClassDict->stClassifiedItem[i]->usPixVal ) ==
168                 val )
169             {
170                 poGDS->papszCategories =
171                     CSLAddString( poGDS->papszCategories,
172                                     poGDS->pGrd->stClassDict->
173                                     stClassifiedItem[i]->szClassName );
174                 break;
175             }
176         }
177         if( i >= static_cast<int>( poGDS->pGrd->stClassDict->nNumClassifiedItems ) )
178             poGDS->papszCategories = CSLAddString( poGDS->papszCategories, "" );
179     }
180 }
181 
~NWT_GRCRasterBand()182 NWT_GRCRasterBand::~NWT_GRCRasterBand() {}
183 
GetNoDataValue(int * pbSuccess)184 double NWT_GRCRasterBand::GetNoDataValue( int *pbSuccess )
185 {
186     if( pbSuccess != nullptr )
187         *pbSuccess = TRUE;
188 
189     return 0.0;  // Northwood grid 0 is always null.
190 }
191 
192 // return an array of null terminated strings for the class names
GetCategoryNames()193 char **NWT_GRCRasterBand::GetCategoryNames()
194 {
195     NWT_GRCDataset *poGDS = cpl::down_cast<NWT_GRCDataset *>( poDS );
196 
197     return poGDS->papszCategories;
198 }
199 
200 // return the color table
GetColorTable()201 GDALColorTable *NWT_GRCRasterBand::GetColorTable()
202 {
203     NWT_GRCDataset *poGDS = cpl::down_cast<NWT_GRCDataset *>( poDS );
204 
205     return poGDS->poColorTable;
206 }
207 
GetColorInterpretation()208 GDALColorInterp NWT_GRCRasterBand::GetColorInterpretation()
209 {
210     if( nBand == 1 )
211         return GCI_PaletteIndex;
212 
213     return GCI_Undefined;
214 }
215 
216 /************************************************************************/
217 /*                             IReadBlock()                             */
218 /************************************************************************/
IReadBlock(CPL_UNUSED int nBlockXOff,int nBlockYOff,void * pImage)219 CPLErr NWT_GRCRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff,
220                                       int nBlockYOff,
221                                       void *pImage )
222 {
223     NWT_GRCDataset *poGDS = cpl::down_cast<NWT_GRCDataset *>( poDS );
224     const int nBytesPerPixel = poGDS->pGrd->nBitsPerPixel / 8;
225     if( nBytesPerPixel <= 0 || nBlockXSize > INT_MAX / nBytesPerPixel )
226         return CE_Failure;
227     const int nRecordSize = nBlockXSize * nBytesPerPixel;
228 
229     if( nBand == 1 )
230     {                            //grc's are just one band of indices
231         VSIFSeekL( poGDS->fp, 1024 + nRecordSize * static_cast<vsi_l_offset>(nBlockYOff), SEEK_SET );
232         if( static_cast<int>(VSIFReadL( pImage, 1, nRecordSize, poGDS->fp )) != nRecordSize )
233             return CE_Failure;
234     }
235     else
236     {
237         CPLError( CE_Failure, CPLE_IllegalArg,
238                   "No band number %d",
239                   nBand );
240         return CE_Failure;
241     }
242     return CE_None;
243 }
244 
245 /************************************************************************/
246 /* ==================================================================== */
247 /*                          NWT_GRCDataset                              */
248 /* ==================================================================== */
249 /************************************************************************/
NWT_GRCDataset()250 NWT_GRCDataset::NWT_GRCDataset() :
251     fp(nullptr),
252     pGrd(nullptr),
253     papszCategories(nullptr),
254     pszProjection(nullptr),
255     poColorTable(nullptr)
256 {
257     memset(abyHeader, 0, sizeof(abyHeader) );
258 }
259 
260 /************************************************************************/
261 /*                            ~NWT_GRCDataset()                         */
262 /************************************************************************/
~NWT_GRCDataset()263 NWT_GRCDataset::~NWT_GRCDataset()
264 {
265     delete poColorTable;
266     CSLDestroy( papszCategories );
267 
268     NWT_GRCDataset::FlushCache();
269     pGrd->fp = nullptr;       // this prevents nwtCloseGrid from closing the fp
270     nwtCloseGrid( pGrd );
271 
272     if( fp != nullptr )
273         VSIFCloseL( fp );
274 
275     CPLFree( pszProjection );
276 }
277 
278 /************************************************************************/
279 /*                          GetGeoTransform()                           */
280 /************************************************************************/
GetGeoTransform(double * padfTransform)281 CPLErr NWT_GRCDataset::GetGeoTransform( double *padfTransform )
282 {
283     padfTransform[0] = pGrd->dfMinX - ( pGrd->dfStepSize * 0.5 );
284     padfTransform[3] = pGrd->dfMaxY + ( pGrd->dfStepSize * 0.5 );
285     padfTransform[1] = pGrd->dfStepSize;
286     padfTransform[2] = 0.0;
287 
288     padfTransform[4] = 0.0;
289     padfTransform[5] = -1 * pGrd->dfStepSize;
290 
291     return CE_None;
292 }
293 
294 /************************************************************************/
295 /*                          GetProjectionRef()                          */
296 /************************************************************************/
_GetProjectionRef()297 const char *NWT_GRCDataset::_GetProjectionRef()
298 {
299     if (pszProjection == nullptr)
300     {
301         OGRSpatialReference *poSpatialRef
302           = MITABCoordSys2SpatialRef( pGrd->cMICoordSys );
303         if (poSpatialRef)
304         {
305             poSpatialRef->exportToWkt( &pszProjection );
306             poSpatialRef->Release();
307         }
308     }
309     return pszProjection;
310 }
311 
312 /************************************************************************/
313 /*                              Identify()                              */
314 /************************************************************************/
315 
Identify(GDALOpenInfo * poOpenInfo)316 int NWT_GRCDataset::Identify( GDALOpenInfo * poOpenInfo )
317 {
318 /* -------------------------------------------------------------------- */
319 /*  Look for the header                                                 */
320 /* -------------------------------------------------------------------- */
321     if( poOpenInfo->nHeaderBytes < 1024 )
322         return FALSE;
323 
324     if( poOpenInfo->pabyHeader[0] != 'H' ||
325         poOpenInfo->pabyHeader[1] != 'G' ||
326         poOpenInfo->pabyHeader[2] != 'P' ||
327         poOpenInfo->pabyHeader[3] != 'C' ||
328         poOpenInfo->pabyHeader[4] != '8' )
329         return FALSE;
330 
331     return TRUE;
332 }
333 
334 /************************************************************************/
335 /*                                Open()                                */
336 /************************************************************************/
337 
Open(GDALOpenInfo * poOpenInfo)338 GDALDataset *NWT_GRCDataset::Open( GDALOpenInfo * poOpenInfo )
339 {
340     if( !Identify(poOpenInfo) || poOpenInfo->fpL == nullptr )
341         return nullptr;
342 
343 /* -------------------------------------------------------------------- */
344 /*      Create a corresponding GDALDataset.                             */
345 /* -------------------------------------------------------------------- */
346     NWT_GRCDataset *poDS = new NWT_GRCDataset();
347 
348     poDS->fp = poOpenInfo->fpL;
349     poOpenInfo->fpL = nullptr;
350 
351 /* -------------------------------------------------------------------- */
352 /*      Read the header.                                                */
353 /* -------------------------------------------------------------------- */
354     VSIFSeekL( poDS->fp, 0, SEEK_SET );
355     VSIFReadL( poDS->abyHeader, 1, 1024, poDS->fp );
356     poDS->pGrd = static_cast<NWT_GRID *>( malloc( sizeof (NWT_GRID) ) );
357 
358     poDS->pGrd->fp = poDS->fp;
359 
360     if (!nwt_ParseHeader( poDS->pGrd, poDS->abyHeader ) ||
361         !GDALCheckDatasetDimensions(poDS->pGrd->nXSide, poDS->pGrd->nYSide) ||
362         poDS->pGrd->stClassDict == nullptr)
363     {
364         delete poDS;
365         return nullptr;
366     }
367 
368     if( poDS->pGrd->nBitsPerPixel != 8 &&
369         poDS->pGrd->nBitsPerPixel != 16 &&
370         poDS->pGrd->nBitsPerPixel != 32 )
371     {
372         delete poDS;
373         return nullptr;
374     }
375 
376     poDS->nRasterXSize = poDS->pGrd->nXSide;
377     poDS->nRasterYSize = poDS->pGrd->nYSide;
378 
379 /* -------------------------------------------------------------------- */
380 /*      Create band information objects.                                */
381 /* -------------------------------------------------------------------- */
382     poDS->SetBand( 1, new NWT_GRCRasterBand( poDS, 1) );    //Class Indexes
383 
384 /* -------------------------------------------------------------------- */
385 /*      Initialize any PAM information.                                 */
386 /* -------------------------------------------------------------------- */
387     poDS->SetDescription( poOpenInfo->pszFilename );
388     poDS->TryLoadXML();
389 
390 /* -------------------------------------------------------------------- */
391 /*      Check for external overviews.                                   */
392 /* -------------------------------------------------------------------- */
393     poDS->oOvManager.Initialize( poDS,
394                                  poOpenInfo->pszFilename,
395                                  poOpenInfo->GetSiblingFiles() );
396 
397     return poDS;
398 }
399 
400 /************************************************************************/
401 /*                          GDALRegister_GRC()                          */
402 /************************************************************************/
403 
GDALRegister_NWT_GRC()404 void GDALRegister_NWT_GRC()
405 
406 {
407     if( GDALGetDriverByName( "NWT_GRC" ) != nullptr )
408         return;
409 
410     GDALDriver *poDriver = new GDALDriver();
411 
412     poDriver->SetDescription( "NWT_GRC" );
413     poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" );
414     poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
415                                "Northwood Classified Grid Format .grc/.tab");
416     poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
417                                "drivers/raster/nwtgrd.html#driver-capabilities-nwt-grc" );
418     poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "grc" );
419     poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
420 
421     poDriver->pfnOpen = NWT_GRCDataset::Open;
422     poDriver->pfnIdentify = NWT_GRCDataset::Identify;
423 
424     GetGDALDriverManager()->RegisterDriver( poDriver );
425 }
426