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 CPL_CVSID("$Id: gxfdataset.cpp 3565f178b0cf927c6e755d7da1af362499edca5f 2021-03-08 21:56:06 +0100 Even Rouault $")
35
36 /************************************************************************/
37 /* ==================================================================== */
38 /* GXFDataset */
39 /* ==================================================================== */
40 /************************************************************************/
41
42 class GXFRasterBand;
43
44 class GXFDataset final: public GDALPamDataset
45 {
46 friend class GXFRasterBand;
47
48 GXFHandle hGXF;
49
50 char *pszProjection;
51 double dfNoDataValue;
52 GDALDataType eDataType;
53
54 public:
55 GXFDataset();
56 ~GXFDataset();
57
58 static GDALDataset *Open( GDALOpenInfo * );
59
60 CPLErr GetGeoTransform( double * padfTransform ) override;
61 const char *_GetProjectionRef() override;
GetSpatialRef() const62 const OGRSpatialReference* GetSpatialRef() const override {
63 return GetSpatialRefFromOldGetProjectionRef();
64 }
65 };
66
67 /************************************************************************/
68 /* ==================================================================== */
69 /* GXFRasterBand */
70 /* ==================================================================== */
71 /************************************************************************/
72
73 class GXFRasterBand final: public GDALPamRasterBand
74 {
75 friend class GXFDataset;
76
77 public:
78
79 GXFRasterBand( GXFDataset *, int );
80 double GetNoDataValue( int* bGotNoDataValue ) override;
81
82 virtual CPLErr IReadBlock( int, int, void * ) override;
83 };
84
85 /************************************************************************/
86 /* GXFRasterBand() */
87 /************************************************************************/
88
GXFRasterBand(GXFDataset * poDSIn,int nBandIn)89 GXFRasterBand::GXFRasterBand( GXFDataset *poDSIn, int nBandIn )
90
91 {
92 poDS = poDSIn;
93 nBand = nBandIn;
94
95 eDataType = poDSIn->eDataType;
96
97 nBlockXSize = poDS->GetRasterXSize();
98 nBlockYSize = 1;
99 }
100
101 /************************************************************************/
102 /* GetNoDataValue() */
103 /************************************************************************/
104
GetNoDataValue(int * bGotNoDataValue)105 double GXFRasterBand::GetNoDataValue(int* bGotNoDataValue)
106
107 {
108 GXFDataset *poGXF_DS = (GXFDataset *) poDS;
109 if( bGotNoDataValue )
110 *bGotNoDataValue = (fabs(poGXF_DS->dfNoDataValue - -1e12) > .1);
111 if( eDataType == GDT_Float32 )
112 return (double)(float)poGXF_DS->dfNoDataValue;
113
114 return poGXF_DS->dfNoDataValue;
115 }
116
117 /************************************************************************/
118 /* IReadBlock() */
119 /************************************************************************/
120
IReadBlock(CPL_UNUSED int nBlockXOff,int nBlockYOff,void * pImage)121 CPLErr GXFRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff,
122 int nBlockYOff,
123 void * pImage )
124 {
125 GXFDataset * const poGXF_DS = (GXFDataset *) poDS;
126
127 if( eDataType == GDT_Float32)
128 {
129 double *padfBuffer = (double *) VSIMalloc2(sizeof(double), nBlockXSize);
130 if( padfBuffer == nullptr )
131 return CE_Failure;
132 const CPLErr eErr =
133 GXFGetScanline( poGXF_DS->hGXF, nBlockYOff, padfBuffer );
134
135 float *pafBuffer = (float *) pImage;
136 for( int i = 0; i < nBlockXSize; i++ )
137 pafBuffer[i] = (float) padfBuffer[i];
138
139 CPLFree( padfBuffer );
140
141 return eErr;
142 }
143
144 const CPLErr eErr =
145 eDataType == GDT_Float64
146 ? GXFGetScanline( poGXF_DS->hGXF, nBlockYOff, (double*)pImage )
147 : CE_Failure;
148
149 return eErr;
150 }
151
152 /************************************************************************/
153 /* ==================================================================== */
154 /* GXFDataset */
155 /* ==================================================================== */
156 /************************************************************************/
157
158 /************************************************************************/
159 /* GXFDataset() */
160 /************************************************************************/
161
GXFDataset()162 GXFDataset::GXFDataset() :
163 hGXF(nullptr),
164 pszProjection(nullptr),
165 dfNoDataValue(0),
166 eDataType(GDT_Float32)
167 {}
168
169 /************************************************************************/
170 /* ~GXFDataset() */
171 /************************************************************************/
172
~GXFDataset()173 GXFDataset::~GXFDataset()
174
175 {
176 FlushCache();
177 if( hGXF != nullptr )
178 GXFClose( hGXF );
179 CPLFree( pszProjection );
180 }
181
182 /************************************************************************/
183 /* GetGeoTransform() */
184 /************************************************************************/
185
GetGeoTransform(double * padfTransform)186 CPLErr GXFDataset::GetGeoTransform( double * padfTransform )
187
188 {
189 double dfXOrigin = 0.0;
190 double dfYOrigin = 0.0;
191 double dfXSize = 0.0;
192 double dfYSize = 0.0;
193 double dfRotation = 0.0;
194
195 const CPLErr eErr =
196 GXFGetPosition( hGXF, &dfXOrigin, &dfYOrigin,
197 &dfXSize, &dfYSize, &dfRotation );
198
199 if( eErr != CE_None )
200 return eErr;
201
202 // Transform to radians.
203 dfRotation = (dfRotation / 360.0) * 2.0 * M_PI;
204
205 padfTransform[1] = dfXSize * cos(dfRotation);
206 padfTransform[2] = dfYSize * sin(dfRotation);
207 padfTransform[4] = dfXSize * sin(dfRotation);
208 padfTransform[5] = -1 * dfYSize * cos(dfRotation);
209
210 // take into account that GXF is point or center of pixel oriented.
211 padfTransform[0] = dfXOrigin - 0.5*padfTransform[1] - 0.5*padfTransform[2];
212 padfTransform[3] = dfYOrigin - 0.5*padfTransform[4] - 0.5*padfTransform[5];
213
214 return CE_None;
215 }
216
217 /************************************************************************/
218 /* GetProjectionRef() */
219 /************************************************************************/
220
_GetProjectionRef()221 const char *GXFDataset::_GetProjectionRef()
222
223 {
224 return pszProjection;
225 }
226
227 /************************************************************************/
228 /* Open() */
229 /************************************************************************/
230
Open(GDALOpenInfo * poOpenInfo)231 GDALDataset *GXFDataset::Open( GDALOpenInfo * poOpenInfo )
232
233 {
234 /* -------------------------------------------------------------------- */
235 /* Before trying GXFOpen() we first verify that there is at */
236 /* least one "\n#keyword" type signature in the first chunk of */
237 /* the file. */
238 /* -------------------------------------------------------------------- */
239 if( poOpenInfo->nHeaderBytes < 50 || poOpenInfo->fpL == nullptr )
240 return nullptr;
241
242 bool bFoundKeyword = false;
243 bool bFoundIllegal = false;
244 for( int i = 0; i < poOpenInfo->nHeaderBytes-1; i++ )
245 {
246 if( (poOpenInfo->pabyHeader[i] == 10
247 || poOpenInfo->pabyHeader[i] == 13)
248 && poOpenInfo->pabyHeader[i+1] == '#' )
249 {
250 if( STARTS_WITH((const char*)poOpenInfo->pabyHeader + i + 2,
251 "include") )
252 return nullptr;
253 if( STARTS_WITH((const char*)poOpenInfo->pabyHeader + i + 2,
254 "define") )
255 return nullptr;
256 if( STARTS_WITH((const char*)poOpenInfo->pabyHeader + i + 2,
257 "ifdef") )
258 return nullptr;
259 bFoundKeyword = true;
260 }
261 if( poOpenInfo->pabyHeader[i] == 0 )
262 {
263 bFoundIllegal = true;
264 break;
265 }
266 }
267
268 if( !bFoundKeyword || bFoundIllegal )
269 return nullptr;
270
271 /* -------------------------------------------------------------------- */
272 /* At this point it is plausible that this is a GXF file, but */
273 /* we also now verify that there is a #GRID keyword before */
274 /* passing it off to GXFOpen(). We check in the first 50K. */
275 /* -------------------------------------------------------------------- */
276 CPL_IGNORE_RET_VAL(poOpenInfo->TryToIngest(50000));
277 bool bGotGrid = false;
278
279 const char* pszBigBuf = (const char*)poOpenInfo->pabyHeader;
280 for( int i = 0; i < poOpenInfo->nHeaderBytes - 5 && !bGotGrid; i++ )
281 {
282 if( pszBigBuf[i] == '#' && STARTS_WITH_CI(pszBigBuf+i+1, "GRID") )
283 bGotGrid = true;
284 }
285
286 if( !bGotGrid )
287 return nullptr;
288
289 VSIFCloseL( poOpenInfo->fpL );
290 poOpenInfo->fpL = nullptr;
291
292 /* -------------------------------------------------------------------- */
293 /* Try opening the dataset. */
294 /* -------------------------------------------------------------------- */
295
296 GXFHandle l_hGXF = GXFOpen( poOpenInfo->pszFilename );
297
298 if( l_hGXF == nullptr )
299 return nullptr;
300
301 /* -------------------------------------------------------------------- */
302 /* Confirm the requested access is supported. */
303 /* -------------------------------------------------------------------- */
304 if( poOpenInfo->eAccess == GA_Update )
305 {
306 GXFClose(l_hGXF);
307 CPLError( CE_Failure, CPLE_NotSupported,
308 "The GXF driver does not support update access to existing"
309 " datasets." );
310 return nullptr;
311 }
312
313 /* -------------------------------------------------------------------- */
314 /* Create a corresponding GDALDataset. */
315 /* -------------------------------------------------------------------- */
316 GXFDataset *poDS = new GXFDataset();
317
318 const char* pszGXFDataType = CPLGetConfigOption("GXF_DATATYPE", "Float32");
319 GDALDataType eDT = GDALGetDataTypeByName(pszGXFDataType);
320 if( !(eDT == GDT_Float32 || eDT == GDT_Float64) )
321 {
322 CPLError(CE_Warning, CPLE_NotSupported,
323 "Unsupported value for GXF_DATATYPE : %s", pszGXFDataType);
324 eDT = GDT_Float32;
325 }
326
327 poDS->hGXF = l_hGXF;
328 poDS->eDataType = eDT;
329
330 /* -------------------------------------------------------------------- */
331 /* Establish the projection. */
332 /* -------------------------------------------------------------------- */
333 poDS->pszProjection = GXFGetMapProjectionAsOGCWKT( l_hGXF );
334
335 /* -------------------------------------------------------------------- */
336 /* Capture some information from the file that is of interest. */
337 /* -------------------------------------------------------------------- */
338 GXFGetRawInfo( l_hGXF, &(poDS->nRasterXSize), &(poDS->nRasterYSize), nullptr,
339 nullptr, nullptr, &(poDS->dfNoDataValue) );
340
341 if( poDS->nRasterXSize <= 0 || poDS->nRasterYSize <= 0 )
342 {
343 CPLError( CE_Failure, CPLE_AppDefined,
344 "Invalid dimensions : %d x %d",
345 poDS->nRasterXSize, poDS->nRasterYSize);
346 delete poDS;
347 return nullptr;
348 }
349
350 /* -------------------------------------------------------------------- */
351 /* Create band information objects. */
352 /* -------------------------------------------------------------------- */
353 poDS->nBands = 1;
354 poDS->SetBand( 1, new GXFRasterBand( poDS, 1 ));
355
356 /* -------------------------------------------------------------------- */
357 /* Initialize any PAM information. */
358 /* -------------------------------------------------------------------- */
359 poDS->SetDescription( poOpenInfo->pszFilename );
360 poDS->TryLoadXML();
361
362 /* -------------------------------------------------------------------- */
363 /* Check for external overviews. */
364 /* -------------------------------------------------------------------- */
365 poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename,
366 poOpenInfo->GetSiblingFiles() );
367
368 return poDS;
369 }
370
371 /************************************************************************/
372 /* GDALRegister_GXF() */
373 /************************************************************************/
374
GDALRegister_GXF()375 void GDALRegister_GXF()
376
377 {
378 if( GDALGetDriverByName( "GXF" ) != nullptr )
379 return;
380
381 GDALDriver *poDriver = new GDALDriver();
382
383 poDriver->SetDescription( "GXF" );
384 poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" );
385 poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
386 "GeoSoft Grid Exchange Format" );
387 poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "drivers/raster/gxf.html" );
388 poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "gxf" );
389 poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
390
391 poDriver->pfnOpen = GXFDataset::Open;
392
393 GetGDALDriverManager()->RegisterDriver( poDriver );
394 }
395