LCOV - code coverage report
Current view: top level - frmts/blx - blxdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 146 180 81.1 %
Date: 2025-02-18 14:19:29 Functions: 13 14 92.9 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  BLX Driver
       4             :  * Purpose:  GDAL BLX support.
       5             :  * Author:   Henrik Johansson, henrik@johome.net
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2006, Henrik Johansson <henrik@johome.net>
       9             :  * Copyright (c) 2008-2011, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ******************************************************************************
      13             :  *
      14             :  */
      15             : 
      16             : #include "cpl_string.h"
      17             : #include "gdal_frmts.h"
      18             : #include "gdal_pam.h"
      19             : 
      20             : CPL_C_START
      21             : #include "blx.h"
      22             : CPL_C_END
      23             : 
      24             : class BLXDataset final : public GDALPamDataset
      25             : {
      26             :     friend class BLXRasterBand;
      27             : 
      28             :     CPLErr GetGeoTransform(double *padfTransform) override;
      29             :     const OGRSpatialReference *GetSpatialRef() const override;
      30             : 
      31             :     OGRSpatialReference m_oSRS{};
      32             :     blxcontext_t *blxcontext = nullptr;
      33             : 
      34             :     bool bIsOverview = false;
      35             :     std::vector<std::unique_ptr<BLXDataset>> apoOverviewDS{};
      36             : 
      37             :   public:
      38             :     BLXDataset();
      39             :     ~BLXDataset();
      40             : 
      41             :     static GDALDataset *Open(GDALOpenInfo *);
      42             : };
      43             : 
      44             : class BLXRasterBand final : public GDALPamRasterBand
      45             : {
      46             :     int overviewLevel;
      47             : 
      48             :   public:
      49             :     BLXRasterBand(BLXDataset *, int, int overviewLevel = 0);
      50             : 
      51             :     double GetNoDataValue(int *pbSuccess = nullptr) override;
      52             :     GDALColorInterp GetColorInterpretation(void) override;
      53             :     int GetOverviewCount() override;
      54             :     GDALRasterBand *GetOverview(int) override;
      55             : 
      56             :     CPLErr IReadBlock(int, int, void *) override;
      57             : };
      58             : 
      59       30327 : GDALDataset *BLXDataset::Open(GDALOpenInfo *poOpenInfo)
      60             : 
      61             : {
      62             :     // --------------------------------------------------------------------
      63             :     //      First that the header looks like a BLX header
      64             :     // --------------------------------------------------------------------
      65       30327 :     if (poOpenInfo->fpL == nullptr || poOpenInfo->nHeaderBytes < 102)
      66       27865 :         return nullptr;
      67             : 
      68        2462 :     if (!blx_checkheader((const char *)poOpenInfo->pabyHeader))
      69        2449 :         return nullptr;
      70             : 
      71             :     // --------------------------------------------------------------------
      72             :     //      Create a corresponding GDALDataset.
      73             :     // --------------------------------------------------------------------
      74          13 :     BLXDataset *poDS = new BLXDataset();
      75             : 
      76             :     // --------------------------------------------------------------------
      77             :     //      Open BLX file
      78             :     // --------------------------------------------------------------------
      79          13 :     poDS->blxcontext = blx_create_context();
      80          13 :     if (poDS->blxcontext == nullptr)
      81             :     {
      82           0 :         delete poDS;
      83           0 :         return nullptr;
      84             :     }
      85          13 :     if (blxopen(poDS->blxcontext, poOpenInfo->pszFilename, "rb") != 0)
      86             :     {
      87           0 :         delete poDS;
      88           0 :         return nullptr;
      89             :     }
      90             : 
      91          13 :     if ((poDS->blxcontext->cell_xsize % (1 << (1 + BLX_OVERVIEWLEVELS))) != 0 ||
      92          13 :         (poDS->blxcontext->cell_ysize % (1 << (1 + BLX_OVERVIEWLEVELS))) != 0)
      93             :     {
      94           0 :         delete poDS;
      95           0 :         return nullptr;
      96             :     }
      97             : 
      98             :     // Update dataset header from BLX context
      99          13 :     poDS->nRasterXSize = poDS->blxcontext->xsize;
     100          13 :     poDS->nRasterYSize = poDS->blxcontext->ysize;
     101             : 
     102             :     // --------------------------------------------------------------------
     103             :     //      Create band information objects.
     104             :     // --------------------------------------------------------------------
     105          13 :     poDS->nBands = 1;
     106          13 :     poDS->SetBand(1, new BLXRasterBand(poDS, 1));
     107             : 
     108             :     // Create overview bands
     109          65 :     for (int i = 0; i < BLX_OVERVIEWLEVELS; i++)
     110             :     {
     111          52 :         poDS->apoOverviewDS.emplace_back(std::make_unique<BLXDataset>());
     112          52 :         poDS->apoOverviewDS[i]->blxcontext = poDS->blxcontext;
     113          52 :         poDS->apoOverviewDS[i]->bIsOverview = true;
     114          52 :         poDS->apoOverviewDS[i]->nRasterXSize = poDS->nRasterXSize >> (i + 1);
     115          52 :         poDS->apoOverviewDS[i]->nRasterYSize = poDS->nRasterYSize >> (i + 1);
     116          52 :         poDS->nBands = 1;
     117         104 :         poDS->apoOverviewDS[i]->SetBand(
     118          52 :             1, new BLXRasterBand(poDS->apoOverviewDS[i].get(), 1, i + 1));
     119             :     }
     120             : 
     121             :     /* -------------------------------------------------------------------- */
     122             :     /*      Confirm the requested access is supported.                      */
     123             :     /* -------------------------------------------------------------------- */
     124          13 :     if (poOpenInfo->eAccess == GA_Update)
     125             :     {
     126           0 :         delete poDS;
     127           0 :         ReportUpdateNotSupportedByDriver("BLX");
     128           0 :         return nullptr;
     129             :     }
     130             : 
     131             :     /* -------------------------------------------------------------------- */
     132             :     /*      Initialize any PAM information.                                 */
     133             :     /* -------------------------------------------------------------------- */
     134          13 :     poDS->SetDescription(poOpenInfo->pszFilename);
     135          13 :     poDS->TryLoadXML();
     136             : 
     137          13 :     return poDS;
     138             : }
     139             : 
     140          65 : BLXDataset::BLXDataset()
     141             : {
     142          65 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     143          65 :     m_oSRS.importFromWkt(SRS_WKT_WGS84_LAT_LONG);
     144          65 : }
     145             : 
     146         130 : BLXDataset::~BLXDataset()
     147             : {
     148          65 :     if (!bIsOverview)
     149             :     {
     150          13 :         if (blxcontext)
     151             :         {
     152          13 :             blxclose(blxcontext);
     153          13 :             blx_free_context(blxcontext);
     154             :         }
     155             :     }
     156         130 : }
     157             : 
     158           8 : CPLErr BLXDataset::GetGeoTransform(double *padfTransform)
     159             : 
     160             : {
     161           8 :     padfTransform[0] = blxcontext->lon;
     162           8 :     padfTransform[1] = blxcontext->pixelsize_lon;
     163           8 :     padfTransform[2] = 0.0;
     164           8 :     padfTransform[3] = blxcontext->lat;
     165           8 :     padfTransform[4] = 0.0;
     166           8 :     padfTransform[5] = blxcontext->pixelsize_lat;
     167             : 
     168           8 :     return CE_None;
     169             : }
     170             : 
     171           6 : const OGRSpatialReference *BLXDataset::GetSpatialRef() const
     172             : {
     173           6 :     return &m_oSRS;
     174             : }
     175             : 
     176          65 : BLXRasterBand::BLXRasterBand(BLXDataset *poDSIn, int nBandIn,
     177          65 :                              int overviewLevelIn)
     178          65 :     : overviewLevel(overviewLevelIn)
     179             : {
     180          65 :     BLXDataset *poGDS = poDSIn;
     181             : 
     182          65 :     poDS = poDSIn;
     183          65 :     nBand = nBandIn;
     184             : 
     185          65 :     eDataType = GDT_Int16;
     186             : 
     187          65 :     nBlockXSize = poGDS->blxcontext->cell_xsize >> overviewLevel;
     188          65 :     nBlockYSize = poGDS->blxcontext->cell_ysize >> overviewLevel;
     189          65 : }
     190             : 
     191           1 : int BLXRasterBand::GetOverviewCount()
     192             : {
     193           1 :     BLXDataset *poGDS = cpl::down_cast<BLXDataset *>(poDS);
     194           1 :     return static_cast<int>(poGDS->apoOverviewDS.size());
     195             : }
     196             : 
     197           4 : GDALRasterBand *BLXRasterBand::GetOverview(int i)
     198             : {
     199           4 :     BLXDataset *poGDS = cpl::down_cast<BLXDataset *>(poDS);
     200             : 
     201           4 :     if (i < 0 || static_cast<size_t>(i) >= poGDS->apoOverviewDS.size())
     202           0 :         return nullptr;
     203             : 
     204           4 :     return poGDS->apoOverviewDS[i]->GetRasterBand(nBand);
     205             : }
     206             : 
     207         192 : CPLErr BLXRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
     208             : 
     209             : {
     210         192 :     BLXDataset *poGDS = reinterpret_cast<BLXDataset *>(poDS);
     211             : 
     212         384 :     if (blx_readcell(poGDS->blxcontext, nBlockYOff, nBlockXOff, (short *)pImage,
     213         192 :                      nBlockXSize * nBlockYSize * 2, overviewLevel) == nullptr)
     214             :     {
     215           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Failed to read BLX cell");
     216           0 :         return CE_Failure;
     217             :     }
     218             : 
     219         192 :     return CE_None;
     220             : }
     221             : 
     222           6 : double BLXRasterBand::GetNoDataValue(int *pbSuccess)
     223             : {
     224           6 :     if (pbSuccess)
     225           6 :         *pbSuccess = TRUE;
     226           6 :     return BLX_UNDEF;
     227             : }
     228             : 
     229           0 : GDALColorInterp BLXRasterBand::GetColorInterpretation(void)
     230             : {
     231           0 :     return GCI_GrayIndex;
     232             : }
     233             : 
     234             : /* TODO: check if georeference is the same as for BLX files, WGS84
     235             :  */
     236          20 : static GDALDataset *BLXCreateCopy(const char *pszFilename, GDALDataset *poSrcDS,
     237             :                                   int bStrict, char **papszOptions,
     238             :                                   GDALProgressFunc pfnProgress,
     239             :                                   void *pProgressData)
     240             : 
     241             : {
     242             :     // --------------------------------------------------------------------
     243             :     //      Some rudimentary checks
     244             :     // --------------------------------------------------------------------
     245          20 :     const int nBands = poSrcDS->GetRasterCount();
     246          20 :     if (nBands != 1)
     247             :     {
     248           5 :         CPLError(CE_Failure, CPLE_NotSupported,
     249             :                  "BLX driver doesn't support %d bands.  Must be 1 (grey) ",
     250             :                  nBands);
     251           5 :         return nullptr;
     252             :     }
     253             : 
     254          15 :     if (poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Int16 && bStrict)
     255             :     {
     256          10 :         CPLError(CE_Failure, CPLE_NotSupported,
     257             :                  "BLX driver doesn't support data type %s. "
     258             :                  "Only 16 bit byte bands supported.\n",
     259             :                  GDALGetDataTypeName(
     260             :                      poSrcDS->GetRasterBand(1)->GetRasterDataType()));
     261             : 
     262          10 :         return nullptr;
     263             :     }
     264             : 
     265           5 :     const int nXSize = poSrcDS->GetRasterXSize();
     266           5 :     const int nYSize = poSrcDS->GetRasterYSize();
     267           5 :     if ((nXSize % 128 != 0) || (nYSize % 128 != 0))
     268             :     {
     269           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     270             :                  "BLX driver doesn't support dimensions that are not a "
     271             :                  "multiple of 128.\n");
     272             : 
     273           1 :         return nullptr;
     274             :     }
     275             : 
     276             :     // --------------------------------------------------------------------
     277             :     //      What options has the user selected?
     278             :     // --------------------------------------------------------------------
     279           4 :     int zscale = 1;
     280           4 :     if (CSLFetchNameValue(papszOptions, "ZSCALE") != nullptr)
     281             :     {
     282           0 :         zscale = atoi(CSLFetchNameValue(papszOptions, "ZSCALE"));
     283           0 :         if (zscale < 1)
     284             :         {
     285           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
     286             :                      "ZSCALE=%s is not a legal value in the range >= 1.",
     287             :                      CSLFetchNameValue(papszOptions, "ZSCALE"));
     288           0 :             return nullptr;
     289             :         }
     290             :     }
     291             : 
     292           4 :     int fillundef = 1;
     293           4 :     if (CSLFetchNameValue(papszOptions, "FILLUNDEF") != nullptr &&
     294           0 :         EQUAL(CSLFetchNameValue(papszOptions, "FILLUNDEF"), "NO"))
     295           0 :         fillundef = 0;
     296             : 
     297           4 :     int fillundefval = 0;
     298           4 :     if (CSLFetchNameValue(papszOptions, "FILLUNDEFVAL") != nullptr)
     299             :     {
     300           0 :         fillundefval = atoi(CSLFetchNameValue(papszOptions, "FILLUNDEFVAL"));
     301           0 :         if ((fillundefval < -32768) || (fillundefval > 32767))
     302             :         {
     303           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
     304             :                      "FILLUNDEFVAL=%s is not a legal value in the range "
     305             :                      "-32768, 32767.",
     306             :                      CSLFetchNameValue(papszOptions, "FILLUNDEFVAL"));
     307           0 :             return nullptr;
     308             :         }
     309             :     }
     310             : 
     311           4 :     int endian = LITTLEENDIAN;
     312           5 :     if (CSLFetchNameValue(papszOptions, "BIGENDIAN") != nullptr &&
     313           1 :         !EQUAL(CSLFetchNameValue(papszOptions, "BIGENDIAN"), "NO"))
     314           1 :         endian = BIGENDIAN;
     315             : 
     316             :     // --------------------------------------------------------------------
     317             :     //      Create the dataset.
     318             :     // --------------------------------------------------------------------
     319             : 
     320             :     // Create a BLX context
     321           4 :     blxcontext_t *ctx = blx_create_context();
     322             : 
     323             :     // Setup BLX parameters
     324           4 :     ctx->cell_rows = nYSize / ctx->cell_ysize;
     325           4 :     ctx->cell_cols = nXSize / ctx->cell_xsize;
     326           4 :     ctx->zscale = zscale;
     327           4 :     ctx->fillundef = fillundef;
     328           4 :     ctx->fillundefval = fillundefval;
     329           4 :     ctx->endian = endian;
     330             : 
     331           4 :     if (blxopen(ctx, pszFilename, "wb"))
     332             :     {
     333           2 :         CPLError(CE_Failure, CPLE_OpenFailed, "Unable to create blx file %s.\n",
     334             :                  pszFilename);
     335           2 :         blx_free_context(ctx);
     336           2 :         return nullptr;
     337             :     }
     338             : 
     339             :     // --------------------------------------------------------------------
     340             :     //      Loop over image, copying image data.
     341             :     // --------------------------------------------------------------------
     342             : 
     343           2 :     GInt16 *pabyTile = (GInt16 *)VSI_MALLOC_VERBOSE(
     344             :         sizeof(GInt16) * ctx->cell_xsize * ctx->cell_ysize);
     345           2 :     if (pabyTile == nullptr)
     346             :     {
     347           0 :         blxclose(ctx);
     348           0 :         blx_free_context(ctx);
     349           0 :         return nullptr;
     350             :     }
     351             : 
     352           2 :     CPLErr eErr = CE_None;
     353           2 :     if (!pfnProgress(0.0, nullptr, pProgressData))
     354           0 :         eErr = CE_Failure;
     355             : 
     356          10 :     for (int i = 0; (i < ctx->cell_rows) && (eErr == CE_None); i++)
     357          40 :         for (int j = 0; j < ctx->cell_cols; j++)
     358             :         {
     359          32 :             GDALRasterBand *poBand = poSrcDS->GetRasterBand(1);
     360          64 :             eErr = poBand->RasterIO(GF_Read, j * ctx->cell_xsize,
     361          32 :                                     i * ctx->cell_ysize, ctx->cell_xsize,
     362             :                                     ctx->cell_ysize, pabyTile, ctx->cell_xsize,
     363             :                                     ctx->cell_ysize, GDT_Int16, 0, 0, nullptr);
     364          32 :             if (eErr >= CE_Failure)
     365           0 :                 break;
     366          32 :             blxdata *celldata = pabyTile;
     367          32 :             if (blx_writecell(ctx, celldata, i, j) != 0)
     368             :             {
     369           0 :                 eErr = CE_Failure;
     370           0 :                 break;
     371             :             }
     372             : 
     373          32 :             if (!pfnProgress(1.0 * (i * ctx->cell_cols + j) /
     374          32 :                                  (ctx->cell_rows * ctx->cell_cols),
     375             :                              nullptr, pProgressData))
     376             :             {
     377           0 :                 eErr = CE_Failure;
     378           0 :                 break;
     379             :             }
     380             :         }
     381             : 
     382           2 :     pfnProgress(1.0, nullptr, pProgressData);
     383             : 
     384           2 :     CPLFree(pabyTile);
     385             : 
     386             :     double adfGeoTransform[6];
     387           2 :     if (poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None)
     388             :     {
     389           2 :         ctx->lon = adfGeoTransform[0];
     390           2 :         ctx->lat = adfGeoTransform[3];
     391           2 :         ctx->pixelsize_lon = adfGeoTransform[1];
     392           2 :         ctx->pixelsize_lat = adfGeoTransform[5];
     393             :     }
     394             : 
     395           2 :     blxclose(ctx);
     396           2 :     blx_free_context(ctx);
     397             : 
     398           2 :     if (eErr == CE_None)
     399           2 :         return GDALDataset::FromHandle(GDALOpen(pszFilename, GA_ReadOnly));
     400             : 
     401           0 :     return nullptr;
     402             : }
     403             : 
     404        1686 : void GDALRegister_BLX()
     405             : 
     406             : {
     407        1686 :     if (GDALGetDriverByName("BLX") != nullptr)
     408         302 :         return;
     409             : 
     410        1384 :     GDALDriver *poDriver = new GDALDriver();
     411             : 
     412        1384 :     poDriver->SetDescription("BLX");
     413        1384 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
     414        1384 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Magellan topo (.blx)");
     415        1384 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/blx.html");
     416        1384 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "blx");
     417             : 
     418        1384 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
     419             : 
     420        1384 :     poDriver->pfnOpen = BLXDataset::Open;
     421        1384 :     poDriver->pfnCreateCopy = BLXCreateCopy;
     422             : 
     423        1384 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     424             : }

Generated by: LCOV version 1.14