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