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

Generated by: LCOV version 1.14