/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 >) 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 >) 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 | } |