LCOV - code coverage report
Current view: top level - frmts/usgsdem - usgsdemdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 345 405 85.2 %
Date: 2026-09-18 08:43:21 Functions: 18 20 90.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  USGS DEM Driver
       4             :  * Purpose:  All reader for USGS DEM Reader
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  * Portions of this module derived from the VTP USGS DEM driver by Ben
       8             :  * Discoe, see http://www.vterrain.org
       9             :  *
      10             :  ******************************************************************************
      11             :  * Copyright (c) 2001, Frank Warmerdam <warmerdam@pobox.com>
      12             :  * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
      13             :  *
      14             :  * SPDX-License-Identifier: MIT
      15             :  ****************************************************************************/
      16             : 
      17             : #include "gdal_frmts.h"
      18             : #include "gdal_pam.h"
      19             : #include "gdal_driver.h"
      20             : #include "gdal_drivermanager.h"
      21             : #include "gdal_openinfo.h"
      22             : #include "gdal_cpp_functions.h"
      23             : #include "ogr_spatialref.h"
      24             : 
      25             : #include <algorithm>
      26             : #include <cmath>
      27             : 
      28             : typedef struct
      29             : {
      30             :     double x;
      31             :     double y;
      32             : } DPoint2;
      33             : 
      34             : constexpr int USGSDEM_NODATA = -32767;
      35             : 
      36             : GDALDataset *USGSDEMCreateCopy(const char *, GDALDataset *, int, char **,
      37             :                                GDALProgressFunc pfnProgress,
      38             :                                void *pProgressData);
      39             : 
      40             : /************************************************************************/
      41             : /*                              ReadInt()                               */
      42             : /************************************************************************/
      43             : 
      44         248 : static int ReadInt(VSILFILE *fp)
      45             : {
      46             :     char c;
      47         248 :     int nRead = 0;
      48             :     char szBuffer[12];
      49         248 :     bool bInProlog = true;
      50             : 
      51             :     while (true)
      52             :     {
      53        2480 :         if (VSIFReadL(&c, 1, 1, fp) != 1)
      54             :         {
      55           4 :             return 0;
      56             :         }
      57        2476 :         if (bInProlog)
      58             :         {
      59        2152 :             if (!isspace(static_cast<unsigned char>(c)))
      60             :             {
      61         244 :                 bInProlog = false;
      62             :             }
      63             :         }
      64        2476 :         if (!bInProlog)
      65             :         {
      66         568 :             if (c != '-' && c != '+' && !(c >= '0' && c <= '9'))
      67             :             {
      68         244 :                 CPL_IGNORE_RET_VAL(VSIFSeekL(fp, VSIFTellL(fp) - 1, SEEK_SET));
      69         244 :                 break;
      70             :             }
      71         324 :             if (nRead < 11)
      72         324 :                 szBuffer[nRead] = c;
      73         324 :             nRead++;
      74             :         }
      75             :     }
      76         244 :     szBuffer[std::min(nRead, 11)] = 0;
      77         244 :     return atoi(szBuffer);
      78             : }
      79             : 
      80             : typedef struct
      81             : {
      82             :     VSILFILE *fp;
      83             :     int max_size;
      84             :     char *buffer;
      85             :     int buffer_size;
      86             :     int cur_index;
      87             : } Buffer;
      88             : 
      89             : /************************************************************************/
      90             : /*                        USGSDEMRefillBuffer()                         */
      91             : /************************************************************************/
      92             : 
      93          75 : static void USGSDEMRefillBuffer(Buffer *psBuffer)
      94             : {
      95          75 :     memmove(psBuffer->buffer, psBuffer->buffer + psBuffer->cur_index,
      96          75 :             psBuffer->buffer_size - psBuffer->cur_index);
      97             : 
      98          75 :     psBuffer->buffer_size -= psBuffer->cur_index;
      99          75 :     psBuffer->buffer_size += static_cast<int>(
     100         150 :         VSIFReadL(psBuffer->buffer + psBuffer->buffer_size, 1,
     101          75 :                   psBuffer->max_size - psBuffer->buffer_size, psBuffer->fp));
     102          75 :     psBuffer->cur_index = 0;
     103          75 : }
     104             : 
     105             : /************************************************************************/
     106             : /*                      USGSDEMGetCurrentFilePos()                      */
     107             : /************************************************************************/
     108             : 
     109          10 : static vsi_l_offset USGSDEMGetCurrentFilePos(const Buffer *psBuffer)
     110             : {
     111          10 :     return VSIFTellL(psBuffer->fp) - psBuffer->buffer_size +
     112          10 :            psBuffer->cur_index;
     113             : }
     114             : 
     115             : /************************************************************************/
     116             : /*                      USGSDEMSetCurrentFilePos()                      */
     117             : /************************************************************************/
     118             : 
     119          10 : static void USGSDEMSetCurrentFilePos(Buffer *psBuffer, vsi_l_offset nNewPos)
     120             : {
     121          10 :     vsi_l_offset nCurPosFP = VSIFTellL(psBuffer->fp);
     122          10 :     if (nNewPos >= nCurPosFP - psBuffer->buffer_size && nNewPos < nCurPosFP)
     123             :     {
     124           5 :         psBuffer->cur_index =
     125           5 :             static_cast<int>(nNewPos - (nCurPosFP - psBuffer->buffer_size));
     126             :     }
     127             :     else
     128             :     {
     129           5 :         CPL_IGNORE_RET_VAL(VSIFSeekL(psBuffer->fp, nNewPos, SEEK_SET));
     130           5 :         psBuffer->buffer_size = 0;
     131           5 :         psBuffer->cur_index = 0;
     132             :     }
     133          10 : }
     134             : 
     135             : /************************************************************************/
     136             : /*                      USGSDEMReadIntFromBuffer()                      */
     137             : /************************************************************************/
     138             : 
     139     1863830 : static int USGSDEMReadIntFromBuffer(Buffer *psBuffer, int *pbSuccess = nullptr)
     140             : {
     141             :     char c;
     142             : 
     143             :     while (true)
     144             :     {
     145     1863830 :         if (psBuffer->cur_index >= psBuffer->buffer_size)
     146             :         {
     147          67 :             USGSDEMRefillBuffer(psBuffer);
     148          67 :             if (psBuffer->cur_index >= psBuffer->buffer_size)
     149             :             {
     150           0 :                 if (pbSuccess)
     151           0 :                     *pbSuccess = FALSE;
     152           0 :                 return 0;
     153             :             }
     154             :         }
     155             : 
     156     1863830 :         c = psBuffer->buffer[psBuffer->cur_index];
     157     1863830 :         psBuffer->cur_index++;
     158     1863830 :         if (!isspace(static_cast<unsigned char>(c)))
     159       20595 :             break;
     160             :     }
     161             : 
     162       20595 :     GIntBig nVal = 0;
     163       20595 :     int nSign = 1;
     164       20595 :     if (c == '-')
     165        4944 :         nSign = -1;
     166       15651 :     else if (c == '+')
     167           0 :         nSign = 1;
     168       15651 :     else if (c >= '0' && c <= '9')
     169       15651 :         nVal = c - '0';
     170             :     else
     171             :     {
     172           0 :         if (pbSuccess)
     173           0 :             *pbSuccess = FALSE;
     174           0 :         return 0;
     175             :     }
     176             : 
     177             :     while (true)
     178             :     {
     179       59465 :         if (psBuffer->cur_index >= psBuffer->buffer_size)
     180             :         {
     181           0 :             USGSDEMRefillBuffer(psBuffer);
     182           0 :             if (psBuffer->cur_index >= psBuffer->buffer_size)
     183             :             {
     184           0 :                 if (pbSuccess)
     185           0 :                     *pbSuccess = TRUE;
     186           0 :                 return static_cast<int>(nSign * nVal);
     187             :             }
     188             :         }
     189             : 
     190       59465 :         c = psBuffer->buffer[psBuffer->cur_index];
     191       59465 :         if (c >= '0' && c <= '9')
     192             :         {
     193       38870 :             psBuffer->cur_index++;
     194       38870 :             if (nVal * nSign < INT_MAX && nVal * nSign > INT_MIN)
     195             :             {
     196       38870 :                 nVal = nVal * 10 + (c - '0');
     197       38870 :                 if (nVal * nSign > INT_MAX)
     198             :                 {
     199           0 :                     nVal = INT_MAX;
     200           0 :                     nSign = 1;
     201             :                 }
     202       38870 :                 else if (nVal * nSign < INT_MIN)
     203             :                 {
     204           0 :                     nVal = INT_MIN;
     205           0 :                     nSign = 1;
     206             :                 }
     207             :             }
     208             :         }
     209             :         else
     210             :         {
     211       20595 :             if (pbSuccess)
     212       20595 :                 *pbSuccess = TRUE;
     213       20595 :             return static_cast<int>(nSign * nVal);
     214             :         }
     215             :     }
     216             : }
     217             : 
     218             : /************************************************************************/
     219             : /*                    USGSDEMReadDoubleFromBuffer()                     */
     220             : /************************************************************************/
     221             : 
     222       10304 : static double USGSDEMReadDoubleFromBuffer(Buffer *psBuffer, int nCharCount,
     223             :                                           int *pbSuccess = nullptr)
     224             : 
     225             : {
     226       10304 :     if (psBuffer->cur_index + nCharCount > psBuffer->buffer_size)
     227             :     {
     228           8 :         USGSDEMRefillBuffer(psBuffer);
     229           8 :         if (psBuffer->cur_index + nCharCount > psBuffer->buffer_size)
     230             :         {
     231           1 :             if (pbSuccess)
     232           1 :                 *pbSuccess = FALSE;
     233           1 :             return 0;
     234             :         }
     235             :     }
     236             : 
     237       10303 :     char *szPtr = psBuffer->buffer + psBuffer->cur_index;
     238       10303 :     char backupC = szPtr[nCharCount];
     239       10303 :     szPtr[nCharCount] = 0;
     240      257575 :     for (int i = 0; i < nCharCount; i++)
     241             :     {
     242      247272 :         if (szPtr[i] == 'D')
     243       10284 :             szPtr[i] = 'E';
     244             :     }
     245             : 
     246       10303 :     double dfVal = CPLAtof(szPtr);
     247       10303 :     szPtr[nCharCount] = backupC;
     248       10303 :     psBuffer->cur_index += nCharCount;
     249             : 
     250       10303 :     if (pbSuccess)
     251       10303 :         *pbSuccess = TRUE;
     252       10303 :     return dfVal;
     253             : }
     254             : 
     255             : /************************************************************************/
     256             : /*                              DConvert()                              */
     257             : /************************************************************************/
     258             : 
     259         274 : static double DConvert(VSILFILE *fp, int nCharCount)
     260             : 
     261             : {
     262             :     char szBuffer[100];
     263             : 
     264         274 :     CPL_IGNORE_RET_VAL(VSIFReadL(szBuffer, nCharCount, 1, fp));
     265         274 :     szBuffer[nCharCount] = '\0';
     266             : 
     267        7090 :     for (int i = 0; i < nCharCount; i++)
     268             :     {
     269        6816 :         if (szBuffer[i] == 'D')
     270         208 :             szBuffer[i] = 'E';
     271             :     }
     272             : 
     273         548 :     return CPLAtof(szBuffer);
     274             : }
     275             : 
     276             : /************************************************************************/
     277             : /* ==================================================================== */
     278             : /*                              USGSDEMDataset                          */
     279             : /* ==================================================================== */
     280             : /************************************************************************/
     281             : 
     282             : class USGSDEMRasterBand;
     283             : 
     284             : class USGSDEMDataset final : public GDALPamDataset
     285             : {
     286             :     friend class USGSDEMRasterBand;
     287             : 
     288             :     vsi_l_offset nDataStartOffset;
     289             :     GDALDataType eNaturalDataFormat;
     290             : 
     291             :     GDALGeoTransform m_gt{};
     292             :     OGRSpatialReference m_oSRS{};
     293             : 
     294             :     double fVRes;
     295             : 
     296             :     const char *pszUnits;
     297             : 
     298             :     int LoadFromFile(VSILFILE *);
     299             : 
     300             :     VSILFILE *fp;
     301             : 
     302             :   public:
     303             :     USGSDEMDataset();
     304             :     ~USGSDEMDataset() override;
     305             : 
     306             :     static int Identify(GDALOpenInfo *);
     307             :     static GDALDataset *Open(GDALOpenInfo *);
     308             :     CPLErr GetGeoTransform(GDALGeoTransform &gt) const override;
     309             :     const OGRSpatialReference *GetSpatialRef() const override;
     310             : };
     311             : 
     312             : /************************************************************************/
     313             : /* ==================================================================== */
     314             : /*                            USGSDEMRasterBand                         */
     315             : /* ==================================================================== */
     316             : /************************************************************************/
     317             : 
     318             : class USGSDEMRasterBand final : public GDALPamRasterBand
     319             : {
     320             :     friend class USGSDEMDataset;
     321             : 
     322             :   public:
     323             :     explicit USGSDEMRasterBand(USGSDEMDataset *);
     324             : 
     325             :     const char *GetUnitType() override;
     326             :     double GetNoDataValue(int *pbSuccess = nullptr) override;
     327             :     CPLErr IReadBlock(int, int, void *) override;
     328             : };
     329             : 
     330             : /************************************************************************/
     331             : /*                         USGSDEMRasterBand()                          */
     332             : /************************************************************************/
     333             : 
     334          20 : USGSDEMRasterBand::USGSDEMRasterBand(USGSDEMDataset *poDSIn)
     335             : 
     336             : {
     337          20 :     this->poDS = poDSIn;
     338          20 :     this->nBand = 1;
     339             : 
     340          20 :     eDataType = poDSIn->eNaturalDataFormat;
     341             : 
     342          20 :     nBlockXSize = poDSIn->GetRasterXSize();
     343          20 :     nBlockYSize = poDSIn->GetRasterYSize();
     344          20 : }
     345             : 
     346             : /************************************************************************/
     347             : /*                             IReadBlock()                             */
     348             : /************************************************************************/
     349             : 
     350          10 : CPLErr USGSDEMRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff,
     351             :                                      CPL_UNUSED int nBlockYOff, void *pImage)
     352             : 
     353             : {
     354             :     /* int bad = FALSE; */
     355          10 :     USGSDEMDataset *poGDS = cpl::down_cast<USGSDEMDataset *>(poDS);
     356             : 
     357             :     /* -------------------------------------------------------------------- */
     358             :     /*      Initialize image buffer to nodata value.                        */
     359             :     /* -------------------------------------------------------------------- */
     360          10 :     GDALCopyWords(&USGSDEM_NODATA, GDT_Int32, 0, pImage, GetRasterDataType(),
     361             :                   GDALGetDataTypeSizeBytes(GetRasterDataType()),
     362          10 :                   GetXSize() * GetYSize());
     363             : 
     364             :     /* -------------------------------------------------------------------- */
     365             :     /*      Seek to data.                                                   */
     366             :     /* -------------------------------------------------------------------- */
     367          10 :     CPL_IGNORE_RET_VAL(VSIFSeekL(poGDS->fp, poGDS->nDataStartOffset, 0));
     368             : 
     369          10 :     double dfYMin = poGDS->m_gt.yorig + (GetYSize() - 0.5) * poGDS->m_gt.yscale;
     370             : 
     371             :     /* -------------------------------------------------------------------- */
     372             :     /*      Read all the profiles into the image buffer.                    */
     373             :     /* -------------------------------------------------------------------- */
     374             : 
     375             :     Buffer sBuffer;
     376          10 :     sBuffer.max_size = 32768;
     377          10 :     sBuffer.buffer = static_cast<char *>(CPLMalloc(sBuffer.max_size + 1));
     378          10 :     sBuffer.fp = poGDS->fp;
     379          10 :     sBuffer.buffer_size = 0;
     380          10 :     sBuffer.cur_index = 0;
     381             : 
     382        2070 :     for (int i = 0; i < GetXSize(); i++)
     383             :     {
     384             :         int bSuccess;
     385        2061 :         const int nRowNumber = USGSDEMReadIntFromBuffer(&sBuffer, &bSuccess);
     386        2061 :         if (nRowNumber != 1)
     387           1 :             CPLDebug("USGSDEM", "i = %d, nRowNumber = %d", i, nRowNumber);
     388        2061 :         if (bSuccess)
     389             :         {
     390             :             const int nColNumber =
     391        2061 :                 USGSDEMReadIntFromBuffer(&sBuffer, &bSuccess);
     392        2061 :             if (nColNumber != i + 1)
     393             :             {
     394           3 :                 CPLDebug("USGSDEM", "i = %d, nColNumber = %d", i, nColNumber);
     395             :             }
     396             :         }
     397             :         const int nCPoints =
     398        2061 :             (bSuccess) ? USGSDEMReadIntFromBuffer(&sBuffer, &bSuccess) : 0;
     399             : #ifdef DEBUG_VERBOSE
     400             :         CPLDebug("USGSDEM", "i = %d, nCPoints = %d", i, nCPoints);
     401             : #endif
     402             : 
     403        2061 :         if (bSuccess)
     404             :         {
     405             :             const int nNumberOfCols =
     406        2061 :                 USGSDEMReadIntFromBuffer(&sBuffer, &bSuccess);
     407        2061 :             if (nNumberOfCols != 1)
     408             :             {
     409           0 :                 CPLDebug("USGSDEM", "i = %d, nNumberOfCols = %d", i,
     410             :                          nNumberOfCols);
     411             :             }
     412             :         }
     413             : 
     414             :         // x-start
     415        2061 :         if (bSuccess)
     416        2061 :             /* dxStart = */ USGSDEMReadDoubleFromBuffer(&sBuffer, 24,
     417             :                                                         &bSuccess);
     418             : 
     419             :         double dyStart =
     420        2061 :             (bSuccess) ? USGSDEMReadDoubleFromBuffer(&sBuffer, 24, &bSuccess)
     421        2061 :                        : 0;
     422             :         const double dfElevOffset =
     423        2061 :             (bSuccess) ? USGSDEMReadDoubleFromBuffer(&sBuffer, 24, &bSuccess)
     424        2061 :                        : 0;
     425             : 
     426             :         // min z value
     427        2061 :         if (bSuccess)
     428        2061 :             /* djunk = */ USGSDEMReadDoubleFromBuffer(&sBuffer, 24, &bSuccess);
     429             : 
     430             :         // max z value
     431        2061 :         if (bSuccess)
     432        2060 :             /* djunk = */ USGSDEMReadDoubleFromBuffer(&sBuffer, 24, &bSuccess);
     433        2061 :         if (!bSuccess)
     434             :         {
     435           1 :             CPLFree(sBuffer.buffer);
     436           1 :             return CE_Failure;
     437             :         }
     438             : 
     439        2060 :         if (poGDS->m_oSRS.IsGeographic())
     440           4 :             dyStart = dyStart / 3600.0;
     441             : 
     442        2060 :         double dygap = (dfYMin - dyStart) / poGDS->m_gt.yscale + 0.5;
     443        2060 :         if (dygap <= INT_MIN || dygap >= INT_MAX || !std::isfinite(dygap))
     444             :         {
     445           0 :             CPLFree(sBuffer.buffer);
     446           0 :             return CE_Failure;
     447             :         }
     448        2060 :         int lygap = static_cast<int>(dygap);
     449        2060 :         if (nCPoints <= 0)
     450           0 :             continue;
     451        2060 :         if (lygap > INT_MAX - nCPoints)
     452           0 :             lygap = INT_MAX - nCPoints;
     453        2060 :         if (lygap < 0 && GetYSize() > INT_MAX + lygap)
     454             :         {
     455           0 :             CPLFree(sBuffer.buffer);
     456           0 :             return CE_Failure;
     457             :         }
     458             : 
     459       14411 :         for (int j = lygap; j < (nCPoints + lygap); j++)
     460             :         {
     461       12351 :             const int iY = GetYSize() - j - 1;
     462             : 
     463       12351 :             const int nElev = USGSDEMReadIntFromBuffer(&sBuffer, &bSuccess);
     464             : #ifdef DEBUG_VERBOSE
     465             :             CPLDebug("USGSDEM", "  j - lygap = %d, nElev = %d", j - lygap,
     466             :                      nElev);
     467             : #endif
     468             : 
     469       12351 :             if (!bSuccess)
     470             :             {
     471           0 :                 CPLFree(sBuffer.buffer);
     472           0 :                 return CE_Failure;
     473             :             }
     474             : 
     475       12351 :             if (iY < 0 || iY >= GetYSize())
     476             :             {
     477             :                 /* bad = TRUE; */
     478             :             }
     479       12351 :             else if (nElev == USGSDEM_NODATA)
     480             :                 /* leave in output buffer as nodata */;
     481             :             else
     482             :             {
     483        8389 :                 const float fComputedElev =
     484        8389 :                     static_cast<float>(nElev * poGDS->fVRes + dfElevOffset);
     485             : 
     486        8389 :                 if (GetRasterDataType() == GDT_Int16)
     487             :                 {
     488       16656 :                     GUInt16 nVal = (fComputedElev < -32768) ? -32768
     489             :                                    : (fComputedElev > 32767)
     490             :                                        ? 32767
     491        8328 :                                        : static_cast<GInt16>(fComputedElev);
     492        8328 :                     reinterpret_cast<GInt16 *>(pImage)[i + iY * GetXSize()] =
     493        8328 :                         nVal;
     494             :                 }
     495             :                 else
     496             :                 {
     497          61 :                     reinterpret_cast<float *>(pImage)[i + iY * GetXSize()] =
     498             :                         fComputedElev;
     499             :                 }
     500             :             }
     501             :         }
     502             : 
     503        2060 :         if (poGDS->nDataStartOffset == 1024)
     504             :         {
     505             :             // Seek to the next 1024 byte boundary.
     506             :             // Some files have 'junk' profile values after the valid/declared
     507             :             // ones
     508          10 :             vsi_l_offset nCurPos = USGSDEMGetCurrentFilePos(&sBuffer);
     509          10 :             vsi_l_offset nNewPos = (nCurPos + 1023) / 1024 * 1024;
     510          10 :             if (nNewPos > nCurPos)
     511             :             {
     512          10 :                 USGSDEMSetCurrentFilePos(&sBuffer, nNewPos);
     513             :             }
     514             :         }
     515             :     }
     516           9 :     CPLFree(sBuffer.buffer);
     517             : 
     518           9 :     return CE_None;
     519             : }
     520             : 
     521             : /************************************************************************/
     522             : /*                           GetNoDataValue()                           */
     523             : /************************************************************************/
     524             : 
     525           0 : double USGSDEMRasterBand::GetNoDataValue(int *pbSuccess)
     526             : 
     527             : {
     528           0 :     if (pbSuccess != nullptr)
     529           0 :         *pbSuccess = TRUE;
     530             : 
     531           0 :     return USGSDEM_NODATA;
     532             : }
     533             : 
     534             : /************************************************************************/
     535             : /*                            GetUnitType()                             */
     536             : /************************************************************************/
     537           0 : const char *USGSDEMRasterBand::GetUnitType()
     538             : {
     539           0 :     USGSDEMDataset *poGDS = cpl::down_cast<USGSDEMDataset *>(poDS);
     540             : 
     541           0 :     return poGDS->pszUnits;
     542             : }
     543             : 
     544             : /************************************************************************/
     545             : /* ==================================================================== */
     546             : /*                              USGSDEMDataset                          */
     547             : /* ==================================================================== */
     548             : /************************************************************************/
     549             : 
     550             : /************************************************************************/
     551             : /*                           USGSDEMDataset()                           */
     552             : /************************************************************************/
     553             : 
     554          20 : USGSDEMDataset::USGSDEMDataset()
     555             :     : nDataStartOffset(0), eNaturalDataFormat(GDT_Unknown), fVRes(0.0),
     556          20 :       pszUnits(nullptr), fp(nullptr)
     557             : {
     558          20 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     559          20 : }
     560             : 
     561             : /************************************************************************/
     562             : /*                          ~USGSDEMDataset()                           */
     563             : /************************************************************************/
     564             : 
     565          40 : USGSDEMDataset::~USGSDEMDataset()
     566             : 
     567             : {
     568          20 :     FlushCache(true);
     569             : 
     570          20 :     if (fp != nullptr)
     571          20 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     572          40 : }
     573             : 
     574             : /************************************************************************/
     575             : /*                            LoadFromFile()                            */
     576             : /*                                                                      */
     577             : /*      If the data from DEM is in meters, then values are stored as    */
     578             : /*      shorts. If DEM data is in feet, then height data will be        */
     579             : /*      stored in float, to preserve the precision of the original      */
     580             : /*      data. returns true if the file was successfully opened and      */
     581             : /*      read.                                                           */
     582             : /************************************************************************/
     583             : 
     584          20 : int USGSDEMDataset::LoadFromFile(VSILFILE *InDem)
     585             : {
     586             :     // check for version of DEM format
     587          20 :     CPL_IGNORE_RET_VAL(VSIFSeekL(InDem, 864, 0));
     588             : 
     589             :     // Read DEM into matrix
     590          20 :     const int nRow = ReadInt(InDem);
     591          20 :     const int nColumn = ReadInt(InDem);
     592             :     const bool bNewFormat =
     593          20 :         VSIFTellL(InDem) >= 1024 || nRow != 1 || nColumn != 1;
     594          20 :     if (bNewFormat)
     595             :     {
     596          20 :         CPL_IGNORE_RET_VAL(VSIFSeekL(InDem, 1024, 0));  // New Format
     597          20 :         int i = ReadInt(InDem);
     598          20 :         int j = ReadInt(InDem);
     599          20 :         if (i != 1 || (j != 1 && j != 0))  // File OK?
     600             :         {
     601           4 :             CPL_IGNORE_RET_VAL(
     602           4 :                 VSIFSeekL(InDem, 893, 0));  // Undocumented Format (39109h1.dem)
     603           4 :             i = ReadInt(InDem);
     604           4 :             j = ReadInt(InDem);
     605           4 :             if (i != 1 || j != 1)  // File OK?
     606             :             {
     607           2 :                 CPL_IGNORE_RET_VAL(VSIFSeekL(
     608           2 :                     InDem, 918, 0));  // Latest iteration of the A record, such
     609             :                                       // as in fema06-140cm_2995441b.dem
     610           2 :                 i = ReadInt(InDem);
     611           2 :                 j = ReadInt(InDem);
     612           2 :                 if (i != 1 || j != 1)  // File OK?
     613             :                 {
     614           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     615             :                              "Does not appear to be a USGS DEM file.");
     616           0 :                     return FALSE;
     617             :                 }
     618             :                 else
     619           2 :                     nDataStartOffset = 918;
     620             :             }
     621             :             else
     622           2 :                 nDataStartOffset = 893;
     623             :         }
     624             :         else
     625             :         {
     626          16 :             nDataStartOffset = 1024;
     627             :         }
     628             :     }
     629             :     else
     630           0 :         nDataStartOffset = 864;
     631             : 
     632          20 :     CPL_IGNORE_RET_VAL(VSIFSeekL(InDem, nDataStartOffset, 0));
     633             :     char c;
     634          20 :     if (VSIFReadL(&c, 1, 1, InDem) == 1)
     635             :     {
     636           2 :         if (c == '\n' && VSIFSeekL(InDem, 2 * nDataStartOffset + 1, 0) == 0 &&
     637          22 :             VSIFReadL(&c, 1, 1, InDem) == 1 && c == '\n')
     638             :         {
     639             :             // Some files use 1025 byte records ending with a newline
     640             :             // character.
     641             :             // See https://github.com/OSGeo/gdal/issues/5007
     642           2 :             nDataStartOffset++;
     643             :         }
     644           2 :         else if (c == '\r' && VSIFReadL(&c, 1, 1, InDem) == 1 && c == '\n' &&
     645           2 :                  VSIFSeekL(InDem, 2 * nDataStartOffset + 2, 0) == 0 &&
     646           2 :                  VSIFReadL(&c, 1, 1, InDem) == 1 && c == '\r' &&
     647          20 :                  VSIFReadL(&c, 1, 1, InDem) == 1 && c == '\n')
     648             :         {
     649           2 :             nDataStartOffset += 2;
     650             :         }
     651             :     }
     652             : 
     653          20 :     CPL_IGNORE_RET_VAL(VSIFSeekL(InDem, 156, 0));
     654          20 :     const int nCoordSystem = ReadInt(InDem);
     655          20 :     const int iUTMZone = ReadInt(InDem);
     656             : 
     657          20 :     CPL_IGNORE_RET_VAL(VSIFSeekL(InDem, 528, 0));
     658          20 :     const int nGUnit = ReadInt(InDem);
     659          20 :     const int nVUnit = ReadInt(InDem);
     660             : 
     661             :     // Vertical Units in meters
     662          20 :     if (nVUnit == 1)
     663           0 :         pszUnits = "ft";
     664             :     else
     665          20 :         pszUnits = "m";
     666             : 
     667          20 :     CPL_IGNORE_RET_VAL(VSIFSeekL(InDem, 816, 0));
     668          20 :     const double dxdelta = DConvert(InDem, 12);
     669          20 :     const double dydelta = DConvert(InDem, 12);
     670          20 :     if (dydelta == 0)
     671           0 :         return FALSE;
     672          20 :     fVRes = DConvert(InDem, 12);
     673             : 
     674             :     /* -------------------------------------------------------------------- */
     675             :     /*      Should we treat this as floating point, or GInt16.              */
     676             :     /* -------------------------------------------------------------------- */
     677          20 :     if (nVUnit == 1 || fVRes < 1.0)
     678           4 :         eNaturalDataFormat = GDT_Float32;
     679             :     else
     680          16 :         eNaturalDataFormat = GDT_Int16;
     681             : 
     682             :     /* -------------------------------------------------------------------- */
     683             :     /*      Read four corner coordinates.                                   */
     684             :     /* -------------------------------------------------------------------- */
     685          20 :     CPL_IGNORE_RET_VAL(VSIFSeekL(InDem, 546, 0));
     686             :     DPoint2 corners[4];  // SW, NW, NE, SE
     687         100 :     for (int i = 0; i < 4; i++)
     688             :     {
     689          80 :         corners[i].x = DConvert(InDem, 24);
     690          80 :         corners[i].y = DConvert(InDem, 24);
     691             :     }
     692             : 
     693             :     // find absolute extents of raw vales
     694             :     DPoint2 extent_min, extent_max;
     695          20 :     extent_min.x = std::min(corners[0].x, corners[1].x);
     696          20 :     extent_max.x = std::max(corners[2].x, corners[3].x);
     697          20 :     extent_min.y = std::min(corners[0].y, corners[3].y);
     698          20 :     extent_max.y = std::max(corners[1].y, corners[2].y);
     699             : 
     700          20 :     /* dElevMin = */ DConvert(InDem, 48);
     701          20 :     /* dElevMax = */ DConvert(InDem, 48);
     702             : 
     703          20 :     CPL_IGNORE_RET_VAL(VSIFSeekL(InDem, 858, 0));
     704          20 :     const int nProfiles = ReadInt(InDem);
     705             : 
     706             :     /* -------------------------------------------------------------------- */
     707             :     /*      Collect the spatial reference system.                           */
     708             :     /* -------------------------------------------------------------------- */
     709          40 :     OGRSpatialReference sr;
     710          20 :     sr.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     711          20 :     bool bNAD83 = true;
     712             : 
     713             :     // OLD format header ends at byte 864
     714          20 :     if (bNewFormat)
     715             :     {
     716             :         // year of data compilation
     717          20 :         CPL_IGNORE_RET_VAL(VSIFSeekL(InDem, 876, 0));
     718             :         char szDateBuffer[5];
     719          20 :         CPL_IGNORE_RET_VAL(VSIFReadL(szDateBuffer, 4, 1, InDem));
     720             :         /* szDateBuffer[4] = 0; */
     721             : 
     722             :         // Horizontal datum
     723             :         // 1=North American Datum 1927 (NAD 27)
     724             :         // 2=World Geodetic System 1972 (WGS 72)
     725             :         // 3=WGS 84
     726             :         // 4=NAD 83
     727             :         // 5=Old Hawaii Datum
     728             :         // 6=Puerto Rico Datum
     729          20 :         CPL_IGNORE_RET_VAL(VSIFSeekL(InDem, 890, 0));
     730             : 
     731             :         char szHorzDatum[3];
     732          20 :         CPL_IGNORE_RET_VAL(VSIFReadL(szHorzDatum, 1, 2, InDem));
     733          20 :         szHorzDatum[2] = '\0';
     734          20 :         const int datum = atoi(szHorzDatum);
     735          20 :         switch (datum)
     736             :         {
     737           6 :             case 1:
     738           6 :                 sr.SetWellKnownGeogCS("NAD27");
     739           6 :                 bNAD83 = false;
     740           6 :                 break;
     741             : 
     742           2 :             case 2:
     743           2 :                 sr.SetWellKnownGeogCS("WGS72");
     744           2 :                 break;
     745             : 
     746           0 :             case 3:
     747           0 :                 sr.SetWellKnownGeogCS("WGS84");
     748           0 :                 break;
     749             : 
     750           2 :             case 4:
     751           2 :                 sr.SetWellKnownGeogCS("NAD83");
     752           2 :                 break;
     753             : 
     754           0 :             case -9:
     755           0 :                 break;
     756             : 
     757          10 :             default:
     758          10 :                 sr.SetWellKnownGeogCS("NAD27");
     759          10 :                 break;
     760             :         }
     761             :     }
     762             :     else
     763             :     {
     764           0 :         sr.SetWellKnownGeogCS("NAD27");
     765           0 :         bNAD83 = false;
     766             :     }
     767             : 
     768          20 :     if (nCoordSystem == 1)  // UTM
     769             :     {
     770          14 :         if (iUTMZone >= -60 && iUTMZone <= 60)
     771             :         {
     772          14 :             sr.SetUTM(abs(iUTMZone), iUTMZone >= 0);
     773          14 :             if (nGUnit == 1)
     774             :             {
     775           0 :                 sr.SetLinearUnitsAndUpdateParameters(
     776             :                     SRS_UL_US_FOOT, CPLAtof(SRS_UL_US_FOOT_CONV));
     777             :                 char szUTMName[128];
     778           0 :                 snprintf(szUTMName, sizeof(szUTMName),
     779             :                          "UTM Zone %d, Northern Hemisphere, us-ft", iUTMZone);
     780           0 :                 sr.SetNode("PROJCS", szUTMName);
     781             :             }
     782             :         }
     783             :     }
     784           6 :     else if (nCoordSystem == 2)  // state plane
     785             :     {
     786           0 :         if (nGUnit == 1)
     787           0 :             sr.SetStatePlane(iUTMZone, bNAD83, "Foot",
     788             :                              CPLAtof(SRS_UL_US_FOOT_CONV));
     789             :         else
     790           0 :             sr.SetStatePlane(iUTMZone, bNAD83);
     791             :     }
     792             : 
     793          20 :     m_oSRS = std::move(sr);
     794             : 
     795             :     /* -------------------------------------------------------------------- */
     796             :     /*      For UTM we use the extents (really the UTM coordinates of       */
     797             :     /*      the lat/long corners of the quad) to determine the size in      */
     798             :     /*      pixels and lines, but we have to make the anchors be modulus    */
     799             :     /*      the pixel size which what really gets used.                     */
     800             :     /* -------------------------------------------------------------------- */
     801          20 :     if (nCoordSystem == 1          // UTM
     802           6 :         || nCoordSystem == 2       // State Plane
     803           6 :         || nCoordSystem == -9999)  // unknown
     804             :     {
     805             :         // expand extents modulus the pixel size.
     806          14 :         extent_min.y = floor(extent_min.y / dydelta) * dydelta;
     807          14 :         extent_max.y = ceil(extent_max.y / dydelta) * dydelta;
     808             : 
     809             :         // Forcibly compute X extents based on first profile and pixelsize.
     810          14 :         CPL_IGNORE_RET_VAL(VSIFSeekL(InDem, nDataStartOffset, 0));
     811          14 :         /* njunk = */ ReadInt(InDem);
     812          14 :         /* njunk = */ ReadInt(InDem);
     813          14 :         /* njunk = */ ReadInt(InDem);
     814          14 :         /* njunk = */ ReadInt(InDem);
     815          14 :         const double dxStart = DConvert(InDem, 24);
     816             : 
     817          14 :         const double dfRasterYSize =
     818          14 :             (extent_max.y - extent_min.y) / dydelta + 1.5;
     819          28 :         if (dfRasterYSize <= INT_MIN || dfRasterYSize >= INT_MAX ||
     820          14 :             !std::isfinite(dfRasterYSize))
     821           0 :             return FALSE;
     822          14 :         nRasterYSize = static_cast<int>(dfRasterYSize);
     823          14 :         nRasterXSize = nProfiles;
     824             : 
     825          14 :         m_gt.xorig = dxStart - dxdelta / 2.0;
     826          14 :         m_gt.xscale = dxdelta;
     827          14 :         m_gt.xrot = 0.0;
     828          14 :         m_gt.yorig = extent_max.y + dydelta / 2.0;
     829          14 :         m_gt.yrot = 0.0;
     830          14 :         m_gt.yscale = -dydelta;
     831             :     }
     832             :     /* -------------------------------------------------------------------- */
     833             :     /*      Geographic -- use corners directly.                             */
     834             :     /* -------------------------------------------------------------------- */
     835             :     else
     836             :     {
     837           6 :         const double dfRasterYSize =
     838           6 :             (extent_max.y - extent_min.y) / dydelta + 1.5;
     839          12 :         if (dfRasterYSize <= INT_MIN || dfRasterYSize >= INT_MAX ||
     840           6 :             !std::isfinite(dfRasterYSize))
     841           0 :             return FALSE;
     842           6 :         nRasterYSize = static_cast<int>(dfRasterYSize);
     843           6 :         nRasterXSize = nProfiles;
     844             : 
     845             :         // Translate extents from arc-seconds to decimal degrees.
     846           6 :         m_gt.xorig = (extent_min.x - dxdelta / 2.0) / 3600.0;
     847           6 :         m_gt.xscale = dxdelta / 3600.0;
     848           6 :         m_gt.xrot = 0.0;
     849           6 :         m_gt.yorig = (extent_max.y + dydelta / 2.0) / 3600.0;
     850           6 :         m_gt.yrot = 0.0;
     851           6 :         m_gt.yscale = (-dydelta) / 3600.0;
     852             :     }
     853             : 
     854             :     // IReadBlock() not ready for more than INT_MAX pixels, and that
     855             :     // would behave badly
     856          40 :     if (!GDALCheckDatasetDimensions(nRasterXSize, nRasterYSize) ||
     857          20 :         nRasterXSize > INT_MAX / nRasterYSize)
     858             :     {
     859           0 :         return FALSE;
     860             :     }
     861             : 
     862          20 :     return TRUE;
     863             : }
     864             : 
     865             : /************************************************************************/
     866             : /*                          GetGeoTransform()                           */
     867             : /************************************************************************/
     868             : 
     869           6 : CPLErr USGSDEMDataset::GetGeoTransform(GDALGeoTransform &gt) const
     870             : 
     871             : {
     872           6 :     gt = m_gt;
     873           6 :     return CE_None;
     874             : }
     875             : 
     876             : /************************************************************************/
     877             : /*                           GetSpatialRef()                            */
     878             : /************************************************************************/
     879             : 
     880           6 : const OGRSpatialReference *USGSDEMDataset::GetSpatialRef() const
     881             : 
     882             : {
     883           6 :     return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
     884             : }
     885             : 
     886             : /************************************************************************/
     887             : /*                              Identify()                              */
     888             : /************************************************************************/
     889             : 
     890       63131 : int USGSDEMDataset::Identify(GDALOpenInfo *poOpenInfo)
     891             : 
     892             : {
     893       63131 :     if (poOpenInfo->nHeaderBytes < 200)
     894       58229 :         return FALSE;
     895             : 
     896        4902 :     if (!STARTS_WITH_CI((const char *)poOpenInfo->pabyHeader + 156, "     0") &&
     897        4890 :         !STARTS_WITH_CI((const char *)poOpenInfo->pabyHeader + 156, "     1") &&
     898        4862 :         !STARTS_WITH_CI((const char *)poOpenInfo->pabyHeader + 156, "     2") &&
     899        4862 :         !STARTS_WITH_CI((const char *)poOpenInfo->pabyHeader + 156, "     3") &&
     900        4862 :         !STARTS_WITH_CI((const char *)poOpenInfo->pabyHeader + 156, " -9999"))
     901        4862 :         return FALSE;
     902             : 
     903          40 :     if (!STARTS_WITH_CI((const char *)poOpenInfo->pabyHeader + 150, "     1") &&
     904           4 :         !STARTS_WITH_CI((const char *)poOpenInfo->pabyHeader + 150, "     4"))
     905           0 :         return FALSE;
     906             : 
     907          40 :     return TRUE;
     908             : }
     909             : 
     910             : /************************************************************************/
     911             : /*                                Open()                                */
     912             : /************************************************************************/
     913             : 
     914          20 : GDALDataset *USGSDEMDataset::Open(GDALOpenInfo *poOpenInfo)
     915             : 
     916             : {
     917          20 :     if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
     918           0 :         return nullptr;
     919             : 
     920             :     /* -------------------------------------------------------------------- */
     921             :     /*      Create a corresponding GDALDataset.                             */
     922             :     /* -------------------------------------------------------------------- */
     923          20 :     USGSDEMDataset *poDS = new USGSDEMDataset();
     924             : 
     925          20 :     poDS->fp = poOpenInfo->fpL;
     926          20 :     poOpenInfo->fpL = nullptr;
     927             : 
     928             :     /* -------------------------------------------------------------------- */
     929             :     /*      Read the file.                                                  */
     930             :     /* -------------------------------------------------------------------- */
     931          20 :     if (!poDS->LoadFromFile(poDS->fp))
     932             :     {
     933           0 :         delete poDS;
     934           0 :         return nullptr;
     935             :     }
     936             : 
     937             :     /* -------------------------------------------------------------------- */
     938             :     /*      Confirm the requested access is supported.                      */
     939             :     /* -------------------------------------------------------------------- */
     940          20 :     if (poOpenInfo->eAccess == GA_Update)
     941             :     {
     942           0 :         delete poDS;
     943           0 :         ReportUpdateNotSupportedByDriver("USGSDEM");
     944           0 :         return nullptr;
     945             :     }
     946             : 
     947             :     /* -------------------------------------------------------------------- */
     948             :     /*      Create band information objects.                                */
     949             :     /* -------------------------------------------------------------------- */
     950          20 :     poDS->SetBand(1, new USGSDEMRasterBand(poDS));
     951             : 
     952          20 :     poDS->SetMetadataItem(GDALMD_AREA_OR_POINT, GDALMD_AOP_POINT);
     953             : 
     954             :     /* -------------------------------------------------------------------- */
     955             :     /*      Initialize any PAM information.                                 */
     956             :     /* -------------------------------------------------------------------- */
     957          20 :     poDS->SetDescription(poOpenInfo->pszFilename);
     958          20 :     poDS->TryLoadXML();
     959             : 
     960             :     /* -------------------------------------------------------------------- */
     961             :     /*      Open overviews.                                                 */
     962             :     /* -------------------------------------------------------------------- */
     963          20 :     poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
     964             : 
     965          20 :     return poDS;
     966             : }
     967             : 
     968             : /************************************************************************/
     969             : /*                        GDALRegister_USGSDEM()                        */
     970             : /************************************************************************/
     971             : 
     972        2062 : void GDALRegister_USGSDEM()
     973             : 
     974             : {
     975        2062 :     if (GDALGetDriverByName("USGSDEM") != nullptr)
     976         263 :         return;
     977             : 
     978        1799 :     GDALDriver *poDriver = new GDALDriver();
     979             : 
     980        1799 :     poDriver->SetDescription("USGSDEM");
     981        1799 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
     982        1799 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "dem");
     983        1799 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
     984        1799 :                               "USGS Optional ASCII DEM (and CDED)");
     985        1799 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
     986        1799 :                               "drivers/raster/usgsdem.html");
     987             : 
     988        1799 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
     989             : 
     990        1799 :     poDriver->pfnOpen = USGSDEMDataset::Open;
     991        1799 :     poDriver->pfnIdentify = USGSDEMDataset::Identify;
     992             : 
     993        1799 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     994             : }

Generated by: LCOV version 1.14