Coverage Report

Created: 2025-11-15 08:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/gxf/gxfdataset.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GXF Reader
4
 * Purpose:  GDAL binding for GXF reader.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 1998, Frank Warmerdam
9
 * Copyright (c) 2008-2012, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "gdal_frmts.h"
15
#include "gdal_pam.h"
16
#include "gdal_driver.h"
17
#include "gdal_drivermanager.h"
18
#include "gdal_openinfo.h"
19
#include "gdal_cpp_functions.h"
20
#include "gxfopen.h"
21
22
/************************************************************************/
23
/* ==================================================================== */
24
/*                              GXFDataset                              */
25
/* ==================================================================== */
26
/************************************************************************/
27
28
class GXFRasterBand;
29
30
class GXFDataset final : public GDALPamDataset
31
{
32
    friend class GXFRasterBand;
33
34
    GXFHandle hGXF;
35
36
    OGRSpatialReference m_oSRS{};
37
    double dfNoDataValue;
38
    GDALDataType eDataType;
39
40
  public:
41
    GXFDataset();
42
    ~GXFDataset() override;
43
44
    static GDALDataset *Open(GDALOpenInfo *);
45
46
    CPLErr GetGeoTransform(GDALGeoTransform &gt) const override;
47
    const OGRSpatialReference *GetSpatialRef() const override;
48
};
49
50
/************************************************************************/
51
/* ==================================================================== */
52
/*                            GXFRasterBand                             */
53
/* ==================================================================== */
54
/************************************************************************/
55
56
class GXFRasterBand final : public GDALPamRasterBand
57
{
58
    friend class GXFDataset;
59
60
  public:
61
    GXFRasterBand(GXFDataset *, int);
62
    double GetNoDataValue(int *bGotNoDataValue) override;
63
64
    CPLErr IReadBlock(int, int, void *) override;
65
};
66
67
/************************************************************************/
68
/*                           GXFRasterBand()                            */
69
/************************************************************************/
70
71
GXFRasterBand::GXFRasterBand(GXFDataset *poDSIn, int nBandIn)
72
73
14
{
74
14
    poDS = poDSIn;
75
14
    nBand = nBandIn;
76
77
14
    eDataType = poDSIn->eDataType;
78
79
14
    nBlockXSize = poDS->GetRasterXSize();
80
14
    nBlockYSize = 1;
81
14
}
82
83
/************************************************************************/
84
/*                          GetNoDataValue()                          */
85
/************************************************************************/
86
87
double GXFRasterBand::GetNoDataValue(int *bGotNoDataValue)
88
89
42
{
90
42
    GXFDataset *poGXF_DS = cpl::down_cast<GXFDataset *>(poDS);
91
42
    if (bGotNoDataValue)
92
42
        *bGotNoDataValue = (fabs(poGXF_DS->dfNoDataValue - -1e12) > .1);
93
42
    if (eDataType == GDT_Float32)
94
42
        return (double)(float)poGXF_DS->dfNoDataValue;
95
96
0
    return poGXF_DS->dfNoDataValue;
97
42
}
98
99
/************************************************************************/
100
/*                             IReadBlock()                             */
101
/************************************************************************/
102
103
CPLErr GXFRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
104
                                 void *pImage)
105
3.36k
{
106
3.36k
    GXFDataset *const poGXF_DS = cpl::down_cast<GXFDataset *>(poDS);
107
108
3.36k
    if (eDataType == GDT_Float32)
109
3.36k
    {
110
3.36k
        double *padfBuffer = (double *)VSIMalloc2(sizeof(double), nBlockXSize);
111
3.36k
        if (padfBuffer == nullptr)
112
0
            return CE_Failure;
113
3.36k
        const CPLErr eErr =
114
3.36k
            GXFGetScanline(poGXF_DS->hGXF, nBlockYOff, padfBuffer);
115
116
3.36k
        float *pafBuffer = (float *)pImage;
117
25.9k
        for (int i = 0; i < nBlockXSize; i++)
118
22.6k
            pafBuffer[i] = (float)padfBuffer[i];
119
120
3.36k
        CPLFree(padfBuffer);
121
122
3.36k
        return eErr;
123
3.36k
    }
124
125
0
    const CPLErr eErr =
126
0
        eDataType == GDT_Float64
127
0
            ? GXFGetScanline(poGXF_DS->hGXF, nBlockYOff, (double *)pImage)
128
0
            : CE_Failure;
129
130
0
    return eErr;
131
3.36k
}
132
133
/************************************************************************/
134
/* ==================================================================== */
135
/*                              GXFDataset                              */
136
/* ==================================================================== */
137
/************************************************************************/
138
139
/************************************************************************/
140
/*                             GXFDataset()                             */
141
/************************************************************************/
142
143
GXFDataset::GXFDataset()
144
20
    : hGXF(nullptr), dfNoDataValue(0), eDataType(GDT_Float32)
145
20
{
146
20
    m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
147
20
}
148
149
/************************************************************************/
150
/*                            ~GXFDataset()                             */
151
/************************************************************************/
152
153
GXFDataset::~GXFDataset()
154
155
20
{
156
20
    FlushCache(true);
157
20
    if (hGXF != nullptr)
158
20
        GXFClose(hGXF);
159
20
}
160
161
/************************************************************************/
162
/*                          GetGeoTransform()                           */
163
/************************************************************************/
164
165
CPLErr GXFDataset::GetGeoTransform(GDALGeoTransform &gt) const
166
167
14
{
168
14
    double dfXOrigin = 0.0;
169
14
    double dfYOrigin = 0.0;
170
14
    double dfXSize = 0.0;
171
14
    double dfYSize = 0.0;
172
14
    double dfRotation = 0.0;
173
174
14
    const CPLErr eErr = GXFGetPosition(hGXF, &dfXOrigin, &dfYOrigin, &dfXSize,
175
14
                                       &dfYSize, &dfRotation);
176
177
14
    if (eErr != CE_None)
178
0
        return eErr;
179
180
    // Transform to radians.
181
14
    dfRotation = (dfRotation / 360.0) * 2.0 * M_PI;
182
183
14
    gt[1] = dfXSize * cos(dfRotation);
184
14
    gt[2] = dfYSize * sin(dfRotation);
185
14
    gt[4] = dfXSize * sin(dfRotation);
186
14
    gt[5] = -1 * dfYSize * cos(dfRotation);
187
188
    // take into account that GXF is point or center of pixel oriented.
189
14
    gt[0] = dfXOrigin - 0.5 * gt[1] - 0.5 * gt[2];
190
14
    gt[3] = dfYOrigin - 0.5 * gt[4] - 0.5 * gt[5];
191
192
14
    return CE_None;
193
14
}
194
195
/************************************************************************/
196
/*                         GetSpatialRef()                              */
197
/************************************************************************/
198
199
const OGRSpatialReference *GXFDataset::GetSpatialRef() const
200
14
{
201
14
    return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
202
14
}
203
204
/************************************************************************/
205
/*                                Open()                                */
206
/************************************************************************/
207
208
GDALDataset *GXFDataset::Open(GDALOpenInfo *poOpenInfo)
209
210
603k
{
211
    /* -------------------------------------------------------------------- */
212
    /*      Before trying GXFOpen() we first verify that there is at        */
213
    /*      least one "\n#keyword" type signature in the first chunk of     */
214
    /*      the file.                                                       */
215
    /* -------------------------------------------------------------------- */
216
603k
    if (poOpenInfo->nHeaderBytes < 50 || poOpenInfo->fpL == nullptr)
217
548k
        return nullptr;
218
219
54.2k
    bool bFoundKeyword = false;
220
54.2k
    bool bFoundIllegal = false;
221
29.7M
    for (int i = 0; i < poOpenInfo->nHeaderBytes - 1; i++)
222
29.7M
    {
223
29.7M
        if ((poOpenInfo->pabyHeader[i] == 10 ||
224
29.0M
             poOpenInfo->pabyHeader[i] == 13) &&
225
903k
            poOpenInfo->pabyHeader[i + 1] == '#')
226
28.9k
        {
227
28.9k
            if (STARTS_WITH((const char *)poOpenInfo->pabyHeader + i + 2,
228
28.9k
                            "include"))
229
1
                return nullptr;
230
28.9k
            if (STARTS_WITH((const char *)poOpenInfo->pabyHeader + i + 2,
231
28.9k
                            "define"))
232
0
                return nullptr;
233
28.9k
            if (STARTS_WITH((const char *)poOpenInfo->pabyHeader + i + 2,
234
28.9k
                            "ifdef"))
235
1
                return nullptr;
236
28.9k
            bFoundKeyword = true;
237
28.9k
        }
238
29.7M
        if (poOpenInfo->pabyHeader[i] == 0)
239
40.1k
        {
240
40.1k
            bFoundIllegal = true;
241
40.1k
            break;
242
40.1k
        }
243
29.7M
    }
244
245
54.2k
    if (!bFoundKeyword || bFoundIllegal)
246
48.0k
        return nullptr;
247
248
    /* -------------------------------------------------------------------- */
249
    /*      At this point it is plausible that this is a GXF file, but      */
250
    /*      we also now verify that there is a #GRID keyword before         */
251
    /*      passing it off to GXFOpen().  We check in the first 50K.        */
252
    /* -------------------------------------------------------------------- */
253
6.17k
    CPL_IGNORE_RET_VAL(poOpenInfo->TryToIngest(50000));
254
6.17k
    bool bGotGrid = false;
255
256
6.17k
    const char *pszBigBuf = (const char *)poOpenInfo->pabyHeader;
257
113M
    for (int i = 0; i < poOpenInfo->nHeaderBytes - 5 && !bGotGrid; i++)
258
113M
    {
259
113M
        if (pszBigBuf[i] == '#' && STARTS_WITH_CI(pszBigBuf + i + 1, "GRID"))
260
1.69k
            bGotGrid = true;
261
113M
    }
262
263
6.17k
    if (!bGotGrid)
264
4.47k
        return nullptr;
265
266
1.69k
    VSIFCloseL(poOpenInfo->fpL);
267
1.69k
    poOpenInfo->fpL = nullptr;
268
269
    /* -------------------------------------------------------------------- */
270
    /*      Try opening the dataset.                                        */
271
    /* -------------------------------------------------------------------- */
272
273
1.69k
    GXFHandle l_hGXF = GXFOpen(poOpenInfo->pszFilename);
274
275
1.69k
    if (l_hGXF == nullptr)
276
1.67k
        return nullptr;
277
278
    /* -------------------------------------------------------------------- */
279
    /*      Confirm the requested access is supported.                      */
280
    /* -------------------------------------------------------------------- */
281
20
    if (poOpenInfo->eAccess == GA_Update)
282
0
    {
283
0
        GXFClose(l_hGXF);
284
0
        ReportUpdateNotSupportedByDriver("GXF");
285
0
        return nullptr;
286
0
    }
287
288
    /* -------------------------------------------------------------------- */
289
    /*      Create a corresponding GDALDataset.                             */
290
    /* -------------------------------------------------------------------- */
291
20
    GXFDataset *poDS = new GXFDataset();
292
293
20
    const char *pszGXFDataType = CPLGetConfigOption("GXF_DATATYPE", "Float32");
294
20
    GDALDataType eDT = GDALGetDataTypeByName(pszGXFDataType);
295
20
    if (!(eDT == GDT_Float32 || eDT == GDT_Float64))
296
0
    {
297
0
        CPLError(CE_Warning, CPLE_NotSupported,
298
0
                 "Unsupported value for GXF_DATATYPE : %s", pszGXFDataType);
299
0
        eDT = GDT_Float32;
300
0
    }
301
302
20
    poDS->hGXF = l_hGXF;
303
20
    poDS->eDataType = eDT;
304
305
    /* -------------------------------------------------------------------- */
306
    /*      Establish the projection.                                       */
307
    /* -------------------------------------------------------------------- */
308
20
    char *pszProjection = GXFGetMapProjectionAsOGCWKT(l_hGXF);
309
20
    if (pszProjection && pszProjection[0] != '\0')
310
5
        poDS->m_oSRS.importFromWkt(pszProjection);
311
20
    CPLFree(pszProjection);
312
313
    /* -------------------------------------------------------------------- */
314
    /*      Capture some information from the file that is of interest.     */
315
    /* -------------------------------------------------------------------- */
316
20
    GXFGetRawInfo(l_hGXF, &(poDS->nRasterXSize), &(poDS->nRasterYSize), nullptr,
317
20
                  nullptr, nullptr, &(poDS->dfNoDataValue));
318
319
20
    if (poDS->nRasterXSize <= 0 || poDS->nRasterYSize <= 0)
320
6
    {
321
6
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid dimensions : %d x %d",
322
6
                 poDS->nRasterXSize, poDS->nRasterYSize);
323
6
        delete poDS;
324
6
        return nullptr;
325
6
    }
326
327
    /* -------------------------------------------------------------------- */
328
    /*      Create band information objects.                                */
329
    /* -------------------------------------------------------------------- */
330
14
    poDS->nBands = 1;
331
14
    poDS->SetBand(1, new GXFRasterBand(poDS, 1));
332
333
    /* -------------------------------------------------------------------- */
334
    /*      Initialize any PAM information.                                 */
335
    /* -------------------------------------------------------------------- */
336
14
    poDS->SetDescription(poOpenInfo->pszFilename);
337
14
    poDS->TryLoadXML();
338
339
    /* -------------------------------------------------------------------- */
340
    /*      Check for external overviews.                                   */
341
    /* -------------------------------------------------------------------- */
342
14
    poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename,
343
14
                                poOpenInfo->GetSiblingFiles());
344
345
14
    return poDS;
346
20
}
347
348
/************************************************************************/
349
/*                          GDALRegister_GXF()                          */
350
/************************************************************************/
351
352
void GDALRegister_GXF()
353
354
22
{
355
22
    if (GDALGetDriverByName("GXF") != nullptr)
356
0
        return;
357
358
22
    GDALDriver *poDriver = new GDALDriver();
359
360
22
    poDriver->SetDescription("GXF");
361
22
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
362
22
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
363
22
                              "GeoSoft Grid Exchange Format");
364
22
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/gxf.html");
365
22
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "gxf");
366
22
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
367
368
22
    poDriver->pfnOpen = GXFDataset::Open;
369
370
22
    GetGDALDriverManager()->RegisterDriver(poDriver);
371
22
}