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

Generated by: LCOV version 1.14