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