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

Generated by: LCOV version 1.14