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 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "gdal_frmts.h"
31 : #include "gdal_pam.h"
32 : #include "gxfopen.h"
33 :
34 : /************************************************************************/
35 : /* ==================================================================== */
36 : /* GXFDataset */
37 : /* ==================================================================== */
38 : /************************************************************************/
39 :
40 : class GXFRasterBand;
41 :
42 : class GXFDataset final : public GDALPamDataset
43 : {
44 : friend class GXFRasterBand;
45 :
46 : GXFHandle hGXF;
47 :
48 : OGRSpatialReference m_oSRS{};
49 : double dfNoDataValue;
50 : GDALDataType eDataType;
51 :
52 : public:
53 : GXFDataset();
54 : ~GXFDataset();
55 :
56 : static GDALDataset *Open(GDALOpenInfo *);
57 :
58 : CPLErr GetGeoTransform(double *padfTransform) override;
59 : const OGRSpatialReference *GetSpatialRef() const override;
60 : };
61 :
62 : /************************************************************************/
63 : /* ==================================================================== */
64 : /* GXFRasterBand */
65 : /* ==================================================================== */
66 : /************************************************************************/
67 :
68 : class GXFRasterBand final : public GDALPamRasterBand
69 : {
70 : friend class GXFDataset;
71 :
72 : public:
73 : GXFRasterBand(GXFDataset *, int);
74 : double GetNoDataValue(int *bGotNoDataValue) override;
75 :
76 : virtual CPLErr IReadBlock(int, int, void *) override;
77 : };
78 :
79 : /************************************************************************/
80 : /* GXFRasterBand() */
81 : /************************************************************************/
82 :
83 4 : GXFRasterBand::GXFRasterBand(GXFDataset *poDSIn, int nBandIn)
84 :
85 : {
86 4 : poDS = poDSIn;
87 4 : nBand = nBandIn;
88 :
89 4 : eDataType = poDSIn->eDataType;
90 :
91 4 : nBlockXSize = poDS->GetRasterXSize();
92 4 : nBlockYSize = 1;
93 4 : }
94 :
95 : /************************************************************************/
96 : /* GetNoDataValue() */
97 : /************************************************************************/
98 :
99 0 : double GXFRasterBand::GetNoDataValue(int *bGotNoDataValue)
100 :
101 : {
102 0 : GXFDataset *poGXF_DS = (GXFDataset *)poDS;
103 0 : if (bGotNoDataValue)
104 0 : *bGotNoDataValue = (fabs(poGXF_DS->dfNoDataValue - -1e12) > .1);
105 0 : if (eDataType == GDT_Float32)
106 0 : return (double)(float)poGXF_DS->dfNoDataValue;
107 :
108 0 : return poGXF_DS->dfNoDataValue;
109 : }
110 :
111 : /************************************************************************/
112 : /* IReadBlock() */
113 : /************************************************************************/
114 :
115 11 : CPLErr GXFRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
116 : void *pImage)
117 : {
118 11 : GXFDataset *const poGXF_DS = (GXFDataset *)poDS;
119 :
120 11 : if (eDataType == GDT_Float32)
121 : {
122 11 : double *padfBuffer = (double *)VSIMalloc2(sizeof(double), nBlockXSize);
123 11 : if (padfBuffer == nullptr)
124 0 : return CE_Failure;
125 : const CPLErr eErr =
126 11 : GXFGetScanline(poGXF_DS->hGXF, nBlockYOff, padfBuffer);
127 :
128 11 : float *pafBuffer = (float *)pImage;
129 103 : for (int i = 0; i < nBlockXSize; i++)
130 92 : pafBuffer[i] = (float)padfBuffer[i];
131 :
132 11 : CPLFree(padfBuffer);
133 :
134 11 : return eErr;
135 : }
136 :
137 : const CPLErr eErr =
138 0 : eDataType == GDT_Float64
139 0 : ? GXFGetScanline(poGXF_DS->hGXF, nBlockYOff, (double *)pImage)
140 0 : : CE_Failure;
141 :
142 0 : return eErr;
143 : }
144 :
145 : /************************************************************************/
146 : /* ==================================================================== */
147 : /* GXFDataset */
148 : /* ==================================================================== */
149 : /************************************************************************/
150 :
151 : /************************************************************************/
152 : /* GXFDataset() */
153 : /************************************************************************/
154 :
155 4 : GXFDataset::GXFDataset()
156 4 : : hGXF(nullptr), dfNoDataValue(0), eDataType(GDT_Float32)
157 : {
158 4 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
159 4 : }
160 :
161 : /************************************************************************/
162 : /* ~GXFDataset() */
163 : /************************************************************************/
164 :
165 8 : GXFDataset::~GXFDataset()
166 :
167 : {
168 4 : FlushCache(true);
169 4 : if (hGXF != nullptr)
170 4 : GXFClose(hGXF);
171 8 : }
172 :
173 : /************************************************************************/
174 : /* GetGeoTransform() */
175 : /************************************************************************/
176 :
177 0 : CPLErr GXFDataset::GetGeoTransform(double *padfTransform)
178 :
179 : {
180 0 : double dfXOrigin = 0.0;
181 0 : double dfYOrigin = 0.0;
182 0 : double dfXSize = 0.0;
183 0 : double dfYSize = 0.0;
184 0 : double dfRotation = 0.0;
185 :
186 0 : const CPLErr eErr = GXFGetPosition(hGXF, &dfXOrigin, &dfYOrigin, &dfXSize,
187 : &dfYSize, &dfRotation);
188 :
189 0 : if (eErr != CE_None)
190 0 : return eErr;
191 :
192 : // Transform to radians.
193 0 : dfRotation = (dfRotation / 360.0) * 2.0 * M_PI;
194 :
195 0 : padfTransform[1] = dfXSize * cos(dfRotation);
196 0 : padfTransform[2] = dfYSize * sin(dfRotation);
197 0 : padfTransform[4] = dfXSize * sin(dfRotation);
198 0 : padfTransform[5] = -1 * dfYSize * cos(dfRotation);
199 :
200 : // take into account that GXF is point or center of pixel oriented.
201 0 : padfTransform[0] =
202 0 : dfXOrigin - 0.5 * padfTransform[1] - 0.5 * padfTransform[2];
203 0 : padfTransform[3] =
204 0 : dfYOrigin - 0.5 * padfTransform[4] - 0.5 * padfTransform[5];
205 :
206 0 : return CE_None;
207 : }
208 :
209 : /************************************************************************/
210 : /* GetSpatialRef() */
211 : /************************************************************************/
212 :
213 1 : const OGRSpatialReference *GXFDataset::GetSpatialRef() const
214 : {
215 1 : return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
216 : }
217 :
218 : /************************************************************************/
219 : /* Open() */
220 : /************************************************************************/
221 :
222 28795 : GDALDataset *GXFDataset::Open(GDALOpenInfo *poOpenInfo)
223 :
224 : {
225 : /* -------------------------------------------------------------------- */
226 : /* Before trying GXFOpen() we first verify that there is at */
227 : /* least one "\n#keyword" type signature in the first chunk of */
228 : /* the file. */
229 : /* -------------------------------------------------------------------- */
230 28795 : if (poOpenInfo->nHeaderBytes < 50 || poOpenInfo->fpL == nullptr)
231 26056 : return nullptr;
232 :
233 2739 : bool bFoundKeyword = false;
234 2739 : bool bFoundIllegal = false;
235 596022 : for (int i = 0; i < poOpenInfo->nHeaderBytes - 1; i++)
236 : {
237 595281 : if ((poOpenInfo->pabyHeader[i] == 10 ||
238 579964 : poOpenInfo->pabyHeader[i] == 13) &&
239 16434 : poOpenInfo->pabyHeader[i + 1] == '#')
240 : {
241 153 : if (STARTS_WITH((const char *)poOpenInfo->pabyHeader + i + 2,
242 : "include"))
243 0 : return nullptr;
244 153 : if (STARTS_WITH((const char *)poOpenInfo->pabyHeader + i + 2,
245 : "define"))
246 0 : return nullptr;
247 153 : if (STARTS_WITH((const char *)poOpenInfo->pabyHeader + i + 2,
248 : "ifdef"))
249 0 : return nullptr;
250 153 : bFoundKeyword = true;
251 : }
252 595281 : if (poOpenInfo->pabyHeader[i] == 0)
253 : {
254 1998 : bFoundIllegal = true;
255 1998 : break;
256 : }
257 : }
258 :
259 2739 : if (!bFoundKeyword || bFoundIllegal)
260 2702 : return nullptr;
261 :
262 : /* -------------------------------------------------------------------- */
263 : /* At this point it is plausible that this is a GXF file, but */
264 : /* we also now verify that there is a #GRID keyword before */
265 : /* passing it off to GXFOpen(). We check in the first 50K. */
266 : /* -------------------------------------------------------------------- */
267 37 : CPL_IGNORE_RET_VAL(poOpenInfo->TryToIngest(50000));
268 37 : bool bGotGrid = false;
269 :
270 37 : const char *pszBigBuf = (const char *)poOpenInfo->pabyHeader;
271 8693 : for (int i = 0; i < poOpenInfo->nHeaderBytes - 5 && !bGotGrid; i++)
272 : {
273 8656 : if (pszBigBuf[i] == '#' && STARTS_WITH_CI(pszBigBuf + i + 1, "GRID"))
274 4 : bGotGrid = true;
275 : }
276 :
277 37 : if (!bGotGrid)
278 33 : return nullptr;
279 :
280 4 : VSIFCloseL(poOpenInfo->fpL);
281 4 : poOpenInfo->fpL = nullptr;
282 :
283 : /* -------------------------------------------------------------------- */
284 : /* Try opening the dataset. */
285 : /* -------------------------------------------------------------------- */
286 :
287 4 : GXFHandle l_hGXF = GXFOpen(poOpenInfo->pszFilename);
288 :
289 4 : if (l_hGXF == nullptr)
290 0 : return nullptr;
291 :
292 : /* -------------------------------------------------------------------- */
293 : /* Confirm the requested access is supported. */
294 : /* -------------------------------------------------------------------- */
295 4 : if (poOpenInfo->eAccess == GA_Update)
296 : {
297 0 : GXFClose(l_hGXF);
298 0 : CPLError(CE_Failure, CPLE_NotSupported,
299 : "The GXF driver does not support update access to existing"
300 : " datasets.");
301 0 : return nullptr;
302 : }
303 :
304 : /* -------------------------------------------------------------------- */
305 : /* Create a corresponding GDALDataset. */
306 : /* -------------------------------------------------------------------- */
307 4 : GXFDataset *poDS = new GXFDataset();
308 :
309 4 : const char *pszGXFDataType = CPLGetConfigOption("GXF_DATATYPE", "Float32");
310 4 : GDALDataType eDT = GDALGetDataTypeByName(pszGXFDataType);
311 4 : if (!(eDT == GDT_Float32 || eDT == GDT_Float64))
312 : {
313 0 : CPLError(CE_Warning, CPLE_NotSupported,
314 : "Unsupported value for GXF_DATATYPE : %s", pszGXFDataType);
315 0 : eDT = GDT_Float32;
316 : }
317 :
318 4 : poDS->hGXF = l_hGXF;
319 4 : poDS->eDataType = eDT;
320 :
321 : /* -------------------------------------------------------------------- */
322 : /* Establish the projection. */
323 : /* -------------------------------------------------------------------- */
324 4 : char *pszProjection = GXFGetMapProjectionAsOGCWKT(l_hGXF);
325 4 : if (pszProjection && pszProjection[0] != '\0')
326 2 : poDS->m_oSRS.importFromWkt(pszProjection);
327 4 : CPLFree(pszProjection);
328 :
329 : /* -------------------------------------------------------------------- */
330 : /* Capture some information from the file that is of interest. */
331 : /* -------------------------------------------------------------------- */
332 4 : GXFGetRawInfo(l_hGXF, &(poDS->nRasterXSize), &(poDS->nRasterYSize), nullptr,
333 : nullptr, nullptr, &(poDS->dfNoDataValue));
334 :
335 4 : if (poDS->nRasterXSize <= 0 || poDS->nRasterYSize <= 0)
336 : {
337 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid dimensions : %d x %d",
338 : poDS->nRasterXSize, poDS->nRasterYSize);
339 0 : delete poDS;
340 0 : return nullptr;
341 : }
342 :
343 : /* -------------------------------------------------------------------- */
344 : /* Create band information objects. */
345 : /* -------------------------------------------------------------------- */
346 4 : poDS->nBands = 1;
347 4 : poDS->SetBand(1, new GXFRasterBand(poDS, 1));
348 :
349 : /* -------------------------------------------------------------------- */
350 : /* Initialize any PAM information. */
351 : /* -------------------------------------------------------------------- */
352 4 : poDS->SetDescription(poOpenInfo->pszFilename);
353 4 : poDS->TryLoadXML();
354 :
355 : /* -------------------------------------------------------------------- */
356 : /* Check for external overviews. */
357 : /* -------------------------------------------------------------------- */
358 4 : poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename,
359 : poOpenInfo->GetSiblingFiles());
360 :
361 4 : return poDS;
362 : }
363 :
364 : /************************************************************************/
365 : /* GDALRegister_GXF() */
366 : /************************************************************************/
367 :
368 1512 : void GDALRegister_GXF()
369 :
370 : {
371 1512 : if (GDALGetDriverByName("GXF") != nullptr)
372 295 : return;
373 :
374 1217 : GDALDriver *poDriver = new GDALDriver();
375 :
376 1217 : poDriver->SetDescription("GXF");
377 1217 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
378 1217 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
379 1217 : "GeoSoft Grid Exchange Format");
380 1217 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/gxf.html");
381 1217 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "gxf");
382 1217 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
383 :
384 1217 : poDriver->pfnOpen = GXFDataset::Open;
385 :
386 1217 : GetGDALDriverManager()->RegisterDriver(poDriver);
387 : }
|