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

OSGeo / gdal / 15899162844

26 Jun 2025 10:14AM UTC coverage: 71.088% (+0.004%) from 71.084%
15899162844

Pull #12623

github

web-flow
Merge c704a8392 into f5cb024d4
Pull Request #12623: gdal raster overview add: add a --overview-src option

209 of 244 new or added lines in 5 files covered. (85.66%)

96 existing lines in 44 files now uncovered.

574014 of 807474 relevant lines covered (71.09%)

250815.03 hits per line

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

81.41
/frmts/aaigrid/aaigriddataset.cpp
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  Implements Arc/Info ASCII Grid Format.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2001, Frank Warmerdam (warmerdam@pobox.com)
9
 * Copyright (c) 2007-2012, Even Rouault <even dot rouault at spatialys.com>
10
 * Copyright (c) 2014, Kyle Shannon <kyle at pobox dot com>
11
 *
12
 * SPDX-License-Identifier: MIT
13
 ****************************************************************************/
14

15
// We need cpl_port as first include to avoid VSIStatBufL being not
16
// defined on i586-mingw32msvc.
17
#include "cpl_port.h"
18
#include "aaigriddataset.h"
19
#include "gdal_frmts.h"
20

21
#include <cassert>
22
#include <cctype>
23
#include <climits>
24
#include <cmath>
25
#include <cstddef>
26
#include <cstdio>
27
#include <cstdlib>
28
#include <cstring>
29
#if HAVE_FCNTL_H
30
#include <fcntl.h>
31
#endif
32

33
#include <algorithm>
34
#include <limits>
35
#include <string>
36

37
#include "cpl_conv.h"
38
#include "cpl_error.h"
39
#include "cpl_progress.h"
40
#include "cpl_string.h"
41
#include "cpl_vsi.h"
42
#include "gdal.h"
43
#include "gdal_pam.h"
44
#include "gdal_priv.h"
45
#include "ogr_core.h"
46
#include "ogr_spatialref.h"
47

48
namespace
49
{
50

51
float DoubleToFloatClamp(double dfValue)
2,643✔
52
{
53
    if (dfValue <= std::numeric_limits<float>::lowest())
2,643✔
54
        return std::numeric_limits<float>::lowest();
×
55
    if (dfValue >= std::numeric_limits<float>::max())
2,643✔
56
        return std::numeric_limits<float>::max();
×
57
    return static_cast<float>(dfValue);
2,643✔
58
}
59

60
// Cast to float and back for make sure the NoData value matches
61
// that expressed by a float value.  Clamps to the range of a float
62
// if the value is too large.  Preserves +/-inf and NaN.
63
// TODO(schwehr): This should probably be moved to port as it is likely
64
// to be needed for other formats.
65
double MapNoDataToFloat(double dfNoDataValue)
17✔
66
{
67
    if (std::isinf(dfNoDataValue) || std::isnan(dfNoDataValue))
17✔
68
        return dfNoDataValue;
3✔
69

70
    if (dfNoDataValue >= std::numeric_limits<float>::max())
14✔
71
        return std::numeric_limits<float>::max();
×
72

73
    if (dfNoDataValue <= -std::numeric_limits<float>::max())
14✔
74
        return -std::numeric_limits<float>::max();
×
75

76
    return static_cast<double>(static_cast<float>(dfNoDataValue));
14✔
77
}
78

79
}  // namespace
80

81
static CPLString OSR_GDS(char **papszNV, const char *pszField,
82
                         const char *pszDefaultValue);
83

84
/************************************************************************/
85
/*                           AAIGRasterBand()                           */
86
/************************************************************************/
87

88
AAIGRasterBand::AAIGRasterBand(AAIGDataset *poDSIn, int nDataStart)
208✔
89
    : panLineOffset(nullptr)
208✔
90
{
91
    poDS = poDSIn;
208✔
92

93
    nBand = 1;
208✔
94
    eDataType = poDSIn->eDataType;
208✔
95

96
    nBlockXSize = poDSIn->nRasterXSize;
208✔
97
    nBlockYSize = 1;
208✔
98

99
    panLineOffset = static_cast<GUIntBig *>(
208✔
100
        VSI_CALLOC_VERBOSE(poDSIn->nRasterYSize, sizeof(GUIntBig)));
208✔
101
    if (panLineOffset == nullptr)
208✔
102
    {
103
        return;
×
104
    }
105
    panLineOffset[0] = nDataStart;
208✔
106
}
107

108
/************************************************************************/
109
/*                          ~AAIGRasterBand()                           */
110
/************************************************************************/
111

112
AAIGRasterBand::~AAIGRasterBand()
416✔
113
{
114
    CPLFree(panLineOffset);
208✔
115
}
416✔
116

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

121
CPLErr AAIGRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
1,125✔
122

123
{
124
    AAIGDataset *poODS = static_cast<AAIGDataset *>(poDS);
1,125✔
125

126
    if (nBlockYOff < 0 || nBlockYOff > poODS->nRasterYSize - 1 ||
1,125✔
127
        nBlockXOff != 0 || panLineOffset == nullptr || poODS->fp == nullptr)
1,125✔
128
        return CE_Failure;
×
129

130
    if (panLineOffset[nBlockYOff] == 0)
1,125✔
131
    {
132
        for (int iPrevLine = 1; iPrevLine <= nBlockYOff; iPrevLine++)
22✔
133
            if (panLineOffset[iPrevLine] == 0)
18✔
134
                IReadBlock(nBlockXOff, iPrevLine - 1, nullptr);
18✔
135
    }
136

137
    if (panLineOffset[nBlockYOff] == 0)
1,125✔
138
        return CE_Failure;
×
139

140
    if (poODS->Seek(panLineOffset[nBlockYOff]) != 0)
1,125✔
141
    {
142
        ReportError(CE_Failure, CPLE_FileIO,
×
143
                    "Can't seek to offset %lu in input file to read data.",
144
                    static_cast<long unsigned int>(panLineOffset[nBlockYOff]));
×
145
        return CE_Failure;
×
146
    }
147

148
    for (int iPixel = 0; iPixel < poODS->nRasterXSize;)
34,438✔
149
    {
150
        // Suck up any pre-white space.
151
        char chNext = '\0';
33,313✔
152
        do
11,096✔
153
        {
154
            chNext = poODS->Getc();
44,409✔
155
        } while (isspace(static_cast<unsigned char>(chNext)));
44,409✔
156

157
        char szToken[500] = {'\0'};
33,313✔
158
        int iTokenChar = 0;
33,313✔
159
        while (chNext != '\0' && !isspace(static_cast<unsigned char>(chNext)))
134,727✔
160
        {
161
            if (iTokenChar == sizeof(szToken) - 2)
101,414✔
162
            {
163
                ReportError(CE_Failure, CPLE_FileIO,
×
164
                            "Token too long at scanline %d.", nBlockYOff);
165
                return CE_Failure;
×
166
            }
167

168
            szToken[iTokenChar++] = chNext;
101,414✔
169
            chNext = poODS->Getc();
101,414✔
170
        }
171

172
        if (chNext == '\0' && (iPixel != poODS->nRasterXSize - 1 ||
33,313✔
173
                               nBlockYOff != poODS->nRasterYSize - 1))
56✔
174
        {
175
            ReportError(CE_Failure, CPLE_FileIO,
×
176
                        "File short, can't read line %d.", nBlockYOff);
177
            return CE_Failure;
×
178
        }
179

180
        szToken[iTokenChar] = '\0';
33,313✔
181

182
        if (pImage != nullptr)
33,313✔
183
        {
184
            // "null" seems to be specific of D12 software
185
            // See https://github.com/OSGeo/gdal/issues/5095
186
            if (eDataType == GDT_Float64)
33,115✔
187
            {
188
                if (strcmp(szToken, "null") == 0)
104✔
189
                    reinterpret_cast<double *>(pImage)[iPixel] =
2✔
190
                        -std::numeric_limits<double>::max();
2✔
191
                else
192
                    reinterpret_cast<double *>(pImage)[iPixel] =
204✔
193
                        CPLAtofM(szToken);
102✔
194
            }
195
            else if (eDataType == GDT_Float32)
33,011✔
196
            {
197
                if (strcmp(szToken, "null") == 0)
2,645✔
198
                    reinterpret_cast<float *>(pImage)[iPixel] =
2✔
199
                        -std::numeric_limits<float>::max();
2✔
200
                else
201
                    reinterpret_cast<float *>(pImage)[iPixel] =
2,643✔
202
                        DoubleToFloatClamp(CPLAtofM(szToken));
2,643✔
203
            }
204
            else
205
                reinterpret_cast<GInt32 *>(pImage)[iPixel] =
30,366✔
206
                    static_cast<GInt32>(atoi(szToken));
30,366✔
207
        }
208

209
        iPixel++;
33,313✔
210
    }
211

212
    if (nBlockYOff < poODS->nRasterYSize - 1)
1,125✔
213
        panLineOffset[nBlockYOff + 1] = poODS->Tell();
992✔
214

215
    return CE_None;
1,125✔
216
}
217

218
/************************************************************************/
219
/*                           GetNoDataValue()                           */
220
/************************************************************************/
221

222
double AAIGRasterBand::GetNoDataValue(int *pbSuccess)
285✔
223

224
{
225
    AAIGDataset *poODS = static_cast<AAIGDataset *>(poDS);
285✔
226

227
    if (pbSuccess)
285✔
228
        *pbSuccess = poODS->bNoDataSet;
258✔
229

230
    return poODS->dfNoDataValue;
285✔
231
}
232

233
/************************************************************************/
234
/*                           SetNoDataValue()                           */
235
/************************************************************************/
236

237
CPLErr AAIGRasterBand::SetNoDataValue(double dfNoData)
×
238

239
{
240
    AAIGDataset *poODS = static_cast<AAIGDataset *>(poDS);
×
241

242
    poODS->bNoDataSet = true;
×
243
    poODS->dfNoDataValue = dfNoData;
×
244

245
    return CE_None;
×
246
}
247

248
/************************************************************************/
249
/* ==================================================================== */
250
/*                            AAIGDataset                               */
251
/* ==================================================================== */
252
/************************************************************************/
253

254
/************************************************************************/
255
/*                            AAIGDataset()                            */
256
/************************************************************************/
257

258
AAIGDataset::AAIGDataset()
209✔
259
    : fp(nullptr), papszPrj(nullptr), nBufferOffset(0), nOffsetInBuffer(256),
260
      eDataType(GDT_Int32), bNoDataSet(false), dfNoDataValue(-9999.0)
209✔
261
{
262
    m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
209✔
263
    memset(achReadBuf, 0, sizeof(achReadBuf));
209✔
264
}
209✔
265

266
/************************************************************************/
267
/*                           ~AAIGDataset()                            */
268
/************************************************************************/
269

270
AAIGDataset::~AAIGDataset()
408✔
271

272
{
273
    FlushCache(true);
209✔
274

275
    if (fp != nullptr)
209✔
276
    {
277
        if (VSIFCloseL(fp) != 0)
208✔
278
        {
279
            ReportError(CE_Failure, CPLE_FileIO, "I/O error");
×
280
        }
281
    }
282

283
    CSLDestroy(papszPrj);
209✔
284
}
408✔
285

286
/************************************************************************/
287
/*                                Tell()                                */
288
/************************************************************************/
289

290
GUIntBig AAIGDataset::Tell() const
992✔
291
{
292
    return nBufferOffset + nOffsetInBuffer;
992✔
293
}
294

295
/************************************************************************/
296
/*                                Seek()                                */
297
/************************************************************************/
298

299
int AAIGDataset::Seek(GUIntBig nNewOffset)
1,125✔
300

301
{
302
    nOffsetInBuffer = sizeof(achReadBuf);
1,125✔
303
    return VSIFSeekL(fp, nNewOffset, SEEK_SET);
1,125✔
304
}
305

306
/************************************************************************/
307
/*                                Getc()                                */
308
/*                                                                      */
309
/*      Read a single character from the input file (efficiently we     */
310
/*      hope).                                                          */
311
/************************************************************************/
312

313
char AAIGDataset::Getc()
145,823✔
314

315
{
316
    if (nOffsetInBuffer < static_cast<int>(sizeof(achReadBuf)))
145,823✔
317
        return achReadBuf[nOffsetInBuffer++];
144,498✔
318

319
    nBufferOffset = VSIFTellL(fp);
1,325✔
320
    const int nRead =
321
        static_cast<int>(VSIFReadL(achReadBuf, 1, sizeof(achReadBuf), fp));
1,325✔
322
    for (unsigned int i = nRead; i < sizeof(achReadBuf); i++)
83,131✔
323
        achReadBuf[i] = '\0';
81,806✔
324

325
    nOffsetInBuffer = 0;
1,325✔
326

327
    return achReadBuf[nOffsetInBuffer++];
1,325✔
328
}
329

330
/************************************************************************/
331
/*                            GetFileList()                             */
332
/************************************************************************/
333

334
char **AAIGDataset::GetFileList()
32✔
335

336
{
337
    char **papszFileList = GDALPamDataset::GetFileList();
32✔
338

339
    if (papszPrj != nullptr)
32✔
340
        papszFileList = CSLAddString(papszFileList, osPrjFilename);
12✔
341

342
    return papszFileList;
32✔
343
}
344

345
/************************************************************************/
346
/*                            Identify()                                */
347
/************************************************************************/
348

349
int AAIGDataset::Identify(GDALOpenInfo *poOpenInfo)
62,650✔
350

351
{
352
    // Does this look like an AI grid file?
353
    if (poOpenInfo->nHeaderBytes < 40 ||
62,650✔
354
        !(STARTS_WITH_CI(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
7,834✔
355
                         "ncols") ||
7,431✔
356
          STARTS_WITH_CI(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
7,431✔
357
                         "nrows") ||
7,431✔
358
          STARTS_WITH_CI(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
7,431✔
359
                         "xllcorner") ||
7,431✔
360
          STARTS_WITH_CI(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
7,431✔
361
                         "yllcorner") ||
7,431✔
362
          STARTS_WITH_CI(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
7,431✔
363
                         "xllcenter") ||
7,431✔
364
          STARTS_WITH_CI(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
7,431✔
365
                         "yllcenter") ||
7,431✔
366
          STARTS_WITH_CI(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
7,431✔
367
                         "dx") ||
7,431✔
368
          STARTS_WITH_CI(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
7,431✔
369
                         "dy") ||
370
          STARTS_WITH_CI(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
7,431✔
371
                         "cellsize")))
372
        return FALSE;
62,247✔
373

374
    return TRUE;
403✔
375
}
376

377
/************************************************************************/
378
/*                            Identify()                                */
379
/************************************************************************/
380

381
int GRASSASCIIDataset::Identify(GDALOpenInfo *poOpenInfo)
62,190✔
382

383
{
384
    // Does this look like a GRASS ASCII grid file?
385
    if (poOpenInfo->nHeaderBytes < 40 ||
62,190✔
386
        !(STARTS_WITH_CI(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
7,434✔
387
                         "north:") ||
7,430✔
388
          STARTS_WITH_CI(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
7,430✔
389
                         "south:") ||
7,430✔
390
          STARTS_WITH_CI(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
7,430✔
391
                         "east:") ||
7,430✔
392
          STARTS_WITH_CI(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
7,430✔
393
                         "west:") ||
7,430✔
394
          STARTS_WITH_CI(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
7,430✔
395
                         "rows:") ||
396
          STARTS_WITH_CI(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
7,430✔
397
                         "cols:")))
398
        return FALSE;
62,198✔
399

UNCOV
400
    return TRUE;
×
401
}
402

403
/************************************************************************/
404
/*                            Identify()                                */
405
/************************************************************************/
406

407
int ISGDataset::Identify(GDALOpenInfo *poOpenInfo)
62,206✔
408

409
{
410
    // Does this look like a ISG grid file?
411
    if (poOpenInfo->nHeaderBytes < 40 ||
62,206✔
412
        !strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
7,438✔
413
                "model name"))
414
    {
415
        return FALSE;
62,190✔
416
    }
417
    for (int i = 0; i < 2; ++i)
17✔
418
    {
419
        if (strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
17✔
420
                   "lat min") != nullptr &&
17✔
421
            strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
17✔
422
                   "lat max") != nullptr &&
17✔
423
            strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
17✔
424
                   "lon min") != nullptr &&
17✔
425
            strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
17✔
426
                   "lon max") != nullptr &&
17✔
427
            strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
17✔
428
                   "nrows") != nullptr &&
16✔
429
            strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
16✔
430
                   "ncols") != nullptr)
431
        {
432
            return TRUE;
16✔
433
        }
434
        // Some files like https://isgeoid.polimi.it/Geoid/Europe/Slovenia/public/Slovenia_2016_SLO_VRP2016_Koper_hybrQ_20221122.isg
435
        // have initial comment lines, so we may need to ingest more bytes
436
        if (i == 0)
1✔
437
        {
438
            if (poOpenInfo->nHeaderBytes >= 8192)
1✔
439
                break;
×
440
            poOpenInfo->TryToIngest(8192);
1✔
441
        }
442
    }
443

444
    return TRUE;
×
445
}
446

447
/************************************************************************/
448
/*                                Open()                                */
449
/************************************************************************/
450

451
GDALDataset *AAIGDataset::Open(GDALOpenInfo *poOpenInfo)
199✔
452
{
453
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
454
    // During fuzzing, do not use Identify to reject crazy content.
455
    if (!Identify(poOpenInfo))
199✔
456
        return nullptr;
×
457
#endif
458

459
    return CommonOpen(poOpenInfo, FORMAT_AAIG);
199✔
460
}
461

462
/************************************************************************/
463
/*                          ParseHeader()                               */
464
/************************************************************************/
465

466
int AAIGDataset::ParseHeader(const char *pszHeader, const char *pszDataType)
199✔
467
{
468
    char **papszTokens = CSLTokenizeString2(pszHeader, " \n\r\t", 0);
199✔
469
    const int nTokens = CSLCount(papszTokens);
199✔
470

471
    int i = 0;
199✔
472
    if ((i = CSLFindString(papszTokens, "ncols")) < 0 || i + 1 >= nTokens)
199✔
473
    {
474
        CSLDestroy(papszTokens);
×
475
        return FALSE;
×
476
    }
477
    nRasterXSize = atoi(papszTokens[i + 1]);
199✔
478
    if ((i = CSLFindString(papszTokens, "nrows")) < 0 || i + 1 >= nTokens)
199✔
479
    {
480
        CSLDestroy(papszTokens);
×
481
        return FALSE;
×
482
    }
483
    nRasterYSize = atoi(papszTokens[i + 1]);
199✔
484

485
    if (!GDALCheckDatasetDimensions(nRasterXSize, nRasterYSize))
199✔
486
    {
487
        CSLDestroy(papszTokens);
×
488
        return FALSE;
×
489
    }
490

491
    // TODO(schwehr): Would be good to also factor the file size into the max.
492
    // TODO(schwehr): Allow the user to disable this check.
493
    // The driver allocates a panLineOffset array based on nRasterYSize
494
    constexpr int kMaxDimSize = 10000000;  // 1e7 cells.
199✔
495
    if (nRasterXSize > kMaxDimSize || nRasterYSize > kMaxDimSize)
199✔
496
    {
497
        CSLDestroy(papszTokens);
×
498
        return FALSE;
×
499
    }
500

501
    double dfCellDX = 0.0;
199✔
502
    double dfCellDY = 0.0;
199✔
503
    if ((i = CSLFindString(papszTokens, "cellsize")) < 0)
199✔
504
    {
505
        int iDX, iDY;
506
        if ((iDX = CSLFindString(papszTokens, "dx")) < 0 ||
4✔
507
            (iDY = CSLFindString(papszTokens, "dy")) < 0 ||
4✔
508
            iDX + 1 >= nTokens || iDY + 1 >= nTokens)
8✔
509
        {
510
            CSLDestroy(papszTokens);
×
511
            return FALSE;
×
512
        }
513

514
        dfCellDX = CPLAtofM(papszTokens[iDX + 1]);
4✔
515
        dfCellDY = CPLAtofM(papszTokens[iDY + 1]);
4✔
516
    }
517
    else
518
    {
519
        if (i + 1 >= nTokens)
195✔
520
        {
521
            CSLDestroy(papszTokens);
×
522
            return FALSE;
×
523
        }
524
        dfCellDY = CPLAtofM(papszTokens[i + 1]);
195✔
525
        dfCellDX = dfCellDY;
195✔
526
    }
527

528
    int j = 0;
199✔
529
    if ((i = CSLFindString(papszTokens, "xllcorner")) >= 0 &&
199✔
530
        (j = CSLFindString(papszTokens, "yllcorner")) >= 0 && i + 1 < nTokens &&
398✔
531
        j + 1 < nTokens)
199✔
532
    {
533
        m_gt[0] = CPLAtofM(papszTokens[i + 1]);
199✔
534

535
        // Small hack to compensate from insufficient precision in cellsize
536
        // parameter in datasets of
537
        // http://ccafs-climate.org/data/A2a_2020s/hccpr_hadcm3
538
        if ((nRasterXSize % 360) == 0 && fabs(m_gt[0] - (-180.0)) < 1e-12 &&
×
539
            dfCellDX == dfCellDY &&
199✔
540
            fabs(dfCellDX - (360.0 / nRasterXSize)) < 1e-9)
×
541
        {
542
            dfCellDY = 360.0 / nRasterXSize;
×
543
            dfCellDX = dfCellDY;
×
544
        }
545

546
        m_gt[1] = dfCellDX;
199✔
547
        m_gt[2] = 0.0;
199✔
548
        m_gt[3] = CPLAtofM(papszTokens[j + 1]) + nRasterYSize * dfCellDY;
199✔
549
        m_gt[4] = 0.0;
199✔
550
        m_gt[5] = -dfCellDY;
199✔
551
    }
552
    else if ((i = CSLFindString(papszTokens, "xllcenter")) >= 0 &&
×
553
             (j = CSLFindString(papszTokens, "yllcenter")) >= 0 &&
×
554
             i + 1 < nTokens && j + 1 < nTokens)
×
555
    {
556
        SetMetadataItem(GDALMD_AREA_OR_POINT, GDALMD_AOP_POINT);
×
557

558
        m_gt[0] = CPLAtofM(papszTokens[i + 1]) - 0.5 * dfCellDX;
×
559
        m_gt[1] = dfCellDX;
×
560
        m_gt[2] = 0.0;
×
561
        m_gt[3] = CPLAtofM(papszTokens[j + 1]) - 0.5 * dfCellDY +
×
562
                  nRasterYSize * dfCellDY;
×
563
        m_gt[4] = 0.0;
×
564
        m_gt[5] = -dfCellDY;
×
565
    }
566
    else
567
    {
568
        m_gt[0] = 0.0;
×
569
        m_gt[1] = dfCellDX;
×
570
        m_gt[2] = 0.0;
×
571
        m_gt[3] = 0.0;
×
572
        m_gt[4] = 0.0;
×
573
        m_gt[5] = -dfCellDY;
×
574
    }
575

576
    if ((i = CSLFindString(papszTokens, "NODATA_value")) >= 0 &&
278✔
577
        i + 1 < nTokens)
79✔
578
    {
579
        const char *pszNoData = papszTokens[i + 1];
79✔
580

581
        bNoDataSet = true;
79✔
582
        if (strcmp(pszNoData, "null") == 0)
79✔
583
        {
584
            // "null" seems to be specific of D12 software
585
            // See https://github.com/OSGeo/gdal/issues/5095
586
            if (pszDataType == nullptr || eDataType == GDT_Float32)
2✔
587
            {
588
                dfNoDataValue = -std::numeric_limits<float>::max();
1✔
589
                eDataType = GDT_Float32;
1✔
590
            }
591
            else
592
            {
593
                dfNoDataValue = -std::numeric_limits<double>::max();
1✔
594
                eDataType = GDT_Float64;
1✔
595
            }
596
        }
597
        else
598
        {
599
            dfNoDataValue = CPLAtofM(pszNoData);
77✔
600
            if (pszDataType == nullptr &&
149✔
601
                (strchr(pszNoData, '.') != nullptr ||
72✔
602
                 strchr(pszNoData, ',') != nullptr ||
128✔
603
                 std::isnan(dfNoDataValue) ||
64✔
604
                 std::numeric_limits<int>::min() > dfNoDataValue ||
61✔
605
                 dfNoDataValue > std::numeric_limits<int>::max()))
61✔
606
            {
607
                eDataType = GDT_Float32;
11✔
608
                if (!std::isinf(dfNoDataValue) &&
22✔
609
                    (fabs(dfNoDataValue) < std::numeric_limits<float>::min() ||
11✔
610
                     fabs(dfNoDataValue) > std::numeric_limits<float>::max()))
10✔
611
                {
612
                    eDataType = GDT_Float64;
1✔
613
                }
614
            }
615
            if (eDataType == GDT_Float32)
77✔
616
            {
617
                dfNoDataValue = MapNoDataToFloat(dfNoDataValue);
10✔
618
            }
619
        }
620
    }
621

622
    CSLDestroy(papszTokens);
199✔
623

624
    return TRUE;
199✔
625
}
626

627
/************************************************************************/
628
/*                                Open()                                */
629
/************************************************************************/
630

631
GDALDataset *GRASSASCIIDataset::Open(GDALOpenInfo *poOpenInfo)
2✔
632
{
633
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
634
    // During fuzzing, do not use Identify to reject crazy content.
635
    if (!Identify(poOpenInfo))
2✔
636
        return nullptr;
×
637
#endif
638

639
    return CommonOpen(poOpenInfo, FORMAT_GRASSASCII);
2✔
640
}
641

642
/************************************************************************/
643
/*                          ParseHeader()                               */
644
/************************************************************************/
645

646
int GRASSASCIIDataset::ParseHeader(const char *pszHeader,
2✔
647
                                   const char *pszDataType)
648
{
649
    char **papszTokens = CSLTokenizeString2(pszHeader, " \n\r\t:", 0);
2✔
650
    const int nTokens = CSLCount(papszTokens);
2✔
651
    int i = 0;
2✔
652
    if ((i = CSLFindString(papszTokens, "cols")) < 0 || i + 1 >= nTokens)
2✔
653
    {
654
        CSLDestroy(papszTokens);
×
655
        return FALSE;
×
656
    }
657
    nRasterXSize = atoi(papszTokens[i + 1]);
2✔
658
    if ((i = CSLFindString(papszTokens, "rows")) < 0 || i + 1 >= nTokens)
2✔
659
    {
660
        CSLDestroy(papszTokens);
×
661
        return FALSE;
×
662
    }
663
    nRasterYSize = atoi(papszTokens[i + 1]);
2✔
664

665
    if (!GDALCheckDatasetDimensions(nRasterXSize, nRasterYSize))
2✔
666
    {
667
        CSLDestroy(papszTokens);
×
668
        return FALSE;
×
669
    }
670

671
    // TODO(schwehr): Would be good to also factor the file size into the max.
672
    // TODO(schwehr): Allow the user to disable this check.
673
    // The driver allocates a panLineOffset array based on nRasterYSize
674
    constexpr int kMaxDimSize = 10000000;  // 1e7 cells.
2✔
675
    if (nRasterXSize > kMaxDimSize || nRasterYSize > kMaxDimSize)
2✔
676
    {
677
        CSLDestroy(papszTokens);
×
678
        return FALSE;
×
679
    }
680

681
    const int iNorth = CSLFindString(papszTokens, "north");
2✔
682
    const int iSouth = CSLFindString(papszTokens, "south");
2✔
683
    const int iEast = CSLFindString(papszTokens, "east");
2✔
684
    const int iWest = CSLFindString(papszTokens, "west");
2✔
685

686
    if (iNorth == -1 || iSouth == -1 || iEast == -1 || iWest == -1 ||
4✔
687
        std::max(std::max(iNorth, iSouth), std::max(iEast, iWest)) + 1 >=
2✔
688
            nTokens)
689
    {
690
        CSLDestroy(papszTokens);
×
691
        return FALSE;
×
692
    }
693

694
    const double dfNorth = CPLAtofM(papszTokens[iNorth + 1]);
2✔
695
    const double dfSouth = CPLAtofM(papszTokens[iSouth + 1]);
2✔
696
    const double dfEast = CPLAtofM(papszTokens[iEast + 1]);
2✔
697
    const double dfWest = CPLAtofM(papszTokens[iWest + 1]);
2✔
698
    const double dfPixelXSize = (dfEast - dfWest) / nRasterXSize;
2✔
699
    const double dfPixelYSize = (dfNorth - dfSouth) / nRasterYSize;
2✔
700

701
    m_gt[0] = dfWest;
2✔
702
    m_gt[1] = dfPixelXSize;
2✔
703
    m_gt[2] = 0.0;
2✔
704
    m_gt[3] = dfNorth;
2✔
705
    m_gt[4] = 0.0;
2✔
706
    m_gt[5] = -dfPixelYSize;
2✔
707

708
    if ((i = CSLFindString(papszTokens, "null")) >= 0 && i + 1 < nTokens)
2✔
709
    {
710
        const char *pszNoData = papszTokens[i + 1];
×
711

712
        bNoDataSet = true;
×
713
        dfNoDataValue = CPLAtofM(pszNoData);
×
714
        if (pszDataType == nullptr &&
×
715
            (strchr(pszNoData, '.') != nullptr ||
×
716
             strchr(pszNoData, ',') != nullptr || std::isnan(dfNoDataValue) ||
×
717
             std::numeric_limits<int>::min() > dfNoDataValue ||
×
718
             dfNoDataValue > std::numeric_limits<int>::max()))
×
719
        {
720
            eDataType = GDT_Float32;
×
721
        }
722
        if (eDataType == GDT_Float32)
×
723
        {
724
            dfNoDataValue = MapNoDataToFloat(dfNoDataValue);
×
725
        }
726
    }
727

728
    if ((i = CSLFindString(papszTokens, "type")) >= 0 && i + 1 < nTokens)
2✔
729
    {
730
        const char *pszType = papszTokens[i + 1];
×
731
        if (EQUAL(pszType, "int"))
×
732
            eDataType = GDT_Int32;
×
733
        else if (EQUAL(pszType, "float"))
×
734
            eDataType = GDT_Float32;
×
735
        else if (EQUAL(pszType, "double"))
×
736
            eDataType = GDT_Float64;
×
737
        else
738
        {
739
            ReportError(CE_Warning, CPLE_AppDefined,
×
740
                        "Invalid value for type parameter : %s", pszType);
741
        }
742
    }
743

744
    CSLDestroy(papszTokens);
2✔
745

746
    return TRUE;
2✔
747
}
748

749
/************************************************************************/
750
/*                                Open()                                */
751
/************************************************************************/
752

753
GDALDataset *ISGDataset::Open(GDALOpenInfo *poOpenInfo)
8✔
754
{
755
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
756
    // During fuzzing, do not use Identify to reject crazy content.
757
    if (!Identify(poOpenInfo))
8✔
758
        return nullptr;
×
759
#endif
760

761
    return CommonOpen(poOpenInfo, FORMAT_ISG);
8✔
762
}
763

764
/************************************************************************/
765
/*                          ParseHeader()                               */
766
/************************************************************************/
767

768
int ISGDataset::ParseHeader(const char *pszHeader, const char *)
8✔
769
{
770
    // See https://www.isgeoid.polimi.it/Geoid/ISG_format_v10_20160121.pdf
771
    //     https://www.isgeoid.polimi.it/Geoid/ISG_format_v101_20180915.pdf
772
    //     https://www.isgeoid.polimi.it/Geoid/ISG_format_v20_20200625.pdf
773

774
    CPLStringList aosLines(CSLTokenizeString2(pszHeader, "\n\r", 0));
16✔
775
    CPLString osLatMin;
16✔
776
    CPLString osLatMax;
16✔
777
    CPLString osLonMin;
16✔
778
    CPLString osLonMax;
16✔
779
    CPLString osDeltaLat;
16✔
780
    CPLString osDeltaLon;
16✔
781
    CPLString osRows;
16✔
782
    CPLString osCols;
16✔
783
    CPLString osNodata;
16✔
784
    std::string osISGFormat;
16✔
785
    std::string osDataFormat;    // ISG 2.0
16✔
786
    std::string osDataOrdering;  // ISG 2.0
16✔
787
    std::string osCoordType;     // ISG 2.0
16✔
788
    std::string osCoordUnits;    // ISG 2.0
16✔
789
    for (int iLine = 0; iLine < aosLines.size(); iLine++)
180✔
790
    {
791
        CPLStringList aosTokens(CSLTokenizeString2(aosLines[iLine], ":=", 0));
344✔
792
        if (aosTokens.size() == 2)
172✔
793
        {
794
            const CPLString osLeft(CPLString(aosTokens[0]).Trim());
280✔
795
            CPLString osRight(CPLString(aosTokens[1]).Trim());
280✔
796
            if (osLeft == "lat min")
140✔
797
                osLatMin = std::move(osRight);
8✔
798
            else if (osLeft == "lat max")
132✔
799
                osLatMax = std::move(osRight);
8✔
800
            else if (osLeft == "lon min")
124✔
801
                osLonMin = std::move(osRight);
8✔
802
            else if (osLeft == "lon max")
116✔
803
                osLonMax = std::move(osRight);
8✔
804
            else if (osLeft == "delta lat")
108✔
805
                osDeltaLat = std::move(osRight);
8✔
806
            else if (osLeft == "delta lon")
100✔
807
                osDeltaLon = std::move(osRight);
8✔
808
            else if (osLeft == "nrows")
92✔
809
                osRows = std::move(osRight);
8✔
810
            else if (osLeft == "ncols")
84✔
811
                osCols = std::move(osRight);
8✔
812
            else if (osLeft == "nodata")
76✔
813
                osNodata = std::move(osRight);
8✔
814
            else if (osLeft == "model name")
68✔
815
                SetMetadataItem("MODEL_NAME", osRight);
8✔
816
            else if (osLeft == "model type")
60✔
817
                SetMetadataItem("MODEL_TYPE", osRight);
8✔
818
            else if (osLeft == "units" || osLeft == "data units")
52✔
819
                osUnits = std::move(osRight);
8✔
820
            else if (osLeft == "ISG format")
44✔
821
                osISGFormat = std::move(osRight);
8✔
822
            else if (osLeft == "data format")
36✔
823
                osDataFormat = std::move(osRight);
2✔
824
            else if (osLeft == "data ordering")
34✔
825
                osDataOrdering = std::move(osRight);
2✔
826
            else if (osLeft == "coord type")
32✔
827
                osCoordType = std::move(osRight);
2✔
828
            else if (osLeft == "coord units")
30✔
829
                osCoordUnits = std::move(osRight);
2✔
830
        }
831
    }
832
    const double dfVersion =
833
        osISGFormat.empty() ? 0.0 : CPLAtof(osISGFormat.c_str());
8✔
834
    if (osLatMin.empty() || osLatMax.empty() || osLonMin.empty() ||
24✔
835
        osLonMax.empty() || osDeltaLat.empty() || osDeltaLon.empty() ||
24✔
836
        osRows.empty() || osCols.empty())
24✔
837
    {
838
        return FALSE;
×
839
    }
840
    if (!osDataFormat.empty() && osDataFormat != "grid")
8✔
841
    {
842
        CPLError(CE_Failure, CPLE_NotSupported,
×
843
                 "ISG: data format = %s not supported", osDataFormat.c_str());
844
        return FALSE;
×
845
    }
846
    if (!osDataOrdering.empty() && osDataOrdering != "N-to-S, W-to-E")
8✔
847
    {
848
        CPLError(CE_Failure, CPLE_NotSupported,
×
849
                 "ISG: data ordering = %s not supported",
850
                 osDataOrdering.c_str());
851
        return FALSE;
×
852
    }
853
    if (!osCoordType.empty() && osCoordType != "geodetic")
8✔
854
    {
855
        CPLError(CE_Failure, CPLE_NotSupported,
×
856
                 "ISG: coord type = %s not supported", osCoordType.c_str());
857
        return FALSE;
×
858
    }
859

860
    const auto parseDMS = [](CPLString &str)
6✔
861
    {
862
        const std::string degreeSymbol{"\xc2\xb0"};
12✔
863
        str.replaceAll(degreeSymbol, "D");
6✔
864
        return CPLDMSToDec(str);
12✔
865
    };
866

867
    bool useDMS = false;
8✔
868
    if (!osCoordUnits.empty())
8✔
869
    {
870
        if (osCoordUnits == "dms")
2✔
871
        {
872
            // CPLDMSToDec does not support the non ascii char for degree used in ISG.
873
            // just replace it with "D" to make it compatible.
874
            useDMS = true;
1✔
875
        }
876
        else if (osCoordUnits != "deg")
1✔
877
        {
878
            CPLError(CE_Failure, CPLE_NotSupported,
×
879
                     "ISG: coord units = %s not supported",
880
                     osCoordUnits.c_str());
881
            return FALSE;
×
882
        }
883
    }
884
    double dfLatMin = useDMS ? parseDMS(osLatMin) : CPLAtof(osLatMin);
8✔
885
    double dfLatMax = useDMS ? parseDMS(osLatMax) : CPLAtof(osLatMax);
8✔
886
    double dfLonMin = useDMS ? parseDMS(osLonMin) : CPLAtof(osLonMin);
8✔
887
    double dfLonMax = useDMS ? parseDMS(osLonMax) : CPLAtof(osLonMax);
8✔
888
    double dfDeltaLon = useDMS ? parseDMS(osDeltaLon) : CPLAtof(osDeltaLon);
8✔
889
    double dfDeltaLat = useDMS ? parseDMS(osDeltaLat) : CPLAtof(osDeltaLat);
8✔
890
    if (dfVersion >= 2.0)
8✔
891
    {
892
        dfLatMin -= dfDeltaLat / 2.0;
2✔
893
        dfLatMax += dfDeltaLat / 2.0;
2✔
894
        dfLonMin -= dfDeltaLon / 2.0;
2✔
895
        dfLonMax += dfDeltaLon / 2.0;
2✔
896
    }
897
    const int nRows = atoi(osRows);
8✔
898
    const int nCols = atoi(osCols);
8✔
899
    if (nRows <= 0 || nCols <= 0 ||
8✔
900
        !(dfDeltaLat > 0 && dfDeltaLon > 0 && dfDeltaLat < 180 &&
8✔
901
          dfDeltaLon < 360))
8✔
902
    {
903
        return FALSE;
×
904
    }
905

906
    // Correct rounding errors.
907

908
    const auto TryRoundTo = [](double &dfDelta, double dfRoundedDelta,
14✔
909
                               double &dfMin, double &dfMax, int nVals,
910
                               double dfRelTol)
911
    {
912
        double dfMinTry = dfMin;
14✔
913
        double dfMaxTry = dfMax;
14✔
914
        double dfDeltaTry = dfDelta;
14✔
915
        if (dfRoundedDelta != dfDelta &&
14✔
916
            fabs(fabs(dfMin / dfRoundedDelta) -
6✔
917
                 (floor(fabs(dfMin / dfRoundedDelta)) + 0.5)) < dfRelTol &&
6✔
918
            fabs(fabs(dfMax / dfRoundedDelta) -
6✔
919
                 (floor(fabs(dfMax / dfRoundedDelta)) + 0.5)) < dfRelTol)
6✔
920
        {
921
            {
922
                double dfVal = (floor(fabs(dfMin / dfRoundedDelta)) + 0.5) *
5✔
923
                               dfRoundedDelta;
924
                dfMinTry = (dfMin < 0) ? -dfVal : dfVal;
5✔
925
            }
926
            {
927
                double dfVal = (floor(fabs(dfMax / dfRoundedDelta)) + 0.5) *
5✔
928
                               dfRoundedDelta;
929
                dfMaxTry = (dfMax < 0) ? -dfVal : dfVal;
5✔
930
            }
931
            dfDeltaTry = dfRoundedDelta;
5✔
932
        }
933
        else if (dfRoundedDelta != dfDelta &&
9✔
934
                 fabs(fabs(dfMin / dfRoundedDelta) -
1✔
935
                      (floor(fabs(dfMin / dfRoundedDelta) + 0.5) + 0.)) <
1✔
936
                     dfRelTol &&
×
937
                 fabs(fabs(dfMax / dfRoundedDelta) -
×
938
                      (floor(fabs(dfMax / dfRoundedDelta) + 0.5) + 0.)) <
×
939
                     dfRelTol)
940
        {
941
            {
942
                double dfVal =
×
943
                    (floor(fabs(dfMin / dfRoundedDelta) + 0.5) + 0.) *
×
944
                    dfRoundedDelta;
945
                dfMinTry = (dfMin < 0) ? -dfVal : dfVal;
×
946
            }
947
            {
948
                double dfVal =
×
949
                    (floor(fabs(dfMax / dfRoundedDelta) + 0.5) + 0.) *
×
950
                    dfRoundedDelta;
951
                dfMaxTry = (dfMax < 0) ? -dfVal : dfVal;
×
952
            }
953
            dfDeltaTry = dfRoundedDelta;
×
954
        }
955
        if (fabs(dfMinTry + dfDeltaTry * nVals - dfMaxTry) <
14✔
956
            dfRelTol * dfDeltaTry)
14✔
957
        {
958
            dfMin = dfMinTry;
10✔
959
            dfMax = dfMaxTry;
10✔
960
            dfDelta = dfDeltaTry;
10✔
961
            return true;
10✔
962
        }
963
        return false;
4✔
964
    };
965

966
    const double dfRoundedDeltaLon =
967
        (osDeltaLon == "0.0167" ||
8✔
968
         (dfDeltaLon < 1 &&
7✔
969
          fabs(1. / dfDeltaLon - floor(1. / dfDeltaLon + 0.5)) < 0.06))
7✔
970
            ? 1. / floor(1. / dfDeltaLon + 0.5)
15✔
971
            : dfDeltaLon;
8✔
972

973
    const double dfRoundedDeltaLat =
974
        (osDeltaLat == "0.0167" ||
8✔
975
         (dfDeltaLat < 1 &&
4✔
976
          fabs(1. / dfDeltaLat - floor(1. / dfDeltaLat + 0.5)) < 0.06))
4✔
977
            ? 1. / floor(1. / dfDeltaLat + 0.5)
12✔
978
            : dfDeltaLat;
8✔
979

980
    bool bOK = TryRoundTo(dfDeltaLon, dfRoundedDeltaLon, dfLonMin, dfLonMax,
8✔
981
                          nCols, 1e-2) &&
12✔
982
               TryRoundTo(dfDeltaLat, dfRoundedDeltaLat, dfLatMin, dfLatMax,
4✔
983
                          nRows, 1e-2);
8✔
984
    if (!bOK && osDeltaLon == "0.0167" && osDeltaLat == "0.0167")
8✔
985
    {
986
        // For https://www.isgeoid.polimi.it/Geoid/America/Argentina/public/GEOIDEAR16_20160419.isg
987
        bOK =
1✔
988
            TryRoundTo(dfDeltaLon, 0.016667, dfLonMin, dfLonMax, nCols, 1e-1) &&
2✔
989
            TryRoundTo(dfDeltaLat, 0.016667, dfLatMin, dfLatMax, nRows, 1e-1);
1✔
990
    }
991
    if (!bOK)
8✔
992
    {
993
        // 0.005 is what would be needed for the above GEOIDEAR16_20160419.isg
994
        // file without the specific fine tuning done.
995
        if ((fabs((dfLonMax - dfLonMin) / nCols - dfDeltaLon) <
6✔
996
                 0.005 * dfDeltaLon &&
4✔
997
             fabs((dfLatMax - dfLatMin) / nRows - dfDeltaLat) <
1✔
998
                 0.005 * dfDeltaLat) ||
5✔
999
            CPLTestBool(
2✔
1000
                CPLGetConfigOption("ISG_SKIP_GEOREF_CONSISTENCY_CHECK", "NO")))
1001
        {
1002
            CPLError(CE_Warning, CPLE_AppDefined,
2✔
1003
                     "Georeference might be slightly approximate due to "
1004
                     "rounding of coordinates and resolution in file header.");
1005
            dfDeltaLon = (dfLonMax - dfLonMin) / nCols;
2✔
1006
            dfDeltaLat = (dfLatMax - dfLatMin) / nRows;
2✔
1007
        }
1008
        else
1009
        {
1010
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
1011
                     "Inconsistent extent/resolution/raster dimension, or "
1012
                     "rounding of coordinates and resolution in file header "
1013
                     "higher than accepted. You may skip this consistency "
1014
                     "check by setting the ISG_SKIP_GEOREF_CONSISTENCY_CHECK "
1015
                     "configuration option to YES.");
1016
            return false;
1✔
1017
        }
1018
    }
1019
    nRasterXSize = nCols;
7✔
1020
    nRasterYSize = nRows;
7✔
1021
    m_gt[0] = dfLonMin;
7✔
1022
    m_gt[1] = dfDeltaLon;
7✔
1023
    m_gt[2] = 0.0;
7✔
1024
    m_gt[3] = dfLatMax;
7✔
1025
    m_gt[4] = 0.0;
7✔
1026
    m_gt[5] = -dfDeltaLat;
7✔
1027
    if (!osNodata.empty())
7✔
1028
    {
1029
        bNoDataSet = true;
7✔
1030
        dfNoDataValue = MapNoDataToFloat(CPLAtof(osNodata));
7✔
1031
    }
1032
    return TRUE;
7✔
1033
}
1034

1035
/************************************************************************/
1036
/*                           CommonOpen()                               */
1037
/************************************************************************/
1038

1039
GDALDataset *AAIGDataset::CommonOpen(GDALOpenInfo *poOpenInfo,
209✔
1040
                                     GridFormat eFormat)
1041
{
1042
    if (poOpenInfo->fpL == nullptr)
209✔
1043
        return nullptr;
×
1044

1045
    // Create a corresponding GDALDataset.
1046
    AAIGDataset *poDS = nullptr;
209✔
1047

1048
    if (eFormat == FORMAT_AAIG)
209✔
1049
        poDS = new AAIGDataset();
199✔
1050
    else if (eFormat == FORMAT_GRASSASCII)
10✔
1051
        poDS = new GRASSASCIIDataset();
2✔
1052
    else
1053
    {
1054
        poDS = new ISGDataset();
8✔
1055
        poDS->eDataType = GDT_Float32;
8✔
1056
    }
1057

1058
    const char *pszDataTypeOption = eFormat == FORMAT_AAIG ? "AAIGRID_DATATYPE"
219✔
1059
                                    : eFormat == FORMAT_GRASSASCII
1060
                                        ? "GRASSASCIIGRID_DATATYPE"
10✔
1061
                                        : nullptr;
1062

1063
    const char *pszDataType =
1064
        pszDataTypeOption ? CPLGetConfigOption(pszDataTypeOption, nullptr)
209✔
1065
                          : nullptr;
209✔
1066
    if (pszDataType == nullptr)
209✔
1067
    {
1068
        pszDataType =
1069
            CSLFetchNameValue(poOpenInfo->papszOpenOptions, "DATATYPE");
207✔
1070
    }
1071
    if (pszDataType != nullptr)
209✔
1072
    {
1073
        poDS->eDataType = GDALGetDataTypeByName(pszDataType);
6✔
1074
        if (!(poDS->eDataType == GDT_Int32 || poDS->eDataType == GDT_Float32 ||
6✔
1075
              poDS->eDataType == GDT_Float64))
6✔
1076
        {
1077
            ReportError(poOpenInfo->pszFilename, CE_Warning, CPLE_NotSupported,
×
1078
                        "Unsupported value for %s : %s", pszDataTypeOption,
1079
                        pszDataType);
1080
            poDS->eDataType = GDT_Int32;
×
1081
            pszDataType = nullptr;
×
1082
        }
1083
    }
1084

1085
    // Parse the header.
1086
    if (!poDS->ParseHeader(
209✔
1087
            reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
209✔
1088
            pszDataType))
209✔
1089
    {
1090
        delete poDS;
1✔
1091
        return nullptr;
1✔
1092
    }
1093

1094
    poDS->fp = poOpenInfo->fpL;
208✔
1095
    poOpenInfo->fpL = nullptr;
208✔
1096

1097
    // Find the start of real data.
1098
    int nStartOfData = 0;
208✔
1099

1100
    if (eFormat == FORMAT_ISG)
208✔
1101
    {
1102
        const char *pszEOH =
1103
            strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
7✔
1104
                   "end_of_head");
1105
        if (pszEOH == nullptr)
7✔
1106
        {
1107
            delete poDS;
×
1108
            return nullptr;
×
1109
        }
1110
        for (int i = 0; pszEOH[i]; i++)
443✔
1111
        {
1112
            if (pszEOH[i] == '\n' || pszEOH[i] == '\r')
443✔
1113
            {
1114
                nStartOfData =
7✔
1115
                    static_cast<int>(pszEOH - reinterpret_cast<const char *>(
7✔
1116
                                                  poOpenInfo->pabyHeader)) +
7✔
1117
                    i;
1118
                break;
7✔
1119
            }
1120
        }
1121
        if (nStartOfData == 0)
7✔
1122
        {
1123
            delete poDS;
×
1124
            return nullptr;
×
1125
        }
1126
        if (poOpenInfo->pabyHeader[nStartOfData] == '\n' ||
7✔
1127
            poOpenInfo->pabyHeader[nStartOfData] == '\r')
×
1128
        {
1129
            nStartOfData++;
7✔
1130
        }
1131

1132
        poDS->m_oSRS.importFromWkt(SRS_WKT_WGS84_LAT_LONG);
7✔
1133
    }
1134
    else
1135
    {
1136
        for (int i = 2; true; i++)
22,644✔
1137
        {
1138
            if (poOpenInfo->pabyHeader[i] == '\0')
22,644✔
1139
            {
1140
                ReportError(poOpenInfo->pszFilename, CE_Failure,
×
1141
                            CPLE_AppDefined,
1142
                            "Couldn't find data values in ASCII Grid file.");
1143
                delete poDS;
×
1144
                return nullptr;
×
1145
            }
1146

1147
            if (poOpenInfo->pabyHeader[i - 1] == '\n' ||
22,644✔
1148
                poOpenInfo->pabyHeader[i - 2] == '\n' ||
21,554✔
1149
                poOpenInfo->pabyHeader[i - 1] == '\r' ||
20,665✔
1150
                poOpenInfo->pabyHeader[i - 2] == '\r')
20,581✔
1151
            {
1152
                if ((!isalpha(static_cast<unsigned char>(
2,063✔
1153
                         poOpenInfo->pabyHeader[i])) ||
2,063✔
1154
                     // null seems to be specific of D12 software
1155
                     // See https://github.com/OSGeo/gdal/issues/5095
1156
                     (i + 5 < poOpenInfo->nHeaderBytes &&
1,783✔
1157
                      memcmp(poOpenInfo->pabyHeader + i, "null ", 5) == 0) ||
1,783✔
1158
                     (i + 4 < poOpenInfo->nHeaderBytes &&
1,781✔
1159
                      EQUALN(reinterpret_cast<const char *>(
1,781✔
1160
                                 poOpenInfo->pabyHeader + i),
1161
                             "nan ", 4))) &&
285✔
1162
                    poOpenInfo->pabyHeader[i] != '\n' &&
285✔
1163
                    poOpenInfo->pabyHeader[i] != '\r')
201✔
1164
                {
1165
                    nStartOfData = i;
201✔
1166

1167
                    // Beginning of real data found.
1168
                    break;
201✔
1169
                }
1170
            }
1171
        }
1172
    }
1173

1174
    // Recognize the type of data.
1175
    CPLAssert(nullptr != poDS->fp);
208✔
1176

1177
    if (pszDataType == nullptr && poDS->eDataType != GDT_Float32 &&
208✔
1178
        poDS->eDataType != GDT_Float64)
184✔
1179
    {
1180
        // Allocate 100K chunk + 1 extra byte for NULL character.
1181
        constexpr size_t nChunkSize = 1024 * 100;
183✔
1182
        GByte *pabyChunk = static_cast<GByte *>(
1183
            VSI_CALLOC_VERBOSE(nChunkSize + 1, sizeof(GByte)));
183✔
1184
        if (pabyChunk == nullptr)
183✔
1185
        {
1186
            delete poDS;
×
1187
            return nullptr;
×
1188
        }
1189
        pabyChunk[nChunkSize] = '\0';
183✔
1190

1191
        if (VSIFSeekL(poDS->fp, nStartOfData, SEEK_SET) < 0)
183✔
1192
        {
1193
            delete poDS;
×
1194
            VSIFree(pabyChunk);
×
1195
            return nullptr;
×
1196
        }
1197

1198
        // Scan for dot in subsequent chunks of data.
1199
        while (!VSIFEofL(poDS->fp))
366✔
1200
        {
1201
            const size_t nLen = VSIFReadL(pabyChunk, 1, nChunkSize, poDS->fp);
183✔
1202

1203
            for (size_t i = 0; i < nLen; i++)
175,584✔
1204
            {
1205
                const GByte ch = pabyChunk[i];
175,446✔
1206
                if (ch == '.' || ch == ',' || ch == 'e' || ch == 'E')
175,446✔
1207
                {
1208
                    poDS->eDataType = GDT_Float32;
45✔
1209
                    break;
45✔
1210
                }
1211
            }
1212
        }
1213

1214
        // Deallocate chunk.
1215
        VSIFree(pabyChunk);
183✔
1216
    }
1217

1218
    // Create band information objects.
1219
    AAIGRasterBand *band = new AAIGRasterBand(poDS, nStartOfData);
208✔
1220
    poDS->SetBand(1, band);
208✔
1221
    if (band->panLineOffset == nullptr)
208✔
1222
    {
1223
        delete poDS;
×
1224
        return nullptr;
×
1225
    }
1226
    if (!poDS->osUnits.empty())
208✔
1227
    {
1228
        poDS->GetRasterBand(1)->SetUnitType(poDS->osUnits);
7✔
1229
    }
1230

1231
    // Try to read projection file.
1232
    char *const pszDirname =
1233
        CPLStrdup(CPLGetPathSafe(poOpenInfo->pszFilename).c_str());
208✔
1234
    char *const pszBasename =
1235
        CPLStrdup(CPLGetBasenameSafe(poOpenInfo->pszFilename).c_str());
208✔
1236

1237
    poDS->osPrjFilename = CPLFormFilenameSafe(pszDirname, pszBasename, "prj");
208✔
1238
    int nRet = 0;
208✔
1239
    {
1240
        VSIStatBufL sStatBuf;
1241
        nRet = VSIStatL(poDS->osPrjFilename, &sStatBuf);
208✔
1242
    }
1243
    if (nRet != 0 && VSIIsCaseSensitiveFS(poDS->osPrjFilename))
208✔
1244
    {
1245
        poDS->osPrjFilename =
1246
            CPLFormFilenameSafe(pszDirname, pszBasename, "PRJ");
160✔
1247

1248
        VSIStatBufL sStatBuf;
1249
        nRet = VSIStatL(poDS->osPrjFilename, &sStatBuf);
160✔
1250
    }
1251

1252
    if (nRet == 0)
208✔
1253
    {
1254
        poDS->papszPrj = CSLLoad(poDS->osPrjFilename);
50✔
1255

1256
        CPLDebug("AAIGrid", "Loaded SRS from %s", poDS->osPrjFilename.c_str());
50✔
1257

1258
        OGRSpatialReference oSRS;
100✔
1259
        oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
50✔
1260
        if (oSRS.importFromESRI(poDS->papszPrj) == OGRERR_NONE)
50✔
1261
        {
1262
            // If geographic values are in seconds, we must transform.
1263
            // Is there a code for minutes too?
1264
            if (oSRS.IsGeographic() &&
61✔
1265
                EQUAL(OSR_GDS(poDS->papszPrj, "Units", ""), "DS"))
61✔
1266
            {
1267
                poDS->m_gt[0] /= 3600.0;
×
1268
                poDS->m_gt[1] /= 3600.0;
×
1269
                poDS->m_gt[2] /= 3600.0;
×
1270
                poDS->m_gt[3] /= 3600.0;
×
1271
                poDS->m_gt[4] /= 3600.0;
×
1272
                poDS->m_gt[5] /= 3600.0;
×
1273
            }
1274

1275
            poDS->m_oSRS = std::move(oSRS);
50✔
1276
        }
1277
    }
1278

1279
    CPLFree(pszDirname);
208✔
1280
    CPLFree(pszBasename);
208✔
1281

1282
    // Initialize any PAM information.
1283
    poDS->SetDescription(poOpenInfo->pszFilename);
208✔
1284
    poDS->TryLoadXML();
208✔
1285

1286
    // Check for external overviews.
1287
    poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename,
416✔
1288
                                poOpenInfo->GetSiblingFiles());
208✔
1289

1290
    return poDS;
208✔
1291
}
1292

1293
/************************************************************************/
1294
/*                          GetGeoTransform()                           */
1295
/************************************************************************/
1296

1297
CPLErr AAIGDataset::GetGeoTransform(GDALGeoTransform &gt) const
146✔
1298

1299
{
1300
    gt = m_gt;
146✔
1301
    return CE_None;
146✔
1302
}
1303

1304
/************************************************************************/
1305
/*                          GetSpatialRef()                             */
1306
/************************************************************************/
1307

1308
const OGRSpatialReference *AAIGDataset::GetSpatialRef() const
108✔
1309
{
1310
    return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
108✔
1311
}
1312

1313
/************************************************************************/
1314
/*                          CreateCopy()                                */
1315
/************************************************************************/
1316

1317
GDALDataset *AAIGDataset::CreateCopy(const char *pszFilename,
49✔
1318
                                     GDALDataset *poSrcDS, int /* bStrict */,
1319
                                     char **papszOptions,
1320
                                     GDALProgressFunc pfnProgress,
1321
                                     void *pProgressData)
1322
{
1323
    const int nBands = poSrcDS->GetRasterCount();
49✔
1324
    const int nXSize = poSrcDS->GetRasterXSize();
49✔
1325
    const int nYSize = poSrcDS->GetRasterYSize();
49✔
1326

1327
    // Some rudimentary checks.
1328
    if (nBands != 1)
49✔
1329
    {
1330
        ReportError(pszFilename, CE_Failure, CPLE_NotSupported,
5✔
1331
                    "AAIG driver doesn't support %d bands.  Must be 1 band.",
1332
                    nBands);
1333

1334
        return nullptr;
5✔
1335
    }
1336

1337
    if (!pfnProgress(0.0, nullptr, pProgressData))
44✔
1338
        return nullptr;
×
1339

1340
    // Create the dataset.
1341
    VSILFILE *fpImage = VSIFOpenL(pszFilename, "wt");
44✔
1342
    if (fpImage == nullptr)
44✔
1343
    {
1344
        ReportError(pszFilename, CE_Failure, CPLE_OpenFailed,
3✔
1345
                    "Unable to create file.");
1346
        return nullptr;
3✔
1347
    }
1348

1349
    // Write ASCII Grid file header.
1350
    GDALGeoTransform gt;
41✔
1351
    char szHeader[2000] = {};
41✔
1352
    const char *pszForceCellsize =
1353
        CSLFetchNameValue(papszOptions, "FORCE_CELLSIZE");
41✔
1354

1355
    poSrcDS->GetGeoTransform(gt);
41✔
1356

1357
    const double dfYLLCorner = gt[5] < 0 ? gt[3] + nYSize * gt[5] : gt[3];
41✔
1358
    if (std::abs(gt[1] + gt[5]) < 0.0000001 ||
43✔
1359
        std::abs(gt[1] - gt[5]) < 0.0000001 ||
43✔
1360
        (pszForceCellsize && CPLTestBool(pszForceCellsize)))
×
1361
    {
1362
        CPLsnprintf(szHeader, sizeof(szHeader),
40✔
1363
                    "ncols        %d\n"
1364
                    "nrows        %d\n"
1365
                    "xllcorner    %.12f\n"
1366
                    "yllcorner    %.12f\n"
1367
                    "cellsize     %.12f\n",
1368
                    nXSize, nYSize, gt[0], dfYLLCorner, gt[1]);
40✔
1369
    }
1370
    else
1371
    {
1372
        if (pszForceCellsize == nullptr)
1✔
1373
            ReportError(pszFilename, CE_Warning, CPLE_AppDefined,
1✔
1374
                        "Producing a Golden Surfer style file with DX and DY "
1375
                        "instead of CELLSIZE since the input pixels are "
1376
                        "non-square.  Use the FORCE_CELLSIZE=TRUE creation "
1377
                        "option to force use of DX for even though this will "
1378
                        "be distorted.  Most ASCII Grid readers (ArcGIS "
1379
                        "included) do not support the DX and DY parameters.");
1380
        CPLsnprintf(szHeader, sizeof(szHeader),
1✔
1381
                    "ncols        %d\n"
1382
                    "nrows        %d\n"
1383
                    "xllcorner    %.12f\n"
1384
                    "yllcorner    %.12f\n"
1385
                    "dx           %.12f\n"
1386
                    "dy           %.12f\n",
1387
                    nXSize, nYSize, gt[0], dfYLLCorner, gt[1], fabs(gt[5]));
1✔
1388
    }
1389

1390
    // Builds the format string used for printing float values.
1391
    char szFormatFloat[32] = {'\0'};
41✔
1392
    strcpy(szFormatFloat, "%.20g");
41✔
1393
    const char *pszDecimalPrecision =
1394
        CSLFetchNameValue(papszOptions, "DECIMAL_PRECISION");
41✔
1395
    const char *pszSignificantDigits =
1396
        CSLFetchNameValue(papszOptions, "SIGNIFICANT_DIGITS");
41✔
1397
    bool bIgnoreSigDigits = false;
41✔
1398
    if (pszDecimalPrecision && pszSignificantDigits)
41✔
1399
    {
1400
        ReportError(pszFilename, CE_Warning, CPLE_AppDefined,
×
1401
                    "Conflicting precision arguments, using DECIMAL_PRECISION");
1402
        bIgnoreSigDigits = true;
×
1403
    }
1404
    int nPrecision;
1405
    if (pszSignificantDigits && !bIgnoreSigDigits)
41✔
1406
    {
1407
        nPrecision = atoi(pszSignificantDigits);
2✔
1408
        if (nPrecision >= 0)
2✔
1409
            snprintf(szFormatFloat, sizeof(szFormatFloat), "%%.%dg",
2✔
1410
                     nPrecision);
1411
        CPLDebug("AAIGrid", "Setting precision format: %s", szFormatFloat);
2✔
1412
    }
1413
    else if (pszDecimalPrecision)
39✔
1414
    {
1415
        nPrecision = atoi(pszDecimalPrecision);
2✔
1416
        if (nPrecision >= 0)
2✔
1417
            snprintf(szFormatFloat, sizeof(szFormatFloat), "%%.%df",
2✔
1418
                     nPrecision);
1419
        CPLDebug("AAIGrid", "Setting precision format: %s", szFormatFloat);
2✔
1420
    }
1421

1422
    // Handle nodata (optionally).
1423
    GDALRasterBand *poBand = poSrcDS->GetRasterBand(1);
41✔
1424
    const bool bReadAsInt = poBand->GetRasterDataType() == GDT_Byte ||
41✔
1425
                            poBand->GetRasterDataType() == GDT_Int16 ||
24✔
1426
                            poBand->GetRasterDataType() == GDT_UInt16 ||
85✔
1427
                            poBand->GetRasterDataType() == GDT_Int32;
20✔
1428

1429
    // Write `nodata' value to header if it is exists in source dataset
1430
    int bSuccess = FALSE;
41✔
1431
    const double dfNoData = poBand->GetNoDataValue(&bSuccess);
41✔
1432
    if (bSuccess)
41✔
1433
    {
1434
        snprintf(szHeader + strlen(szHeader),
2✔
1435
                 sizeof(szHeader) - strlen(szHeader), "%s", "NODATA_value ");
2✔
1436
        if (bReadAsInt)
2✔
1437
            snprintf(szHeader + strlen(szHeader),
×
1438
                     sizeof(szHeader) - strlen(szHeader), "%d",
×
1439
                     static_cast<int>(dfNoData));
1440
        else
1441
            CPLsnprintf(szHeader + strlen(szHeader),
2✔
1442
                        sizeof(szHeader) - strlen(szHeader), szFormatFloat,
2✔
1443
                        dfNoData);
1444
        snprintf(szHeader + strlen(szHeader),
2✔
1445
                 sizeof(szHeader) - strlen(szHeader), "%s", "\n");
2✔
1446
    }
1447

1448
    if (VSIFWriteL(szHeader, strlen(szHeader), 1, fpImage) != 1)
41✔
1449
    {
1450
        CPL_IGNORE_RET_VAL(VSIFCloseL(fpImage));
3✔
1451
        return nullptr;
3✔
1452
    }
1453

1454
    // Loop over image, copying image data.
1455

1456
    // Write scanlines to output file
1457
    int *panScanline = bReadAsInt
1458
                           ? static_cast<int *>(CPLMalloc(sizeof(int) * nXSize))
38✔
1459
                           : nullptr;
38✔
1460

1461
    double *padfScanline =
1462
        bReadAsInt ? nullptr
38✔
1463
                   : static_cast<double *>(CPLMalloc(sizeof(double) * nXSize));
14✔
1464

1465
    CPLErr eErr = CE_None;
38✔
1466

1467
    bool bHasOutputDecimalDot = false;
38✔
1468
    for (int iLine = 0; eErr == CE_None && iLine < nYSize; iLine++)
548✔
1469
    {
1470
        CPLString osBuf;
1,020✔
1471
        const int iSrcLine = gt[5] < 0 ? iLine : nYSize - 1 - iLine;
510✔
1472
        eErr = poBand->RasterIO(GF_Read, 0, iSrcLine, nXSize, 1,
510✔
1473
                                bReadAsInt ? static_cast<void *>(panScanline)
1474
                                           : static_cast<void *>(padfScanline),
1475
                                nXSize, 1, bReadAsInt ? GDT_Int32 : GDT_Float64,
1476
                                0, 0, nullptr);
1477

1478
        if (bReadAsInt)
510✔
1479
        {
1480
            for (int iPixel = 0; iPixel < nXSize; iPixel++)
14,657✔
1481
            {
1482
                snprintf(szHeader, sizeof(szHeader), "%d", panScanline[iPixel]);
14,316✔
1483
                osBuf += szHeader;
14,316✔
1484
                osBuf += ' ';
14,316✔
1485
                if ((iPixel > 0 && (iPixel % 1024) == 0) ||
14,316✔
1486
                    iPixel == nXSize - 1)
14,316✔
1487
                {
1488
                    if (VSIFWriteL(osBuf, static_cast<int>(osBuf.size()), 1,
696✔
1489
                                   fpImage) != 1)
696✔
1490
                    {
1491
                        eErr = CE_Failure;
7✔
1492
                        ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
7✔
1493
                                    "Write failed, disk full?");
1494
                        break;
7✔
1495
                    }
1496
                    osBuf = "";
341✔
1497
                }
1498
            }
1499
        }
1500
        else
1501
        {
1502
            assert(padfScanline);
162✔
1503

1504
            for (int iPixel = 0; iPixel < nXSize; iPixel++)
2,514✔
1505
            {
1506
                CPLsnprintf(szHeader, sizeof(szHeader), szFormatFloat,
2,352✔
1507
                            padfScanline[iPixel]);
2,352✔
1508

1509
                // Make sure that as least one value has a decimal point (#6060)
1510
                if (!bHasOutputDecimalDot)
2,352✔
1511
                {
1512
                    if (strchr(szHeader, '.') || strchr(szHeader, 'e') ||
14✔
1513
                        strchr(szHeader, 'E'))
11✔
1514
                    {
1515
                        bHasOutputDecimalDot = true;
3✔
1516
                    }
1517
                    else if (!std::isinf(padfScanline[iPixel]) &&
22✔
1518
                             !std::isnan(padfScanline[iPixel]))
11✔
1519
                    {
1520
                        strcat(szHeader, ".0");
11✔
1521
                        bHasOutputDecimalDot = true;
11✔
1522
                    }
1523
                }
1524

1525
                osBuf += szHeader;
2,352✔
1526
                osBuf += ' ';
2,352✔
1527
                if ((iPixel > 0 && (iPixel % 1024) == 0) ||
2,352✔
1528
                    iPixel == nXSize - 1)
2,352✔
1529
                {
1530
                    if (VSIFWriteL(osBuf, static_cast<int>(osBuf.size()), 1,
324✔
1531
                                   fpImage) != 1)
324✔
1532
                    {
1533
                        eErr = CE_Failure;
×
1534
                        ReportError(pszFilename, CE_Failure, CPLE_AppDefined,
×
1535
                                    "Write failed, disk full?");
1536
                        break;
×
1537
                    }
1538
                    osBuf = "";
162✔
1539
                }
1540
            }
1541
        }
1542
        if (VSIFWriteL("\n", 1, 1, fpImage) != 1)
510✔
1543
            eErr = CE_Failure;
1✔
1544

1545
        if (eErr == CE_None &&
1,013✔
1546
            !pfnProgress((iLine + 1) / static_cast<double>(nYSize), nullptr,
503✔
1547
                         pProgressData))
1548
        {
1549
            eErr = CE_Failure;
×
1550
            ReportError(pszFilename, CE_Failure, CPLE_UserInterrupt,
×
1551
                        "User terminated CreateCopy()");
1552
        }
1553
    }
1554

1555
    CPLFree(panScanline);
38✔
1556
    CPLFree(padfScanline);
38✔
1557
    if (VSIFCloseL(fpImage) != 0)
38✔
1558
        eErr = CE_Failure;
×
1559

1560
    if (eErr != CE_None)
38✔
1561
        return nullptr;
7✔
1562

1563
    // Try to write projection file.
1564
    const char *pszOriginalProjection = poSrcDS->GetProjectionRef();
31✔
1565
    if (!EQUAL(pszOriginalProjection, ""))
31✔
1566
    {
1567
        char *pszDirname = CPLStrdup(CPLGetPathSafe(pszFilename).c_str());
21✔
1568
        char *pszBasename = CPLStrdup(CPLGetBasenameSafe(pszFilename).c_str());
21✔
1569
        char *pszPrjFilename = CPLStrdup(
21✔
1570
            CPLFormFilenameSafe(pszDirname, pszBasename, "prj").c_str());
42✔
1571
        VSILFILE *fp = VSIFOpenL(pszPrjFilename, "wt");
21✔
1572
        if (fp != nullptr)
21✔
1573
        {
1574
            OGRSpatialReference oSRS;
42✔
1575
            oSRS.importFromWkt(pszOriginalProjection);
21✔
1576
            oSRS.morphToESRI();
21✔
1577
            char *pszESRIProjection = nullptr;
21✔
1578
            oSRS.exportToWkt(&pszESRIProjection);
21✔
1579
            CPL_IGNORE_RET_VAL(VSIFWriteL(pszESRIProjection, 1,
21✔
1580
                                          strlen(pszESRIProjection), fp));
1581

1582
            CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
21✔
1583
            CPLFree(pszESRIProjection);
21✔
1584
        }
1585
        else
1586
        {
1587
            ReportError(pszFilename, CE_Failure, CPLE_FileIO,
×
1588
                        "Unable to create file %s.", pszPrjFilename);
1589
        }
1590
        CPLFree(pszDirname);
21✔
1591
        CPLFree(pszBasename);
21✔
1592
        CPLFree(pszPrjFilename);
21✔
1593
    }
1594

1595
    // Re-open dataset, and copy any auxiliary pam information.
1596

1597
    // If writing to stdout, we can't reopen it, so return
1598
    // a fake dataset to make the caller happy.
1599
    CPLPushErrorHandler(CPLQuietErrorHandler);
31✔
1600
    GDALPamDataset *poDS =
1601
        reinterpret_cast<GDALPamDataset *>(GDALOpen(pszFilename, GA_ReadOnly));
31✔
1602
    CPLPopErrorHandler();
31✔
1603
    if (poDS)
31✔
1604
    {
1605
        poDS->CloneInfo(poSrcDS, GCIF_PAM_DEFAULT);
31✔
1606
        return poDS;
31✔
1607
    }
1608

1609
    CPLErrorReset();
×
1610

1611
    AAIGDataset *poAAIG_DS = new AAIGDataset();
×
1612
    poAAIG_DS->nRasterXSize = nXSize;
×
1613
    poAAIG_DS->nRasterYSize = nYSize;
×
1614
    poAAIG_DS->nBands = 1;
×
1615
    poAAIG_DS->SetBand(1, new AAIGRasterBand(poAAIG_DS, 1));
×
1616
    return poAAIG_DS;
×
1617
}
1618

1619
/************************************************************************/
1620
/*                              OSR_GDS()                               */
1621
/************************************************************************/
1622

1623
static CPLString OSR_GDS(char **papszNV, const char *pszField,
11✔
1624
                         const char *pszDefaultValue)
1625

1626
{
1627
    if (papszNV == nullptr || papszNV[0] == nullptr)
11✔
1628
        return pszDefaultValue;
×
1629

1630
    int iLine = 0;  // Used after for.
11✔
1631
    for (; papszNV[iLine] != nullptr &&
22✔
1632
           !EQUALN(papszNV[iLine], pszField, strlen(pszField));
11✔
1633
         iLine++)
1634
    {
1635
    }
1636

1637
    if (papszNV[iLine] == nullptr)
11✔
1638
        return pszDefaultValue;
11✔
1639
    else
1640
    {
1641
        char **papszTokens = CSLTokenizeString(papszNV[iLine]);
×
1642

1643
        CPLString osResult;
×
1644
        if (CSLCount(papszTokens) > 1)
×
1645
            osResult = papszTokens[1];
×
1646
        else
1647
            osResult = pszDefaultValue;
×
1648

1649
        CSLDestroy(papszTokens);
×
1650
        return osResult;
×
1651
    }
1652
}
1653

1654
/************************************************************************/
1655
/*                        GDALRegister_AAIGrid()                        */
1656
/************************************************************************/
1657

1658
void GDALRegister_AAIGrid()
1,911✔
1659

1660
{
1661
    if (GDALGetDriverByName("AAIGrid") != nullptr)
1,911✔
1662
        return;
282✔
1663

1664
    GDALDriver *poDriver = new GDALDriver();
1,629✔
1665

1666
    poDriver->SetDescription("AAIGrid");
1,629✔
1667
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1,629✔
1668
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Arc/Info ASCII Grid");
1,629✔
1669
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
1,629✔
1670
                              "drivers/raster/aaigrid.html");
1,629✔
1671
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "asc");
1,629✔
1672
    poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
1,629✔
1673
                              "Byte UInt16 Int16 Int32 Float32");
1,629✔
1674

1675
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1,629✔
1676
    poDriver->SetMetadataItem(
1,629✔
1677
        GDAL_DMD_CREATIONOPTIONLIST,
1678
        "<CreationOptionList>\n"
1679
        "   <Option name='FORCE_CELLSIZE' type='boolean' description='Force "
1680
        "use of CELLSIZE, default is FALSE.'/>\n"
1681
        "   <Option name='DECIMAL_PRECISION' type='int' description='Number of "
1682
        "decimal when writing floating-point numbers(%f).'/>\n"
1683
        "   <Option name='SIGNIFICANT_DIGITS' type='int' description='Number "
1684
        "of significant digits when writing floating-point numbers(%g).'/>\n"
1685
        "</CreationOptionList>\n");
1,629✔
1686
    poDriver->SetMetadataItem(GDAL_DMD_OPENOPTIONLIST,
1,629✔
1687
                              "<OpenOptionList>\n"
1688
                              "   <Option name='DATATYPE' type='string-select' "
1689
                              "description='Data type to be used.'>\n"
1690
                              "       <Value>Int32</Value>\n"
1691
                              "       <Value>Float32</Value>\n"
1692
                              "       <Value>Float64</Value>\n"
1693
                              "   </Option>\n"
1694
                              "</OpenOptionList>\n");
1,629✔
1695

1696
    poDriver->pfnOpen = AAIGDataset::Open;
1,629✔
1697
    poDriver->pfnIdentify = AAIGDataset::Identify;
1,629✔
1698
    poDriver->pfnCreateCopy = AAIGDataset::CreateCopy;
1,629✔
1699

1700
    GetGDALDriverManager()->RegisterDriver(poDriver);
1,629✔
1701
}
1702

1703
/************************************************************************/
1704
/*                   GDALRegister_GRASSASCIIGrid()                      */
1705
/************************************************************************/
1706

1707
void GDALRegister_GRASSASCIIGrid()
1,911✔
1708

1709
{
1710
    if (GDALGetDriverByName("GRASSASCIIGrid") != nullptr)
1,911✔
1711
        return;
282✔
1712

1713
    GDALDriver *poDriver = new GDALDriver();
1,629✔
1714

1715
    poDriver->SetDescription("GRASSASCIIGrid");
1,629✔
1716
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1,629✔
1717
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "GRASS ASCII Grid");
1,629✔
1718
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
1,629✔
1719
                              "drivers/raster/grassasciigrid.html");
1,629✔
1720

1721
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1,629✔
1722

1723
    poDriver->pfnOpen = GRASSASCIIDataset::Open;
1,629✔
1724
    poDriver->pfnIdentify = GRASSASCIIDataset::Identify;
1,629✔
1725

1726
    GetGDALDriverManager()->RegisterDriver(poDriver);
1,629✔
1727
}
1728

1729
/************************************************************************/
1730
/*                       GDALRegister_ISG()                             */
1731
/************************************************************************/
1732

1733
void GDALRegister_ISG()
1,911✔
1734

1735
{
1736
    if (GDALGetDriverByName("ISG") != nullptr)
1,911✔
1737
        return;
282✔
1738

1739
    GDALDriver *poDriver = new GDALDriver();
1,629✔
1740

1741
    poDriver->SetDescription("ISG");
1,629✔
1742
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1,629✔
1743
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
1,629✔
1744
                              "International Service for the Geoid");
1,629✔
1745
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/isg.html");
1,629✔
1746
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "isg");
1,629✔
1747

1748
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1,629✔
1749

1750
    poDriver->pfnOpen = ISGDataset::Open;
1,629✔
1751
    poDriver->pfnIdentify = ISGDataset::Identify;
1,629✔
1752

1753
    GetGDALDriverManager()->RegisterDriver(poDriver);
1,629✔
1754
}
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