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

Generated by: LCOV version 1.14