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