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: 2024-11-21 22:18:42 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       29844 : GDALDataset *BLXDataset::Open(GDALOpenInfo *poOpenInfo)
      60             : 
      61             : {
      62             :     // --------------------------------------------------------------------
      63             :     //      First that the header looks like a BLX header
      64             :     // --------------------------------------------------------------------
      65       29844 :     if (poOpenInfo->fpL == nullptr || poOpenInfo->nHeaderBytes < 102)
      66       27434 :         return nullptr;
      67             : 
      68        2410 :     if (!blx_checkheader((const char *)poOpenInfo->pabyHeader))
      69        2397 :         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 :         CPLError(CE_Failure, CPLE_NotSupported,
     128             :                  "The BLX driver does not support update access to existing"
     129             :                  " datasets.\n");
     130           0 :         return nullptr;
     131             :     }
     132             : 
     133             :     /* -------------------------------------------------------------------- */
     134             :     /*      Initialize any PAM information.                                 */
     135             :     /* -------------------------------------------------------------------- */
     136          13 :     poDS->SetDescription(poOpenInfo->pszFilename);
     137          13 :     poDS->TryLoadXML();
     138             : 
     139          13 :     return poDS;
     140             : }
     141             : 
     142          65 : BLXDataset::BLXDataset()
     143             : {
     144          65 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     145          65 :     m_oSRS.importFromWkt(SRS_WKT_WGS84_LAT_LONG);
     146          65 : }
     147             : 
     148         130 : BLXDataset::~BLXDataset()
     149             : {
     150          65 :     if (!bIsOverview)
     151             :     {
     152          13 :         if (blxcontext)
     153             :         {
     154          13 :             blxclose(blxcontext);
     155          13 :             blx_free_context(blxcontext);
     156             :         }
     157             :     }
     158         130 : }
     159             : 
     160           8 : CPLErr BLXDataset::GetGeoTransform(double *padfTransform)
     161             : 
     162             : {
     163           8 :     padfTransform[0] = blxcontext->lon;
     164           8 :     padfTransform[1] = blxcontext->pixelsize_lon;
     165           8 :     padfTransform[2] = 0.0;
     166           8 :     padfTransform[3] = blxcontext->lat;
     167           8 :     padfTransform[4] = 0.0;
     168           8 :     padfTransform[5] = blxcontext->pixelsize_lat;
     169             : 
     170           8 :     return CE_None;
     171             : }
     172             : 
     173           6 : const OGRSpatialReference *BLXDataset::GetSpatialRef() const
     174             : {
     175           6 :     return &m_oSRS;
     176             : }
     177             : 
     178          65 : BLXRasterBand::BLXRasterBand(BLXDataset *poDSIn, int nBandIn,
     179          65 :                              int overviewLevelIn)
     180          65 :     : overviewLevel(overviewLevelIn)
     181             : {
     182          65 :     BLXDataset *poGDS = poDSIn;
     183             : 
     184          65 :     poDS = poDSIn;
     185          65 :     nBand = nBandIn;
     186             : 
     187          65 :     eDataType = GDT_Int16;
     188             : 
     189          65 :     nBlockXSize = poGDS->blxcontext->cell_xsize >> overviewLevel;
     190          65 :     nBlockYSize = poGDS->blxcontext->cell_ysize >> overviewLevel;
     191          65 : }
     192             : 
     193           1 : int BLXRasterBand::GetOverviewCount()
     194             : {
     195           1 :     BLXDataset *poGDS = cpl::down_cast<BLXDataset *>(poDS);
     196           1 :     return static_cast<int>(poGDS->apoOverviewDS.size());
     197             : }
     198             : 
     199           4 : GDALRasterBand *BLXRasterBand::GetOverview(int i)
     200             : {
     201           4 :     BLXDataset *poGDS = cpl::down_cast<BLXDataset *>(poDS);
     202             : 
     203           4 :     if (i < 0 || static_cast<size_t>(i) >= poGDS->apoOverviewDS.size())
     204           0 :         return nullptr;
     205             : 
     206           4 :     return poGDS->apoOverviewDS[i]->GetRasterBand(nBand);
     207             : }
     208             : 
     209         192 : CPLErr BLXRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
     210             : 
     211             : {
     212         192 :     BLXDataset *poGDS = reinterpret_cast<BLXDataset *>(poDS);
     213             : 
     214         384 :     if (blx_readcell(poGDS->blxcontext, nBlockYOff, nBlockXOff, (short *)pImage,
     215         192 :                      nBlockXSize * nBlockYSize * 2, overviewLevel) == nullptr)
     216             :     {
     217           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Failed to read BLX cell");
     218           0 :         return CE_Failure;
     219             :     }
     220             : 
     221         192 :     return CE_None;
     222             : }
     223             : 
     224           6 : double BLXRasterBand::GetNoDataValue(int *pbSuccess)
     225             : {
     226           6 :     if (pbSuccess)
     227           6 :         *pbSuccess = TRUE;
     228           6 :     return BLX_UNDEF;
     229             : }
     230             : 
     231           0 : GDALColorInterp BLXRasterBand::GetColorInterpretation(void)
     232             : {
     233           0 :     return GCI_GrayIndex;
     234             : }
     235             : 
     236             : /* TODO: check if georeference is the same as for BLX files, WGS84
     237             :  */
     238          20 : static GDALDataset *BLXCreateCopy(const char *pszFilename, GDALDataset *poSrcDS,
     239             :                                   int bStrict, char **papszOptions,
     240             :                                   GDALProgressFunc pfnProgress,
     241             :                                   void *pProgressData)
     242             : 
     243             : {
     244             :     // --------------------------------------------------------------------
     245             :     //      Some rudimentary checks
     246             :     // --------------------------------------------------------------------
     247          20 :     const int nBands = poSrcDS->GetRasterCount();
     248          20 :     if (nBands != 1)
     249             :     {
     250           5 :         CPLError(CE_Failure, CPLE_NotSupported,
     251             :                  "BLX driver doesn't support %d bands.  Must be 1 (grey) ",
     252             :                  nBands);
     253           5 :         return nullptr;
     254             :     }
     255             : 
     256          15 :     if (poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Int16 && bStrict)
     257             :     {
     258          10 :         CPLError(CE_Failure, CPLE_NotSupported,
     259             :                  "BLX driver doesn't support data type %s. "
     260             :                  "Only 16 bit byte bands supported.\n",
     261             :                  GDALGetDataTypeName(
     262             :                      poSrcDS->GetRasterBand(1)->GetRasterDataType()));
     263             : 
     264          10 :         return nullptr;
     265             :     }
     266             : 
     267           5 :     const int nXSize = poSrcDS->GetRasterXSize();
     268           5 :     const int nYSize = poSrcDS->GetRasterYSize();
     269           5 :     if ((nXSize % 128 != 0) || (nYSize % 128 != 0))
     270             :     {
     271           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     272             :                  "BLX driver doesn't support dimensions that are not a "
     273             :                  "multiple of 128.\n");
     274             : 
     275           1 :         return nullptr;
     276             :     }
     277             : 
     278             :     // --------------------------------------------------------------------
     279             :     //      What options has the user selected?
     280             :     // --------------------------------------------------------------------
     281           4 :     int zscale = 1;
     282           4 :     if (CSLFetchNameValue(papszOptions, "ZSCALE") != nullptr)
     283             :     {
     284           0 :         zscale = atoi(CSLFetchNameValue(papszOptions, "ZSCALE"));
     285           0 :         if (zscale < 1)
     286             :         {
     287           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
     288             :                      "ZSCALE=%s is not a legal value in the range >= 1.",
     289             :                      CSLFetchNameValue(papszOptions, "ZSCALE"));
     290           0 :             return nullptr;
     291             :         }
     292             :     }
     293             : 
     294           4 :     int fillundef = 1;
     295           4 :     if (CSLFetchNameValue(papszOptions, "FILLUNDEF") != nullptr &&
     296           0 :         EQUAL(CSLFetchNameValue(papszOptions, "FILLUNDEF"), "NO"))
     297           0 :         fillundef = 0;
     298             : 
     299           4 :     int fillundefval = 0;
     300           4 :     if (CSLFetchNameValue(papszOptions, "FILLUNDEFVAL") != nullptr)
     301             :     {
     302           0 :         fillundefval = atoi(CSLFetchNameValue(papszOptions, "FILLUNDEFVAL"));
     303           0 :         if ((fillundefval < -32768) || (fillundefval > 32767))
     304             :         {
     305           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
     306             :                      "FILLUNDEFVAL=%s is not a legal value in the range "
     307             :                      "-32768, 32767.",
     308             :                      CSLFetchNameValue(papszOptions, "FILLUNDEFVAL"));
     309           0 :             return nullptr;
     310             :         }
     311             :     }
     312             : 
     313           4 :     int endian = LITTLEENDIAN;
     314           5 :     if (CSLFetchNameValue(papszOptions, "BIGENDIAN") != nullptr &&
     315           1 :         !EQUAL(CSLFetchNameValue(papszOptions, "BIGENDIAN"), "NO"))
     316           1 :         endian = BIGENDIAN;
     317             : 
     318             :     // --------------------------------------------------------------------
     319             :     //      Create the dataset.
     320             :     // --------------------------------------------------------------------
     321             : 
     322             :     // Create a BLX context
     323           4 :     blxcontext_t *ctx = blx_create_context();
     324             : 
     325             :     // Setup BLX parameters
     326           4 :     ctx->cell_rows = nYSize / ctx->cell_ysize;
     327           4 :     ctx->cell_cols = nXSize / ctx->cell_xsize;
     328           4 :     ctx->zscale = zscale;
     329           4 :     ctx->fillundef = fillundef;
     330           4 :     ctx->fillundefval = fillundefval;
     331           4 :     ctx->endian = endian;
     332             : 
     333           4 :     if (blxopen(ctx, pszFilename, "wb"))
     334             :     {
     335           2 :         CPLError(CE_Failure, CPLE_OpenFailed, "Unable to create blx file %s.\n",
     336             :                  pszFilename);
     337           2 :         blx_free_context(ctx);
     338           2 :         return nullptr;
     339             :     }
     340             : 
     341             :     // --------------------------------------------------------------------
     342             :     //      Loop over image, copying image data.
     343             :     // --------------------------------------------------------------------
     344             : 
     345           2 :     GInt16 *pabyTile = (GInt16 *)VSI_MALLOC_VERBOSE(
     346             :         sizeof(GInt16) * ctx->cell_xsize * ctx->cell_ysize);
     347           2 :     if (pabyTile == nullptr)
     348             :     {
     349           0 :         blxclose(ctx);
     350           0 :         blx_free_context(ctx);
     351           0 :         return nullptr;
     352             :     }
     353             : 
     354           2 :     CPLErr eErr = CE_None;
     355           2 :     if (!pfnProgress(0.0, nullptr, pProgressData))
     356           0 :         eErr = CE_Failure;
     357             : 
     358          10 :     for (int i = 0; (i < ctx->cell_rows) && (eErr == CE_None); i++)
     359          40 :         for (int j = 0; j < ctx->cell_cols; j++)
     360             :         {
     361          32 :             GDALRasterBand *poBand = poSrcDS->GetRasterBand(1);
     362          64 :             eErr = poBand->RasterIO(GF_Read, j * ctx->cell_xsize,
     363          32 :                                     i * ctx->cell_ysize, ctx->cell_xsize,
     364             :                                     ctx->cell_ysize, pabyTile, ctx->cell_xsize,
     365             :                                     ctx->cell_ysize, GDT_Int16, 0, 0, nullptr);
     366          32 :             if (eErr >= CE_Failure)
     367           0 :                 break;
     368          32 :             blxdata *celldata = pabyTile;
     369          32 :             if (blx_writecell(ctx, celldata, i, j) != 0)
     370             :             {
     371           0 :                 eErr = CE_Failure;
     372           0 :                 break;
     373             :             }
     374             : 
     375          32 :             if (!pfnProgress(1.0 * (i * ctx->cell_cols + j) /
     376          32 :                                  (ctx->cell_rows * ctx->cell_cols),
     377             :                              nullptr, pProgressData))
     378             :             {
     379           0 :                 eErr = CE_Failure;
     380           0 :                 break;
     381             :             }
     382             :         }
     383             : 
     384           2 :     pfnProgress(1.0, nullptr, pProgressData);
     385             : 
     386           2 :     CPLFree(pabyTile);
     387             : 
     388             :     double adfGeoTransform[6];
     389           2 :     if (poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None)
     390             :     {
     391           2 :         ctx->lon = adfGeoTransform[0];
     392           2 :         ctx->lat = adfGeoTransform[3];
     393           2 :         ctx->pixelsize_lon = adfGeoTransform[1];
     394           2 :         ctx->pixelsize_lat = adfGeoTransform[5];
     395             :     }
     396             : 
     397           2 :     blxclose(ctx);
     398           2 :     blx_free_context(ctx);
     399             : 
     400           2 :     if (eErr == CE_None)
     401           2 :         return GDALDataset::FromHandle(GDALOpen(pszFilename, GA_ReadOnly));
     402             : 
     403           0 :     return nullptr;
     404             : }
     405             : 
     406        1595 : void GDALRegister_BLX()
     407             : 
     408             : {
     409        1595 :     if (GDALGetDriverByName("BLX") != nullptr)
     410         302 :         return;
     411             : 
     412        1293 :     GDALDriver *poDriver = new GDALDriver();
     413             : 
     414        1293 :     poDriver->SetDescription("BLX");
     415        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
     416        1293 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Magellan topo (.blx)");
     417        1293 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/blx.html");
     418        1293 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "blx");
     419             : 
     420        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
     421             : 
     422        1293 :     poDriver->pfnOpen = BLXDataset::Open;
     423        1293 :     poDriver->pfnCreateCopy = BLXCreateCopy;
     424             : 
     425        1293 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     426             : }

Generated by: LCOV version 1.14