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

Generated by: LCOV version 1.14