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