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