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