LCOV - code coverage report
Current view: top level - frmts/raw - btdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 300 392 76.5 %
Date: 2025-10-01 17:07:58 Functions: 17 21 81.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  VTP .bt Driver
       4             :  * Purpose:  Implementation of VTP .bt elevation format read/write support.
       5             :  *           http://www.vterrain.org/Implementation/Formats/BT.html
       6             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2003, Frank Warmerdam
      10             :  * Copyright (c) 2007-2011, Even Rouault <even dot rouault at spatialys.com>
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  ****************************************************************************/
      14             : 
      15             : #include "gdal_frmts.h"
      16             : #include "gdal_priv.h"
      17             : #include "ogr_spatialref.h"
      18             : #include "rawdataset.h"
      19             : #include <cmath>
      20             : #include <cstdlib>
      21             : 
      22             : /************************************************************************/
      23             : /* ==================================================================== */
      24             : /*                              BTDataset                               */
      25             : /* ==================================================================== */
      26             : /************************************************************************/
      27             : 
      28             : class BTDataset final : public GDALPamDataset
      29             : {
      30             :     friend class BTRasterBand;
      31             : 
      32             :     VSILFILE *fpImage;  // image data file.
      33             : 
      34             :     int bGeoTransformValid;
      35             :     GDALGeoTransform m_gt{};
      36             : 
      37             :     OGRSpatialReference m_oSRS{};
      38             : 
      39             :     int nVersionCode;  // version times 10.
      40             : 
      41             :     int bHeaderModified;
      42             :     unsigned char abyHeader[256];
      43             : 
      44             :     float m_fVscale;
      45             : 
      46             :     CPL_DISALLOW_COPY_ASSIGN(BTDataset)
      47             : 
      48             :   public:
      49             :     BTDataset();
      50             :     ~BTDataset() override;
      51             : 
      52           4 :     const OGRSpatialReference *GetSpatialRef() const override
      53             :     {
      54           4 :         return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
      55             :     }
      56             : 
      57             :     CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override;
      58             :     CPLErr GetGeoTransform(GDALGeoTransform &) const override;
      59             :     CPLErr SetGeoTransform(const GDALGeoTransform &) override;
      60             : 
      61             :     CPLErr FlushCache(bool bAtClosing) override;
      62             : 
      63             :     static GDALDataset *Open(GDALOpenInfo *);
      64             :     static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
      65             :                                int nBandsIn, GDALDataType eType,
      66             :                                char **papszOptions);
      67             : };
      68             : 
      69             : /************************************************************************/
      70             : /* ==================================================================== */
      71             : /*                            BTRasterBand                              */
      72             : /* ==================================================================== */
      73             : /************************************************************************/
      74             : 
      75             : class BTRasterBand final : public GDALPamRasterBand
      76             : {
      77             :     VSILFILE *fpImage;
      78             : 
      79             :     CPL_DISALLOW_COPY_ASSIGN(BTRasterBand)
      80             : 
      81             :   public:
      82             :     BTRasterBand(GDALDataset *poDS, VSILFILE *fp, GDALDataType eType);
      83             : 
      84          54 :     ~BTRasterBand() override
      85          27 :     {
      86          54 :     }
      87             : 
      88             :     CPLErr IReadBlock(int, int, void *) override;
      89             :     CPLErr IWriteBlock(int, int, void *) override;
      90             : 
      91             :     const char *GetUnitType() override;
      92             :     CPLErr SetUnitType(const char *) override;
      93             :     double GetNoDataValue(int * = nullptr) override;
      94             :     CPLErr SetNoDataValue(double) override;
      95             : };
      96             : 
      97             : /************************************************************************/
      98             : /*                           BTRasterBand()                             */
      99             : /************************************************************************/
     100             : 
     101          27 : BTRasterBand::BTRasterBand(GDALDataset *poDSIn, VSILFILE *fp,
     102          27 :                            GDALDataType eType)
     103          27 :     : fpImage(fp)
     104             : {
     105          27 :     poDS = poDSIn;
     106          27 :     nBand = 1;
     107          27 :     eDataType = eType;
     108             : 
     109          27 :     nBlockXSize = 1;
     110          27 :     nBlockYSize = poDS->GetRasterYSize();
     111          27 : }
     112             : 
     113             : /************************************************************************/
     114             : /*                             IReadBlock()                             */
     115             : /************************************************************************/
     116             : 
     117          80 : CPLErr BTRasterBand::IReadBlock(int nBlockXOff, CPL_UNUSED int nBlockYOff,
     118             :                                 void *pImage)
     119             : {
     120          80 :     CPLAssert(nBlockYOff == 0);
     121             : 
     122          80 :     const int nDataSize = GDALGetDataTypeSizeBytes(eDataType);
     123             : 
     124             :     /* -------------------------------------------------------------------- */
     125             :     /*      Seek to profile.                                                */
     126             :     /* -------------------------------------------------------------------- */
     127         160 :     if (VSIFSeekL(fpImage,
     128          80 :                   256 + static_cast<vsi_l_offset>(nBlockXOff) * nDataSize *
     129          80 :                             nRasterYSize,
     130          80 :                   SEEK_SET) != 0)
     131             :     {
     132           0 :         CPLError(CE_Failure, CPLE_FileIO, ".bt Seek failed:%s",
     133           0 :                  VSIStrerror(errno));
     134           0 :         return CE_Failure;
     135             :     }
     136             : 
     137             :     /* -------------------------------------------------------------------- */
     138             :     /*      Read the profile.                                               */
     139             :     /* -------------------------------------------------------------------- */
     140          80 :     if (VSIFReadL(pImage, nDataSize, nRasterYSize, fpImage) !=
     141          80 :         static_cast<size_t>(nRasterYSize))
     142             :     {
     143           0 :         CPLError(CE_Failure, CPLE_FileIO, ".bt Read failed:%s",
     144           0 :                  VSIStrerror(errno));
     145           0 :         return CE_Failure;
     146             :     }
     147             : 
     148             : /* -------------------------------------------------------------------- */
     149             : /*      Swap on MSB platforms.                                          */
     150             : /* -------------------------------------------------------------------- */
     151             : #ifdef CPL_MSB
     152             :     GDALSwapWords(pImage, nDataSize, nRasterYSize, nDataSize);
     153             : #endif
     154             : 
     155             :     /* -------------------------------------------------------------------- */
     156             :     /*      Vertical flip, since GDAL expects values from top to bottom,    */
     157             :     /*      but in .bt they are bottom to top.                              */
     158             :     /* -------------------------------------------------------------------- */
     159          80 :     GByte abyWrk[8] = {0};
     160          80 :     CPLAssert(nDataSize <= 8);
     161         880 :     for (int i = 0; i < nRasterYSize / 2; i++)
     162             :     {
     163         800 :         memcpy(abyWrk, reinterpret_cast<GByte *>(pImage) + i * nDataSize,
     164             :                nDataSize);
     165         800 :         memcpy(reinterpret_cast<GByte *>(pImage) + i * nDataSize,
     166         800 :                reinterpret_cast<GByte *>(pImage) +
     167         800 :                    (nRasterYSize - i - 1) * nDataSize,
     168             :                nDataSize);
     169         800 :         memcpy(reinterpret_cast<GByte *>(pImage) +
     170         800 :                    (nRasterYSize - i - 1) * nDataSize,
     171             :                abyWrk, nDataSize);
     172             :     }
     173             : 
     174          80 :     return CE_None;
     175             : }
     176             : 
     177             : /************************************************************************/
     178             : /*                            IWriteBlock()                             */
     179             : /************************************************************************/
     180             : 
     181         110 : CPLErr BTRasterBand::IWriteBlock(int nBlockXOff, CPL_UNUSED int nBlockYOff,
     182             :                                  void *pImage)
     183             : 
     184             : {
     185         110 :     CPLAssert(nBlockYOff == 0);
     186             : 
     187         110 :     const int nDataSize = GDALGetDataTypeSizeBytes(eDataType);
     188             : 
     189             :     /* -------------------------------------------------------------------- */
     190             :     /*      Seek to profile.                                                */
     191             :     /* -------------------------------------------------------------------- */
     192         110 :     if (VSIFSeekL(fpImage, 256 + nBlockXOff * nDataSize * nRasterYSize,
     193         110 :                   SEEK_SET) != 0)
     194             :     {
     195           0 :         CPLError(CE_Failure, CPLE_FileIO, ".bt Seek failed:%s",
     196           0 :                  VSIStrerror(errno));
     197           0 :         return CE_Failure;
     198             :     }
     199             : 
     200             :     /* -------------------------------------------------------------------- */
     201             :     /*      Allocate working buffer.                                        */
     202             :     /* -------------------------------------------------------------------- */
     203             :     GByte *pabyWrkBlock = static_cast<GByte *>(
     204         110 :         CPLMalloc(static_cast<size_t>(nDataSize) * nRasterYSize));
     205             : 
     206             :     /* -------------------------------------------------------------------- */
     207             :     /*      Vertical flip data into work buffer, since GDAL expects         */
     208             :     /*      values from top to bottom, but in .bt they are bottom to        */
     209             :     /*      top.                                                            */
     210             :     /* -------------------------------------------------------------------- */
     211        2010 :     for (int i = 0; i < nRasterYSize; i++)
     212             :     {
     213        1900 :         memcpy(pabyWrkBlock +
     214        1900 :                    static_cast<size_t>(nRasterYSize - i - 1) * nDataSize,
     215        1900 :                reinterpret_cast<GByte *>(pImage) + i * nDataSize, nDataSize);
     216             :     }
     217             : 
     218             : /* -------------------------------------------------------------------- */
     219             : /*      Swap on MSB platforms.                                          */
     220             : /* -------------------------------------------------------------------- */
     221             : #ifdef CPL_MSB
     222             :     GDALSwapWords(pabyWrkBlock, nDataSize, nRasterYSize, nDataSize);
     223             : #endif
     224             : 
     225             :     /* -------------------------------------------------------------------- */
     226             :     /*      Read the profile.                                               */
     227             :     /* -------------------------------------------------------------------- */
     228         110 :     if (VSIFWriteL(pabyWrkBlock, nDataSize, nRasterYSize, fpImage) !=
     229         110 :         static_cast<size_t>(nRasterYSize))
     230             :     {
     231           0 :         CPLFree(pabyWrkBlock);
     232           0 :         CPLError(CE_Failure, CPLE_FileIO, ".bt Write failed:%s",
     233           0 :                  VSIStrerror(errno));
     234           0 :         return CE_Failure;
     235             :     }
     236             : 
     237         110 :     CPLFree(pabyWrkBlock);
     238             : 
     239         110 :     return CE_None;
     240             : }
     241             : 
     242          20 : double BTRasterBand::GetNoDataValue(int *pbSuccess /*= NULL */)
     243             : {
     244             :     // First check in PAM
     245          20 :     int bSuccess = FALSE;
     246          20 :     double dfRet = GDALPamRasterBand::GetNoDataValue(&bSuccess);
     247          20 :     if (bSuccess)
     248             :     {
     249           0 :         if (pbSuccess != nullptr)
     250           0 :             *pbSuccess = TRUE;
     251           0 :         return dfRet;
     252             :     }
     253             : 
     254             :     // Otherwise defaults to -32768
     255          20 :     if (pbSuccess != nullptr)
     256          16 :         *pbSuccess = TRUE;
     257             : 
     258          20 :     return -32768;
     259             : }
     260             : 
     261           0 : CPLErr BTRasterBand::SetNoDataValue(double dfNoDataValue)
     262             : 
     263             : {
     264             :     // First check if there's an existing nodata value in PAM
     265           0 :     int bSuccess = FALSE;
     266           0 :     GDALPamRasterBand::GetNoDataValue(&bSuccess);
     267           0 :     if (bSuccess)
     268             :     {
     269             :         // if so override it in PAM
     270           0 :         return GDALPamRasterBand::SetNoDataValue(dfNoDataValue);
     271             :     }
     272             : 
     273             :     // if nothing in PAM yet and the nodatavalue is the default one, do
     274             :     // nothing
     275           0 :     if (dfNoDataValue == -32768.0)
     276           0 :         return CE_None;
     277             :     // other nodata value ? then go to PAM
     278           0 :     return GDALPamRasterBand::SetNoDataValue(dfNoDataValue);
     279             : }
     280             : 
     281             : /************************************************************************/
     282             : /*                            GetUnitType()                             */
     283             : /************************************************************************/
     284             : 
     285           0 : static bool approx_equals(float a, float b)
     286             : {
     287           0 :     const float epsilon = 1e-5f;
     288           0 :     return fabs(a - b) <= epsilon;
     289             : }
     290             : 
     291           0 : const char *BTRasterBand::GetUnitType(void)
     292             : {
     293           0 :     const BTDataset &ds = *cpl::down_cast<const BTDataset *>(poDS);
     294           0 :     float f = ds.m_fVscale;
     295           0 :     if (f == 1.0f)
     296           0 :         return "m";
     297           0 :     if (approx_equals(f, 0.3048f))
     298           0 :         return "ft";
     299           0 :     if (approx_equals(f, 1200.0f / 3937.0f))
     300           0 :         return "sft";
     301             : 
     302             :     // todo: the BT spec allows for any value for
     303             :     // ds.m_fVscale, so rigorous adherence would
     304             :     // require testing for all possible units you
     305             :     // may want to support, such as km, yards, miles, etc.
     306             :     // But m/ft/sft seem to be the top three.
     307             : 
     308           0 :     return "";
     309             : }
     310             : 
     311             : /************************************************************************/
     312             : /*                            SetUnitType()                             */
     313             : /************************************************************************/
     314             : 
     315           0 : CPLErr BTRasterBand::SetUnitType(const char *psz)
     316             : {
     317           0 :     BTDataset &ds = *cpl::down_cast<BTDataset *>(poDS);
     318           0 :     if (EQUAL(psz, "m"))
     319           0 :         ds.m_fVscale = 1.0f;
     320           0 :     else if (EQUAL(psz, "ft"))
     321           0 :         ds.m_fVscale = 0.3048f;
     322           0 :     else if (EQUAL(psz, "sft"))
     323           0 :         ds.m_fVscale = 1200.0f / 3937.0f;
     324             :     else
     325           0 :         return CE_Failure;
     326             : 
     327           0 :     float fScale = ds.m_fVscale;
     328             : 
     329           0 :     CPL_LSBPTR32(&fScale);
     330             : 
     331             :     // Update header's elevation scale field.
     332           0 :     memcpy(ds.abyHeader + 62, &fScale, sizeof(fScale));
     333             : 
     334           0 :     ds.bHeaderModified = TRUE;
     335           0 :     return CE_None;
     336             : }
     337             : 
     338             : /************************************************************************/
     339             : /* ==================================================================== */
     340             : /*                              BTDataset                               */
     341             : /* ==================================================================== */
     342             : /************************************************************************/
     343             : 
     344             : /************************************************************************/
     345             : /*                             BTDataset()                              */
     346             : /************************************************************************/
     347             : 
     348          27 : BTDataset::BTDataset()
     349             :     : fpImage(nullptr), bGeoTransformValid(FALSE), nVersionCode(0),
     350          27 :       bHeaderModified(FALSE), m_fVscale(0.0)
     351             : {
     352          27 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     353             : 
     354          27 :     memset(abyHeader, 0, sizeof(abyHeader));
     355          27 : }
     356             : 
     357             : /************************************************************************/
     358             : /*                             ~BTDataset()                             */
     359             : /************************************************************************/
     360             : 
     361          54 : BTDataset::~BTDataset()
     362             : 
     363             : {
     364          27 :     BTDataset::FlushCache(true);
     365          27 :     if (fpImage != nullptr)
     366             :     {
     367          27 :         if (VSIFCloseL(fpImage) != 0)
     368             :         {
     369           0 :             CPLError(CE_Failure, CPLE_FileIO, "I/O error");
     370             :         }
     371             :     }
     372          54 : }
     373             : 
     374             : /************************************************************************/
     375             : /*                             FlushCache()                             */
     376             : /*                                                                      */
     377             : /*      We override this to include flush out the header block.         */
     378             : /************************************************************************/
     379             : 
     380          28 : CPLErr BTDataset::FlushCache(bool bAtClosing)
     381             : 
     382             : {
     383          28 :     CPLErr eErr = GDALDataset::FlushCache(bAtClosing);
     384             : 
     385          28 :     if (!bHeaderModified)
     386          17 :         return eErr;
     387             : 
     388          11 :     bHeaderModified = FALSE;
     389             : 
     390          22 :     if (VSIFSeekL(fpImage, 0, SEEK_SET) != 0 ||
     391          11 :         VSIFWriteL(abyHeader, 256, 1, fpImage) != 1)
     392             :     {
     393           0 :         eErr = CE_Failure;
     394             :     }
     395          11 :     return eErr;
     396             : }
     397             : 
     398             : /************************************************************************/
     399             : /*                          GetGeoTransform()                           */
     400             : /************************************************************************/
     401             : 
     402           7 : CPLErr BTDataset::GetGeoTransform(GDALGeoTransform &gt) const
     403             : 
     404             : {
     405           7 :     gt = m_gt;
     406             : 
     407           7 :     if (bGeoTransformValid)
     408           7 :         return CE_None;
     409             :     else
     410           0 :         return CE_Failure;
     411             : }
     412             : 
     413             : /************************************************************************/
     414             : /*                          SetGeoTransform()                           */
     415             : /************************************************************************/
     416             : 
     417          11 : CPLErr BTDataset::SetGeoTransform(const GDALGeoTransform &gt)
     418             : 
     419             : {
     420          11 :     CPLErr eErr = CE_None;
     421             : 
     422          11 :     m_gt = gt;
     423          11 :     if (gt[2] != 0.0 || gt[4] != 0.0)
     424             :     {
     425           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     426             :                  ".bt format does not support rotational coefficients "
     427             :                  "in geotransform, ignoring.");
     428           0 :         eErr = CE_Failure;
     429             :     }
     430             : 
     431             :     /* -------------------------------------------------------------------- */
     432             :     /*      Compute bounds, and update header info.                         */
     433             :     /* -------------------------------------------------------------------- */
     434          11 :     const double dfLeft = m_gt[0];
     435          11 :     const double dfRight = dfLeft + m_gt[1] * nRasterXSize;
     436          11 :     const double dfTop = m_gt[3];
     437          11 :     const double dfBottom = dfTop + m_gt[5] * nRasterYSize;
     438             : 
     439          11 :     memcpy(abyHeader + 28, &dfLeft, 8);
     440          11 :     memcpy(abyHeader + 36, &dfRight, 8);
     441          11 :     memcpy(abyHeader + 44, &dfBottom, 8);
     442          11 :     memcpy(abyHeader + 52, &dfTop, 8);
     443             : 
     444          11 :     CPL_LSBPTR64(abyHeader + 28);
     445          11 :     CPL_LSBPTR64(abyHeader + 36);
     446          11 :     CPL_LSBPTR64(abyHeader + 44);
     447          11 :     CPL_LSBPTR64(abyHeader + 52);
     448             : 
     449          11 :     bHeaderModified = TRUE;
     450             : 
     451          11 :     return eErr;
     452             : }
     453             : 
     454             : /************************************************************************/
     455             : /*                           SetSpatialRef()                            */
     456             : /************************************************************************/
     457             : 
     458          10 : CPLErr BTDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
     459             : 
     460             : {
     461          10 :     CPLErr eErr = CE_None;
     462             : 
     463          10 :     if (poSRS)
     464          10 :         m_oSRS = *poSRS;
     465             :     else
     466           0 :         m_oSRS.Clear();
     467             : 
     468          10 :     bHeaderModified = TRUE;
     469             : 
     470             : /* -------------------------------------------------------------------- */
     471             : /*      Parse projection.                                               */
     472             : /* -------------------------------------------------------------------- */
     473             : 
     474             : /* -------------------------------------------------------------------- */
     475             : /*      Linear units.                                                   */
     476             : /* -------------------------------------------------------------------- */
     477             : #if 0
     478             :     if( m_oSRS.IsGeographic() )
     479             :     {
     480             :         // nShortTemp = 0;
     481             :     }
     482             :     else
     483             :     {
     484             :         const double dfLinear = m_oSRS.GetLinearUnits();
     485             : 
     486             :         if( std::abs(dfLinear - 0.3048) < 0.0000001 )
     487             :             nShortTemp = 2;
     488             :         else if( std::abs(dfLinear - CPLAtof(SRS_UL_US_FOOT_CONV))
     489             :                  < 0.00000001 )
     490             :             nShortTemp = 3;
     491             :         else
     492             :             nShortTemp = 1;
     493             :     }
     494             : #endif
     495          10 :     GInt16 nShortTemp = CPL_LSBWORD16(1);
     496          10 :     memcpy(abyHeader + 22, &nShortTemp, 2);
     497             : 
     498             :     /* -------------------------------------------------------------------- */
     499             :     /*      UTM Zone                                                        */
     500             :     /* -------------------------------------------------------------------- */
     501          10 :     int bNorth = FALSE;
     502             : 
     503          10 :     nShortTemp = static_cast<GInt16>(m_oSRS.GetUTMZone(&bNorth));
     504          10 :     if (bNorth)
     505           3 :         nShortTemp = -nShortTemp;
     506             : 
     507          10 :     CPL_LSBPTR16(&nShortTemp);
     508          10 :     memcpy(abyHeader + 24, &nShortTemp, 2);
     509             : 
     510             :     /* -------------------------------------------------------------------- */
     511             :     /*      Datum                                                           */
     512             :     /* -------------------------------------------------------------------- */
     513          14 :     if (m_oSRS.GetAuthorityName("GEOGCS|DATUM") != nullptr &&
     514           4 :         EQUAL(m_oSRS.GetAuthorityName("GEOGCS|DATUM"), "EPSG"))
     515           4 :         nShortTemp = static_cast<GInt16>(
     516           4 :             atoi(m_oSRS.GetAuthorityCode("GEOGCS|DATUM")) + 2000);
     517             :     else
     518           6 :         nShortTemp = -2;
     519          10 :     CPL_LSBPTR16(&nShortTemp); /* datum unknown */
     520          10 :     memcpy(abyHeader + 26, &nShortTemp, 2);
     521             : 
     522             :     /* -------------------------------------------------------------------- */
     523             :     /*      Write out the projection to a .prj file.                        */
     524             :     /* -------------------------------------------------------------------- */
     525          10 :     char *pszProjection = nullptr;
     526          10 :     const char *const apszOptions[] = {"FORMAT=WKT1", nullptr};
     527          10 :     m_oSRS.exportToWkt(&pszProjection, apszOptions);
     528          10 :     if (pszProjection)
     529             :     {
     530             :         const std::string osPrjFile =
     531          20 :             CPLResetExtensionSafe(GetDescription(), "prj");
     532          10 :         VSILFILE *fp = VSIFOpenL(osPrjFile.c_str(), "wt");
     533          10 :         if (fp != nullptr)
     534             :         {
     535          10 :             CPL_IGNORE_RET_VAL(VSIFPrintfL(fp, "%s\n", pszProjection));
     536          10 :             CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     537          10 :             abyHeader[60] = 1;
     538             :         }
     539             :         else
     540             :         {
     541           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     542             :                      "Unable to write out .prj file.");
     543           0 :             eErr = CE_Failure;
     544             :         }
     545          10 :         CPLFree(pszProjection);
     546             :     }
     547             : 
     548          10 :     return eErr;
     549             : }
     550             : 
     551             : /************************************************************************/
     552             : /*                                Open()                                */
     553             : /************************************************************************/
     554             : 
     555       34118 : GDALDataset *BTDataset::Open(GDALOpenInfo *poOpenInfo)
     556             : 
     557             : {
     558             :     /* -------------------------------------------------------------------- */
     559             :     /*      Verify that this is some form of binterr file.                  */
     560             :     /* -------------------------------------------------------------------- */
     561       34118 :     if (poOpenInfo->nHeaderBytes < 256 || poOpenInfo->fpL == nullptr)
     562       31187 :         return nullptr;
     563             : 
     564        2931 :     if (!STARTS_WITH(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
     565             :                      "binterr"))
     566        2904 :         return nullptr;
     567             : 
     568             :     /* -------------------------------------------------------------------- */
     569             :     /*      Create the dataset.                                             */
     570             :     /* -------------------------------------------------------------------- */
     571          27 :     BTDataset *poDS = new BTDataset();
     572             : 
     573          27 :     memcpy(poDS->abyHeader, poOpenInfo->pabyHeader, 256);
     574             : 
     575             :     /* -------------------------------------------------------------------- */
     576             :     /*      Get the version.                                                */
     577             :     /* -------------------------------------------------------------------- */
     578          27 :     char szVersion[4] = {};
     579             : 
     580          27 :     strncpy(szVersion, reinterpret_cast<char *>(poDS->abyHeader + 7), 3);
     581          27 :     szVersion[3] = '\0';
     582          27 :     poDS->nVersionCode = static_cast<int>(CPLAtof(szVersion) * 10);
     583             : 
     584             :     /* -------------------------------------------------------------------- */
     585             :     /*      Extract core header information, being careful about the        */
     586             :     /*      version.                                                        */
     587             :     /* -------------------------------------------------------------------- */
     588             : 
     589          27 :     GInt32 nIntTemp = 0;
     590          27 :     memcpy(&nIntTemp, poDS->abyHeader + 10, 4);
     591          27 :     poDS->nRasterXSize = CPL_LSBWORD32(nIntTemp);
     592             : 
     593          27 :     memcpy(&nIntTemp, poDS->abyHeader + 14, 4);
     594          27 :     poDS->nRasterYSize = CPL_LSBWORD32(nIntTemp);
     595             : 
     596          27 :     if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
     597             :     {
     598           0 :         delete poDS;
     599           0 :         return nullptr;
     600             :     }
     601             : 
     602          27 :     GInt16 nDataSize = 0;
     603          27 :     memcpy(&nDataSize, poDS->abyHeader + 18, 2);
     604          27 :     CPL_LSBPTR16(&nDataSize);
     605             : 
     606          27 :     GDALDataType eType = GDT_Unknown;
     607          27 :     if (poDS->abyHeader[20] != 0 && nDataSize == 4)
     608          15 :         eType = GDT_Float32;
     609          12 :     else if (poDS->abyHeader[20] == 0 && nDataSize == 4)
     610           6 :         eType = GDT_Int32;
     611           6 :     else if (poDS->abyHeader[20] == 0 && nDataSize == 2)
     612           6 :         eType = GDT_Int16;
     613             :     else
     614             :     {
     615           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     616             :                  ".bt file data type unknown, got datasize=%d.", nDataSize);
     617           0 :         delete poDS;
     618           0 :         return nullptr;
     619             :     }
     620             : 
     621             :     /*
     622             :         rcg, apr 7/06: read offset 62 for vert. units.
     623             :         If zero, assume 1.0 as per spec.
     624             : 
     625             :     */
     626          27 :     memcpy(&poDS->m_fVscale, poDS->abyHeader + 62, 4);
     627          27 :     CPL_LSBPTR32(&poDS->m_fVscale);
     628          27 :     if (poDS->m_fVscale == 0.0f)
     629           0 :         poDS->m_fVscale = 1.0f;
     630             : 
     631             :     /* -------------------------------------------------------------------- */
     632             :     /*      Try to read a .prj file if it is indicated.                     */
     633             :     /* -------------------------------------------------------------------- */
     634          27 :     OGRSpatialReference oSRS;
     635          27 :     oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     636             : 
     637          27 :     if (poDS->nVersionCode >= 12 && poDS->abyHeader[60] != 0)
     638             :     {
     639             :         const std::string osPrjFile =
     640          22 :             CPLResetExtensionSafe(poOpenInfo->pszFilename, "prj");
     641          11 :         VSILFILE *fp = VSIFOpenL(osPrjFile.c_str(), "rt");
     642          11 :         if (fp != nullptr)
     643             :         {
     644          11 :             const int nBufMax = 10000;
     645             : 
     646          11 :             char *pszBuffer = static_cast<char *>(CPLMalloc(nBufMax));
     647             :             const int nBytes =
     648          11 :                 static_cast<int>(VSIFReadL(pszBuffer, 1, nBufMax - 1, fp));
     649          11 :             CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     650             : 
     651          11 :             pszBuffer[nBytes] = '\0';
     652             : 
     653          11 :             if (oSRS.importFromWkt(pszBuffer) != OGRERR_NONE)
     654             :             {
     655           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     656             :                          "Unable to parse .prj file, "
     657             :                          "coordinate system missing.");
     658             :             }
     659          11 :             CPLFree(pszBuffer);
     660             :         }
     661             :     }
     662             : 
     663             :     /* -------------------------------------------------------------------- */
     664             :     /*      If we didn't find a .prj file, try to use internal info.        */
     665             :     /* -------------------------------------------------------------------- */
     666          27 :     if (oSRS.GetRoot() == nullptr)
     667             :     {
     668          16 :         GInt16 nUTMZone = 0;
     669          16 :         memcpy(&nUTMZone, poDS->abyHeader + 24, 2);
     670          16 :         CPL_LSBPTR16(&nUTMZone);
     671             : 
     672          16 :         GInt16 nDatum = 0;
     673          16 :         memcpy(&nDatum, poDS->abyHeader + 26, 2);
     674          16 :         CPL_LSBPTR16(&nDatum);
     675             : 
     676          16 :         GInt16 nHUnits = 0;
     677          16 :         memcpy(&nHUnits, poDS->abyHeader + 22, 2);
     678          16 :         CPL_LSBPTR16(&nHUnits);
     679             : 
     680          16 :         if (nUTMZone != 0)
     681           0 :             oSRS.SetUTM(std::abs(static_cast<int>(nUTMZone)), nUTMZone > 0);
     682          16 :         else if (nHUnits != 0)
     683          16 :             oSRS.SetLocalCS("Unknown");
     684             : 
     685          16 :         if (nHUnits == 1)
     686          16 :             oSRS.SetLinearUnits(SRS_UL_METER, 1.0);
     687           0 :         else if (nHUnits == 2)
     688           0 :             oSRS.SetLinearUnits(SRS_UL_FOOT, CPLAtof(SRS_UL_FOOT_CONV));
     689           0 :         else if (nHUnits == 3)
     690           0 :             oSRS.SetLinearUnits(SRS_UL_US_FOOT, CPLAtof(SRS_UL_US_FOOT_CONV));
     691             : 
     692             :         // Translate some of the more obvious old USGS datum codes
     693          16 :         if (nDatum == 0)
     694           0 :             nDatum = 6201;
     695          16 :         else if (nDatum == 1)
     696           0 :             nDatum = 6209;
     697          16 :         else if (nDatum == 2)
     698           0 :             nDatum = 6210;
     699          16 :         else if (nDatum == 3)
     700           0 :             nDatum = 6202;
     701          16 :         else if (nDatum == 4)
     702           0 :             nDatum = 6203;
     703          16 :         else if (nDatum == 6)
     704           0 :             nDatum = 6222;
     705          16 :         else if (nDatum == 7)
     706           0 :             nDatum = 6230;
     707          16 :         else if (nDatum == 13)
     708           0 :             nDatum = 6267;
     709          16 :         else if (nDatum == 14)
     710           0 :             nDatum = 6269;
     711          16 :         else if (nDatum == 17)
     712           0 :             nDatum = 6277;
     713          16 :         else if (nDatum == 19)
     714           0 :             nDatum = 6284;
     715          16 :         else if (nDatum == 21)
     716           0 :             nDatum = 6301;
     717          16 :         else if (nDatum == 22)
     718           0 :             nDatum = 6322;
     719          16 :         else if (nDatum == 23)
     720           0 :             nDatum = 6326;
     721             : 
     722          16 :         if (!oSRS.IsLocal())
     723             :         {
     724           0 :             if (nDatum >= 6000)
     725             :             {
     726             :                 char szName[32];
     727           0 :                 snprintf(szName, sizeof(szName), "EPSG:%d", nDatum - 2000);
     728           0 :                 oSRS.SetWellKnownGeogCS(szName);
     729             :             }
     730             :             else
     731           0 :                 oSRS.SetWellKnownGeogCS("WGS84");
     732             :         }
     733             :     }
     734             : 
     735             :     /* -------------------------------------------------------------------- */
     736             :     /*      Convert coordinate system back to WKT.                          */
     737             :     /* -------------------------------------------------------------------- */
     738          27 :     if (oSRS.GetRoot() != nullptr)
     739          27 :         poDS->m_oSRS = std::move(oSRS);
     740             : 
     741             :     /* -------------------------------------------------------------------- */
     742             :     /*      Get georeferencing bounds.                                      */
     743             :     /* -------------------------------------------------------------------- */
     744          27 :     if (poDS->nVersionCode >= 11)
     745             :     {
     746          27 :         double dfLeft = 0.0;
     747          27 :         memcpy(&dfLeft, poDS->abyHeader + 28, 8);
     748          27 :         CPL_LSBPTR64(&dfLeft);
     749             : 
     750          27 :         double dfRight = 0.0;
     751          27 :         memcpy(&dfRight, poDS->abyHeader + 36, 8);
     752          27 :         CPL_LSBPTR64(&dfRight);
     753             : 
     754          27 :         double dfBottom = 0.0;
     755          27 :         memcpy(&dfBottom, poDS->abyHeader + 44, 8);
     756          27 :         CPL_LSBPTR64(&dfBottom);
     757             : 
     758          27 :         double dfTop = 0.0;
     759          27 :         memcpy(&dfTop, poDS->abyHeader + 52, 8);
     760          27 :         CPL_LSBPTR64(&dfTop);
     761             : 
     762          27 :         poDS->m_gt[0] = dfLeft;
     763          27 :         poDS->m_gt[1] = (dfRight - dfLeft) / poDS->nRasterXSize;
     764          27 :         poDS->m_gt[2] = 0.0;
     765          27 :         poDS->m_gt[3] = dfTop;
     766          27 :         poDS->m_gt[4] = 0.0;
     767          27 :         poDS->m_gt[5] = (dfBottom - dfTop) / poDS->nRasterYSize;
     768             : 
     769          27 :         poDS->bGeoTransformValid = TRUE;
     770             :     }
     771             : 
     772          27 :     poDS->eAccess = poOpenInfo->eAccess;
     773          27 :     poDS->fpImage = poOpenInfo->fpL;
     774          27 :     poOpenInfo->fpL = nullptr;
     775             : 
     776             :     /* -------------------------------------------------------------------- */
     777             :     /*      Create band information objects                                 */
     778             :     /* -------------------------------------------------------------------- */
     779          27 :     poDS->SetBand(1, new BTRasterBand(poDS, poDS->fpImage, eType));
     780             : 
     781             : #ifdef notdef
     782             :     poDS->bGeoTransformValid =
     783             :         GDALReadWorldFile(poOpenInfo->pszFilename, ".wld", m_gt.data());
     784             : #endif
     785             : 
     786             :     /* -------------------------------------------------------------------- */
     787             :     /*      Initialize any PAM information.                                 */
     788             :     /* -------------------------------------------------------------------- */
     789          27 :     poDS->SetDescription(poOpenInfo->pszFilename);
     790          27 :     poDS->TryLoadXML();
     791             : 
     792             :     /* -------------------------------------------------------------------- */
     793             :     /*      Check for overviews.                                            */
     794             :     /* -------------------------------------------------------------------- */
     795          27 :     poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
     796             : 
     797          27 :     return poDS;
     798             : }
     799             : 
     800             : /************************************************************************/
     801             : /*                               Create()                               */
     802             : /************************************************************************/
     803             : 
     804          66 : GDALDataset *BTDataset::Create(const char *pszFilename, int nXSize, int nYSize,
     805             :                                int nBandsIn, GDALDataType eType,
     806             :                                CPL_UNUSED char **papszOptions)
     807             : {
     808             : 
     809             :     /* -------------------------------------------------------------------- */
     810             :     /*      Verify input options.                                           */
     811             :     /* -------------------------------------------------------------------- */
     812          66 :     if (eType != GDT_Int16 && eType != GDT_Int32 && eType != GDT_Float32)
     813             :     {
     814          38 :         CPLError(CE_Failure, CPLE_AppDefined,
     815             :                  "Attempt to create .bt dataset with an illegal "
     816             :                  "data type (%s), only Int16, Int32 and Float32 supported.",
     817             :                  GDALGetDataTypeName(eType));
     818             : 
     819          38 :         return nullptr;
     820             :     }
     821             : 
     822          28 :     if (nBandsIn != 1)
     823             :     {
     824           3 :         CPLError(
     825             :             CE_Failure, CPLE_AppDefined,
     826             :             "Attempt to create .bt dataset with %d bands, only 1 supported",
     827             :             nBandsIn);
     828             : 
     829           3 :         return nullptr;
     830             :     }
     831             : 
     832             :     /* -------------------------------------------------------------------- */
     833             :     /*      Try to create the file.                                         */
     834             :     /* -------------------------------------------------------------------- */
     835          25 :     VSILFILE *fp = VSIFOpenL(pszFilename, "wb");
     836             : 
     837          25 :     if (fp == nullptr)
     838             :     {
     839           3 :         CPLError(CE_Failure, CPLE_OpenFailed,
     840             :                  "Attempt to create file `%s' failed.", pszFilename);
     841           3 :         return nullptr;
     842             :     }
     843             : 
     844             :     /* -------------------------------------------------------------------- */
     845             :     /*      Setup base header.                                              */
     846             :     /* -------------------------------------------------------------------- */
     847          22 :     unsigned char abyHeader[256] = {};
     848             : 
     849          22 :     memcpy(abyHeader, "binterr1.3", 10);
     850             : 
     851          22 :     GInt32 nTemp = CPL_LSBWORD32(nXSize);
     852          22 :     memcpy(abyHeader + 10, &nTemp, 4);
     853             : 
     854          22 :     nTemp = CPL_LSBWORD32(nYSize);
     855          22 :     memcpy(abyHeader + 14, &nTemp, 4);
     856             : 
     857             :     GInt16 nShortTemp = static_cast<GInt16>(
     858          22 :         CPL_LSBWORD16(static_cast<GInt16>(GDALGetDataTypeSizeBytes(eType))));
     859          22 :     memcpy(abyHeader + 18, &nShortTemp, 2);
     860             : 
     861          22 :     if (eType == GDT_Float32)
     862           6 :         abyHeader[20] = 1;
     863             :     else
     864          16 :         abyHeader[20] = 0;
     865             : 
     866          22 :     nShortTemp = CPL_LSBWORD16(1); /* meters */
     867          22 :     memcpy(abyHeader + 22, &nShortTemp, 2);
     868             : 
     869          22 :     nShortTemp = CPL_LSBWORD16(0); /* not utm */
     870          22 :     memcpy(abyHeader + 24, &nShortTemp, 2);
     871             : 
     872          22 :     nShortTemp = CPL_LSBWORD16(-2); /* datum unknown */
     873          22 :     memcpy(abyHeader + 26, &nShortTemp, 2);
     874             : 
     875             :     /* -------------------------------------------------------------------- */
     876             :     /*      Set dummy extents.                                              */
     877             :     /* -------------------------------------------------------------------- */
     878          22 :     double dfLeft = 0.0;
     879          22 :     double dfRight = nXSize;
     880          22 :     double dfTop = nYSize;
     881          22 :     double dfBottom = 0.0;
     882             : 
     883          22 :     memcpy(abyHeader + 28, &dfLeft, 8);
     884          22 :     memcpy(abyHeader + 36, &dfRight, 8);
     885          22 :     memcpy(abyHeader + 44, &dfBottom, 8);
     886          22 :     memcpy(abyHeader + 52, &dfTop, 8);
     887             : 
     888          22 :     CPL_LSBPTR64(abyHeader + 28);
     889          22 :     CPL_LSBPTR64(abyHeader + 36);
     890          22 :     CPL_LSBPTR64(abyHeader + 44);
     891          22 :     CPL_LSBPTR64(abyHeader + 52);
     892             : 
     893             :     /* -------------------------------------------------------------------- */
     894             :     /*      Set dummy scale.                                                */
     895             :     /* -------------------------------------------------------------------- */
     896          22 :     float fScale = 1.0;
     897             : 
     898          22 :     memcpy(abyHeader + 62, &fScale, 4);
     899          22 :     CPL_LSBPTR32(abyHeader + 62);
     900             : 
     901             :     /* -------------------------------------------------------------------- */
     902             :     /*      Write to disk.                                                  */
     903             :     /* -------------------------------------------------------------------- */
     904          22 :     if (VSIFWriteL(abyHeader, 256, 1, fp) != 1 ||
     905          16 :         VSIFSeekL(fp,
     906          16 :                   static_cast<vsi_l_offset>(GDALGetDataTypeSizeBytes(eType)) *
     907          16 :                           nXSize * nYSize -
     908             :                       1,
     909          38 :                   SEEK_CUR) != 0 ||
     910          16 :         VSIFWriteL(abyHeader + 255, 1, 1, fp) != 1)
     911             :     {
     912          10 :         CPLError(CE_Failure, CPLE_FileIO,
     913             :                  "Failed to extent file to its full size, out of disk space?");
     914             : 
     915          10 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     916          10 :         VSIUnlink(pszFilename);
     917          10 :         return nullptr;
     918             :     }
     919             : 
     920          12 :     if (VSIFCloseL(fp) != 0)
     921             :     {
     922           0 :         CPLError(CE_Failure, CPLE_FileIO,
     923             :                  "Failed to extent file to its full size, out of disk space?");
     924           0 :         VSIUnlink(pszFilename);
     925           0 :         return nullptr;
     926             :     }
     927             : 
     928          12 :     return GDALDataset::Open(pszFilename, GDAL_OF_RASTER | GDAL_OF_UPDATE);
     929             : }
     930             : 
     931             : /************************************************************************/
     932             : /*                          GDALRegister_BT()                           */
     933             : /************************************************************************/
     934             : 
     935        2033 : void GDALRegister_BT()
     936             : 
     937             : {
     938        2033 :     if (GDALGetDriverByName("BT") != nullptr)
     939         283 :         return;
     940             : 
     941        1750 :     GDALDriver *poDriver = new GDALDriver();
     942             : 
     943        1750 :     poDriver->SetDescription("BT");
     944        1750 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
     945        1750 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
     946        1750 :                               "VTP .bt (Binary Terrain) 1.3 Format");
     947        1750 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/bt.html");
     948        1750 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "bt");
     949        1750 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
     950        1750 :                               "Int16 Int32 Float32");
     951        1750 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
     952             : 
     953        1750 :     poDriver->pfnOpen = BTDataset::Open;
     954        1750 :     poDriver->pfnCreate = BTDataset::Create;
     955             : 
     956        1750 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     957             : }

Generated by: LCOV version 1.14