LCOV - code coverage report
Current view: top level - frmts/northwood - grcdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 109 155 70.3 %
Date: 2025-10-01 17:07:58 Functions: 10 16 62.5 %

          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 &gt) 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 &gt) 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             : }

Generated by: LCOV version 1.14