LCOV - code coverage report
Current view: top level - frmts/gxf - gxfdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 92 133 69.2 %
Date: 2025-10-24 23:03:13 Functions: 8 10 80.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GXF Reader
       4             :  * Purpose:  GDAL binding for GXF reader.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 1998, Frank Warmerdam
       9             :  * Copyright (c) 2008-2012, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "gdal_frmts.h"
      15             : #include "gdal_pam.h"
      16             : #include "gdal_driver.h"
      17             : #include "gdal_drivermanager.h"
      18             : #include "gdal_openinfo.h"
      19             : #include "gdal_cpp_functions.h"
      20             : #include "gxfopen.h"
      21             : 
      22             : /************************************************************************/
      23             : /* ==================================================================== */
      24             : /*                              GXFDataset                              */
      25             : /* ==================================================================== */
      26             : /************************************************************************/
      27             : 
      28             : class GXFRasterBand;
      29             : 
      30             : class GXFDataset final : public GDALPamDataset
      31             : {
      32             :     friend class GXFRasterBand;
      33             : 
      34             :     GXFHandle hGXF;
      35             : 
      36             :     OGRSpatialReference m_oSRS{};
      37             :     double dfNoDataValue;
      38             :     GDALDataType eDataType;
      39             : 
      40             :   public:
      41             :     GXFDataset();
      42             :     ~GXFDataset() override;
      43             : 
      44             :     static GDALDataset *Open(GDALOpenInfo *);
      45             : 
      46             :     CPLErr GetGeoTransform(GDALGeoTransform &gt) const override;
      47             :     const OGRSpatialReference *GetSpatialRef() const override;
      48             : };
      49             : 
      50             : /************************************************************************/
      51             : /* ==================================================================== */
      52             : /*                            GXFRasterBand                             */
      53             : /* ==================================================================== */
      54             : /************************************************************************/
      55             : 
      56             : class GXFRasterBand final : public GDALPamRasterBand
      57             : {
      58             :     friend class GXFDataset;
      59             : 
      60             :   public:
      61             :     GXFRasterBand(GXFDataset *, int);
      62             :     double GetNoDataValue(int *bGotNoDataValue) override;
      63             : 
      64             :     CPLErr IReadBlock(int, int, void *) override;
      65             : };
      66             : 
      67             : /************************************************************************/
      68             : /*                           GXFRasterBand()                            */
      69             : /************************************************************************/
      70             : 
      71           4 : GXFRasterBand::GXFRasterBand(GXFDataset *poDSIn, int nBandIn)
      72             : 
      73             : {
      74           4 :     poDS = poDSIn;
      75           4 :     nBand = nBandIn;
      76             : 
      77           4 :     eDataType = poDSIn->eDataType;
      78             : 
      79           4 :     nBlockXSize = poDS->GetRasterXSize();
      80           4 :     nBlockYSize = 1;
      81           4 : }
      82             : 
      83             : /************************************************************************/
      84             : /*                          GetNoDataValue()                          */
      85             : /************************************************************************/
      86             : 
      87           0 : double GXFRasterBand::GetNoDataValue(int *bGotNoDataValue)
      88             : 
      89             : {
      90           0 :     GXFDataset *poGXF_DS = cpl::down_cast<GXFDataset *>(poDS);
      91           0 :     if (bGotNoDataValue)
      92           0 :         *bGotNoDataValue = (fabs(poGXF_DS->dfNoDataValue - -1e12) > .1);
      93           0 :     if (eDataType == GDT_Float32)
      94           0 :         return (double)(float)poGXF_DS->dfNoDataValue;
      95             : 
      96           0 :     return poGXF_DS->dfNoDataValue;
      97             : }
      98             : 
      99             : /************************************************************************/
     100             : /*                             IReadBlock()                             */
     101             : /************************************************************************/
     102             : 
     103          11 : CPLErr GXFRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
     104             :                                  void *pImage)
     105             : {
     106          11 :     GXFDataset *const poGXF_DS = cpl::down_cast<GXFDataset *>(poDS);
     107             : 
     108          11 :     if (eDataType == GDT_Float32)
     109             :     {
     110          11 :         double *padfBuffer = (double *)VSIMalloc2(sizeof(double), nBlockXSize);
     111          11 :         if (padfBuffer == nullptr)
     112           0 :             return CE_Failure;
     113             :         const CPLErr eErr =
     114          11 :             GXFGetScanline(poGXF_DS->hGXF, nBlockYOff, padfBuffer);
     115             : 
     116          11 :         float *pafBuffer = (float *)pImage;
     117         103 :         for (int i = 0; i < nBlockXSize; i++)
     118          92 :             pafBuffer[i] = (float)padfBuffer[i];
     119             : 
     120          11 :         CPLFree(padfBuffer);
     121             : 
     122          11 :         return eErr;
     123             :     }
     124             : 
     125             :     const CPLErr eErr =
     126           0 :         eDataType == GDT_Float64
     127           0 :             ? GXFGetScanline(poGXF_DS->hGXF, nBlockYOff, (double *)pImage)
     128           0 :             : CE_Failure;
     129             : 
     130           0 :     return eErr;
     131             : }
     132             : 
     133             : /************************************************************************/
     134             : /* ==================================================================== */
     135             : /*                              GXFDataset                              */
     136             : /* ==================================================================== */
     137             : /************************************************************************/
     138             : 
     139             : /************************************************************************/
     140             : /*                             GXFDataset()                             */
     141             : /************************************************************************/
     142             : 
     143           4 : GXFDataset::GXFDataset()
     144           4 :     : hGXF(nullptr), dfNoDataValue(0), eDataType(GDT_Float32)
     145             : {
     146           4 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     147           4 : }
     148             : 
     149             : /************************************************************************/
     150             : /*                            ~GXFDataset()                             */
     151             : /************************************************************************/
     152             : 
     153           8 : GXFDataset::~GXFDataset()
     154             : 
     155             : {
     156           4 :     FlushCache(true);
     157           4 :     if (hGXF != nullptr)
     158           4 :         GXFClose(hGXF);
     159           8 : }
     160             : 
     161             : /************************************************************************/
     162             : /*                          GetGeoTransform()                           */
     163             : /************************************************************************/
     164             : 
     165           0 : CPLErr GXFDataset::GetGeoTransform(GDALGeoTransform &gt) const
     166             : 
     167             : {
     168           0 :     double dfXOrigin = 0.0;
     169           0 :     double dfYOrigin = 0.0;
     170           0 :     double dfXSize = 0.0;
     171           0 :     double dfYSize = 0.0;
     172           0 :     double dfRotation = 0.0;
     173             : 
     174           0 :     const CPLErr eErr = GXFGetPosition(hGXF, &dfXOrigin, &dfYOrigin, &dfXSize,
     175             :                                        &dfYSize, &dfRotation);
     176             : 
     177           0 :     if (eErr != CE_None)
     178           0 :         return eErr;
     179             : 
     180             :     // Transform to radians.
     181           0 :     dfRotation = (dfRotation / 360.0) * 2.0 * M_PI;
     182             : 
     183           0 :     gt[1] = dfXSize * cos(dfRotation);
     184           0 :     gt[2] = dfYSize * sin(dfRotation);
     185           0 :     gt[4] = dfXSize * sin(dfRotation);
     186           0 :     gt[5] = -1 * dfYSize * cos(dfRotation);
     187             : 
     188             :     // take into account that GXF is point or center of pixel oriented.
     189           0 :     gt[0] = dfXOrigin - 0.5 * gt[1] - 0.5 * gt[2];
     190           0 :     gt[3] = dfYOrigin - 0.5 * gt[4] - 0.5 * gt[5];
     191             : 
     192           0 :     return CE_None;
     193             : }
     194             : 
     195             : /************************************************************************/
     196             : /*                         GetSpatialRef()                              */
     197             : /************************************************************************/
     198             : 
     199           1 : const OGRSpatialReference *GXFDataset::GetSpatialRef() const
     200             : {
     201           1 :     return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
     202             : }
     203             : 
     204             : /************************************************************************/
     205             : /*                                Open()                                */
     206             : /************************************************************************/
     207             : 
     208       34124 : GDALDataset *GXFDataset::Open(GDALOpenInfo *poOpenInfo)
     209             : 
     210             : {
     211             :     /* -------------------------------------------------------------------- */
     212             :     /*      Before trying GXFOpen() we first verify that there is at        */
     213             :     /*      least one "\n#keyword" type signature in the first chunk of     */
     214             :     /*      the file.                                                       */
     215             :     /* -------------------------------------------------------------------- */
     216       34124 :     if (poOpenInfo->nHeaderBytes < 50 || poOpenInfo->fpL == nullptr)
     217       31068 :         return nullptr;
     218             : 
     219        3056 :     bool bFoundKeyword = false;
     220        3056 :     bool bFoundIllegal = false;
     221      850781 :     for (int i = 0; i < poOpenInfo->nHeaderBytes - 1; i++)
     222             :     {
     223      849910 :         if ((poOpenInfo->pabyHeader[i] == 10 ||
     224      827724 :              poOpenInfo->pabyHeader[i] == 13) &&
     225       23411 :             poOpenInfo->pabyHeader[i + 1] == '#')
     226             :         {
     227         194 :             if (STARTS_WITH((const char *)poOpenInfo->pabyHeader + i + 2,
     228             :                             "include"))
     229           0 :                 return nullptr;
     230         194 :             if (STARTS_WITH((const char *)poOpenInfo->pabyHeader + i + 2,
     231             :                             "define"))
     232           0 :                 return nullptr;
     233         194 :             if (STARTS_WITH((const char *)poOpenInfo->pabyHeader + i + 2,
     234             :                             "ifdef"))
     235           0 :                 return nullptr;
     236         194 :             bFoundKeyword = true;
     237             :         }
     238      849910 :         if (poOpenInfo->pabyHeader[i] == 0)
     239             :         {
     240        2185 :             bFoundIllegal = true;
     241        2185 :             break;
     242             :         }
     243             :     }
     244             : 
     245        3056 :     if (!bFoundKeyword || bFoundIllegal)
     246        3012 :         return nullptr;
     247             : 
     248             :     /* -------------------------------------------------------------------- */
     249             :     /*      At this point it is plausible that this is a GXF file, but      */
     250             :     /*      we also now verify that there is a #GRID keyword before         */
     251             :     /*      passing it off to GXFOpen().  We check in the first 50K.        */
     252             :     /* -------------------------------------------------------------------- */
     253          44 :     CPL_IGNORE_RET_VAL(poOpenInfo->TryToIngest(50000));
     254          44 :     bool bGotGrid = false;
     255             : 
     256          44 :     const char *pszBigBuf = (const char *)poOpenInfo->pabyHeader;
     257       36219 :     for (int i = 0; i < poOpenInfo->nHeaderBytes - 5 && !bGotGrid; i++)
     258             :     {
     259       36175 :         if (pszBigBuf[i] == '#' && STARTS_WITH_CI(pszBigBuf + i + 1, "GRID"))
     260           4 :             bGotGrid = true;
     261             :     }
     262             : 
     263          44 :     if (!bGotGrid)
     264          40 :         return nullptr;
     265             : 
     266           4 :     VSIFCloseL(poOpenInfo->fpL);
     267           4 :     poOpenInfo->fpL = nullptr;
     268             : 
     269             :     /* -------------------------------------------------------------------- */
     270             :     /*      Try opening the dataset.                                        */
     271             :     /* -------------------------------------------------------------------- */
     272             : 
     273           4 :     GXFHandle l_hGXF = GXFOpen(poOpenInfo->pszFilename);
     274             : 
     275           4 :     if (l_hGXF == nullptr)
     276           0 :         return nullptr;
     277             : 
     278             :     /* -------------------------------------------------------------------- */
     279             :     /*      Confirm the requested access is supported.                      */
     280             :     /* -------------------------------------------------------------------- */
     281           4 :     if (poOpenInfo->eAccess == GA_Update)
     282             :     {
     283           0 :         GXFClose(l_hGXF);
     284           0 :         ReportUpdateNotSupportedByDriver("GXF");
     285           0 :         return nullptr;
     286             :     }
     287             : 
     288             :     /* -------------------------------------------------------------------- */
     289             :     /*      Create a corresponding GDALDataset.                             */
     290             :     /* -------------------------------------------------------------------- */
     291           4 :     GXFDataset *poDS = new GXFDataset();
     292             : 
     293           4 :     const char *pszGXFDataType = CPLGetConfigOption("GXF_DATATYPE", "Float32");
     294           4 :     GDALDataType eDT = GDALGetDataTypeByName(pszGXFDataType);
     295           4 :     if (!(eDT == GDT_Float32 || eDT == GDT_Float64))
     296             :     {
     297           0 :         CPLError(CE_Warning, CPLE_NotSupported,
     298             :                  "Unsupported value for GXF_DATATYPE : %s", pszGXFDataType);
     299           0 :         eDT = GDT_Float32;
     300             :     }
     301             : 
     302           4 :     poDS->hGXF = l_hGXF;
     303           4 :     poDS->eDataType = eDT;
     304             : 
     305             :     /* -------------------------------------------------------------------- */
     306             :     /*      Establish the projection.                                       */
     307             :     /* -------------------------------------------------------------------- */
     308           4 :     char *pszProjection = GXFGetMapProjectionAsOGCWKT(l_hGXF);
     309           4 :     if (pszProjection && pszProjection[0] != '\0')
     310           2 :         poDS->m_oSRS.importFromWkt(pszProjection);
     311           4 :     CPLFree(pszProjection);
     312             : 
     313             :     /* -------------------------------------------------------------------- */
     314             :     /*      Capture some information from the file that is of interest.     */
     315             :     /* -------------------------------------------------------------------- */
     316           4 :     GXFGetRawInfo(l_hGXF, &(poDS->nRasterXSize), &(poDS->nRasterYSize), nullptr,
     317             :                   nullptr, nullptr, &(poDS->dfNoDataValue));
     318             : 
     319           4 :     if (poDS->nRasterXSize <= 0 || poDS->nRasterYSize <= 0)
     320             :     {
     321           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid dimensions : %d x %d",
     322             :                  poDS->nRasterXSize, poDS->nRasterYSize);
     323           0 :         delete poDS;
     324           0 :         return nullptr;
     325             :     }
     326             : 
     327             :     /* -------------------------------------------------------------------- */
     328             :     /*      Create band information objects.                                */
     329             :     /* -------------------------------------------------------------------- */
     330           4 :     poDS->nBands = 1;
     331           4 :     poDS->SetBand(1, new GXFRasterBand(poDS, 1));
     332             : 
     333             :     /* -------------------------------------------------------------------- */
     334             :     /*      Initialize any PAM information.                                 */
     335             :     /* -------------------------------------------------------------------- */
     336           4 :     poDS->SetDescription(poOpenInfo->pszFilename);
     337           4 :     poDS->TryLoadXML();
     338             : 
     339             :     /* -------------------------------------------------------------------- */
     340             :     /*      Check for external overviews.                                   */
     341             :     /* -------------------------------------------------------------------- */
     342           8 :     poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename,
     343           4 :                                 poOpenInfo->GetSiblingFiles());
     344             : 
     345           4 :     return poDS;
     346             : }
     347             : 
     348             : /************************************************************************/
     349             : /*                          GDALRegister_GXF()                          */
     350             : /************************************************************************/
     351             : 
     352        2038 : void GDALRegister_GXF()
     353             : 
     354             : {
     355        2038 :     if (GDALGetDriverByName("GXF") != nullptr)
     356         283 :         return;
     357             : 
     358        1755 :     GDALDriver *poDriver = new GDALDriver();
     359             : 
     360        1755 :     poDriver->SetDescription("GXF");
     361        1755 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
     362        1755 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
     363        1755 :                               "GeoSoft Grid Exchange Format");
     364        1755 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/gxf.html");
     365        1755 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "gxf");
     366        1755 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
     367             : 
     368        1755 :     poDriver->pfnOpen = GXFDataset::Open;
     369             : 
     370        1755 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     371             : }

Generated by: LCOV version 1.14