Coverage Report

Created: 2026-06-30 08:33

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
7
{
74
7
    poDS = poDSIn;
75
7
    nBand = nBandIn;
76
77
7
    eDataType = poDSIn->eDataType;
78
79
7
    nBlockXSize = poDS->GetRasterXSize();
80
7
    nBlockYSize = 1;
81
7
}
82
83
/************************************************************************/
84
/*                           GetNoDataValue()                           */
85
/************************************************************************/
86
87
double GXFRasterBand::GetNoDataValue(int *bGotNoDataValue)
88
89
21
{
90
21
    GXFDataset *poGXF_DS = cpl::down_cast<GXFDataset *>(poDS);
91
21
    if (bGotNoDataValue)
92
21
        *bGotNoDataValue = (fabs(poGXF_DS->dfNoDataValue - -1e12) > .1);
93
21
    if (eDataType == GDT_Float32)
94
21
        return (double)(float)poGXF_DS->dfNoDataValue;
95
96
0
    return poGXF_DS->dfNoDataValue;
97
21
}
98
99
/************************************************************************/
100
/*                             IReadBlock()                             */
101
/************************************************************************/
102
103
CPLErr GXFRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
104
                                 void *pImage)
105
7
{
106
7
    GXFDataset *const poGXF_DS = cpl::down_cast<GXFDataset *>(poDS);
107
108
7
    if (eDataType == GDT_Float32)
109
7
    {
110
7
        double *padfBuffer = (double *)VSIMalloc2(sizeof(double), nBlockXSize);
111
7
        if (padfBuffer == nullptr)
112
0
            return CE_Failure;
113
7
        const CPLErr eErr =
114
7
            GXFGetScanline(poGXF_DS->hGXF, nBlockYOff, padfBuffer);
115
116
7
        float *pafBuffer = (float *)pImage;
117
77
        for (int i = 0; i < nBlockXSize; i++)
118
70
            pafBuffer[i] = (float)padfBuffer[i];
119
120
7
        CPLFree(padfBuffer);
121
122
7
        return eErr;
123
7
    }
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
7
}
132
133
/************************************************************************/
134
/* ==================================================================== */
135
/*                              GXFDataset                              */
136
/* ==================================================================== */
137
/************************************************************************/
138
139
/************************************************************************/
140
/*                             GXFDataset()                             */
141
/************************************************************************/
142
143
GXFDataset::GXFDataset()
144
12
    : hGXF(nullptr), dfNoDataValue(0), eDataType(GDT_Float32)
145
12
{
146
12
    m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
147
12
}
148
149
/************************************************************************/
150
/*                            ~GXFDataset()                             */
151
/************************************************************************/
152
153
GXFDataset::~GXFDataset()
154
155
12
{
156
12
    FlushCache(true);
157
12
    if (hGXF != nullptr)
158
12
        GXFClose(hGXF);
159
12
}
160
161
/************************************************************************/
162
/*                          GetGeoTransform()                           */
163
/************************************************************************/
164
165
CPLErr GXFDataset::GetGeoTransform(GDALGeoTransform &gt) const
166
167
7
{
168
7
    double dfXOrigin = 0.0;
169
7
    double dfYOrigin = 0.0;
170
7
    double dfXSize = 0.0;
171
7
    double dfYSize = 0.0;
172
7
    double dfRotation = 0.0;
173
174
7
    const CPLErr eErr = GXFGetPosition(hGXF, &dfXOrigin, &dfYOrigin, &dfXSize,
175
7
                                       &dfYSize, &dfRotation);
176
177
7
    if (eErr != CE_None)
178
0
        return eErr;
179
180
    // Transform to radians.
181
7
    dfRotation = (dfRotation / 360.0) * 2.0 * M_PI;
182
183
7
    gt.xscale = dfXSize * cos(dfRotation);
184
7
    gt.xrot = dfYSize * sin(dfRotation);
185
7
    gt.yrot = dfXSize * sin(dfRotation);
186
7
    gt.yscale = -1 * dfYSize * cos(dfRotation);
187
188
    // take into account that GXF is point or center of pixel oriented.
189
7
    gt.xorig = dfXOrigin - 0.5 * gt.xscale - 0.5 * gt.xrot;
190
7
    gt.yorig = dfYOrigin - 0.5 * gt.yrot - 0.5 * gt.yscale;
191
192
7
    return CE_None;
193
7
}
194
195
/************************************************************************/
196
/*                           GetSpatialRef()                            */
197
/************************************************************************/
198
199
const OGRSpatialReference *GXFDataset::GetSpatialRef() const
200
7
{
201
7
    return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
202
7
}
203
204
/************************************************************************/
205
/*                                Open()                                */
206
/************************************************************************/
207
208
GDALDataset *GXFDataset::Open(GDALOpenInfo *poOpenInfo)
209
210
450k
{
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
450k
    if (poOpenInfo->nHeaderBytes < 50 || poOpenInfo->fpL == nullptr)
217
379k
        return nullptr;
218
219
71.1k
    bool bFoundKeyword = false;
220
71.1k
    bool bFoundIllegal = false;
221
31.9M
    for (int i = 0; i < poOpenInfo->nHeaderBytes - 1; i++)
222
31.9M
    {
223
31.9M
        if ((poOpenInfo->pabyHeader[i] == 10 ||
224
31.2M
             poOpenInfo->pabyHeader[i] == 13) &&
225
974k
            poOpenInfo->pabyHeader[i + 1] == '#')
226
39.8k
        {
227
39.8k
            if (STARTS_WITH((const char *)poOpenInfo->pabyHeader + i + 2,
228
39.8k
                            "include"))
229
1
                return nullptr;
230
39.8k
            if (STARTS_WITH((const char *)poOpenInfo->pabyHeader + i + 2,
231
39.8k
                            "define"))
232
0
                return nullptr;
233
39.8k
            if (STARTS_WITH((const char *)poOpenInfo->pabyHeader + i + 2,
234
39.8k
                            "ifdef"))
235
0
                return nullptr;
236
39.8k
            bFoundKeyword = true;
237
39.8k
        }
238
31.9M
        if (poOpenInfo->pabyHeader[i] == 0)
239
54.7k
        {
240
54.7k
            bFoundIllegal = true;
241
54.7k
            break;
242
54.7k
        }
243
31.9M
    }
244
245
71.1k
    if (!bFoundKeyword || bFoundIllegal)
246
64.6k
        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.58k
    CPL_IGNORE_RET_VAL(poOpenInfo->TryToIngest(50000));
254
6.58k
    bool bGotGrid = false;
255
256
6.58k
    const char *pszBigBuf = (const char *)poOpenInfo->pabyHeader;
257
87.5M
    for (int i = 0; i < poOpenInfo->nHeaderBytes - 5 && !bGotGrid; i++)
258
87.5M
    {
259
87.5M
        if (pszBigBuf[i] == '#' && STARTS_WITH_CI(pszBigBuf + i + 1, "GRID"))
260
2.00k
            bGotGrid = true;
261
87.5M
    }
262
263
6.58k
    if (!bGotGrid)
264
4.58k
        return nullptr;
265
266
2.00k
    VSIFCloseL(poOpenInfo->fpL);
267
2.00k
    poOpenInfo->fpL = nullptr;
268
269
    /* -------------------------------------------------------------------- */
270
    /*      Try opening the dataset.                                        */
271
    /* -------------------------------------------------------------------- */
272
273
2.00k
    GXFHandle l_hGXF = GXFOpen(poOpenInfo->pszFilename);
274
275
2.00k
    if (l_hGXF == nullptr)
276
1.99k
        return nullptr;
277
278
    /* -------------------------------------------------------------------- */
279
    /*      Confirm the requested access is supported.                      */
280
    /* -------------------------------------------------------------------- */
281
12
    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
12
    GXFDataset *poDS = new GXFDataset();
292
293
12
    const char *pszGXFDataType = CPLGetConfigOption("GXF_DATATYPE", "Float32");
294
12
    GDALDataType eDT = GDALGetDataTypeByName(pszGXFDataType);
295
12
    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
12
    poDS->hGXF = l_hGXF;
303
12
    poDS->eDataType = eDT;
304
305
    /* -------------------------------------------------------------------- */
306
    /*      Establish the projection.                                       */
307
    /* -------------------------------------------------------------------- */
308
12
    char *pszProjection = GXFGetMapProjectionAsOGCWKT(l_hGXF);
309
12
    if (pszProjection && pszProjection[0] != '\0')
310
4
        poDS->m_oSRS.importFromWkt(pszProjection);
311
12
    CPLFree(pszProjection);
312
313
    /* -------------------------------------------------------------------- */
314
    /*      Capture some information from the file that is of interest.     */
315
    /* -------------------------------------------------------------------- */
316
12
    GXFGetRawInfo(l_hGXF, &(poDS->nRasterXSize), &(poDS->nRasterYSize), nullptr,
317
12
                  nullptr, nullptr, &(poDS->dfNoDataValue));
318
319
12
    if (poDS->nRasterXSize <= 0 || poDS->nRasterYSize <= 0)
320
5
    {
321
5
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid dimensions : %d x %d",
322
5
                 poDS->nRasterXSize, poDS->nRasterYSize);
323
5
        delete poDS;
324
5
        return nullptr;
325
5
    }
326
327
    /* -------------------------------------------------------------------- */
328
    /*      Create band information objects.                                */
329
    /* -------------------------------------------------------------------- */
330
7
    poDS->nBands = 1;
331
7
    poDS->SetBand(1, new GXFRasterBand(poDS, 1));
332
333
    /* -------------------------------------------------------------------- */
334
    /*      Initialize any PAM information.                                 */
335
    /* -------------------------------------------------------------------- */
336
7
    poDS->SetDescription(poOpenInfo->pszFilename);
337
7
    poDS->TryLoadXML();
338
339
    /* -------------------------------------------------------------------- */
340
    /*      Check for external overviews.                                   */
341
    /* -------------------------------------------------------------------- */
342
7
    poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename,
343
7
                                poOpenInfo->GetSiblingFiles());
344
345
7
    return poDS;
346
12
}
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
}