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