LCOV - code coverage report
Current view: top level - frmts/raw - gtxdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 157 183 85.8 %
Date: 2024-05-03 15:49:35 Functions: 14 15 93.3 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Vertical Datum Transformation
       4             :  * Purpose:  Implementation of NOAA .gtx vertical datum shift file format.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2010, Frank Warmerdam
       9             :  * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * Permission is hereby granted, free of charge, to any person obtaining a
      12             :  * copy of this software and associated documentation files (the "Software"),
      13             :  * to deal in the Software without restriction, including without limitation
      14             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      15             :  * and/or sell copies of the Software, and to permit persons to whom the
      16             :  * Software is furnished to do so, subject to the following conditions:
      17             :  *
      18             :  * The above copyright notice and this permission notice shall be included
      19             :  * in all copies or substantial portions of the Software.
      20             :  *
      21             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      22             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      23             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      24             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      25             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      26             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      27             :  * DEALINGS IN THE SOFTWARE.
      28             :  ****************************************************************************/
      29             : 
      30             : #include "cpl_string.h"
      31             : #include "gdal_frmts.h"
      32             : #include "ogr_srs_api.h"
      33             : #include "rawdataset.h"
      34             : 
      35             : #include <algorithm>
      36             : #include <limits>
      37             : 
      38             : /**
      39             : 
      40             : NOAA .GTX Vertical Datum Grid Shift Format
      41             : 
      42             : All values are bigendian
      43             : 
      44             : Header
      45             : ------
      46             : 
      47             : float64  latitude_of_origin
      48             : float64  longitude_of_origin (0-360)
      49             : float64  cell size (x?y?)
      50             : float64  cell size (x?y?)
      51             : int32    length in pixels
      52             : int32    width in pixels
      53             : 
      54             : Data
      55             : ----
      56             : 
      57             : float32  * width in pixels * length in pixels
      58             : 
      59             : Values are an offset in meters between two vertical datums.
      60             : 
      61             : **/
      62             : 
      63             : /************************************************************************/
      64             : /* ==================================================================== */
      65             : /*                              GTXDataset                              */
      66             : /* ==================================================================== */
      67             : /************************************************************************/
      68             : 
      69             : class GTXDataset final : public RawDataset
      70             : {
      71             :     VSILFILE *fpImage = nullptr;  // image data file.
      72             : 
      73             :     OGRSpatialReference m_oSRS{};
      74             :     double adfGeoTransform[6];
      75             : 
      76             :     CPL_DISALLOW_COPY_ASSIGN(GTXDataset)
      77             : 
      78             :     CPLErr Close() override;
      79             : 
      80             :   public:
      81          47 :     GTXDataset()
      82          47 :     {
      83          47 :         m_oSRS.SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
      84          47 :         m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
      85             : 
      86          47 :         adfGeoTransform[0] = 0.0;
      87          47 :         adfGeoTransform[1] = 1.0;
      88          47 :         adfGeoTransform[2] = 0.0;
      89          47 :         adfGeoTransform[3] = 0.0;
      90          47 :         adfGeoTransform[4] = 0.0;
      91          47 :         adfGeoTransform[5] = 1.0;
      92          47 :     }
      93             : 
      94             :     ~GTXDataset() override;
      95             : 
      96             :     CPLErr GetGeoTransform(double *padfTransform) override;
      97             :     CPLErr SetGeoTransform(double *padfTransform) override;
      98             : 
      99           1 :     const OGRSpatialReference *GetSpatialRef() const override
     100             :     {
     101           1 :         return &m_oSRS;
     102             :     }
     103             : 
     104             :     static GDALDataset *Open(GDALOpenInfo *);
     105             :     static int Identify(GDALOpenInfo *);
     106             :     static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
     107             :                                int nBands, GDALDataType eType,
     108             :                                char **papszOptions);
     109             : };
     110             : 
     111             : /************************************************************************/
     112             : /* ==================================================================== */
     113             : /*                           GTXRasterBand                              */
     114             : /* ==================================================================== */
     115             : /************************************************************************/
     116             : 
     117             : class GTXRasterBand final : public RawRasterBand
     118             : {
     119             :     CPL_DISALLOW_COPY_ASSIGN(GTXRasterBand)
     120             : 
     121             :   public:
     122             :     GTXRasterBand(GDALDataset *poDS, int nBand, VSILFILE *fpRaw,
     123             :                   vsi_l_offset nImgOffset, int nPixelOffset, int nLineOffset,
     124             :                   GDALDataType eDataType, int bNativeOrder);
     125             : 
     126             :     ~GTXRasterBand() override;
     127             : 
     128             :     double GetNoDataValue(int *pbSuccess = nullptr) override;
     129             : };
     130             : 
     131             : /************************************************************************/
     132             : /*                            GTXRasterBand()                           */
     133             : /************************************************************************/
     134             : 
     135          47 : GTXRasterBand::GTXRasterBand(GDALDataset *poDSIn, int nBandIn,
     136             :                              VSILFILE *fpRawIn, vsi_l_offset nImgOffsetIn,
     137             :                              int nPixelOffsetIn, int nLineOffsetIn,
     138          47 :                              GDALDataType eDataTypeIn, int bNativeOrderIn)
     139             :     : RawRasterBand(poDSIn, nBandIn, fpRawIn, nImgOffsetIn, nPixelOffsetIn,
     140             :                     nLineOffsetIn, eDataTypeIn, bNativeOrderIn,
     141          47 :                     RawRasterBand::OwnFP::NO)
     142             : {
     143          47 : }
     144             : 
     145             : /************************************************************************/
     146             : /*                           ~GTXRasterBand()                           */
     147             : /************************************************************************/
     148             : 
     149          94 : GTXRasterBand::~GTXRasterBand()
     150             : {
     151          94 : }
     152             : 
     153             : /************************************************************************/
     154             : /*                           GetNoDataValue()                           */
     155             : /************************************************************************/
     156             : 
     157           0 : double GTXRasterBand::GetNoDataValue(int *pbSuccess)
     158             : {
     159           0 :     if (pbSuccess)
     160           0 :         *pbSuccess = TRUE;
     161           0 :     int bSuccess = FALSE;
     162           0 :     double dfNoData = GDALPamRasterBand::GetNoDataValue(&bSuccess);
     163           0 :     if (bSuccess)
     164             :     {
     165           0 :         return dfNoData;
     166             :     }
     167           0 :     return -88.8888;
     168             : }
     169             : 
     170             : /************************************************************************/
     171             : /* ==================================================================== */
     172             : /*                              GTXDataset                              */
     173             : /* ==================================================================== */
     174             : /************************************************************************/
     175             : 
     176             : /************************************************************************/
     177             : /*                            ~GTXDataset()                             */
     178             : /************************************************************************/
     179             : 
     180          94 : GTXDataset::~GTXDataset()
     181             : 
     182             : {
     183          47 :     GTXDataset::Close();
     184          94 : }
     185             : 
     186             : /************************************************************************/
     187             : /*                              Close()                                 */
     188             : /************************************************************************/
     189             : 
     190          85 : CPLErr GTXDataset::Close()
     191             : {
     192          85 :     CPLErr eErr = CE_None;
     193          85 :     if (nOpenFlags != OPEN_FLAGS_CLOSED)
     194             :     {
     195          47 :         if (GTXDataset::FlushCache(true) != CE_None)
     196           9 :             eErr = CE_Failure;
     197             : 
     198          47 :         if (fpImage)
     199             :         {
     200          47 :             if (VSIFCloseL(fpImage) != 0)
     201             :             {
     202           0 :                 CPLError(CE_Failure, CPLE_FileIO, "I/O error");
     203           0 :                 eErr = CE_Failure;
     204             :             }
     205             :         }
     206             : 
     207          47 :         if (GDALPamDataset::Close() != CE_None)
     208           0 :             eErr = CE_Failure;
     209             :     }
     210          85 :     return eErr;
     211             : }
     212             : 
     213             : /************************************************************************/
     214             : /*                              Identify()                              */
     215             : /************************************************************************/
     216             : 
     217       48876 : int GTXDataset::Identify(GDALOpenInfo *poOpenInfo)
     218             : 
     219             : {
     220       48876 :     if (poOpenInfo->nHeaderBytes < 40)
     221       45515 :         return FALSE;
     222             : 
     223        3361 :     if (!EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "gtx"))
     224        3266 :         return FALSE;
     225             : 
     226          94 :     return TRUE;
     227             : }
     228             : 
     229             : /************************************************************************/
     230             : /*                                Open()                                */
     231             : /************************************************************************/
     232             : 
     233          47 : GDALDataset *GTXDataset::Open(GDALOpenInfo *poOpenInfo)
     234             : 
     235             : {
     236          47 :     if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
     237           0 :         return nullptr;
     238             : 
     239             :     /* -------------------------------------------------------------------- */
     240             :     /*      Create a corresponding GDALDataset.                             */
     241             :     /* -------------------------------------------------------------------- */
     242          94 :     auto poDS = std::make_unique<GTXDataset>();
     243             : 
     244          47 :     poDS->eAccess = poOpenInfo->eAccess;
     245          47 :     std::swap(poDS->fpImage, poOpenInfo->fpL);
     246             : 
     247             :     /* -------------------------------------------------------------------- */
     248             :     /*      Read the header.                                                */
     249             :     /* -------------------------------------------------------------------- */
     250          47 :     poDS->adfGeoTransform[2] = 0.0;
     251          47 :     poDS->adfGeoTransform[4] = 0.0;
     252             : 
     253          47 :     CPL_IGNORE_RET_VAL(
     254          47 :         VSIFReadL(poDS->adfGeoTransform + 3, 8, 1, poDS->fpImage));
     255          47 :     CPL_IGNORE_RET_VAL(
     256          47 :         VSIFReadL(poDS->adfGeoTransform + 0, 8, 1, poDS->fpImage));
     257          47 :     CPL_IGNORE_RET_VAL(
     258          47 :         VSIFReadL(poDS->adfGeoTransform + 5, 8, 1, poDS->fpImage));
     259          47 :     CPL_IGNORE_RET_VAL(
     260          47 :         VSIFReadL(poDS->adfGeoTransform + 1, 8, 1, poDS->fpImage));
     261             : 
     262          47 :     CPL_IGNORE_RET_VAL(VSIFReadL(&(poDS->nRasterYSize), 4, 1, poDS->fpImage));
     263          47 :     CPL_IGNORE_RET_VAL(VSIFReadL(&(poDS->nRasterXSize), 4, 1, poDS->fpImage));
     264             : 
     265          47 :     CPL_MSBPTR32(&(poDS->nRasterYSize));
     266          47 :     CPL_MSBPTR32(&(poDS->nRasterXSize));
     267             : 
     268          47 :     CPL_MSBPTR64(poDS->adfGeoTransform + 0);
     269          47 :     CPL_MSBPTR64(poDS->adfGeoTransform + 1);
     270          47 :     CPL_MSBPTR64(poDS->adfGeoTransform + 3);
     271          47 :     CPL_MSBPTR64(poDS->adfGeoTransform + 5);
     272             : 
     273          47 :     poDS->adfGeoTransform[3] += poDS->adfGeoTransform[5] *
     274          47 :                                 (static_cast<double>(poDS->nRasterYSize) - 1);
     275             : 
     276          47 :     poDS->adfGeoTransform[0] -= poDS->adfGeoTransform[1] * 0.5;
     277          47 :     poDS->adfGeoTransform[3] += poDS->adfGeoTransform[5] * 0.5;
     278             : 
     279          47 :     poDS->adfGeoTransform[5] *= -1;
     280             : 
     281          47 :     if (CPLFetchBool(poOpenInfo->papszOpenOptions,
     282             :                      "SHIFT_ORIGIN_IN_MINUS_180_PLUS_180", false))
     283             :     {
     284           0 :         if (poDS->adfGeoTransform[0] < -180.0 - poDS->adfGeoTransform[1])
     285           0 :             poDS->adfGeoTransform[0] += 360.0;
     286           0 :         else if (poDS->adfGeoTransform[0] > 180.0)
     287           0 :             poDS->adfGeoTransform[0] -= 360.0;
     288             :     }
     289             : 
     290          94 :     if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) ||
     291          47 :         static_cast<vsi_l_offset>(poDS->nRasterXSize) * poDS->nRasterYSize >
     292          47 :             std::numeric_limits<vsi_l_offset>::max() / sizeof(double))
     293             :     {
     294           0 :         return nullptr;
     295             :     }
     296             : 
     297             :     /* -------------------------------------------------------------------- */
     298             :     /*      Guess the data type. Since October 1, 2009, it should be        */
     299             :     /*      Float32. Before it was double.                                  */
     300             :     /* -------------------------------------------------------------------- */
     301          47 :     CPL_IGNORE_RET_VAL(VSIFSeekL(poDS->fpImage, 0, SEEK_END));
     302          47 :     const vsi_l_offset nSize = VSIFTellL(poDS->fpImage);
     303             : 
     304          47 :     GDALDataType eDT = GDT_Float32;
     305          94 :     if (nSize - 40 == sizeof(double) *
     306          47 :                           static_cast<vsi_l_offset>(poDS->nRasterXSize) *
     307          47 :                           poDS->nRasterYSize)
     308           0 :         eDT = GDT_Float64;
     309          47 :     const int nDTSize = GDALGetDataTypeSizeBytes(eDT);
     310          47 :     if (nDTSize <= 0 || poDS->nRasterXSize > INT_MAX / nDTSize)
     311             :     {
     312           0 :         return nullptr;
     313             :     }
     314             : 
     315             :     /* -------------------------------------------------------------------- */
     316             :     /*      Create band information object.                                 */
     317             :     /* -------------------------------------------------------------------- */
     318             :     auto poBand = std::make_unique<GTXRasterBand>(
     319          47 :         poDS.get(), 1, poDS->fpImage,
     320          47 :         static_cast<vsi_l_offset>(poDS->nRasterYSize - 1) * poDS->nRasterXSize *
     321          47 :                 nDTSize +
     322             :             40,
     323         141 :         nDTSize, poDS->nRasterXSize * -nDTSize, eDT, !CPL_IS_LSB);
     324          47 :     if (!poBand->IsValid())
     325           0 :         return nullptr;
     326          47 :     poDS->SetBand(1, std::move(poBand));
     327             : 
     328             :     /* -------------------------------------------------------------------- */
     329             :     /*      Initialize any PAM information.                                 */
     330             :     /* -------------------------------------------------------------------- */
     331          47 :     poDS->SetDescription(poOpenInfo->pszFilename);
     332          47 :     poDS->TryLoadXML();
     333             : 
     334             :     /* -------------------------------------------------------------------- */
     335             :     /*      Check for overviews.                                            */
     336             :     /* -------------------------------------------------------------------- */
     337          47 :     poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
     338             : 
     339          47 :     return poDS.release();
     340             : }
     341             : 
     342             : /************************************************************************/
     343             : /*                          GetGeoTransform()                           */
     344             : /************************************************************************/
     345             : 
     346           3 : CPLErr GTXDataset::GetGeoTransform(double *padfTransform)
     347             : 
     348             : {
     349           3 :     memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6);
     350           3 :     return CE_None;
     351             : }
     352             : 
     353             : /************************************************************************/
     354             : /*                          SetGeoTransform()                           */
     355             : /************************************************************************/
     356             : 
     357          33 : CPLErr GTXDataset::SetGeoTransform(double *padfTransform)
     358             : 
     359             : {
     360          33 :     if (padfTransform[2] != 0.0 || padfTransform[4] != 0.0)
     361             :     {
     362           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     363             :                  "Attempt to write skewed or rotated geotransform to gtx.");
     364           0 :         return CE_Failure;
     365             :     }
     366             : 
     367          33 :     memcpy(adfGeoTransform, padfTransform, sizeof(double) * 6);
     368             : 
     369          33 :     const double dfXOrigin = adfGeoTransform[0] + 0.5 * adfGeoTransform[1];
     370          33 :     const double dfYOrigin =
     371          33 :         adfGeoTransform[3] + (nRasterYSize - 0.5) * adfGeoTransform[5];
     372          33 :     const double dfWidth = adfGeoTransform[1];
     373          33 :     const double dfHeight = -adfGeoTransform[5];
     374             : 
     375          33 :     unsigned char header[32] = {'\0'};
     376          33 :     memcpy(header + 0, &dfYOrigin, 8);
     377          33 :     CPL_MSBPTR64(header + 0);
     378             : 
     379          33 :     memcpy(header + 8, &dfXOrigin, 8);
     380          33 :     CPL_MSBPTR64(header + 8);
     381             : 
     382          33 :     memcpy(header + 16, &dfHeight, 8);
     383          33 :     CPL_MSBPTR64(header + 16);
     384             : 
     385          33 :     memcpy(header + 24, &dfWidth, 8);
     386          33 :     CPL_MSBPTR64(header + 24);
     387             : 
     388          66 :     if (VSIFSeekL(fpImage, SEEK_SET, 0) != 0 ||
     389          33 :         VSIFWriteL(header, 32, 1, fpImage) != 1)
     390             :     {
     391           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     392             :                  "Attempt to write geotransform header to GTX failed.");
     393           0 :         return CE_Failure;
     394             :     }
     395             : 
     396          33 :     return CE_None;
     397             : }
     398             : 
     399             : /************************************************************************/
     400             : /*                               Create()                               */
     401             : /************************************************************************/
     402             : 
     403          81 : GDALDataset *GTXDataset::Create(const char *pszFilename, int nXSize, int nYSize,
     404             :                                 int /* nBands */, GDALDataType eType,
     405             :                                 char ** /* papszOptions */)
     406             : {
     407          81 :     if (eType != GDT_Float32)
     408             :     {
     409          44 :         CPLError(CE_Failure, CPLE_AppDefined,
     410             :                  "Attempt to create gtx file with unsupported data type '%s'.",
     411             :                  GDALGetDataTypeName(eType));
     412          44 :         return nullptr;
     413             :     }
     414             : 
     415          37 :     if (!EQUAL(CPLGetExtension(pszFilename), "gtx"))
     416             :     {
     417           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     418             :                  "Attempt to create gtx file with extension other than gtx.");
     419           0 :         return nullptr;
     420             :     }
     421             : 
     422             :     /* -------------------------------------------------------------------- */
     423             :     /*      Try to create the file.                                         */
     424             :     /* -------------------------------------------------------------------- */
     425          37 :     VSILFILE *fp = VSIFOpenL(pszFilename, "wb");
     426          37 :     if (fp == nullptr)
     427             :     {
     428           3 :         CPLError(CE_Failure, CPLE_OpenFailed,
     429             :                  "Attempt to create file `%s' failed.\n", pszFilename);
     430           3 :         return nullptr;
     431             :     }
     432             : 
     433             :     /* -------------------------------------------------------------------- */
     434             :     /*      Write out the header with stub georeferencing.                  */
     435             :     /* -------------------------------------------------------------------- */
     436             : 
     437          34 :     unsigned char header[40] = {'\0'};
     438          34 :     double dfYOrigin = 0.0;
     439          34 :     memcpy(header + 0, &dfYOrigin, 8);
     440          34 :     CPL_MSBPTR64(header + 0);
     441             : 
     442          34 :     double dfXOrigin = 0.0;
     443          34 :     memcpy(header + 8, &dfXOrigin, 8);
     444          34 :     CPL_MSBPTR64(header + 8);
     445             : 
     446          34 :     double dfYSize = 0.01;
     447          34 :     memcpy(header + 16, &dfYSize, 8);
     448          34 :     CPL_MSBPTR64(header + 16);
     449             : 
     450          34 :     double dfXSize = 0.01;
     451          34 :     memcpy(header + 24, &dfXSize, 8);
     452          34 :     CPL_MSBPTR64(header + 24);
     453             : 
     454          34 :     GInt32 nYSize32 = nYSize;
     455          34 :     memcpy(header + 32, &nYSize32, 4);
     456          34 :     CPL_MSBPTR32(header + 32);
     457             : 
     458          34 :     GInt32 nXSize32 = nXSize;
     459          34 :     memcpy(header + 36, &nXSize32, 4);
     460          34 :     CPL_MSBPTR32(header + 36);
     461             : 
     462          34 :     CPL_IGNORE_RET_VAL(VSIFWriteL(header, 40, 1, fp));
     463          34 :     CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     464             : 
     465          34 :     return GDALDataset::FromHandle(GDALOpen(pszFilename, GA_Update));
     466             : }
     467             : 
     468             : /************************************************************************/
     469             : /*                          GDALRegister_GTX()                          */
     470             : /************************************************************************/
     471             : 
     472        1511 : void GDALRegister_GTX()
     473             : 
     474             : {
     475        1511 :     if (GDALGetDriverByName("GTX") != nullptr)
     476         295 :         return;
     477             : 
     478        1216 :     GDALDriver *poDriver = new GDALDriver();
     479             : 
     480        1216 :     poDriver->SetDescription("GTX");
     481        1216 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
     482        1216 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "NOAA Vertical Datum .GTX");
     483        1216 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "gtx");
     484        1216 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
     485             :     // poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
     486             :     //                            "frmt_various.html#GTX" );
     487        1216 :     poDriver->SetMetadataItem(
     488             :         GDAL_DMD_OPENOPTIONLIST,
     489             :         "<OpenOptionList>"
     490             :         "   <Option name='SHIFT_ORIGIN_IN_MINUS_180_PLUS_180' type='boolean' "
     491             :         "description='Whether to apply a +/-360 deg shift to the longitude of "
     492             :         "the top left corner so that it is in the [-180,180] range' "
     493             :         "default='NO'/>"
     494        1216 :         "</OpenOptionList>");
     495             : 
     496        1216 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Float32");
     497             : 
     498        1216 :     poDriver->pfnOpen = GTXDataset::Open;
     499        1216 :     poDriver->pfnIdentify = GTXDataset::Identify;
     500        1216 :     poDriver->pfnCreate = GTXDataset::Create;
     501             : 
     502        1216 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     503             : }

Generated by: LCOV version 1.14