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