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

Generated by: LCOV version 1.14