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