LCOV - code coverage report
Current view: top level - frmts/usgsdem - usgsdemdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 333 391 85.2 %
Date: 2025-10-01 17:07:58 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         222 : static int ReadInt(VSILFILE *fp)
      45             : {
      46             :     char c;
      47         222 :     int nRead = 0;
      48             :     char szBuffer[12];
      49         222 :     bool bInProlog = true;
      50             : 
      51             :     while (true)
      52             :     {
      53        2266 :         if (VSIFReadL(&c, 1, 1, fp) != 1)
      54             :         {
      55           4 :             return 0;
      56             :         }
      57        2262 :         if (bInProlog)
      58             :         {
      59        1972 :             if (!isspace(static_cast<unsigned char>(c)))
      60             :             {
      61         218 :                 bInProlog = false;
      62             :             }
      63             :         }
      64        2262 :         if (!bInProlog)
      65             :         {
      66         508 :             if (c != '-' && c != '+' && !(c >= '0' && c <= '9'))
      67             :             {
      68         218 :                 CPL_IGNORE_RET_VAL(VSIFSeekL(fp, VSIFTellL(fp) - 1, SEEK_SET));
      69         218 :                 break;
      70             :             }
      71         290 :             if (nRead < 11)
      72         290 :                 szBuffer[nRead] = c;
      73         290 :             nRead++;
      74             :         }
      75             :     }
      76         218 :     szBuffer[std::min(nRead, 11)] = 0;
      77         218 :     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          42 : static void USGSDEMRefillBuffer(Buffer *psBuffer)
      94             : {
      95          42 :     memmove(psBuffer->buffer, psBuffer->buffer + psBuffer->cur_index,
      96          42 :             psBuffer->buffer_size - psBuffer->cur_index);
      97             : 
      98          42 :     psBuffer->buffer_size -= psBuffer->cur_index;
      99          42 :     psBuffer->buffer_size += static_cast<int>(
     100          84 :         VSIFReadL(psBuffer->buffer + psBuffer->buffer_size, 1,
     101          42 :                   psBuffer->max_size - psBuffer->buffer_size, psBuffer->fp));
     102          42 :     psBuffer->cur_index = 0;
     103          42 : }
     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      942758 : static int USGSDEMReadIntFromBuffer(Buffer *psBuffer, int *pbSuccess = nullptr)
     140             : {
     141             :     char c;
     142             : 
     143             :     while (true)
     144             :     {
     145      942758 :         if (psBuffer->cur_index >= psBuffer->buffer_size)
     146             :         {
     147          38 :             USGSDEMRefillBuffer(psBuffer);
     148          38 :             if (psBuffer->cur_index >= psBuffer->buffer_size)
     149             :             {
     150           0 :                 if (pbSuccess)
     151           0 :                     *pbSuccess = FALSE;
     152           0 :                 return 0;
     153             :             }
     154             :         }
     155             : 
     156      942758 :         c = psBuffer->buffer[psBuffer->cur_index];
     157      942758 :         psBuffer->cur_index++;
     158      942758 :         if (!isspace(static_cast<unsigned char>(c)))
     159       14451 :             break;
     160             :     }
     161             : 
     162       14451 :     GIntBig nVal = 0;
     163       14451 :     int nSign = 1;
     164       14451 :     if (c == '-')
     165        4944 :         nSign = -1;
     166        9507 :     else if (c == '+')
     167           0 :         nSign = 1;
     168        9507 :     else if (c >= '0' && c <= '9')
     169        9507 :         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       47517 :         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       47517 :         c = psBuffer->buffer[psBuffer->cur_index];
     191       47517 :         if (c >= '0' && c <= '9')
     192             :         {
     193       33066 :             psBuffer->cur_index++;
     194       33066 :             if (nVal * nSign < INT_MAX && nVal * nSign > INT_MIN)
     195             :             {
     196       33066 :                 nVal = nVal * 10 + (c - '0');
     197       33066 :                 if (nVal * nSign > INT_MAX)
     198             :                 {
     199           0 :                     nVal = INT_MAX;
     200           0 :                     nSign = 1;
     201             :                 }
     202       33066 :                 else if (nVal * nSign < INT_MIN)
     203             :                 {
     204           0 :                     nVal = INT_MIN;
     205           0 :                     nSign = 1;
     206             :                 }
     207             :             }
     208             :         }
     209             :         else
     210             :         {
     211       14451 :             if (pbSuccess)
     212       14451 :                 *pbSuccess = TRUE;
     213       14451 :             return static_cast<int>(nSign * nVal);
     214             :         }
     215             :     }
     216             : }
     217             : 
     218             : /************************************************************************/
     219             : /*                USGSDEMReadDoubleFromBuffer()                         */
     220             : /************************************************************************/
     221             : 
     222        5184 : static double USGSDEMReadDoubleFromBuffer(Buffer *psBuffer, int nCharCount,
     223             :                                           int *pbSuccess = nullptr)
     224             : 
     225             : {
     226        5184 :     if (psBuffer->cur_index + nCharCount > psBuffer->buffer_size)
     227             :     {
     228           4 :         USGSDEMRefillBuffer(psBuffer);
     229           4 :         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        5183 :     char *szPtr = psBuffer->buffer + psBuffer->cur_index;
     238        5183 :     char backupC = szPtr[nCharCount];
     239        5183 :     szPtr[nCharCount] = 0;
     240      129575 :     for (int i = 0; i < nCharCount; i++)
     241             :     {
     242      124392 :         if (szPtr[i] == 'D')
     243        5164 :             szPtr[i] = 'E';
     244             :     }
     245             : 
     246        5183 :     double dfVal = CPLAtof(szPtr);
     247        5183 :     szPtr[nCharCount] = backupC;
     248        5183 :     psBuffer->cur_index += nCharCount;
     249             : 
     250        5183 :     if (pbSuccess)
     251        5183 :         *pbSuccess = TRUE;
     252        5183 :     return dfVal;
     253             : }
     254             : 
     255             : /************************************************************************/
     256             : /*                              DConvert()                              */
     257             : /************************************************************************/
     258             : 
     259         246 : static double DConvert(VSILFILE *fp, int nCharCount)
     260             : 
     261             : {
     262             :     char szBuffer[100];
     263             : 
     264         246 :     CPL_IGNORE_RET_VAL(VSIFReadL(szBuffer, nCharCount, 1, fp));
     265         246 :     szBuffer[nCharCount] = '\0';
     266             : 
     267        6366 :     for (int i = 0; i < nCharCount; i++)
     268             :     {
     269        6120 :         if (szBuffer[i] == 'D')
     270         178 :             szBuffer[i] = 'E';
     271             :     }
     272             : 
     273         492 :     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             :     int 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          18 : USGSDEMRasterBand::USGSDEMRasterBand(USGSDEMDataset *poDSIn)
     335             : 
     336             : {
     337          18 :     this->poDS = poDSIn;
     338          18 :     this->nBand = 1;
     339             : 
     340          18 :     eDataType = poDSIn->eNaturalDataFormat;
     341             : 
     342          18 :     nBlockXSize = poDSIn->GetRasterXSize();
     343          18 :     nBlockYSize = poDSIn->GetRasterYSize();
     344          18 : }
     345             : 
     346             : /************************************************************************/
     347             : /*                             IReadBlock()                             */
     348             : /************************************************************************/
     349             : 
     350           9 : CPLErr USGSDEMRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff,
     351             :                                      CPL_UNUSED int nBlockYOff, void *pImage)
     352             : 
     353             : {
     354             :     /* int bad = FALSE; */
     355           9 :     USGSDEMDataset *poGDS = cpl::down_cast<USGSDEMDataset *>(poDS);
     356             : 
     357             :     /* -------------------------------------------------------------------- */
     358             :     /*      Initialize image buffer to nodata value.                        */
     359             :     /* -------------------------------------------------------------------- */
     360           9 :     GDALCopyWords(&USGSDEM_NODATA, GDT_Int32, 0, pImage, GetRasterDataType(),
     361             :                   GDALGetDataTypeSizeBytes(GetRasterDataType()),
     362           9 :                   GetXSize() * GetYSize());
     363             : 
     364             :     /* -------------------------------------------------------------------- */
     365             :     /*      Seek to data.                                                   */
     366             :     /* -------------------------------------------------------------------- */
     367           9 :     CPL_IGNORE_RET_VAL(VSIFSeekL(poGDS->fp, poGDS->nDataStartOffset, 0));
     368             : 
     369           9 :     double dfYMin = poGDS->m_gt[3] + (GetYSize() - 0.5) * poGDS->m_gt[5];
     370             : 
     371             :     /* -------------------------------------------------------------------- */
     372             :     /*      Read all the profiles into the image buffer.                    */
     373             :     /* -------------------------------------------------------------------- */
     374             : 
     375             :     Buffer sBuffer;
     376           9 :     sBuffer.max_size = 32768;
     377           9 :     sBuffer.buffer = static_cast<char *>(CPLMalloc(sBuffer.max_size + 1));
     378           9 :     sBuffer.fp = poGDS->fp;
     379           9 :     sBuffer.buffer_size = 0;
     380           9 :     sBuffer.cur_index = 0;
     381             : 
     382        1045 :     for (int i = 0; i < GetXSize(); i++)
     383             :     {
     384             :         int bSuccess;
     385        1037 :         const int nRowNumber = USGSDEMReadIntFromBuffer(&sBuffer, &bSuccess);
     386        1037 :         if (nRowNumber != 1)
     387           1 :             CPLDebug("USGSDEM", "i = %d, nRowNumber = %d", i, nRowNumber);
     388        1037 :         if (bSuccess)
     389             :         {
     390             :             const int nColNumber =
     391        1037 :                 USGSDEMReadIntFromBuffer(&sBuffer, &bSuccess);
     392        1037 :             if (nColNumber != i + 1)
     393             :             {
     394           3 :                 CPLDebug("USGSDEM", "i = %d, nColNumber = %d", i, nColNumber);
     395             :             }
     396             :         }
     397             :         const int nCPoints =
     398        1037 :             (bSuccess) ? USGSDEMReadIntFromBuffer(&sBuffer, &bSuccess) : 0;
     399             : #ifdef DEBUG_VERBOSE
     400             :         CPLDebug("USGSDEM", "i = %d, nCPoints = %d", i, nCPoints);
     401             : #endif
     402             : 
     403        1037 :         if (bSuccess)
     404             :         {
     405             :             const int nNumberOfCols =
     406        1037 :                 USGSDEMReadIntFromBuffer(&sBuffer, &bSuccess);
     407        1037 :             if (nNumberOfCols != 1)
     408             :             {
     409           0 :                 CPLDebug("USGSDEM", "i = %d, nNumberOfCols = %d", i,
     410             :                          nNumberOfCols);
     411             :             }
     412             :         }
     413             : 
     414             :         // x-start
     415        1037 :         if (bSuccess)
     416        1037 :             /* dxStart = */ USGSDEMReadDoubleFromBuffer(&sBuffer, 24,
     417             :                                                         &bSuccess);
     418             : 
     419             :         double dyStart =
     420        1037 :             (bSuccess) ? USGSDEMReadDoubleFromBuffer(&sBuffer, 24, &bSuccess)
     421        1037 :                        : 0;
     422             :         const double dfElevOffset =
     423        1037 :             (bSuccess) ? USGSDEMReadDoubleFromBuffer(&sBuffer, 24, &bSuccess)
     424        1037 :                        : 0;
     425             : 
     426             :         // min z value
     427        1037 :         if (bSuccess)
     428        1037 :             /* djunk = */ USGSDEMReadDoubleFromBuffer(&sBuffer, 24, &bSuccess);
     429             : 
     430             :         // max z value
     431        1037 :         if (bSuccess)
     432        1036 :             /* djunk = */ USGSDEMReadDoubleFromBuffer(&sBuffer, 24, &bSuccess);
     433        1037 :         if (!bSuccess)
     434             :         {
     435           1 :             CPLFree(sBuffer.buffer);
     436           1 :             return CE_Failure;
     437             :         }
     438             : 
     439        1036 :         if (poGDS->m_oSRS.IsGeographic())
     440           4 :             dyStart = dyStart / 3600.0;
     441             : 
     442        1036 :         double dygap = (dfYMin - dyStart) / poGDS->m_gt[5] + 0.5;
     443        1036 :         if (dygap <= INT_MIN || dygap >= INT_MAX || !std::isfinite(dygap))
     444             :         {
     445           0 :             CPLFree(sBuffer.buffer);
     446           0 :             return CE_Failure;
     447             :         }
     448        1036 :         int lygap = static_cast<int>(dygap);
     449        1036 :         if (nCPoints <= 0)
     450           0 :             continue;
     451        1036 :         if (lygap > INT_MAX - nCPoints)
     452           0 :             lygap = INT_MAX - nCPoints;
     453        1036 :         if (lygap < 0 && GetYSize() > INT_MAX + lygap)
     454             :         {
     455           0 :             CPLFree(sBuffer.buffer);
     456           0 :             return CE_Failure;
     457             :         }
     458             : 
     459       11339 :         for (int j = lygap; j < (nCPoints + lygap); j++)
     460             :         {
     461       10303 :             const int iY = GetYSize() - j - 1;
     462             : 
     463       10303 :             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       10303 :             if (!bSuccess)
     470             :             {
     471           0 :                 CPLFree(sBuffer.buffer);
     472           0 :                 return CE_Failure;
     473             :             }
     474             : 
     475       10303 :             if (iY < 0 || iY >= GetYSize())
     476             :             {
     477             :                 /* bad = TRUE; */
     478             :             }
     479       10303 :             else if (nElev == USGSDEM_NODATA)
     480             :                 /* leave in output buffer as nodata */;
     481             :             else
     482             :             {
     483        6341 :                 const float fComputedElev =
     484        6341 :                     static_cast<float>(nElev * poGDS->fVRes + dfElevOffset);
     485             : 
     486        6341 :                 if (GetRasterDataType() == GDT_Int16)
     487             :                 {
     488       12560 :                     GUInt16 nVal = (fComputedElev < -32768) ? -32768
     489             :                                    : (fComputedElev > 32767)
     490             :                                        ? 32767
     491        6280 :                                        : static_cast<GInt16>(fComputedElev);
     492        6280 :                     reinterpret_cast<GInt16 *>(pImage)[i + iY * GetXSize()] =
     493        6280 :                         nVal;
     494             :                 }
     495             :                 else
     496             :                 {
     497          61 :                     reinterpret_cast<float *>(pImage)[i + iY * GetXSize()] =
     498             :                         fComputedElev;
     499             :                 }
     500             :             }
     501             :         }
     502             : 
     503        1036 :         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           8 :     CPLFree(sBuffer.buffer);
     517             : 
     518           8 :     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          18 : USGSDEMDataset::USGSDEMDataset()
     555             :     : nDataStartOffset(0), eNaturalDataFormat(GDT_Unknown), fVRes(0.0),
     556          18 :       pszUnits(nullptr), fp(nullptr)
     557             : {
     558          18 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     559          18 : }
     560             : 
     561             : /************************************************************************/
     562             : /*                            ~USGSDEMDataset()                         */
     563             : /************************************************************************/
     564             : 
     565          36 : USGSDEMDataset::~USGSDEMDataset()
     566             : 
     567             : {
     568          18 :     FlushCache(true);
     569             : 
     570          18 :     if (fp != nullptr)
     571          18 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     572          36 : }
     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          18 : int USGSDEMDataset::LoadFromFile(VSILFILE *InDem)
     585             : {
     586             :     // check for version of DEM format
     587          18 :     CPL_IGNORE_RET_VAL(VSIFSeekL(InDem, 864, 0));
     588             : 
     589             :     // Read DEM into matrix
     590          18 :     const int nRow = ReadInt(InDem);
     591          18 :     const int nColumn = ReadInt(InDem);
     592             :     const bool bNewFormat =
     593          18 :         VSIFTellL(InDem) >= 1024 || nRow != 1 || nColumn != 1;
     594          18 :     if (bNewFormat)
     595             :     {
     596          18 :         CPL_IGNORE_RET_VAL(VSIFSeekL(InDem, 1024, 0));  // New Format
     597          18 :         int i = ReadInt(InDem);
     598          18 :         int j = ReadInt(InDem);
     599          18 :         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             :                     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          14 :             nDataStartOffset = 1024;
     627             : 
     628             :             // Some files use 1025 byte records ending with a newline character.
     629             :             // See https://github.com/OSGeo/gdal/issues/5007
     630          14 :             CPL_IGNORE_RET_VAL(VSIFSeekL(InDem, 1024, 0));
     631             :             char c;
     632          28 :             if (VSIFReadL(&c, 1, 1, InDem) == 1 && c == '\n' &&
     633           2 :                 VSIFSeekL(InDem, 1024 + 1024 + 1, 0) == 0 &&
     634          28 :                 VSIFReadL(&c, 1, 1, InDem) == 1 && c == '\n')
     635             :             {
     636           2 :                 nDataStartOffset = 1025;
     637             :             }
     638             :         }
     639             :     }
     640             :     else
     641           0 :         nDataStartOffset = 864;
     642             : 
     643          18 :     CPL_IGNORE_RET_VAL(VSIFSeekL(InDem, 156, 0));
     644          18 :     const int nCoordSystem = ReadInt(InDem);
     645          18 :     const int iUTMZone = ReadInt(InDem);
     646             : 
     647          18 :     CPL_IGNORE_RET_VAL(VSIFSeekL(InDem, 528, 0));
     648          18 :     const int nGUnit = ReadInt(InDem);
     649          18 :     const int nVUnit = ReadInt(InDem);
     650             : 
     651             :     // Vertical Units in meters
     652          18 :     if (nVUnit == 1)
     653           0 :         pszUnits = "ft";
     654             :     else
     655          18 :         pszUnits = "m";
     656             : 
     657          18 :     CPL_IGNORE_RET_VAL(VSIFSeekL(InDem, 816, 0));
     658          18 :     const double dxdelta = DConvert(InDem, 12);
     659          18 :     const double dydelta = DConvert(InDem, 12);
     660          18 :     if (dydelta == 0)
     661           0 :         return FALSE;
     662          18 :     fVRes = DConvert(InDem, 12);
     663             : 
     664             :     /* -------------------------------------------------------------------- */
     665             :     /*      Should we treat this as floating point, or GInt16.              */
     666             :     /* -------------------------------------------------------------------- */
     667          18 :     if (nVUnit == 1 || fVRes < 1.0)
     668           4 :         eNaturalDataFormat = GDT_Float32;
     669             :     else
     670          14 :         eNaturalDataFormat = GDT_Int16;
     671             : 
     672             :     /* -------------------------------------------------------------------- */
     673             :     /*      Read four corner coordinates.                                   */
     674             :     /* -------------------------------------------------------------------- */
     675          18 :     CPL_IGNORE_RET_VAL(VSIFSeekL(InDem, 546, 0));
     676             :     DPoint2 corners[4];  // SW, NW, NE, SE
     677          90 :     for (int i = 0; i < 4; i++)
     678             :     {
     679          72 :         corners[i].x = DConvert(InDem, 24);
     680          72 :         corners[i].y = DConvert(InDem, 24);
     681             :     }
     682             : 
     683             :     // find absolute extents of raw vales
     684             :     DPoint2 extent_min, extent_max;
     685          18 :     extent_min.x = std::min(corners[0].x, corners[1].x);
     686          18 :     extent_max.x = std::max(corners[2].x, corners[3].x);
     687          18 :     extent_min.y = std::min(corners[0].y, corners[3].y);
     688          18 :     extent_max.y = std::max(corners[1].y, corners[2].y);
     689             : 
     690          18 :     /* dElevMin = */ DConvert(InDem, 48);
     691          18 :     /* dElevMax = */ DConvert(InDem, 48);
     692             : 
     693          18 :     CPL_IGNORE_RET_VAL(VSIFSeekL(InDem, 858, 0));
     694          18 :     const int nProfiles = ReadInt(InDem);
     695             : 
     696             :     /* -------------------------------------------------------------------- */
     697             :     /*      Collect the spatial reference system.                           */
     698             :     /* -------------------------------------------------------------------- */
     699          36 :     OGRSpatialReference sr;
     700          18 :     sr.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     701          18 :     bool bNAD83 = true;
     702             : 
     703             :     // OLD format header ends at byte 864
     704          18 :     if (bNewFormat)
     705             :     {
     706             :         // year of data compilation
     707          18 :         CPL_IGNORE_RET_VAL(VSIFSeekL(InDem, 876, 0));
     708             :         char szDateBuffer[5];
     709          18 :         CPL_IGNORE_RET_VAL(VSIFReadL(szDateBuffer, 4, 1, InDem));
     710             :         /* szDateBuffer[4] = 0; */
     711             : 
     712             :         // Horizontal datum
     713             :         // 1=North American Datum 1927 (NAD 27)
     714             :         // 2=World Geodetic System 1972 (WGS 72)
     715             :         // 3=WGS 84
     716             :         // 4=NAD 83
     717             :         // 5=Old Hawaii Datum
     718             :         // 6=Puerto Rico Datum
     719          18 :         CPL_IGNORE_RET_VAL(VSIFSeekL(InDem, 890, 0));
     720             : 
     721             :         char szHorzDatum[3];
     722          18 :         CPL_IGNORE_RET_VAL(VSIFReadL(szHorzDatum, 1, 2, InDem));
     723          18 :         szHorzDatum[2] = '\0';
     724          18 :         const int datum = atoi(szHorzDatum);
     725          18 :         switch (datum)
     726             :         {
     727           4 :             case 1:
     728           4 :                 sr.SetWellKnownGeogCS("NAD27");
     729           4 :                 bNAD83 = false;
     730           4 :                 break;
     731             : 
     732           2 :             case 2:
     733           2 :                 sr.SetWellKnownGeogCS("WGS72");
     734           2 :                 break;
     735             : 
     736           0 :             case 3:
     737           0 :                 sr.SetWellKnownGeogCS("WGS84");
     738           0 :                 break;
     739             : 
     740           2 :             case 4:
     741           2 :                 sr.SetWellKnownGeogCS("NAD83");
     742           2 :                 break;
     743             : 
     744           0 :             case -9:
     745           0 :                 break;
     746             : 
     747          10 :             default:
     748          10 :                 sr.SetWellKnownGeogCS("NAD27");
     749          10 :                 break;
     750             :         }
     751             :     }
     752             :     else
     753             :     {
     754           0 :         sr.SetWellKnownGeogCS("NAD27");
     755           0 :         bNAD83 = false;
     756             :     }
     757             : 
     758          18 :     if (nCoordSystem == 1)  // UTM
     759             :     {
     760          12 :         if (iUTMZone >= -60 && iUTMZone <= 60)
     761             :         {
     762          12 :             sr.SetUTM(abs(iUTMZone), iUTMZone >= 0);
     763          12 :             if (nGUnit == 1)
     764             :             {
     765           0 :                 sr.SetLinearUnitsAndUpdateParameters(
     766             :                     SRS_UL_US_FOOT, CPLAtof(SRS_UL_US_FOOT_CONV));
     767             :                 char szUTMName[128];
     768           0 :                 snprintf(szUTMName, sizeof(szUTMName),
     769             :                          "UTM Zone %d, Northern Hemisphere, us-ft", iUTMZone);
     770           0 :                 sr.SetNode("PROJCS", szUTMName);
     771             :             }
     772             :         }
     773             :     }
     774           6 :     else if (nCoordSystem == 2)  // state plane
     775             :     {
     776           0 :         if (nGUnit == 1)
     777           0 :             sr.SetStatePlane(iUTMZone, bNAD83, "Foot",
     778             :                              CPLAtof(SRS_UL_US_FOOT_CONV));
     779             :         else
     780           0 :             sr.SetStatePlane(iUTMZone, bNAD83);
     781             :     }
     782             : 
     783          18 :     m_oSRS = std::move(sr);
     784             : 
     785             :     /* -------------------------------------------------------------------- */
     786             :     /*      For UTM we use the extents (really the UTM coordinates of       */
     787             :     /*      the lat/long corners of the quad) to determine the size in      */
     788             :     /*      pixels and lines, but we have to make the anchors be modulus    */
     789             :     /*      the pixel size which what really gets used.                     */
     790             :     /* -------------------------------------------------------------------- */
     791          18 :     if (nCoordSystem == 1          // UTM
     792           6 :         || nCoordSystem == 2       // State Plane
     793           6 :         || nCoordSystem == -9999)  // unknown
     794             :     {
     795             :         // expand extents modulus the pixel size.
     796          12 :         extent_min.y = floor(extent_min.y / dydelta) * dydelta;
     797          12 :         extent_max.y = ceil(extent_max.y / dydelta) * dydelta;
     798             : 
     799             :         // Forcibly compute X extents based on first profile and pixelsize.
     800          12 :         CPL_IGNORE_RET_VAL(VSIFSeekL(InDem, nDataStartOffset, 0));
     801          12 :         /* njunk = */ ReadInt(InDem);
     802          12 :         /* njunk = */ ReadInt(InDem);
     803          12 :         /* njunk = */ ReadInt(InDem);
     804          12 :         /* njunk = */ ReadInt(InDem);
     805          12 :         const double dxStart = DConvert(InDem, 24);
     806             : 
     807          12 :         nRasterYSize =
     808          12 :             static_cast<int>((extent_max.y - extent_min.y) / dydelta + 1.5);
     809          12 :         nRasterXSize = nProfiles;
     810             : 
     811          12 :         m_gt[0] = dxStart - dxdelta / 2.0;
     812          12 :         m_gt[1] = dxdelta;
     813          12 :         m_gt[2] = 0.0;
     814          12 :         m_gt[3] = extent_max.y + dydelta / 2.0;
     815          12 :         m_gt[4] = 0.0;
     816          12 :         m_gt[5] = -dydelta;
     817             :     }
     818             :     /* -------------------------------------------------------------------- */
     819             :     /*      Geographic -- use corners directly.                             */
     820             :     /* -------------------------------------------------------------------- */
     821             :     else
     822             :     {
     823           6 :         nRasterYSize =
     824           6 :             static_cast<int>((extent_max.y - extent_min.y) / dydelta + 1.5);
     825           6 :         nRasterXSize = nProfiles;
     826             : 
     827             :         // Translate extents from arc-seconds to decimal degrees.
     828           6 :         m_gt[0] = (extent_min.x - dxdelta / 2.0) / 3600.0;
     829           6 :         m_gt[1] = dxdelta / 3600.0;
     830           6 :         m_gt[2] = 0.0;
     831           6 :         m_gt[3] = (extent_max.y + dydelta / 2.0) / 3600.0;
     832           6 :         m_gt[4] = 0.0;
     833           6 :         m_gt[5] = (-dydelta) / 3600.0;
     834             :     }
     835             : 
     836             :     // IReadBlock() not ready for more than INT_MAX pixels, and that
     837             :     // would behave badly
     838          36 :     if (!GDALCheckDatasetDimensions(nRasterXSize, nRasterYSize) ||
     839          18 :         nRasterXSize > INT_MAX / nRasterYSize)
     840             :     {
     841           0 :         return FALSE;
     842             :     }
     843             : 
     844          18 :     return TRUE;
     845             : }
     846             : 
     847             : /************************************************************************/
     848             : /*                          GetGeoTransform()                           */
     849             : /************************************************************************/
     850             : 
     851           6 : CPLErr USGSDEMDataset::GetGeoTransform(GDALGeoTransform &gt) const
     852             : 
     853             : {
     854           6 :     gt = m_gt;
     855           6 :     return CE_None;
     856             : }
     857             : 
     858             : /************************************************************************/
     859             : /*                          GetSpatialRef()                             */
     860             : /************************************************************************/
     861             : 
     862           6 : const OGRSpatialReference *USGSDEMDataset::GetSpatialRef() const
     863             : 
     864             : {
     865           6 :     return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
     866             : }
     867             : 
     868             : /************************************************************************/
     869             : /*                              Identify()                              */
     870             : /************************************************************************/
     871             : 
     872       58349 : int USGSDEMDataset::Identify(GDALOpenInfo *poOpenInfo)
     873             : 
     874             : {
     875       58349 :     if (poOpenInfo->nHeaderBytes < 200)
     876       55149 :         return FALSE;
     877             : 
     878        3200 :     if (!STARTS_WITH_CI((const char *)poOpenInfo->pabyHeader + 156, "     0") &&
     879        3192 :         !STARTS_WITH_CI((const char *)poOpenInfo->pabyHeader + 156, "     1") &&
     880        3168 :         !STARTS_WITH_CI((const char *)poOpenInfo->pabyHeader + 156, "     2") &&
     881        3168 :         !STARTS_WITH_CI((const char *)poOpenInfo->pabyHeader + 156, "     3") &&
     882        3168 :         !STARTS_WITH_CI((const char *)poOpenInfo->pabyHeader + 156, " -9999"))
     883        3168 :         return FALSE;
     884             : 
     885          32 :     if (!STARTS_WITH_CI((const char *)poOpenInfo->pabyHeader + 150, "     1") &&
     886           4 :         !STARTS_WITH_CI((const char *)poOpenInfo->pabyHeader + 150, "     4"))
     887           0 :         return FALSE;
     888             : 
     889          32 :     return TRUE;
     890             : }
     891             : 
     892             : /************************************************************************/
     893             : /*                                Open()                                */
     894             : /************************************************************************/
     895             : 
     896          18 : GDALDataset *USGSDEMDataset::Open(GDALOpenInfo *poOpenInfo)
     897             : 
     898             : {
     899          18 :     if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
     900           0 :         return nullptr;
     901             : 
     902             :     /* -------------------------------------------------------------------- */
     903             :     /*      Create a corresponding GDALDataset.                             */
     904             :     /* -------------------------------------------------------------------- */
     905          18 :     USGSDEMDataset *poDS = new USGSDEMDataset();
     906             : 
     907          18 :     poDS->fp = poOpenInfo->fpL;
     908          18 :     poOpenInfo->fpL = nullptr;
     909             : 
     910             :     /* -------------------------------------------------------------------- */
     911             :     /*      Read the file.                                                  */
     912             :     /* -------------------------------------------------------------------- */
     913          18 :     if (!poDS->LoadFromFile(poDS->fp))
     914             :     {
     915           0 :         delete poDS;
     916           0 :         return nullptr;
     917             :     }
     918             : 
     919             :     /* -------------------------------------------------------------------- */
     920             :     /*      Confirm the requested access is supported.                      */
     921             :     /* -------------------------------------------------------------------- */
     922          18 :     if (poOpenInfo->eAccess == GA_Update)
     923             :     {
     924           0 :         delete poDS;
     925           0 :         ReportUpdateNotSupportedByDriver("USGSDEM");
     926           0 :         return nullptr;
     927             :     }
     928             : 
     929             :     /* -------------------------------------------------------------------- */
     930             :     /*      Create band information objects.                                */
     931             :     /* -------------------------------------------------------------------- */
     932          18 :     poDS->SetBand(1, new USGSDEMRasterBand(poDS));
     933             : 
     934          18 :     poDS->SetMetadataItem(GDALMD_AREA_OR_POINT, GDALMD_AOP_POINT);
     935             : 
     936             :     /* -------------------------------------------------------------------- */
     937             :     /*      Initialize any PAM information.                                 */
     938             :     /* -------------------------------------------------------------------- */
     939          18 :     poDS->SetDescription(poOpenInfo->pszFilename);
     940          18 :     poDS->TryLoadXML();
     941             : 
     942             :     /* -------------------------------------------------------------------- */
     943             :     /*      Open overviews.                                                 */
     944             :     /* -------------------------------------------------------------------- */
     945          18 :     poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
     946             : 
     947          18 :     return poDS;
     948             : }
     949             : 
     950             : /************************************************************************/
     951             : /*                        GDALRegister_USGSDEM()                        */
     952             : /************************************************************************/
     953             : 
     954        2033 : void GDALRegister_USGSDEM()
     955             : 
     956             : {
     957        2033 :     if (GDALGetDriverByName("USGSDEM") != nullptr)
     958         283 :         return;
     959             : 
     960        1750 :     GDALDriver *poDriver = new GDALDriver();
     961             : 
     962        1750 :     poDriver->SetDescription("USGSDEM");
     963        1750 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
     964        1750 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "dem");
     965        1750 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
     966        1750 :                               "USGS Optional ASCII DEM (and CDED)");
     967        1750 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
     968        1750 :                               "drivers/raster/usgsdem.html");
     969             : 
     970        1750 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
     971             : 
     972        1750 :     poDriver->pfnOpen = USGSDEMDataset::Open;
     973        1750 :     poDriver->pfnIdentify = USGSDEMDataset::Identify;
     974             : 
     975        1750 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     976             : }

Generated by: LCOV version 1.14