LCOV - code coverage report
Current view: top level - frmts/ngsgeoid - ngsgeoiddataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 131 148 88.5 %
Date: 2024-05-02 22:57:13 Functions: 11 12 91.7 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  NGSGEOID driver
       4             :  * Purpose:  GDALDataset driver for NGSGEOID dataset.
       5             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2011, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * Permission is hereby granted, free of charge, to any person obtaining a
      11             :  * copy of this software and associated documentation files (the "Software"),
      12             :  * to deal in the Software without restriction, including without limitation
      13             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      14             :  * and/or sell copies of the Software, and to permit persons to whom the
      15             :  * Software is furnished to do so, subject to the following conditions:
      16             :  *
      17             :  * The above copyright notice and this permission notice shall be included
      18             :  * in all copies or substantial portions of the Software.
      19             :  *
      20             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      21             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      22             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      23             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      24             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      25             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      26             :  * DEALINGS IN THE SOFTWARE.
      27             :  ****************************************************************************/
      28             : 
      29             : #include "cpl_string.h"
      30             : #include "cpl_vsi_virtual.h"
      31             : #include "gdal_frmts.h"
      32             : #include "gdal_pam.h"
      33             : #include "ogr_srs_api.h"
      34             : 
      35             : #define HEADER_SIZE (4 * 8 + 3 * 4)
      36             : 
      37             : /************************************************************************/
      38             : /* ==================================================================== */
      39             : /*                            NGSGEOIDDataset                           */
      40             : /* ==================================================================== */
      41             : /************************************************************************/
      42             : 
      43             : class NGSGEOIDRasterBand;
      44             : 
      45             : class NGSGEOIDDataset final : public GDALPamDataset
      46             : {
      47             :     friend class NGSGEOIDRasterBand;
      48             : 
      49             :     VSILFILE *fp;
      50             :     double adfGeoTransform[6];
      51             :     int bIsLittleEndian;
      52             :     mutable OGRSpatialReference m_oSRS{};
      53             : 
      54             :     static int GetHeaderInfo(const GByte *pBuffer, double *padfGeoTransform,
      55             :                              int *pnRows, int *pnCols, int *pbIsLittleEndian);
      56             : 
      57             :   public:
      58             :     NGSGEOIDDataset();
      59             :     virtual ~NGSGEOIDDataset();
      60             : 
      61             :     virtual CPLErr GetGeoTransform(double *) override;
      62             :     const OGRSpatialReference *GetSpatialRef() const override;
      63             : 
      64             :     static GDALDataset *Open(GDALOpenInfo *);
      65             :     static int Identify(GDALOpenInfo *);
      66             : };
      67             : 
      68             : /************************************************************************/
      69             : /* ==================================================================== */
      70             : /*                          NGSGEOIDRasterBand                          */
      71             : /* ==================================================================== */
      72             : /************************************************************************/
      73             : 
      74             : class NGSGEOIDRasterBand final : public GDALPamRasterBand
      75             : {
      76             :     friend class NGSGEOIDDataset;
      77             : 
      78             :   public:
      79             :     explicit NGSGEOIDRasterBand(NGSGEOIDDataset *);
      80             : 
      81             :     virtual CPLErr IReadBlock(int, int, void *) override;
      82             : 
      83           0 :     virtual const char *GetUnitType() override
      84             :     {
      85           0 :         return "m";
      86             :     }
      87             : };
      88             : 
      89             : /************************************************************************/
      90             : /*                        NGSGEOIDRasterBand()                          */
      91             : /************************************************************************/
      92             : 
      93           4 : NGSGEOIDRasterBand::NGSGEOIDRasterBand(NGSGEOIDDataset *poDSIn)
      94             : 
      95             : {
      96           4 :     poDS = poDSIn;
      97           4 :     nBand = 1;
      98             : 
      99           4 :     eDataType = GDT_Float32;
     100             : 
     101           4 :     nBlockXSize = poDS->GetRasterXSize();
     102           4 :     nBlockYSize = 1;
     103           4 : }
     104             : 
     105             : /************************************************************************/
     106             : /*                             IReadBlock()                             */
     107             : /************************************************************************/
     108             : 
     109           2 : CPLErr NGSGEOIDRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
     110             :                                       void *pImage)
     111             : 
     112             : {
     113           2 :     NGSGEOIDDataset *poGDS = reinterpret_cast<NGSGEOIDDataset *>(poDS);
     114             : 
     115             :     /* First values in the file corresponds to the south-most line of the
     116             :      * imagery */
     117           2 :     VSIFSeekL(poGDS->fp,
     118           2 :               HEADER_SIZE +
     119           2 :                   static_cast<vsi_l_offset>(nRasterYSize - 1 - nBlockYOff) *
     120           2 :                       nRasterXSize * 4,
     121             :               SEEK_SET);
     122             : 
     123           2 :     if (static_cast<int>(VSIFReadL(pImage, 4, nRasterXSize, poGDS->fp)) !=
     124           2 :         nRasterXSize)
     125           0 :         return CE_Failure;
     126             : 
     127             : #ifdef CPL_MSB
     128             :     if (poGDS->bIsLittleEndian)
     129             :     {
     130             :         GDALSwapWords(pImage, 4, nRasterXSize, 4);
     131             :     }
     132             : #endif
     133             : 
     134             : #ifdef CPL_LSB
     135           2 :     if (!poGDS->bIsLittleEndian)
     136             :     {
     137           1 :         GDALSwapWords(pImage, 4, nRasterXSize, 4);
     138             :     }
     139             : #endif
     140             : 
     141           2 :     return CE_None;
     142             : }
     143             : 
     144             : /************************************************************************/
     145             : /*                          ~NGSGEOIDDataset()                          */
     146             : /************************************************************************/
     147             : 
     148           4 : NGSGEOIDDataset::NGSGEOIDDataset() : fp(nullptr), bIsLittleEndian(TRUE)
     149             : {
     150           4 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     151           4 :     adfGeoTransform[0] = 0;
     152           4 :     adfGeoTransform[1] = 1;
     153           4 :     adfGeoTransform[2] = 0;
     154           4 :     adfGeoTransform[3] = 0;
     155           4 :     adfGeoTransform[4] = 0;
     156           4 :     adfGeoTransform[5] = 1;
     157           4 : }
     158             : 
     159             : /************************************************************************/
     160             : /*                           ~NGSGEOIDDataset()                         */
     161             : /************************************************************************/
     162             : 
     163           8 : NGSGEOIDDataset::~NGSGEOIDDataset()
     164             : 
     165             : {
     166           4 :     FlushCache(true);
     167           4 :     if (fp)
     168           4 :         VSIFCloseL(fp);
     169           8 : }
     170             : 
     171             : /************************************************************************/
     172             : /*                            GetHeaderInfo()                           */
     173             : /************************************************************************/
     174             : 
     175        2646 : int NGSGEOIDDataset::GetHeaderInfo(const GByte *pBuffer,
     176             :                                    double *padfGeoTransform, int *pnRows,
     177             :                                    int *pnCols, int *pbIsLittleEndian)
     178             : {
     179             :     /* First check IKIND marker to determine if the file */
     180             :     /* is in little or big-endian order, and if it is a valid */
     181             :     /* NGSGEOID dataset */
     182             :     int nIKIND;
     183        2646 :     memcpy(&nIKIND, pBuffer + HEADER_SIZE - 4, 4);
     184        2646 :     CPL_LSBPTR32(&nIKIND);
     185        2646 :     if (nIKIND == 1)
     186             :     {
     187          27 :         *pbIsLittleEndian = TRUE;
     188             :     }
     189             :     else
     190             :     {
     191        2619 :         memcpy(&nIKIND, pBuffer + HEADER_SIZE - 4, 4);
     192        2619 :         CPL_MSBPTR32(&nIKIND);
     193        2619 :         if (nIKIND == 1)
     194             :         {
     195           7 :             *pbIsLittleEndian = FALSE;
     196             :         }
     197             :         else
     198             :         {
     199        2612 :             return FALSE;
     200             :         }
     201             :     }
     202             : 
     203             :     double dfSLAT;
     204          34 :     memcpy(&dfSLAT, pBuffer, 8);
     205          34 :     if (*pbIsLittleEndian)
     206             :     {
     207          27 :         CPL_LSBPTR64(&dfSLAT);
     208             :     }
     209             :     else
     210             :     {
     211           7 :         CPL_MSBPTR64(&dfSLAT);
     212             :     }
     213          34 :     pBuffer += 8;
     214             : 
     215             :     double dfWLON;
     216          34 :     memcpy(&dfWLON, pBuffer, 8);
     217          34 :     if (*pbIsLittleEndian)
     218             :     {
     219          27 :         CPL_LSBPTR64(&dfWLON);
     220             :     }
     221             :     else
     222             :     {
     223           7 :         CPL_MSBPTR64(&dfWLON);
     224             :     }
     225          34 :     pBuffer += 8;
     226             : 
     227             :     double dfDLAT;
     228          34 :     memcpy(&dfDLAT, pBuffer, 8);
     229          34 :     if (*pbIsLittleEndian)
     230             :     {
     231          27 :         CPL_LSBPTR64(&dfDLAT);
     232             :     }
     233             :     else
     234             :     {
     235           7 :         CPL_MSBPTR64(&dfDLAT);
     236             :     }
     237          34 :     pBuffer += 8;
     238             : 
     239             :     double dfDLON;
     240          34 :     memcpy(&dfDLON, pBuffer, 8);
     241          34 :     if (*pbIsLittleEndian)
     242             :     {
     243          27 :         CPL_LSBPTR64(&dfDLON);
     244             :     }
     245             :     else
     246             :     {
     247           7 :         CPL_MSBPTR64(&dfDLON);
     248             :     }
     249          34 :     pBuffer += 8;
     250             : 
     251             :     int nNLAT;
     252          34 :     memcpy(&nNLAT, pBuffer, 4);
     253          34 :     if (*pbIsLittleEndian)
     254             :     {
     255          27 :         CPL_LSBPTR32(&nNLAT);
     256             :     }
     257             :     else
     258             :     {
     259           7 :         CPL_MSBPTR32(&nNLAT);
     260             :     }
     261          34 :     pBuffer += 4;
     262             : 
     263             :     int nNLON;
     264          34 :     memcpy(&nNLON, pBuffer, 4);
     265          34 :     if (*pbIsLittleEndian)
     266             :     {
     267          27 :         CPL_LSBPTR32(&nNLON);
     268             :     }
     269             :     else
     270             :     {
     271           7 :         CPL_MSBPTR32(&nNLON);
     272             :     }
     273             :     /*pBuffer += 4;*/
     274             : 
     275             :     /*CPLDebug("NGSGEOID", "SLAT=%f, WLON=%f, DLAT=%f, DLON=%f, NLAT=%d,
     276             :        NLON=%d, IKIND=%d", dfSLAT, dfWLON, dfDLAT, dfDLON, nNLAT, nNLON,
     277             :        nIKIND);*/
     278             : 
     279          34 :     if (nNLAT <= 0 || nNLON <= 0 || dfDLAT <= 1e-15 || dfDLON <= 1e-15)
     280          22 :         return FALSE;
     281             : 
     282             :     /* Grids go over +180 in longitude */
     283             :     // Test written that way to be robust to NaN values
     284          12 :     if (!(dfSLAT >= -90.0 && dfSLAT + nNLAT * dfDLAT <= 90.0 &&
     285          12 :           dfWLON >= -180.0 && dfWLON + nNLON * dfDLON <= 360.0))
     286           0 :         return FALSE;
     287             : 
     288          12 :     padfGeoTransform[0] = dfWLON - dfDLON / 2;
     289          12 :     padfGeoTransform[1] = dfDLON;
     290          12 :     padfGeoTransform[2] = 0.0;
     291          12 :     padfGeoTransform[3] = dfSLAT + nNLAT * dfDLAT - dfDLAT / 2;
     292          12 :     padfGeoTransform[4] = 0.0;
     293          12 :     padfGeoTransform[5] = -dfDLAT;
     294             : 
     295          12 :     *pnRows = nNLAT;
     296          12 :     *pnCols = nNLON;
     297             : 
     298          12 :     return TRUE;
     299             : }
     300             : 
     301             : /************************************************************************/
     302             : /*                             Identify()                               */
     303             : /************************************************************************/
     304             : 
     305       47986 : int NGSGEOIDDataset::Identify(GDALOpenInfo *poOpenInfo)
     306             : {
     307       47986 :     if (poOpenInfo->nHeaderBytes < HEADER_SIZE)
     308       45344 :         return FALSE;
     309             : 
     310             :     double adfGeoTransform[6];
     311             :     int nRows, nCols;
     312             :     int bIsLittleEndian;
     313        2642 :     if (!GetHeaderInfo(poOpenInfo->pabyHeader, adfGeoTransform, &nRows, &nCols,
     314             :                        &bIsLittleEndian))
     315        2634 :         return FALSE;
     316             : 
     317           8 :     return TRUE;
     318             : }
     319             : 
     320             : /************************************************************************/
     321             : /*                                Open()                                */
     322             : /************************************************************************/
     323             : 
     324           4 : GDALDataset *NGSGEOIDDataset::Open(GDALOpenInfo *poOpenInfo)
     325             : 
     326             : {
     327           4 :     if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
     328           0 :         return nullptr;
     329             : 
     330           4 :     if (poOpenInfo->eAccess == GA_Update)
     331             :     {
     332           0 :         CPLError(
     333             :             CE_Failure, CPLE_NotSupported,
     334             :             "The NGSGEOID driver does not support update access to existing"
     335             :             " datasets.\n");
     336           0 :         return nullptr;
     337             :     }
     338             : 
     339             :     /* -------------------------------------------------------------------- */
     340             :     /*      Create a corresponding GDALDataset.                             */
     341             :     /* -------------------------------------------------------------------- */
     342           4 :     NGSGEOIDDataset *poDS = new NGSGEOIDDataset();
     343           4 :     poDS->fp = poOpenInfo->fpL;
     344           4 :     poOpenInfo->fpL = nullptr;
     345             : 
     346           4 :     int nRows = 0, nCols = 0;
     347           4 :     GetHeaderInfo(poOpenInfo->pabyHeader, poDS->adfGeoTransform, &nRows, &nCols,
     348             :                   &poDS->bIsLittleEndian);
     349           4 :     poDS->nRasterXSize = nCols;
     350           4 :     poDS->nRasterYSize = nRows;
     351             : 
     352             :     /* -------------------------------------------------------------------- */
     353             :     /*      Create band information objects.                                */
     354             :     /* -------------------------------------------------------------------- */
     355           4 :     poDS->nBands = 1;
     356           4 :     poDS->SetBand(1, new NGSGEOIDRasterBand(poDS));
     357             : 
     358             :     /* -------------------------------------------------------------------- */
     359             :     /*      Initialize any PAM information.                                 */
     360             :     /* -------------------------------------------------------------------- */
     361           4 :     poDS->SetDescription(poOpenInfo->pszFilename);
     362           4 :     poDS->TryLoadXML();
     363             : 
     364             :     /* -------------------------------------------------------------------- */
     365             :     /*      Support overviews.                                              */
     366             :     /* -------------------------------------------------------------------- */
     367           4 :     poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
     368           4 :     return poDS;
     369             : }
     370             : 
     371             : /************************************************************************/
     372             : /*                          GetGeoTransform()                           */
     373             : /************************************************************************/
     374             : 
     375           2 : CPLErr NGSGEOIDDataset::GetGeoTransform(double *padfTransform)
     376             : 
     377             : {
     378           2 :     memcpy(padfTransform, adfGeoTransform, 6 * sizeof(double));
     379             : 
     380           2 :     return CE_None;
     381             : }
     382             : 
     383             : /************************************************************************/
     384             : /*                         GetSpatialRef()                              */
     385             : /************************************************************************/
     386             : 
     387           2 : const OGRSpatialReference *NGSGEOIDDataset::GetSpatialRef() const
     388             : {
     389           2 :     if (!m_oSRS.IsEmpty())
     390             :     {
     391           0 :         return &m_oSRS;
     392             :     }
     393             : 
     394           4 :     CPLString osFilename(CPLGetBasename(GetDescription()));
     395           2 :     osFilename.tolower();
     396             : 
     397             :     // See https://www.ngs.noaa.gov/GEOID/GEOID12B/faq_2012B.shtml
     398             : 
     399             :     // GEOID2012 files ?
     400           2 :     if (STARTS_WITH(osFilename, "g2012") && osFilename.size() >= 7)
     401             :     {
     402           0 :         if (osFilename[6] == 'h' /* Hawai */ ||
     403           0 :             osFilename[6] == 's' /* Samoa */)
     404             :         {
     405             :             // NAD83 (PA11)
     406           0 :             m_oSRS.importFromEPSG(6322);
     407             :         }
     408           0 :         else if (osFilename[6] == 'g' /* Guam */)
     409             :         {
     410             :             // NAD83 (MA11)
     411           0 :             m_oSRS.importFromEPSG(6325);
     412             :         }
     413             :         else
     414             :         {
     415             :             // NAD83 (2011)
     416           0 :             m_oSRS.importFromEPSG(6318);
     417             :         }
     418             : 
     419           0 :         return &m_oSRS;
     420             :     }
     421             : 
     422             :     // USGG2012 files ? We should return IGS08, but there is only a
     423             :     // geocentric CRS in EPSG, so manually forge a geographic one from it
     424           2 :     if (STARTS_WITH(osFilename, "s2012"))
     425             :     {
     426           0 :         m_oSRS.importFromWkt(
     427             :             "GEOGCS[\"IGS08\",\n"
     428             :             "    DATUM[\"IGS08\",\n"
     429             :             "        SPHEROID[\"GRS 1980\",6378137,298.257222101,\n"
     430             :             "            AUTHORITY[\"EPSG\",\"7019\"]],\n"
     431             :             "        AUTHORITY[\"EPSG\",\"1141\"]],\n"
     432             :             "    PRIMEM[\"Greenwich\",0,\n"
     433             :             "        AUTHORITY[\"EPSG\",\"8901\"]],\n"
     434             :             "    UNIT[\"degree\",0.0174532925199433,\n"
     435             :             "        AUTHORITY[\"EPSG\",\"9122\"]]]");
     436           0 :         return &m_oSRS;
     437             :     }
     438             : 
     439           2 :     m_oSRS.importFromWkt(SRS_WKT_WGS84_LAT_LONG);
     440           2 :     return &m_oSRS;
     441             : }
     442             : 
     443             : /************************************************************************/
     444             : /*                       GDALRegister_NGSGEOID()                        */
     445             : /************************************************************************/
     446             : 
     447        1512 : void GDALRegister_NGSGEOID()
     448             : 
     449             : {
     450        1512 :     if (GDALGetDriverByName("NGSGEOID") != nullptr)
     451         295 :         return;
     452             : 
     453        1217 :     GDALDriver *poDriver = new GDALDriver();
     454             : 
     455        1217 :     poDriver->SetDescription("NGSGEOID");
     456        1217 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
     457        1217 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "NOAA NGS Geoid Height Grids");
     458        1217 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
     459        1217 :                               "drivers/raster/ngsgeoid.html");
     460        1217 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "bin");
     461             : 
     462        1217 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
     463             : 
     464        1217 :     poDriver->pfnOpen = NGSGEOIDDataset::Open;
     465        1217 :     poDriver->pfnIdentify = NGSGEOIDDataset::Identify;
     466             : 
     467        1217 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     468             : }

Generated by: LCOV version 1.14