LCOV - code coverage report
Current view: top level - frmts/xyz - xyzdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 740 905 81.8 %
Date: 2024-11-21 22:18:42 Functions: 14 14 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  XYZ driver
       4             :  * Purpose:  GDALDataset driver for XYZ dataset.
       5             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2010-2013, 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 <algorithm>
      19             : #include <cmath>
      20             : #include <mutex>
      21             : #include <vector>
      22             : 
      23             : constexpr double RELATIVE_ERROR = 1e-3;
      24             : 
      25             : class XYZDataset;
      26             : 
      27             : // Global cache when we must ingest all grid points
      28             : static std::mutex gMutex;
      29             : static XYZDataset *gpoActiveDS = nullptr;
      30             : static std::vector<short> gasValues;
      31             : static std::vector<float> gafValues;
      32             : 
      33             : /************************************************************************/
      34             : /* ==================================================================== */
      35             : /*                              XYZDataset                              */
      36             : /* ==================================================================== */
      37             : /************************************************************************/
      38             : 
      39             : class XYZRasterBand;
      40             : 
      41             : class XYZDataset final : public GDALPamDataset
      42             : {
      43             :     friend class XYZRasterBand;
      44             : 
      45             :     VSILFILE *fp;
      46             :     int bHasHeaderLine;
      47             :     int nCommentLineCount;
      48             :     char chDecimalSep;
      49             :     int nXIndex;
      50             :     int nYIndex;
      51             :     int nZIndex;
      52             :     int nMinTokens;
      53             :     GIntBig nLineNum;     /* any line */
      54             :     GIntBig nDataLineNum; /* line with values (header line and empty lines
      55             :                              ignored) */
      56             :     double adfGeoTransform[6];
      57             :     int bSameNumberOfValuesPerLine;
      58             :     double dfMinZ;
      59             :     double dfMaxZ;
      60             :     bool bEOF;
      61             :     bool bIngestAll = false;
      62             : 
      63             :     static int IdentifyEx(GDALOpenInfo *, int &, int &nCommentLineCount,
      64             :                           int &nXIndex, int &nYIndex, int &nZIndex);
      65             : 
      66             :   public:
      67             :     XYZDataset();
      68             :     virtual ~XYZDataset();
      69             : 
      70             :     virtual CPLErr GetGeoTransform(double *) override;
      71             : 
      72             :     static GDALDataset *Open(GDALOpenInfo *);
      73             :     static int Identify(GDALOpenInfo *);
      74             :     static GDALDataset *CreateCopy(const char *pszFilename,
      75             :                                    GDALDataset *poSrcDS, int bStrict,
      76             :                                    char **papszOptions,
      77             :                                    GDALProgressFunc pfnProgress,
      78             :                                    void *pProgressData);
      79             : };
      80             : 
      81             : /************************************************************************/
      82             : /* ==================================================================== */
      83             : /*                            XYZRasterBand                             */
      84             : /* ==================================================================== */
      85             : /************************************************************************/
      86             : 
      87             : class XYZRasterBand final : public GDALPamRasterBand
      88             : {
      89             :     friend class XYZDataset;
      90             : 
      91             :     int nLastYOff;
      92             : 
      93             :   public:
      94             :     XYZRasterBand(XYZDataset *, int, GDALDataType);
      95             : 
      96             :     virtual CPLErr IReadBlock(int, int, void *) override;
      97             :     virtual double GetMinimum(int *pbSuccess = nullptr) override;
      98             :     virtual double GetMaximum(int *pbSuccess = nullptr) override;
      99             :     virtual double GetNoDataValue(int *pbSuccess = nullptr) override;
     100             : };
     101             : 
     102             : /************************************************************************/
     103             : /*                           XYZRasterBand()                            */
     104             : /************************************************************************/
     105             : 
     106          37 : XYZRasterBand::XYZRasterBand(XYZDataset *poDSIn, int nBandIn, GDALDataType eDT)
     107          37 :     : nLastYOff(-1)
     108             : {
     109          37 :     poDS = poDSIn;
     110          37 :     nBand = nBandIn;
     111             : 
     112          37 :     eDataType = eDT;
     113             : 
     114          37 :     nBlockXSize = poDSIn->GetRasterXSize();
     115          37 :     nBlockYSize = 1;
     116          37 : }
     117             : 
     118             : /************************************************************************/
     119             : /*                             IReadBlock()                             */
     120             : /************************************************************************/
     121             : 
     122         298 : CPLErr XYZRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
     123             :                                  void *pImage)
     124             : {
     125         298 :     XYZDataset *poGDS = reinterpret_cast<XYZDataset *>(poDS);
     126             : 
     127         298 :     if (poGDS->fp == nullptr)
     128           0 :         return CE_Failure;
     129             : 
     130         298 :     if (poGDS->bIngestAll)
     131             :     {
     132          12 :         CPLAssert(eDataType == GDT_Int16 || eDataType == GDT_Float32);
     133             : 
     134          24 :         std::lock_guard<std::mutex> guard(gMutex);
     135             : 
     136          12 :         if (gpoActiveDS != poGDS || (gasValues.empty() && gafValues.empty()))
     137             :         {
     138           4 :             gpoActiveDS = poGDS;
     139             : 
     140           4 :             const int nGridSize = nRasterXSize * nRasterYSize;
     141             :             try
     142             :             {
     143           4 :                 if (eDataType == GDT_Int16)
     144           2 :                     gasValues.resize(nGridSize);
     145             :                 else
     146           2 :                     gafValues.resize(nGridSize);
     147             :             }
     148           0 :             catch (const std::exception &)
     149             :             {
     150           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory, "Cannot allocate grid");
     151           0 :                 return CE_Failure;
     152             :             }
     153             : 
     154           4 :             poGDS->nDataLineNum = 0;
     155           4 :             poGDS->nLineNum = 0;
     156           4 :             poGDS->bEOF = false;
     157           4 :             VSIFSeekL(poGDS->fp, 0, SEEK_SET);
     158             : 
     159           4 :             for (int i = 0; i < poGDS->nCommentLineCount; i++)
     160             :             {
     161           0 :                 if (CPLReadLine2L(poGDS->fp, 100, nullptr) == nullptr)
     162             :                 {
     163           0 :                     poGDS->bEOF = true;
     164           0 :                     return CE_Failure;
     165             :                 }
     166           0 :                 poGDS->nLineNum++;
     167             :             }
     168             : 
     169           4 :             if (poGDS->bHasHeaderLine)
     170             :             {
     171           4 :                 const char *pszLine = CPLReadLine2L(poGDS->fp, 100, nullptr);
     172           4 :                 if (pszLine == nullptr)
     173             :                 {
     174           0 :                     poGDS->bEOF = true;
     175           0 :                     return CE_Failure;
     176             :                 }
     177           4 :                 poGDS->nLineNum++;
     178             :             }
     179             : 
     180          34 :             for (int i = 0; i < nGridSize; i++)
     181             :             {
     182          30 :                 const char *pszLine = CPLReadLine2L(poGDS->fp, 100, nullptr);
     183          30 :                 if (pszLine == nullptr)
     184             :                 {
     185           0 :                     poGDS->bEOF = true;
     186           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     187             :                              "Cannot read line " CPL_FRMT_GIB,
     188           0 :                              poGDS->nLineNum + 1);
     189           0 :                     return CE_Failure;
     190             :                 }
     191          30 :                 poGDS->nLineNum++;
     192             : 
     193          30 :                 const char *pszPtr = pszLine;
     194             :                 char ch;
     195          30 :                 int nCol = 0;
     196          30 :                 bool bLastWasSep = true;
     197          30 :                 double dfX = 0.0;
     198          30 :                 double dfY = 0.0;
     199          30 :                 double dfZ = 0.0;
     200          30 :                 int nUsefulColsFound = 0;
     201         326 :                 while ((ch = *pszPtr) != '\0')
     202             :                 {
     203         296 :                     if (ch == ' ')
     204             :                     {
     205          60 :                         if (!bLastWasSep)
     206          60 :                             nCol++;
     207          60 :                         bLastWasSep = true;
     208             :                     }
     209         236 :                     else if ((ch == ',' && poGDS->chDecimalSep != ',') ||
     210         236 :                              ch == '\t' || ch == ';')
     211             :                     {
     212           0 :                         nCol++;
     213           0 :                         bLastWasSep = true;
     214             :                     }
     215             :                     else
     216             :                     {
     217         236 :                         if (bLastWasSep)
     218             :                         {
     219          90 :                             if (nCol == poGDS->nXIndex)
     220             :                             {
     221          30 :                                 nUsefulColsFound++;
     222          30 :                                 dfX = CPLAtofDelim(pszPtr, poGDS->chDecimalSep);
     223             :                             }
     224          60 :                             else if (nCol == poGDS->nYIndex)
     225             :                             {
     226          30 :                                 nUsefulColsFound++;
     227          30 :                                 dfY = CPLAtofDelim(pszPtr, poGDS->chDecimalSep);
     228             :                             }
     229          30 :                             else if (nCol == poGDS->nZIndex)
     230             :                             {
     231          30 :                                 nUsefulColsFound++;
     232          30 :                                 dfZ = CPLAtofDelim(pszPtr, poGDS->chDecimalSep);
     233             :                             }
     234             :                         }
     235         236 :                         bLastWasSep = false;
     236             :                     }
     237         296 :                     pszPtr++;
     238             :                 }
     239             : 
     240             :                 /* Skip empty line */
     241          30 :                 if (nCol == 0 && bLastWasSep)
     242           0 :                     continue;
     243             : 
     244          30 :                 if (nUsefulColsFound != 3)
     245             :                 {
     246           0 :                     CPLError(
     247             :                         CE_Failure, CPLE_AppDefined,
     248             :                         "Unexpected number of values at line " CPL_FRMT_GIB,
     249             :                         poGDS->nLineNum);
     250           0 :                     return CE_Failure;
     251             :                 }
     252             : 
     253          30 :                 poGDS->nDataLineNum++;
     254             : 
     255          30 :                 const int nX =
     256          30 :                     static_cast<int>((dfX - 0.5 * poGDS->adfGeoTransform[1] -
     257          30 :                                       poGDS->adfGeoTransform[0]) /
     258          30 :                                          poGDS->adfGeoTransform[1] +
     259             :                                      0.5);
     260          30 :                 const int nY =
     261          30 :                     static_cast<int>((dfY - 0.5 * poGDS->adfGeoTransform[5] -
     262          30 :                                       poGDS->adfGeoTransform[3]) /
     263          30 :                                          poGDS->adfGeoTransform[5] +
     264             :                                      0.5);
     265          30 :                 if (nX < 0 || nX >= nRasterXSize)
     266             :                 {
     267           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     268             :                              "Unexpected X value at line " CPL_FRMT_GIB,
     269             :                              poGDS->nLineNum);
     270           0 :                     return CE_Failure;
     271             :                 }
     272          30 :                 if (nY < 0 || nY >= nRasterYSize)
     273             :                 {
     274           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     275             :                              "Unexpected Y value at line " CPL_FRMT_GIB,
     276             :                              poGDS->nLineNum);
     277           0 :                     return CE_Failure;
     278             :                 }
     279          30 :                 const int nIdx = nX + nY * nRasterXSize;
     280          30 :                 if (eDataType == GDT_Int16)
     281          15 :                     gasValues[nIdx] = static_cast<short>(0.5 + dfZ);
     282             :                 else
     283          15 :                     gafValues[nIdx] = static_cast<float>(dfZ);
     284             :             }
     285             :         }
     286             : 
     287          12 :         if (eDataType == GDT_Int16)
     288           6 :             memcpy(pImage,
     289           6 :                    &gasValues[static_cast<size_t>(nBlockYOff) * nBlockXSize],
     290           6 :                    sizeof(short) * nBlockXSize);
     291             :         else
     292           6 :             memcpy(pImage,
     293           6 :                    &gafValues[static_cast<size_t>(nBlockYOff) * nBlockXSize],
     294           6 :                    sizeof(float) * nBlockXSize);
     295          12 :         return CE_None;
     296             :     }
     297             : 
     298         286 :     if (pImage)
     299             :     {
     300         286 :         int bSuccess = FALSE;
     301         286 :         double dfNoDataValue = GetNoDataValue(&bSuccess);
     302         286 :         if (!bSuccess)
     303         264 :             dfNoDataValue = 0.0;
     304         572 :         GDALCopyWords(&dfNoDataValue, GDT_Float64, 0, pImage, eDataType,
     305         286 :                       GDALGetDataTypeSize(eDataType) / 8, nRasterXSize);
     306             :     }
     307             : 
     308             :     // Only valid if bSameNumberOfValuesPerLine.
     309         286 :     const GIntBig nLineInFile = static_cast<GIntBig>(nBlockYOff) * nBlockXSize;
     310         286 :     if ((poGDS->bSameNumberOfValuesPerLine &&
     311         264 :          poGDS->nDataLineNum > nLineInFile) ||
     312         270 :         (!poGDS->bSameNumberOfValuesPerLine &&
     313          22 :          (nLastYOff == -1 || nBlockYOff == 0)))
     314             :     {
     315          21 :         poGDS->nDataLineNum = 0;
     316          21 :         poGDS->nLineNum = 0;
     317          21 :         poGDS->bEOF = false;
     318          21 :         VSIFSeekL(poGDS->fp, 0, SEEK_SET);
     319             : 
     320          21 :         for (int i = 0; i < poGDS->nCommentLineCount; i++)
     321             :         {
     322           0 :             if (CPLReadLine2L(poGDS->fp, 100, nullptr) == nullptr)
     323             :             {
     324           0 :                 poGDS->bEOF = true;
     325           0 :                 return CE_Failure;
     326             :             }
     327           0 :             poGDS->nLineNum++;
     328             :         }
     329             : 
     330          21 :         if (poGDS->bHasHeaderLine)
     331             :         {
     332          14 :             const char *pszLine = CPLReadLine2L(poGDS->fp, 100, nullptr);
     333          14 :             if (pszLine == nullptr)
     334             :             {
     335           0 :                 poGDS->bEOF = true;
     336           0 :                 return CE_Failure;
     337             :             }
     338          14 :             poGDS->nLineNum++;
     339             :         }
     340             :     }
     341             : 
     342         286 :     if (!poGDS->bSameNumberOfValuesPerLine)
     343             :     {
     344          22 :         if (nBlockYOff < nLastYOff)
     345             :         {
     346           0 :             nLastYOff = -1;
     347           0 :             for (int iY = 0; iY < nBlockYOff; iY++)
     348             :             {
     349           0 :                 if (IReadBlock(0, iY, nullptr) != CE_None)
     350           0 :                     return CE_Failure;
     351             :             }
     352             :         }
     353             :         else
     354             :         {
     355          22 :             if (poGDS->bEOF)
     356             :             {
     357           0 :                 return CE_Failure;
     358             :             }
     359          22 :             for (int iY = nLastYOff + 1; iY < nBlockYOff; iY++)
     360             :             {
     361           0 :                 if (IReadBlock(0, iY, nullptr) != CE_None)
     362           0 :                     return CE_Failure;
     363             :             }
     364             :         }
     365             :     }
     366             :     else
     367             :     {
     368         264 :         if (poGDS->bEOF)
     369             :         {
     370           0 :             return CE_Failure;
     371             :         }
     372         290 :         while (poGDS->nDataLineNum < nLineInFile)
     373             :         {
     374          26 :             const char *pszLine = CPLReadLine2L(poGDS->fp, 100, nullptr);
     375          26 :             if (pszLine == nullptr)
     376             :             {
     377           0 :                 poGDS->bEOF = true;
     378           0 :                 return CE_Failure;
     379             :             }
     380          26 :             poGDS->nLineNum++;
     381             : 
     382          26 :             const char *pszPtr = pszLine;
     383             :             char ch;
     384          26 :             int nCol = 0;
     385          26 :             bool bLastWasSep = true;
     386         146 :             while ((ch = *pszPtr) != '\0')
     387             :             {
     388         120 :                 if (ch == ' ')
     389             :                 {
     390          40 :                     if (!bLastWasSep)
     391          40 :                         nCol++;
     392          40 :                     bLastWasSep = true;
     393             :                 }
     394          80 :                 else if ((ch == ',' && poGDS->chDecimalSep != ',') ||
     395          80 :                          ch == '\t' || ch == ';')
     396             :                 {
     397           0 :                     nCol++;
     398           0 :                     bLastWasSep = true;
     399             :                 }
     400             :                 else
     401             :                 {
     402          80 :                     bLastWasSep = false;
     403             :                 }
     404         120 :                 pszPtr++;
     405             :             }
     406             : 
     407             :             /* Skip empty line */
     408          26 :             if (nCol == 0 && bLastWasSep)
     409           6 :                 continue;
     410             : 
     411          20 :             poGDS->nDataLineNum++;
     412             :         }
     413             :     }
     414             : 
     415         286 :     const double dfExpectedY = poGDS->adfGeoTransform[3] +
     416         286 :                                (0.5 + nBlockYOff) * poGDS->adfGeoTransform[5];
     417             : 
     418         286 :     int idx = -1;
     419             :     while (true)
     420             :     {
     421             :         int nCol;
     422             :         bool bLastWasSep;
     423           7 :         do
     424             :         {
     425       41302 :             const vsi_l_offset nOffsetBefore = VSIFTellL(poGDS->fp);
     426       41302 :             const char *pszLine = CPLReadLine2L(poGDS->fp, 100, nullptr);
     427       41302 :             if (pszLine == nullptr)
     428             :             {
     429           1 :                 poGDS->bEOF = true;
     430           1 :                 if (poGDS->bSameNumberOfValuesPerLine)
     431             :                 {
     432           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     433             :                              "Cannot read line " CPL_FRMT_GIB,
     434           0 :                              poGDS->nLineNum + 1);
     435           0 :                     return CE_Failure;
     436             :                 }
     437             :                 else
     438             :                 {
     439           1 :                     nLastYOff = nBlockYOff;
     440             :                 }
     441           1 :                 return CE_None;
     442             :             }
     443       41301 :             poGDS->nLineNum++;
     444             : 
     445       41301 :             const char *pszPtr = pszLine;
     446             :             char ch;
     447       41301 :             nCol = 0;
     448       41301 :             bLastWasSep = true;
     449       41301 :             double dfX = 0.0;
     450       41301 :             double dfY = 0.0;
     451       41301 :             double dfZ = 0.0;
     452       41301 :             int nUsefulColsFound = 0;
     453     1342950 :             while ((ch = *pszPtr) != '\0')
     454             :             {
     455     1301650 :                 if (ch == ' ')
     456             :                 {
     457        1906 :                     if (!bLastWasSep)
     458        1786 :                         nCol++;
     459        1906 :                     bLastWasSep = true;
     460             :                 }
     461     1299750 :                 else if ((ch == ',' && poGDS->chDecimalSep != ',') ||
     462     1218940 :                          ch == '\t' || ch == ';')
     463             :                 {
     464       80802 :                     nCol++;
     465       80802 :                     bLastWasSep = true;
     466             :                 }
     467             :                 else
     468             :                 {
     469     1218940 :                     if (bLastWasSep)
     470             :                     {
     471      123882 :                         if (nCol == poGDS->nXIndex)
     472             :                         {
     473       41294 :                             nUsefulColsFound++;
     474       41294 :                             if (!poGDS->bSameNumberOfValuesPerLine)
     475          36 :                                 dfX = CPLAtofDelim(pszPtr, poGDS->chDecimalSep);
     476             :                         }
     477       82588 :                         else if (nCol == poGDS->nYIndex)
     478             :                         {
     479       41294 :                             nUsefulColsFound++;
     480       41294 :                             if (!poGDS->bSameNumberOfValuesPerLine)
     481          36 :                                 dfY = CPLAtofDelim(pszPtr, poGDS->chDecimalSep);
     482             :                         }
     483       41294 :                         else if (nCol == poGDS->nZIndex)
     484             :                         {
     485       41294 :                             nUsefulColsFound++;
     486       41294 :                             dfZ = CPLAtofDelim(pszPtr, poGDS->chDecimalSep);
     487             :                         }
     488             :                     }
     489     1218940 :                     bLastWasSep = false;
     490             :                 }
     491     1301650 :                 pszPtr++;
     492             :             }
     493       41301 :             nCol++;
     494             : 
     495       41301 :             if (nUsefulColsFound == 3)
     496             :             {
     497       41294 :                 if (poGDS->bSameNumberOfValuesPerLine)
     498             :                 {
     499       41258 :                     idx++;
     500             :                 }
     501             :                 else
     502             :                 {
     503          36 :                     if (fabs((dfY - dfExpectedY) / poGDS->adfGeoTransform[5]) >
     504             :                         RELATIVE_ERROR)
     505             :                     {
     506           5 :                         if (idx < 0)
     507             :                         {
     508           3 :                             const double dfYDeltaOrigin =
     509           3 :                                 dfY + 0.5 * poGDS->adfGeoTransform[5] -
     510           3 :                                 poGDS->adfGeoTransform[3];
     511           6 :                             if (!(fabs(dfYDeltaOrigin) >
     512           3 :                                       fabs(poGDS->adfGeoTransform[5]) &&
     513           3 :                                   fabs(std::round(dfYDeltaOrigin /
     514           3 :                                                   poGDS->adfGeoTransform[5]) -
     515           3 :                                        (dfYDeltaOrigin /
     516           3 :                                         poGDS->adfGeoTransform[5])) <=
     517             :                                       RELATIVE_ERROR))
     518             :                             {
     519           0 :                                 CPLError(CE_Failure, CPLE_AppDefined,
     520             :                                          "At line " CPL_FRMT_GIB
     521             :                                          ", found Y=%f instead of %f "
     522             :                                          "for nBlockYOff = %d",
     523             :                                          poGDS->nLineNum, dfY, dfExpectedY,
     524             :                                          nBlockYOff);
     525           0 :                                 return CE_Failure;
     526             :                             }
     527             :                         }
     528           5 :                         VSIFSeekL(poGDS->fp, nOffsetBefore, SEEK_SET);
     529           5 :                         nLastYOff = nBlockYOff;
     530           5 :                         poGDS->nLineNum--;
     531           5 :                         return CE_None;
     532             :                     }
     533             : 
     534          31 :                     idx = static_cast<int>((dfX -
     535          31 :                                             0.5 * poGDS->adfGeoTransform[1] -
     536          31 :                                             poGDS->adfGeoTransform[0]) /
     537          31 :                                                poGDS->adfGeoTransform[1] +
     538             :                                            0.5);
     539             :                 }
     540       41289 :                 CPLAssert(idx >= 0 && idx < nRasterXSize);
     541             : 
     542       41289 :                 if (pImage)
     543             :                 {
     544       41289 :                     if (eDataType == GDT_Float32)
     545             :                     {
     546       40416 :                         reinterpret_cast<float *>(pImage)[idx] =
     547       40416 :                             static_cast<float>(dfZ);
     548             :                     }
     549         873 :                     else if (eDataType == GDT_Int32)
     550             :                     {
     551         400 :                         reinterpret_cast<GInt32 *>(pImage)[idx] =
     552         400 :                             static_cast<GInt32>(dfZ);
     553             :                     }
     554         473 :                     else if (eDataType == GDT_Int16)
     555             :                     {
     556          13 :                         reinterpret_cast<GInt16 *>(pImage)[idx] =
     557          13 :                             static_cast<GInt16>(dfZ);
     558             :                     }
     559             :                     else
     560             :                     {
     561         460 :                         reinterpret_cast<GByte *>(pImage)[idx] =
     562         460 :                             static_cast<GByte>(dfZ);
     563             :                     }
     564             :                 }
     565             :             }
     566             :             /* Skip empty line */
     567       41296 :         } while (nCol == 1 && bLastWasSep);
     568             : 
     569       41289 :         poGDS->nDataLineNum++;
     570       41289 :         if (nCol < poGDS->nMinTokens)
     571           0 :             return CE_Failure;
     572             : 
     573       41289 :         if (idx + 1 == nRasterXSize)
     574         280 :             break;
     575       41009 :     }
     576             : 
     577         280 :     if (poGDS->bSameNumberOfValuesPerLine)
     578             :     {
     579         264 :         if (poGDS->nDataLineNum !=
     580         264 :             static_cast<GIntBig>(nBlockYOff + 1) * nBlockXSize)
     581             :         {
     582           0 :             CPLError(CE_Failure, CPLE_AssertionFailed,
     583             :                      "The file does not have the same number of values per "
     584             :                      "line as initially thought. It must be somehow corrupted");
     585           0 :             return CE_Failure;
     586             :         }
     587             :     }
     588             : 
     589         280 :     nLastYOff = nBlockYOff;
     590             : 
     591         280 :     return CE_None;
     592             : }
     593             : 
     594             : /************************************************************************/
     595             : /*                            GetMinimum()                              */
     596             : /************************************************************************/
     597             : 
     598           1 : double XYZRasterBand::GetMinimum(int *pbSuccess)
     599             : {
     600           1 :     XYZDataset *poGDS = reinterpret_cast<XYZDataset *>(poDS);
     601           1 :     if (pbSuccess)
     602           1 :         *pbSuccess = TRUE;
     603           1 :     return poGDS->dfMinZ;
     604             : }
     605             : 
     606             : /************************************************************************/
     607             : /*                            GetMaximum()                              */
     608             : /************************************************************************/
     609             : 
     610           1 : double XYZRasterBand::GetMaximum(int *pbSuccess)
     611             : {
     612           1 :     XYZDataset *poGDS = reinterpret_cast<XYZDataset *>(poDS);
     613           1 :     if (pbSuccess)
     614           1 :         *pbSuccess = TRUE;
     615           1 :     return poGDS->dfMaxZ;
     616             : }
     617             : 
     618             : /************************************************************************/
     619             : /*                          GetNoDataValue()                            */
     620             : /************************************************************************/
     621             : 
     622         298 : double XYZRasterBand::GetNoDataValue(int *pbSuccess)
     623             : {
     624         298 :     XYZDataset *poGDS = reinterpret_cast<XYZDataset *>(poDS);
     625         298 :     if (!poGDS->bSameNumberOfValuesPerLine && poGDS->dfMinZ > -32768 &&
     626          23 :         eDataType != GDT_Byte)
     627             :     {
     628           0 :         if (pbSuccess)
     629           0 :             *pbSuccess = TRUE;
     630           0 :         return (poGDS->dfMinZ > 0) ? 0 : -32768;
     631             :     }
     632         298 :     else if (!poGDS->bSameNumberOfValuesPerLine && poGDS->dfMinZ > 0 &&
     633          23 :              eDataType == GDT_Byte)
     634             :     {
     635          23 :         if (pbSuccess)
     636          23 :             *pbSuccess = TRUE;
     637          23 :         return 0;
     638             :     }
     639             : 
     640         275 :     return GDALPamRasterBand::GetNoDataValue(pbSuccess);
     641             : }
     642             : 
     643             : /************************************************************************/
     644             : /*                            ~XYZDataset()                            */
     645             : /************************************************************************/
     646             : 
     647          37 : XYZDataset::XYZDataset()
     648             :     : fp(nullptr), bHasHeaderLine(FALSE), nCommentLineCount(0),
     649             :       chDecimalSep('.'), nXIndex(-1), nYIndex(-1), nZIndex(-1), nMinTokens(0),
     650             :       nLineNum(0), nDataLineNum(GINTBIG_MAX), bSameNumberOfValuesPerLine(TRUE),
     651          37 :       dfMinZ(0), dfMaxZ(0), bEOF(false)
     652             : {
     653          37 :     adfGeoTransform[0] = 0;
     654          37 :     adfGeoTransform[1] = 1;
     655          37 :     adfGeoTransform[2] = 0;
     656          37 :     adfGeoTransform[3] = 0;
     657          37 :     adfGeoTransform[4] = 0;
     658          37 :     adfGeoTransform[5] = 1;
     659          37 : }
     660             : 
     661             : /************************************************************************/
     662             : /*                            ~XYZDataset()                            */
     663             : /************************************************************************/
     664             : 
     665          74 : XYZDataset::~XYZDataset()
     666             : 
     667             : {
     668          37 :     FlushCache(true);
     669          37 :     if (fp)
     670          37 :         VSIFCloseL(fp);
     671             : 
     672             :     {
     673          74 :         std::lock_guard<std::mutex> guard(gMutex);
     674          37 :         if (gpoActiveDS == this)
     675             :         {
     676           4 :             gpoActiveDS = nullptr;
     677           4 :             gasValues.clear();
     678           4 :             gafValues.clear();
     679             :         }
     680             :     }
     681          74 : }
     682             : 
     683             : /************************************************************************/
     684             : /*                             Identify()                               */
     685             : /************************************************************************/
     686             : 
     687       50724 : int XYZDataset::Identify(GDALOpenInfo *poOpenInfo)
     688             : {
     689             :     int bHasHeaderLine, nCommentLineCount;
     690             :     int nXIndex;
     691             :     int nYIndex;
     692             :     int nZIndex;
     693       50724 :     return IdentifyEx(poOpenInfo, bHasHeaderLine, nCommentLineCount, nXIndex,
     694      101442 :                       nYIndex, nZIndex);
     695             : }
     696             : 
     697             : /************************************************************************/
     698             : /*                            IdentifyEx()                              */
     699             : /************************************************************************/
     700             : 
     701       50746 : int XYZDataset::IdentifyEx(GDALOpenInfo *poOpenInfo, int &bHasHeaderLine,
     702             :                            int &nCommentLineCount, int &nXIndex, int &nYIndex,
     703             :                            int &nZIndex)
     704             : 
     705             : {
     706       50746 :     bHasHeaderLine = FALSE;
     707       50746 :     nCommentLineCount = 0;
     708             : 
     709      101485 :     CPLString osFilename(poOpenInfo->pszFilename);
     710       50749 :     if (EQUAL(CPLGetExtension(osFilename), "GRA") &&
     711           0 :         !poOpenInfo->IsSingleAllowedDriver("XYZ"))
     712             :     {
     713             :         // IGNFHeightASCIIGRID .GRA
     714           0 :         return FALSE;
     715             :     }
     716             : 
     717       50743 :     std::unique_ptr<GDALOpenInfo> poOpenInfoToDelete;  // keep in this scope
     718             :     /*  GZipped .xyz files are common, so automagically open them */
     719             :     /*  if the /vsigzip/ has not been explicitly passed */
     720       50742 :     if (strlen(poOpenInfo->pszFilename) > 6 &&
     721       49882 :         EQUAL(poOpenInfo->pszFilename + strlen(poOpenInfo->pszFilename) - 6,
     722           0 :               "xyz.gz") &&
     723           0 :         !STARTS_WITH_CI(poOpenInfo->pszFilename, "/vsigzip/"))
     724             :     {
     725           0 :         osFilename = "/vsigzip/";
     726           0 :         osFilename += poOpenInfo->pszFilename;
     727           0 :         poOpenInfoToDelete = std::make_unique<GDALOpenInfo>(
     728           0 :             osFilename.c_str(), GA_ReadOnly, poOpenInfo->GetSiblingFiles());
     729           0 :         poOpenInfo = poOpenInfoToDelete.get();
     730             :     }
     731             : 
     732       50743 :     if (poOpenInfo->nHeaderBytes == 0)
     733             :     {
     734       47710 :         return FALSE;
     735             :     }
     736             : 
     737             :     /* -------------------------------------------------------------------- */
     738             :     /*      Check that it looks roughly as an XYZ dataset                   */
     739             :     /* -------------------------------------------------------------------- */
     740        3033 :     const char *pszData =
     741             :         reinterpret_cast<const char *>(poOpenInfo->pabyHeader);
     742             : 
     743        3034 :     if (poOpenInfo->nHeaderBytes >= 4 && STARTS_WITH(pszData, "DSAA") &&
     744           1 :         !poOpenInfo->IsSingleAllowedDriver("XYZ"))
     745             :     {
     746             :         // Do not match GSAG datasets
     747           1 :         return FALSE;
     748             :     }
     749             : 
     750             :     /* Skip comments line at the beginning such as in */
     751             :     /* http://pubs.usgs.gov/of/2003/ofr-03-230/DATA/NSLCU.XYZ */
     752        3032 :     int i = 0;
     753        3032 :     if (pszData[i] == '/')
     754             :     {
     755           9 :         nCommentLineCount++;
     756             : 
     757           9 :         i++;
     758          81 :         for (; i < poOpenInfo->nHeaderBytes; i++)
     759             :         {
     760          81 :             const char ch = pszData[i];
     761          81 :             if (ch == 13 || ch == 10)
     762             :             {
     763           9 :                 if (ch == 13 && pszData[i + 1] == 10)
     764           0 :                     i++;
     765           9 :                 if (pszData[i + 1] == '/')
     766             :                 {
     767           0 :                     nCommentLineCount++;
     768           0 :                     i++;
     769             :                 }
     770             :                 else
     771           9 :                     break;
     772             :             }
     773             :         }
     774             :     }
     775             : 
     776        3032 :     int iStartLine = i;
     777       27310 :     for (; i < poOpenInfo->nHeaderBytes; i++)
     778             :     {
     779       27264 :         const char ch = pszData[i];
     780       27264 :         if (ch == 13 || ch == 10)
     781             :         {
     782             :             break;
     783             :         }
     784       27101 :         else if (ch == ' ' || ch == ',' || ch == '\t' || ch == ';')
     785             :             ;
     786       16289 :         else if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '+' ||
     787       10879 :                  ch == '-' || ch == 'e' || ch == 'E')
     788             :             ;
     789       10260 :         else if (ch == '"' || (ch >= 'a' && ch <= 'z') ||
     790        3818 :                  (ch >= 'A' && ch <= 'Z'))
     791        7437 :             bHasHeaderLine = TRUE;
     792             :         else
     793             :         {
     794        2823 :             return FALSE;
     795             :         }
     796             :     }
     797             : 
     798         209 :     nXIndex = -1;
     799         209 :     nYIndex = -1;
     800         209 :     nZIndex = -1;
     801         418 :     const char *pszColumnOrder = CSLFetchNameValueDef(
     802         209 :         poOpenInfo->papszOpenOptions, "COLUMN_ORDER", "AUTO");
     803         209 :     if (EQUAL(pszColumnOrder, "XYZ"))
     804             :     {
     805           2 :         nXIndex = 0;
     806           2 :         nYIndex = 1;
     807           2 :         nZIndex = 2;
     808           2 :         return TRUE;
     809             :     }
     810         207 :     else if (EQUAL(pszColumnOrder, "YXZ"))
     811             :     {
     812           4 :         nXIndex = 1;
     813           4 :         nYIndex = 0;
     814           4 :         nZIndex = 2;
     815           4 :         return TRUE;
     816             :     }
     817         203 :     else if (!EQUAL(pszColumnOrder, "AUTO"))
     818             :     {
     819           1 :         CPLError(CE_Failure, CPLE_IllegalArg,
     820             :                  "Option COLUMN_ORDER can only be XYZ, YXZ and AUTO."
     821             :                  "%s is not valid",
     822             :                  pszColumnOrder);
     823           1 :         return FALSE;
     824             :     }
     825             : 
     826         202 :     if (bHasHeaderLine)
     827             :     {
     828         124 :         CPLString osHeaderLine;
     829         124 :         osHeaderLine.assign(pszData + iStartLine, i - iStartLine);
     830             :         char **papszTokens =
     831         124 :             CSLTokenizeString2(osHeaderLine, " ,\t;", CSLT_HONOURSTRINGS);
     832         124 :         int nTokens = CSLCount(papszTokens);
     833         855 :         for (int iToken = 0; iToken < nTokens; iToken++)
     834             :         {
     835         731 :             const char *pszToken = papszTokens[iToken];
     836         731 :             if (EQUAL(pszToken, "x") || STARTS_WITH_CI(pszToken, "lon") ||
     837         701 :                 STARTS_WITH_CI(pszToken, "east"))
     838          30 :                 nXIndex = iToken;
     839         701 :             else if (EQUAL(pszToken, "y") || STARTS_WITH_CI(pszToken, "lat") ||
     840         673 :                      STARTS_WITH_CI(pszToken, "north"))
     841          28 :                 nYIndex = iToken;
     842         673 :             else if (EQUAL(pszToken, "z") || STARTS_WITH_CI(pszToken, "alt") ||
     843         645 :                      EQUAL(pszToken, "height"))
     844          28 :                 nZIndex = iToken;
     845             :         }
     846         124 :         CSLDestroy(papszTokens);
     847         124 :         if (nXIndex >= 0 && nYIndex >= 0 && nZIndex >= 0)
     848             :         {
     849          28 :             return TRUE;
     850             :         }
     851             :     }
     852             : 
     853         174 :     bool bHasFoundNewLine = false;
     854         174 :     bool bPrevWasSep = true;
     855         174 :     int nCols = 0;
     856         174 :     int nMaxCols = 0;
     857       11566 :     for (; i < poOpenInfo->nHeaderBytes; i++)
     858             :     {
     859       11488 :         char ch = pszData[i];
     860       11488 :         if (ch == 13 || ch == 10)
     861             :         {
     862         832 :             bHasFoundNewLine = true;
     863         832 :             if (!bPrevWasSep)
     864             :             {
     865         685 :                 nCols++;
     866         685 :                 if (nCols > nMaxCols)
     867          37 :                     nMaxCols = nCols;
     868             :             }
     869         832 :             bPrevWasSep = true;
     870         832 :             nCols = 0;
     871             :         }
     872       10656 :         else if (ch == ' ' || ch == ',' || ch == '\t' || ch == ';')
     873             :         {
     874        1738 :             if (!bPrevWasSep)
     875             :             {
     876        1692 :                 nCols++;
     877        1692 :                 if (nCols > nMaxCols)
     878          93 :                     nMaxCols = nCols;
     879             :             }
     880        1738 :             bPrevWasSep = true;
     881             :         }
     882        8918 :         else if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '+' ||
     883          96 :                  ch == '-' || ch == 'e' || ch == 'E')
     884             :         {
     885        8822 :             bPrevWasSep = false;
     886             :         }
     887             :         else
     888             :         {
     889          96 :             return FALSE;
     890             :         }
     891             :     }
     892             : 
     893          78 :     return bHasFoundNewLine && nMaxCols >= 3;
     894             : }
     895             : 
     896             : /************************************************************************/
     897             : /*                                Open()                                */
     898             : /************************************************************************/
     899             : 
     900          24 : GDALDataset *XYZDataset::Open(GDALOpenInfo *poOpenInfo)
     901             : 
     902             : {
     903             :     int bHasHeaderLine;
     904          24 :     int nCommentLineCount = 0;
     905             : 
     906          24 :     int nXIndex = -1;
     907          24 :     int nYIndex = -1;
     908          24 :     int nZIndex = -1;
     909          24 :     if (!IdentifyEx(poOpenInfo, bHasHeaderLine, nCommentLineCount, nXIndex,
     910             :                     nYIndex, nZIndex))
     911           0 :         return nullptr;
     912             : 
     913          48 :     CPLString osFilename(poOpenInfo->pszFilename);
     914             : 
     915             :     /*  GZipped .xyz files are common, so automagically open them */
     916             :     /*  if the /vsigzip/ has not been explicitly passed */
     917          24 :     if (strlen(poOpenInfo->pszFilename) > 6 &&
     918          24 :         EQUAL(poOpenInfo->pszFilename + strlen(poOpenInfo->pszFilename) - 6,
     919           0 :               "xyz.gz") &&
     920           0 :         !STARTS_WITH_CI(poOpenInfo->pszFilename, "/vsigzip/"))
     921             :     {
     922           0 :         osFilename = "/vsigzip/";
     923           0 :         osFilename += poOpenInfo->pszFilename;
     924             :     }
     925             : 
     926             :     /* -------------------------------------------------------------------- */
     927             :     /*      Find dataset characteristics                                    */
     928             :     /* -------------------------------------------------------------------- */
     929          24 :     VSILFILE *fp = VSIFOpenL(osFilename.c_str(), "rb");
     930          24 :     if (fp == nullptr)
     931           0 :         return nullptr;
     932             : 
     933             :     /* For better performance of CPLReadLine2L() we create a buffered reader */
     934             :     /* (except for /vsigzip/ since it has one internally) */
     935          24 :     if (!STARTS_WITH_CI(poOpenInfo->pszFilename, "/vsigzip/"))
     936          24 :         fp = VSICreateBufferedReaderHandle(fp);
     937             : 
     938          24 :     int nMinTokens = 0;
     939             : 
     940          24 :     for (int i = 0; i < nCommentLineCount; i++)
     941             :     {
     942           0 :         if (CPLReadLine2L(fp, 100, nullptr) == nullptr)
     943             :         {
     944           0 :             VSIFCloseL(fp);
     945           0 :             return nullptr;
     946             :         }
     947             :     }
     948             : 
     949             :     /* -------------------------------------------------------------------- */
     950             :     /*      Parse header line                                               */
     951             :     /* -------------------------------------------------------------------- */
     952          24 :     if (bHasHeaderLine)
     953             :     {
     954          17 :         const char *pszLine = CPLReadLine2L(fp, 100, nullptr);
     955          17 :         if (pszLine == nullptr)
     956             :         {
     957           0 :             VSIFCloseL(fp);
     958           0 :             return nullptr;
     959             :         }
     960          17 :         if (nXIndex < 0 || nYIndex < 0 || nZIndex < 0)
     961             :         {
     962           1 :             CPLError(CE_Warning, CPLE_AppDefined,
     963             :                      "Could not find one of the X, Y or Z column names in "
     964             :                      "header line. Defaulting to the first 3 columns");
     965           1 :             nXIndex = 0;
     966           1 :             nYIndex = 1;
     967           1 :             nZIndex = 2;
     968             :         }
     969             :     }
     970           7 :     else if (nXIndex < 0 || nYIndex < 0 || nZIndex < 0)
     971             :     {
     972           6 :         nXIndex = 0;
     973           6 :         nYIndex = 1;
     974           6 :         nZIndex = 2;
     975             :     }
     976          24 :     nMinTokens = 1 + std::max(std::max(nXIndex, nYIndex), nZIndex);
     977             : 
     978             :     /* -------------------------------------------------------------------- */
     979             :     /*      Parse data lines                                                */
     980             :     /* -------------------------------------------------------------------- */
     981             : 
     982          24 :     GIntBig nLineNum = 0;
     983          24 :     GIntBig nDataLineNum = 0;
     984          24 :     double dfX = 0.0;
     985          24 :     double dfY = 0.0;
     986          24 :     double dfZ = 0.0;
     987          24 :     double dfMinX = 0.0;
     988          24 :     double dfMinY = 0.0;
     989          24 :     double dfMaxX = 0.0;
     990          24 :     double dfMaxY = 0.0;
     991          24 :     double dfMinZ = 0.0;
     992          24 :     double dfMaxZ = 0.0;
     993          24 :     double dfLastX = 0.0;
     994          24 :     double dfLastY = 0.0;
     995          48 :     std::vector<double> adfStepX;
     996          48 :     std::vector<double> adfStepY;
     997          24 :     GDALDataType eDT = GDT_Byte;
     998          24 :     bool bSameNumberOfValuesPerLine = true;
     999          24 :     char chDecimalSep = '\0';
    1000          24 :     int nStepYSign = 0;
    1001          24 :     bool bColOrganization = false;
    1002             : 
    1003             :     const char *pszLine;
    1004          24 :     GIntBig nCountStepX = 0;
    1005          24 :     GIntBig nCountStepY = 0;
    1006       41384 :     while ((pszLine = CPLReadLine2L(fp, 100, nullptr)) != nullptr)
    1007             :     {
    1008       41360 :         nLineNum++;
    1009             : 
    1010       41360 :         const char *pszPtr = pszLine;
    1011             :         char ch;
    1012       41360 :         int nCol = 0;
    1013       41360 :         bool bLastWasSep = true;
    1014       41360 :         if (chDecimalSep == '\0')
    1015             :         {
    1016         871 :             int nCountComma = 0;
    1017         871 :             int nCountFieldSep = 0;
    1018       15575 :             while ((ch = *pszPtr) != '\0')
    1019             :             {
    1020       14715 :                 if (ch == '.')
    1021             :                 {
    1022          11 :                     chDecimalSep = '.';
    1023          11 :                     break;
    1024             :                 }
    1025       14704 :                 else if (ch == ',')
    1026             :                 {
    1027           3 :                     nCountComma++;
    1028           3 :                     bLastWasSep = false;
    1029             :                 }
    1030       14701 :                 else if (ch == ' ')
    1031             :                 {
    1032        1701 :                     if (!bLastWasSep)
    1033        1698 :                         nCountFieldSep++;
    1034        1701 :                     bLastWasSep = true;
    1035             :                 }
    1036       13000 :                 else if (ch == '\t' || ch == ';')
    1037             :                 {
    1038           4 :                     nCountFieldSep++;
    1039           4 :                     bLastWasSep = true;
    1040             :                 }
    1041             :                 else
    1042       12996 :                     bLastWasSep = false;
    1043       14704 :                 pszPtr++;
    1044             :             }
    1045         871 :             if (chDecimalSep == '\0')
    1046             :             {
    1047             :                 /* 1,2,3 */
    1048         860 :                 if (nCountComma >= 2 && nCountFieldSep == 0)
    1049           1 :                     chDecimalSep = '.';
    1050             :                 /* 23,5;33;45 */
    1051         859 :                 else if (nCountComma > 0 && nCountFieldSep > 0)
    1052           1 :                     chDecimalSep = ',';
    1053             :             }
    1054         871 :             pszPtr = pszLine;
    1055         871 :             bLastWasSep = true;
    1056             :         }
    1057             : 
    1058       41360 :         char chLocalDecimalSep = chDecimalSep ? chDecimalSep : '.';
    1059       41360 :         int nUsefulColsFound = 0;
    1060     1343410 :         while ((ch = *pszPtr) != '\0')
    1061             :         {
    1062     1302050 :             if (ch == ' ')
    1063             :             {
    1064        1984 :                 if (!bLastWasSep)
    1065        1864 :                     nCol++;
    1066        1984 :                 bLastWasSep = true;
    1067             :             }
    1068     1300070 :             else if ((ch == ',' && chLocalDecimalSep != ',') || ch == '\t' ||
    1069             :                      ch == ';')
    1070             :             {
    1071       80826 :                 nCol++;
    1072       80826 :                 bLastWasSep = true;
    1073             :             }
    1074             :             else
    1075             :             {
    1076     1219240 :                 if (bLastWasSep)
    1077             :                 {
    1078      124035 :                     if (nCol == nXIndex)
    1079             :                     {
    1080       41345 :                         nUsefulColsFound++;
    1081       41345 :                         dfX = CPLAtofDelim(pszPtr, chLocalDecimalSep);
    1082             :                     }
    1083       82690 :                     else if (nCol == nYIndex)
    1084             :                     {
    1085       41345 :                         nUsefulColsFound++;
    1086       41345 :                         dfY = CPLAtofDelim(pszPtr, chLocalDecimalSep);
    1087             :                     }
    1088       41345 :                     else if (nCol == nZIndex)
    1089             :                     {
    1090       41345 :                         nUsefulColsFound++;
    1091       41345 :                         dfZ = CPLAtofDelim(pszPtr, chLocalDecimalSep);
    1092       41345 :                         if (nDataLineNum == 0)
    1093             :                         {
    1094          24 :                             dfMinZ = dfZ;
    1095          24 :                             dfMaxZ = dfZ;
    1096             :                         }
    1097       41321 :                         else if (dfZ < dfMinZ)
    1098             :                         {
    1099          19 :                             dfMinZ = dfZ;
    1100             :                         }
    1101       41302 :                         else if (dfZ > dfMaxZ)
    1102             :                         {
    1103         297 :                             dfMaxZ = dfZ;
    1104             :                         }
    1105             : 
    1106       41345 :                         if (dfZ < INT_MIN || dfZ > INT_MAX)
    1107             :                         {
    1108           0 :                             eDT = GDT_Float32;
    1109             :                         }
    1110             :                         else
    1111             :                         {
    1112       41345 :                             int nZ = static_cast<int>(dfZ);
    1113       41345 :                             if (static_cast<double>(nZ) != dfZ)
    1114             :                             {
    1115       28327 :                                 eDT = GDT_Float32;
    1116             :                             }
    1117       13018 :                             else if ((eDT == GDT_Byte || eDT == GDT_Int16)
    1118             :                                      // cppcheck-suppress
    1119             :                                      // knownConditionTrueFalse
    1120        5188 :                                      && (nZ < 0 || nZ > 255))
    1121             :                             {
    1122          10 :                                 if (nZ < -32768 || nZ > 32767)
    1123           0 :                                     eDT = GDT_Int32;
    1124             :                                 else
    1125          10 :                                     eDT = GDT_Int16;
    1126             :                             }
    1127             :                         }
    1128             :                     }
    1129             :                 }
    1130     1219240 :                 bLastWasSep = false;
    1131             :             }
    1132     1302050 :             pszPtr++;
    1133             :         }
    1134             :         /* skip empty lines */
    1135       41360 :         if (bLastWasSep && nCol == 0)
    1136             :         {
    1137          15 :             continue;
    1138             :         }
    1139       41345 :         nDataLineNum++;
    1140       41345 :         nCol++;
    1141       41345 :         if (nCol < nMinTokens)
    1142             :         {
    1143           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1144             :                      "At line " CPL_FRMT_GIB
    1145             :                      ", found %d tokens. Expected %d at least",
    1146             :                      nLineNum, nCol, nMinTokens);
    1147           0 :             VSIFCloseL(fp);
    1148           0 :             return nullptr;
    1149             :         }
    1150       41345 :         if (nUsefulColsFound != 3)
    1151             :         {
    1152           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1153             :                      "At line " CPL_FRMT_GIB
    1154             :                      ", did not find X, Y and/or Z values",
    1155             :                      nLineNum);
    1156           0 :             VSIFCloseL(fp);
    1157           0 :             return nullptr;
    1158             :         }
    1159             : 
    1160       41345 :         if (nDataLineNum == 1)
    1161             :         {
    1162          24 :             dfMinX = dfX;
    1163          24 :             dfMaxX = dfX;
    1164          24 :             dfMinY = dfY;
    1165          24 :             dfMaxY = dfY;
    1166             :         }
    1167       41321 :         else if (nDataLineNum == 2 && dfX == dfLastX)
    1168             :         {
    1169             :             // Detect datasets organized by columns
    1170           7 :             if (dfY == dfLastY)
    1171             :             {
    1172           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1173             :                          "Ungridded dataset: At line " CPL_FRMT_GIB
    1174             :                          ", Failed to detect grid layout.",
    1175             :                          nLineNum);
    1176           0 :                 VSIFCloseL(fp);
    1177           0 :                 return nullptr;
    1178             :             }
    1179             : 
    1180           7 :             bColOrganization = true;
    1181           7 :             const double dfStepY = dfY - dfLastY;
    1182           7 :             adfStepY.push_back(fabs(dfStepY));
    1183           7 :             nStepYSign = dfStepY > 0 ? 1 : -1;
    1184             :         }
    1185       41314 :         else if (bColOrganization)
    1186             :         {
    1187          29 :             const double dfStepX = dfX - dfLastX;
    1188          29 :             if (dfStepX == 0)
    1189             :             {
    1190          19 :                 const double dfStepY = dfY - dfLastY;
    1191          19 :                 const double dfExpectedStepY = adfStepY.back() * nStepYSign;
    1192          19 :                 if (fabs((dfStepY - dfExpectedStepY) / dfExpectedStepY) >
    1193             :                     RELATIVE_ERROR)
    1194             :                 {
    1195           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1196             :                              "Ungridded dataset: At line " CPL_FRMT_GIB
    1197             :                              ", Y spacing was %f. Expected %f",
    1198             :                              nLineNum, dfStepY, dfExpectedStepY);
    1199           0 :                     VSIFCloseL(fp);
    1200           0 :                     return nullptr;
    1201             :                 }
    1202             :             }
    1203          10 :             else if (dfStepX > 0)
    1204             :             {
    1205           8 :                 if (adfStepX.empty())
    1206             :                 {
    1207           5 :                     adfStepX.push_back(dfStepX);
    1208             :                 }
    1209           3 :                 else if (fabs((dfStepX - adfStepX.back()) / adfStepX.back()) >
    1210             :                          RELATIVE_ERROR)
    1211             :                 {
    1212           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1213             :                              "Ungridded dataset: At line " CPL_FRMT_GIB
    1214             :                              ", X spacing was %f. Expected %f",
    1215           0 :                              nLineNum, dfStepX, adfStepX.back());
    1216           0 :                     VSIFCloseL(fp);
    1217           0 :                     return nullptr;
    1218             :                 }
    1219             :             }
    1220           2 :             else if (nDataLineNum == 3)
    1221             :             {
    1222           1 :                 const double dfStepY = dfY - dfLastY;
    1223           1 :                 const double dfLastSignedStepY = nStepYSign * adfStepY.back();
    1224           1 :                 if (dfStepY * dfLastSignedStepY > 0 &&
    1225           1 :                     (fabs(dfStepY - dfLastSignedStepY) <=
    1226           1 :                          RELATIVE_ERROR * fabs(dfLastSignedStepY)
    1227             : #ifdef multiple_of_step_y_not_yet_supported
    1228             :                      ||
    1229             :                      (fabs(dfStepY) > fabs(dfLastSignedStepY) &&
    1230             :                       fabs(std::round(dfStepY / dfLastSignedStepY) -
    1231             :                            (dfStepY / dfLastSignedStepY)) <= RELATIVE_ERROR) ||
    1232             :                      (fabs(dfLastSignedStepY) > fabs(dfStepY) &&
    1233             :                       fabs(std::round(dfLastSignedStepY / dfStepY) -
    1234             :                            (dfLastSignedStepY / dfStepY)) <= RELATIVE_ERROR)
    1235             : #endif
    1236             :                          ))
    1237             :                 {
    1238             :                     // Assume it is a file starting with something like:
    1239             :                     // 371999.50 5806917.50 41.21
    1240             :                     // 371999.50 5806918.50 51.99
    1241             :                     // 371998.50 5806919.50 53.50
    1242             :                     // 371999.50 5806919.50 53.68
    1243           1 :                     adfStepX.push_back(dfLastX - dfX);
    1244           1 :                     bColOrganization = false;
    1245             :                 }
    1246             :                 else
    1247             :                 {
    1248           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1249             :                              "Ungridded dataset: At line " CPL_FRMT_GIB
    1250             :                              ", X spacing was %f. Expected >0 value",
    1251             :                              nLineNum, dfX - dfLastX);
    1252           0 :                     VSIFCloseL(fp);
    1253           0 :                     return nullptr;
    1254             :                 }
    1255             :             }
    1256           1 :             else if (!adfStepX.empty() &&
    1257           0 :                      fabs(std::round(-dfStepX / adfStepX[0]) -
    1258           0 :                           (-dfStepX / adfStepX[0])) <= RELATIVE_ERROR)
    1259             :             {
    1260           0 :                 bColOrganization = false;
    1261             :             }
    1262           1 :             else if (adfStepX.empty())
    1263             :             {
    1264           1 :                 adfStepX.push_back(fabs(dfStepX));
    1265           1 :                 bColOrganization = false;
    1266             :             }
    1267             :             else
    1268             :             {
    1269           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1270             :                          "Ungridded dataset: At line " CPL_FRMT_GIB
    1271             :                          ", X spacing was %f. Expected a multiple of %f",
    1272           0 :                          nLineNum, dfStepX, adfStepX[0]);
    1273           0 :                 VSIFCloseL(fp);
    1274           0 :                 return nullptr;
    1275             :             }
    1276             :         }
    1277             :         else
    1278             :         {
    1279       41285 :             double dfStepY = dfY - dfLastY;
    1280       41285 :             if (dfStepY == 0.0)
    1281             :             {
    1282       41018 :                 const double dfStepX = dfX - dfLastX;
    1283       41018 :                 if (dfStepX <= 0)
    1284             :                 {
    1285           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1286             :                              "Ungridded dataset: At line " CPL_FRMT_GIB
    1287             :                              ", X spacing was %f. Expected >0 value",
    1288             :                              nLineNum, dfStepX);
    1289           0 :                     VSIFCloseL(fp);
    1290           0 :                     return nullptr;
    1291             :                 }
    1292       41018 :                 if (std::find(adfStepX.begin(), adfStepX.end(), dfStepX) ==
    1293       82036 :                     adfStepX.end())
    1294             :                 {
    1295          39 :                     bool bAddNewValue = true;
    1296          39 :                     std::vector<double>::iterator oIter = adfStepX.begin();
    1297          39 :                     std::vector<double> adfStepXNew;
    1298          40 :                     while (oIter != adfStepX.end())
    1299             :                     {
    1300          22 :                         if (fabs((dfStepX - *oIter) / dfStepX) < RELATIVE_ERROR)
    1301             :                         {
    1302          20 :                             double dfNewVal = *oIter;
    1303          20 :                             if (nCountStepX > 0)
    1304             :                             {
    1305             :                                 // Update mean step
    1306             :                                 /* n * mean(n) = (n-1) * mean(n-1) + val(n)
    1307             :                                 mean(n) = mean(n-1) + (val(n) - mean(n-1)) / n
    1308             :                               */
    1309          20 :                                 nCountStepX++;
    1310          20 :                                 dfNewVal += (dfStepX - *oIter) / nCountStepX;
    1311             :                             }
    1312             : 
    1313          20 :                             adfStepXNew.push_back(dfNewVal);
    1314          20 :                             bAddNewValue = false;
    1315          20 :                             break;
    1316             :                         }
    1317           3 :                         else if (dfStepX < *oIter &&
    1318           1 :                                  fabs(*oIter -
    1319           1 :                                       static_cast<int>(*oIter / dfStepX + 0.5) *
    1320           1 :                                           dfStepX) /
    1321           1 :                                          dfStepX <
    1322             :                                      RELATIVE_ERROR)
    1323             :                         {
    1324           1 :                             nCountStepX = -1;  // disable update of mean
    1325           1 :                             ++oIter;
    1326             :                         }
    1327           2 :                         else if (dfStepX > *oIter &&
    1328           2 :                                  fabs(dfStepX -
    1329           1 :                                       static_cast<int>(dfStepX / *oIter + 0.5) *
    1330           1 :                                           (*oIter)) /
    1331           1 :                                          dfStepX <
    1332             :                                      RELATIVE_ERROR)
    1333             :                         {
    1334           1 :                             nCountStepX = -1;  // disable update of mean
    1335           1 :                             bAddNewValue = false;
    1336           1 :                             adfStepXNew.push_back(*oIter);
    1337           1 :                             break;
    1338             :                         }
    1339             :                         else
    1340             :                         {
    1341           0 :                             adfStepXNew.push_back(*oIter);
    1342           0 :                             ++oIter;
    1343             :                         }
    1344             :                     }
    1345          39 :                     adfStepX = std::move(adfStepXNew);
    1346          39 :                     if (bAddNewValue)
    1347             :                     {
    1348          18 :                         CPLDebug("XYZ", "New stepX=%.15f", dfStepX);
    1349          18 :                         adfStepX.push_back(dfStepX);
    1350          18 :                         if (adfStepX.size() == 1 && nCountStepX == 0)
    1351             :                         {
    1352          17 :                             nCountStepX++;
    1353             :                         }
    1354           1 :                         else if (adfStepX.size() == 2)
    1355             :                         {
    1356           0 :                             nCountStepX = -1;  // disable update of mean
    1357             :                         }
    1358           1 :                         else if (adfStepX.size() == 10)
    1359             :                         {
    1360           0 :                             CPLError(
    1361             :                                 CE_Failure, CPLE_AppDefined,
    1362             :                                 "Ungridded dataset: too many stepX values");
    1363           0 :                             VSIFCloseL(fp);
    1364           0 :                             return nullptr;
    1365             :                         }
    1366             :                     }
    1367             :                 }
    1368             :             }
    1369             :             else
    1370             :             {
    1371         267 :                 int bNewStepYSign = (dfStepY < 0.0) ? -1 : 1;
    1372         267 :                 if (nStepYSign == 0)
    1373          17 :                     nStepYSign = bNewStepYSign;
    1374         250 :                 else if (nStepYSign != bNewStepYSign)
    1375             :                 {
    1376           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1377             :                              "Ungridded dataset: At line " CPL_FRMT_GIB
    1378             :                              ", change of Y direction",
    1379             :                              nLineNum);
    1380           0 :                     VSIFCloseL(fp);
    1381           0 :                     return nullptr;
    1382             :                 }
    1383         267 :                 if (bNewStepYSign < 0)
    1384         244 :                     dfStepY = -dfStepY;
    1385         267 :                 nCountStepY++;
    1386         267 :                 if (adfStepY.empty())
    1387             :                 {
    1388          17 :                     adfStepY.push_back(dfStepY);
    1389             :                 }
    1390         250 :                 else if (fabs((adfStepY[0] - dfStepY) / dfStepY) >
    1391             :                          RELATIVE_ERROR)
    1392             :                 {
    1393           4 :                     if (dfStepY > adfStepY[0] &&
    1394           2 :                         fabs(std::round(dfStepY / adfStepY[0]) -
    1395           2 :                              (dfStepY / adfStepY[0])) <= RELATIVE_ERROR)
    1396             :                     {
    1397             :                         // The new step is a multiple of the previous one,
    1398             :                         // which means we have a missing line: OK
    1399             :                     }
    1400             :                     else
    1401             :                     {
    1402           0 :                         CPLDebug("XYZ", "New stepY=%.15f prev stepY=%.15f",
    1403           0 :                                  dfStepY, adfStepY[0]);
    1404           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    1405             :                                  "Ungridded dataset: At line " CPL_FRMT_GIB
    1406             :                                  ", too many stepY values",
    1407             :                                  nLineNum);
    1408           0 :                         VSIFCloseL(fp);
    1409           0 :                         return nullptr;
    1410             :                     }
    1411             :                 }
    1412             :                 else
    1413             :                 {
    1414             :                     // Update mean step
    1415         248 :                     adfStepY[0] += (dfStepY - adfStepY[0]) / nCountStepY;
    1416             :                 }
    1417             :             }
    1418             :         }
    1419             : 
    1420       41345 :         if (dfX < dfMinX)
    1421           4 :             dfMinX = dfX;
    1422       41345 :         if (dfX > dfMaxX)
    1423         266 :             dfMaxX = dfX;
    1424       41345 :         if (dfY < dfMinY)
    1425         248 :             dfMinY = dfY;
    1426       41345 :         if (dfY > dfMaxY)
    1427          33 :             dfMaxY = dfY;
    1428             : 
    1429       41345 :         dfLastX = dfX;
    1430       41345 :         dfLastY = dfY;
    1431             :     }
    1432             : 
    1433          24 :     if (adfStepX.size() != 1 || adfStepX[0] == 0)
    1434             :     {
    1435           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Couldn't determine X spacing");
    1436           0 :         VSIFCloseL(fp);
    1437           0 :         return nullptr;
    1438             :     }
    1439             : 
    1440          24 :     if (adfStepY.size() != 1 || adfStepY[0] == 0)
    1441             :     {
    1442           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Couldn't determine Y spacing");
    1443           0 :         VSIFCloseL(fp);
    1444           0 :         return nullptr;
    1445             :     }
    1446             : 
    1447             :     // Decide for a north-up organization
    1448          24 :     if (bColOrganization)
    1449           5 :         nStepYSign = -1;
    1450             : 
    1451          24 :     const double dfXSize = 1 + ((dfMaxX - dfMinX) / adfStepX[0] + 0.5);
    1452          24 :     const double dfYSize = 1 + ((dfMaxY - dfMinY) / adfStepY[0] + 0.5);
    1453             :     // Test written such as to detect NaN values
    1454          24 :     if (!(dfXSize > 0 && dfXSize < INT_MAX) ||
    1455          24 :         !(dfYSize > 0 && dfYSize < INT_MAX))
    1456             :     {
    1457           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid dimensions");
    1458           0 :         VSIFCloseL(fp);
    1459           0 :         return nullptr;
    1460             :     }
    1461          24 :     const int nXSize = static_cast<int>(dfXSize);
    1462          24 :     const int nYSize = static_cast<int>(dfYSize);
    1463          24 :     const double dfStepX = (dfMaxX - dfMinX) / (nXSize - 1);
    1464          24 :     const double dfStepY = (dfMaxY - dfMinY) / (nYSize - 1) * nStepYSign;
    1465             : 
    1466             : #ifdef DEBUG_VERBOSE
    1467             :     CPLDebug("XYZ", "minx=%f maxx=%f stepx=%f", dfMinX, dfMaxX, dfStepX);
    1468             :     CPLDebug("XYZ", "miny=%f maxy=%f stepy=%f", dfMinY, dfMaxY, dfStepY);
    1469             : #endif
    1470             : 
    1471          24 :     if (nDataLineNum != static_cast<GIntBig>(nXSize) * nYSize)
    1472             :     {
    1473           5 :         if (bColOrganization)
    1474             :         {
    1475           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    1476             :                      "The XYZ driver does not support datasets organized by "
    1477             :                      "columns with missing values");
    1478           0 :             VSIFCloseL(fp);
    1479           0 :             return nullptr;
    1480             :         }
    1481           5 :         bSameNumberOfValuesPerLine = false;
    1482             :     }
    1483          19 :     else if (bColOrganization && nDataLineNum > 100 * 1000 * 1000)
    1484             :     {
    1485           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1486             :                  "The XYZ driver cannot load datasets organized by "
    1487             :                  "columns with more than 100 million points");
    1488           0 :         VSIFCloseL(fp);
    1489           0 :         return nullptr;
    1490             :     }
    1491             : 
    1492          24 :     const bool bIngestAll = bColOrganization;
    1493          24 :     if (bIngestAll)
    1494             :     {
    1495           5 :         if (eDT == GDT_Int32)
    1496           0 :             eDT = GDT_Float32;
    1497           5 :         else if (eDT == GDT_Byte)
    1498           1 :             eDT = GDT_Int16;
    1499           5 :         CPLAssert(eDT == GDT_Int16 || eDT == GDT_Float32);
    1500             :     }
    1501             : 
    1502          24 :     if (poOpenInfo->eAccess == GA_Update)
    1503             :     {
    1504           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1505             :                  "The XYZ driver does not support update access to existing"
    1506             :                  " datasets.\n");
    1507           0 :         VSIFCloseL(fp);
    1508           0 :         return nullptr;
    1509             :     }
    1510             : 
    1511             :     /* -------------------------------------------------------------------- */
    1512             :     /*      Create a corresponding GDALDataset.                             */
    1513             :     /* -------------------------------------------------------------------- */
    1514          24 :     XYZDataset *poDS = new XYZDataset();
    1515          24 :     poDS->fp = fp;
    1516          24 :     poDS->bHasHeaderLine = bHasHeaderLine;
    1517          24 :     poDS->nCommentLineCount = nCommentLineCount;
    1518          24 :     poDS->chDecimalSep = chDecimalSep ? chDecimalSep : '.';
    1519          24 :     poDS->nXIndex = nXIndex;
    1520          24 :     poDS->nYIndex = nYIndex;
    1521          24 :     poDS->nZIndex = nZIndex;
    1522          24 :     poDS->nMinTokens = nMinTokens;
    1523          24 :     poDS->nRasterXSize = nXSize;
    1524          24 :     poDS->nRasterYSize = nYSize;
    1525          24 :     poDS->adfGeoTransform[0] = dfMinX - dfStepX / 2;
    1526          24 :     poDS->adfGeoTransform[1] = dfStepX;
    1527          24 :     poDS->adfGeoTransform[3] =
    1528          24 :         (dfStepY < 0) ? dfMaxY - dfStepY / 2 : dfMinY - dfStepY / 2;
    1529          24 :     poDS->adfGeoTransform[5] = dfStepY;
    1530          24 :     poDS->bSameNumberOfValuesPerLine = bSameNumberOfValuesPerLine;
    1531          24 :     poDS->dfMinZ = dfMinZ;
    1532          24 :     poDS->dfMaxZ = dfMaxZ;
    1533          24 :     poDS->bIngestAll = bIngestAll;
    1534             : #ifdef DEBUG_VERBOSE
    1535             :     CPLDebug("XYZ", "bSameNumberOfValuesPerLine = %d",
    1536             :              bSameNumberOfValuesPerLine);
    1537             : #endif
    1538             : 
    1539          24 :     if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
    1540             :     {
    1541           0 :         delete poDS;
    1542           0 :         return nullptr;
    1543             :     }
    1544             : 
    1545             :     /* -------------------------------------------------------------------- */
    1546             :     /*      Create band information objects.                                */
    1547             :     /* -------------------------------------------------------------------- */
    1548          24 :     poDS->nBands = 1;
    1549          48 :     for (int i = 0; i < poDS->nBands; i++)
    1550          24 :         poDS->SetBand(i + 1, new XYZRasterBand(poDS, i + 1, eDT));
    1551             : 
    1552             :     /* -------------------------------------------------------------------- */
    1553             :     /*      Initialize any PAM information.                                 */
    1554             :     /* -------------------------------------------------------------------- */
    1555          24 :     poDS->SetDescription(poOpenInfo->pszFilename);
    1556          24 :     poDS->TryLoadXML();
    1557             : 
    1558             :     /* -------------------------------------------------------------------- */
    1559             :     /*      Support overviews.                                              */
    1560             :     /* -------------------------------------------------------------------- */
    1561          24 :     poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
    1562          24 :     return poDS;
    1563             : }
    1564             : 
    1565             : /************************************************************************/
    1566             : /*                             CreateCopy()                             */
    1567             : /************************************************************************/
    1568             : 
    1569          31 : GDALDataset *XYZDataset::CreateCopy(const char *pszFilename,
    1570             :                                     GDALDataset *poSrcDS, int bStrict,
    1571             :                                     char **papszOptions,
    1572             :                                     GDALProgressFunc pfnProgress,
    1573             :                                     void *pProgressData)
    1574             : {
    1575             :     /* -------------------------------------------------------------------- */
    1576             :     /*      Some some rudimentary checks                                    */
    1577             :     /* -------------------------------------------------------------------- */
    1578          31 :     int nBands = poSrcDS->GetRasterCount();
    1579          31 :     if (nBands == 0)
    1580             :     {
    1581           1 :         CPLError(
    1582             :             CE_Failure, CPLE_NotSupported,
    1583             :             "XYZ driver does not support source dataset with zero band.\n");
    1584           1 :         return nullptr;
    1585             :     }
    1586             : 
    1587          30 :     if (nBands != 1)
    1588             :     {
    1589           4 :         CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
    1590             :                  "XYZ driver only uses the first band of the dataset.\n");
    1591           4 :         if (bStrict)
    1592           4 :             return nullptr;
    1593             :     }
    1594             : 
    1595          26 :     if (pfnProgress && !pfnProgress(0.0, nullptr, pProgressData))
    1596           0 :         return nullptr;
    1597             : 
    1598             :     /* -------------------------------------------------------------------- */
    1599             :     /*      Get source dataset info                                         */
    1600             :     /* -------------------------------------------------------------------- */
    1601             : 
    1602          26 :     int nXSize = poSrcDS->GetRasterXSize();
    1603          26 :     int nYSize = poSrcDS->GetRasterYSize();
    1604             :     double adfGeoTransform[6];
    1605          26 :     poSrcDS->GetGeoTransform(adfGeoTransform);
    1606          26 :     if (adfGeoTransform[2] != 0 || adfGeoTransform[4] != 0)
    1607             :     {
    1608           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1609             :                  "XYZ driver does not support CreateCopy() from skewed or "
    1610             :                  "rotated dataset.\n");
    1611           0 :         return nullptr;
    1612             :     }
    1613             : 
    1614          26 :     const GDALDataType eSrcDT = poSrcDS->GetRasterBand(1)->GetRasterDataType();
    1615             :     GDALDataType eReqDT;
    1616          26 :     if (eSrcDT == GDT_Byte || eSrcDT == GDT_Int16 || eSrcDT == GDT_UInt16 ||
    1617             :         eSrcDT == GDT_Int32)
    1618          18 :         eReqDT = GDT_Int32;
    1619             :     else
    1620           8 :         eReqDT = GDT_Float32;
    1621             : 
    1622             :     /* -------------------------------------------------------------------- */
    1623             :     /*      Create target file                                              */
    1624             :     /* -------------------------------------------------------------------- */
    1625             : 
    1626          26 :     VSILFILE *fp = VSIFOpenL(pszFilename, "wb");
    1627          26 :     if (fp == nullptr)
    1628             :     {
    1629           3 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot create %s", pszFilename);
    1630           3 :         return nullptr;
    1631             :     }
    1632             : 
    1633             :     /* -------------------------------------------------------------------- */
    1634             :     /*      Read creation options                                           */
    1635             :     /* -------------------------------------------------------------------- */
    1636          23 :     const char *pszColSep = CSLFetchNameValue(papszOptions, "COLUMN_SEPARATOR");
    1637          23 :     if (pszColSep == nullptr)
    1638          22 :         pszColSep = " ";
    1639           1 :     else if (EQUAL(pszColSep, "COMMA"))
    1640           0 :         pszColSep = ",";
    1641           1 :     else if (EQUAL(pszColSep, "SPACE"))
    1642           0 :         pszColSep = " ";
    1643           1 :     else if (EQUAL(pszColSep, "SEMICOLON"))
    1644           0 :         pszColSep = ";";
    1645           1 :     else if (EQUAL(pszColSep, "\\t") || EQUAL(pszColSep, "TAB"))
    1646           0 :         pszColSep = "\t";
    1647             : #ifdef DEBUG_VERBOSE
    1648             :     else
    1649             :         CPLDebug("XYZ", "Using raw column separator: '%s' ", pszColSep);
    1650             : #endif
    1651             : 
    1652             :     const char *pszAddHeaderLine =
    1653          23 :         CSLFetchNameValue(papszOptions, "ADD_HEADER_LINE");
    1654          23 :     if (pszAddHeaderLine != nullptr && CPLTestBool(pszAddHeaderLine))
    1655             :     {
    1656           1 :         VSIFPrintfL(fp, "X%sY%sZ\n", pszColSep, pszColSep);
    1657             :     }
    1658             : 
    1659             :     /* -------------------------------------------------------------------- */
    1660             :     /*      Copy imagery                                                    */
    1661             :     /* -------------------------------------------------------------------- */
    1662          23 :     char szFormat[50] = {'\0'};
    1663          23 :     if (eReqDT == GDT_Int32)
    1664          15 :         strcpy(szFormat, "%.17g%c%.17g%c%d\n");
    1665             :     else
    1666           8 :         strcpy(szFormat, "%.17g%c%.17g%c%.17g\n");
    1667             :     const char *pszDecimalPrecision =
    1668          23 :         CSLFetchNameValue(papszOptions, "DECIMAL_PRECISION");
    1669             :     const char *pszSignificantDigits =
    1670          23 :         CSLFetchNameValue(papszOptions, "SIGNIFICANT_DIGITS");
    1671          23 :     bool bIgnoreSigDigits = false;
    1672          23 :     if (pszDecimalPrecision && pszSignificantDigits)
    1673             :     {
    1674           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1675             :                  "Conflicting precision arguments, using DECIMAL_PRECISION");
    1676           0 :         bIgnoreSigDigits = true;
    1677             :     }
    1678             :     int nPrecision;
    1679          23 :     if (pszSignificantDigits && !bIgnoreSigDigits)
    1680             :     {
    1681           0 :         nPrecision = atoi(pszSignificantDigits);
    1682           0 :         if (nPrecision >= 0)
    1683             :         {
    1684           0 :             if (eReqDT == GDT_Int32)
    1685           0 :                 snprintf(szFormat, sizeof(szFormat), "%%.%dg%%c%%.%dg%%c%%d\n",
    1686             :                          nPrecision, nPrecision);
    1687             :             else
    1688           0 :                 snprintf(szFormat, sizeof(szFormat),
    1689             :                          "%%.%dg%%c%%.%dg%%c%%.%dg\n", nPrecision, nPrecision,
    1690             :                          nPrecision);
    1691             :         }
    1692           0 :         CPLDebug("XYZ", "Setting precision format: %s", szFormat);
    1693             :     }
    1694          23 :     else if (pszDecimalPrecision)
    1695             :     {
    1696           0 :         nPrecision = atoi(pszDecimalPrecision);
    1697           0 :         if (nPrecision >= 0)
    1698             :         {
    1699           0 :             if (eReqDT == GDT_Int32)
    1700           0 :                 snprintf(szFormat, sizeof(szFormat), "%%.%df%%c%%.%df%%c%%d\n",
    1701             :                          nPrecision, nPrecision);
    1702             :             else
    1703           0 :                 snprintf(szFormat, sizeof(szFormat),
    1704             :                          "%%.%df%%c%%.%df%%c%%.%df\n", nPrecision, nPrecision,
    1705             :                          nPrecision);
    1706             :         }
    1707           0 :         CPLDebug("XYZ", "Setting precision format: %s", szFormat);
    1708             :     }
    1709             :     void *pLineBuffer =
    1710          23 :         reinterpret_cast<void *>(CPLMalloc(nXSize * sizeof(int)));
    1711          23 :     CPLErr eErr = CE_None;
    1712         405 :     for (int j = 0; j < nYSize && eErr == CE_None; j++)
    1713             :     {
    1714         382 :         eErr = poSrcDS->GetRasterBand(1)->RasterIO(GF_Read, 0, j, nXSize, 1,
    1715             :                                                    pLineBuffer, nXSize, 1,
    1716             :                                                    eReqDT, 0, 0, nullptr);
    1717         382 :         if (eErr != CE_None)
    1718           0 :             break;
    1719         382 :         const double dfY = adfGeoTransform[3] + (j + 0.5) * adfGeoTransform[5];
    1720         382 :         CPLString osBuf;
    1721       42747 :         for (int i = 0; i < nXSize; i++)
    1722             :         {
    1723       42375 :             const double dfX =
    1724       42375 :                 adfGeoTransform[0] + (i + 0.5) * adfGeoTransform[1];
    1725             :             char szBuf[256];
    1726       42375 :             if (eReqDT == GDT_Int32)
    1727        1274 :                 CPLsnprintf(szBuf, sizeof(szBuf), szFormat, dfX, pszColSep[0],
    1728        1274 :                             dfY, pszColSep[0],
    1729        1274 :                             reinterpret_cast<int *>(pLineBuffer)[i]);
    1730             :             else
    1731       41101 :                 CPLsnprintf(szBuf, sizeof(szBuf), szFormat, dfX, pszColSep[0],
    1732       41101 :                             dfY, pszColSep[0],
    1733       41101 :                             reinterpret_cast<float *>(pLineBuffer)[i]);
    1734       42375 :             osBuf += szBuf;
    1735       42375 :             if ((i & 1023) == 0 || i == nXSize - 1)
    1736             :             {
    1737         760 :                 if (VSIFWriteL(osBuf, static_cast<int>(osBuf.size()), 1, fp) !=
    1738             :                     1)
    1739             :                 {
    1740          10 :                     eErr = CE_Failure;
    1741          10 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1742             :                              "Write failed, disk full?\n");
    1743          10 :                     break;
    1744             :                 }
    1745         750 :                 osBuf = "";
    1746             :             }
    1747             :         }
    1748         764 :         if (pfnProgress &&
    1749         382 :             !pfnProgress((j + 1) * 1.0 / nYSize, nullptr, pProgressData))
    1750             :         {
    1751           0 :             eErr = CE_Failure;
    1752           0 :             break;
    1753             :         }
    1754             :     }
    1755          23 :     CPLFree(pLineBuffer);
    1756          23 :     VSIFCloseL(fp);
    1757             : 
    1758          23 :     if (eErr != CE_None)
    1759          10 :         return nullptr;
    1760             : 
    1761             :     /* -------------------------------------------------------------------- */
    1762             :     /*      We don't want to call GDALOpen() since it will be expensive,    */
    1763             :     /*      so we "hand prepare" an XYZ dataset referencing our file.       */
    1764             :     /* -------------------------------------------------------------------- */
    1765          13 :     XYZDataset *poXYZ_DS = new XYZDataset();
    1766          13 :     poXYZ_DS->nRasterXSize = nXSize;
    1767          13 :     poXYZ_DS->nRasterYSize = nYSize;
    1768          13 :     poXYZ_DS->nBands = 1;
    1769          13 :     poXYZ_DS->SetBand(1, new XYZRasterBand(poXYZ_DS, 1, eReqDT));
    1770             :     /* If writing to stdout, we can't reopen it --> silence warning */
    1771          13 :     CPLPushErrorHandler(CPLQuietErrorHandler);
    1772          13 :     poXYZ_DS->fp = VSIFOpenL(pszFilename, "rb");
    1773          13 :     CPLPopErrorHandler();
    1774          13 :     memcpy(&(poXYZ_DS->adfGeoTransform), adfGeoTransform, sizeof(double) * 6);
    1775          13 :     poXYZ_DS->nXIndex = 0;
    1776          13 :     poXYZ_DS->nYIndex = 1;
    1777          13 :     poXYZ_DS->nZIndex = 2;
    1778          13 :     if (pszAddHeaderLine)
    1779             :     {
    1780           1 :         poXYZ_DS->nDataLineNum = 1;
    1781           1 :         poXYZ_DS->bHasHeaderLine = TRUE;
    1782             :     }
    1783             : 
    1784          13 :     return poXYZ_DS;
    1785             : }
    1786             : 
    1787             : /************************************************************************/
    1788             : /*                          GetGeoTransform()                           */
    1789             : /************************************************************************/
    1790             : 
    1791          18 : CPLErr XYZDataset::GetGeoTransform(double *padfTransform)
    1792             : 
    1793             : {
    1794          18 :     memcpy(padfTransform, adfGeoTransform, 6 * sizeof(double));
    1795             : 
    1796          18 :     return CE_None;
    1797             : }
    1798             : 
    1799             : /************************************************************************/
    1800             : /*                         GDALRegister_XYZ()                           */
    1801             : /************************************************************************/
    1802             : 
    1803        1595 : void GDALRegister_XYZ()
    1804             : 
    1805             : {
    1806        1595 :     if (GDALGetDriverByName("XYZ") != nullptr)
    1807         302 :         return;
    1808             : 
    1809        1293 :     GDALDriver *poDriver = new GDALDriver();
    1810             : 
    1811        1293 :     poDriver->SetDescription("XYZ");
    1812        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    1813        1293 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "ASCII Gridded XYZ");
    1814        1293 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/xyz.html");
    1815        1293 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "xyz");
    1816        1293 :     poDriver->SetMetadataItem(
    1817             :         GDAL_DMD_CREATIONOPTIONLIST,
    1818             :         "<CreationOptionList>"
    1819             :         "   <Option name='COLUMN_SEPARATOR' type='string' default=' ' "
    1820             :         "description='Separator between fields.'/>"
    1821             :         "   <Option name='ADD_HEADER_LINE' type='boolean' default='false' "
    1822             :         "description='Add an header line with column names.'/>"
    1823             :         "   <Option name='SIGNIFICANT_DIGITS' type='int' description='Number "
    1824             :         "of significant digits when writing floating-point numbers (%g format; "
    1825             :         "default with 18).'/>\n"
    1826             :         "   <Option name='DECIMAL_PRECISION' type='int' description='Number of "
    1827             :         "decimal places when writing floating-point numbers (%f format).'/>\n"
    1828        1293 :         "</CreationOptionList>");
    1829        1293 :     poDriver->SetMetadataItem(
    1830             :         GDAL_DMD_OPENOPTIONLIST,
    1831             :         "<OpenOptionList>"
    1832             :         "   <Option name='COLUMN_ORDER' type='string-select' default='AUTO' "
    1833             :         "description='Specifies the order of the columns. It overrides the "
    1834             :         "header.'>"
    1835             :         "       <Value>AUTO</Value>"
    1836             :         "       <Value>XYZ</Value>"
    1837             :         "       <Value>YXZ</Value>"
    1838             :         "   </Option>"
    1839        1293 :         "</OpenOptionList>");
    1840             : 
    1841        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
    1842             : 
    1843        1293 :     poDriver->pfnOpen = XYZDataset::Open;
    1844        1293 :     poDriver->pfnIdentify = XYZDataset::Identify;
    1845        1293 :     poDriver->pfnCreateCopy = XYZDataset::CreateCopy;
    1846             : 
    1847        1293 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1848             : }

Generated by: LCOV version 1.14