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 : pGrd->fp = nullptr; // this prevents nwtCloseGrid from closing the fp 245 2 : nwtCloseGrid(pGrd); 246 : 247 2 : if (fp != nullptr) 248 2 : VSIFCloseL(fp); 249 4 : } 250 : 251 : /************************************************************************/ 252 : /* GetGeoTransform() */ 253 : /************************************************************************/ 254 0 : CPLErr NWT_GRCDataset::GetGeoTransform(double *padfTransform) 255 : { 256 0 : padfTransform[0] = pGrd->dfMinX - (pGrd->dfStepSize * 0.5); 257 0 : padfTransform[3] = pGrd->dfMaxY + (pGrd->dfStepSize * 0.5); 258 0 : padfTransform[1] = pGrd->dfStepSize; 259 0 : padfTransform[2] = 0.0; 260 : 261 0 : padfTransform[4] = 0.0; 262 0 : padfTransform[5] = -1 * pGrd->dfStepSize; 263 : 264 0 : return CE_None; 265 : } 266 : 267 : /************************************************************************/ 268 : /* GetSpatialRef() */ 269 : /************************************************************************/ 270 0 : const OGRSpatialReference *NWT_GRCDataset::GetSpatialRef() const 271 : { 272 0 : if (m_oSRS.IsEmpty()) 273 : { 274 : OGRSpatialReference *poSpatialRef = 275 0 : MITABCoordSys2SpatialRef(pGrd->cMICoordSys); 276 0 : if (poSpatialRef) 277 : { 278 0 : m_oSRS = *poSpatialRef; 279 0 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); 280 0 : poSpatialRef->Release(); 281 : } 282 : } 283 0 : return m_oSRS.IsEmpty() ? nullptr : &m_oSRS; 284 : } 285 : 286 : /************************************************************************/ 287 : /* Identify() */ 288 : /************************************************************************/ 289 : 290 50848 : int NWT_GRCDataset::Identify(GDALOpenInfo *poOpenInfo) 291 : { 292 : /* -------------------------------------------------------------------- */ 293 : /* Look for the header */ 294 : /* -------------------------------------------------------------------- */ 295 50848 : if (poOpenInfo->nHeaderBytes < 1024) 296 49136 : return FALSE; 297 : 298 1712 : if (poOpenInfo->pabyHeader[0] != 'H' || poOpenInfo->pabyHeader[1] != 'G' || 299 8 : poOpenInfo->pabyHeader[2] != 'P' || poOpenInfo->pabyHeader[3] != 'C' || 300 8 : poOpenInfo->pabyHeader[4] != '8') 301 1708 : return FALSE; 302 : 303 4 : return TRUE; 304 : } 305 : 306 : /************************************************************************/ 307 : /* Open() */ 308 : /************************************************************************/ 309 : 310 2 : GDALDataset *NWT_GRCDataset::Open(GDALOpenInfo *poOpenInfo) 311 : { 312 2 : if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr) 313 0 : return nullptr; 314 : 315 : /* -------------------------------------------------------------------- */ 316 : /* Create a corresponding GDALDataset. */ 317 : /* -------------------------------------------------------------------- */ 318 2 : NWT_GRCDataset *poDS = new NWT_GRCDataset(); 319 : 320 2 : poDS->fp = poOpenInfo->fpL; 321 2 : poOpenInfo->fpL = nullptr; 322 : 323 : /* -------------------------------------------------------------------- */ 324 : /* Read the header. */ 325 : /* -------------------------------------------------------------------- */ 326 2 : VSIFSeekL(poDS->fp, 0, SEEK_SET); 327 2 : VSIFReadL(poDS->abyHeader, 1, 1024, poDS->fp); 328 2 : poDS->pGrd = static_cast<NWT_GRID *>(malloc(sizeof(NWT_GRID))); 329 : 330 2 : poDS->pGrd->fp = poDS->fp; 331 : 332 2 : if (!nwt_ParseHeader(poDS->pGrd, poDS->abyHeader) || 333 4 : !GDALCheckDatasetDimensions(poDS->pGrd->nXSide, poDS->pGrd->nYSide) || 334 2 : poDS->pGrd->stClassDict == nullptr) 335 : { 336 0 : delete poDS; 337 0 : return nullptr; 338 : } 339 : 340 2 : if (poDS->pGrd->nBitsPerPixel != 8 && poDS->pGrd->nBitsPerPixel != 16 && 341 0 : poDS->pGrd->nBitsPerPixel != 32) 342 : { 343 0 : delete poDS; 344 0 : return nullptr; 345 : } 346 : 347 2 : poDS->nRasterXSize = poDS->pGrd->nXSide; 348 2 : poDS->nRasterYSize = poDS->pGrd->nYSide; 349 : 350 : /* -------------------------------------------------------------------- */ 351 : /* Create band information objects. */ 352 : /* -------------------------------------------------------------------- */ 353 2 : poDS->SetBand(1, new NWT_GRCRasterBand(poDS, 1)); // Class Indexes 354 : 355 : /* -------------------------------------------------------------------- */ 356 : /* Initialize any PAM information. */ 357 : /* -------------------------------------------------------------------- */ 358 2 : poDS->SetDescription(poOpenInfo->pszFilename); 359 2 : poDS->TryLoadXML(); 360 : 361 : /* -------------------------------------------------------------------- */ 362 : /* Check for external overviews. */ 363 : /* -------------------------------------------------------------------- */ 364 4 : poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename, 365 2 : poOpenInfo->GetSiblingFiles()); 366 : 367 2 : return poDS; 368 : } 369 : 370 : /************************************************************************/ 371 : /* GDALRegister_GRC() */ 372 : /************************************************************************/ 373 : 374 1595 : void GDALRegister_NWT_GRC() 375 : 376 : { 377 1595 : if (GDALGetDriverByName("NWT_GRC") != nullptr) 378 302 : return; 379 : 380 1293 : GDALDriver *poDriver = new GDALDriver(); 381 : 382 1293 : poDriver->SetDescription("NWT_GRC"); 383 1293 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES"); 384 1293 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, 385 1293 : "Northwood Classified Grid Format .grc/.tab"); 386 1293 : poDriver->SetMetadataItem( 387 : GDAL_DMD_HELPTOPIC, 388 1293 : "drivers/raster/nwtgrd.html#driver-capabilities-nwt-grc"); 389 1293 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "grc"); 390 1293 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES"); 391 : 392 1293 : poDriver->pfnOpen = NWT_GRCDataset::Open; 393 1293 : poDriver->pfnIdentify = NWT_GRCDataset::Identify; 394 : 395 1293 : GetGDALDriverManager()->RegisterDriver(poDriver); 396 : }