Line data Source code
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 >) 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 4 : GXFRasterBand::GXFRasterBand(GXFDataset *poDSIn, int nBandIn)
72 :
73 : {
74 4 : poDS = poDSIn;
75 4 : nBand = nBandIn;
76 :
77 4 : eDataType = poDSIn->eDataType;
78 :
79 4 : nBlockXSize = poDS->GetRasterXSize();
80 4 : nBlockYSize = 1;
81 4 : }
82 :
83 : /************************************************************************/
84 : /* GetNoDataValue() */
85 : /************************************************************************/
86 :
87 0 : double GXFRasterBand::GetNoDataValue(int *bGotNoDataValue)
88 :
89 : {
90 0 : GXFDataset *poGXF_DS = cpl::down_cast<GXFDataset *>(poDS);
91 0 : if (bGotNoDataValue)
92 0 : *bGotNoDataValue = (fabs(poGXF_DS->dfNoDataValue - -1e12) > .1);
93 0 : if (eDataType == GDT_Float32)
94 0 : return (double)(float)poGXF_DS->dfNoDataValue;
95 :
96 0 : return poGXF_DS->dfNoDataValue;
97 : }
98 :
99 : /************************************************************************/
100 : /* IReadBlock() */
101 : /************************************************************************/
102 :
103 11 : CPLErr GXFRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
104 : void *pImage)
105 : {
106 11 : GXFDataset *const poGXF_DS = cpl::down_cast<GXFDataset *>(poDS);
107 :
108 11 : if (eDataType == GDT_Float32)
109 : {
110 11 : double *padfBuffer = (double *)VSIMalloc2(sizeof(double), nBlockXSize);
111 11 : if (padfBuffer == nullptr)
112 0 : return CE_Failure;
113 : const CPLErr eErr =
114 11 : GXFGetScanline(poGXF_DS->hGXF, nBlockYOff, padfBuffer);
115 :
116 11 : float *pafBuffer = (float *)pImage;
117 103 : for (int i = 0; i < nBlockXSize; i++)
118 92 : pafBuffer[i] = (float)padfBuffer[i];
119 :
120 11 : CPLFree(padfBuffer);
121 :
122 11 : return eErr;
123 : }
124 :
125 : 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 : }
132 :
133 : /************************************************************************/
134 : /* ==================================================================== */
135 : /* GXFDataset */
136 : /* ==================================================================== */
137 : /************************************************************************/
138 :
139 : /************************************************************************/
140 : /* GXFDataset() */
141 : /************************************************************************/
142 :
143 4 : GXFDataset::GXFDataset()
144 4 : : hGXF(nullptr), dfNoDataValue(0), eDataType(GDT_Float32)
145 : {
146 4 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
147 4 : }
148 :
149 : /************************************************************************/
150 : /* ~GXFDataset() */
151 : /************************************************************************/
152 :
153 8 : GXFDataset::~GXFDataset()
154 :
155 : {
156 4 : FlushCache(true);
157 4 : if (hGXF != nullptr)
158 4 : GXFClose(hGXF);
159 8 : }
160 :
161 : /************************************************************************/
162 : /* GetGeoTransform() */
163 : /************************************************************************/
164 :
165 0 : CPLErr GXFDataset::GetGeoTransform(GDALGeoTransform >) const
166 :
167 : {
168 0 : double dfXOrigin = 0.0;
169 0 : double dfYOrigin = 0.0;
170 0 : double dfXSize = 0.0;
171 0 : double dfYSize = 0.0;
172 0 : double dfRotation = 0.0;
173 :
174 0 : const CPLErr eErr = GXFGetPosition(hGXF, &dfXOrigin, &dfYOrigin, &dfXSize,
175 : &dfYSize, &dfRotation);
176 :
177 0 : if (eErr != CE_None)
178 0 : return eErr;
179 :
180 : // Transform to radians.
181 0 : dfRotation = (dfRotation / 360.0) * 2.0 * M_PI;
182 :
183 0 : gt[1] = dfXSize * cos(dfRotation);
184 0 : gt[2] = dfYSize * sin(dfRotation);
185 0 : gt[4] = dfXSize * sin(dfRotation);
186 0 : gt[5] = -1 * dfYSize * cos(dfRotation);
187 :
188 : // take into account that GXF is point or center of pixel oriented.
189 0 : gt[0] = dfXOrigin - 0.5 * gt[1] - 0.5 * gt[2];
190 0 : gt[3] = dfYOrigin - 0.5 * gt[4] - 0.5 * gt[5];
191 :
192 0 : return CE_None;
193 : }
194 :
195 : /************************************************************************/
196 : /* GetSpatialRef() */
197 : /************************************************************************/
198 :
199 1 : const OGRSpatialReference *GXFDataset::GetSpatialRef() const
200 : {
201 1 : return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
202 : }
203 :
204 : /************************************************************************/
205 : /* Open() */
206 : /************************************************************************/
207 :
208 34124 : GDALDataset *GXFDataset::Open(GDALOpenInfo *poOpenInfo)
209 :
210 : {
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 34124 : if (poOpenInfo->nHeaderBytes < 50 || poOpenInfo->fpL == nullptr)
217 31068 : return nullptr;
218 :
219 3056 : bool bFoundKeyword = false;
220 3056 : bool bFoundIllegal = false;
221 850781 : for (int i = 0; i < poOpenInfo->nHeaderBytes - 1; i++)
222 : {
223 849910 : if ((poOpenInfo->pabyHeader[i] == 10 ||
224 827724 : poOpenInfo->pabyHeader[i] == 13) &&
225 23411 : poOpenInfo->pabyHeader[i + 1] == '#')
226 : {
227 194 : if (STARTS_WITH((const char *)poOpenInfo->pabyHeader + i + 2,
228 : "include"))
229 0 : return nullptr;
230 194 : if (STARTS_WITH((const char *)poOpenInfo->pabyHeader + i + 2,
231 : "define"))
232 0 : return nullptr;
233 194 : if (STARTS_WITH((const char *)poOpenInfo->pabyHeader + i + 2,
234 : "ifdef"))
235 0 : return nullptr;
236 194 : bFoundKeyword = true;
237 : }
238 849910 : if (poOpenInfo->pabyHeader[i] == 0)
239 : {
240 2185 : bFoundIllegal = true;
241 2185 : break;
242 : }
243 : }
244 :
245 3056 : if (!bFoundKeyword || bFoundIllegal)
246 3012 : 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 44 : CPL_IGNORE_RET_VAL(poOpenInfo->TryToIngest(50000));
254 44 : bool bGotGrid = false;
255 :
256 44 : const char *pszBigBuf = (const char *)poOpenInfo->pabyHeader;
257 36219 : for (int i = 0; i < poOpenInfo->nHeaderBytes - 5 && !bGotGrid; i++)
258 : {
259 36175 : if (pszBigBuf[i] == '#' && STARTS_WITH_CI(pszBigBuf + i + 1, "GRID"))
260 4 : bGotGrid = true;
261 : }
262 :
263 44 : if (!bGotGrid)
264 40 : return nullptr;
265 :
266 4 : VSIFCloseL(poOpenInfo->fpL);
267 4 : poOpenInfo->fpL = nullptr;
268 :
269 : /* -------------------------------------------------------------------- */
270 : /* Try opening the dataset. */
271 : /* -------------------------------------------------------------------- */
272 :
273 4 : GXFHandle l_hGXF = GXFOpen(poOpenInfo->pszFilename);
274 :
275 4 : if (l_hGXF == nullptr)
276 0 : return nullptr;
277 :
278 : /* -------------------------------------------------------------------- */
279 : /* Confirm the requested access is supported. */
280 : /* -------------------------------------------------------------------- */
281 4 : if (poOpenInfo->eAccess == GA_Update)
282 : {
283 0 : GXFClose(l_hGXF);
284 0 : ReportUpdateNotSupportedByDriver("GXF");
285 0 : return nullptr;
286 : }
287 :
288 : /* -------------------------------------------------------------------- */
289 : /* Create a corresponding GDALDataset. */
290 : /* -------------------------------------------------------------------- */
291 4 : GXFDataset *poDS = new GXFDataset();
292 :
293 4 : const char *pszGXFDataType = CPLGetConfigOption("GXF_DATATYPE", "Float32");
294 4 : GDALDataType eDT = GDALGetDataTypeByName(pszGXFDataType);
295 4 : if (!(eDT == GDT_Float32 || eDT == GDT_Float64))
296 : {
297 0 : CPLError(CE_Warning, CPLE_NotSupported,
298 : "Unsupported value for GXF_DATATYPE : %s", pszGXFDataType);
299 0 : eDT = GDT_Float32;
300 : }
301 :
302 4 : poDS->hGXF = l_hGXF;
303 4 : poDS->eDataType = eDT;
304 :
305 : /* -------------------------------------------------------------------- */
306 : /* Establish the projection. */
307 : /* -------------------------------------------------------------------- */
308 4 : char *pszProjection = GXFGetMapProjectionAsOGCWKT(l_hGXF);
309 4 : if (pszProjection && pszProjection[0] != '\0')
310 2 : poDS->m_oSRS.importFromWkt(pszProjection);
311 4 : CPLFree(pszProjection);
312 :
313 : /* -------------------------------------------------------------------- */
314 : /* Capture some information from the file that is of interest. */
315 : /* -------------------------------------------------------------------- */
316 4 : GXFGetRawInfo(l_hGXF, &(poDS->nRasterXSize), &(poDS->nRasterYSize), nullptr,
317 : nullptr, nullptr, &(poDS->dfNoDataValue));
318 :
319 4 : if (poDS->nRasterXSize <= 0 || poDS->nRasterYSize <= 0)
320 : {
321 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid dimensions : %d x %d",
322 : poDS->nRasterXSize, poDS->nRasterYSize);
323 0 : delete poDS;
324 0 : return nullptr;
325 : }
326 :
327 : /* -------------------------------------------------------------------- */
328 : /* Create band information objects. */
329 : /* -------------------------------------------------------------------- */
330 4 : poDS->nBands = 1;
331 4 : poDS->SetBand(1, new GXFRasterBand(poDS, 1));
332 :
333 : /* -------------------------------------------------------------------- */
334 : /* Initialize any PAM information. */
335 : /* -------------------------------------------------------------------- */
336 4 : poDS->SetDescription(poOpenInfo->pszFilename);
337 4 : poDS->TryLoadXML();
338 :
339 : /* -------------------------------------------------------------------- */
340 : /* Check for external overviews. */
341 : /* -------------------------------------------------------------------- */
342 8 : poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename,
343 4 : poOpenInfo->GetSiblingFiles());
344 :
345 4 : return poDS;
346 : }
347 :
348 : /************************************************************************/
349 : /* GDALRegister_GXF() */
350 : /************************************************************************/
351 :
352 2038 : void GDALRegister_GXF()
353 :
354 : {
355 2038 : if (GDALGetDriverByName("GXF") != nullptr)
356 283 : return;
357 :
358 1755 : GDALDriver *poDriver = new GDALDriver();
359 :
360 1755 : poDriver->SetDescription("GXF");
361 1755 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
362 1755 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
363 1755 : "GeoSoft Grid Exchange Format");
364 1755 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/gxf.html");
365 1755 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "gxf");
366 1755 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
367 :
368 1755 : poDriver->pfnOpen = GXFDataset::Open;
369 :
370 1755 : GetGDALDriverManager()->RegisterDriver(poDriver);
371 : }
|