LCOV - code coverage report
Current view: top level - frmts/pcraster - pcrasterdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 145 182 79.7 %
Date: 2026-02-04 02:44:53 Functions: 12 13 92.3 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  PCRaster Integration
       4             :  * Purpose:  PCRaster CSF 2.0 raster file driver
       5             :  * Author:   Kor de Jong, Oliver Schmitz
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) PCRaster owners
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_string.h"
      14             : #include "gdal_pam.h"
      15             : 
      16             : #include "pcrasterrasterband.h"
      17             : #include "pcrasterdataset.h"
      18             : #include "pcrasterutil.h"
      19             : #include "pcrasterdrivercore.h"
      20             : 
      21             : /*!
      22             :   \file
      23             :   This file contains the implementation of the PCRasterDataset class.
      24             : */
      25             : 
      26             : //------------------------------------------------------------------------------
      27             : // DEFINITION OF STATIC PCRDATASET MEMBERS
      28             : //------------------------------------------------------------------------------
      29             : 
      30             : //! Tries to open the file described by \a info.
      31             : /*!
      32             :   \param     info Object with information about the dataset to open.
      33             :   \return    Pointer to newly allocated GDALDataset or 0.
      34             : 
      35             :   Returns a nullptr if the file could not be opened.
      36             : */
      37          13 : GDALDataset *PCRasterDataset::open(GDALOpenInfo *info)
      38             : {
      39          13 :     PCRasterDataset *dataset = nullptr;
      40             : 
      41          13 :     if (PCRasterDriverIdentify(info))
      42             :     {
      43          13 :         MOPEN_PERM mode = info->eAccess == GA_Update ? M_READ_WRITE : M_READ;
      44             : 
      45          13 :         MAP *map = mapOpen(info->pszFilename, mode);
      46             : 
      47          13 :         if (map)
      48             :         {
      49          13 :             CPLErrorReset();
      50          13 :             dataset = new PCRasterDataset(map, info->eAccess);
      51          13 :             if (CPLGetLastErrorType() != CE_None)
      52             :             {
      53           0 :                 delete dataset;
      54           0 :                 return nullptr;
      55             :             }
      56             :         }
      57             :     }
      58             : 
      59             :     /* -------------------------------------------------------------------- */
      60             :     /*      Initialize any PAM information and overviews.                   */
      61             :     /* -------------------------------------------------------------------- */
      62          13 :     if (dataset)
      63             :     {
      64          13 :         dataset->SetDescription(info->pszFilename);
      65          13 :         dataset->TryLoadXML();
      66             : 
      67          13 :         dataset->oOvManager.Initialize(dataset, info->pszFilename);
      68             :     }
      69             : 
      70          13 :     return dataset;
      71             : }
      72             : 
      73             : //! Writes a raster to \a filename as a PCRaster raster file.
      74             : /*!
      75             :   \warning   The source raster must have only 1 band. Currently, the values in
      76             :              the source raster must be stored in one of the supported cell
      77             :              representations (CR_UINT1, CR_INT4, CR_REAL4, CR_REAL8).
      78             : 
      79             :   The meta data item PCRASTER_VALUESCALE will be checked to see what value
      80             :   scale to use. Otherwise a value scale is determined using
      81             :   GDALType2ValueScale(GDALDataType).
      82             : 
      83             :   This function always writes rasters using CR_UINT1, CR_INT4 or CR_REAL4
      84             :   cell representations.
      85             : */
      86          20 : GDALDataset *PCRasterDataset::createCopy(char const *filename,
      87             :                                          GDALDataset *source,
      88             :                                          CPL_UNUSED int strict, CSLConstList,
      89             :                                          GDALProgressFunc progress,
      90             :                                          void *progressData)
      91             : {
      92             :     // Checks.
      93          20 :     const int nrBands = source->GetRasterCount();
      94          20 :     if (nrBands != 1)
      95             :     {
      96           5 :         CPLError(CE_Failure, CPLE_NotSupported,
      97             :                  "PCRaster driver: Too many bands ('%d'): must be 1 band",
      98             :                  nrBands);
      99           5 :         return nullptr;
     100             :     }
     101             : 
     102          15 :     GDALRasterBand *raster = source->GetRasterBand(1);
     103             : 
     104             :     // Create PCRaster raster. Determine properties of raster to create.
     105             : 
     106             :     // The in-file type of the cells.
     107             :     CSF_CR fileCellRepresentation =
     108          15 :         GDALType2CellRepresentation(raster->GetRasterDataType(), false);
     109             : 
     110          15 :     if (fileCellRepresentation == CR_UNDEFINED)
     111             :     {
     112           4 :         CPLError(
     113             :             CE_Failure, CPLE_NotSupported,
     114             :             "PCRaster driver: Cannot determine a valid cell representation");
     115           4 :         return nullptr;
     116             :     }
     117             : 
     118             :     // The value scale of the values.
     119          11 :     CSF_VS valueScale = VS_UNDEFINED;
     120          22 :     std::string osString;
     121          11 :     if (source->GetMetadataItem("PCRASTER_VALUESCALE"))
     122             :     {
     123           1 :         osString = source->GetMetadataItem("PCRASTER_VALUESCALE");
     124             :     }
     125             : 
     126          11 :     valueScale = !osString.empty()
     127          11 :                      ? string2ValueScale(osString)
     128          10 :                      : GDALType2ValueScale(raster->GetRasterDataType());
     129             : 
     130          11 :     if (valueScale == VS_UNDEFINED)
     131             :     {
     132           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     133             :                  "PCRaster driver: Cannot determine a valid value scale");
     134           0 :         return nullptr;
     135             :     }
     136             : 
     137          11 :     CSF_PT const projection = PT_YDECT2B;
     138          11 :     const REAL8 angle = 0.0;
     139          11 :     REAL8 west = 0.0;
     140          11 :     REAL8 north = 0.0;
     141          11 :     REAL8 cellSize = 1.0;
     142             : 
     143          11 :     GDALGeoTransform gt;
     144          11 :     if (source->GetGeoTransform(gt) == CE_None)
     145             :     {
     146          11 :         if (gt[2] == 0.0 && gt[4] == 0.0)
     147             :         {
     148          11 :             west = static_cast<REAL8>(gt[0]);
     149          11 :             north = static_cast<REAL8>(gt[3]);
     150          11 :             cellSize = static_cast<REAL8>(gt[1]);
     151             :         }
     152             :     }
     153             : 
     154             :     // The in-memory type of the cells.
     155             :     CSF_CR appCellRepresentation =
     156          11 :         GDALType2CellRepresentation(raster->GetRasterDataType(), true);
     157             : 
     158          11 :     if (appCellRepresentation == CR_UNDEFINED)
     159             :     {
     160           0 :         CPLError(
     161             :             CE_Failure, CPLE_NotSupported,
     162             :             "PCRaster driver: Cannot determine a valid cell representation");
     163           0 :         return nullptr;
     164             :     }
     165             : 
     166             :     // Check whether value scale fits the cell representation. Adjust when
     167             :     // needed.
     168          11 :     valueScale = fitValueScale(valueScale, appCellRepresentation);
     169             : 
     170             :     // Create a raster with the in file cell representation.
     171          11 :     const size_t nrRows = raster->GetYSize();
     172          11 :     const size_t nrCols = raster->GetXSize();
     173          11 :     MAP *map = Rcreate(filename, nrRows, nrCols, fileCellRepresentation,
     174             :                        valueScale, projection, west, north, angle, cellSize);
     175             : 
     176          11 :     if (!map)
     177             :     {
     178           3 :         CPLError(CE_Failure, CPLE_OpenFailed,
     179             :                  "PCRaster driver: Unable to create raster %s", filename);
     180           3 :         return nullptr;
     181             :     }
     182             : 
     183             :     // Try to convert in app cell representation to the cell representation
     184             :     // of the file.
     185           8 :     if (RuseAs(map, appCellRepresentation))
     186             :     {
     187           3 :         CPLError(CE_Failure, CPLE_NotSupported,
     188             :                  "PCRaster driver: Cannot convert cells: %s", MstrError());
     189           3 :         Mclose(map);
     190           3 :         return nullptr;
     191             :     }
     192             : 
     193             :     int hasMissingValue;
     194           5 :     double missingValue = raster->GetNoDataValue(&hasMissingValue);
     195             : 
     196             :     // This is needed to get my (KDJ) unit tests running.
     197             :     // I am still uncertain why this is needed. If the input raster has float32
     198             :     // values and the output int32, than the missing value in the dataset object
     199             :     // is not updated like the values are.
     200           5 :     if (missingValue == ::missingValue(CR_REAL4) &&
     201             :         fileCellRepresentation == CR_INT4)
     202             :     {
     203           0 :         missingValue = ::missingValue(fileCellRepresentation);
     204             :     }
     205             : 
     206             :     // TODO: Proper translation of TODO.
     207             :     // TODO: Support conversion to INT2 (?) INT4. ruseas.c see line 503.
     208             :     // Conversion r 159.
     209             : 
     210             :     // Create buffer for one row of values.
     211           5 :     void *buffer = Rmalloc(map, nrCols);
     212             : 
     213             :     // Copy values from source to target.
     214           5 :     CPLErr errorCode = CE_None;
     215         145 :     for (size_t row = 0; errorCode == CE_None && row < nrRows; ++row)
     216             :     {
     217             : 
     218             :         // Get row from source.
     219         140 :         if (raster->RasterIO(
     220             :                 GF_Read, 0, static_cast<int>(row), static_cast<int>(nrCols), 1,
     221             :                 buffer, static_cast<int>(nrCols), 1,
     222         140 :                 raster->GetRasterDataType(), 0, 0, nullptr) != CE_None)
     223             :         {
     224           0 :             CPLError(CE_Failure, CPLE_FileIO,
     225             :                      "PCRaster driver: Error reading from source raster");
     226           0 :             errorCode = CE_Failure;
     227           0 :             break;
     228             :         }
     229             : 
     230             :         // Upon reading values are converted to the
     231             :         // right data type. This includes the missing value. If the source
     232             :         // value cannot be represented in the target data type it is set to a
     233             :         // missing value.
     234             : 
     235         140 :         if (hasMissingValue)
     236             :         {
     237         100 :             alterToStdMV(buffer, nrCols, appCellRepresentation, missingValue);
     238             :         }
     239             : 
     240         140 :         if (valueScale == VS_BOOLEAN)
     241             :         {
     242          10 :             castValuesToBooleanRange(buffer, nrCols, appCellRepresentation);
     243             :         }
     244             : 
     245             :         // Write row in target.
     246         140 :         RputRow(map, row, buffer);
     247             : 
     248         140 :         if (!progress((row + 1) / (static_cast<double>(nrRows)), nullptr,
     249             :                       progressData))
     250             :         {
     251           0 :             CPLError(CE_Failure, CPLE_UserInterrupt,
     252             :                      "PCRaster driver: User terminated CreateCopy()");
     253           0 :             errorCode = CE_Failure;
     254           0 :             break;
     255             :         }
     256             :     }
     257             : 
     258           5 :     Mclose(map);
     259           5 :     map = nullptr;
     260             : 
     261           5 :     free(buffer);
     262           5 :     buffer = nullptr;
     263             : 
     264           5 :     if (errorCode != CE_None)
     265           0 :         return nullptr;
     266             : 
     267             :     /* -------------------------------------------------------------------- */
     268             :     /*      Re-open dataset, and copy any auxiliary pam information.        */
     269             :     /* -------------------------------------------------------------------- */
     270           5 :     GDALPamDataset *poDS = cpl::down_cast<GDALPamDataset *>(
     271             :         GDALDataset::Open(filename, GDAL_OF_RASTER | GDAL_OF_UPDATE));
     272             : 
     273           5 :     if (poDS)
     274           5 :         poDS->CloneInfo(source, GCIF_PAM_DEFAULT);
     275             : 
     276           5 :     return poDS;
     277             : }
     278             : 
     279             : //------------------------------------------------------------------------------
     280             : // DEFINITION OF PCRDATASET MEMBERS
     281             : //------------------------------------------------------------------------------
     282             : 
     283             : //! Constructor.
     284             : /*!
     285             :   \param     mapIn PCRaster map handle. It is ours to close.
     286             : */
     287          13 : PCRasterDataset::PCRasterDataset(MAP *mapIn, GDALAccess eAccessIn)
     288             :     : GDALPamDataset(), d_map(mapIn), d_west(0.0), d_north(0.0),
     289             :       d_cellSize(0.0), d_cellRepresentation(CR_UNDEFINED),
     290             :       d_valueScale(VS_UNDEFINED), d_defaultNoDataValue(0.0),
     291          13 :       d_location_changed(false)
     292             : {
     293             :     // Read header info.
     294          13 :     eAccess = eAccessIn;
     295          13 :     nRasterXSize = static_cast<int>(RgetNrCols(d_map));
     296          13 :     nRasterYSize = static_cast<int>(RgetNrRows(d_map));
     297          13 :     if (!GDALCheckDatasetDimensions(nRasterXSize, nRasterYSize))
     298             :     {
     299           0 :         return;
     300             :     }
     301          13 :     d_west = static_cast<double>(RgetXUL(d_map));
     302          13 :     d_north = static_cast<double>(RgetYUL(d_map));
     303          13 :     d_cellSize = static_cast<double>(RgetCellSize(d_map));
     304          13 :     d_cellRepresentation = RgetUseCellRepr(d_map);
     305          13 :     if (d_cellRepresentation == CR_UNDEFINED)
     306             :     {
     307           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     308             :                  "d_cellRepresentation != CR_UNDEFINED");
     309             :     }
     310          13 :     d_valueScale = RgetValueScale(d_map);
     311          13 :     if (d_valueScale == VS_UNDEFINED)
     312             :     {
     313           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     314             :                  "d_valueScale != VS_UNDEFINED");
     315             :     }
     316          13 :     d_defaultNoDataValue = ::missingValue(d_cellRepresentation);
     317             : 
     318             :     // Create band information objects.
     319          13 :     nBands = 1;
     320          13 :     SetBand(1, new PCRasterRasterBand(this));
     321             : 
     322          13 :     SetMetadataItem("PCRASTER_VALUESCALE",
     323          26 :                     valueScale2String(d_valueScale).c_str());
     324             : }
     325             : 
     326             : //! Destructor.
     327             : /*!
     328             :   \warning   The map given in the constructor is closed.
     329             : */
     330          26 : PCRasterDataset::~PCRasterDataset()
     331             : {
     332          13 :     FlushCache(true);
     333          13 :     Mclose(d_map);
     334          26 : }
     335             : 
     336             : //! Sets projections info.
     337             : /*!
     338             :   \param     gt Array to fill.
     339             : 
     340             :   CSF 2.0 supports the notion of y coordinates which increase from north to
     341             :   south. Support for this has been dropped and applications reading PCRaster
     342             :   rasters will treat or already treat y coordinates as increasing from south
     343             :   to north only.
     344             : */
     345           9 : CPLErr PCRasterDataset::GetGeoTransform(GDALGeoTransform &gt) const
     346             : {
     347             :     // x = west + nrCols * cellsize
     348           9 :     gt[0] = d_west;
     349           9 :     gt[1] = d_cellSize;
     350           9 :     gt[2] = 0.0;
     351             : 
     352             :     // y = north + nrRows * -cellsize
     353           9 :     gt[3] = d_north;
     354           9 :     gt[4] = 0.0;
     355           9 :     gt[5] = -1.0 * d_cellSize;
     356             : 
     357           9 :     return CE_None;
     358             : }
     359             : 
     360             : //! Returns the map handle.
     361             : /*!
     362             :   \return    Map handle.
     363             : */
     364         480 : MAP *PCRasterDataset::map() const
     365             : {
     366         480 :     return d_map;
     367             : }
     368             : 
     369             : //! Returns the in-app cell representation.
     370             : /*!
     371             :   \return    cell representation
     372             :   \warning   This might not be the same representation as use to store the
     373             :   values in the file. \sa        valueScale()
     374             : */
     375         433 : CSF_CR PCRasterDataset::cellRepresentation() const
     376             : {
     377         433 :     return d_cellRepresentation;
     378             : }
     379             : 
     380             : //! Returns the value scale of the data.
     381             : /*!
     382             :   \return    Value scale
     383             :   \sa        cellRepresentation()
     384             : */
     385          20 : CSF_VS PCRasterDataset::valueScale() const
     386             : {
     387          20 :     return d_valueScale;
     388             : }
     389             : 
     390             : //! Returns the value of the missing value.
     391             : /*!
     392             :   \return    Missing value
     393             : */
     394         457 : double PCRasterDataset::defaultNoDataValue() const
     395             : {
     396         457 :     return d_defaultNoDataValue;
     397             : }
     398             : 
     399          33 : GDALDataset *PCRasterDataset::create(const char *filename, int nr_cols,
     400             :                                      int nr_rows, int nrBands,
     401             :                                      GDALDataType gdalType,
     402             :                                      CSLConstList papszParamList)
     403             : {
     404             :     // Checks
     405          33 :     if (nrBands != 1)
     406             :     {
     407          18 :         CPLError(CE_Failure, CPLE_NotSupported,
     408             :                  "PCRaster driver : "
     409             :                  "attempt to create dataset with too many bands (%d); "
     410             :                  "must be 1 band.\n",
     411             :                  nrBands);
     412          18 :         return nullptr;
     413             :     }
     414             : 
     415          15 :     const int row_col_max = INT4_MAX - 1;
     416          15 :     if (nr_cols > row_col_max)
     417             :     {
     418           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     419             :                  "PCRaster driver : "
     420             :                  "attempt to create dataset with too many columns (%d); "
     421             :                  "must be smaller than %d.",
     422             :                  nr_cols, row_col_max);
     423           0 :         return nullptr;
     424             :     }
     425             : 
     426          15 :     if (nr_rows > row_col_max)
     427             :     {
     428           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     429             :                  "PCRaster driver : "
     430             :                  "attempt to create dataset with too many rows (%d); "
     431             :                  "must be smaller than %d.",
     432             :                  nr_rows, row_col_max);
     433           0 :         return nullptr;
     434             :     }
     435             : 
     436          15 :     if (gdalType != GDT_UInt8 && gdalType != GDT_Int32 &&
     437             :         gdalType != GDT_Float32)
     438             :     {
     439          11 :         CPLError(CE_Failure, CPLE_AppDefined,
     440             :                  "PCRaster driver: "
     441             :                  "attempt to create dataset with an illegal data type (%s); "
     442             :                  "use either Byte, Int32 or Float32.",
     443             :                  GDALGetDataTypeName(gdalType));
     444          11 :         return nullptr;
     445             :     }
     446             : 
     447             :     // value scale must be specified by the user,
     448             :     // determines cell representation
     449             :     const char *valueScale =
     450           4 :         CSLFetchNameValue(papszParamList, "PCRASTER_VALUESCALE");
     451             : 
     452           4 :     if (valueScale == nullptr)
     453             :     {
     454           3 :         CPLError(CE_Failure, CPLE_AppDefined,
     455             :                  "PCRaster driver: value scale can not be determined; "
     456             :                  "specify PCRASTER_VALUESCALE.");
     457           3 :         return nullptr;
     458             :     }
     459             : 
     460           1 :     CSF_VS csf_value_scale = string2ValueScale(valueScale);
     461             : 
     462           1 :     if (csf_value_scale == VS_UNDEFINED)
     463             :     {
     464           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     465             :                  "PCRaster driver: value scale can not be determined (%s); "
     466             :                  "use either VS_BOOLEAN, VS_NOMINAL, VS_ORDINAL, VS_SCALAR, "
     467             :                  "VS_DIRECTION, VS_LDD",
     468             :                  valueScale);
     469           0 :         return nullptr;
     470             :     }
     471             : 
     472             :     CSF_CR csf_cell_representation =
     473           1 :         GDALType2CellRepresentation(gdalType, false);
     474             : 
     475             :     // default values
     476           1 :     REAL8 west = 0.0;
     477           1 :     REAL8 north = 0.0;
     478           1 :     REAL8 length = 1.0;
     479           1 :     REAL8 angle = 0.0;
     480           1 :     CSF_PT projection = PT_YDECT2B;
     481             : 
     482             :     // Create a new raster
     483           1 :     MAP *map = Rcreate(filename, nr_rows, nr_cols, csf_cell_representation,
     484             :                        csf_value_scale, projection, west, north, angle, length);
     485             : 
     486           1 :     if (!map)
     487             :     {
     488           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     489             :                  "PCRaster driver: Unable to create raster %s", filename);
     490           0 :         return nullptr;
     491             :     }
     492             : 
     493           1 :     Mclose(map);
     494           1 :     map = nullptr;
     495             : 
     496           1 :     return GDALDataset::Open(filename, GDAL_OF_RASTER | GDAL_OF_UPDATE);
     497             : }
     498             : 
     499           0 : CPLErr PCRasterDataset::SetGeoTransform(const GDALGeoTransform &gt)
     500             : {
     501           0 :     if ((gt[2] != 0.0) || (gt[4] != 0.0))
     502             :     {
     503           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     504             :                  "PCRaster driver: "
     505             :                  "rotated geogtations are not supported.");
     506           0 :         return CE_Failure;
     507             :     }
     508             : 
     509           0 :     if (gt[1] != gt[5] * -1.0)
     510             :     {
     511           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     512             :                  "PCRaster driver: "
     513             :                  "only the same width and height for cells is supported.");
     514           0 :         return CE_Failure;
     515             :     }
     516             : 
     517           0 :     d_west = gt[0];
     518           0 :     d_north = gt[3];
     519           0 :     d_cellSize = gt[1];
     520           0 :     d_location_changed = true;
     521             : 
     522           0 :     return CE_None;
     523             : }
     524             : 
     525          20 : bool PCRasterDataset::location_changed() const
     526             : {
     527          20 :     return d_location_changed;
     528             : }

Generated by: LCOV version 1.14