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

Generated by: LCOV version 1.14