LCOV - code coverage report
Current view: top level - frmts/raw - byndataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 326 405 80.5 %
Date: 2024-11-21 22:18:42 Functions: 21 21 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Natural Resources Canada's Geoid BYN file format
       4             :  * Purpose:  Implementation of BYN format
       5             :  * Author:   Ivan Lucena, ivan.lucena@outlook.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2018, Ivan Lucena
       9             :  * Copyright (c) 2018, Even Rouault
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "byndataset.h"
      16             : #include "rawdataset.h"
      17             : 
      18             : #include "cpl_string.h"
      19             : #include "gdal_frmts.h"
      20             : #include "ogr_spatialref.h"
      21             : #include "ogr_srs_api.h"
      22             : 
      23             : #include <algorithm>
      24             : #include <cstdlib>
      25             : #include <limits>
      26             : 
      27             : // Specification at
      28             : // https://www.nrcan.gc.ca/sites/www.nrcan.gc.ca/files/earthsciences/pdf/gpshgrid_e.pdf
      29             : 
      30             : const static BYNEllipsoids EllipsoidTable[] = {
      31             :     {"GRS80", 6378137.0, 298.257222101},
      32             :     {"WGS84", 6378137.0, 298.257223564},
      33             :     {"ALT1", 6378136.3, 298.256415099},
      34             :     {"GRS67", 6378160.0, 298.247167427},
      35             :     {"ELLIP1", 6378136.46, 298.256415099},
      36             :     {"ALT2", 6378136.3, 298.257},
      37             :     {"ELLIP2", 6378136.0, 298.257},
      38             :     {"CLARKE 1866", 6378206.4, 294.9786982}};
      39             : 
      40             : /************************************************************************/
      41             : /*                            BYNRasterBand()                           */
      42             : /************************************************************************/
      43             : 
      44           8 : BYNRasterBand::BYNRasterBand(GDALDataset *poDSIn, int nBandIn,
      45             :                              VSILFILE *fpRawIn, vsi_l_offset nImgOffsetIn,
      46             :                              int nPixelOffsetIn, int nLineOffsetIn,
      47           8 :                              GDALDataType eDataTypeIn, int bNativeOrderIn)
      48             :     : RawRasterBand(poDSIn, nBandIn, fpRawIn, nImgOffsetIn, nPixelOffsetIn,
      49             :                     nLineOffsetIn, eDataTypeIn, bNativeOrderIn,
      50           8 :                     RawRasterBand::OwnFP::NO)
      51             : {
      52           8 : }
      53             : 
      54             : /************************************************************************/
      55             : /*                           ~BYNRasterBand()                           */
      56             : /************************************************************************/
      57             : 
      58          16 : BYNRasterBand::~BYNRasterBand()
      59             : {
      60          16 : }
      61             : 
      62             : /************************************************************************/
      63             : /*                           GetNoDataValue()                           */
      64             : /************************************************************************/
      65             : 
      66           6 : double BYNRasterBand::GetNoDataValue(int *pbSuccess)
      67             : {
      68           6 :     if (pbSuccess)
      69           5 :         *pbSuccess = TRUE;
      70           6 :     int bSuccess = FALSE;
      71           6 :     double dfNoData = GDALPamRasterBand::GetNoDataValue(&bSuccess);
      72           6 :     if (bSuccess)
      73             :     {
      74           2 :         return dfNoData;
      75             :     }
      76           4 :     const double dfFactor =
      77           4 :         reinterpret_cast<BYNDataset *>(poDS)->hHeader.dfFactor;
      78           4 :     return eDataType == GDT_Int16 ? 32767.0 : 9999.0 * dfFactor;
      79             : }
      80             : 
      81             : /************************************************************************/
      82             : /*                              GetScale()                              */
      83             : /************************************************************************/
      84             : 
      85           1 : double BYNRasterBand::GetScale(int *pbSuccess)
      86             : {
      87           1 :     if (pbSuccess != nullptr)
      88           1 :         *pbSuccess = TRUE;
      89           1 :     const double dfFactor =
      90           1 :         reinterpret_cast<BYNDataset *>(poDS)->hHeader.dfFactor;
      91           1 :     return (dfFactor != 0.0) ? 1.0 / dfFactor : 0.0;
      92             : }
      93             : 
      94             : /************************************************************************/
      95             : /*                              SetScale()                              */
      96             : /************************************************************************/
      97             : 
      98           1 : CPLErr BYNRasterBand::SetScale(double dfNewValue)
      99             : {
     100           1 :     BYNDataset *poIDS = reinterpret_cast<BYNDataset *>(poDS);
     101           1 :     poIDS->hHeader.dfFactor = 1.0 / dfNewValue;
     102           1 :     return CE_None;
     103             : }
     104             : 
     105             : /************************************************************************/
     106             : /* ==================================================================== */
     107             : /*                              BYNDataset                              */
     108             : /* ==================================================================== */
     109             : /************************************************************************/
     110             : 
     111           8 : BYNDataset::BYNDataset()
     112             :     : fpImage(nullptr), hHeader{0, 0, 0, 0, 0, 0,   0,   0, 0.0, 0,   0, 0,
     113           8 :                                 0, 0, 0, 0, 0, 0.0, 0.0, 0, 0,   0.0, 0}
     114             : {
     115           8 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     116           8 :     adfGeoTransform[0] = 0.0;
     117           8 :     adfGeoTransform[1] = 1.0;
     118           8 :     adfGeoTransform[2] = 0.0;
     119           8 :     adfGeoTransform[3] = 0.0;
     120           8 :     adfGeoTransform[4] = 0.0;
     121           8 :     adfGeoTransform[5] = 1.0;
     122           8 : }
     123             : 
     124             : /************************************************************************/
     125             : /*                            ~BYNDataset()                             */
     126             : /************************************************************************/
     127             : 
     128          16 : BYNDataset::~BYNDataset()
     129             : 
     130             : {
     131           8 :     BYNDataset::Close();
     132          16 : }
     133             : 
     134             : /************************************************************************/
     135             : /*                              Close()                                 */
     136             : /************************************************************************/
     137             : 
     138          16 : CPLErr BYNDataset::Close()
     139             : {
     140          16 :     CPLErr eErr = CE_None;
     141          16 :     if (nOpenFlags != OPEN_FLAGS_CLOSED)
     142             :     {
     143           8 :         if (BYNDataset::FlushCache(true) != CE_None)
     144           0 :             eErr = CE_Failure;
     145             : 
     146           8 :         if (GetAccess() == GA_Update)
     147           1 :             UpdateHeader();
     148             : 
     149           8 :         if (fpImage != nullptr)
     150             :         {
     151           8 :             if (VSIFCloseL(fpImage) != 0)
     152             :             {
     153           0 :                 eErr = CE_Failure;
     154           0 :                 CPLError(CE_Failure, CPLE_FileIO, "I/O error");
     155             :             }
     156             :         }
     157             : 
     158           8 :         if (GDALPamDataset::Close() != CE_None)
     159           0 :             eErr = CE_Failure;
     160             :     }
     161          16 :     return eErr;
     162             : }
     163             : 
     164             : /************************************************************************/
     165             : /*                              Identify()                              */
     166             : /************************************************************************/
     167             : 
     168       51299 : int BYNDataset::Identify(GDALOpenInfo *poOpenInfo)
     169             : 
     170             : {
     171       51299 :     if (poOpenInfo->nHeaderBytes < BYN_HDR_SZ)
     172       48089 :         return FALSE;
     173             : 
     174             : /* -------------------------------------------------------------------- */
     175             : /*      Check file extension (.byn/.err)                                */
     176             : /* -------------------------------------------------------------------- */
     177             : #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
     178        3210 :     const char *pszFileExtension = CPLGetExtension(poOpenInfo->pszFilename);
     179             : 
     180        3210 :     if (!EQUAL(pszFileExtension, "byn") && !EQUAL(pszFileExtension, "err"))
     181             :     {
     182        3194 :         return FALSE;
     183             :     }
     184             : #endif
     185             : 
     186             :     /* -------------------------------------------------------------------- */
     187             :     /*      Check some value's ranges on header                             */
     188             :     /* -------------------------------------------------------------------- */
     189             : 
     190          16 :     BYNHeader hHeader = {0, 0, 0, 0, 0, 0,   0,   0, 0.0, 0,   0, 0,
     191             :                          0, 0, 0, 0, 0, 0.0, 0.0, 0, 0,   0.0, 0};
     192             : 
     193          16 :     buffer2header(poOpenInfo->pabyHeader, &hHeader);
     194             : 
     195          16 :     if (hHeader.nGlobal < 0 || hHeader.nGlobal > 1 || hHeader.nType < 0 ||
     196          16 :         hHeader.nType > 9 || (hHeader.nSizeOf != 2 && hHeader.nSizeOf != 4) ||
     197          16 :         hHeader.nVDatum < 0 || hHeader.nVDatum > 3 || hHeader.nDescrip < 0 ||
     198          16 :         hHeader.nDescrip > 3 || hHeader.nSubType < 0 || hHeader.nSubType > 9 ||
     199          16 :         hHeader.nDatum < 0 || hHeader.nDatum > 1 || hHeader.nEllipsoid < 0 ||
     200          16 :         hHeader.nEllipsoid > 7 || hHeader.nByteOrder < 0 ||
     201          16 :         hHeader.nByteOrder > 1 || hHeader.nScale < 0 || hHeader.nScale > 1)
     202           0 :         return FALSE;
     203             : 
     204             : #if 0
     205             :     // We have disabled those checks as invalid values are often found in some
     206             :     // datasets, such as http://s3.microsurvey.com/os/fieldgenius/geoids/Lithuania.zip
     207             :     // We don't use those fields, so we may just ignore them.
     208             :     if((hHeader.nTideSys   < 0 || hHeader.nTideSys   > 2 ||
     209             :         hHeader.nPtType    < 0 || hHeader.nPtType    > 1 ))
     210             :     {
     211             :         // Some datasets use 0xCC as a marker for invalidity for
     212             :         // records starting from Geopotential Wo
     213             :         for( int i = 52; i < 78; i++ )
     214             :         {
     215             :             if( poOpenInfo->pabyHeader[i] != 0xCC )
     216             :                 return FALSE;
     217             :         }
     218             :     }
     219             : #endif
     220             : 
     221          16 :     if (hHeader.nScale == 0)
     222             :     {
     223          32 :         if ((std::abs(static_cast<GIntBig>(hHeader.nSouth) -
     224          32 :                       (hHeader.nDLat / 2)) > BYN_MAX_LAT) ||
     225          16 :             (std::abs(static_cast<GIntBig>(hHeader.nNorth) +
     226          32 :                       (hHeader.nDLat / 2)) > BYN_MAX_LAT) ||
     227          16 :             (std::abs(static_cast<GIntBig>(hHeader.nWest) -
     228          48 :                       (hHeader.nDLon / 2)) > BYN_MAX_LON) ||
     229          16 :             (std::abs(static_cast<GIntBig>(hHeader.nEast) +
     230          16 :                       (hHeader.nDLon / 2)) > BYN_MAX_LON))
     231           0 :             return FALSE;
     232             :     }
     233             :     else
     234             :     {
     235           0 :         if ((std::abs(static_cast<GIntBig>(hHeader.nSouth) -
     236           0 :                       (hHeader.nDLat / 2)) > BYN_MAX_LAT_SCL) ||
     237           0 :             (std::abs(static_cast<GIntBig>(hHeader.nNorth) +
     238           0 :                       (hHeader.nDLat / 2)) > BYN_MAX_LAT_SCL) ||
     239           0 :             (std::abs(static_cast<GIntBig>(hHeader.nWest) -
     240           0 :                       (hHeader.nDLon / 2)) > BYN_MAX_LON_SCL) ||
     241           0 :             (std::abs(static_cast<GIntBig>(hHeader.nEast) +
     242           0 :                       (hHeader.nDLon / 2)) > BYN_MAX_LON_SCL))
     243           0 :             return FALSE;
     244             :     }
     245             : 
     246          16 :     return TRUE;
     247             : }
     248             : 
     249             : /************************************************************************/
     250             : /*                                Open()                                */
     251             : /************************************************************************/
     252             : 
     253           8 : GDALDataset *BYNDataset::Open(GDALOpenInfo *poOpenInfo)
     254             : 
     255             : {
     256           8 :     if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
     257           0 :         return nullptr;
     258             : 
     259             :     /* -------------------------------------------------------------------- */
     260             :     /*      Create a corresponding GDALDataset.                             */
     261             :     /* -------------------------------------------------------------------- */
     262             : 
     263          16 :     auto poDS = std::make_unique<BYNDataset>();
     264             : 
     265           8 :     poDS->eAccess = poOpenInfo->eAccess;
     266           8 :     std::swap(poDS->fpImage, poOpenInfo->fpL);
     267             : 
     268             :     /* -------------------------------------------------------------------- */
     269             :     /*      Read the header.                                                */
     270             :     /* -------------------------------------------------------------------- */
     271             : 
     272           8 :     buffer2header(poOpenInfo->pabyHeader, &poDS->hHeader);
     273             : 
     274             :     /********************************/
     275             :     /* Scale boundaries and spacing */
     276             :     /********************************/
     277             : 
     278           8 :     double dfSouth = poDS->hHeader.nSouth;
     279           8 :     double dfNorth = poDS->hHeader.nNorth;
     280           8 :     double dfWest = poDS->hHeader.nWest;
     281           8 :     double dfEast = poDS->hHeader.nEast;
     282           8 :     double dfDLat = poDS->hHeader.nDLat;
     283           8 :     double dfDLon = poDS->hHeader.nDLon;
     284             : 
     285           8 :     if (poDS->hHeader.nScale == 1)
     286             :     {
     287           0 :         dfSouth *= BYN_SCALE;
     288           0 :         dfNorth *= BYN_SCALE;
     289           0 :         dfWest *= BYN_SCALE;
     290           0 :         dfEast *= BYN_SCALE;
     291           0 :         dfDLat *= BYN_SCALE;
     292           0 :         dfDLon *= BYN_SCALE;
     293             :     }
     294             : 
     295             :     /******************************/
     296             :     /* Calculate rows and columns */
     297             :     /******************************/
     298             : 
     299           8 :     double dfXSize = -1;
     300           8 :     double dfYSize = -1;
     301             : 
     302           8 :     poDS->nRasterXSize = -1;
     303           8 :     poDS->nRasterYSize = -1;
     304             : 
     305           8 :     if (dfDLat != 0.0 && dfDLon != 0.0)
     306             :     {
     307           8 :         dfXSize = ((dfEast - dfWest + 1.0) / dfDLon) + 1.0;
     308           8 :         dfYSize = ((dfNorth - dfSouth + 1.0) / dfDLat) + 1.0;
     309             :     }
     310             : 
     311           8 :     if (dfXSize > 0.0 && dfXSize < std::numeric_limits<double>::max() &&
     312          16 :         dfYSize > 0.0 && dfYSize < std::numeric_limits<double>::max())
     313             :     {
     314           8 :         poDS->nRasterXSize = static_cast<GInt32>(dfXSize);
     315           8 :         poDS->nRasterYSize = static_cast<GInt32>(dfYSize);
     316             :     }
     317             : 
     318           8 :     if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
     319             :     {
     320           0 :         return nullptr;
     321             :     }
     322             : 
     323             :     /*****************************/
     324             :     /* Build GeoTransform matrix */
     325             :     /*****************************/
     326             : 
     327           8 :     poDS->adfGeoTransform[0] = (dfWest - (dfDLon / 2.0)) / 3600.0;
     328           8 :     poDS->adfGeoTransform[1] = dfDLon / 3600.0;
     329           8 :     poDS->adfGeoTransform[2] = 0.0;
     330           8 :     poDS->adfGeoTransform[3] = (dfNorth + (dfDLat / 2.0)) / 3600.0;
     331           8 :     poDS->adfGeoTransform[4] = 0.0;
     332           8 :     poDS->adfGeoTransform[5] = -1 * dfDLat / 3600.0;
     333             : 
     334             :     /*********************/
     335             :     /* Set data type     */
     336             :     /*********************/
     337             : 
     338           8 :     GDALDataType eDT = GDT_Unknown;
     339             : 
     340           8 :     if (poDS->hHeader.nSizeOf == 2)
     341           0 :         eDT = GDT_Int16;
     342           8 :     else if (poDS->hHeader.nSizeOf == 4)
     343           8 :         eDT = GDT_Int32;
     344             :     else
     345             :     {
     346           0 :         return nullptr;
     347             :     }
     348             : 
     349             :     /* -------------------------------------------------------------------- */
     350             :     /*      Create band information object.                                 */
     351             :     /* -------------------------------------------------------------------- */
     352             : 
     353           8 :     const int nDTSize = GDALGetDataTypeSizeBytes(eDT);
     354             : 
     355           8 :     int bIsLSB = poDS->hHeader.nByteOrder == 1 ? 1 : 0;
     356             : 
     357             :     auto poBand = std::make_unique<BYNRasterBand>(
     358           8 :         poDS.get(), 1, poDS->fpImage, BYN_HDR_SZ, nDTSize,
     359          24 :         poDS->nRasterXSize * nDTSize, eDT, CPL_IS_LSB == bIsLSB);
     360           8 :     if (!poBand->IsValid())
     361           0 :         return nullptr;
     362           8 :     poDS->SetBand(1, std::move(poBand));
     363             : 
     364             :     /* -------------------------------------------------------------------- */
     365             :     /*      Initialize any PAM information.                                 */
     366             :     /* -------------------------------------------------------------------- */
     367             : 
     368           8 :     poDS->SetDescription(poOpenInfo->pszFilename);
     369           8 :     poDS->TryLoadXML();
     370             : 
     371             :     /* -------------------------------------------------------------------- */
     372             :     /*      Check for overviews.                                            */
     373             :     /* -------------------------------------------------------------------- */
     374             : 
     375           8 :     poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
     376             : 
     377           8 :     return poDS.release();
     378             : }
     379             : 
     380             : /************************************************************************/
     381             : /*                          GetGeoTransform()                           */
     382             : /************************************************************************/
     383             : 
     384           2 : CPLErr BYNDataset::GetGeoTransform(double *padfTransform)
     385             : 
     386             : {
     387           2 :     memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6);
     388           2 :     return CE_None;
     389             : }
     390             : 
     391             : /************************************************************************/
     392             : /*                          SetGeoTransform()                           */
     393             : /************************************************************************/
     394             : 
     395           1 : CPLErr BYNDataset::SetGeoTransform(double *padfTransform)
     396             : 
     397             : {
     398           1 :     if (padfTransform[2] != 0.0 || padfTransform[4] != 0.0)
     399             :     {
     400           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     401             :                  "Attempt to write skewed or rotated geotransform to byn.");
     402           0 :         return CE_Failure;
     403             :     }
     404           1 :     memcpy(adfGeoTransform, padfTransform, sizeof(double) * 6);
     405           1 :     return CE_None;
     406             : }
     407             : 
     408             : /************************************************************************/
     409             : /*                          GetSpatialRef()                             */
     410             : /************************************************************************/
     411             : 
     412           1 : const OGRSpatialReference *BYNDataset::GetSpatialRef() const
     413             : 
     414             : {
     415           1 :     if (!m_oSRS.IsEmpty())
     416           0 :         return &m_oSRS;
     417             : 
     418             :     /* Try to use a prefefined EPSG compound CS */
     419             : 
     420           1 :     if (hHeader.nDatum == 1 && hHeader.nVDatum == 2)
     421             :     {
     422           0 :         m_oSRS.importFromEPSG(BYN_DATUM_1_VDATUM_2);
     423           0 :         return &m_oSRS;
     424             :     }
     425             : 
     426             :     /* Build the GEOGCS based on Datum ( or Ellipsoid )*/
     427             : 
     428           1 :     bool bNoGeogCS = false;
     429             : 
     430           1 :     if (hHeader.nDatum == 0)
     431           1 :         m_oSRS.importFromEPSG(BYN_DATUM_0);
     432           0 :     else if (hHeader.nDatum == 1)
     433           0 :         m_oSRS.importFromEPSG(BYN_DATUM_1);
     434             :     else
     435             :     {
     436             :         /* Build GEOGCS based on Ellipsoid (Table 3) */
     437             : 
     438           0 :         if (hHeader.nEllipsoid > -1 &&
     439           0 :             hHeader.nEllipsoid <
     440             :                 static_cast<GInt16>(CPL_ARRAYSIZE(EllipsoidTable)))
     441           0 :             m_oSRS.SetGeogCS(
     442           0 :                 CPLSPrintf("BYN Ellipsoid(%d)", hHeader.nEllipsoid),
     443           0 :                 "Unspecified", EllipsoidTable[hHeader.nEllipsoid].pszName,
     444           0 :                 EllipsoidTable[hHeader.nEllipsoid].dfSemiMajor,
     445           0 :                 EllipsoidTable[hHeader.nEllipsoid].dfInvFlattening);
     446             :         else
     447           0 :             bNoGeogCS = true;
     448             :     }
     449             : 
     450             :     /* Build the VERT_CS based on VDatum */
     451             : 
     452           2 :     OGRSpatialReference oSRSComp;
     453           2 :     OGRSpatialReference oSRSVert;
     454             : 
     455           1 :     int nVertCS = 0;
     456             : 
     457           1 :     if (hHeader.nVDatum == 1)
     458           0 :         nVertCS = BYN_VDATUM_1;
     459           1 :     else if (hHeader.nVDatum == 2)
     460           1 :         nVertCS = BYN_VDATUM_2;
     461           0 :     else if (hHeader.nVDatum == 3)
     462           0 :         nVertCS = BYN_VDATUM_3;
     463             :     else
     464             :     {
     465             :         /* Return GEOGCS ( .err files ) */
     466             : 
     467           0 :         if (bNoGeogCS)
     468           0 :             return nullptr;
     469             : 
     470           0 :         return &m_oSRS;
     471             :     }
     472             : 
     473           1 :     oSRSVert.importFromEPSG(nVertCS);
     474             : 
     475             :     /* Create CPMPD_CS with GEOGCS and VERT_CS */
     476             : 
     477           1 :     if (oSRSComp.SetCompoundCS(CPLSPrintf("BYN Datum(%d) & VDatum(%d)",
     478           1 :                                           hHeader.nDatum, hHeader.nDatum),
     479           2 :                                &m_oSRS, &oSRSVert) == CE_None)
     480             :     {
     481             :         /* Return COMPD_CS with GEOGCS and VERT_CS */
     482             : 
     483           1 :         m_oSRS = std::move(oSRSComp);
     484           1 :         m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     485           1 :         return &m_oSRS;
     486             :     }
     487             : 
     488           0 :     return nullptr;
     489             : }
     490             : 
     491             : /************************************************************************/
     492             : /*                          SetSpatialRef()                             */
     493             : /************************************************************************/
     494             : 
     495           1 : CPLErr BYNDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
     496             : 
     497             : {
     498           1 :     if (poSRS == nullptr)
     499           0 :         return CE_Failure;
     500             : 
     501           1 :     m_oSRS = *poSRS;
     502             : 
     503             :     /* Try to recognize prefefined EPSG compound CS */
     504             : 
     505           1 :     if (poSRS->IsCompound())
     506             :     {
     507           1 :         const char *pszAuthName = poSRS->GetAuthorityName("COMPD_CS");
     508           1 :         const char *pszAuthCode = poSRS->GetAuthorityCode("COMPD_CS");
     509             : 
     510           1 :         if (pszAuthName != nullptr && pszAuthCode != nullptr &&
     511           0 :             EQUAL(pszAuthName, "EPSG") &&
     512           0 :             atoi(pszAuthCode) == BYN_DATUM_1_VDATUM_2)
     513             :         {
     514           0 :             hHeader.nVDatum = 2;
     515           0 :             hHeader.nDatum = 1;
     516           0 :             return CE_None;
     517             :         }
     518             :     }
     519             : 
     520           1 :     OGRSpatialReference oSRSTemp;
     521             : 
     522             :     /* Try to match GEOGCS */
     523             : 
     524           1 :     if (poSRS->IsGeographic())
     525             :     {
     526           1 :         oSRSTemp.importFromEPSG(BYN_DATUM_0);
     527           1 :         if (poSRS->IsSameGeogCS(&oSRSTemp))
     528           1 :             hHeader.nDatum = 0;
     529             :         else
     530             :         {
     531           0 :             oSRSTemp.importFromEPSG(BYN_DATUM_1);
     532           0 :             if (poSRS->IsSameGeogCS(&oSRSTemp))
     533           0 :                 hHeader.nDatum = 1;
     534             :         }
     535             :     }
     536             : 
     537             :     /* Try to match VERT_CS */
     538             : 
     539           1 :     if (poSRS->IsVertical())
     540             :     {
     541           1 :         oSRSTemp.importFromEPSG(BYN_VDATUM_1);
     542           1 :         if (poSRS->IsSameVertCS(&oSRSTemp))
     543           0 :             hHeader.nVDatum = 1;
     544             :         else
     545             :         {
     546           1 :             oSRSTemp.importFromEPSG(BYN_VDATUM_2);
     547           1 :             if (poSRS->IsSameVertCS(&oSRSTemp))
     548           1 :                 hHeader.nVDatum = 2;
     549             :             else
     550             :             {
     551           0 :                 oSRSTemp.importFromEPSG(BYN_VDATUM_3);
     552           0 :                 if (poSRS->IsSameVertCS(&oSRSTemp))
     553           0 :                     hHeader.nVDatum = 3;
     554             :             }
     555             :         }
     556             :     }
     557             : 
     558           1 :     return CE_None;
     559             : }
     560             : 
     561             : /*----------------------------------------------------------------------*/
     562             : /*                           buffer2header()                            */
     563             : /*----------------------------------------------------------------------*/
     564             : 
     565          24 : void BYNDataset::buffer2header(const GByte *pabyBuf, BYNHeader *pohHeader)
     566             : 
     567             : {
     568          24 :     memcpy(&pohHeader->nSouth, pabyBuf, 4);
     569          24 :     memcpy(&pohHeader->nNorth, pabyBuf + 4, 4);
     570          24 :     memcpy(&pohHeader->nWest, pabyBuf + 8, 4);
     571          24 :     memcpy(&pohHeader->nEast, pabyBuf + 12, 4);
     572          24 :     memcpy(&pohHeader->nDLat, pabyBuf + 16, 2);
     573          24 :     memcpy(&pohHeader->nDLon, pabyBuf + 18, 2);
     574          24 :     memcpy(&pohHeader->nGlobal, pabyBuf + 20, 2);
     575          24 :     memcpy(&pohHeader->nType, pabyBuf + 22, 2);
     576          24 :     memcpy(&pohHeader->dfFactor, pabyBuf + 24, 8);
     577          24 :     memcpy(&pohHeader->nSizeOf, pabyBuf + 32, 2);
     578          24 :     memcpy(&pohHeader->nVDatum, pabyBuf + 34, 2);
     579          24 :     memcpy(&pohHeader->nDescrip, pabyBuf + 40, 2);
     580          24 :     memcpy(&pohHeader->nSubType, pabyBuf + 42, 2);
     581          24 :     memcpy(&pohHeader->nDatum, pabyBuf + 44, 2);
     582          24 :     memcpy(&pohHeader->nEllipsoid, pabyBuf + 46, 2);
     583          24 :     memcpy(&pohHeader->nByteOrder, pabyBuf + 48, 2);
     584          24 :     memcpy(&pohHeader->nScale, pabyBuf + 50, 2);
     585          24 :     memcpy(&pohHeader->dfWo, pabyBuf + 52, 8);
     586          24 :     memcpy(&pohHeader->dfGM, pabyBuf + 60, 8);
     587          24 :     memcpy(&pohHeader->nTideSys, pabyBuf + 68, 2);
     588          24 :     memcpy(&pohHeader->nRealiz, pabyBuf + 70, 2);
     589          24 :     memcpy(&pohHeader->dEpoch, pabyBuf + 72, 4);
     590          24 :     memcpy(&pohHeader->nPtType, pabyBuf + 76, 2);
     591             : 
     592             : #if defined(CPL_MSB)
     593             :     CPL_LSBPTR32(&pohHeader->nSouth);
     594             :     CPL_LSBPTR32(&pohHeader->nNorth);
     595             :     CPL_LSBPTR32(&pohHeader->nWest);
     596             :     CPL_LSBPTR32(&pohHeader->nEast);
     597             :     CPL_LSBPTR16(&pohHeader->nDLat);
     598             :     CPL_LSBPTR16(&pohHeader->nDLon);
     599             :     CPL_LSBPTR16(&pohHeader->nGlobal);
     600             :     CPL_LSBPTR16(&pohHeader->nType);
     601             :     CPL_LSBPTR64(&pohHeader->dfFactor);
     602             :     CPL_LSBPTR16(&pohHeader->nSizeOf);
     603             :     CPL_LSBPTR16(&pohHeader->nVDatum);
     604             :     CPL_LSBPTR16(&pohHeader->nDescrip);
     605             :     CPL_LSBPTR16(&pohHeader->nSubType);
     606             :     CPL_LSBPTR16(&pohHeader->nDatum);
     607             :     CPL_LSBPTR16(&pohHeader->nEllipsoid);
     608             :     CPL_LSBPTR16(&pohHeader->nByteOrder);
     609             :     CPL_LSBPTR16(&pohHeader->nScale);
     610             :     CPL_LSBPTR64(&pohHeader->dfWo);
     611             :     CPL_LSBPTR64(&pohHeader->dfGM);
     612             :     CPL_LSBPTR16(&pohHeader->nTideSys);
     613             :     CPL_LSBPTR16(&pohHeader->nRealiz);
     614             :     CPL_LSBPTR32(&pohHeader->dEpoch);
     615             :     CPL_LSBPTR16(&pohHeader->nPtType);
     616             : #endif
     617             : 
     618             : #if DEBUG
     619          24 :     CPLDebug("BYN", "South         = %d", pohHeader->nSouth);
     620          24 :     CPLDebug("BYN", "North         = %d", pohHeader->nNorth);
     621          24 :     CPLDebug("BYN", "West          = %d", pohHeader->nWest);
     622          24 :     CPLDebug("BYN", "East          = %d", pohHeader->nEast);
     623          24 :     CPLDebug("BYN", "DLat          = %d", pohHeader->nDLat);
     624          24 :     CPLDebug("BYN", "DLon          = %d", pohHeader->nDLon);
     625          24 :     CPLDebug("BYN", "DGlobal       = %d", pohHeader->nGlobal);
     626          24 :     CPLDebug("BYN", "DType         = %d", pohHeader->nType);
     627          24 :     CPLDebug("BYN", "Factor        = %f", pohHeader->dfFactor);
     628          24 :     CPLDebug("BYN", "SizeOf        = %d", pohHeader->nSizeOf);
     629          24 :     CPLDebug("BYN", "VDatum        = %d", pohHeader->nVDatum);
     630          24 :     CPLDebug("BYN", "Data          = %d", pohHeader->nDescrip);
     631          24 :     CPLDebug("BYN", "SubType       = %d", pohHeader->nSubType);
     632          24 :     CPLDebug("BYN", "Datum         = %d", pohHeader->nDatum);
     633          24 :     CPLDebug("BYN", "Ellipsoid     = %d", pohHeader->nEllipsoid);
     634          24 :     CPLDebug("BYN", "ByteOrder     = %d", pohHeader->nByteOrder);
     635          24 :     CPLDebug("BYN", "Scale         = %d", pohHeader->nScale);
     636          24 :     CPLDebug("BYN", "Wo            = %f", pohHeader->dfWo);
     637          24 :     CPLDebug("BYN", "GM            = %f", pohHeader->dfGM);
     638          24 :     CPLDebug("BYN", "TideSystem    = %d", pohHeader->nTideSys);
     639          24 :     CPLDebug("BYN", "RefRealzation = %d", pohHeader->nRealiz);
     640          24 :     CPLDebug("BYN", "Epoch         = %f", pohHeader->dEpoch);
     641          24 :     CPLDebug("BYN", "PtType        = %d", pohHeader->nPtType);
     642             : #endif
     643          24 : }
     644             : 
     645             : /*----------------------------------------------------------------------*/
     646             : /*                           header2buffer()                            */
     647             : /*----------------------------------------------------------------------*/
     648             : 
     649           2 : void BYNDataset::header2buffer(const BYNHeader *pohHeader, GByte *pabyBuf)
     650             : 
     651             : {
     652           2 :     memcpy(pabyBuf, &pohHeader->nSouth, 4);
     653           2 :     memcpy(pabyBuf + 4, &pohHeader->nNorth, 4);
     654           2 :     memcpy(pabyBuf + 8, &pohHeader->nWest, 4);
     655           2 :     memcpy(pabyBuf + 12, &pohHeader->nEast, 4);
     656           2 :     memcpy(pabyBuf + 16, &pohHeader->nDLat, 2);
     657           2 :     memcpy(pabyBuf + 18, &pohHeader->nDLon, 2);
     658           2 :     memcpy(pabyBuf + 20, &pohHeader->nGlobal, 2);
     659           2 :     memcpy(pabyBuf + 22, &pohHeader->nType, 2);
     660           2 :     memcpy(pabyBuf + 24, &pohHeader->dfFactor, 8);
     661           2 :     memcpy(pabyBuf + 32, &pohHeader->nSizeOf, 2);
     662           2 :     memcpy(pabyBuf + 34, &pohHeader->nVDatum, 2);
     663           2 :     memcpy(pabyBuf + 40, &pohHeader->nDescrip, 2);
     664           2 :     memcpy(pabyBuf + 42, &pohHeader->nSubType, 2);
     665           2 :     memcpy(pabyBuf + 44, &pohHeader->nDatum, 2);
     666           2 :     memcpy(pabyBuf + 46, &pohHeader->nEllipsoid, 2);
     667           2 :     memcpy(pabyBuf + 48, &pohHeader->nByteOrder, 2);
     668           2 :     memcpy(pabyBuf + 50, &pohHeader->nScale, 2);
     669           2 :     memcpy(pabyBuf + 52, &pohHeader->dfWo, 8);
     670           2 :     memcpy(pabyBuf + 60, &pohHeader->dfGM, 8);
     671           2 :     memcpy(pabyBuf + 68, &pohHeader->nTideSys, 2);
     672           2 :     memcpy(pabyBuf + 70, &pohHeader->nRealiz, 2);
     673           2 :     memcpy(pabyBuf + 72, &pohHeader->dEpoch, 4);
     674           2 :     memcpy(pabyBuf + 76, &pohHeader->nPtType, 2);
     675             : 
     676             : #if defined(CPL_MSB)
     677             :     CPL_LSBPTR32(pabyBuf);
     678             :     CPL_LSBPTR32(pabyBuf + 4);
     679             :     CPL_LSBPTR32(pabyBuf + 8);
     680             :     CPL_LSBPTR32(pabyBuf + 12);
     681             :     CPL_LSBPTR16(pabyBuf + 16);
     682             :     CPL_LSBPTR16(pabyBuf + 18);
     683             :     CPL_LSBPTR16(pabyBuf + 20);
     684             :     CPL_LSBPTR16(pabyBuf + 22);
     685             :     CPL_LSBPTR64(pabyBuf + 24);
     686             :     CPL_LSBPTR16(pabyBuf + 32);
     687             :     CPL_LSBPTR16(pabyBuf + 34);
     688             :     CPL_LSBPTR16(pabyBuf + 40);
     689             :     CPL_LSBPTR16(pabyBuf + 42);
     690             :     CPL_LSBPTR16(pabyBuf + 44);
     691             :     CPL_LSBPTR16(pabyBuf + 46);
     692             :     CPL_LSBPTR16(pabyBuf + 48);
     693             :     CPL_LSBPTR16(pabyBuf + 50);
     694             :     CPL_LSBPTR64(pabyBuf + 52);
     695             :     CPL_LSBPTR64(pabyBuf + 60);
     696             :     CPL_LSBPTR16(pabyBuf + 68);
     697             :     CPL_LSBPTR16(pabyBuf + 70);
     698             :     CPL_LSBPTR32(pabyBuf + 72);
     699             :     CPL_LSBPTR16(pabyBuf + 76);
     700             : #endif
     701           2 : }
     702             : 
     703             : /************************************************************************/
     704             : /*                               Create()                               */
     705             : /************************************************************************/
     706             : 
     707          50 : GDALDataset *BYNDataset::Create(const char *pszFilename, int nXSize, int nYSize,
     708             :                                 int /* nBands */, GDALDataType eType,
     709             :                                 char ** /* papszOptions */)
     710             : {
     711          50 :     if (eType != GDT_Int16 && eType != GDT_Int32)
     712             :     {
     713          43 :         CPLError(CE_Failure, CPLE_AppDefined,
     714             :                  "Attempt to create byn file with unsupported data type '%s'.",
     715             :                  GDALGetDataTypeName(eType));
     716          43 :         return nullptr;
     717             :     }
     718             : 
     719             :     /* -------------------------------------------------------------------- */
     720             :     /*      Check file extension (.byn/.err)                                */
     721             :     /* -------------------------------------------------------------------- */
     722             : 
     723           7 :     char *pszFileExtension = CPLStrdup(CPLGetExtension(pszFilename));
     724             : 
     725           7 :     if (!EQUAL(pszFileExtension, "byn") && !EQUAL(pszFileExtension, "err"))
     726             :     {
     727           6 :         CPLError(
     728             :             CE_Failure, CPLE_AppDefined,
     729             :             "Attempt to create byn file with extension other than byn/err.");
     730           6 :         CPLFree(pszFileExtension);
     731           6 :         return nullptr;
     732             :     }
     733             : 
     734           1 :     CPLFree(pszFileExtension);
     735             : 
     736             :     /* -------------------------------------------------------------------- */
     737             :     /*      Try to create the file.                                         */
     738             :     /* -------------------------------------------------------------------- */
     739             : 
     740           1 :     VSILFILE *fp = VSIFOpenL(pszFilename, "wb+");
     741           1 :     if (fp == nullptr)
     742             :     {
     743           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     744             :                  "Attempt to create file `%s' failed.\n", pszFilename);
     745           0 :         return nullptr;
     746             :     }
     747             : 
     748             :     /* -------------------------------------------------------------------- */
     749             :     /*      Write an empty header.                                          */
     750             :     /* -------------------------------------------------------------------- */
     751             : 
     752           1 :     GByte abyBuf[BYN_HDR_SZ] = {'\0'};
     753             : 
     754             :     /* Load header with some commum values */
     755             : 
     756           1 :     BYNHeader hHeader = {0, 0, 0, 0, 0, 0,   0,   0, 0.0, 0,   0, 0,
     757             :                          0, 0, 0, 0, 0, 0.0, 0.0, 0, 0,   0.0, 0};
     758             : 
     759             :     /* Set temporary values on header */
     760             : 
     761           1 :     hHeader.nSouth = 0;
     762           1 :     hHeader.nNorth = nYSize - 2;
     763           1 :     hHeader.nWest = 0;
     764           1 :     hHeader.nEast = nXSize - 2;
     765           1 :     hHeader.nDLat = 1;
     766           1 :     hHeader.nDLon = 1;
     767           1 :     hHeader.nSizeOf = static_cast<GInt16>(GDALGetDataTypeSizeBytes(eType));
     768             : 
     769             :     /* Prepare buffer for writing */
     770             : 
     771           1 :     header2buffer(&hHeader, abyBuf);
     772             : 
     773             :     /* Write initial header */
     774             : 
     775           1 :     VSIFWriteL(abyBuf, BYN_HDR_SZ, 1, fp);
     776           1 :     VSIFCloseL(fp);
     777             : 
     778           1 :     return GDALDataset::FromHandle(GDALOpen(pszFilename, GA_Update));
     779             : }
     780             : 
     781             : /************************************************************************/
     782             : /*                          UpdateHeader()                              */
     783             : /************************************************************************/
     784             : 
     785           1 : void BYNDataset::UpdateHeader()
     786             : {
     787           1 :     double dfDLon = (adfGeoTransform[1] * 3600.0);
     788           1 :     double dfDLat = (adfGeoTransform[5] * 3600.0 * -1);
     789           1 :     double dfWest = (adfGeoTransform[0] * 3600.0) + (dfDLon / 2);
     790           1 :     double dfNorth = (adfGeoTransform[3] * 3600.0) - (dfDLat / 2);
     791           1 :     double dfSouth = dfNorth - ((nRasterYSize - 1) * dfDLat);
     792           1 :     double dfEast = dfWest + ((nRasterXSize - 1) * dfDLon);
     793             : 
     794           1 :     if (hHeader.nScale == 1)
     795             :     {
     796           0 :         dfSouth /= BYN_SCALE;
     797           0 :         dfNorth /= BYN_SCALE;
     798           0 :         dfWest /= BYN_SCALE;
     799           0 :         dfEast /= BYN_SCALE;
     800           0 :         dfDLat /= BYN_SCALE;
     801           0 :         dfDLon /= BYN_SCALE;
     802             :     }
     803             : 
     804           1 :     hHeader.nSouth = static_cast<GInt32>(dfSouth);
     805           1 :     hHeader.nNorth = static_cast<GInt32>(dfNorth);
     806           1 :     hHeader.nWest = static_cast<GInt32>(dfWest);
     807           1 :     hHeader.nEast = static_cast<GInt32>(dfEast);
     808           1 :     hHeader.nDLat = static_cast<GInt16>(dfDLat);
     809           1 :     hHeader.nDLon = static_cast<GInt16>(dfDLon);
     810             : 
     811             :     GByte abyBuf[BYN_HDR_SZ];
     812             : 
     813           1 :     header2buffer(&hHeader, abyBuf);
     814             : 
     815           1 :     const char *pszValue = GetMetadataItem("GLOBAL");
     816           1 :     if (pszValue != nullptr)
     817           0 :         hHeader.nGlobal = static_cast<GInt16>(atoi(pszValue));
     818             : 
     819           1 :     pszValue = GetMetadataItem("TYPE");
     820           1 :     if (pszValue != nullptr)
     821           0 :         hHeader.nType = static_cast<GInt16>(atoi(pszValue));
     822             : 
     823           1 :     pszValue = GetMetadataItem("DESCRIPTION");
     824           1 :     if (pszValue != nullptr)
     825           0 :         hHeader.nDescrip = static_cast<GInt16>(atoi(pszValue));
     826             : 
     827           1 :     pszValue = GetMetadataItem("SUBTYPE");
     828           1 :     if (pszValue != nullptr)
     829           0 :         hHeader.nSubType = static_cast<GInt16>(atoi(pszValue));
     830             : 
     831           1 :     pszValue = GetMetadataItem("WO");
     832           1 :     if (pszValue != nullptr)
     833           0 :         hHeader.dfWo = CPLAtof(pszValue);
     834             : 
     835           1 :     pszValue = GetMetadataItem("GM");
     836           1 :     if (pszValue != nullptr)
     837           0 :         hHeader.dfGM = CPLAtof(pszValue);
     838             : 
     839           1 :     pszValue = GetMetadataItem("TIDESYSTEM");
     840           1 :     if (pszValue != nullptr)
     841           0 :         hHeader.nTideSys = static_cast<GInt16>(atoi(pszValue));
     842             : 
     843           1 :     pszValue = GetMetadataItem("REALIZATION");
     844           1 :     if (pszValue != nullptr)
     845           0 :         hHeader.nRealiz = static_cast<GInt16>(atoi(pszValue));
     846             : 
     847           1 :     pszValue = GetMetadataItem("EPOCH");
     848           1 :     if (pszValue != nullptr)
     849           0 :         hHeader.dEpoch = static_cast<float>(CPLAtof(pszValue));
     850             : 
     851           1 :     pszValue = GetMetadataItem("PTTYPE");
     852           1 :     if (pszValue != nullptr)
     853           0 :         hHeader.nPtType = static_cast<GInt16>(atoi(pszValue));
     854             : 
     855           1 :     CPL_IGNORE_RET_VAL(VSIFSeekL(fpImage, 0, SEEK_SET));
     856           1 :     CPL_IGNORE_RET_VAL(VSIFWriteL(abyBuf, BYN_HDR_SZ, 1, fpImage));
     857             : 
     858             :     /* GDALPam metadata update */
     859             : 
     860           1 :     SetMetadataItem("GLOBAL", CPLSPrintf("%d", hHeader.nGlobal), "BYN");
     861           1 :     SetMetadataItem("TYPE", CPLSPrintf("%d", hHeader.nType), "BYN");
     862           1 :     SetMetadataItem("DESCRIPTION", CPLSPrintf("%d", hHeader.nDescrip), "BYN");
     863           1 :     SetMetadataItem("SUBTYPE", CPLSPrintf("%d", hHeader.nSubType), "BYN");
     864           1 :     SetMetadataItem("WO", CPLSPrintf("%g", hHeader.dfWo), "BYN");
     865           1 :     SetMetadataItem("GM", CPLSPrintf("%g", hHeader.dfGM), "BYN");
     866           1 :     SetMetadataItem("TIDESYSTEM", CPLSPrintf("%d", hHeader.nTideSys), "BYN");
     867           1 :     SetMetadataItem("REALIZATION", CPLSPrintf("%d", hHeader.nRealiz), "BYN");
     868           1 :     SetMetadataItem("EPOCH", CPLSPrintf("%g", hHeader.dEpoch), "BYN");
     869           1 :     SetMetadataItem("PTTYPE", CPLSPrintf("%d", hHeader.nPtType), "BYN");
     870           1 : }
     871             : 
     872             : /************************************************************************/
     873             : /*                          GDALRegister_BYN()                          */
     874             : /************************************************************************/
     875             : 
     876        1595 : void GDALRegister_BYN()
     877             : 
     878             : {
     879        1595 :     if (GDALGetDriverByName("BYN") != nullptr)
     880         302 :         return;
     881             : 
     882        1293 :     GDALDriver *poDriver = new GDALDriver();
     883             : 
     884        1293 :     poDriver->SetDescription("BYN");
     885        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
     886        1293 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
     887        1293 :                               "Natural Resources Canada's Geoid");
     888        1293 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "byn err");
     889        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
     890        1293 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/byn.html");
     891        1293 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Int16 Int32");
     892             : 
     893        1293 :     poDriver->pfnOpen = BYNDataset::Open;
     894        1293 :     poDriver->pfnIdentify = BYNDataset::Identify;
     895        1293 :     poDriver->pfnCreate = BYNDataset::Create;
     896             : 
     897        1293 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     898             : }

Generated by: LCOV version 1.14