1 /******************************************************************************
2  * $Id: gxfopen.c 68c453b0a55be4fa90b52f55167e59d8b4a9e0e9 2019-08-20 16:34:12 +0200 Even Rouault $
3  *
4  * Project:  GXF Reader
5  * Purpose:  Majority of Geosoft GXF reading code.
6  * Author:   Frank Warmerdam, warmerdam@pobox.com
7  *
8  ******************************************************************************
9  * Copyright (c) 1998, Global Geomatics
10  * Copyright (c) 1998, Frank Warmerdam
11  * Copyright (c) 2008-2012, Even Rouault <even dot rouault at spatialys.com>
12  *
13  * Permission is hereby granted, free of charge, to any person obtaining a
14  * copy of this software and associated documentation files (the "Software"),
15  * to deal in the Software without restriction, including without limitation
16  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
17  * and/or sell copies of the Software, and to permit persons to whom the
18  * Software is furnished to do so, subject to the following conditions:
19  *
20  * The above copyright notice and this permission notice shall be included
21  * in all copies or substantial portions of the Software.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29  * DEALINGS IN THE SOFTWARE.
30  ****************************************************************************/
31 
32 #include "cpl_port.h"
33 
34 #include <ctype.h>
35 #include "gxfopen.h"
36 
37 CPL_CVSID("$Id: gxfopen.c 68c453b0a55be4fa90b52f55167e59d8b4a9e0e9 2019-08-20 16:34:12 +0200 Even Rouault $")
38 
39 
40 /* this is also defined in gdal.h which we avoid in this separable component */
41 #define CPLE_WrongFormat	200
42 
43 #define MAX_LINE_COUNT_PER_HEADER       1000
44 #define MAX_HEADER_COUNT                1000
45 
46 /************************************************************************/
47 /*                         GXFReadHeaderValue()                         */
48 /*                                                                      */
49 /*      Read one entry from the file header, and return it and its      */
50 /*      value in clean form.                                            */
51 /************************************************************************/
52 
GXFReadHeaderValue(VSILFILE * fp,char * pszHTitle)53 static char **GXFReadHeaderValue( VSILFILE * fp, char * pszHTitle )
54 
55 {
56     const char	*pszLine;
57     char	**papszReturn = NULL;
58     int		i;
59     int     nLineCount = 0, nReturnLineCount = 0;
60     int     bContinuedLine = FALSE;
61 
62 /* -------------------------------------------------------------------- */
63 /*      Try to read a line.  If we fail or if this isn't a proper       */
64 /*      header value then return the failure.                           */
65 /* -------------------------------------------------------------------- */
66     pszLine = CPLReadLineL( fp );
67     if( pszLine == NULL )
68     {
69         strcpy( pszHTitle, "#EOF" );
70         return( NULL );
71     }
72 
73 /* -------------------------------------------------------------------- */
74 /*      Extract the title.  It should be terminated by some sort of     */
75 /*      white space.                                                    */
76 /* -------------------------------------------------------------------- */
77     for( i = 0; i < 70 && !isspace((unsigned char)pszLine[i]) && pszLine[i] != '\0'; i++ ) {}
78 
79     strncpy( pszHTitle, pszLine, i );
80     pszHTitle[i] = '\0';
81 
82 /* -------------------------------------------------------------------- */
83 /*      If this is #GRID, then return ... we are at the end of the      */
84 /*      header.                                                         */
85 /* -------------------------------------------------------------------- */
86     if( EQUAL(pszHTitle,"#GRID") )
87         return NULL;
88 
89 /* -------------------------------------------------------------------- */
90 /*      Skip white space.                                               */
91 /* -------------------------------------------------------------------- */
92     while( isspace((unsigned char)pszLine[i]) )
93         i++;
94 
95 /* -------------------------------------------------------------------- */
96 /*    If we have reached the end of the line, try to read another line. */
97 /* -------------------------------------------------------------------- */
98     if( pszLine[i] == '\0' )
99     {
100         pszLine = CPLReadLineL( fp );
101         if( pszLine == NULL )
102         {
103             strcpy( pszHTitle, "#EOF" );
104             return( NULL );
105         }
106     }
107 
108 /* -------------------------------------------------------------------- */
109 /*      Keeping adding the value stuff as new lines till we reach a     */
110 /*      `#' mark at the beginning of a new line.                        */
111 /* -------------------------------------------------------------------- */
112     do {
113         vsi_l_offset    nCurPos;
114         char            chNextChar = 0;
115         char		*pszTrimmedLine;
116         size_t      nLen = strlen(pszLine);
117 
118         /* Lines are supposed to be limited to 80 characters */
119         if( nLen > 1024 )
120         {
121             CSLDestroy(papszReturn);
122             return NULL;
123         }
124 
125         pszTrimmedLine = CPLStrdup( pszLine );
126 
127         for( i = ((int)nLen)-1; i >= 0 && pszLine[i] == ' '; i-- )
128             pszTrimmedLine[i] = '\0';
129 
130         if( bContinuedLine )
131         {
132             char* pszTmp = (char*) VSIMalloc(strlen(papszReturn[nReturnLineCount-1]) + strlen(pszTrimmedLine) + 1);
133             if( pszTmp == NULL )
134             {
135                 CSLDestroy(papszReturn);
136                 CPLFree(pszTrimmedLine);
137                 return NULL;
138             }
139             strcpy(pszTmp, papszReturn[nReturnLineCount-1]);
140             if( pszTrimmedLine[0] == '\0' )
141                 pszTmp[strlen(papszReturn[nReturnLineCount-1]) - 1] = 0;
142             else
143                 strcpy(pszTmp + (strlen(papszReturn[nReturnLineCount-1]) - 1), pszTrimmedLine);
144             CPLFree(papszReturn[nReturnLineCount-1]);
145             papszReturn[nReturnLineCount-1] = pszTmp;
146         }
147         else
148         {
149             papszReturn = CSLAddString( papszReturn, pszTrimmedLine );
150             nReturnLineCount ++;
151         }
152 
153         /* Is it a continued line ? */
154         bContinuedLine = ( i >= 0 && pszTrimmedLine[i] == '\\' );
155 
156         CPLFree( pszTrimmedLine );
157 
158         nCurPos = VSIFTellL(fp);
159         if( VSIFReadL(&chNextChar, 1, 1, fp) != 1 )
160         {
161             CSLDestroy(papszReturn);
162             return NULL;
163         }
164         VSIFSeekL(fp, nCurPos, SEEK_SET);
165 
166         if( chNextChar == '#' )
167             pszLine = NULL;
168         else
169         {
170             pszLine = CPLReadLineL( fp );
171             nLineCount ++;
172         }
173     } while( pszLine != NULL && nLineCount < MAX_LINE_COUNT_PER_HEADER );
174 
175     return( papszReturn );
176 }
177 
178 /************************************************************************/
179 /*                              GXFOpen()                               */
180 /************************************************************************/
181 
182 /**
183  * Open a GXF file, and collect contents of the header.
184  *
185  * @param pszFilename the name of the file to open.
186  *
187  * @return a handle for use with other GXF functions to access the file.  This
188  * will be NULL if the access fails.
189  */
190 
GXFOpen(const char * pszFilename)191 GXFHandle GXFOpen( const char * pszFilename )
192 
193 {
194     VSILFILE	*fp;
195     GXFInfo_t	*psGXF;
196     char	szTitle[71];
197     char	**papszList;
198     int     nHeaderCount = 0;
199 
200 /* -------------------------------------------------------------------- */
201 /*      We open in binary to ensure that we can efficiently seek()      */
202 /*      to any location when reading scanlines randomly.  If we         */
203 /*      opened as text we might still be able to seek(), but I          */
204 /*      believe that on Windows, the C library has to read through      */
205 /*      all the data to find the right spot taking into account DOS     */
206 /*      CRs.                                                            */
207 /* -------------------------------------------------------------------- */
208     fp = VSIFOpenL( pszFilename, "rb" );
209 
210     if( fp == NULL )
211     {
212         /* how to effectively communicate this error out? */
213         CPLError( CE_Failure, CPLE_OpenFailed,
214                   "Unable to open file: %s\n", pszFilename );
215         return NULL;
216     }
217 
218 /* -------------------------------------------------------------------- */
219 /*      Create the GXF Information object.                              */
220 /* -------------------------------------------------------------------- */
221     psGXF = (GXFInfo_t *) VSICalloc( sizeof(GXFInfo_t), 1 );
222     psGXF->fp = fp;
223     psGXF->dfTransformScale = 1.0;
224     psGXF->nSense = GXFS_LL_RIGHT;
225     psGXF->dfXPixelSize = 1.0;
226     psGXF->dfYPixelSize = 1.0;
227     psGXF->dfSetDummyTo = -1e12;
228 
229     psGXF->dfUnitToMeter = 1.0;
230     psGXF->pszTitle = VSIStrdup("");
231 
232 /* -------------------------------------------------------------------- */
233 /*      Read the header, one line at a time.                            */
234 /* -------------------------------------------------------------------- */
235     while( (papszList = GXFReadHeaderValue( fp, szTitle)) != NULL && nHeaderCount < MAX_HEADER_COUNT )
236     {
237         if( STARTS_WITH_CI(szTitle, "#TITL") )
238         {
239             CPLFree( psGXF->pszTitle );
240             psGXF->pszTitle = CPLStrdup( papszList[0] );
241         }
242         else if( STARTS_WITH_CI(szTitle, "#POIN") )
243         {
244             psGXF->nRawXSize = atoi(papszList[0]);
245         }
246         else if( STARTS_WITH_CI(szTitle, "#ROWS") )
247         {
248             psGXF->nRawYSize = atoi(papszList[0]);
249         }
250         else if( STARTS_WITH_CI(szTitle, "#PTSE") )
251         {
252             psGXF->dfXPixelSize = CPLAtof(papszList[0]);
253         }
254         else if( STARTS_WITH_CI(szTitle, "#RWSE") )
255         {
256             psGXF->dfYPixelSize = CPLAtof(papszList[0]);
257         }
258         else if( STARTS_WITH_CI(szTitle, "#DUMM") )
259         {
260             memset( psGXF->szDummy, 0, sizeof(psGXF->szDummy));
261             strncpy( psGXF->szDummy, papszList[0], sizeof(psGXF->szDummy) - 1);
262             psGXF->dfSetDummyTo = CPLAtof(papszList[0]);
263         }
264         else if( STARTS_WITH_CI(szTitle, "#XORI") )
265         {
266             psGXF->dfXOrigin = CPLAtof(papszList[0]);
267         }
268         else if( STARTS_WITH_CI(szTitle, "#YORI") )
269         {
270             psGXF->dfYOrigin = CPLAtof(papszList[0]);
271         }
272         else if( STARTS_WITH_CI(szTitle, "#ZMIN") )
273         {
274             psGXF->dfZMinimum = CPLAtof(papszList[0]);
275         }
276         else if( STARTS_WITH_CI(szTitle, "#ZMAX") )
277         {
278             psGXF->dfZMaximum = CPLAtof(papszList[0]);
279         }
280         else if( STARTS_WITH_CI(szTitle, "#SENS") )
281         {
282             psGXF->nSense = atoi(papszList[0]);
283         }
284         else if( STARTS_WITH_CI(szTitle,"#MAP_PROJECTION") &&
285                  psGXF->papszMapProjection == NULL )
286         {
287             psGXF->papszMapProjection = papszList;
288             papszList = NULL;
289         }
290         else if( STARTS_WITH_CI(szTitle,"#MAP_D") &&
291                  psGXF->papszMapDatumTransform == NULL  )
292         {
293             psGXF->papszMapDatumTransform = papszList;
294             papszList = NULL;
295         }
296         else if( STARTS_WITH_CI(szTitle, "#UNIT") &&
297                  psGXF->pszUnitName == NULL )
298         {
299             char	**papszFields;
300 
301             papszFields = CSLTokenizeStringComplex( papszList[0], ", ",
302                                                     TRUE, TRUE );
303 
304             if( CSLCount(papszFields) > 1 )
305             {
306                 psGXF->pszUnitName = VSIStrdup( papszFields[0] );
307                 psGXF->dfUnitToMeter = CPLAtof( papszFields[1] );
308                 if( psGXF->dfUnitToMeter == 0.0 )
309                     psGXF->dfUnitToMeter = 1.0;
310             }
311 
312             CSLDestroy( papszFields );
313         }
314         else if( STARTS_WITH_CI(szTitle, "#TRAN") &&
315                  psGXF->pszTransformName == NULL )
316         {
317             char	**papszFields;
318 
319             papszFields = CSLTokenizeStringComplex( papszList[0], ", ",
320                                                     TRUE, TRUE );
321 
322             if( CSLCount(papszFields) > 1 )
323             {
324                 psGXF->dfTransformScale = CPLAtof(papszFields[0]);
325                 psGXF->dfTransformOffset = CPLAtof(papszFields[1]);
326             }
327 
328             if( CSLCount(papszFields) > 2 )
329                 psGXF->pszTransformName = CPLStrdup( papszFields[2] );
330 
331             CSLDestroy( papszFields );
332         }
333         else if( STARTS_WITH_CI(szTitle,"#GTYPE") )
334         {
335             psGXF->nGType = atoi(papszList[0]);
336             if( psGXF->nGType < 0 || psGXF->nGType > 20 )
337             {
338                 CSLDestroy( papszList );
339                 GXFClose( psGXF );
340                 return NULL;
341             }
342         }
343 
344         CSLDestroy( papszList );
345         nHeaderCount ++;
346     }
347 
348     CSLDestroy( papszList );
349 
350 /* -------------------------------------------------------------------- */
351 /*      Did we find the #GRID?                                          */
352 /* -------------------------------------------------------------------- */
353     if( !STARTS_WITH_CI(szTitle, "#GRID") )
354     {
355         GXFClose( psGXF );
356         CPLError( CE_Failure, CPLE_WrongFormat,
357                   "Didn't parse through to #GRID successfully in.\n"
358                   "file `%s'.\n",
359                   pszFilename );
360 
361         return NULL;
362     }
363 
364 /* -------------------------------------------------------------------- */
365 /*      Allocate, and initialize the raw scanline offset array.         */
366 /* -------------------------------------------------------------------- */
367     if( psGXF->nRawYSize <= 0 || psGXF->nRawYSize >= INT_MAX )
368     {
369         GXFClose( psGXF );
370         return NULL;
371     }
372 
373     /* Avoid excessive memory allocation */
374     if( psGXF->nRawYSize >= 1000000 )
375     {
376         vsi_l_offset nCurOffset;
377         vsi_l_offset nFileSize;
378         nCurOffset = VSIFTellL( psGXF->fp );
379         VSIFSeekL( psGXF->fp, 0, SEEK_END );
380         nFileSize = VSIFTellL( psGXF->fp );
381         VSIFSeekL( psGXF->fp, nCurOffset, SEEK_SET );
382         if( (vsi_l_offset)psGXF->nRawYSize > nFileSize )
383         {
384             GXFClose( psGXF );
385             return NULL;
386         }
387     }
388 
389     psGXF->panRawLineOffset = (vsi_l_offset *)
390         VSICalloc( sizeof(vsi_l_offset), psGXF->nRawYSize+1 );
391     if( psGXF->panRawLineOffset == NULL )
392     {
393         GXFClose( psGXF );
394         return NULL;
395     }
396 
397     psGXF->panRawLineOffset[0] = VSIFTellL( psGXF->fp );
398 
399 /* -------------------------------------------------------------------- */
400 /*      Update the zmin/zmax values to take into account #TRANSFORM     */
401 /*      information.                                                    */
402 /* -------------------------------------------------------------------- */
403     if( psGXF->dfZMinimum != 0.0 || psGXF->dfZMaximum != 0.0 )
404     {
405         psGXF->dfZMinimum = (psGXF->dfZMinimum * psGXF->dfTransformScale)
406             			+ psGXF->dfTransformOffset;
407         psGXF->dfZMaximum = (psGXF->dfZMaximum * psGXF->dfTransformScale)
408             			+ psGXF->dfTransformOffset;
409     }
410 
411     return( (GXFHandle) psGXF );
412 }
413 
414 /************************************************************************/
415 /*                              GXFClose()                              */
416 /************************************************************************/
417 
418 /**
419  * Close GXF file opened with GXFOpen().
420  *
421  * @param hGXF handle to GXF file.
422  */
423 
GXFClose(GXFHandle hGXF)424 void GXFClose( GXFHandle hGXF )
425 
426 {
427     GXFInfo_t	*psGXF = (GXFInfo_t *) hGXF;
428 
429     CPLFree( psGXF->panRawLineOffset );
430     CPLFree( psGXF->pszUnitName );
431     CSLDestroy( psGXF->papszMapDatumTransform );
432     CSLDestroy( psGXF->papszMapProjection );
433     CPLFree( psGXF->pszTitle );
434     CPLFree( psGXF->pszTransformName );
435 
436     VSIFCloseL( psGXF->fp );
437 
438     CPLReadLineL( NULL );
439 
440     CPLFree( psGXF );
441 }
442 
443 /************************************************************************/
444 /*                           GXFParseBase90()                           */
445 /*                                                                      */
446 /*      Parse a base 90 number ... exceptions (repeat, and dummy)       */
447 /*      values have to be recognised outside this function.             */
448 /************************************************************************/
449 
450 CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
451 static
GXFParseBase90(GXFInfo_t * psGXF,const char * pszText,int bScale)452 double GXFParseBase90( GXFInfo_t * psGXF, const char * pszText,
453                        int bScale )
454 
455 {
456     int		i = 0;
457     unsigned int nValue = 0;
458 
459     while( i < psGXF->nGType )
460     {
461         nValue = nValue*90U + (unsigned)(pszText[i] - 37);
462         i++;
463     }
464 
465     if( bScale )
466         return( (nValue * psGXF->dfTransformScale) + psGXF->dfTransformOffset);
467     else
468         return( nValue );
469 }
470 
471 
472 /************************************************************************/
473 /*                       GXFReadRawScanlineFrom()                       */
474 /************************************************************************/
475 
GXFReadRawScanlineFrom(GXFInfo_t * psGXF,vsi_l_offset iOffset,vsi_l_offset * pnNewOffset,double * padfLineBuf)476 static CPLErr GXFReadRawScanlineFrom( GXFInfo_t * psGXF, vsi_l_offset iOffset,
477                                       vsi_l_offset * pnNewOffset, double * padfLineBuf )
478 
479 {
480     const char	*pszLine;
481     int		nValuesRead = 0, nValuesSought = psGXF->nRawXSize;
482 
483     if( VSIFSeekL( psGXF->fp, iOffset, SEEK_SET ) != 0 )
484         return CE_Failure;
485 
486     while( nValuesRead < nValuesSought )
487     {
488         pszLine = CPLReadLineL( psGXF->fp );
489         if( pszLine == NULL )
490             break;
491 
492 /* -------------------------------------------------------------------- */
493 /*      Uncompressed case.                                              */
494 /* -------------------------------------------------------------------- */
495         if( psGXF->nGType == 0 )
496         {
497             /* we could just tokenize the line, but that's pretty expensive.
498                Instead I will parse on white space ``by hand''. */
499             while( *pszLine != '\0' && nValuesRead < nValuesSought )
500             {
501                 int		i;
502 
503                 /* skip leading white space */
504                 for( ; isspace((unsigned char)*pszLine); pszLine++ ) {}
505 
506                 /* Skip the data value (non white space) */
507                 for( i = 0; pszLine[i] != '\0' && !isspace((unsigned char)pszLine[i]); i++) {}
508 
509                 if( strncmp(pszLine,psGXF->szDummy,i) == 0 )
510                 {
511                     padfLineBuf[nValuesRead++] = psGXF->dfSetDummyTo;
512                 }
513                 else
514                 {
515                     padfLineBuf[nValuesRead++] = CPLAtof(pszLine);
516                 }
517 
518                 /* skip further whitespace */
519                 for( pszLine += i; isspace((unsigned char)*pszLine); pszLine++ ) {}
520             }
521         }
522 
523 /* -------------------------------------------------------------------- */
524 /*      Compressed case.                                                */
525 /* -------------------------------------------------------------------- */
526         else
527         {
528             size_t nLineLenOri = strlen(pszLine);
529             int nLineLen = (int)nLineLenOri;
530 
531             while( *pszLine != '\0' && nValuesRead < nValuesSought )
532             {
533                 if( nLineLen < psGXF->nGType )
534                     return CE_Failure;
535 
536                 if( pszLine[0] == '!' )
537                 {
538                     padfLineBuf[nValuesRead++] = psGXF->dfSetDummyTo;
539                 }
540                 else if( pszLine[0] == '"' )
541                 {
542                     int		nCount, i;
543                     double	dfValue;
544 
545                     pszLine += psGXF->nGType;
546                     nLineLen -= psGXF->nGType;
547                     if( nLineLen < psGXF->nGType )
548                     {
549                         pszLine = CPLReadLineL( psGXF->fp );
550                         if( pszLine == NULL )
551                             return CE_Failure;
552                         nLineLenOri = strlen(pszLine);
553                         nLineLen = (int)nLineLenOri;
554                         if( nLineLen < psGXF->nGType )
555                             return CE_Failure;
556                     }
557 
558                     nCount = (int) GXFParseBase90( psGXF, pszLine, FALSE);
559                     pszLine += psGXF->nGType;
560                     nLineLen -= psGXF->nGType;
561 
562                     if( nLineLen < psGXF->nGType )
563                     {
564                         pszLine = CPLReadLineL( psGXF->fp );
565                         if( pszLine == NULL )
566                             return CE_Failure;
567                         nLineLenOri = strlen(pszLine);
568                         nLineLen = (int)nLineLenOri;
569                         if( nLineLen < psGXF->nGType )
570                             return CE_Failure;
571                     }
572 
573                     if( *pszLine == '!' )
574                         dfValue = psGXF->dfSetDummyTo;
575                     else
576                         dfValue = GXFParseBase90( psGXF, pszLine, TRUE );
577 
578                     if( nValuesRead + nCount > nValuesSought )
579                     {
580                         CPLError(CE_Failure, CPLE_AppDefined, "Wrong count value");
581                         return CE_Failure;
582                     }
583 
584                     for( i=0; i < nCount && nValuesRead < nValuesSought; i++ )
585                         padfLineBuf[nValuesRead++] = dfValue;
586                 }
587                 else
588                 {
589                     padfLineBuf[nValuesRead++] =
590                         GXFParseBase90( psGXF, pszLine, TRUE );
591                 }
592 
593                 pszLine += psGXF->nGType;
594                 nLineLen -= psGXF->nGType;
595             }
596         }
597     }
598 
599 /* -------------------------------------------------------------------- */
600 /*      Return the new offset, if requested.                            */
601 /* -------------------------------------------------------------------- */
602     if( pnNewOffset != NULL )
603     {
604         *pnNewOffset = VSIFTellL( psGXF->fp );
605     }
606 
607     return CE_None;
608 }
609 
610 /************************************************************************/
611 /*                           GXFGetScanline()                           */
612 /************************************************************************/
613 
614 /**
615  * Read a scanline of raster data from GXF file.
616  *
617  * This function operates similarly to GXFGetRawScanline(), but it
618  * attempts to mirror data horizontally or vertically based on the #SENSE
619  * flag to return data in a top to bottom, and left to right organization.
620  * If the file is organized in columns (#SENSE is GXFS_UR_DOWN, GXFS_UL_DOWN,
621  * GXFS_LR_UP, or GXFS_LL_UP) then this function will fail, returning
622  * CE_Failure, and reporting a sense error.
623  *
624  * See GXFGetRawScanline() for other notes.
625  *
626  * @param hGXF the GXF file handle, as returned from GXFOpen().
627  * @param iScanline the scanline to read, zero is the top scanline.
628  * @param padfLineBuf a buffer of doubles into which the scanline pixel
629  * values are read.  This must be at least as long as a scanline.
630  *
631  * @return CE_None if access succeeds or CE_Failure if something goes wrong.
632  */
633 
GXFGetScanline(GXFHandle hGXF,int iScanline,double * padfLineBuf)634 CPLErr GXFGetScanline( GXFHandle hGXF, int iScanline, double * padfLineBuf )
635 
636 {
637     GXFInfo_t	*psGXF = (GXFInfo_t *) hGXF;
638     CPLErr	nErr;
639     int		iRawScanline;
640 
641     if( psGXF->nSense == GXFS_LL_RIGHT
642         || psGXF->nSense == GXFS_LR_LEFT )
643     {
644         iRawScanline = psGXF->nRawYSize - iScanline - 1;
645     }
646 
647     else if( psGXF->nSense == GXFS_UL_RIGHT
648              || psGXF->nSense == GXFS_UR_LEFT )
649     {
650         iRawScanline = iScanline;
651     }
652     else
653     {
654         CPLError( CE_Failure, CPLE_AppDefined,
655                   "Unable to support vertically oriented images." );
656         return( CE_Failure );
657     }
658 
659     nErr = GXFGetRawScanline( hGXF, iRawScanline, padfLineBuf );
660 
661     if( nErr == CE_None
662         && (psGXF->nSense == GXFS_LR_LEFT || psGXF->nSense == GXFS_UR_LEFT) )
663     {
664         int	i;
665         double	dfTemp;
666 
667         for( i = psGXF->nRawXSize / 2 - 1; i >= 0; i-- )
668         {
669             dfTemp = padfLineBuf[i];
670             padfLineBuf[i] = padfLineBuf[psGXF->nRawXSize-i-1];
671             padfLineBuf[psGXF->nRawXSize-i-1] = dfTemp;
672         }
673     }
674 
675     return( nErr );
676 }
677 
678 /************************************************************************/
679 /*                         GXFGetRawScanline()                          */
680 /************************************************************************/
681 
682 /**
683  * Read a scanline of raster data from GXF file.
684  *
685  * This function will read a row of data from the GXF file.  It is "Raw"
686  * in the sense that it doesn't attempt to account for the #SENSE flag as
687  * the GXFGetScanline() function does.  Unlike GXFGetScanline(), this function
688  * supports column organized files.
689  *
690  * Any dummy pixels are assigned the dummy value indicated by GXFGetRawInfo().
691  *
692  * @param hGXF the GXF file handle, as returned from GXFOpen().
693  * @param iScanline the scanline to read, zero is the first scanline in the
694  * file.
695  * @param padfLineBuf a buffer of doubles into which the scanline pixel
696  * values are read.  This must be at least as long as a scanline.
697  *
698  * @return CE_None if access succeeds or CE_Failure if something goes wrong.
699  */
700 
GXFGetRawScanline(GXFHandle hGXF,int iScanline,double * padfLineBuf)701 CPLErr GXFGetRawScanline( GXFHandle hGXF, int iScanline, double * padfLineBuf )
702 
703 {
704     GXFInfo_t	*psGXF = (GXFInfo_t *) hGXF;
705     CPLErr	eErr;
706 
707 /* -------------------------------------------------------------------- */
708 /*      Validate scanline.                                              */
709 /* -------------------------------------------------------------------- */
710     if( iScanline < 0 || iScanline >= psGXF->nRawYSize )
711     {
712         CPLError( CE_Failure, CPLE_IllegalArg,
713                   "GXFGetRawScanline(): Scanline `%d' does not exist.\n",
714                   iScanline );
715         return CE_Failure;
716     }
717 
718 /* -------------------------------------------------------------------- */
719 /*      If we don't have the requested scanline, fetch preceding        */
720 /*      scanlines to find the pointer to this scanline.                 */
721 /* -------------------------------------------------------------------- */
722     if( psGXF->panRawLineOffset[iScanline] == 0 )
723     {
724         int		i;
725 
726         CPLAssert( iScanline > 0 );
727 
728         for( i = 0; i < iScanline; i++ )
729         {
730             if( psGXF->panRawLineOffset[i+1] == 0 )
731             {
732                 eErr = GXFGetRawScanline( hGXF, i, padfLineBuf );
733                 if( eErr != CE_None )
734                     return( eErr );
735             }
736         }
737     }
738 
739 /* -------------------------------------------------------------------- */
740 /*      Get this scanline, and update the offset for the next line.     */
741 /* -------------------------------------------------------------------- */
742     eErr =
743         GXFReadRawScanlineFrom( psGXF, psGXF->panRawLineOffset[iScanline],
744                                 psGXF->panRawLineOffset+iScanline+1,
745                                 padfLineBuf );
746 
747     return eErr;
748 }
749 
750 /************************************************************************/
751 /*                         GXFScanForZMinMax()                          */
752 /*                                                                      */
753 /*      The header doesn't contain the ZMin/ZMax values, but the        */
754 /*      application has requested it ... scan the entire image for      */
755 /*      it.                                                             */
756 /************************************************************************/
757 
GXFScanForZMinMax(GXFHandle hGXF)758 static void GXFScanForZMinMax( GXFHandle hGXF )
759 
760 {
761     GXFInfo_t	*psGXF = (GXFInfo_t *) hGXF;
762     int		iLine, iPixel;
763     double	*padfScanline;
764 
765     padfScanline = (double *) VSICalloc(sizeof(double),psGXF->nRawXSize);
766     if( padfScanline == NULL )
767         return;
768 
769     psGXF->dfZMinimum = 1e50;
770     psGXF->dfZMaximum = -1e50;
771 
772     for( iLine = 0; iLine < psGXF->nRawYSize; iLine++ )
773     {
774         if( GXFGetRawScanline( hGXF, iLine, padfScanline ) != CE_None )
775             break;
776 
777         for( iPixel = 0; iPixel < psGXF->nRawXSize; iPixel++ )
778         {
779             if( padfScanline[iPixel] != psGXF->dfSetDummyTo )
780             {
781                 psGXF->dfZMinimum =
782                     MIN(psGXF->dfZMinimum,padfScanline[iPixel]);
783                 psGXF->dfZMaximum =
784                     MAX(psGXF->dfZMaximum,padfScanline[iPixel]);
785             }
786         }
787     }
788 
789     VSIFree( padfScanline );
790 
791 /* -------------------------------------------------------------------- */
792 /*      Did we get any real data points?                                */
793 /* -------------------------------------------------------------------- */
794     if( psGXF->dfZMinimum > psGXF->dfZMaximum )
795     {
796         psGXF->dfZMinimum = 0.0;
797         psGXF->dfZMaximum = 0.0;
798     }
799 }
800 
801 /************************************************************************/
802 /*                             GXFGetRawInfo()                          */
803 /************************************************************************/
804 
805 /**
806  * Fetch header information about a GXF file.
807  *
808  * Note that the X and Y sizes are of the raw raster and don't take into
809  * account the #SENSE flag.  If the file is column oriented (rows in the
810  * files are actually columns in the raster) these values would need to be
811  * transposed for the actual raster.
812  *
813  * The legal pnSense values are:
814  * <ul>
815  * <li> GXFS_LL_UP(-1): lower left origin, scanning up.
816  * <li> GXFS_LL_RIGHT(1): lower left origin, scanning right.
817  * <li> GXFS_UL_RIGHT(-2): upper left origin, scanning right.
818  * <li> GXFS_UL_DOWN(2): upper left origin, scanning down.
819  * <li> GXFS_UR_DOWN(-3): upper right origin, scanning down.
820  * <li> GXFS_UR_LEFT(3): upper right origin, scanning left.
821  * <li> GXFS_LR_LEFT(-4): lower right origin, scanning left.
822  * <li> GXFS_LR_UP(4): lower right origin, scanning up.
823  * </ul>
824  *
825  * Note that the GXFGetScanline() function attempts to provide a GXFS_UL_RIGHT
826  * view onto files, but doesn't handle the *_DOWN and *_UP oriented files.
827  *
828  * The Z min and max values may not occur in the GXF header.  If they are
829  * requested, and aren't available in the header the entire file is scanned
830  * in order to establish them.  This can be expensive.
831  *
832  * If no #DUMMY value was specified in the file, a default of -1e12 is used.
833  *
834  * @param hGXF handle to GXF file returned by GXFOpen().
835  * @param pnXSize int to be set with the width of the raw raster.  May be NULL.
836  * @param pnYSize int to be set with the height of the raw raster. May be NULL.
837  * @param pnSense int to set with #SENSE flag, may be NULL.
838  * @param pdfZMin double to set with minimum raster value, may be NULL.
839  * @param pdfZMax double to set with minimum raster value, may be NULL.
840  * @param pdfDummy double to set with dummy (nodata / invalid data) pixel
841  * value.
842  */
843 
GXFGetRawInfo(GXFHandle hGXF,int * pnXSize,int * pnYSize,int * pnSense,double * pdfZMin,double * pdfZMax,double * pdfDummy)844 CPLErr GXFGetRawInfo( GXFHandle hGXF, int *pnXSize, int *pnYSize,
845                       int * pnSense, double * pdfZMin, double * pdfZMax,
846                       double * pdfDummy )
847 
848 {
849     GXFInfo_t	*psGXF = (GXFInfo_t *) hGXF;
850 
851     if( pnXSize != NULL )
852         *pnXSize = psGXF->nRawXSize;
853 
854     if( pnYSize != NULL )
855         *pnYSize = psGXF->nRawYSize;
856 
857     if( pnSense != NULL )
858         *pnSense = psGXF->nSense;
859 
860     if( (pdfZMin != NULL || pdfZMax != NULL)
861         && psGXF->dfZMinimum == 0.0 && psGXF->dfZMaximum == 0.0 )
862     {
863         GXFScanForZMinMax( hGXF );
864     }
865 
866     if( pdfZMin != NULL )
867         *pdfZMin = psGXF->dfZMinimum;
868 
869     if( pdfZMax != NULL )
870         *pdfZMax = psGXF->dfZMaximum;
871 
872     if( pdfDummy != NULL )
873         *pdfDummy = psGXF->dfSetDummyTo;
874 
875     return( CE_None );
876 }
877 
878 /************************************************************************/
879 /*                        GXFGetMapProjection()                         */
880 /************************************************************************/
881 
882 /**
883  * Return the lines related to the map projection.  It is up to
884  * the caller to parse them and interpret.  The return result
885  * will be NULL if no #MAP_PROJECTION line was found in the header.
886  *
887  * @param hGXF the GXF file handle.
888  *
889  * @return a NULL terminated array of string pointers containing the
890  * projection, or NULL.  The strings remained owned by the GXF API, and
891  * should not be modified or freed by the caller.
892  */
893 
GXFGetMapProjection(GXFHandle hGXF)894 char **GXFGetMapProjection( GXFHandle hGXF )
895 
896 {
897     return( ((GXFInfo_t *) hGXF)->papszMapProjection );
898 }
899 
900 /************************************************************************/
901 /*                      GXFGetMapDatumTransform()                       */
902 /************************************************************************/
903 
904 /**
905  * Return the lines related to the datum transformation.  It is up to
906  * the caller to parse them and interpret.  The return result
907  * will be NULL if no #MAP_DATUM_TRANSFORM line was found in the header.
908  *
909  * @param hGXF the GXF file handle.
910  *
911  * @return a NULL terminated array of string pointers containing the
912  * datum, or NULL.  The strings remained owned by the GXF API, and
913  * should not be modified or freed by the caller.
914  */
915 
GXFGetMapDatumTransform(GXFHandle hGXF)916 char **GXFGetMapDatumTransform( GXFHandle hGXF )
917 
918 {
919     return( ((GXFInfo_t *) hGXF)->papszMapDatumTransform );
920 }
921 
922 /************************************************************************/
923 /*                         GXFGetRawPosition()                          */
924 /************************************************************************/
925 
926 /**
927  * Get the raw grid positioning information.
928  *
929  * Note that these coordinates refer to the raw grid, and are in the units
930  * specified by the #UNITS field.  See GXFGetPosition() for a similar
931  * function that takes into account the #SENSE values similarly to
932  * GXFGetScanline().
933  *
934  * Note that the pixel values are considered to be point values in GXF,
935  * and thus the origin is for the first point.  If you consider the pixels
936  * to be areas, then the origin is for the center of the origin pixel, not
937  * the outer corner.
938  *
939  * @param hGXF the GXF file handle.
940  * @param pdfXOrigin X position of the origin in the base coordinate system.
941  * @param pdfYOrigin Y position of the origin in the base coordinate system.
942  * @param pdfXPixelSize X pixel size in base coordinates.
943  * @param pdfYPixelSize Y pixel size in base coordinates.
944  * @param pdfRotation rotation in degrees counter-clockwise from the
945  * base coordinate system.
946  *
947  * @return Returns CE_None if successful, or CE_Failure if no posiitioning
948  * information was found in the file.
949  */
950 
951 
GXFGetRawPosition(GXFHandle hGXF,double * pdfXOrigin,double * pdfYOrigin,double * pdfXPixelSize,double * pdfYPixelSize,double * pdfRotation)952 CPLErr GXFGetRawPosition( GXFHandle hGXF,
953                           double * pdfXOrigin, double * pdfYOrigin,
954                           double * pdfXPixelSize, double * pdfYPixelSize,
955                           double * pdfRotation )
956 
957 {
958     GXFInfo_t	*psGXF = (GXFInfo_t *) hGXF;
959 
960     if( pdfXOrigin != NULL )
961         *pdfXOrigin = psGXF->dfXOrigin;
962     if( pdfYOrigin != NULL )
963         *pdfYOrigin = psGXF->dfYOrigin;
964     if( pdfXPixelSize != NULL )
965         *pdfXPixelSize = psGXF->dfXPixelSize;
966     if( pdfYPixelSize != NULL )
967         *pdfYPixelSize = psGXF->dfYPixelSize;
968     if( pdfRotation != NULL )
969         *pdfRotation = psGXF->dfRotation;
970 
971     if( psGXF->dfXOrigin == 0.0 && psGXF->dfYOrigin == 0.0
972         && psGXF->dfXPixelSize == 0.0 && psGXF->dfYPixelSize == 0.0 )
973         return( CE_Failure );
974     else
975         return( CE_None );
976 }
977 
978 
979 /************************************************************************/
980 /*                           GXFGetPosition()                           */
981 /************************************************************************/
982 
983 /**
984  * Get the grid positioning information.
985  *
986  * Note that these coordinates refer to the grid positioning after taking
987  * into account the #SENSE flag (as is done by the GXFGetScanline()) function.
988  *
989  * Note that the pixel values are considered to be point values in GXF,
990  * and thus the origin is for the first point.  If you consider the pixels
991  * to be areas, then the origin is for the center of the origin pixel, not
992  * the outer corner.
993  *
994  * This function does not support vertically oriented images, nor does it
995  * properly transform rotation for images with a SENSE other than
996  * GXFS_UL_RIGHT.
997  *
998  * @param hGXF the GXF file handle.
999  * @param pdfXOrigin X position of the origin in the base coordinate system.
1000  * @param pdfYOrigin Y position of the origin in the base coordinate system.
1001  * @param pdfXPixelSize X pixel size in base coordinates.
1002  * @param pdfYPixelSize Y pixel size in base coordinates.
1003  * @param pdfRotation rotation in degrees counter-clockwise from the
1004  * base coordinate system.
1005  *
1006  * @return Returns CE_None if successful, or CE_Failure if no posiitioning
1007  * information was found in the file.
1008  */
1009 
1010 
GXFGetPosition(GXFHandle hGXF,double * pdfXOrigin,double * pdfYOrigin,double * pdfXPixelSize,double * pdfYPixelSize,double * pdfRotation)1011 CPLErr GXFGetPosition( GXFHandle hGXF,
1012                        double * pdfXOrigin, double * pdfYOrigin,
1013                        double * pdfXPixelSize, double * pdfYPixelSize,
1014                        double * pdfRotation )
1015 
1016 {
1017     GXFInfo_t	*psGXF = (GXFInfo_t *) hGXF;
1018     double	dfCXOrigin, dfCYOrigin, dfCXPixelSize, dfCYPixelSize;
1019 
1020     switch( psGXF->nSense )
1021     {
1022       case GXFS_UL_RIGHT:
1023         dfCXOrigin = psGXF->dfXOrigin;
1024         dfCYOrigin = psGXF->dfYOrigin;
1025         dfCXPixelSize = psGXF->dfXPixelSize;
1026         dfCYPixelSize = psGXF->dfYPixelSize;
1027         break;
1028 
1029       case GXFS_UR_LEFT:
1030         dfCXOrigin = psGXF->dfXOrigin
1031             	     - (psGXF->nRawXSize-1) * psGXF->dfXPixelSize;
1032         dfCYOrigin = psGXF->dfYOrigin;
1033         dfCXPixelSize = psGXF->dfXPixelSize;
1034         dfCYPixelSize = psGXF->dfYPixelSize;
1035         break;
1036 
1037       case GXFS_LL_RIGHT:
1038         dfCXOrigin = psGXF->dfXOrigin;
1039         dfCYOrigin = psGXF->dfYOrigin
1040                      + (psGXF->nRawYSize-1) * psGXF->dfYPixelSize;
1041         dfCXPixelSize = psGXF->dfXPixelSize;
1042         dfCYPixelSize = psGXF->dfYPixelSize;
1043         break;
1044 
1045       case GXFS_LR_LEFT:
1046         dfCXOrigin = psGXF->dfXOrigin
1047             	     - (psGXF->nRawXSize-1) * psGXF->dfXPixelSize;
1048         dfCYOrigin = psGXF->dfYOrigin
1049                      + (psGXF->nRawYSize-1) * psGXF->dfYPixelSize;
1050         dfCXPixelSize = psGXF->dfXPixelSize;
1051         dfCYPixelSize = psGXF->dfYPixelSize;
1052         break;
1053 
1054       default:
1055         CPLError( CE_Failure, CPLE_AppDefined,
1056            "GXFGetPosition() doesn't support vertically organized images." );
1057         return CE_Failure;
1058     }
1059 
1060     if( pdfXOrigin != NULL )
1061         *pdfXOrigin = dfCXOrigin;
1062     if( pdfYOrigin != NULL )
1063         *pdfYOrigin = dfCYOrigin;
1064     if( pdfXPixelSize != NULL )
1065         *pdfXPixelSize = dfCXPixelSize;
1066     if( pdfYPixelSize != NULL )
1067         *pdfYPixelSize = dfCYPixelSize;
1068     if( pdfRotation != NULL )
1069         *pdfRotation = psGXF->dfRotation;
1070 
1071     if( psGXF->dfXOrigin == 0.0 && psGXF->dfYOrigin == 0.0
1072         && psGXF->dfXPixelSize == 0.0 && psGXF->dfYPixelSize == 0.0 )
1073         return( CE_Failure );
1074     else
1075         return( CE_None );
1076 }
1077