LCOV - code coverage report
Current view: top level - frmts/northwood - grddataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 422 493 85.6 %
Date: 2025-10-27 00:14:23 Functions: 21 22 95.5 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GRD Reader
       4             :  * Purpose:  GDAL driver for Northwood Grid Format
       5             :  * Author:   Perry Casson
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2006, 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 <string>
      15             : #include <cstring>
      16             : #include <cstdio>
      17             : #include <climits>
      18             : #include "gdal_frmts.h"
      19             : #include "gdal_pam.h"
      20             : #include "gdal_driver.h"
      21             : #include "gdal_drivermanager.h"
      22             : #include "gdal_openinfo.h"
      23             : #include "gdal_cpp_functions.h"
      24             : #include "northwood.h"
      25             : #include "ogrmitabspatialref.h"
      26             : 
      27             : #ifndef NO_MITAB_SUPPORT
      28             : #ifdef MSVC
      29             : #include "..\..\ogr\ogrsf_frmts\mitab\mitab.h"
      30             : #else
      31             : #include "../../ogr/ogrsf_frmts/mitab/mitab.h"
      32             : #endif
      33             : #endif
      34             : 
      35             : constexpr float NODATA = -1.e37f;
      36             : constexpr double SCALE16BIT = 65534.0;
      37             : constexpr double SCALE32BIT = 4294967294.0;
      38             : 
      39             : void replaceExt(std::string &s, const std::string &newExt);
      40             : 
      41             : /************************************************************************/
      42             : /* Replace the extension on a filepath with an alternative extension    */
      43             : /************************************************************************/
      44           0 : void replaceExt(std::string &s, const std::string &newExt)
      45             : {
      46             : 
      47           0 :     std::string::size_type i = s.rfind('.', s.length());
      48             : 
      49           0 :     if (i != std::string::npos)
      50             :     {
      51           0 :         s.replace(i + 1, newExt.length(), newExt);
      52             :     }
      53           0 : }
      54             : 
      55             : /************************************************************************/
      56             : /* ==================================================================== */
      57             : /*                      NWT_GRDDataset                                  */
      58             : /* ==================================================================== */
      59             : /************************************************************************/
      60             : class NWT_GRDRasterBand;
      61             : 
      62             : class NWT_GRDDataset final : public GDALPamDataset
      63             : {
      64             :     friend class NWT_GRDRasterBand;
      65             : 
      66             :     VSILFILE *fp;
      67             :     GByte abyHeader[1024];
      68             :     NWT_GRID *pGrd;
      69             :     NWT_RGB ColorMap[4096];
      70             :     bool bUpdateHeader;
      71             :     mutable OGRSpatialReference *m_poSRS = nullptr;
      72             : 
      73             : #ifndef NO_MITAB_SUPPORT
      74             :     // Update the header data with latest changes
      75             :     int UpdateHeader();
      76             :     int WriteTab();
      77             : #endif
      78             : 
      79             :     NWT_GRDDataset(const NWT_GRDDataset &) = delete;
      80             :     NWT_GRDDataset &operator=(const NWT_GRDDataset &) = delete;
      81             : 
      82             :   public:
      83             :     NWT_GRDDataset();
      84             :     ~NWT_GRDDataset() override;
      85             : 
      86             :     static GDALDataset *Open(GDALOpenInfo *);
      87             :     static int Identify(GDALOpenInfo *);
      88             : 
      89             : #ifndef NO_MITAB_SUPPORT
      90             :     static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
      91             :                                int nBandsIn, GDALDataType eType,
      92             :                                char **papszParamList);
      93             :     static GDALDataset *CreateCopy(const char *pszFilename,
      94             :                                    GDALDataset *poSrcDS, int bStrict,
      95             :                                    char **papszOptions,
      96             :                                    GDALProgressFunc pfnProgress,
      97             :                                    void *pProgressData);
      98             : #endif
      99             : 
     100             :     CPLErr GetGeoTransform(GDALGeoTransform &gt) const override;
     101             :     CPLErr SetGeoTransform(const GDALGeoTransform &gt) override;
     102             :     CPLErr FlushCache(bool bAtClosing) override;
     103             : 
     104             :     const OGRSpatialReference *GetSpatialRef() const override;
     105             : 
     106             : #ifndef NO_MITAB_SUPPORT
     107             :     CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override;
     108             : #endif
     109             : };
     110             : 
     111             : /************************************************************************/
     112             : /* ==================================================================== */
     113             : /*                            NWT_GRDRasterBand                         */
     114             : /* ==================================================================== */
     115             : /************************************************************************/
     116             : 
     117             : class NWT_GRDRasterBand final : public GDALPamRasterBand
     118             : {
     119             :     friend class NWT_GRDDataset;
     120             : 
     121             :     int bHaveOffsetScale;
     122             :     double dfOffset;
     123             :     double dfScale;
     124             :     double dfNoData;
     125             : 
     126             :   public:
     127             :     NWT_GRDRasterBand(NWT_GRDDataset *, int, int);
     128             : 
     129             :     CPLErr IReadBlock(int, int, void *) override;
     130             :     CPLErr IWriteBlock(int, int, void *) override;
     131             :     double GetNoDataValue(int *pbSuccess) override;
     132             :     CPLErr SetNoDataValue(double dfNoData) override;
     133             : 
     134             :     GDALColorInterp GetColorInterpretation() override;
     135             : };
     136             : 
     137             : /************************************************************************/
     138             : /*                           NWT_GRDRasterBand()                        */
     139             : /************************************************************************/
     140          45 : NWT_GRDRasterBand::NWT_GRDRasterBand(NWT_GRDDataset *poDSIn, int nBandIn,
     141          45 :                                      int nBands)
     142          45 :     : bHaveOffsetScale(FALSE), dfOffset(0.0), dfScale(1.0), dfNoData(0.0)
     143             : {
     144          45 :     poDS = poDSIn;
     145          45 :     nBand = nBandIn;
     146             : 
     147             :     /*
     148             :      * If nBand = 4 we have opened in read mode and have created the 3 'virtual'
     149             :      * RGB bands. so the 4th band is the actual data Otherwise, if we have
     150             :      * opened in update mode, there is only 1 band, which is the actual data
     151             :      */
     152          45 :     if (nBand == 4 || nBands == 1)
     153             :     {
     154          15 :         bHaveOffsetScale = TRUE;
     155          15 :         dfOffset = poDSIn->pGrd->fZMin;
     156             : 
     157          15 :         if (poDSIn->pGrd->cFormat == 0x00)
     158             :         {
     159          15 :             eDataType = GDT_Float32;
     160          15 :             dfScale = (poDSIn->pGrd->fZMax - poDSIn->pGrd->fZMin) / SCALE16BIT;
     161             :         }
     162             :         else
     163             :         {
     164           0 :             eDataType = GDT_Float32;
     165           0 :             dfScale = (poDSIn->pGrd->fZMax - poDSIn->pGrd->fZMin) / SCALE32BIT;
     166             :         }
     167             :     }
     168             :     else
     169             :     {
     170          30 :         bHaveOffsetScale = FALSE;
     171          30 :         dfOffset = 0;
     172          30 :         dfScale = 1.0;
     173          30 :         eDataType = GDT_Byte;
     174             :     }
     175          45 :     nBlockXSize = poDS->GetRasterXSize();
     176          45 :     nBlockYSize = 1;
     177          45 : }
     178             : 
     179         148 : double NWT_GRDRasterBand::GetNoDataValue(int *pbSuccess)
     180             : {
     181         148 :     NWT_GRDDataset *poGDS = cpl::down_cast<NWT_GRDDataset *>(poDS);
     182             :     double dRetval;
     183         148 :     if ((nBand == 4) || (poGDS->nBands == 1))
     184             :     {
     185         148 :         if (pbSuccess != nullptr)
     186         147 :             *pbSuccess = TRUE;
     187         148 :         if (dfNoData != 0.0)
     188             :         {
     189           0 :             dRetval = dfNoData;
     190             :         }
     191             :         else
     192             :         {
     193         148 :             dRetval = NODATA;
     194             :         }
     195             : 
     196         148 :         return dRetval;
     197             :     }
     198             : 
     199           0 :     if (pbSuccess != nullptr)
     200           0 :         *pbSuccess = FALSE;
     201             : 
     202           0 :     return 0;
     203             : }
     204             : 
     205           1 : CPLErr NWT_GRDRasterBand::SetNoDataValue(double dfNoDataIn)
     206             : {
     207             :     // This is essentially a 'virtual' no data value.
     208             :     // Once set, when writing an value == dfNoData will
     209             :     // be converted to the no data value (0) on disk.
     210             :     // If opened again; the no data value will always be the
     211             :     // default (-1.e37f)
     212           1 :     dfNoData = dfNoDataIn;
     213           1 :     return CE_None;
     214             : }
     215             : 
     216           5 : GDALColorInterp NWT_GRDRasterBand::GetColorInterpretation()
     217             : {
     218           5 :     NWT_GRDDataset *poGDS = cpl::down_cast<NWT_GRDDataset *>(poDS);
     219             :     // return GCI_RGB;
     220           5 :     if ((nBand == 4) || (poGDS->nBands == 1))
     221           5 :         return GCI_GrayIndex;
     222           0 :     else if (nBand == 1)
     223           0 :         return GCI_RedBand;
     224           0 :     else if (nBand == 2)
     225           0 :         return GCI_GreenBand;
     226           0 :     else if (nBand == 3)
     227           0 :         return GCI_BlueBand;
     228             : 
     229           0 :     return GCI_Undefined;
     230             : }
     231             : 
     232             : /************************************************************************/
     233             : /*                             IWriteBlock()                            */
     234             : /************************************************************************/
     235          48 : CPLErr NWT_GRDRasterBand::IWriteBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
     236             :                                       void *pImage)
     237             : {
     238             : 
     239             :     // Each block is an entire row of the dataset, so the x offset should always
     240             :     // be 0
     241          48 :     CPLAssert(nBlockXOff == 0);
     242          48 :     NWT_GRDDataset *poGDS = cpl::down_cast<NWT_GRDDataset *>(poDS);
     243             : 
     244          48 :     if (dfScale == 0.0)
     245           1 :         return CE_Failure;
     246             : 
     247             :     // Ensure the blocksize is not beyond the system limits and
     248             :     // initialize the size of the record
     249          47 :     if (nBlockXSize > INT_MAX / 2)
     250             :     {
     251           0 :         return CE_Failure;
     252             :     }
     253          47 :     const int nRecordSize = nBlockXSize * 2;
     254             : 
     255             :     // Seek to the write position in the GRD file
     256          47 :     VSIFSeekL(poGDS->fp,
     257          47 :               1024 + nRecordSize * static_cast<vsi_l_offset>(nBlockYOff),
     258             :               SEEK_SET);
     259             : 
     260             :     // Cast pImage to float
     261          47 :     const float *pfImage = static_cast<const float *>(pImage);
     262             : 
     263             :     // Initialize output array
     264          47 :     GByte *pabyRecord = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nRecordSize));
     265          47 :     if (pabyRecord == nullptr)
     266           0 :         return CE_Failure;
     267             : 
     268             :     // We only ever write to band 4; RGB bands are basically 'virtual'
     269             :     // (i.e. the RGB colour is computed from the raw data).
     270             :     // For all intents and purposes, there is essentially 1 band on disk.
     271          47 :     if (nBand == 1)
     272             :     {
     273        3008 :         for (int i = 0; i < nBlockXSize; i++)
     274             :         {
     275        2961 :             const float fValue = pfImage[i];
     276             :             unsigned short nWrite;  // The stretched value to be written
     277             : 
     278             :             // We allow data to be interpreted by a user-defined null value
     279             :             // (a 'virtual' value, since it is always 0 on disk) or
     280             :             // if not defined we default to the GRD standard of -1E37.
     281             :             // We allow a little bit of flexibility in that if it is below -1E37
     282             :             // it is in all probability still intended as a null value.
     283        2961 :             if ((fValue == dfNoData) || (fValue <= NODATA))
     284             :             {
     285         381 :                 nWrite = 0;
     286             :             }
     287             :             else
     288             :             {
     289        2580 :                 if (fValue < poGDS->pGrd->fZMin)
     290             :                 {
     291           0 :                     poGDS->pGrd->fZMin = fValue;
     292             :                 }
     293        2580 :                 else if (fValue > poGDS->pGrd->fZMax)
     294             :                 {
     295           0 :                     poGDS->pGrd->fZMax = fValue;
     296             :                 }
     297             :                 // Data on disk is stretched within the unsigned short range so
     298             :                 // we must convert (the inverse of what is done in IReadBlock),
     299             :                 // based on the Z value range
     300        2580 :                 nWrite = static_cast<unsigned short>(
     301        2580 :                     ((fValue - dfOffset) / dfScale) + 1);
     302             :             }
     303        2961 :             CPL_LSBPTR16(&nWrite);
     304             :             // Copy the result to the byte array (2 bytes per value)
     305        2961 :             memcpy(pabyRecord + 2 * i, &nWrite, 2);
     306             :         }
     307             : 
     308             :         // Write the buffer to disk
     309          47 :         if (VSIFWriteL(pabyRecord, 1, nRecordSize, poGDS->fp) !=
     310          47 :             static_cast<size_t>(nRecordSize))
     311             :         {
     312           0 :             CPLError(CE_Failure, CPLE_FileIO,
     313             :                      "Failed to write scanline %d to file.\n", nBlockYOff);
     314           0 :             CPLFree(pabyRecord);
     315           0 :             return CE_Failure;
     316             :         }
     317             :     }
     318             :     else
     319             :     {
     320           0 :         CPLError(CE_Failure, CPLE_IllegalArg, "Writing to band %d is not valid",
     321             :                  nBand);
     322           0 :         CPLFree(pabyRecord);
     323           0 :         return CE_Failure;
     324             :     }
     325          47 :     CPLFree(pabyRecord);
     326          47 :     return CE_None;
     327             : }
     328             : 
     329             : /************************************************************************/
     330             : /*                             IReadBlock()                             */
     331             : /************************************************************************/
     332         282 : CPLErr NWT_GRDRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
     333             :                                      void *pImage)
     334             : {
     335         282 :     NWT_GRDDataset *poGDS = cpl::down_cast<NWT_GRDDataset *>(poDS);
     336         282 :     if (nBlockXSize > INT_MAX / 2)
     337           0 :         return CE_Failure;
     338         282 :     const int nRecordSize = nBlockXSize * 2;
     339             : 
     340             :     // Seek to the data position
     341         282 :     VSIFSeekL(poGDS->fp,
     342         282 :               1024 + nRecordSize * static_cast<vsi_l_offset>(nBlockYOff),
     343             :               SEEK_SET);
     344             : 
     345         282 :     GByte *pabyRecord = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nRecordSize));
     346         282 :     if (pabyRecord == nullptr)
     347           0 :         return CE_Failure;
     348             : 
     349             :     // Read the data
     350         282 :     if (static_cast<int>(VSIFReadL(pabyRecord, 1, nRecordSize, poGDS->fp)) !=
     351             :         nRecordSize)
     352             :     {
     353           0 :         CPLFree(pabyRecord);
     354           0 :         return CE_Failure;
     355             :     }
     356             : 
     357         282 :     if ((nBand == 4) || (poGDS->nBands == 1))  // Z values
     358             :     {
     359             :         int bSuccess;
     360         141 :         const float fNoData = static_cast<float>(GetNoDataValue(&bSuccess));
     361        9024 :         for (int i = 0; i < nBlockXSize; i++)
     362             :         {
     363             :             unsigned short raw1;
     364        8883 :             memcpy(&raw1, pabyRecord + 2 * i, 2);
     365        8883 :             CPL_LSBPTR16(&raw1);
     366        8883 :             if (raw1 == 0)
     367             :             {
     368        1143 :                 static_cast<float *>(pImage)[i] = fNoData;  // null value
     369             :             }
     370             :             else
     371             :             {
     372        7740 :                 static_cast<float *>(pImage)[i] =
     373        7740 :                     static_cast<float>(dfOffset + ((raw1 - 1) * dfScale));
     374             :             }
     375         141 :         }
     376             :     }
     377         141 :     else if (nBand == 1)  // red values
     378             :     {
     379        3008 :         for (int i = 0; i < nBlockXSize; i++)
     380             :         {
     381             :             unsigned short raw1;
     382        2961 :             memcpy(&raw1, pabyRecord + 2 * i, 2);
     383        2961 :             CPL_LSBPTR16(&raw1);
     384        2961 :             static_cast<unsigned char *>(pImage)[i] =
     385        2961 :                 poGDS->ColorMap[raw1 / 16].r;
     386             :         }
     387             :     }
     388          94 :     else if (nBand == 2)  // green
     389             :     {
     390        3008 :         for (int i = 0; i < nBlockXSize; i++)
     391             :         {
     392             :             unsigned short raw1;
     393        2961 :             memcpy(&raw1, pabyRecord + 2 * i, 2);
     394        2961 :             CPL_LSBPTR16(&raw1);
     395        2961 :             static_cast<unsigned char *>(pImage)[i] =
     396        2961 :                 poGDS->ColorMap[raw1 / 16].g;
     397             :         }
     398             :     }
     399          47 :     else if (nBand == 3)  // blue
     400             :     {
     401        3008 :         for (int i = 0; i < nBlockXSize; i++)
     402             :         {
     403             :             unsigned short raw1;
     404        2961 :             memcpy(&raw1, pabyRecord + 2 * i, 2);
     405        2961 :             CPL_LSBPTR16(&raw1);
     406        2961 :             static_cast<unsigned char *>(pImage)[i] =
     407        2961 :                 poGDS->ColorMap[raw1 / 16].b;
     408             :         }
     409             :     }
     410             :     else
     411             :     {
     412           0 :         CPLError(CE_Failure, CPLE_IllegalArg, "No band number %d", nBand);
     413           0 :         CPLFree(pabyRecord);
     414           0 :         return CE_Failure;
     415             :     }
     416             : 
     417         282 :     CPLFree(pabyRecord);
     418             : 
     419         282 :     return CE_None;
     420             : }
     421             : 
     422             : /************************************************************************/
     423             : /* ==================================================================== */
     424             : /*                             NWT_GRDDataset                           */
     425             : /* ==================================================================== */
     426             : /************************************************************************/
     427             : 
     428          26 : NWT_GRDDataset::NWT_GRDDataset()
     429          26 :     : fp(nullptr), pGrd(nullptr), bUpdateHeader(false)
     430             : {
     431             :     // poCT = NULL;
     432      106522 :     for (size_t i = 0; i < CPL_ARRAYSIZE(ColorMap); ++i)
     433             :     {
     434      106496 :         ColorMap[i].r = 0;
     435      106496 :         ColorMap[i].g = 0;
     436      106496 :         ColorMap[i].b = 0;
     437             :     }
     438          26 : }
     439             : 
     440             : /************************************************************************/
     441             : /*                            ~NWT_GRDDataset()                         */
     442             : /************************************************************************/
     443             : 
     444          52 : NWT_GRDDataset::~NWT_GRDDataset()
     445             : {
     446             : 
     447             :     // Make sure any changes to the header etc are written
     448             :     // if we are in update mode.
     449          26 :     if (eAccess == GA_Update)
     450             :     {
     451          14 :         NWT_GRDDataset::FlushCache(true);
     452             :     }
     453          26 :     if (pGrd)
     454             :     {
     455          26 :         pGrd->fp = nullptr;  // this prevents nwtCloseGrid from closing the fp
     456          26 :         nwtCloseGrid(pGrd);
     457             :     }
     458          26 :     if (m_poSRS)
     459           1 :         m_poSRS->Release();
     460             : 
     461          26 :     if (fp != nullptr)
     462          25 :         VSIFCloseL(fp);
     463          52 : }
     464             : 
     465             : /************************************************************************/
     466             : /*                 ~FlushCache(bool bAtClosing)                         */
     467             : /************************************************************************/
     468          17 : CPLErr NWT_GRDDataset::FlushCache(bool bAtClosing)
     469             : {
     470             :     // Ensure the header and TAB file are up to date
     471          17 :     if (bUpdateHeader && pGrd)
     472             :     {
     473             : #ifndef NO_MITAB_SUPPORT
     474           3 :         UpdateHeader();
     475             : #endif
     476             :     }
     477             : 
     478             :     // Call the parent method
     479          17 :     return GDALPamDataset::FlushCache(bAtClosing);
     480             : }
     481             : 
     482             : /************************************************************************/
     483             : /*                          GetGeoTransform()                           */
     484             : /************************************************************************/
     485             : 
     486           3 : CPLErr NWT_GRDDataset::GetGeoTransform(GDALGeoTransform &gt) const
     487             : {
     488           3 :     gt[0] = pGrd->dfMinX - (pGrd->dfStepSize * 0.5);
     489           3 :     gt[3] = pGrd->dfMaxY + (pGrd->dfStepSize * 0.5);
     490           3 :     gt[1] = pGrd->dfStepSize;
     491           3 :     gt[2] = 0.0;
     492             : 
     493           3 :     gt[4] = 0.0;
     494           3 :     gt[5] = -1 * pGrd->dfStepSize;
     495             : 
     496           3 :     return CE_None;
     497             : }
     498             : 
     499             : /************************************************************************/
     500             : /*                          SetGeoTransform()                           */
     501             : /************************************************************************/
     502             : 
     503           3 : CPLErr NWT_GRDDataset::SetGeoTransform(const GDALGeoTransform &gt)
     504             : {
     505           3 :     if (gt[2] != 0.0 || gt[4] != 0.0)
     506             :     {
     507             : 
     508           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     509             :                  "GRD datasets do not support skew/rotation");
     510           0 :         return CE_Failure;
     511             :     }
     512           3 :     pGrd->dfStepSize = gt[1];
     513             : 
     514             :     // GRD format sets the min/max coordinates to the centre of the
     515             :     // cell; We must account for this when copying the GDAL geotransform
     516             :     // which references the top left corner
     517           3 :     pGrd->dfMinX = gt[0] + (pGrd->dfStepSize * 0.5);
     518           3 :     pGrd->dfMaxY = gt[3] - (pGrd->dfStepSize * 0.5);
     519             : 
     520             :     // Now set the miny and maxx
     521           3 :     pGrd->dfMaxX = pGrd->dfMinX + (pGrd->dfStepSize * (nRasterXSize - 1));
     522           3 :     pGrd->dfMinY = pGrd->dfMaxY - (pGrd->dfStepSize * (nRasterYSize - 1));
     523           3 :     bUpdateHeader = true;
     524             : 
     525           3 :     return CE_None;
     526             : }
     527             : 
     528             : /************************************************************************/
     529             : /*                          GetSpatialRef()                             */
     530             : /************************************************************************/
     531           1 : const OGRSpatialReference *NWT_GRDDataset::GetSpatialRef() const
     532             : {
     533             : 
     534             :     // First try getting it from the PAM dataset
     535           1 :     const OGRSpatialReference *poSRS = GDALPamDataset::GetSpatialRef();
     536           1 :     if (poSRS)
     537           0 :         return poSRS;
     538             : 
     539           1 :     if (m_poSRS)
     540           0 :         return m_poSRS;
     541             : 
     542             :     // If that isn't possible, read it from the GRD file. This may be a less
     543             :     //  complete projection string.
     544             :     OGRSpatialReference *poSpatialRef =
     545           1 :         MITABCoordSys2SpatialRef(pGrd->cMICoordSys);
     546           1 :     m_poSRS = poSpatialRef;
     547             : 
     548           1 :     return m_poSRS;
     549             : }
     550             : 
     551             : #ifndef NO_MITAB_SUPPORT
     552             : /************************************************************************/
     553             : /*                            SetSpatialRef()                           */
     554             : /************************************************************************/
     555             : 
     556           3 : CPLErr NWT_GRDDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
     557             : {
     558             : 
     559           3 :     char *psTABProj = MITABSpatialRef2CoordSys(poSRS);
     560           3 :     strncpy(pGrd->cMICoordSys, psTABProj, sizeof(pGrd->cMICoordSys) - 1);
     561           3 :     pGrd->cMICoordSys[255] = '\0';
     562             : 
     563             :     // Free temp projection.
     564           3 :     CPLFree(psTABProj);
     565             :     // Set projection in PAM dataset, so that
     566             :     // GDAL can always retrieve the complete projection.
     567           3 :     GDALPamDataset::SetSpatialRef(poSRS);
     568           3 :     bUpdateHeader = true;
     569             : 
     570           3 :     return CE_None;
     571             : }
     572             : #endif
     573             : 
     574             : /************************************************************************/
     575             : /*                              Identify()                              */
     576             : /************************************************************************/
     577             : 
     578       58532 : int NWT_GRDDataset::Identify(GDALOpenInfo *poOpenInfo)
     579             : {
     580             :     /* -------------------------------------------------------------------- */
     581             :     /*  Look for the header                                                 */
     582             :     /* -------------------------------------------------------------------- */
     583       58532 :     if (poOpenInfo->nHeaderBytes < 1024)
     584       56588 :         return FALSE;
     585             : 
     586        1944 :     if (poOpenInfo->pabyHeader[0] != 'H' || poOpenInfo->pabyHeader[1] != 'G' ||
     587          27 :         poOpenInfo->pabyHeader[2] != 'P' || poOpenInfo->pabyHeader[3] != 'C' ||
     588          27 :         poOpenInfo->pabyHeader[4] != '1')
     589        1920 :         return FALSE;
     590             : 
     591          24 :     return TRUE;
     592             : }
     593             : 
     594             : /************************************************************************/
     595             : /*                                Open()                                */
     596             : /************************************************************************/
     597          12 : GDALDataset *NWT_GRDDataset::Open(GDALOpenInfo *poOpenInfo)
     598             : {
     599          12 :     if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
     600           0 :         return nullptr;
     601             : 
     602             :     /* -------------------------------------------------------------------- */
     603             :     /*      Create a corresponding GDALDataset.                             */
     604             :     /* -------------------------------------------------------------------- */
     605          12 :     int nBandsToCreate = 0;
     606             : 
     607          12 :     NWT_GRDDataset *poDS = new NWT_GRDDataset();
     608             : 
     609          12 :     poDS->fp = poOpenInfo->fpL;
     610          12 :     poOpenInfo->fpL = nullptr;
     611             : 
     612          12 :     if (poOpenInfo->eAccess == GA_Update)
     613             :     {
     614           0 :         nBandsToCreate = 1;
     615             :     }
     616             :     else
     617             :     {
     618          12 :         nBandsToCreate = atoi(CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
     619             :                                                    "BAND_COUNT", "4"));
     620          12 :         if (nBandsToCreate != 1 && nBandsToCreate != 4)
     621             :         {
     622           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Wrong value for BAND_COUNT");
     623           0 :             delete poDS;
     624           0 :             return nullptr;
     625             :         }
     626             :     }
     627          12 :     poDS->eAccess = poOpenInfo->eAccess;
     628             : 
     629             :     /* -------------------------------------------------------------------- */
     630             :     /*      Read the header.                                                */
     631             :     /* -------------------------------------------------------------------- */
     632          12 :     VSIFSeekL(poDS->fp, 0, SEEK_SET);
     633          12 :     VSIFReadL(poDS->abyHeader, 1, 1024, poDS->fp);
     634          12 :     poDS->pGrd = static_cast<NWT_GRID *>(calloc(1, sizeof(NWT_GRID)));
     635          12 :     if (!poDS->pGrd)
     636             :     {
     637           0 :         delete poDS;
     638           0 :         return nullptr;
     639             :     }
     640             : 
     641          12 :     poDS->pGrd->fp = poDS->fp;
     642             : 
     643          24 :     if (!nwt_ParseHeader(poDS->pGrd, poDS->abyHeader) ||
     644          12 :         !GDALCheckDatasetDimensions(poDS->pGrd->nXSide, poDS->pGrd->nYSide))
     645             :     {
     646           0 :         delete poDS;
     647           0 :         return nullptr;
     648             :     }
     649             : 
     650          12 :     poDS->nRasterXSize = poDS->pGrd->nXSide;
     651          12 :     poDS->nRasterYSize = poDS->pGrd->nYSide;
     652             : 
     653             :     // create a colorTable
     654             :     // if( poDS->pGrd->iNumColorInflections > 0 )
     655             :     //   poDS->CreateColorTable();
     656          12 :     nwt_LoadColors(poDS->ColorMap, 4096, poDS->pGrd);
     657             :     /* -------------------------------------------------------------------- */
     658             :     /*      Create band information objects.                                */
     659             :     /* If opening in read-only mode, then we create 4 bands (RGBZ)          */
     660             :     /* with data values being available in band 4. If opening in update mode*/
     661             :     /* we create 1 band (the data values). This is because in reality, there*/
     662             :     /* is only 1 band stored on disk. The RGB bands are 'virtual' - derived */
     663             :     /* from the data values on the fly                                      */
     664             :     /* -------------------------------------------------------------------- */
     665          54 :     for (int i = 0; i < nBandsToCreate; ++i)
     666             :     {
     667          42 :         poDS->SetBand(i + 1,
     668          42 :                       new NWT_GRDRasterBand(poDS, i + 1, nBandsToCreate));
     669             :     }
     670             : 
     671             :     /* -------------------------------------------------------------------- */
     672             :     /*      Initialize any PAM information.                                 */
     673             :     /* -------------------------------------------------------------------- */
     674          12 :     poDS->SetDescription(poOpenInfo->pszFilename);
     675          12 :     poDS->TryLoadXML();
     676             : 
     677             :     /* -------------------------------------------------------------------- */
     678             :     /*      Check for external overviews.                                   */
     679             :     /* -------------------------------------------------------------------- */
     680          24 :     poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename,
     681          12 :                                 poOpenInfo->GetSiblingFiles());
     682             : 
     683          12 :     return poDS;
     684             : }
     685             : 
     686             : #ifndef NO_MITAB_SUPPORT
     687             : /************************************************************************/
     688             : /*                                UpdateHeader()                        */
     689             : /************************************************************************/
     690          16 : int NWT_GRDDataset::UpdateHeader()
     691             : {
     692          16 :     int iStatus = 0;
     693          16 :     TABRawBinBlock *poHeaderBlock = new TABRawBinBlock(TABReadWrite, TRUE);
     694          16 :     poHeaderBlock->InitNewBlock(fp, 1024);
     695             : 
     696             :     // Write the header string
     697          16 :     poHeaderBlock->WriteBytes(5, reinterpret_cast<const GByte *>("HGPC1\0"));
     698             : 
     699             :     // Version number
     700          16 :     poHeaderBlock->WriteFloat(pGrd->fVersion);
     701             : 
     702             :     // Dimensions
     703          16 :     poHeaderBlock->WriteInt16(static_cast<GInt16>(pGrd->nXSide));
     704          16 :     poHeaderBlock->WriteInt16(static_cast<GInt16>(pGrd->nYSide));
     705             : 
     706             :     // Extents
     707          16 :     poHeaderBlock->WriteDouble(pGrd->dfMinX);
     708          16 :     poHeaderBlock->WriteDouble(pGrd->dfMaxX);
     709          16 :     poHeaderBlock->WriteDouble(pGrd->dfMinY);
     710          16 :     poHeaderBlock->WriteDouble(pGrd->dfMaxY);
     711             : 
     712             :     // Z value range
     713          16 :     poHeaderBlock->WriteFloat(pGrd->fZMin);
     714          16 :     poHeaderBlock->WriteFloat(pGrd->fZMax);
     715          16 :     poHeaderBlock->WriteFloat(pGrd->fZMinScale);
     716          16 :     poHeaderBlock->WriteFloat(pGrd->fZMaxScale);
     717             : 
     718             :     // Description String
     719          16 :     int nChar = static_cast<int>(strlen(pGrd->cDescription));
     720          16 :     poHeaderBlock->WriteBytes(
     721          16 :         nChar, reinterpret_cast<const GByte *>(pGrd->cDescription));
     722          16 :     poHeaderBlock->WriteZeros(32 - nChar);
     723             : 
     724             :     // Unit Name String
     725          16 :     nChar = static_cast<int>(strlen(pGrd->cZUnits));
     726          16 :     poHeaderBlock->WriteBytes(nChar,
     727          16 :                               reinterpret_cast<const GByte *>(pGrd->cZUnits));
     728          16 :     poHeaderBlock->WriteZeros(32 - nChar);
     729             : 
     730             :     // Ignore 126 - 141 as unknown usage
     731          16 :     poHeaderBlock->WriteZeros(15);
     732             : 
     733             :     // Hill shading
     734          16 :     poHeaderBlock->WriteInt16(pGrd->bHillShadeExists ? 1 : 0);
     735          16 :     poHeaderBlock->WriteInt16(0);
     736             : 
     737          16 :     poHeaderBlock->WriteByte(pGrd->cHillShadeBrightness);
     738          16 :     poHeaderBlock->WriteByte(pGrd->cHillShadeContrast);
     739             : 
     740             :     // Ignore 147 - 257 as unknown usage
     741          16 :     poHeaderBlock->WriteZeros(110);
     742             : 
     743             :     // Write spatial reference
     744          16 :     poHeaderBlock->WriteBytes(
     745          16 :         static_cast<int>(strlen(pGrd->cMICoordSys)),
     746          16 :         reinterpret_cast<const GByte *>(pGrd->cMICoordSys));
     747          16 :     poHeaderBlock->WriteZeros(256 -
     748          16 :                               static_cast<int>(strlen(pGrd->cMICoordSys)));
     749             : 
     750             :     // Unit code
     751          16 :     poHeaderBlock->WriteByte(static_cast<GByte>(pGrd->iZUnits));
     752             : 
     753             :     // Info on shading
     754          16 :     GByte byDisplayStatus = 0;
     755          16 :     if (pGrd->bShowHillShade)
     756             :     {
     757           0 :         byDisplayStatus |= 1 << 6;
     758             :     }
     759          16 :     if (pGrd->bShowGradient)
     760             :     {
     761           0 :         byDisplayStatus |= 1 << 7;
     762             :     }
     763             : 
     764          16 :     poHeaderBlock->WriteByte(byDisplayStatus);
     765          16 :     poHeaderBlock->WriteInt16(0);  // Data Type?
     766             : 
     767             :     // Colour inflections
     768          16 :     poHeaderBlock->WriteInt16(pGrd->iNumColorInflections);
     769          64 :     for (int i = 0; i < pGrd->iNumColorInflections; i++)
     770             :     {
     771          48 :         poHeaderBlock->WriteFloat(pGrd->stInflection[i].zVal);
     772          48 :         poHeaderBlock->WriteByte(pGrd->stInflection[i].r);
     773          48 :         poHeaderBlock->WriteByte(pGrd->stInflection[i].g);
     774          48 :         poHeaderBlock->WriteByte(pGrd->stInflection[i].b);
     775             :     }
     776             : 
     777             :     // Fill in unused blanks
     778          16 :     poHeaderBlock->WriteZeros((966 - poHeaderBlock->GetCurAddress()));
     779             : 
     780             :     // Azimuth and Inclination
     781          16 :     poHeaderBlock->WriteFloat(pGrd->fHillShadeAzimuth);
     782          16 :     poHeaderBlock->WriteFloat(pGrd->fHillShadeAngle);
     783             : 
     784             :     // Write to disk
     785          16 :     iStatus = poHeaderBlock->CommitToFile();
     786             : 
     787          16 :     delete poHeaderBlock;
     788             : 
     789             :     // Update the TAB file to catch any changes
     790          16 :     if (WriteTab() != 0)
     791          10 :         iStatus = -1;
     792             : 
     793          16 :     return iStatus;
     794             : }
     795             : 
     796          16 : int NWT_GRDDataset::WriteTab()
     797             : {
     798             :     // Create the filename for the .tab file.
     799          32 :     const std::string sTabFile(CPLResetExtensionSafe(pGrd->szFileName, "tab"));
     800             : 
     801          16 :     VSILFILE *tabfp = VSIFOpenL(sTabFile.c_str(), "wt");
     802          16 :     if (tabfp == nullptr)
     803             :     {
     804           0 :         CPLError(CE_Failure, CPLE_FileIO, "Failed to create file `%s'",
     805             :                  sTabFile.c_str());
     806           0 :         return -1;
     807             :     }
     808             : 
     809          16 :     bool bOK = true;
     810          16 :     bOK &= VSIFPrintfL(tabfp, "!table\n") > 0;
     811          16 :     bOK &= VSIFPrintfL(tabfp, "!version 500\n") > 0;
     812          16 :     bOK &= VSIFPrintfL(tabfp, "!charset %s\n", "Neutral") > 0;
     813          16 :     bOK &= VSIFPrintfL(tabfp, "\n") > 0;
     814             : 
     815          16 :     bOK &= VSIFPrintfL(tabfp, "Definition Table\n") > 0;
     816          32 :     const std::string path(pGrd->szFileName);
     817          16 :     const std::string basename = path.substr(path.find_last_of("/\\") + 1);
     818          16 :     bOK &= VSIFPrintfL(tabfp, "  File \"%s\"\n", basename.c_str()) > 0;
     819          16 :     bOK &= VSIFPrintfL(tabfp, "  Type \"RASTER\"\n") > 0;
     820             : 
     821          16 :     double dMapUnitsPerPixel =
     822          16 :         (pGrd->dfMaxX - pGrd->dfMinX) / (static_cast<double>(pGrd->nXSide) - 1);
     823          16 :     double dShift = dMapUnitsPerPixel / 2.0;
     824             : 
     825          32 :     bOK &= VSIFPrintfL(tabfp, "  (%f,%f) (%d,%d) Label \"Pt 1\",\n",
     826          16 :                        pGrd->dfMinX - dShift, pGrd->dfMaxY + dShift, 0, 0) > 0;
     827          32 :     bOK &= VSIFPrintfL(tabfp, "  (%f,%f) (%d,%d) Label \"Pt 2\",\n",
     828          16 :                        pGrd->dfMaxX - dShift, pGrd->dfMinY + dShift,
     829          16 :                        pGrd->nXSide - 1, pGrd->nYSide - 1) > 0;
     830          32 :     bOK &= VSIFPrintfL(tabfp, "  (%f,%f) (%d,%d) Label \"Pt 3\"\n",
     831          16 :                        pGrd->dfMinX - dShift, pGrd->dfMinY + dShift, 0,
     832          16 :                        pGrd->nYSide - 1) > 0;
     833             : 
     834          16 :     bOK &= VSIFPrintfL(tabfp, "  CoordSys %s\n", pGrd->cMICoordSys) > 0;
     835          16 :     bOK &= VSIFPrintfL(tabfp, "  Units \"m\"\n") > 0;
     836             : 
     837             :     // Raster Styles.
     838             : 
     839             :     // Raster is a grid, which is style 6.
     840          16 :     bOK &= VSIFPrintfL(tabfp, "  RasterStyle 6 1\n") > 0;
     841             : 
     842             :     // Brightness - style 1
     843          16 :     if (pGrd->style.iBrightness > 0)
     844             :     {
     845          32 :         bOK &= VSIFPrintfL(tabfp, "  RasterStyle 1 %d\n",
     846          16 :                            pGrd->style.iBrightness) > 0;
     847             :     }
     848             : 
     849             :     // Contrast - style 2
     850          16 :     if (pGrd->style.iContrast > 0)
     851             :     {
     852          32 :         bOK &= VSIFPrintfL(tabfp, "  RasterStyle 2 %d\n",
     853          16 :                            pGrd->style.iContrast) > 0;
     854             :     }
     855             : 
     856             :     // Greyscale - style 3; only need to write if TRUE
     857          16 :     if (pGrd->style.bGreyscale == TRUE)
     858             :     {
     859           0 :         bOK &= VSIFPrintfL(tabfp, "  RasterStyle 3 1\n") > 0;
     860             :     }
     861             : 
     862             :     // Flag to render one colour transparent - style 4
     863          16 :     if (pGrd->style.bTransparent == TRUE)
     864             :     {
     865           0 :         bOK &= VSIFPrintfL(tabfp, "  RasterStyle 4 1\n") > 0;
     866           0 :         if (pGrd->style.iTransColour > 0)
     867             :         {
     868           0 :             bOK &= VSIFPrintfL(tabfp, "  RasterStyle 7 %d\n",
     869           0 :                                pGrd->style.iTransColour) > 0;
     870             :         }
     871             :     }
     872             : 
     873             :     // Transparency of immage
     874          16 :     if (pGrd->style.iTranslucency > 0)
     875             :     {
     876           0 :         bOK &= VSIFPrintfL(tabfp, "  RasterStyle 8 %d\n",
     877           0 :                            pGrd->style.iTranslucency) > 0;
     878             :     }
     879             : 
     880          16 :     bOK &= VSIFPrintfL(tabfp, "begin_metadata\n") > 0;
     881          16 :     bOK &= VSIFPrintfL(tabfp, "\"\\MapInfo\" = \"\"\n") > 0;
     882          16 :     bOK &= VSIFPrintfL(tabfp, "\"\\Vm\" = \"\"\n") > 0;
     883          16 :     bOK &= VSIFPrintfL(tabfp, "\"\\Vm\\Grid\" = \"Numeric\"\n") > 0;
     884          16 :     bOK &= VSIFPrintfL(tabfp, "\"\\Vm\\GridName\" = \"%s\"\n",
     885          16 :                        basename.c_str()) > 0;
     886          16 :     bOK &= VSIFPrintfL(tabfp, "\"\\IsReadOnly\" = \"FALSE\"\n") > 0;
     887          16 :     bOK &= VSIFPrintfL(tabfp, "end_metadata\n") > 0;
     888             : 
     889          16 :     if (VSIFCloseL(tabfp) != 0)
     890           0 :         bOK = false;
     891             : 
     892          16 :     return (bOK) ? 0 : -1;
     893             : }
     894             : 
     895             : /************************************************************************/
     896             : /*                                Create()                              */
     897             : /************************************************************************/
     898          57 : GDALDataset *NWT_GRDDataset::Create(const char *pszFilename, int nXSize,
     899             :                                     int nYSize, int nBandsIn,
     900             :                                     GDALDataType eType, char **papszParamList)
     901             : {
     902          57 :     if (nBandsIn != 1)
     903             :     {
     904          18 :         CPLError(CE_Failure, CPLE_FileIO,
     905             :                  "Only single band datasets are supported for writing");
     906          18 :         return nullptr;
     907             :     }
     908          39 :     if (eType != GDT_Float32)
     909             :     {
     910          25 :         CPLError(CE_Failure, CPLE_FileIO,
     911             :                  "Float32 is the only supported data type");
     912          25 :         return nullptr;
     913             :     }
     914          14 :     NWT_GRDDataset *poDS = new NWT_GRDDataset();
     915          14 :     poDS->eAccess = GA_Update;
     916          14 :     poDS->pGrd = static_cast<NWT_GRID *>(calloc(1, sizeof(NWT_GRID)));
     917          14 :     if (!poDS->pGrd)
     918             :     {
     919           0 :         delete poDS;
     920           0 :         return nullptr;
     921             :     }
     922             : 
     923             :     // We currently only support GRD grid types (could potentially support GRC
     924             :     // in the papszParamList). Also only support GDT_Float32 as the data type.
     925             :     // GRD format allows for data to be stretched to 32bit or 16bit integers on
     926             :     // disk, so it would be feasible to support other data types
     927          14 :     poDS->pGrd->cFormat = 0x00;
     928             : 
     929             :     // File version
     930          14 :     poDS->pGrd->fVersion = 2.0;
     931             : 
     932             :     // Dimensions
     933          14 :     poDS->pGrd->nXSide = nXSize;
     934          14 :     poDS->pGrd->nYSide = nYSize;
     935          14 :     poDS->nRasterXSize = nXSize;
     936          14 :     poDS->nRasterYSize = nYSize;
     937             : 
     938             :     // Some default values to get started with. These will
     939             :     // be altered when SetGeoTransform is called.
     940          14 :     poDS->pGrd->dfMinX = -2E+307;
     941          14 :     poDS->pGrd->dfMinY = -2E+307;
     942          14 :     poDS->pGrd->dfMaxX = 2E+307;
     943          14 :     poDS->pGrd->dfMaxY = 2E+307;
     944             : 
     945             :     float fZMin, fZMax;
     946             :     // See if the user passed the min/max values
     947          14 :     if (CSLFetchNameValue(papszParamList, "ZMIN") == nullptr)
     948             :     {
     949           1 :         fZMin = static_cast<float>(-2E+37);
     950             :     }
     951             :     else
     952             :     {
     953          13 :         fZMin = static_cast<float>(
     954          13 :             CPLAtof(CSLFetchNameValue(papszParamList, "ZMIN")));
     955             :     }
     956             : 
     957          14 :     if (CSLFetchNameValue(papszParamList, "ZMAX") == nullptr)
     958             :     {
     959           1 :         fZMax = static_cast<float>(2E+38);
     960             :     }
     961             :     else
     962             :     {
     963          13 :         fZMax = static_cast<float>(
     964          13 :             CPLAtof(CSLFetchNameValue(papszParamList, "ZMAX")));
     965             :     }
     966             : 
     967          14 :     poDS->pGrd->fZMin = fZMin;
     968          14 :     poDS->pGrd->fZMax = fZMax;
     969             :     // pGrd->dfStepSize = (pGrd->dfMaxX - pGrd->dfMinX) / (pGrd->nXSide - 1);
     970          14 :     poDS->pGrd->fZMinScale = fZMin;
     971          14 :     poDS->pGrd->fZMaxScale = fZMax;
     972             :     // poDS->pGrd->iZUnits
     973          14 :     memset(poDS->pGrd->cZUnits, 0, 32);
     974          14 :     memset(poDS->pGrd->cMICoordSys, 0, 256);
     975             : 
     976             :     // Some default colour inflections; Basic scale from blue to red
     977          14 :     poDS->pGrd->iNumColorInflections = 3;
     978             : 
     979             :     // Lowest inflection
     980          14 :     poDS->pGrd->stInflection[0].zVal = poDS->pGrd->fZMin;
     981          14 :     poDS->pGrd->stInflection[0].r = 0;
     982          14 :     poDS->pGrd->stInflection[0].g = 0;
     983          14 :     poDS->pGrd->stInflection[0].b = 255;
     984             : 
     985             :     // Mean inflection
     986          14 :     poDS->pGrd->stInflection[1].zVal =
     987          14 :         (poDS->pGrd->fZMax - poDS->pGrd->fZMin) / 2;
     988          14 :     poDS->pGrd->stInflection[1].r = 255;
     989          14 :     poDS->pGrd->stInflection[1].g = 255;
     990          14 :     poDS->pGrd->stInflection[1].b = 0;
     991             : 
     992             :     // Highest inflection
     993          14 :     poDS->pGrd->stInflection[2].zVal = poDS->pGrd->fZMax;
     994          14 :     poDS->pGrd->stInflection[2].r = 255;
     995          14 :     poDS->pGrd->stInflection[2].g = 0;
     996          14 :     poDS->pGrd->stInflection[2].b = 0;
     997             : 
     998          14 :     poDS->pGrd->bHillShadeExists = FALSE;
     999          14 :     poDS->pGrd->bShowGradient = FALSE;
    1000          14 :     poDS->pGrd->bShowHillShade = FALSE;
    1001          14 :     poDS->pGrd->cHillShadeBrightness = 0;
    1002          14 :     poDS->pGrd->cHillShadeContrast = 0;
    1003          14 :     poDS->pGrd->fHillShadeAzimuth = 0;
    1004          14 :     poDS->pGrd->fHillShadeAngle = 0;
    1005             : 
    1006             :     // Set the raster style settings. These aren't used anywhere other than to
    1007             :     // write the TAB file
    1008          14 :     if (CSLFetchNameValue(papszParamList, "BRIGHTNESS") == nullptr)
    1009             :     {
    1010          14 :         poDS->pGrd->style.iBrightness = 50;
    1011             :     }
    1012             :     else
    1013             :     {
    1014           0 :         poDS->pGrd->style.iBrightness =
    1015           0 :             atoi(CSLFetchNameValue(papszParamList, "BRIGHTNESS"));
    1016             :     }
    1017             : 
    1018          14 :     if (CSLFetchNameValue(papszParamList, "CONTRAST") == nullptr)
    1019             :     {
    1020          14 :         poDS->pGrd->style.iContrast = 50;
    1021             :     }
    1022             :     else
    1023             :     {
    1024           0 :         poDS->pGrd->style.iContrast =
    1025           0 :             atoi(CSLFetchNameValue(papszParamList, "CONTRAST"));
    1026             :     }
    1027             : 
    1028          14 :     if (CSLFetchNameValue(papszParamList, "TRANSCOLOR") == nullptr)
    1029             :     {
    1030          14 :         poDS->pGrd->style.iTransColour = 0;
    1031             :     }
    1032             :     else
    1033             :     {
    1034           0 :         poDS->pGrd->style.iTransColour =
    1035           0 :             atoi(CSLFetchNameValue(papszParamList, "TRANSCOLOR"));
    1036             :     }
    1037             : 
    1038          14 :     if (CSLFetchNameValue(papszParamList, "TRANSLUCENCY") == nullptr)
    1039             :     {
    1040          14 :         poDS->pGrd->style.iTranslucency = 0;
    1041             :     }
    1042             :     else
    1043             :     {
    1044           0 :         poDS->pGrd->style.iTranslucency =
    1045           0 :             atoi(CSLFetchNameValue(papszParamList, "TRANSLUCENCY"));
    1046             :     }
    1047             : 
    1048          14 :     poDS->pGrd->style.bGreyscale = FALSE;
    1049          14 :     poDS->pGrd->style.bGrey = FALSE;
    1050          14 :     poDS->pGrd->style.bColour = FALSE;
    1051          14 :     poDS->pGrd->style.bTransparent = FALSE;
    1052             : 
    1053             :     // Open the grid file
    1054          14 :     poDS->fp = VSIFOpenL(pszFilename, "wb");
    1055          14 :     if (poDS->fp == nullptr)
    1056             :     {
    1057           1 :         CPLError(CE_Failure, CPLE_FileIO, "Failed to create GRD file");
    1058           1 :         delete poDS;
    1059           1 :         return nullptr;
    1060             :     }
    1061             : 
    1062          13 :     poDS->pGrd->fp = poDS->fp;
    1063          13 :     strncpy(poDS->pGrd->szFileName, pszFilename,
    1064             :             sizeof(poDS->pGrd->szFileName) - 1);
    1065          13 :     poDS->pGrd->szFileName[sizeof(poDS->pGrd->szFileName) - 1] = '\0';
    1066             : 
    1067             :     // Seek to the start of the file and enter the default header info
    1068          13 :     VSIFSeekL(poDS->fp, 0, SEEK_SET);
    1069          13 :     if (poDS->UpdateHeader() != 0)
    1070             :     {
    1071          10 :         CPLError(CE_Failure, CPLE_FileIO, "Failed to create GRD file");
    1072          10 :         delete poDS;
    1073          10 :         return nullptr;
    1074             :     }
    1075             : 
    1076             :     /* -------------------------------------------------------------------- */
    1077             :     /*      Create band information objects;                                */
    1078             :     /*      Only 1 band is allowed                                          */
    1079             :     /* -------------------------------------------------------------------- */
    1080           3 :     poDS->SetBand(1, new NWT_GRDRasterBand(poDS, 1, 1));  // z
    1081             : 
    1082           3 :     poDS->oOvManager.Initialize(poDS, pszFilename);
    1083           3 :     poDS->FlushCache(false);  // Write the header to disk.
    1084             : 
    1085           3 :     return poDS;
    1086             : }
    1087             : 
    1088             : /************************************************************************/
    1089             : /*                                CreateCopy()                          */
    1090             : /************************************************************************/
    1091          30 : GDALDataset *NWT_GRDDataset::CreateCopy(const char *pszFilename,
    1092             :                                         GDALDataset *poSrcDS, int bStrict,
    1093             :                                         char **papszOptions,
    1094             :                                         GDALProgressFunc pfnProgress,
    1095             :                                         void *pProgressData)
    1096             : {
    1097             : 
    1098          30 :     if (poSrcDS->GetRasterCount() != 1)
    1099             :     {
    1100           5 :         CPLError(CE_Failure, CPLE_FileIO,
    1101             :                  "Only single band datasets are supported for writing");
    1102           5 :         return nullptr;
    1103             :     }
    1104             : 
    1105          25 :     char **tmpOptions = CSLDuplicate(papszOptions);
    1106             : 
    1107             :     /*
    1108             :      * Compute the statistics if ZMAX and ZMIN are not provided
    1109             :      */
    1110          25 :     double dfMin = 0.0;
    1111          25 :     double dfMax = 0.0;
    1112          25 :     double dfMean = 0.0;
    1113          25 :     double dfStdDev = 0.0;
    1114          25 :     GDALRasterBand *pBand = poSrcDS->GetRasterBand(1);
    1115          25 :     char sMax[10] = {};
    1116          25 :     char sMin[10] = {};
    1117             : 
    1118          25 :     if ((CSLFetchNameValue(papszOptions, "ZMAX") == nullptr) ||
    1119           0 :         (CSLFetchNameValue(papszOptions, "ZMIN") == nullptr))
    1120             :     {
    1121          25 :         CPL_IGNORE_RET_VAL(pBand->GetStatistics(FALSE, TRUE, &dfMin, &dfMax,
    1122          25 :                                                 &dfMean, &dfStdDev));
    1123             :     }
    1124             : 
    1125          25 :     if (CSLFetchNameValue(papszOptions, "ZMAX") == nullptr)
    1126             :     {
    1127          25 :         CPLsnprintf(sMax, sizeof(sMax), "%f", dfMax);
    1128          25 :         tmpOptions = CSLSetNameValue(tmpOptions, "ZMAX", sMax);
    1129             :     }
    1130          25 :     if (CSLFetchNameValue(papszOptions, "ZMIN") == nullptr)
    1131             :     {
    1132          25 :         CPLsnprintf(sMin, sizeof(sMin), "%f", dfMin);
    1133          25 :         tmpOptions = CSLSetNameValue(tmpOptions, "ZMIN", sMin);
    1134             :     }
    1135             : 
    1136             :     GDALDriver *poDriver =
    1137          25 :         GDALDriver::FromHandle(GDALGetDriverByName("NWT_GRD"));
    1138          25 :     GDALDataset *poDstDS = poDriver->DefaultCreateCopy(
    1139             :         pszFilename, poSrcDS, bStrict, tmpOptions, pfnProgress, pProgressData);
    1140             : 
    1141          25 :     CSLDestroy(tmpOptions);
    1142             : 
    1143          25 :     return poDstDS;
    1144             : }
    1145             : #endif  // NO_MITAB_SUPPORT
    1146             : 
    1147             : /************************************************************************/
    1148             : /*                          GDALRegister_GRD()                          */
    1149             : /************************************************************************/
    1150        2038 : void GDALRegister_NWT_GRD()
    1151             : {
    1152        2038 :     if (GDALGetDriverByName("NWT_GRD") != nullptr)
    1153         283 :         return;
    1154             : 
    1155        1755 :     GDALDriver *poDriver = new GDALDriver();
    1156             : 
    1157        1755 :     poDriver->SetDescription("NWT_GRD");
    1158        1755 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    1159        1755 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
    1160        1755 :                               "Northwood Numeric Grid Format .grd/.tab");
    1161        1755 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/nwtgrd.html");
    1162        1755 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "grd");
    1163        1755 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
    1164             : 
    1165        1755 :     poDriver->SetMetadataItem(
    1166             :         GDAL_DMD_OPENOPTIONLIST,
    1167             :         "<OpenOptionList>"
    1168             :         "    <Option name='BAND_COUNT' type='int' description='1 (Z) or 4 "
    1169             :         "(RGBZ). Only used in read-only mode' default='4'/>"
    1170        1755 :         "</OpenOptionList>");
    1171             : 
    1172        1755 :     poDriver->pfnOpen = NWT_GRDDataset::Open;
    1173        1755 :     poDriver->pfnIdentify = NWT_GRDDataset::Identify;
    1174             : 
    1175             : #ifndef NO_MITAB_SUPPORT
    1176             : 
    1177        1755 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Float32");
    1178        1755 :     poDriver->SetMetadataItem(
    1179             :         GDAL_DMD_CREATIONOPTIONLIST,
    1180             :         "<CreationOptionList>"
    1181             :         "    <Option name='ZMIN' type='float' description='Minimum cell value "
    1182             :         "of raster for defining RGB scaling' default='-2E+37'/>"
    1183             :         "    <Option name='ZMAX' type='float' description='Maximum cell value "
    1184             :         "of raster for defining RGB scaling' default='2E+38'/>"
    1185             :         "    <Option name='BRIGHTNESS' type='int' description='Brightness to "
    1186             :         "be recorded in TAB file. Only affects reading with MapInfo' "
    1187             :         "default='50'/>"
    1188             :         "    <Option name='CONTRAST' type='int' description='Contrast to be "
    1189             :         "recorded in TAB file. Only affects reading with MapInfo' "
    1190             :         "default='50'/>"
    1191             :         "    <Option name='TRANSCOLOR' type='int' description='Transparent "
    1192             :         "color to be recorded in TAB file. Only affects reading with MapInfo' "
    1193             :         "default='0'/>"
    1194             :         "    <Option name='TRANSLUCENCY' type='int' description='Level of "
    1195             :         "translucency to be recorded in TAB file. Only affects reading with "
    1196             :         "MapInfo' default='0'/>"
    1197        1755 :         "</CreationOptionList>");
    1198             : 
    1199        1755 :     poDriver->pfnCreate = NWT_GRDDataset::Create;
    1200        1755 :     poDriver->pfnCreateCopy = NWT_GRDDataset::CreateCopy;
    1201             : #endif
    1202             : 
    1203        1755 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1204             : }

Generated by: LCOV version 1.14