Coverage Report

Created: 2025-07-23 09:13

/src/gdal/frmts/raw/ace2dataset.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  ACE2 Driver
4
 * Purpose:  Implementation of ACE2 elevation format read support.
5
 *           http://tethys.eaprs.cse.dmu.ac.uk/ACE2/shared/documentation
6
 * Author:   Even Rouault, <even dot rouault at spatialys.com>
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 2011-2012, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "gdal_frmts.h"
15
#include "ogr_spatialref.h"
16
#include "rawdataset.h"
17
18
static const char *const apszCategorySource[] = {
19
    "Pure SRTM (above 60deg N pure GLOBE data, below 60S pure ACE [original] "
20
    "data)",
21
    "SRTM voids filled by interpolation and/or altimeter data",
22
    "SRTM data warped using the ERS-1 Geodetic Mission",
23
    "SRTM data warped using EnviSat & ERS-2 data",
24
    "Mean lake level data derived from Altimetry",
25
    "GLOBE/ACE data warped using combined altimetry (only above 60deg N)",
26
    "Pure altimetry data (derived from ERS-1 Geodetic Mission, ERS-2 and "
27
    "EnviSat data using Delaunay Triangulation",
28
    nullptr};
29
30
static const char *const apszCategoryQuality[] = {
31
    "Generic - use base datasets",
32
    "Accuracy of greater than +/- 16m",
33
    "Accuracy between +/- 16m - +/- 10m",
34
    "Accuracy between +/-10m - +/-5m",
35
    "Accuracy between +/-5m - +/-1m",
36
    "Accuracy between +/-1m",
37
    nullptr};
38
39
static const char *const apszCategoryConfidence[] = {
40
    "No confidence could be derived due to lack of data",
41
    "Heights generated by interpolation",
42
    "Low confidence",
43
    "Low confidence",
44
    "Low confidence",
45
    "Medium confidence",
46
    "Medium confidence",
47
    "Medium confidence",
48
    "Medium confidence",
49
    "Medium confidence",
50
    "Medium confidence",
51
    "Medium confidence",
52
    "Medium confidence",
53
    "High confidence",
54
    "High confidence",
55
    "High confidence",
56
    "High confidence",
57
    "Inland water confidence",
58
    "Inland water confidence",
59
    "Inland water confidence",
60
    "Inland water confidence",
61
    "Inland water confidence",
62
    nullptr};
63
64
/************************************************************************/
65
/* ==================================================================== */
66
/*                             ACE2Dataset                              */
67
/* ==================================================================== */
68
/************************************************************************/
69
70
class ACE2Dataset final : public GDALPamDataset
71
{
72
    friend class ACE2RasterBand;
73
74
    OGRSpatialReference m_oSRS{};
75
    GDALGeoTransform m_gt{};
76
77
  public:
78
    ACE2Dataset();
79
80
    const OGRSpatialReference *GetSpatialRef() const override
81
0
    {
82
0
        return &m_oSRS;
83
0
    }
84
85
    CPLErr GetGeoTransform(GDALGeoTransform &gt) const override;
86
87
    static GDALDataset *Open(GDALOpenInfo *);
88
    static int Identify(GDALOpenInfo *);
89
};
90
91
/************************************************************************/
92
/* ==================================================================== */
93
/*                            ACE2RasterBand                            */
94
/* ==================================================================== */
95
/************************************************************************/
96
97
class ACE2RasterBand final : public RawRasterBand
98
{
99
  public:
100
    ACE2RasterBand(VSILFILE *fpRaw, GDALDataType eDataType, int nXSize,
101
                   int nYSize);
102
103
    const char *GetUnitType() override;
104
    char **GetCategoryNames() override;
105
};
106
107
/************************************************************************/
108
/* ==================================================================== */
109
/*                             ACE2Dataset                              */
110
/* ==================================================================== */
111
/************************************************************************/
112
113
/************************************************************************/
114
/*                            ACE2Dataset()                             */
115
/************************************************************************/
116
117
ACE2Dataset::ACE2Dataset()
118
0
{
119
0
    m_oSRS.SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
120
0
    m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
121
0
}
122
123
/************************************************************************/
124
/*                          GetGeoTransform()                           */
125
/************************************************************************/
126
127
CPLErr ACE2Dataset::GetGeoTransform(GDALGeoTransform &gt) const
128
129
0
{
130
0
    gt = m_gt;
131
0
    return CE_None;
132
0
}
133
134
/************************************************************************/
135
/*                          ACE2RasterBand()                            */
136
/************************************************************************/
137
138
ACE2RasterBand::ACE2RasterBand(VSILFILE *fpRawIn, GDALDataType eDataTypeIn,
139
                               int nXSize, int nYSize)
140
0
    : RawRasterBand(fpRawIn, 0, GDALGetDataTypeSizeBytes(eDataTypeIn),
141
0
                    nXSize * GDALGetDataTypeSizeBytes(eDataTypeIn), eDataTypeIn,
142
0
                    CPL_IS_LSB, nXSize, nYSize, RawRasterBand::OwnFP::YES)
143
0
{
144
0
}
145
146
/************************************************************************/
147
/*                             GetUnitType()                            */
148
/************************************************************************/
149
150
const char *ACE2RasterBand::GetUnitType()
151
0
{
152
0
    if (eDataType == GDT_Float32)
153
0
        return "m";
154
155
0
    return "";
156
0
}
157
158
/************************************************************************/
159
/*                         GetCategoryNames()                           */
160
/************************************************************************/
161
162
char **ACE2RasterBand::GetCategoryNames()
163
0
{
164
0
    if (eDataType != GDT_Int16)
165
0
        return nullptr;
166
167
0
    const char *pszName = poDS->GetDescription();
168
169
0
    if (strstr(pszName, "_SOURCE_"))
170
0
        return const_cast<char **>(apszCategorySource);
171
0
    if (strstr(pszName, "_QUALITY_"))
172
0
        return const_cast<char **>(apszCategoryQuality);
173
0
    if (strstr(pszName, "_CONF_"))
174
0
        return const_cast<char **>(apszCategoryConfidence);
175
176
0
    return nullptr;
177
0
}
178
179
/************************************************************************/
180
/*                             Identify()                               */
181
/************************************************************************/
182
183
int ACE2Dataset::Identify(GDALOpenInfo *poOpenInfo)
184
185
561k
{
186
561k
    if (!(poOpenInfo->IsExtensionEqualToCI("ACE2") ||
187
561k
          strstr(poOpenInfo->pszFilename, ".ACE2.gz") ||
188
561k
          strstr(poOpenInfo->pszFilename, ".ace2.gz")))
189
560k
        return FALSE;
190
191
941
    return TRUE;
192
561k
}
193
194
/************************************************************************/
195
/*                                Open()                                */
196
/************************************************************************/
197
198
GDALDataset *ACE2Dataset::Open(GDALOpenInfo *poOpenInfo)
199
200
470
{
201
470
    if (!Identify(poOpenInfo))
202
0
        return nullptr;
203
204
470
    const std::string osBasename = CPLGetBasenameSafe(poOpenInfo->pszFilename);
205
470
    const char *pszBasename = osBasename.c_str();
206
207
470
    if (strlen(pszBasename) < 7)
208
39
        return nullptr;
209
210
    /* Determine southwest coordinates from filename */
211
212
    /* e.g. 30S120W_5M.ACE2 */
213
431
    char pszLatLonValueString[4] = {'\0'};
214
431
    memset(pszLatLonValueString, 0, 4);
215
    // cppcheck-suppress redundantCopy
216
431
    strncpy(pszLatLonValueString, &pszBasename[0], 2);
217
431
    int southWestLat = atoi(pszLatLonValueString);
218
431
    memset(pszLatLonValueString, 0, 4);
219
    // cppcheck-suppress redundantCopy
220
431
    strncpy(pszLatLonValueString, &pszBasename[3], 3);
221
431
    int southWestLon = atoi(pszLatLonValueString);
222
223
431
    if (pszBasename[2] == 'N' || pszBasename[2] == 'n')
224
38
        /*southWestLat = southWestLat*/;
225
393
    else if (pszBasename[2] == 'S' || pszBasename[2] == 's')
226
24
        southWestLat = southWestLat * -1;
227
369
    else
228
369
        return nullptr;
229
230
62
    if (pszBasename[6] == 'E' || pszBasename[6] == 'e')
231
12
        /*southWestLon = southWestLon*/;
232
50
    else if (pszBasename[6] == 'W' || pszBasename[6] == 'w')
233
2
        southWestLon = southWestLon * -1;
234
48
    else
235
48
        return nullptr;
236
237
14
    GDALDataType eDT = GDT_Unknown;
238
14
    if (strstr(pszBasename, "_CONF_") || strstr(pszBasename, "_QUALITY_") ||
239
14
        strstr(pszBasename, "_SOURCE_"))
240
0
        eDT = GDT_Int16;
241
14
    else
242
14
        eDT = GDT_Float32;
243
14
    const int nWordSize = GDALGetDataTypeSizeBytes(eDT);
244
245
14
    VSIStatBufL sStat;
246
14
    if (strstr(pszBasename, "_5M"))
247
0
        sStat.st_size = 180 * 180 * nWordSize;
248
14
    else if (strstr(pszBasename, "_30S"))
249
0
        sStat.st_size = 1800 * 1800 * nWordSize;
250
14
    else if (strstr(pszBasename, "_9S"))
251
0
        sStat.st_size = 6000 * 6000 * nWordSize;
252
14
    else if (strstr(pszBasename, "_3S"))
253
0
        sStat.st_size = 18000 * 18000 * nWordSize;
254
    /* Check file size otherwise */
255
14
    else if (VSIStatL(poOpenInfo->pszFilename, &sStat) != 0)
256
11
    {
257
11
        return nullptr;
258
11
    }
259
260
3
    int nXSize = 0;
261
3
    int nYSize = 0;
262
263
3
    double dfPixelSize = 0;
264
3
    if (sStat.st_size == 180 * 180 * nWordSize)
265
0
    {
266
        /* 5 minute */
267
0
        nXSize = 180;
268
0
        nYSize = 180;
269
0
        dfPixelSize = 5.0 / 60;
270
0
    }
271
3
    else if (sStat.st_size == 1800 * 1800 * nWordSize)
272
0
    {
273
        /* 30 s */
274
0
        nXSize = 1800;
275
0
        nYSize = 1800;
276
0
        dfPixelSize = 30.0 / 3600;
277
0
    }
278
3
    else if (sStat.st_size == 6000 * 6000 * nWordSize)
279
0
    {
280
        /* 9 s */
281
0
        nXSize = 6000;
282
0
        nYSize = 6000;
283
0
        dfPixelSize = 9.0 / 3600;
284
0
    }
285
3
    else if (sStat.st_size == 18000 * 18000 * nWordSize)
286
0
    {
287
        /* 3 s */
288
0
        nXSize = 18000;
289
0
        nYSize = 18000;
290
0
        dfPixelSize = 3.0 / 3600;
291
0
    }
292
3
    else
293
3
        return nullptr;
294
295
    /* -------------------------------------------------------------------- */
296
    /*      Open file.                                                      */
297
    /* -------------------------------------------------------------------- */
298
299
0
    CPLString osFilename = poOpenInfo->pszFilename;
300
0
    if ((strstr(poOpenInfo->pszFilename, ".ACE2.gz") ||
301
0
         strstr(poOpenInfo->pszFilename, ".ace2.gz")) &&
302
0
        !STARTS_WITH(poOpenInfo->pszFilename, "/vsigzip/"))
303
0
        osFilename = "/vsigzip/" + osFilename;
304
305
0
    VSILFILE *fpImage = VSIFOpenL(osFilename, "rb+");
306
0
    if (fpImage == nullptr)
307
0
        return nullptr;
308
309
    /* -------------------------------------------------------------------- */
310
    /*      Create the dataset.                                             */
311
    /* -------------------------------------------------------------------- */
312
0
    auto poDS = std::make_unique<ACE2Dataset>();
313
314
0
    poDS->nRasterXSize = nXSize;
315
0
    poDS->nRasterYSize = nYSize;
316
317
0
    poDS->m_gt[0] = southWestLon;
318
0
    poDS->m_gt[1] = dfPixelSize;
319
0
    poDS->m_gt[2] = 0.0;
320
0
    poDS->m_gt[3] = southWestLat + nYSize * dfPixelSize;
321
0
    poDS->m_gt[4] = 0.0;
322
0
    poDS->m_gt[5] = -dfPixelSize;
323
324
    /* -------------------------------------------------------------------- */
325
    /*      Create band information objects                                 */
326
    /* -------------------------------------------------------------------- */
327
0
    auto poBand =
328
0
        std::make_unique<ACE2RasterBand>(fpImage, eDT, nXSize, nYSize);
329
0
    if (!poBand->IsValid())
330
0
        return nullptr;
331
0
    poDS->SetBand(1, std::move(poBand));
332
333
    /* -------------------------------------------------------------------- */
334
    /*      Initialize any PAM information.                                 */
335
    /* -------------------------------------------------------------------- */
336
0
    poDS->SetDescription(poOpenInfo->pszFilename);
337
0
    poDS->TryLoadXML();
338
339
    /* -------------------------------------------------------------------- */
340
    /*      Check for overviews.                                            */
341
    /* -------------------------------------------------------------------- */
342
0
    poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
343
344
0
    return poDS.release();
345
0
}
346
347
/************************************************************************/
348
/*                          GDALRegister_ACE2()                         */
349
/************************************************************************/
350
351
void GDALRegister_ACE2()
352
353
22
{
354
22
    if (GDALGetDriverByName("ACE2") != nullptr)
355
0
        return;
356
357
22
    GDALDriver *poDriver = new GDALDriver();
358
359
22
    poDriver->SetDescription("ACE2");
360
22
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
361
22
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "ACE2");
362
22
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/ace2.html");
363
22
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "ACE2");
364
22
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
365
366
22
    poDriver->pfnOpen = ACE2Dataset::Open;
367
22
    poDriver->pfnIdentify = ACE2Dataset::Identify;
368
369
22
    GetGDALDriverManager()->RegisterDriver(poDriver);
370
22
}