LCOV - code coverage report
Current view: top level - frmts/gxf - gxfopen.c (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 210 382 55.0 %
Date: 2024-11-25 13:07:18 Functions: 8 13 61.5 %

          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             : }

Generated by: LCOV version 1.14