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