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

Generated by: LCOV version 1.14