• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 14023130831

23 Mar 2025 09:23PM UTC coverage: 70.463% (+0.01%) from 70.452%
14023130831

Pull #12013

github

web-flow
Merge ba7475e98 into 8eb234d4d
Pull Request #12013: Add gdal raster select/astype/scale/unscale

210 of 214 new or added lines in 12 files covered. (98.13%)

69 existing lines in 34 files now uncovered.

554668 of 787176 relevant lines covered (70.46%)

222369.68 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.66
/frmts/xyz/xyzdataset.cpp
1
/******************************************************************************
2
 *
3
 * Project:  XYZ driver
4
 * Purpose:  GDALDataset driver for XYZ dataset.
5
 * Author:   Even Rouault, <even dot rouault at spatialys.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12

13
#include "cpl_string.h"
14
#include "cpl_vsi_virtual.h"
15
#include "gdal_frmts.h"
16
#include "gdal_pam.h"
17

18
#include <algorithm>
19
#include <cmath>
20
#include <mutex>
21
#include <vector>
22

23
constexpr double RELATIVE_ERROR = 1e-3;
24

25
class XYZDataset;
26

27
// Global cache when we must ingest all grid points
28
static std::mutex gMutex;
29
static XYZDataset *gpoActiveDS = nullptr;
30
static std::vector<short> gasValues;
31
static std::vector<float> gafValues;
32

33
/************************************************************************/
34
/* ==================================================================== */
35
/*                              XYZDataset                              */
36
/* ==================================================================== */
37
/************************************************************************/
38

39
class XYZRasterBand;
40

41
class XYZDataset final : public GDALPamDataset
42
{
43
    friend class XYZRasterBand;
44

45
    VSILFILE *fp;
46
    int bHasHeaderLine;
47
    int nCommentLineCount;
48
    char chDecimalSep;
49
    int nXIndex;
50
    int nYIndex;
51
    int nZIndex;
52
    int nMinTokens;
53
    GIntBig nLineNum;     /* any line */
54
    GIntBig nDataLineNum; /* line with values (header line and empty lines
55
                             ignored) */
56
    double adfGeoTransform[6];
57
    int bSameNumberOfValuesPerLine;
58
    double dfMinZ;
59
    double dfMaxZ;
60
    bool bEOF;
61
    bool bIngestAll = false;
62

63
    static int IdentifyEx(GDALOpenInfo *, int &, int &nCommentLineCount,
64
                          int &nXIndex, int &nYIndex, int &nZIndex);
65

66
  public:
67
    XYZDataset();
68
    virtual ~XYZDataset();
69

70
    virtual CPLErr GetGeoTransform(double *) override;
71

72
    static GDALDataset *Open(GDALOpenInfo *);
73
    static int Identify(GDALOpenInfo *);
74
    static GDALDataset *CreateCopy(const char *pszFilename,
75
                                   GDALDataset *poSrcDS, int bStrict,
76
                                   char **papszOptions,
77
                                   GDALProgressFunc pfnProgress,
78
                                   void *pProgressData);
79
};
80

81
/************************************************************************/
82
/* ==================================================================== */
83
/*                            XYZRasterBand                             */
84
/* ==================================================================== */
85
/************************************************************************/
86

87
class XYZRasterBand final : public GDALPamRasterBand
88
{
89
    friend class XYZDataset;
90

91
    int nLastYOff;
92

93
  public:
94
    XYZRasterBand(XYZDataset *, int, GDALDataType);
95

96
    virtual CPLErr IReadBlock(int, int, void *) override;
97
    virtual double GetMinimum(int *pbSuccess = nullptr) override;
98
    virtual double GetMaximum(int *pbSuccess = nullptr) override;
99
    virtual double GetNoDataValue(int *pbSuccess = nullptr) override;
100
};
101

102
/************************************************************************/
103
/*                           XYZRasterBand()                            */
104
/************************************************************************/
105

106
XYZRasterBand::XYZRasterBand(XYZDataset *poDSIn, int nBandIn, GDALDataType eDT)
38✔
107
    : nLastYOff(-1)
38✔
108
{
109
    poDS = poDSIn;
38✔
110
    nBand = nBandIn;
38✔
111

112
    eDataType = eDT;
38✔
113

114
    nBlockXSize = poDSIn->GetRasterXSize();
38✔
115
    nBlockYSize = 1;
38✔
116
}
38✔
117

118
/************************************************************************/
119
/*                             IReadBlock()                             */
120
/************************************************************************/
121

122
CPLErr XYZRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
318✔
123
                                 void *pImage)
124
{
125
    XYZDataset *poGDS = reinterpret_cast<XYZDataset *>(poDS);
318✔
126

127
    if (poGDS->fp == nullptr)
318✔
128
        return CE_Failure;
×
129

130
    if (poGDS->bIngestAll)
318✔
131
    {
132
        CPLAssert(eDataType == GDT_Int16 || eDataType == GDT_Float32);
12✔
133

134
        std::lock_guard<std::mutex> guard(gMutex);
24✔
135

136
        if (gpoActiveDS != poGDS || (gasValues.empty() && gafValues.empty()))
12✔
137
        {
138
            gpoActiveDS = poGDS;
4✔
139

140
            const int nGridSize = nRasterXSize * nRasterYSize;
4✔
141
            try
142
            {
143
                if (eDataType == GDT_Int16)
4✔
144
                    gasValues.resize(nGridSize);
2✔
145
                else
146
                    gafValues.resize(nGridSize);
2✔
147
            }
148
            catch (const std::exception &)
×
149
            {
150
                CPLError(CE_Failure, CPLE_OutOfMemory, "Cannot allocate grid");
×
151
                return CE_Failure;
×
152
            }
153

154
            poGDS->nDataLineNum = 0;
4✔
155
            poGDS->nLineNum = 0;
4✔
156
            poGDS->bEOF = false;
4✔
157
            VSIFSeekL(poGDS->fp, 0, SEEK_SET);
4✔
158

159
            for (int i = 0; i < poGDS->nCommentLineCount; i++)
4✔
160
            {
161
                if (CPLReadLine2L(poGDS->fp, 100, nullptr) == nullptr)
×
162
                {
163
                    poGDS->bEOF = true;
×
164
                    return CE_Failure;
×
165
                }
166
                poGDS->nLineNum++;
×
167
            }
168

169
            if (poGDS->bHasHeaderLine)
4✔
170
            {
171
                const char *pszLine = CPLReadLine2L(poGDS->fp, 100, nullptr);
4✔
172
                if (pszLine == nullptr)
4✔
173
                {
174
                    poGDS->bEOF = true;
×
175
                    return CE_Failure;
×
176
                }
177
                poGDS->nLineNum++;
4✔
178
            }
179

180
            for (int i = 0; i < nGridSize; i++)
34✔
181
            {
182
                const char *pszLine = CPLReadLine2L(poGDS->fp, 100, nullptr);
30✔
183
                if (pszLine == nullptr)
30✔
184
                {
185
                    poGDS->bEOF = true;
×
186
                    CPLError(CE_Failure, CPLE_AppDefined,
×
187
                             "Cannot read line " CPL_FRMT_GIB,
188
                             poGDS->nLineNum + 1);
×
189
                    return CE_Failure;
×
190
                }
191
                poGDS->nLineNum++;
30✔
192

193
                const char *pszPtr = pszLine;
30✔
194
                char ch;
195
                int nCol = 0;
30✔
196
                bool bLastWasSep = true;
30✔
197
                double dfX = 0.0;
30✔
198
                double dfY = 0.0;
30✔
199
                double dfZ = 0.0;
30✔
200
                int nUsefulColsFound = 0;
30✔
201
                while ((ch = *pszPtr) != '\0')
326✔
202
                {
203
                    if (ch == ' ')
296✔
204
                    {
205
                        if (!bLastWasSep)
60✔
206
                            nCol++;
60✔
207
                        bLastWasSep = true;
60✔
208
                    }
209
                    else if ((ch == ',' && poGDS->chDecimalSep != ',') ||
236✔
210
                             ch == '\t' || ch == ';')
236✔
211
                    {
212
                        nCol++;
×
213
                        bLastWasSep = true;
×
214
                    }
215
                    else
216
                    {
217
                        if (bLastWasSep)
236✔
218
                        {
219
                            if (nCol == poGDS->nXIndex)
90✔
220
                            {
221
                                nUsefulColsFound++;
30✔
222
                                dfX = CPLAtofDelim(pszPtr, poGDS->chDecimalSep);
30✔
223
                            }
224
                            else if (nCol == poGDS->nYIndex)
60✔
225
                            {
226
                                nUsefulColsFound++;
30✔
227
                                dfY = CPLAtofDelim(pszPtr, poGDS->chDecimalSep);
30✔
228
                            }
229
                            else if (nCol == poGDS->nZIndex)
30✔
230
                            {
231
                                nUsefulColsFound++;
30✔
232
                                dfZ = CPLAtofDelim(pszPtr, poGDS->chDecimalSep);
30✔
233
                            }
234
                        }
235
                        bLastWasSep = false;
236✔
236
                    }
237
                    pszPtr++;
296✔
238
                }
239

240
                /* Skip empty line */
241
                if (nCol == 0 && bLastWasSep)
30✔
242
                    continue;
×
243

244
                if (nUsefulColsFound != 3)
30✔
245
                {
246
                    CPLError(
×
247
                        CE_Failure, CPLE_AppDefined,
248
                        "Unexpected number of values at line " CPL_FRMT_GIB,
249
                        poGDS->nLineNum);
250
                    return CE_Failure;
×
251
                }
252

253
                poGDS->nDataLineNum++;
30✔
254

255
                const int nX =
30✔
256
                    static_cast<int>((dfX - 0.5 * poGDS->adfGeoTransform[1] -
30✔
257
                                      poGDS->adfGeoTransform[0]) /
30✔
258
                                         poGDS->adfGeoTransform[1] +
30✔
259
                                     0.5);
260
                const int nY =
30✔
261
                    static_cast<int>((dfY - 0.5 * poGDS->adfGeoTransform[5] -
30✔
262
                                      poGDS->adfGeoTransform[3]) /
30✔
263
                                         poGDS->adfGeoTransform[5] +
30✔
264
                                     0.5);
265
                if (nX < 0 || nX >= nRasterXSize)
30✔
266
                {
267
                    CPLError(CE_Failure, CPLE_AppDefined,
×
268
                             "Unexpected X value at line " CPL_FRMT_GIB,
269
                             poGDS->nLineNum);
270
                    return CE_Failure;
×
271
                }
272
                if (nY < 0 || nY >= nRasterYSize)
30✔
273
                {
274
                    CPLError(CE_Failure, CPLE_AppDefined,
×
275
                             "Unexpected Y value at line " CPL_FRMT_GIB,
276
                             poGDS->nLineNum);
277
                    return CE_Failure;
×
278
                }
279
                const int nIdx = nX + nY * nRasterXSize;
30✔
280
                if (eDataType == GDT_Int16)
30✔
281
                    gasValues[nIdx] = static_cast<short>(0.5 + dfZ);
15✔
282
                else
283
                    gafValues[nIdx] = static_cast<float>(dfZ);
15✔
284
            }
285
        }
286

287
        if (eDataType == GDT_Int16)
12✔
288
            memcpy(pImage,
6✔
289
                   &gasValues[static_cast<size_t>(nBlockYOff) * nBlockXSize],
6✔
290
                   sizeof(short) * nBlockXSize);
6✔
291
        else
292
            memcpy(pImage,
6✔
293
                   &gafValues[static_cast<size_t>(nBlockYOff) * nBlockXSize],
6✔
294
                   sizeof(float) * nBlockXSize);
6✔
295
        return CE_None;
12✔
296
    }
297

298
    if (pImage)
306✔
299
    {
300
        int bSuccess = FALSE;
306✔
301
        double dfNoDataValue = GetNoDataValue(&bSuccess);
306✔
302
        if (!bSuccess)
306✔
303
            dfNoDataValue = 0.0;
284✔
304
        GDALCopyWords(&dfNoDataValue, GDT_Float64, 0, pImage, eDataType,
612✔
305
                      GDALGetDataTypeSize(eDataType) / 8, nRasterXSize);
306✔
306
    }
307

308
    // Only valid if bSameNumberOfValuesPerLine.
309
    const GIntBig nLineInFile = static_cast<GIntBig>(nBlockYOff) * nBlockXSize;
306✔
310
    if ((poGDS->bSameNumberOfValuesPerLine &&
306✔
311
         poGDS->nDataLineNum > nLineInFile) ||
284✔
312
        (!poGDS->bSameNumberOfValuesPerLine &&
289✔
313
         (nLastYOff == -1 || nBlockYOff == 0)))
22✔
314
    {
315
        poGDS->nDataLineNum = 0;
22✔
316
        poGDS->nLineNum = 0;
22✔
317
        poGDS->bEOF = false;
22✔
318
        VSIFSeekL(poGDS->fp, 0, SEEK_SET);
22✔
319

320
        for (int i = 0; i < poGDS->nCommentLineCount; i++)
22✔
321
        {
322
            if (CPLReadLine2L(poGDS->fp, 100, nullptr) == nullptr)
×
323
            {
324
                poGDS->bEOF = true;
×
325
                return CE_Failure;
×
326
            }
327
            poGDS->nLineNum++;
×
328
        }
329

330
        if (poGDS->bHasHeaderLine)
22✔
331
        {
332
            const char *pszLine = CPLReadLine2L(poGDS->fp, 100, nullptr);
14✔
333
            if (pszLine == nullptr)
14✔
334
            {
335
                poGDS->bEOF = true;
×
336
                return CE_Failure;
×
337
            }
338
            poGDS->nLineNum++;
14✔
339
        }
340
    }
341

342
    if (!poGDS->bSameNumberOfValuesPerLine)
306✔
343
    {
344
        if (nBlockYOff < nLastYOff)
22✔
345
        {
346
            nLastYOff = -1;
×
347
            for (int iY = 0; iY < nBlockYOff; iY++)
×
348
            {
349
                if (IReadBlock(0, iY, nullptr) != CE_None)
×
350
                    return CE_Failure;
×
351
            }
352
        }
353
        else
354
        {
355
            if (poGDS->bEOF)
22✔
356
            {
357
                return CE_Failure;
×
358
            }
359
            for (int iY = nLastYOff + 1; iY < nBlockYOff; iY++)
22✔
360
            {
361
                if (IReadBlock(0, iY, nullptr) != CE_None)
×
362
                    return CE_Failure;
×
363
            }
364
        }
365
    }
366
    else
367
    {
368
        if (poGDS->bEOF)
284✔
369
        {
370
            return CE_Failure;
×
371
        }
372
        while (poGDS->nDataLineNum < nLineInFile)
310✔
373
        {
374
            const char *pszLine = CPLReadLine2L(poGDS->fp, 100, nullptr);
26✔
375
            if (pszLine == nullptr)
26✔
376
            {
377
                poGDS->bEOF = true;
×
378
                return CE_Failure;
×
379
            }
380
            poGDS->nLineNum++;
26✔
381

382
            const char *pszPtr = pszLine;
26✔
383
            char ch;
384
            int nCol = 0;
26✔
385
            bool bLastWasSep = true;
26✔
386
            while ((ch = *pszPtr) != '\0')
146✔
387
            {
388
                if (ch == ' ')
120✔
389
                {
390
                    if (!bLastWasSep)
40✔
391
                        nCol++;
40✔
392
                    bLastWasSep = true;
40✔
393
                }
394
                else if ((ch == ',' && poGDS->chDecimalSep != ',') ||
80✔
395
                         ch == '\t' || ch == ';')
80✔
396
                {
397
                    nCol++;
×
398
                    bLastWasSep = true;
×
399
                }
400
                else
401
                {
402
                    bLastWasSep = false;
80✔
403
                }
404
                pszPtr++;
120✔
405
            }
406

407
            /* Skip empty line */
408
            if (nCol == 0 && bLastWasSep)
26✔
409
                continue;
6✔
410

411
            poGDS->nDataLineNum++;
20✔
412
        }
413
    }
414

415
    const double dfExpectedY = poGDS->adfGeoTransform[3] +
306✔
416
                               (0.5 + nBlockYOff) * poGDS->adfGeoTransform[5];
306✔
417

418
    int idx = -1;
306✔
419
    while (true)
420
    {
421
        int nCol;
422
        bool bLastWasSep;
423
        do
7✔
424
        {
425
            const vsi_l_offset nOffsetBefore = VSIFTellL(poGDS->fp);
41,702✔
426
            const char *pszLine = CPLReadLine2L(poGDS->fp, 100, nullptr);
41,702✔
427
            if (pszLine == nullptr)
41,702✔
428
            {
429
                poGDS->bEOF = true;
1✔
430
                if (poGDS->bSameNumberOfValuesPerLine)
1✔
431
                {
432
                    CPLError(CE_Failure, CPLE_AppDefined,
×
433
                             "Cannot read line " CPL_FRMT_GIB,
434
                             poGDS->nLineNum + 1);
×
435
                    return CE_Failure;
×
436
                }
437
                else
438
                {
439
                    nLastYOff = nBlockYOff;
1✔
440
                }
441
                return CE_None;
1✔
442
            }
443
            poGDS->nLineNum++;
41,701✔
444

445
            const char *pszPtr = pszLine;
41,701✔
446
            char ch;
447
            nCol = 0;
41,701✔
448
            bLastWasSep = true;
41,701✔
449
            double dfX = 0.0;
41,701✔
450
            double dfY = 0.0;
41,701✔
451
            double dfZ = 0.0;
41,701✔
452
            int nUsefulColsFound = 0;
41,701✔
453
            while ((ch = *pszPtr) != '\0')
1,353,710✔
454
            {
455
                if (ch == ' ')
1,312,010✔
456
                {
457
                    if (!bLastWasSep)
2,706✔
458
                        nCol++;
2,586✔
459
                    bLastWasSep = true;
2,706✔
460
                }
461
                else if ((ch == ',' && poGDS->chDecimalSep != ',') ||
1,309,300✔
462
                         ch == '\t' || ch == ';')
1,228,500✔
463
                {
464
                    nCol++;
80,802✔
465
                    bLastWasSep = true;
80,802✔
466
                }
467
                else
468
                {
469
                    if (bLastWasSep)
1,228,500✔
470
                    {
471
                        if (nCol == poGDS->nXIndex)
125,082✔
472
                        {
473
                            nUsefulColsFound++;
41,694✔
474
                            if (!poGDS->bSameNumberOfValuesPerLine)
41,694✔
475
                                dfX = CPLAtofDelim(pszPtr, poGDS->chDecimalSep);
36✔
476
                        }
477
                        else if (nCol == poGDS->nYIndex)
83,388✔
478
                        {
479
                            nUsefulColsFound++;
41,694✔
480
                            if (!poGDS->bSameNumberOfValuesPerLine)
41,694✔
481
                                dfY = CPLAtofDelim(pszPtr, poGDS->chDecimalSep);
36✔
482
                        }
483
                        else if (nCol == poGDS->nZIndex)
41,694✔
484
                        {
485
                            nUsefulColsFound++;
41,694✔
486
                            dfZ = CPLAtofDelim(pszPtr, poGDS->chDecimalSep);
41,694✔
487
                        }
488
                    }
489
                    bLastWasSep = false;
1,228,500✔
490
                }
491
                pszPtr++;
1,312,010✔
492
            }
493
            nCol++;
41,701✔
494

495
            if (nUsefulColsFound == 3)
41,701✔
496
            {
497
                if (poGDS->bSameNumberOfValuesPerLine)
41,694✔
498
                {
499
                    idx++;
41,658✔
500
                }
501
                else
502
                {
503
                    if (fabs((dfY - dfExpectedY) / poGDS->adfGeoTransform[5]) >
36✔
504
                        RELATIVE_ERROR)
505
                    {
506
                        if (idx < 0)
5✔
507
                        {
508
                            const double dfYDeltaOrigin =
3✔
509
                                dfY + 0.5 * poGDS->adfGeoTransform[5] -
3✔
510
                                poGDS->adfGeoTransform[3];
3✔
511
                            if (!(fabs(dfYDeltaOrigin) >
6✔
512
                                      fabs(poGDS->adfGeoTransform[5]) &&
3✔
513
                                  fabs(std::round(dfYDeltaOrigin /
3✔
514
                                                  poGDS->adfGeoTransform[5]) -
3✔
515
                                       (dfYDeltaOrigin /
3✔
516
                                        poGDS->adfGeoTransform[5])) <=
3✔
517
                                      RELATIVE_ERROR))
518
                            {
519
                                CPLError(CE_Failure, CPLE_AppDefined,
×
520
                                         "At line " CPL_FRMT_GIB
521
                                         ", found Y=%f instead of %f "
522
                                         "for nBlockYOff = %d",
523
                                         poGDS->nLineNum, dfY, dfExpectedY,
524
                                         nBlockYOff);
525
                                return CE_Failure;
×
526
                            }
527
                        }
528
                        VSIFSeekL(poGDS->fp, nOffsetBefore, SEEK_SET);
5✔
529
                        nLastYOff = nBlockYOff;
5✔
530
                        poGDS->nLineNum--;
5✔
531
                        return CE_None;
5✔
532
                    }
533

534
                    idx = static_cast<int>((dfX -
31✔
535
                                            0.5 * poGDS->adfGeoTransform[1] -
31✔
536
                                            poGDS->adfGeoTransform[0]) /
31✔
537
                                               poGDS->adfGeoTransform[1] +
31✔
538
                                           0.5);
539
                }
540
                CPLAssert(idx >= 0 && idx < nRasterXSize);
41,689✔
541

542
                if (pImage)
41,689✔
543
                {
544
                    if (eDataType == GDT_Float32)
41,689✔
545
                    {
546
                        reinterpret_cast<float *>(pImage)[idx] =
40,416✔
547
                            static_cast<float>(dfZ);
40,416✔
548
                    }
549
                    else if (eDataType == GDT_Int32)
1,273✔
550
                    {
551
                        reinterpret_cast<GInt32 *>(pImage)[idx] =
400✔
552
                            static_cast<GInt32>(dfZ);
400✔
553
                    }
554
                    else if (eDataType == GDT_Int16)
873✔
555
                    {
556
                        reinterpret_cast<GInt16 *>(pImage)[idx] =
13✔
557
                            static_cast<GInt16>(dfZ);
13✔
558
                    }
559
                    else
560
                    {
561
                        reinterpret_cast<GByte *>(pImage)[idx] =
860✔
562
                            static_cast<GByte>(dfZ);
860✔
563
                    }
564
                }
565
            }
566
            /* Skip empty line */
567
        } while (nCol == 1 && bLastWasSep);
41,696✔
568

569
        poGDS->nDataLineNum++;
41,689✔
570
        if (nCol < poGDS->nMinTokens)
41,689✔
571
            return CE_Failure;
×
572

573
        if (idx + 1 == nRasterXSize)
41,689✔
574
            break;
300✔
575
    }
41,389✔
576

577
    if (poGDS->bSameNumberOfValuesPerLine)
300✔
578
    {
579
        if (poGDS->nDataLineNum !=
284✔
580
            static_cast<GIntBig>(nBlockYOff + 1) * nBlockXSize)
284✔
581
        {
582
            CPLError(CE_Failure, CPLE_AssertionFailed,
×
583
                     "The file does not have the same number of values per "
584
                     "line as initially thought. It must be somehow corrupted");
585
            return CE_Failure;
×
586
        }
587
    }
588

589
    nLastYOff = nBlockYOff;
300✔
590

591
    return CE_None;
300✔
592
}
593

594
/************************************************************************/
595
/*                            GetMinimum()                              */
596
/************************************************************************/
597

598
double XYZRasterBand::GetMinimum(int *pbSuccess)
1✔
599
{
600
    XYZDataset *poGDS = reinterpret_cast<XYZDataset *>(poDS);
1✔
601
    if (pbSuccess)
1✔
602
        *pbSuccess = TRUE;
1✔
603
    return poGDS->dfMinZ;
1✔
604
}
605

606
/************************************************************************/
607
/*                            GetMaximum()                              */
608
/************************************************************************/
609

610
double XYZRasterBand::GetMaximum(int *pbSuccess)
1✔
611
{
612
    XYZDataset *poGDS = reinterpret_cast<XYZDataset *>(poDS);
1✔
613
    if (pbSuccess)
1✔
614
        *pbSuccess = TRUE;
1✔
615
    return poGDS->dfMaxZ;
1✔
616
}
617

618
/************************************************************************/
619
/*                          GetNoDataValue()                            */
620
/************************************************************************/
621

622
double XYZRasterBand::GetNoDataValue(int *pbSuccess)
318✔
623
{
624
    XYZDataset *poGDS = reinterpret_cast<XYZDataset *>(poDS);
318✔
625
    if (!poGDS->bSameNumberOfValuesPerLine && poGDS->dfMinZ > -32768 &&
318✔
626
        eDataType != GDT_Byte)
23✔
627
    {
628
        if (pbSuccess)
×
629
            *pbSuccess = TRUE;
×
630
        return (poGDS->dfMinZ > 0) ? 0 : -32768;
×
631
    }
632
    else if (!poGDS->bSameNumberOfValuesPerLine && poGDS->dfMinZ > 0 &&
318✔
633
             eDataType == GDT_Byte)
23✔
634
    {
635
        if (pbSuccess)
23✔
636
            *pbSuccess = TRUE;
23✔
637
        return 0;
23✔
638
    }
639

640
    return GDALPamRasterBand::GetNoDataValue(pbSuccess);
295✔
641
}
642

643
/************************************************************************/
644
/*                            ~XYZDataset()                            */
645
/************************************************************************/
646

647
XYZDataset::XYZDataset()
38✔
648
    : fp(nullptr), bHasHeaderLine(FALSE), nCommentLineCount(0),
649
      chDecimalSep('.'), nXIndex(-1), nYIndex(-1), nZIndex(-1), nMinTokens(0),
650
      nLineNum(0), nDataLineNum(GINTBIG_MAX), bSameNumberOfValuesPerLine(TRUE),
651
      dfMinZ(0), dfMaxZ(0), bEOF(false)
38✔
652
{
653
    adfGeoTransform[0] = 0;
38✔
654
    adfGeoTransform[1] = 1;
38✔
655
    adfGeoTransform[2] = 0;
38✔
656
    adfGeoTransform[3] = 0;
38✔
657
    adfGeoTransform[4] = 0;
38✔
658
    adfGeoTransform[5] = 1;
38✔
659
}
38✔
660

661
/************************************************************************/
662
/*                            ~XYZDataset()                            */
663
/************************************************************************/
664

665
XYZDataset::~XYZDataset()
76✔
666

667
{
668
    FlushCache(true);
38✔
669
    if (fp)
38✔
670
        VSIFCloseL(fp);
38✔
671

672
    {
673
        std::lock_guard<std::mutex> guard(gMutex);
76✔
674
        if (gpoActiveDS == this)
38✔
675
        {
676
            gpoActiveDS = nullptr;
4✔
677
            gasValues.clear();
4✔
678
            gafValues.clear();
4✔
679
        }
680
    }
681
}
76✔
682

683
/************************************************************************/
684
/*                             Identify()                               */
685
/************************************************************************/
686

687
int XYZDataset::Identify(GDALOpenInfo *poOpenInfo)
48,883✔
688
{
689
    int bHasHeaderLine, nCommentLineCount;
690
    int nXIndex;
691
    int nYIndex;
692
    int nZIndex;
693
    return IdentifyEx(poOpenInfo, bHasHeaderLine, nCommentLineCount, nXIndex,
48,883✔
694
                      nYIndex, nZIndex);
97,698✔
695
}
696

697
/************************************************************************/
698
/*                            IdentifyEx()                              */
699
/************************************************************************/
700

701
int XYZDataset::IdentifyEx(GDALOpenInfo *poOpenInfo, int &bHasHeaderLine,
48,908✔
702
                           int &nCommentLineCount, int &nXIndex, int &nYIndex,
703
                           int &nZIndex)
704

705
{
706
    bHasHeaderLine = FALSE;
48,908✔
707
    nCommentLineCount = 0;
48,908✔
708

709
    CPLString osFilename(poOpenInfo->pszFilename);
97,796✔
710
    if (EQUAL(CPLGetExtensionSafe(osFilename).c_str(), "GRA") &&
48,905✔
UNCOV
711
        !poOpenInfo->IsSingleAllowedDriver("XYZ"))
×
712
    {
713
        // IGNFHeightASCIIGRID .GRA
714
        return FALSE;
×
715
    }
716

717
    std::unique_ptr<GDALOpenInfo> poOpenInfoToDelete;  // keep in this scope
48,902✔
718
    /*  GZipped .xyz files are common, so automagically open them */
719
    /*  if the /vsigzip/ has not been explicitly passed */
720
    if (strlen(poOpenInfo->pszFilename) > 6 &&
48,884✔
721
        EQUAL(poOpenInfo->pszFilename + strlen(poOpenInfo->pszFilename) - 6,
47,920✔
722
              "xyz.gz") &&
×
723
        !STARTS_WITH_CI(poOpenInfo->pszFilename, "/vsigzip/"))
×
724
    {
725
        osFilename = "/vsigzip/";
×
726
        osFilename += poOpenInfo->pszFilename;
×
727
        poOpenInfoToDelete = std::make_unique<GDALOpenInfo>(
×
728
            osFilename.c_str(), GA_ReadOnly, poOpenInfo->GetSiblingFiles());
×
729
        poOpenInfo = poOpenInfoToDelete.get();
×
730
    }
731

732
    if (poOpenInfo->nHeaderBytes == 0)
48,902✔
733
    {
734
        return FALSE;
45,844✔
735
    }
736

737
    /* -------------------------------------------------------------------- */
738
    /*      Check that it looks roughly as an XYZ dataset                   */
739
    /* -------------------------------------------------------------------- */
740
    const char *pszData =
3,058✔
741
        reinterpret_cast<const char *>(poOpenInfo->pabyHeader);
742

743
    if (poOpenInfo->nHeaderBytes >= 4 && STARTS_WITH(pszData, "DSAA") &&
3,058✔
744
        !poOpenInfo->IsSingleAllowedDriver("XYZ"))
×
745
    {
746
        // Do not match GSAG datasets
747
        return FALSE;
×
748
    }
749

750
    /* Skip comments line at the beginning such as in */
751
    /* http://pubs.usgs.gov/of/2003/ofr-03-230/DATA/NSLCU.XYZ */
752
    int i = 0;
3,058✔
753
    if (pszData[i] == '/')
3,058✔
754
    {
755
        nCommentLineCount++;
×
756

757
        i++;
×
758
        for (; i < poOpenInfo->nHeaderBytes; i++)
×
759
        {
760
            const char ch = pszData[i];
×
761
            if (ch == 13 || ch == 10)
×
762
            {
763
                if (ch == 13 && pszData[i + 1] == 10)
×
764
                    i++;
×
765
                if (pszData[i + 1] == '/')
×
766
                {
767
                    nCommentLineCount++;
×
768
                    i++;
×
769
                }
770
                else
771
                    break;
×
772
            }
773
        }
774
    }
775

776
    int iStartLine = i;
3,058✔
777
    for (; i < poOpenInfo->nHeaderBytes; i++)
27,896✔
778
    {
779
        const char ch = pszData[i];
27,850✔
780
        if (ch == 13 || ch == 10)
27,850✔
781
        {
782
            break;
783
        }
784
        else if (ch == ' ' || ch == ',' || ch == '\t' || ch == ';')
27,689✔
785
            ;
786
        else if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '+' ||
16,757✔
787
                 ch == '-' || ch == 'e' || ch == 'E')
11,255✔
788
            ;
789
        else if (ch == '"' || (ch >= 'a' && ch <= 'z') ||
10,602✔
790
                 (ch >= 'A' && ch <= 'Z'))
3,841✔
791
            bHasHeaderLine = TRUE;
7,751✔
792
        else
793
        {
794
            return FALSE;
2,851✔
795
        }
796
    }
797

798
    nXIndex = -1;
207✔
799
    nYIndex = -1;
207✔
800
    nZIndex = -1;
207✔
801
    const char *pszColumnOrder = CSLFetchNameValueDef(
414✔
802
        poOpenInfo->papszOpenOptions, "COLUMN_ORDER", "AUTO");
207✔
803
    if (EQUAL(pszColumnOrder, "XYZ"))
207✔
804
    {
805
        nXIndex = 0;
2✔
806
        nYIndex = 1;
2✔
807
        nZIndex = 2;
2✔
808
        return TRUE;
2✔
809
    }
810
    else if (EQUAL(pszColumnOrder, "YXZ"))
205✔
811
    {
812
        nXIndex = 1;
4✔
813
        nYIndex = 0;
4✔
814
        nZIndex = 2;
4✔
815
        return TRUE;
4✔
816
    }
817
    else if (!EQUAL(pszColumnOrder, "AUTO"))
201✔
818
    {
819
        CPLError(CE_Failure, CPLE_IllegalArg,
1✔
820
                 "Option COLUMN_ORDER can only be XYZ, YXZ and AUTO."
821
                 "%s is not valid",
822
                 pszColumnOrder);
823
        return FALSE;
1✔
824
    }
825

826
    if (bHasHeaderLine)
200✔
827
    {
828
        CPLString osHeaderLine;
109✔
829
        osHeaderLine.assign(pszData + iStartLine, i - iStartLine);
109✔
830
        char **papszTokens =
831
            CSLTokenizeString2(osHeaderLine, " ,\t;", CSLT_HONOURSTRINGS);
109✔
832
        int nTokens = CSLCount(papszTokens);
109✔
833
        for (int iToken = 0; iToken < nTokens; iToken++)
833✔
834
        {
835
            const char *pszToken = papszTokens[iToken];
724✔
836
            if (EQUAL(pszToken, "x") || STARTS_WITH_CI(pszToken, "lon") ||
724✔
837
                STARTS_WITH_CI(pszToken, "east"))
694✔
838
                nXIndex = iToken;
30✔
839
            else if (EQUAL(pszToken, "y") || STARTS_WITH_CI(pszToken, "lat") ||
694✔
840
                     STARTS_WITH_CI(pszToken, "north"))
666✔
841
                nYIndex = iToken;
28✔
842
            else if (EQUAL(pszToken, "z") || STARTS_WITH_CI(pszToken, "alt") ||
666✔
843
                     EQUAL(pszToken, "height"))
638✔
844
                nZIndex = iToken;
28✔
845
        }
846
        CSLDestroy(papszTokens);
109✔
847
        if (nXIndex >= 0 && nYIndex >= 0 && nZIndex >= 0)
109✔
848
        {
849
            return TRUE;
28✔
850
        }
851
    }
852

853
    bool bHasFoundNewLine = false;
172✔
854
    bool bPrevWasSep = true;
172✔
855
    int nCols = 0;
172✔
856
    int nMaxCols = 0;
172✔
857
    for (; i < poOpenInfo->nHeaderBytes; i++)
13,564✔
858
    {
859
        char ch = pszData[i];
13,484✔
860
        if (ch == 13 || ch == 10)
13,484✔
861
        {
862
            bHasFoundNewLine = true;
902✔
863
            if (!bPrevWasSep)
902✔
864
            {
865
                nCols++;
757✔
866
                if (nCols > nMaxCols)
757✔
867
                    nMaxCols = nCols;
39✔
868
            }
869
            bPrevWasSep = true;
902✔
870
            nCols = 0;
902✔
871
        }
872
        else if (ch == ' ' || ch == ',' || ch == '\t' || ch == ';')
12,582✔
873
        {
874
            if (!bPrevWasSep)
1,890✔
875
            {
876
                nCols++;
1,844✔
877
                if (nCols > nMaxCols)
1,844✔
878
                    nMaxCols = nCols;
101✔
879
            }
880
            bPrevWasSep = true;
1,890✔
881
        }
882
        else if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '+' ||
10,692✔
883
                 ch == '-' || ch == 'e' || ch == 'E')
92✔
884
        {
885
            bPrevWasSep = false;
10,600✔
886
        }
887
        else
888
        {
889
            return FALSE;
92✔
890
        }
891
    }
892

893
    return bHasFoundNewLine && nMaxCols >= 3;
80✔
894
}
895

896
/************************************************************************/
897
/*                                Open()                                */
898
/************************************************************************/
899

900
GDALDataset *XYZDataset::Open(GDALOpenInfo *poOpenInfo)
25✔
901

902
{
903
    int bHasHeaderLine;
904
    int nCommentLineCount = 0;
25✔
905

906
    int nXIndex = -1;
25✔
907
    int nYIndex = -1;
25✔
908
    int nZIndex = -1;
25✔
909
    if (!IdentifyEx(poOpenInfo, bHasHeaderLine, nCommentLineCount, nXIndex,
25✔
910
                    nYIndex, nZIndex))
911
        return nullptr;
×
912

913
    CPLString osFilename(poOpenInfo->pszFilename);
50✔
914

915
    /*  GZipped .xyz files are common, so automagically open them */
916
    /*  if the /vsigzip/ has not been explicitly passed */
917
    if (strlen(poOpenInfo->pszFilename) > 6 &&
25✔
918
        EQUAL(poOpenInfo->pszFilename + strlen(poOpenInfo->pszFilename) - 6,
25✔
919
              "xyz.gz") &&
×
920
        !STARTS_WITH_CI(poOpenInfo->pszFilename, "/vsigzip/"))
×
921
    {
922
        osFilename = "/vsigzip/";
×
923
        osFilename += poOpenInfo->pszFilename;
×
924
    }
925

926
    /* -------------------------------------------------------------------- */
927
    /*      Find dataset characteristics                                    */
928
    /* -------------------------------------------------------------------- */
929
    VSILFILE *fp = VSIFOpenL(osFilename.c_str(), "rb");
25✔
930
    if (fp == nullptr)
25✔
931
        return nullptr;
×
932

933
    /* For better performance of CPLReadLine2L() we create a buffered reader */
934
    /* (except for /vsigzip/ since it has one internally) */
935
    if (!STARTS_WITH_CI(poOpenInfo->pszFilename, "/vsigzip/"))
25✔
936
        fp = VSICreateBufferedReaderHandle(fp);
25✔
937

938
    int nMinTokens = 0;
25✔
939

940
    for (int i = 0; i < nCommentLineCount; i++)
25✔
941
    {
942
        if (CPLReadLine2L(fp, 100, nullptr) == nullptr)
×
943
        {
944
            VSIFCloseL(fp);
×
945
            return nullptr;
×
946
        }
947
    }
948

949
    /* -------------------------------------------------------------------- */
950
    /*      Parse header line                                               */
951
    /* -------------------------------------------------------------------- */
952
    if (bHasHeaderLine)
25✔
953
    {
954
        const char *pszLine = CPLReadLine2L(fp, 100, nullptr);
17✔
955
        if (pszLine == nullptr)
17✔
956
        {
957
            VSIFCloseL(fp);
×
958
            return nullptr;
×
959
        }
960
        if (nXIndex < 0 || nYIndex < 0 || nZIndex < 0)
17✔
961
        {
962
            CPLError(CE_Warning, CPLE_AppDefined,
1✔
963
                     "Could not find one of the X, Y or Z column names in "
964
                     "header line. Defaulting to the first 3 columns");
965
            nXIndex = 0;
1✔
966
            nYIndex = 1;
1✔
967
            nZIndex = 2;
1✔
968
        }
969
    }
970
    else if (nXIndex < 0 || nYIndex < 0 || nZIndex < 0)
8✔
971
    {
972
        nXIndex = 0;
7✔
973
        nYIndex = 1;
7✔
974
        nZIndex = 2;
7✔
975
    }
976
    nMinTokens = 1 + std::max(std::max(nXIndex, nYIndex), nZIndex);
25✔
977

978
    /* -------------------------------------------------------------------- */
979
    /*      Parse data lines                                                */
980
    /* -------------------------------------------------------------------- */
981

982
    GIntBig nLineNum = 0;
25✔
983
    GIntBig nDataLineNum = 0;
25✔
984
    double dfX = 0.0;
25✔
985
    double dfY = 0.0;
25✔
986
    double dfZ = 0.0;
25✔
987
    double dfMinX = 0.0;
25✔
988
    double dfMinY = 0.0;
25✔
989
    double dfMaxX = 0.0;
25✔
990
    double dfMaxY = 0.0;
25✔
991
    double dfMinZ = 0.0;
25✔
992
    double dfMaxZ = 0.0;
25✔
993
    double dfLastX = 0.0;
25✔
994
    double dfLastY = 0.0;
25✔
995
    std::vector<double> adfStepX;
50✔
996
    std::vector<double> adfStepY;
50✔
997
    GDALDataType eDT = GDT_Byte;
25✔
998
    bool bSameNumberOfValuesPerLine = true;
25✔
999
    char chDecimalSep = '\0';
25✔
1000
    int nStepYSign = 0;
25✔
1001
    bool bColOrganization = false;
25✔
1002

1003
    const char *pszLine;
1004
    GIntBig nCountStepX = 0;
25✔
1005
    GIntBig nCountStepY = 0;
25✔
1006
    while ((pszLine = CPLReadLine2L(fp, 100, nullptr)) != nullptr)
41,785✔
1007
    {
1008
        nLineNum++;
41,760✔
1009

1010
        const char *pszPtr = pszLine;
41,760✔
1011
        char ch;
1012
        int nCol = 0;
41,760✔
1013
        bool bLastWasSep = true;
41,760✔
1014
        if (chDecimalSep == '\0')
41,760✔
1015
        {
1016
            int nCountComma = 0;
872✔
1017
            int nCountFieldSep = 0;
872✔
1018
            while ((ch = *pszPtr) != '\0')
15,582✔
1019
            {
1020
                if (ch == '.')
14,722✔
1021
                {
1022
                    chDecimalSep = '.';
12✔
1023
                    break;
12✔
1024
                }
1025
                else if (ch == ',')
14,710✔
1026
                {
1027
                    nCountComma++;
3✔
1028
                    bLastWasSep = false;
3✔
1029
                }
1030
                else if (ch == ' ')
14,707✔
1031
                {
1032
                    if (!bLastWasSep)
1,701✔
1033
                        nCountFieldSep++;
1,698✔
1034
                    bLastWasSep = true;
1,701✔
1035
                }
1036
                else if (ch == '\t' || ch == ';')
13,006✔
1037
                {
1038
                    nCountFieldSep++;
4✔
1039
                    bLastWasSep = true;
4✔
1040
                }
1041
                else
1042
                    bLastWasSep = false;
13,002✔
1043
                pszPtr++;
14,710✔
1044
            }
1045
            if (chDecimalSep == '\0')
872✔
1046
            {
1047
                /* 1,2,3 */
1048
                if (nCountComma >= 2 && nCountFieldSep == 0)
860✔
1049
                    chDecimalSep = '.';
1✔
1050
                /* 23,5;33;45 */
1051
                else if (nCountComma > 0 && nCountFieldSep > 0)
859✔
1052
                    chDecimalSep = ',';
1✔
1053
            }
1054
            pszPtr = pszLine;
872✔
1055
            bLastWasSep = true;
872✔
1056
        }
1057

1058
        char chLocalDecimalSep = chDecimalSep ? chDecimalSep : '.';
41,760✔
1059
        int nUsefulColsFound = 0;
41,760✔
1060
        while ((ch = *pszPtr) != '\0')
1,354,170✔
1061
        {
1062
            if (ch == ' ')
1,312,410✔
1063
            {
1064
                if (!bLastWasSep)
2,784✔
1065
                    nCol++;
2,664✔
1066
                bLastWasSep = true;
2,784✔
1067
            }
1068
            else if ((ch == ',' && chLocalDecimalSep != ',') || ch == '\t' ||
1,309,620✔
1069
                     ch == ';')
1070
            {
1071
                nCol++;
80,826✔
1072
                bLastWasSep = true;
80,826✔
1073
            }
1074
            else
1075
            {
1076
                if (bLastWasSep)
1,228,800✔
1077
                {
1078
                    if (nCol == nXIndex)
125,235✔
1079
                    {
1080
                        nUsefulColsFound++;
41,745✔
1081
                        dfX = CPLAtofDelim(pszPtr, chLocalDecimalSep);
41,745✔
1082
                    }
1083
                    else if (nCol == nYIndex)
83,490✔
1084
                    {
1085
                        nUsefulColsFound++;
41,745✔
1086
                        dfY = CPLAtofDelim(pszPtr, chLocalDecimalSep);
41,745✔
1087
                    }
1088
                    else if (nCol == nZIndex)
41,745✔
1089
                    {
1090
                        nUsefulColsFound++;
41,745✔
1091
                        dfZ = CPLAtofDelim(pszPtr, chLocalDecimalSep);
41,745✔
1092
                        if (nDataLineNum == 0)
41,745✔
1093
                        {
1094
                            dfMinZ = dfZ;
25✔
1095
                            dfMaxZ = dfZ;
25✔
1096
                        }
1097
                        else if (dfZ < dfMinZ)
41,720✔
1098
                        {
1099
                            dfMinZ = dfZ;
22✔
1100
                        }
1101
                        else if (dfZ > dfMaxZ)
41,698✔
1102
                        {
1103
                            dfMaxZ = dfZ;
306✔
1104
                        }
1105

1106
                        if (dfZ < INT_MIN || dfZ > INT_MAX)
41,745✔
1107
                        {
1108
                            eDT = GDT_Float32;
×
1109
                        }
1110
                        else
1111
                        {
1112
                            int nZ = static_cast<int>(dfZ);
41,745✔
1113
                            if (static_cast<double>(nZ) != dfZ)
41,745✔
1114
                            {
1115
                                eDT = GDT_Float32;
28,327✔
1116
                            }
1117
                            else if ((eDT == GDT_Byte || eDT == GDT_Int16)
13,418✔
1118
                                     // cppcheck-suppress
1119
                                     // knownConditionTrueFalse
1120
                                     && (nZ < 0 || nZ > 255))
5,588✔
1121
                            {
1122
                                if (nZ < -32768 || nZ > 32767)
10✔
1123
                                    eDT = GDT_Int32;
×
1124
                                else
1125
                                    eDT = GDT_Int16;
10✔
1126
                            }
1127
                        }
1128
                    }
1129
                }
1130
                bLastWasSep = false;
1,228,800✔
1131
            }
1132
            pszPtr++;
1,312,410✔
1133
        }
1134
        /* skip empty lines */
1135
        if (bLastWasSep && nCol == 0)
41,760✔
1136
        {
1137
            continue;
15✔
1138
        }
1139
        nDataLineNum++;
41,745✔
1140
        nCol++;
41,745✔
1141
        if (nCol < nMinTokens)
41,745✔
1142
        {
1143
            CPLError(CE_Failure, CPLE_AppDefined,
×
1144
                     "At line " CPL_FRMT_GIB
1145
                     ", found %d tokens. Expected %d at least",
1146
                     nLineNum, nCol, nMinTokens);
1147
            VSIFCloseL(fp);
×
1148
            return nullptr;
×
1149
        }
1150
        if (nUsefulColsFound != 3)
41,745✔
1151
        {
1152
            CPLError(CE_Failure, CPLE_AppDefined,
×
1153
                     "At line " CPL_FRMT_GIB
1154
                     ", did not find X, Y and/or Z values",
1155
                     nLineNum);
1156
            VSIFCloseL(fp);
×
1157
            return nullptr;
×
1158
        }
1159

1160
        if (nDataLineNum == 1)
41,745✔
1161
        {
1162
            dfMinX = dfX;
25✔
1163
            dfMaxX = dfX;
25✔
1164
            dfMinY = dfY;
25✔
1165
            dfMaxY = dfY;
25✔
1166
        }
1167
        else if (nDataLineNum == 2 && dfX == dfLastX)
41,720✔
1168
        {
1169
            // Detect datasets organized by columns
1170
            if (dfY == dfLastY)
7✔
1171
            {
1172
                CPLError(CE_Failure, CPLE_AppDefined,
×
1173
                         "Ungridded dataset: At line " CPL_FRMT_GIB
1174
                         ", Failed to detect grid layout.",
1175
                         nLineNum);
1176
                VSIFCloseL(fp);
×
1177
                return nullptr;
×
1178
            }
1179

1180
            bColOrganization = true;
7✔
1181
            const double dfStepY = dfY - dfLastY;
7✔
1182
            adfStepY.push_back(fabs(dfStepY));
7✔
1183
            nStepYSign = dfStepY > 0 ? 1 : -1;
7✔
1184
        }
1185
        else if (bColOrganization)
41,713✔
1186
        {
1187
            const double dfStepX = dfX - dfLastX;
29✔
1188
            if (dfStepX == 0)
29✔
1189
            {
1190
                const double dfStepY = dfY - dfLastY;
19✔
1191
                const double dfExpectedStepY = adfStepY.back() * nStepYSign;
19✔
1192
                if (fabs((dfStepY - dfExpectedStepY) / dfExpectedStepY) >
19✔
1193
                    RELATIVE_ERROR)
1194
                {
1195
                    CPLError(CE_Failure, CPLE_AppDefined,
×
1196
                             "Ungridded dataset: At line " CPL_FRMT_GIB
1197
                             ", Y spacing was %f. Expected %f",
1198
                             nLineNum, dfStepY, dfExpectedStepY);
1199
                    VSIFCloseL(fp);
×
1200
                    return nullptr;
×
1201
                }
1202
            }
1203
            else if (dfStepX > 0)
10✔
1204
            {
1205
                if (adfStepX.empty())
8✔
1206
                {
1207
                    adfStepX.push_back(dfStepX);
5✔
1208
                }
1209
                else if (fabs((dfStepX - adfStepX.back()) / adfStepX.back()) >
3✔
1210
                         RELATIVE_ERROR)
1211
                {
1212
                    CPLError(CE_Failure, CPLE_AppDefined,
×
1213
                             "Ungridded dataset: At line " CPL_FRMT_GIB
1214
                             ", X spacing was %f. Expected %f",
1215
                             nLineNum, dfStepX, adfStepX.back());
×
1216
                    VSIFCloseL(fp);
×
1217
                    return nullptr;
×
1218
                }
1219
            }
1220
            else if (nDataLineNum == 3)
2✔
1221
            {
1222
                const double dfStepY = dfY - dfLastY;
1✔
1223
                const double dfLastSignedStepY = nStepYSign * adfStepY.back();
1✔
1224
                if (dfStepY * dfLastSignedStepY > 0 &&
1✔
1225
                    (fabs(dfStepY - dfLastSignedStepY) <=
1✔
1226
                         RELATIVE_ERROR * fabs(dfLastSignedStepY)
1✔
1227
#ifdef multiple_of_step_y_not_yet_supported
1228
                     ||
1229
                     (fabs(dfStepY) > fabs(dfLastSignedStepY) &&
1230
                      fabs(std::round(dfStepY / dfLastSignedStepY) -
1231
                           (dfStepY / dfLastSignedStepY)) <= RELATIVE_ERROR) ||
1232
                     (fabs(dfLastSignedStepY) > fabs(dfStepY) &&
1233
                      fabs(std::round(dfLastSignedStepY / dfStepY) -
1234
                           (dfLastSignedStepY / dfStepY)) <= RELATIVE_ERROR)
1235
#endif
1236
                         ))
1237
                {
1238
                    // Assume it is a file starting with something like:
1239
                    // 371999.50 5806917.50 41.21
1240
                    // 371999.50 5806918.50 51.99
1241
                    // 371998.50 5806919.50 53.50
1242
                    // 371999.50 5806919.50 53.68
1243
                    adfStepX.push_back(dfLastX - dfX);
1✔
1244
                    bColOrganization = false;
1✔
1245
                }
1246
                else
1247
                {
1248
                    CPLError(CE_Failure, CPLE_AppDefined,
×
1249
                             "Ungridded dataset: At line " CPL_FRMT_GIB
1250
                             ", X spacing was %f. Expected >0 value",
1251
                             nLineNum, dfX - dfLastX);
1252
                    VSIFCloseL(fp);
×
1253
                    return nullptr;
×
1254
                }
1255
            }
1256
            else if (!adfStepX.empty() &&
1✔
1257
                     fabs(std::round(-dfStepX / adfStepX[0]) -
×
1258
                          (-dfStepX / adfStepX[0])) <= RELATIVE_ERROR)
×
1259
            {
1260
                bColOrganization = false;
×
1261
            }
1262
            else if (adfStepX.empty())
1✔
1263
            {
1264
                adfStepX.push_back(fabs(dfStepX));
1✔
1265
                bColOrganization = false;
1✔
1266
            }
1267
            else
1268
            {
1269
                CPLError(CE_Failure, CPLE_AppDefined,
×
1270
                         "Ungridded dataset: At line " CPL_FRMT_GIB
1271
                         ", X spacing was %f. Expected a multiple of %f",
1272
                         nLineNum, dfStepX, adfStepX[0]);
×
1273
                VSIFCloseL(fp);
×
1274
                return nullptr;
×
1275
            }
1276
        }
1277
        else
1278
        {
1279
            double dfStepY = dfY - dfLastY;
41,684✔
1280
            if (dfStepY == 0.0)
41,684✔
1281
            {
1282
                const double dfStepX = dfX - dfLastX;
41,398✔
1283
                if (dfStepX <= 0)
41,398✔
1284
                {
1285
                    CPLError(CE_Failure, CPLE_AppDefined,
×
1286
                             "Ungridded dataset: At line " CPL_FRMT_GIB
1287
                             ", X spacing was %f. Expected >0 value",
1288
                             nLineNum, dfStepX);
1289
                    VSIFCloseL(fp);
×
1290
                    return nullptr;
×
1291
                }
1292
                if (std::find(adfStepX.begin(), adfStepX.end(), dfStepX) ==
41,398✔
1293
                    adfStepX.end())
82,796✔
1294
                {
1295
                    bool bAddNewValue = true;
40✔
1296
                    std::vector<double>::iterator oIter = adfStepX.begin();
40✔
1297
                    std::vector<double> adfStepXNew;
40✔
1298
                    while (oIter != adfStepX.end())
41✔
1299
                    {
1300
                        if (fabs((dfStepX - *oIter) / dfStepX) < RELATIVE_ERROR)
22✔
1301
                        {
1302
                            double dfNewVal = *oIter;
20✔
1303
                            if (nCountStepX > 0)
20✔
1304
                            {
1305
                                // Update mean step
1306
                                /* n * mean(n) = (n-1) * mean(n-1) + val(n)
1307
                                mean(n) = mean(n-1) + (val(n) - mean(n-1)) / n
1308
                              */
1309
                                nCountStepX++;
20✔
1310
                                dfNewVal += (dfStepX - *oIter) / nCountStepX;
20✔
1311
                            }
1312

1313
                            adfStepXNew.push_back(dfNewVal);
20✔
1314
                            bAddNewValue = false;
20✔
1315
                            break;
20✔
1316
                        }
1317
                        else if (dfStepX < *oIter &&
3✔
1318
                                 fabs(*oIter -
1✔
1319
                                      static_cast<int>(*oIter / dfStepX + 0.5) *
1✔
1320
                                          dfStepX) /
1✔
1321
                                         dfStepX <
1✔
1322
                                     RELATIVE_ERROR)
1323
                        {
1324
                            nCountStepX = -1;  // disable update of mean
1✔
1325
                            ++oIter;
1✔
1326
                        }
1327
                        else if (dfStepX > *oIter &&
2✔
1328
                                 fabs(dfStepX -
2✔
1329
                                      static_cast<int>(dfStepX / *oIter + 0.5) *
1✔
1330
                                          (*oIter)) /
1✔
1331
                                         dfStepX <
1✔
1332
                                     RELATIVE_ERROR)
1333
                        {
1334
                            nCountStepX = -1;  // disable update of mean
1✔
1335
                            bAddNewValue = false;
1✔
1336
                            adfStepXNew.push_back(*oIter);
1✔
1337
                            break;
1✔
1338
                        }
1339
                        else
1340
                        {
1341
                            adfStepXNew.push_back(*oIter);
×
1342
                            ++oIter;
×
1343
                        }
1344
                    }
1345
                    adfStepX = std::move(adfStepXNew);
40✔
1346
                    if (bAddNewValue)
40✔
1347
                    {
1348
                        CPLDebug("XYZ", "New stepX=%.15f", dfStepX);
19✔
1349
                        adfStepX.push_back(dfStepX);
19✔
1350
                        if (adfStepX.size() == 1 && nCountStepX == 0)
19✔
1351
                        {
1352
                            nCountStepX++;
18✔
1353
                        }
1354
                        else if (adfStepX.size() == 2)
1✔
1355
                        {
1356
                            nCountStepX = -1;  // disable update of mean
×
1357
                        }
1358
                        else if (adfStepX.size() == 10)
1✔
1359
                        {
1360
                            CPLError(
×
1361
                                CE_Failure, CPLE_AppDefined,
1362
                                "Ungridded dataset: too many stepX values");
1363
                            VSIFCloseL(fp);
×
1364
                            return nullptr;
×
1365
                        }
1366
                    }
1367
                }
1368
            }
1369
            else
1370
            {
1371
                int bNewStepYSign = (dfStepY < 0.0) ? -1 : 1;
286✔
1372
                if (nStepYSign == 0)
286✔
1373
                    nStepYSign = bNewStepYSign;
18✔
1374
                else if (nStepYSign != bNewStepYSign)
268✔
1375
                {
1376
                    CPLError(CE_Failure, CPLE_AppDefined,
×
1377
                             "Ungridded dataset: At line " CPL_FRMT_GIB
1378
                             ", change of Y direction",
1379
                             nLineNum);
1380
                    VSIFCloseL(fp);
×
1381
                    return nullptr;
×
1382
                }
1383
                if (bNewStepYSign < 0)
286✔
1384
                    dfStepY = -dfStepY;
263✔
1385
                nCountStepY++;
286✔
1386
                if (adfStepY.empty())
286✔
1387
                {
1388
                    adfStepY.push_back(dfStepY);
18✔
1389
                }
1390
                else if (fabs((adfStepY[0] - dfStepY) / dfStepY) >
268✔
1391
                         RELATIVE_ERROR)
1392
                {
1393
                    if (dfStepY > adfStepY[0] &&
4✔
1394
                        fabs(std::round(dfStepY / adfStepY[0]) -
2✔
1395
                             (dfStepY / adfStepY[0])) <= RELATIVE_ERROR)
2✔
1396
                    {
1397
                        // The new step is a multiple of the previous one,
1398
                        // which means we have a missing line: OK
1399
                    }
1400
                    else
1401
                    {
1402
                        CPLDebug("XYZ", "New stepY=%.15f prev stepY=%.15f",
×
1403
                                 dfStepY, adfStepY[0]);
×
1404
                        CPLError(CE_Failure, CPLE_AppDefined,
×
1405
                                 "Ungridded dataset: At line " CPL_FRMT_GIB
1406
                                 ", too many stepY values",
1407
                                 nLineNum);
1408
                        VSIFCloseL(fp);
×
1409
                        return nullptr;
×
1410
                    }
1411
                }
1412
                else
1413
                {
1414
                    // Update mean step
1415
                    adfStepY[0] += (dfStepY - adfStepY[0]) / nCountStepY;
266✔
1416
                }
1417
            }
1418
        }
1419

1420
        if (dfX < dfMinX)
41,745✔
1421
            dfMinX = dfX;
4✔
1422
        if (dfX > dfMaxX)
41,745✔
1423
            dfMaxX = dfX;
285✔
1424
        if (dfY < dfMinY)
41,745✔
1425
            dfMinY = dfY;
267✔
1426
        if (dfY > dfMaxY)
41,745✔
1427
            dfMaxY = dfY;
33✔
1428

1429
        dfLastX = dfX;
41,745✔
1430
        dfLastY = dfY;
41,745✔
1431
    }
1432

1433
    if (adfStepX.size() != 1 || adfStepX[0] == 0)
25✔
1434
    {
1435
        CPLError(CE_Failure, CPLE_AppDefined, "Couldn't determine X spacing");
×
1436
        VSIFCloseL(fp);
×
1437
        return nullptr;
×
1438
    }
1439

1440
    if (adfStepY.size() != 1 || adfStepY[0] == 0)
25✔
1441
    {
1442
        CPLError(CE_Failure, CPLE_AppDefined, "Couldn't determine Y spacing");
×
1443
        VSIFCloseL(fp);
×
1444
        return nullptr;
×
1445
    }
1446

1447
    // Decide for a north-up organization
1448
    if (bColOrganization)
25✔
1449
        nStepYSign = -1;
5✔
1450

1451
    const double dfXSize = 1 + ((dfMaxX - dfMinX) / adfStepX[0] + 0.5);
25✔
1452
    const double dfYSize = 1 + ((dfMaxY - dfMinY) / adfStepY[0] + 0.5);
25✔
1453
    // Test written such as to detect NaN values
1454
    if (!(dfXSize > 0 && dfXSize < INT_MAX) ||
25✔
1455
        !(dfYSize > 0 && dfYSize < INT_MAX))
25✔
1456
    {
1457
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid dimensions");
×
1458
        VSIFCloseL(fp);
×
1459
        return nullptr;
×
1460
    }
1461
    const int nXSize = static_cast<int>(dfXSize);
25✔
1462
    const int nYSize = static_cast<int>(dfYSize);
25✔
1463
    const double dfStepX = (dfMaxX - dfMinX) / (nXSize - 1);
25✔
1464
    const double dfStepY = (dfMaxY - dfMinY) / (nYSize - 1) * nStepYSign;
25✔
1465

1466
#ifdef DEBUG_VERBOSE
1467
    CPLDebug("XYZ", "minx=%f maxx=%f stepx=%f", dfMinX, dfMaxX, dfStepX);
1468
    CPLDebug("XYZ", "miny=%f maxy=%f stepy=%f", dfMinY, dfMaxY, dfStepY);
1469
#endif
1470

1471
    if (nDataLineNum != static_cast<GIntBig>(nXSize) * nYSize)
25✔
1472
    {
1473
        if (bColOrganization)
5✔
1474
        {
1475
            CPLError(CE_Failure, CPLE_NotSupported,
×
1476
                     "The XYZ driver does not support datasets organized by "
1477
                     "columns with missing values");
1478
            VSIFCloseL(fp);
×
1479
            return nullptr;
×
1480
        }
1481
        bSameNumberOfValuesPerLine = false;
5✔
1482
    }
1483
    else if (bColOrganization && nDataLineNum > 100 * 1000 * 1000)
20✔
1484
    {
1485
        CPLError(CE_Failure, CPLE_NotSupported,
×
1486
                 "The XYZ driver cannot load datasets organized by "
1487
                 "columns with more than 100 million points");
1488
        VSIFCloseL(fp);
×
1489
        return nullptr;
×
1490
    }
1491

1492
    const bool bIngestAll = bColOrganization;
25✔
1493
    if (bIngestAll)
25✔
1494
    {
1495
        if (eDT == GDT_Int32)
5✔
1496
            eDT = GDT_Float32;
×
1497
        else if (eDT == GDT_Byte)
5✔
1498
            eDT = GDT_Int16;
1✔
1499
        CPLAssert(eDT == GDT_Int16 || eDT == GDT_Float32);
5✔
1500
    }
1501

1502
    if (poOpenInfo->eAccess == GA_Update)
25✔
1503
    {
1504
        ReportUpdateNotSupportedByDriver("XYZ");
×
1505
        VSIFCloseL(fp);
×
1506
        return nullptr;
×
1507
    }
1508

1509
    /* -------------------------------------------------------------------- */
1510
    /*      Create a corresponding GDALDataset.                             */
1511
    /* -------------------------------------------------------------------- */
1512
    XYZDataset *poDS = new XYZDataset();
25✔
1513
    poDS->fp = fp;
25✔
1514
    poDS->bHasHeaderLine = bHasHeaderLine;
25✔
1515
    poDS->nCommentLineCount = nCommentLineCount;
25✔
1516
    poDS->chDecimalSep = chDecimalSep ? chDecimalSep : '.';
25✔
1517
    poDS->nXIndex = nXIndex;
25✔
1518
    poDS->nYIndex = nYIndex;
25✔
1519
    poDS->nZIndex = nZIndex;
25✔
1520
    poDS->nMinTokens = nMinTokens;
25✔
1521
    poDS->nRasterXSize = nXSize;
25✔
1522
    poDS->nRasterYSize = nYSize;
25✔
1523
    poDS->adfGeoTransform[0] = dfMinX - dfStepX / 2;
25✔
1524
    poDS->adfGeoTransform[1] = dfStepX;
25✔
1525
    poDS->adfGeoTransform[3] =
25✔
1526
        (dfStepY < 0) ? dfMaxY - dfStepY / 2 : dfMinY - dfStepY / 2;
25✔
1527
    poDS->adfGeoTransform[5] = dfStepY;
25✔
1528
    poDS->bSameNumberOfValuesPerLine = bSameNumberOfValuesPerLine;
25✔
1529
    poDS->dfMinZ = dfMinZ;
25✔
1530
    poDS->dfMaxZ = dfMaxZ;
25✔
1531
    poDS->bIngestAll = bIngestAll;
25✔
1532
#ifdef DEBUG_VERBOSE
1533
    CPLDebug("XYZ", "bSameNumberOfValuesPerLine = %d",
1534
             bSameNumberOfValuesPerLine);
1535
#endif
1536

1537
    if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
25✔
1538
    {
1539
        delete poDS;
×
1540
        return nullptr;
×
1541
    }
1542

1543
    /* -------------------------------------------------------------------- */
1544
    /*      Create band information objects.                                */
1545
    /* -------------------------------------------------------------------- */
1546
    poDS->nBands = 1;
25✔
1547
    for (int i = 0; i < poDS->nBands; i++)
50✔
1548
        poDS->SetBand(i + 1, new XYZRasterBand(poDS, i + 1, eDT));
25✔
1549

1550
    /* -------------------------------------------------------------------- */
1551
    /*      Initialize any PAM information.                                 */
1552
    /* -------------------------------------------------------------------- */
1553
    poDS->SetDescription(poOpenInfo->pszFilename);
25✔
1554
    poDS->TryLoadXML();
25✔
1555

1556
    /* -------------------------------------------------------------------- */
1557
    /*      Support overviews.                                              */
1558
    /* -------------------------------------------------------------------- */
1559
    poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
25✔
1560
    return poDS;
25✔
1561
}
1562

1563
/************************************************************************/
1564
/*                             CreateCopy()                             */
1565
/************************************************************************/
1566

1567
GDALDataset *XYZDataset::CreateCopy(const char *pszFilename,
31✔
1568
                                    GDALDataset *poSrcDS, int bStrict,
1569
                                    char **papszOptions,
1570
                                    GDALProgressFunc pfnProgress,
1571
                                    void *pProgressData)
1572
{
1573
    /* -------------------------------------------------------------------- */
1574
    /*      Some some rudimentary checks                                    */
1575
    /* -------------------------------------------------------------------- */
1576
    int nBands = poSrcDS->GetRasterCount();
31✔
1577
    if (nBands == 0)
31✔
1578
    {
1579
        CPLError(
1✔
1580
            CE_Failure, CPLE_NotSupported,
1581
            "XYZ driver does not support source dataset with zero band.\n");
1582
        return nullptr;
1✔
1583
    }
1584

1585
    if (nBands != 1)
30✔
1586
    {
1587
        CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
4✔
1588
                 "XYZ driver only uses the first band of the dataset.\n");
1589
        if (bStrict)
4✔
1590
            return nullptr;
4✔
1591
    }
1592

1593
    if (pfnProgress && !pfnProgress(0.0, nullptr, pProgressData))
26✔
1594
        return nullptr;
×
1595

1596
    /* -------------------------------------------------------------------- */
1597
    /*      Get source dataset info                                         */
1598
    /* -------------------------------------------------------------------- */
1599

1600
    int nXSize = poSrcDS->GetRasterXSize();
26✔
1601
    int nYSize = poSrcDS->GetRasterYSize();
26✔
1602
    double adfGeoTransform[6];
1603
    poSrcDS->GetGeoTransform(adfGeoTransform);
26✔
1604
    if (adfGeoTransform[2] != 0 || adfGeoTransform[4] != 0)
26✔
1605
    {
1606
        CPLError(CE_Failure, CPLE_NotSupported,
×
1607
                 "XYZ driver does not support CreateCopy() from skewed or "
1608
                 "rotated dataset.\n");
1609
        return nullptr;
×
1610
    }
1611

1612
    const GDALDataType eSrcDT = poSrcDS->GetRasterBand(1)->GetRasterDataType();
26✔
1613
    GDALDataType eReqDT;
1614
    if (eSrcDT == GDT_Byte || eSrcDT == GDT_Int16 || eSrcDT == GDT_UInt16 ||
26✔
1615
        eSrcDT == GDT_Int32)
1616
        eReqDT = GDT_Int32;
18✔
1617
    else
1618
        eReqDT = GDT_Float32;
8✔
1619

1620
    /* -------------------------------------------------------------------- */
1621
    /*      Create target file                                              */
1622
    /* -------------------------------------------------------------------- */
1623

1624
    VSILFILE *fp = VSIFOpenL(pszFilename, "wb");
26✔
1625
    if (fp == nullptr)
26✔
1626
    {
1627
        CPLError(CE_Failure, CPLE_AppDefined, "Cannot create %s", pszFilename);
3✔
1628
        return nullptr;
3✔
1629
    }
1630

1631
    /* -------------------------------------------------------------------- */
1632
    /*      Read creation options                                           */
1633
    /* -------------------------------------------------------------------- */
1634
    const char *pszColSep = CSLFetchNameValue(papszOptions, "COLUMN_SEPARATOR");
23✔
1635
    if (pszColSep == nullptr)
23✔
1636
        pszColSep = " ";
22✔
1637
    else if (EQUAL(pszColSep, "COMMA"))
1✔
1638
        pszColSep = ",";
×
1639
    else if (EQUAL(pszColSep, "SPACE"))
1✔
1640
        pszColSep = " ";
×
1641
    else if (EQUAL(pszColSep, "SEMICOLON"))
1✔
1642
        pszColSep = ";";
×
1643
    else if (EQUAL(pszColSep, "\\t") || EQUAL(pszColSep, "TAB"))
1✔
1644
        pszColSep = "\t";
×
1645
#ifdef DEBUG_VERBOSE
1646
    else
1647
        CPLDebug("XYZ", "Using raw column separator: '%s' ", pszColSep);
1648
#endif
1649

1650
    const char *pszAddHeaderLine =
1651
        CSLFetchNameValue(papszOptions, "ADD_HEADER_LINE");
23✔
1652
    if (pszAddHeaderLine != nullptr && CPLTestBool(pszAddHeaderLine))
23✔
1653
    {
1654
        VSIFPrintfL(fp, "X%sY%sZ\n", pszColSep, pszColSep);
1✔
1655
    }
1656

1657
    /* -------------------------------------------------------------------- */
1658
    /*      Copy imagery                                                    */
1659
    /* -------------------------------------------------------------------- */
1660
    char szFormat[50] = {'\0'};
23✔
1661
    if (eReqDT == GDT_Int32)
23✔
1662
        strcpy(szFormat, "%.17g%c%.17g%c%d\n");
15✔
1663
    else
1664
        strcpy(szFormat, "%.17g%c%.17g%c%.17g\n");
8✔
1665
    const char *pszDecimalPrecision =
1666
        CSLFetchNameValue(papszOptions, "DECIMAL_PRECISION");
23✔
1667
    const char *pszSignificantDigits =
1668
        CSLFetchNameValue(papszOptions, "SIGNIFICANT_DIGITS");
23✔
1669
    bool bIgnoreSigDigits = false;
23✔
1670
    if (pszDecimalPrecision && pszSignificantDigits)
23✔
1671
    {
1672
        CPLError(CE_Warning, CPLE_AppDefined,
×
1673
                 "Conflicting precision arguments, using DECIMAL_PRECISION");
1674
        bIgnoreSigDigits = true;
×
1675
    }
1676
    int nPrecision;
1677
    if (pszSignificantDigits && !bIgnoreSigDigits)
23✔
1678
    {
1679
        nPrecision = atoi(pszSignificantDigits);
×
1680
        if (nPrecision >= 0)
×
1681
        {
1682
            if (eReqDT == GDT_Int32)
×
1683
                snprintf(szFormat, sizeof(szFormat), "%%.%dg%%c%%.%dg%%c%%d\n",
×
1684
                         nPrecision, nPrecision);
1685
            else
1686
                snprintf(szFormat, sizeof(szFormat),
×
1687
                         "%%.%dg%%c%%.%dg%%c%%.%dg\n", nPrecision, nPrecision,
1688
                         nPrecision);
1689
        }
1690
        CPLDebug("XYZ", "Setting precision format: %s", szFormat);
×
1691
    }
1692
    else if (pszDecimalPrecision)
23✔
1693
    {
1694
        nPrecision = atoi(pszDecimalPrecision);
×
1695
        if (nPrecision >= 0)
×
1696
        {
1697
            if (eReqDT == GDT_Int32)
×
1698
                snprintf(szFormat, sizeof(szFormat), "%%.%df%%c%%.%df%%c%%d\n",
×
1699
                         nPrecision, nPrecision);
1700
            else
1701
                snprintf(szFormat, sizeof(szFormat),
×
1702
                         "%%.%df%%c%%.%df%%c%%.%df\n", nPrecision, nPrecision,
1703
                         nPrecision);
1704
        }
1705
        CPLDebug("XYZ", "Setting precision format: %s", szFormat);
×
1706
    }
1707
    void *pLineBuffer =
1708
        reinterpret_cast<void *>(CPLMalloc(nXSize * sizeof(int)));
23✔
1709
    CPLErr eErr = CE_None;
23✔
1710
    for (int j = 0; j < nYSize && eErr == CE_None; j++)
405✔
1711
    {
1712
        eErr = poSrcDS->GetRasterBand(1)->RasterIO(GF_Read, 0, j, nXSize, 1,
382✔
1713
                                                   pLineBuffer, nXSize, 1,
1714
                                                   eReqDT, 0, 0, nullptr);
1715
        if (eErr != CE_None)
382✔
1716
            break;
×
1717
        const double dfY = adfGeoTransform[3] + (j + 0.5) * adfGeoTransform[5];
382✔
1718
        CPLString osBuf;
382✔
1719
        for (int i = 0; i < nXSize; i++)
42,747✔
1720
        {
1721
            const double dfX =
42,375✔
1722
                adfGeoTransform[0] + (i + 0.5) * adfGeoTransform[1];
42,375✔
1723
            char szBuf[256];
1724
            if (eReqDT == GDT_Int32)
42,375✔
1725
                CPLsnprintf(szBuf, sizeof(szBuf), szFormat, dfX, pszColSep[0],
1,274✔
1726
                            dfY, pszColSep[0],
1,274✔
1727
                            reinterpret_cast<int *>(pLineBuffer)[i]);
1,274✔
1728
            else
1729
                CPLsnprintf(szBuf, sizeof(szBuf), szFormat, dfX, pszColSep[0],
41,101✔
1730
                            dfY, pszColSep[0],
41,101✔
1731
                            reinterpret_cast<float *>(pLineBuffer)[i]);
41,101✔
1732
            osBuf += szBuf;
42,375✔
1733
            if ((i & 1023) == 0 || i == nXSize - 1)
42,375✔
1734
            {
1735
                if (VSIFWriteL(osBuf, static_cast<int>(osBuf.size()), 1, fp) !=
760✔
1736
                    1)
1737
                {
1738
                    eErr = CE_Failure;
10✔
1739
                    CPLError(CE_Failure, CPLE_AppDefined,
10✔
1740
                             "Write failed, disk full?\n");
1741
                    break;
10✔
1742
                }
1743
                osBuf = "";
750✔
1744
            }
1745
        }
1746
        if (pfnProgress &&
764✔
1747
            !pfnProgress((j + 1) * 1.0 / nYSize, nullptr, pProgressData))
382✔
1748
        {
1749
            eErr = CE_Failure;
×
1750
            break;
×
1751
        }
1752
    }
1753
    CPLFree(pLineBuffer);
23✔
1754
    VSIFCloseL(fp);
23✔
1755

1756
    if (eErr != CE_None)
23✔
1757
        return nullptr;
10✔
1758

1759
    /* -------------------------------------------------------------------- */
1760
    /*      We don't want to call GDALOpen() since it will be expensive,    */
1761
    /*      so we "hand prepare" an XYZ dataset referencing our file.       */
1762
    /* -------------------------------------------------------------------- */
1763
    XYZDataset *poXYZ_DS = new XYZDataset();
13✔
1764
    poXYZ_DS->nRasterXSize = nXSize;
13✔
1765
    poXYZ_DS->nRasterYSize = nYSize;
13✔
1766
    poXYZ_DS->nBands = 1;
13✔
1767
    poXYZ_DS->SetBand(1, new XYZRasterBand(poXYZ_DS, 1, eReqDT));
13✔
1768
    /* If writing to stdout, we can't reopen it --> silence warning */
1769
    CPLPushErrorHandler(CPLQuietErrorHandler);
13✔
1770
    poXYZ_DS->fp = VSIFOpenL(pszFilename, "rb");
13✔
1771
    CPLPopErrorHandler();
13✔
1772
    memcpy(&(poXYZ_DS->adfGeoTransform), adfGeoTransform, sizeof(double) * 6);
13✔
1773
    poXYZ_DS->nXIndex = 0;
13✔
1774
    poXYZ_DS->nYIndex = 1;
13✔
1775
    poXYZ_DS->nZIndex = 2;
13✔
1776
    if (pszAddHeaderLine)
13✔
1777
    {
1778
        poXYZ_DS->nDataLineNum = 1;
1✔
1779
        poXYZ_DS->bHasHeaderLine = TRUE;
1✔
1780
    }
1781

1782
    return poXYZ_DS;
13✔
1783
}
1784

1785
/************************************************************************/
1786
/*                          GetGeoTransform()                           */
1787
/************************************************************************/
1788

1789
CPLErr XYZDataset::GetGeoTransform(double *padfTransform)
19✔
1790

1791
{
1792
    memcpy(padfTransform, adfGeoTransform, 6 * sizeof(double));
19✔
1793

1794
    return CE_None;
19✔
1795
}
1796

1797
/************************************************************************/
1798
/*                         GDALRegister_XYZ()                           */
1799
/************************************************************************/
1800

1801
void GDALRegister_XYZ()
1,646✔
1802

1803
{
1804
    if (GDALGetDriverByName("XYZ") != nullptr)
1,646✔
1805
        return;
281✔
1806

1807
    GDALDriver *poDriver = new GDALDriver();
1,365✔
1808

1809
    poDriver->SetDescription("XYZ");
1,365✔
1810
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1,365✔
1811
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "ASCII Gridded XYZ");
1,365✔
1812
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/xyz.html");
1,365✔
1813
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "xyz");
1,365✔
1814
    poDriver->SetMetadataItem(
1,365✔
1815
        GDAL_DMD_CREATIONOPTIONLIST,
1816
        "<CreationOptionList>"
1817
        "   <Option name='COLUMN_SEPARATOR' type='string' default=' ' "
1818
        "description='Separator between fields.'/>"
1819
        "   <Option name='ADD_HEADER_LINE' type='boolean' default='false' "
1820
        "description='Add an header line with column names.'/>"
1821
        "   <Option name='SIGNIFICANT_DIGITS' type='int' description='Number "
1822
        "of significant digits when writing floating-point numbers (%g format; "
1823
        "default with 18).'/>\n"
1824
        "   <Option name='DECIMAL_PRECISION' type='int' description='Number of "
1825
        "decimal places when writing floating-point numbers (%f format).'/>\n"
1826
        "</CreationOptionList>");
1,365✔
1827
    poDriver->SetMetadataItem(
1,365✔
1828
        GDAL_DMD_OPENOPTIONLIST,
1829
        "<OpenOptionList>"
1830
        "   <Option name='COLUMN_ORDER' type='string-select' default='AUTO' "
1831
        "description='Specifies the order of the columns. It overrides the "
1832
        "header.'>"
1833
        "       <Value>AUTO</Value>"
1834
        "       <Value>XYZ</Value>"
1835
        "       <Value>YXZ</Value>"
1836
        "   </Option>"
1837
        "</OpenOptionList>");
1,365✔
1838

1839
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1,365✔
1840

1841
    poDriver->pfnOpen = XYZDataset::Open;
1,365✔
1842
    poDriver->pfnIdentify = XYZDataset::Identify;
1,365✔
1843
    poDriver->pfnCreateCopy = XYZDataset::CreateCopy;
1,365✔
1844

1845
    GetGDALDriverManager()->RegisterDriver(poDriver);
1,365✔
1846
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc