LCOV - code coverage report
Current view: top level - frmts/zmap - zmapdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 277 340 81.5 %
Date: 2025-02-20 10:14:44 Functions: 14 14 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  ZMap driver
       4             :  * Purpose:  GDALDataset driver for ZMap dataset.
       5             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2011-2012, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_string.h"
      14             : #include "cpl_vsi_virtual.h"
      15             : #include "gdal_frmts.h"
      16             : #include "gdal_pam.h"
      17             : 
      18             : #include <array>
      19             : #include <cmath>
      20             : #include <deque>
      21             : 
      22             : /************************************************************************/
      23             : /* ==================================================================== */
      24             : /*                              ZMapDataset                             */
      25             : /* ==================================================================== */
      26             : /************************************************************************/
      27             : 
      28             : class ZMapRasterBand;
      29             : 
      30          19 : class ZMapDataset final : public GDALPamDataset
      31             : {
      32             :     friend class ZMapRasterBand;
      33             : 
      34             :     VSIVirtualHandleUniquePtr m_fp{};
      35             :     int m_nValuesPerLine = 0;
      36             :     int m_nFieldSize = 0;
      37             :     int m_nDecimalCount = 0;
      38             :     int m_nColNum = -1;
      39             :     double m_dfNoDataValue = 0;
      40             :     vsi_l_offset m_nDataStartOff = 0;
      41             :     std::array<double, 6> m_adfGeoTransform = {{0, 1, 0, 0, 0, 1}};
      42             :     int m_nFirstDataLine = 0;
      43             :     int m_nCurLine = 0;
      44             :     std::deque<double> m_odfQueue{};
      45             : 
      46             :   public:
      47             :     ZMapDataset();
      48             :     virtual ~ZMapDataset();
      49             : 
      50             :     virtual CPLErr GetGeoTransform(double *) override;
      51             : 
      52             :     static GDALDataset *Open(GDALOpenInfo *);
      53             :     static int Identify(GDALOpenInfo *);
      54             :     static GDALDataset *CreateCopy(const char *pszFilename,
      55             :                                    GDALDataset *poSrcDS, int bStrict,
      56             :                                    char **papszOptions,
      57             :                                    GDALProgressFunc pfnProgress,
      58             :                                    void *pProgressData);
      59             : };
      60             : 
      61             : /************************************************************************/
      62             : /* ==================================================================== */
      63             : /*                            ZMapRasterBand                            */
      64             : /* ==================================================================== */
      65             : /************************************************************************/
      66             : 
      67             : class ZMapRasterBand final : public GDALPamRasterBand
      68             : {
      69             :     friend class ZMapDataset;
      70             : 
      71             :   public:
      72             :     explicit ZMapRasterBand(ZMapDataset *);
      73             : 
      74             :     virtual CPLErr IReadBlock(int, int, void *) override;
      75             :     virtual double GetNoDataValue(int *pbSuccess = nullptr) override;
      76             : };
      77             : 
      78             : /************************************************************************/
      79             : /*                           ZMapRasterBand()                           */
      80             : /************************************************************************/
      81             : 
      82          19 : ZMapRasterBand::ZMapRasterBand(ZMapDataset *poDSIn)
      83             : 
      84             : {
      85          19 :     poDS = poDSIn;
      86          19 :     nBand = 1;
      87             : 
      88          19 :     eDataType = GDT_Float64;
      89             : 
      90             :     // The format is column oriented! That is we have first the value of
      91             :     // pixel (col=0, line=0), then the one of (col=0, line=1), etc.
      92          19 :     nBlockXSize = 1;
      93          19 :     nBlockYSize = poDSIn->GetRasterYSize();
      94          19 : }
      95             : 
      96             : /************************************************************************/
      97             : /*                             IReadBlock()                             */
      98             : /************************************************************************/
      99             : 
     100          42 : CPLErr ZMapRasterBand::IReadBlock(int nBlockXOff, CPL_UNUSED int nBlockYOff,
     101             :                                   void *pImage)
     102             : {
     103          42 :     ZMapDataset *poGDS = cpl::down_cast<ZMapDataset *>(poDS);
     104             : 
     105             :     // If seeking backwards in term of columns, reset reading to the first
     106             :     // column
     107          42 :     if (nBlockXOff < poGDS->m_nColNum + 1)
     108             :     {
     109           0 :         poGDS->m_fp->Seek(poGDS->m_nDataStartOff, SEEK_SET);
     110           0 :         poGDS->m_nColNum = -1;
     111           0 :         poGDS->m_nCurLine = poGDS->m_nFirstDataLine;
     112           0 :         poGDS->m_odfQueue.clear();
     113             :     }
     114             : 
     115          42 :     if (nBlockXOff > poGDS->m_nColNum + 1)
     116             :     {
     117           0 :         for (int i = poGDS->m_nColNum + 1; i < nBlockXOff; i++)
     118             :         {
     119           0 :             if (IReadBlock(i, 0, nullptr) != CE_None)
     120           0 :                 return CE_Failure;
     121             :         }
     122             :     }
     123             : 
     124          42 :     int iRow = 0;
     125          42 :     const double dfExp = std::pow(10.0, poGDS->m_nDecimalCount);
     126          42 :     double *padfImage = reinterpret_cast<double *>(pImage);
     127             : 
     128             :     // If we have previously read too many values, start by consuming the
     129             :     // queue
     130          45 :     while (iRow < nRasterYSize && !poGDS->m_odfQueue.empty())
     131             :     {
     132           3 :         if (padfImage)
     133           3 :             padfImage[iRow] = poGDS->m_odfQueue.front();
     134           3 :         ++iRow;
     135           3 :         poGDS->m_odfQueue.pop_front();
     136             :     }
     137             : 
     138             :     // Now read as many lines as needed to finish filling the column buffer
     139         245 :     while (iRow < nRasterYSize)
     140             :     {
     141         203 :         constexpr int MARGIN = 16;  // Should be at least 2 for \r\n
     142         203 :         char *pszLine = const_cast<char *>(CPLReadLine2L(
     143             :             poGDS->m_fp.get(),
     144         203 :             poGDS->m_nValuesPerLine * poGDS->m_nFieldSize + MARGIN, nullptr));
     145         203 :         ++poGDS->m_nCurLine;
     146         203 :         if (pszLine == nullptr)
     147           0 :             return CE_Failure;
     148             : 
     149             :         // Each line should have at most m_nValuesPerLine values of size
     150             :         // m_nFieldSize
     151         203 :         const int nLineLen = static_cast<int>(strlen(pszLine));
     152         203 :         if ((nLineLen % poGDS->m_nFieldSize) != 0)
     153             :         {
     154           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     155             :                      "Line %d has length %d, which is not a multiple of %d",
     156             :                      poGDS->m_nCurLine, nLineLen, poGDS->m_nFieldSize);
     157           0 :             return CE_Failure;
     158             :         }
     159             : 
     160         203 :         const int nValuesThisLine = nLineLen / poGDS->m_nFieldSize;
     161         203 :         if (nValuesThisLine > poGDS->m_nValuesPerLine)
     162             :         {
     163           0 :             CPLError(
     164             :                 CE_Failure, CPLE_AppDefined,
     165             :                 "Line %d has %d values, whereas the maximum expected is %d",
     166             :                 poGDS->m_nCurLine, nValuesThisLine, poGDS->m_nValuesPerLine);
     167           0 :             return CE_Failure;
     168             :         }
     169             : 
     170        1013 :         for (int iValueThisLine = 0; iValueThisLine < nValuesThisLine;
     171             :              iValueThisLine++)
     172             :         {
     173         810 :             char *pszValue = pszLine + iValueThisLine * poGDS->m_nFieldSize;
     174         810 :             const char chSaved = pszValue[poGDS->m_nFieldSize];
     175         810 :             pszValue[poGDS->m_nFieldSize] = 0;
     176         810 :             const double dfVal = strchr(pszValue, '.') != nullptr
     177         810 :                                      ? CPLAtofM(pszValue)
     178           0 :                                      : atoi(pszValue) * dfExp;
     179         810 :             pszValue[poGDS->m_nFieldSize] = chSaved;
     180         810 :             if (iRow < nRasterYSize)
     181             :             {
     182         807 :                 if (padfImage)
     183         807 :                     padfImage[iRow] = dfVal;
     184         807 :                 ++iRow;
     185             :             }
     186             :             else
     187             :             {
     188           3 :                 poGDS->m_odfQueue.push_back(dfVal);
     189             :             }
     190             :         }
     191             :     }
     192             : 
     193          42 :     poGDS->m_nColNum++;
     194             : 
     195          42 :     return CE_None;
     196             : }
     197             : 
     198             : /************************************************************************/
     199             : /*                          GetNoDataValue()                            */
     200             : /************************************************************************/
     201             : 
     202           3 : double ZMapRasterBand::GetNoDataValue(int *pbSuccess)
     203             : {
     204           3 :     ZMapDataset *poGDS = cpl::down_cast<ZMapDataset *>(poDS);
     205             : 
     206           3 :     if (pbSuccess)
     207           3 :         *pbSuccess = TRUE;
     208             : 
     209           3 :     return poGDS->m_dfNoDataValue;
     210             : }
     211             : 
     212             : /************************************************************************/
     213             : /*                            ~ZMapDataset()                            */
     214             : /************************************************************************/
     215             : 
     216             : ZMapDataset::ZMapDataset() = default;
     217             : 
     218             : /************************************************************************/
     219             : /*                            ~ZMapDataset()                            */
     220             : /************************************************************************/
     221             : 
     222          38 : ZMapDataset::~ZMapDataset()
     223             : 
     224             : {
     225          19 :     FlushCache(true);
     226          38 : }
     227             : 
     228             : /************************************************************************/
     229             : /*                             Identify()                               */
     230             : /************************************************************************/
     231             : 
     232       51697 : int ZMapDataset::Identify(GDALOpenInfo *poOpenInfo)
     233             : {
     234       51697 :     if (poOpenInfo->nHeaderBytes == 0)
     235       48639 :         return FALSE;
     236             : 
     237             :     /* -------------------------------------------------------------------- */
     238             :     /*      Check that it looks roughly as a ZMap dataset                   */
     239             :     /* -------------------------------------------------------------------- */
     240        3058 :     const char *pszData =
     241             :         reinterpret_cast<const char *>(poOpenInfo->pabyHeader);
     242             : 
     243             :     /* Skip comments line at the beginning */
     244        3058 :     int i = 0;
     245        3058 :     if (pszData[i] == '!')
     246             :     {
     247          33 :         i++;
     248         706 :         for (; i < poOpenInfo->nHeaderBytes; i++)
     249             :         {
     250         706 :             char ch = pszData[i];
     251         706 :             if (ch == 13 || ch == 10)
     252             :             {
     253          89 :                 i++;
     254          89 :                 if (ch == 13 && pszData[i] == 10)
     255           0 :                     i++;
     256          89 :                 if (pszData[i] != '!')
     257          33 :                     break;
     258             :             }
     259             :         }
     260             :     }
     261             : 
     262        3058 :     if (pszData[i] != '@')
     263        3030 :         return FALSE;
     264          28 :     i++;
     265             : 
     266          56 :     const CPLStringList aosTokens(CSLTokenizeString2(pszData + i, ",", 0));
     267          28 :     if (aosTokens.size() < 3)
     268             :     {
     269           4 :         return FALSE;
     270             :     }
     271             : 
     272          24 :     const char *pszToken = aosTokens[1];
     273          48 :     while (*pszToken == ' ')
     274          24 :         pszToken++;
     275             : 
     276          24 :     return STARTS_WITH(pszToken, "GRID");
     277             : }
     278             : 
     279             : /************************************************************************/
     280             : /*                                Open()                                */
     281             : /************************************************************************/
     282             : 
     283          19 : GDALDataset *ZMapDataset::Open(GDALOpenInfo *poOpenInfo)
     284             : 
     285             : {
     286          19 :     if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
     287           0 :         return nullptr;
     288             : 
     289             :     /* -------------------------------------------------------------------- */
     290             :     /*      Confirm the requested access is supported.                      */
     291             :     /* -------------------------------------------------------------------- */
     292          19 :     if (poOpenInfo->eAccess == GA_Update)
     293             :     {
     294           0 :         ReportUpdateNotSupportedByDriver("ZMAP");
     295           0 :         return nullptr;
     296             :     }
     297             : 
     298          38 :     auto poDS = std::make_unique<ZMapDataset>();
     299          19 :     poDS->m_fp.reset(poOpenInfo->fpL);
     300          19 :     poOpenInfo->fpL = nullptr;
     301             : 
     302             :     /* -------------------------------------------------------------------- */
     303             :     /*      Find dataset characteristics                                    */
     304             :     /* -------------------------------------------------------------------- */
     305             : 
     306             :     const char *pszLine;
     307          19 :     int nLine = 0;
     308          19 :     constexpr int MAX_HEADER_LINE = 1024;
     309          76 :     while ((pszLine = CPLReadLine2L(poDS->m_fp.get(), MAX_HEADER_LINE,
     310          76 :                                     nullptr)) != nullptr)
     311             :     {
     312          76 :         ++nLine;
     313          76 :         if (*pszLine == '!')
     314             :         {
     315          57 :             continue;
     316             :         }
     317             :         else
     318          19 :             break;
     319             :     }
     320             :     // cppcheck-suppress knownConditionTrueFalse
     321          19 :     if (pszLine == nullptr)
     322             :     {
     323           0 :         return nullptr;
     324             :     }
     325             : 
     326             :     /* Parse first header line */
     327          38 :     CPLStringList aosTokensFirstLine(CSLTokenizeString2(pszLine, ",", 0));
     328          19 :     if (aosTokensFirstLine.size() != 3)
     329             :     {
     330           0 :         return nullptr;
     331             :     }
     332             : 
     333          19 :     const int nValuesPerLine = atoi(aosTokensFirstLine[2]);
     334          19 :     if (nValuesPerLine <= 0)
     335             :     {
     336           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     337             :                  "Invalid/unsupported value for nValuesPerLine = %d",
     338             :                  nValuesPerLine);
     339           0 :         return nullptr;
     340             :     }
     341             : 
     342             :     /* Parse second header line */
     343          19 :     pszLine = CPLReadLine2L(poDS->m_fp.get(), MAX_HEADER_LINE, nullptr);
     344          19 :     ++nLine;
     345          19 :     if (pszLine == nullptr)
     346             :     {
     347           0 :         return nullptr;
     348             :     }
     349             :     const CPLStringList aosTokensSecondLine(
     350          38 :         CSLTokenizeString2(pszLine, ",", 0));
     351          19 :     if (aosTokensSecondLine.size() != 5)
     352             :     {
     353           0 :         return nullptr;
     354             :     }
     355             : 
     356          19 :     const int nFieldSize = atoi(aosTokensSecondLine[0]);
     357          19 :     const double dfNoDataValue = CPLAtofM(aosTokensSecondLine[1]);
     358          19 :     const int nDecimalCount = atoi(aosTokensSecondLine[3]);
     359          19 :     const int nColumnNumber = atoi(aosTokensSecondLine[4]);
     360             : 
     361          19 :     if (nFieldSize <= 0 || nFieldSize >= 40)
     362             :     {
     363           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     364             :                  "Invalid/unsupported value for nFieldSize = %d", nFieldSize);
     365           0 :         return nullptr;
     366             :     }
     367             : 
     368          19 :     if (nDecimalCount <= 0 || nDecimalCount >= nFieldSize)
     369             :     {
     370           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     371             :                  "Invalid/unsupported value for nDecimalCount = %d",
     372             :                  nDecimalCount);
     373           0 :         return nullptr;
     374             :     }
     375             : 
     376          19 :     if (nColumnNumber != 1)
     377             :     {
     378           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     379             :                  "Invalid/unsupported value for nColumnNumber = %d",
     380             :                  nColumnNumber);
     381           0 :         return nullptr;
     382             :     }
     383             : 
     384          19 :     if (nValuesPerLine <= 0 || nFieldSize > 1024 * 1024 / nValuesPerLine)
     385             :     {
     386           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     387             :                  "Invalid/unsupported value for nFieldSize = %d x "
     388             :                  "nValuesPerLine = %d",
     389             :                  nFieldSize, nValuesPerLine);
     390           0 :         return nullptr;
     391             :     }
     392             : 
     393             :     /* Parse third header line */
     394          19 :     pszLine = CPLReadLine2L(poDS->m_fp.get(), MAX_HEADER_LINE, nullptr);
     395          19 :     ++nLine;
     396          19 :     if (pszLine == nullptr)
     397             :     {
     398           0 :         return nullptr;
     399             :     }
     400          38 :     const CPLStringList aosTokensThirdLine(CSLTokenizeString2(pszLine, ",", 0));
     401          19 :     if (aosTokensThirdLine.size() != 6)
     402             :     {
     403           0 :         return nullptr;
     404             :     }
     405             : 
     406          19 :     const int nRows = atoi(aosTokensThirdLine[0]);
     407          19 :     const int nCols = atoi(aosTokensThirdLine[1]);
     408          19 :     const double dfMinX = CPLAtofM(aosTokensThirdLine[2]);
     409          19 :     const double dfMaxX = CPLAtofM(aosTokensThirdLine[3]);
     410          19 :     const double dfMinY = CPLAtofM(aosTokensThirdLine[4]);
     411          19 :     const double dfMaxY = CPLAtofM(aosTokensThirdLine[5]);
     412             : 
     413          19 :     if (!GDALCheckDatasetDimensions(nCols, nRows) || nCols == 1 || nRows == 1)
     414             :     {
     415           0 :         return nullptr;
     416             :     }
     417             : 
     418             :     /* Ignore fourth header line */
     419          19 :     pszLine = CPLReadLine2L(poDS->m_fp.get(), MAX_HEADER_LINE, nullptr);
     420          19 :     ++nLine;
     421          19 :     if (pszLine == nullptr)
     422             :     {
     423           0 :         return nullptr;
     424             :     }
     425             : 
     426             :     /* Check fifth header line */
     427          19 :     pszLine = CPLReadLine2L(poDS->m_fp.get(), MAX_HEADER_LINE, nullptr);
     428          19 :     ++nLine;
     429          19 :     if (pszLine == nullptr || pszLine[0] != '@')
     430             :     {
     431           0 :         return nullptr;
     432             :     }
     433             : 
     434             :     /* -------------------------------------------------------------------- */
     435             :     /*      Create a corresponding GDALDataset.                             */
     436             :     /* -------------------------------------------------------------------- */
     437          19 :     poDS->m_nDataStartOff = VSIFTellL(poDS->m_fp.get());
     438          19 :     poDS->m_nValuesPerLine = nValuesPerLine;
     439          19 :     poDS->m_nFieldSize = nFieldSize;
     440          19 :     poDS->m_nDecimalCount = nDecimalCount;
     441          19 :     poDS->nRasterXSize = nCols;
     442          19 :     poDS->nRasterYSize = nRows;
     443          19 :     poDS->m_dfNoDataValue = dfNoDataValue;
     444          19 :     poDS->m_nFirstDataLine = nLine;
     445             : 
     446          19 :     if (CPLTestBool(CPLGetConfigOption("ZMAP_PIXEL_IS_POINT", "FALSE")))
     447             :     {
     448           0 :         const double dfStepX = (dfMaxX - dfMinX) / (nCols - 1);
     449           0 :         const double dfStepY = (dfMaxY - dfMinY) / (nRows - 1);
     450             : 
     451           0 :         poDS->m_adfGeoTransform[0] = dfMinX - dfStepX / 2;
     452           0 :         poDS->m_adfGeoTransform[1] = dfStepX;
     453           0 :         poDS->m_adfGeoTransform[3] = dfMaxY + dfStepY / 2;
     454           0 :         poDS->m_adfGeoTransform[5] = -dfStepY;
     455             :     }
     456             :     else
     457             :     {
     458          19 :         const double dfStepX = (dfMaxX - dfMinX) / nCols;
     459          19 :         const double dfStepY = (dfMaxY - dfMinY) / nRows;
     460             : 
     461          19 :         poDS->m_adfGeoTransform[0] = dfMinX;
     462          19 :         poDS->m_adfGeoTransform[1] = dfStepX;
     463          19 :         poDS->m_adfGeoTransform[3] = dfMaxY;
     464          19 :         poDS->m_adfGeoTransform[5] = -dfStepY;
     465             :     }
     466             : 
     467             :     /* -------------------------------------------------------------------- */
     468             :     /*      Create band information objects.                                */
     469             :     /* -------------------------------------------------------------------- */
     470          19 :     poDS->nBands = 1;
     471          19 :     poDS->SetBand(1, std::make_unique<ZMapRasterBand>(poDS.get()));
     472             : 
     473             :     /* -------------------------------------------------------------------- */
     474             :     /*      Initialize any PAM information.                                 */
     475             :     /* -------------------------------------------------------------------- */
     476          19 :     poDS->SetDescription(poOpenInfo->pszFilename);
     477          19 :     poDS->TryLoadXML();
     478             : 
     479             :     /* -------------------------------------------------------------------- */
     480             :     /*      Support overviews.                                              */
     481             :     /* -------------------------------------------------------------------- */
     482          19 :     poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
     483          19 :     return poDS.release();
     484             : }
     485             : 
     486             : /************************************************************************/
     487             : /*                       WriteRightJustified()                          */
     488             : /************************************************************************/
     489             : 
     490        1668 : static void WriteRightJustified(VSIVirtualHandleUniquePtr &fp,
     491             :                                 const char *pszValue, int nWidth)
     492             : {
     493        1668 :     int nLen = (int)strlen(pszValue);
     494        1668 :     CPLAssert(nLen <= nWidth);
     495       16550 :     for (int i = 0; i < nWidth - nLen; i++)
     496       14882 :         fp->Write(" ", 1, 1);
     497        1668 :     fp->Write(pszValue, 1, nLen);
     498        1668 : }
     499             : 
     500          70 : static void WriteRightJustified(VSIVirtualHandleUniquePtr &fp, int nValue,
     501             :                                 int nWidth)
     502             : {
     503         140 :     CPLString osValue(CPLSPrintf("%d", nValue));
     504          70 :     WriteRightJustified(fp, osValue.c_str(), nWidth);
     505          70 : }
     506             : 
     507        1584 : static void WriteRightJustified(VSIVirtualHandleUniquePtr &fp, double dfValue,
     508             :                                 int nWidth, int nDecimals = -1)
     509             : {
     510             :     char szFormat[32];
     511        1584 :     if (nDecimals >= 0)
     512        1584 :         snprintf(szFormat, sizeof(szFormat), "%%.%df", nDecimals);
     513             :     else
     514           0 :         snprintf(szFormat, sizeof(szFormat), "%%g");
     515        1584 :     char *pszValue = const_cast<char *>(CPLSPrintf(szFormat, dfValue));
     516        1584 :     char *pszE = strchr(pszValue, 'e');
     517        1584 :     if (pszE)
     518           0 :         *pszE = 'E';
     519             : 
     520        1584 :     if (static_cast<int>(strlen(pszValue)) > nWidth)
     521             :     {
     522          16 :         CPLAssert(nDecimals >= 0);
     523          16 :         snprintf(szFormat, sizeof(szFormat), "%%.%dg", nDecimals);
     524          16 :         pszValue = const_cast<char *>(CPLSPrintf(szFormat, dfValue));
     525          16 :         pszE = strchr(pszValue, 'e');
     526          16 :         if (pszE)
     527          14 :             *pszE = 'E';
     528             :     }
     529             : 
     530        3168 :     CPLString osValue(pszValue);
     531        1584 :     WriteRightJustified(fp, osValue.c_str(), nWidth);
     532        1584 : }
     533             : 
     534             : /************************************************************************/
     535             : /*                             CreateCopy()                             */
     536             : /************************************************************************/
     537             : 
     538          22 : GDALDataset *ZMapDataset::CreateCopy(const char *pszFilename,
     539             :                                      GDALDataset *poSrcDS, int bStrict,
     540             :                                      CPL_UNUSED char **papszOptions,
     541             :                                      GDALProgressFunc pfnProgress,
     542             :                                      void *pProgressData)
     543             : {
     544             :     /* -------------------------------------------------------------------- */
     545             :     /*      Some some rudimentary checks                                    */
     546             :     /* -------------------------------------------------------------------- */
     547          22 :     int nBands = poSrcDS->GetRasterCount();
     548          22 :     if (nBands == 0)
     549             :     {
     550           1 :         CPLError(
     551             :             CE_Failure, CPLE_NotSupported,
     552             :             "ZMap driver does not support source dataset with zero band.\n");
     553           1 :         return nullptr;
     554             :     }
     555             : 
     556          21 :     if (nBands != 1)
     557             :     {
     558           4 :         CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
     559             :                  "ZMap driver only uses the first band of the dataset.\n");
     560           4 :         if (bStrict)
     561           4 :             return nullptr;
     562             :     }
     563             : 
     564          17 :     if (pfnProgress && !pfnProgress(0.0, nullptr, pProgressData))
     565           0 :         return nullptr;
     566             : 
     567             :     /* -------------------------------------------------------------------- */
     568             :     /*      Get source dataset info                                         */
     569             :     /* -------------------------------------------------------------------- */
     570             : 
     571          17 :     const int nXSize = poSrcDS->GetRasterXSize();
     572          17 :     const int nYSize = poSrcDS->GetRasterYSize();
     573          17 :     if (nXSize == 1 || nYSize == 1)
     574             :     {
     575           0 :         return nullptr;
     576             :     }
     577             : 
     578             :     double adfGeoTransform[6];
     579          17 :     poSrcDS->GetGeoTransform(adfGeoTransform);
     580          17 :     if (adfGeoTransform[2] != 0 || adfGeoTransform[4] != 0)
     581             :     {
     582           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     583             :                  "ZMap driver does not support CreateCopy() from skewed or "
     584             :                  "rotated dataset.\n");
     585           0 :         return nullptr;
     586             :     }
     587             : 
     588             :     /* -------------------------------------------------------------------- */
     589             :     /*      Create target file                                              */
     590             :     /* -------------------------------------------------------------------- */
     591             : 
     592          34 :     auto fp = VSIVirtualHandleUniquePtr(VSIFOpenL(pszFilename, "wb"));
     593          17 :     if (fp == nullptr)
     594             :     {
     595           3 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot create %s", pszFilename);
     596           3 :         return nullptr;
     597             :     }
     598             : 
     599          14 :     const int nFieldSize = 20;
     600          14 :     const int nValuesPerLine = 4;
     601          14 :     const int nDecimalCount = 7;
     602             : 
     603          14 :     int bHasNoDataValue = FALSE;
     604             :     double dfNoDataValue =
     605          14 :         poSrcDS->GetRasterBand(1)->GetNoDataValue(&bHasNoDataValue);
     606          14 :     if (!bHasNoDataValue)
     607          13 :         dfNoDataValue = 1.e30;
     608             : 
     609          14 :     fp->Printf("!\n");
     610          14 :     fp->Printf("! Created by GDAL.\n");
     611          14 :     fp->Printf("!\n");
     612          14 :     fp->Printf("@GRID FILE, GRID, %d\n", nValuesPerLine);
     613             : 
     614          14 :     WriteRightJustified(fp, nFieldSize, 10);
     615          14 :     fp->Printf(",");
     616          14 :     WriteRightJustified(fp, dfNoDataValue, nFieldSize, nDecimalCount);
     617          14 :     fp->Printf(",");
     618          14 :     WriteRightJustified(fp, "", 10);
     619          14 :     fp->Printf(",");
     620          14 :     WriteRightJustified(fp, nDecimalCount, 10);
     621          14 :     fp->Printf(",");
     622          14 :     WriteRightJustified(fp, 1, 10);
     623          14 :     fp->Printf("\n");
     624             : 
     625          14 :     WriteRightJustified(fp, nYSize, 10);
     626          14 :     fp->Printf(",");
     627          14 :     WriteRightJustified(fp, nXSize, 10);
     628          14 :     fp->Printf(",");
     629             : 
     630          14 :     if (CPLTestBool(CPLGetConfigOption("ZMAP_PIXEL_IS_POINT", "FALSE")))
     631             :     {
     632           0 :         WriteRightJustified(fp, adfGeoTransform[0] + adfGeoTransform[1] / 2, 14,
     633             :                             7);
     634           0 :         fp->Printf(",");
     635           0 :         WriteRightJustified(fp,
     636           0 :                             adfGeoTransform[0] + adfGeoTransform[1] * nXSize -
     637           0 :                                 adfGeoTransform[1] / 2,
     638             :                             14, 7);
     639           0 :         fp->Printf(",");
     640           0 :         WriteRightJustified(fp,
     641           0 :                             adfGeoTransform[3] + adfGeoTransform[5] * nYSize -
     642           0 :                                 adfGeoTransform[5] / 2,
     643             :                             14, 7);
     644           0 :         fp->Printf(",");
     645           0 :         WriteRightJustified(fp, adfGeoTransform[3] + adfGeoTransform[5] / 2, 14,
     646             :                             7);
     647             :     }
     648             :     else
     649             :     {
     650          14 :         WriteRightJustified(fp, adfGeoTransform[0], 14, 7);
     651          14 :         fp->Printf(",");
     652          14 :         WriteRightJustified(
     653          14 :             fp, adfGeoTransform[0] + adfGeoTransform[1] * nXSize, 14, 7);
     654          14 :         fp->Printf(",");
     655          14 :         WriteRightJustified(
     656          14 :             fp, adfGeoTransform[3] + adfGeoTransform[5] * nYSize, 14, 7);
     657          14 :         fp->Printf(",");
     658          14 :         WriteRightJustified(fp, adfGeoTransform[3], 14, 7);
     659             :     }
     660             : 
     661          14 :     fp->Printf("\n");
     662             : 
     663          14 :     fp->Printf("0.0, 0.0, 0.0\n");
     664          14 :     fp->Printf("@\n");
     665             : 
     666             :     /* -------------------------------------------------------------------- */
     667             :     /*      Copy imagery                                                    */
     668             :     /* -------------------------------------------------------------------- */
     669          28 :     std::vector<double> adfLineBuffer(nYSize);
     670             : 
     671          14 :     CPLErr eErr = CE_None;
     672          14 :     const bool bEmitEOLAtEndOfColumn = CPLTestBool(
     673             :         CPLGetConfigOption("ZMAP_EMIT_EOL_AT_END_OF_COLUMN", "YES"));
     674          14 :     bool bEOLPrinted = false;
     675          14 :     int nValuesThisLine = 0;
     676         148 :     for (int i = 0; i < nXSize && eErr == CE_None; i++)
     677             :     {
     678         268 :         eErr = poSrcDS->GetRasterBand(1)->RasterIO(
     679         134 :             GF_Read, i, 0, 1, nYSize, adfLineBuffer.data(), 1, nYSize,
     680             :             GDT_Float64, 0, 0, nullptr);
     681         134 :         if (eErr != CE_None)
     682           0 :             break;
     683        1648 :         for (int j = 0; j < nYSize; j++)
     684             :         {
     685        1514 :             WriteRightJustified(fp, adfLineBuffer[j], nFieldSize,
     686             :                                 nDecimalCount);
     687        1514 :             ++nValuesThisLine;
     688        1514 :             if (nValuesThisLine == nValuesPerLine)
     689             :             {
     690         322 :                 bEOLPrinted = true;
     691         322 :                 nValuesThisLine = 0;
     692         322 :                 fp->Printf("\n");
     693             :             }
     694             :             else
     695        1192 :                 bEOLPrinted = false;
     696             :         }
     697         134 :         if (bEmitEOLAtEndOfColumn && !bEOLPrinted)
     698             :         {
     699         112 :             bEOLPrinted = true;
     700         112 :             nValuesThisLine = 0;
     701         112 :             fp->Printf("\n");
     702             :         }
     703             : 
     704         268 :         if (pfnProgress != nullptr &&
     705         134 :             !pfnProgress((i + 1) * 1.0 / nXSize, nullptr, pProgressData))
     706             :         {
     707           0 :             eErr = CE_Failure;
     708           0 :             break;
     709             :         }
     710             :     }
     711          14 :     if (!bEOLPrinted)
     712           1 :         fp->Printf("\n");
     713             : 
     714          14 :     if (eErr != CE_None || fp->Close() != 0)
     715           0 :         return nullptr;
     716             : 
     717          14 :     fp.reset();
     718          28 :     GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
     719          14 :     return ZMapDataset::Open(&oOpenInfo);
     720             : }
     721             : 
     722             : /************************************************************************/
     723             : /*                          GetGeoTransform()                           */
     724             : /************************************************************************/
     725             : 
     726           1 : CPLErr ZMapDataset::GetGeoTransform(double *padfTransform)
     727             : 
     728             : {
     729           1 :     memcpy(padfTransform, m_adfGeoTransform.data(), 6 * sizeof(double));
     730             : 
     731           1 :     return CE_None;
     732             : }
     733             : 
     734             : /************************************************************************/
     735             : /*                         GDALRegister_ZMap()                          */
     736             : /************************************************************************/
     737             : 
     738        1686 : void GDALRegister_ZMap()
     739             : 
     740             : {
     741        1686 :     if (GDALGetDriverByName("ZMap") != nullptr)
     742         302 :         return;
     743             : 
     744        2768 :     auto poDriver = std::make_unique<GDALDriver>();
     745             : 
     746        1384 :     poDriver->SetDescription("ZMap");
     747        1384 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
     748        1384 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "ZMap Plus Grid");
     749        1384 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/zmap.html");
     750        1384 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "dat");
     751        1384 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
     752             : 
     753        1384 :     poDriver->pfnOpen = ZMapDataset::Open;
     754        1384 :     poDriver->pfnIdentify = ZMapDataset::Identify;
     755        1384 :     poDriver->pfnCreateCopy = ZMapDataset::CreateCopy;
     756             : 
     757        1384 :     GetGDALDriverManager()->RegisterDriver(poDriver.release());
     758             : }

Generated by: LCOV version 1.14