LCOV - code coverage report
Current view: top level - frmts/xyz - xyzdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 728 905 80.4 %
Date: 2026-03-05 10:33:42 Functions: 14 14 100.0 %

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

Generated by: LCOV version 1.14