LCOV - code coverage report
Current view: top level - apps - gdalalg_raster_reclassify.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 76 79 96.2 %
Date: 2026-08-22 15:37:05 Functions: 4 4 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  "reclassify" step of "raster pipeline"
       5             :  * Author:   Daniel Baston
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2025, ISciences LLC
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdalalg_raster_reclassify.h"
      14             : 
      15             : #include "cpl_vsi_virtual.h"
      16             : #include "gdal_priv.h"
      17             : #include "gdal_utils.h"
      18             : #include "../frmts/vrt/vrtdataset.h"
      19             : #include "../frmts/vrt/vrtreclassifier.h"
      20             : 
      21             : #include <array>
      22             : 
      23             : //! @cond Doxygen_Suppress
      24             : 
      25             : #ifndef _
      26             : #define _(x) (x)
      27             : #endif
      28             : 
      29             : /************************************************************************/
      30             : /*    GDALRasterReclassifyAlgorithm::GDALRasterReclassifyAlgorithm()    */
      31             : /************************************************************************/
      32             : 
      33          97 : GDALRasterReclassifyAlgorithm::GDALRasterReclassifyAlgorithm(
      34          97 :     bool standaloneStep)
      35             :     : GDALRasterPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
      36          97 :                                       standaloneStep)
      37             : {
      38             :     AddArg("mapping", 'm',
      39             :            _("Reclassification mappings (or specify a @<filename> to point to "
      40             :              "a file containing mappings"),
      41         194 :            &m_mapping)
      42          97 :         .SetRequired();
      43             :     AddArg("keep-color-table", 0, _("Preserve the input color table"),
      44          97 :            &m_keepColorTable);
      45          97 :     AddOutputDataTypeArg(&m_type);
      46          97 : }
      47             : 
      48             : /************************************************************************/
      49             : /*                 GDALRasterReclassifyValidateMappings                 */
      50             : /************************************************************************/
      51             : 
      52          12 : static bool GDALReclassifyValidateMappings(GDALDataset &input,
      53             :                                            const std::string &mappings,
      54             :                                            GDALDataType eDstType)
      55             : {
      56             :     int hasNoData;
      57             :     std::optional<double> noData =
      58          12 :         input.GetRasterBand(1)->GetNoDataValue(&hasNoData);
      59          12 :     if (!hasNoData)
      60             :     {
      61           8 :         noData.reset();
      62             :     }
      63             : 
      64          12 :     gdal::Reclassifier reclassifier;
      65          24 :     return reclassifier.Init(mappings.c_str(), noData, eDstType) == CE_None;
      66             : }
      67             : 
      68             : /************************************************************************/
      69             : /*                 GDALRasterReclassifyCreateVRTDerived                 */
      70             : /************************************************************************/
      71             : 
      72             : static std::unique_ptr<GDALDataset>
      73          11 : GDALReclassifyCreateVRTDerived(GDALDataset &input, const std::string &mappings,
      74             :                                GDALDataType eDstType, bool keepColorTable)
      75             : {
      76          11 :     const auto nX = input.GetRasterXSize();
      77          11 :     const auto nY = input.GetRasterYSize();
      78             : 
      79          22 :     auto poDS = VRTDataset::CreateVRTDataset("", nX, nY, 0, eDstType, nullptr);
      80             : 
      81          11 :     GDALGeoTransform gt;
      82          11 :     if (input.GetGeoTransform(gt) == CE_None)
      83           7 :         poDS->SetGeoTransform(gt);
      84          11 :     poDS->SetSpatialRef(input.GetSpatialRef());
      85             : 
      86          22 :     CPLStringList papszBandArgs;
      87          11 :     papszBandArgs.SetNameValue("subclass", "VRTDerivedRasterBand");
      88             : 
      89          24 :     for (int iBand = 1; iBand <= input.GetRasterCount(); ++iBand)
      90             :     {
      91          13 :         GDALRasterBand *poSrcBand = input.GetRasterBand(iBand);
      92          13 :         const GDALDataType srcType = poSrcBand->GetRasterDataType();
      93          13 :         const GDALDataType bandType =
      94          13 :             eDstType == GDT_Unknown ? srcType : eDstType;
      95          13 :         const GDALDataType xferType = GDALDataTypeUnion(srcType, bandType);
      96             : 
      97          13 :         if (poDS->AddBand(bandType, papszBandArgs) != CE_None)
      98             :         {
      99           0 :             return nullptr;
     100             :         }
     101             : 
     102             :         VRTDerivedRasterBand *poDstBand =
     103          13 :             cpl::down_cast<VRTDerivedRasterBand *>(poDS->GetRasterBand(iBand));
     104          13 :         poDstBand->SetSourceTransferType(xferType);
     105          13 :         poDstBand->SetPixelFunctionName("reclassify");
     106          13 :         poDstBand->AddPixelFunctionArgument("mapping", mappings.c_str());
     107             : 
     108          13 :         if (keepColorTable && poSrcBand->GetColorTable() != nullptr)
     109             :         {
     110           1 :             poDstBand->SetColorTable(poSrcBand->GetColorTable());
     111             :         }
     112             : 
     113          13 :         GDALCopyNoDataValue(poDstBand, poSrcBand);
     114          13 :         poDstBand->AddSimpleSource(poSrcBand);
     115             :     }
     116             : 
     117          11 :     return poDS;
     118             : }
     119             : 
     120             : /************************************************************************/
     121             : /*               GDALRasterReclassifyAlgorithm::RunStep()               */
     122             : /************************************************************************/
     123             : 
     124          15 : bool GDALRasterReclassifyAlgorithm::RunStep(GDALPipelineStepRunContext &)
     125             : {
     126          15 :     const auto poSrcDS = m_inputDataset[0].GetDatasetRef();
     127          15 :     CPLAssert(poSrcDS);
     128          15 :     CPLAssert(m_outputDataset.GetName().empty());
     129          15 :     CPLAssert(!m_outputDataset.GetDatasetRef());
     130             : 
     131             :     // Already validated by argument parser
     132             :     const GDALDataType eDstType =
     133          15 :         m_type.empty() ? GDT_Unknown : GDALGetDataTypeByName(m_type.c_str());
     134             : 
     135          15 :     const auto nErrorCount = CPLGetErrorCounter();
     136          15 :     if (!m_mapping.empty() && m_mapping[0] == '@')
     137             :     {
     138             :         auto f =
     139           6 :             VSIVirtualHandleUniquePtr(VSIFOpenL(m_mapping.c_str() + 1, "r"));
     140           6 :         if (!f)
     141             :         {
     142           1 :             ReportError(CE_Failure, CPLE_FileIO, "Cannot open %s",
     143           1 :                         m_mapping.c_str() + 1);
     144           1 :             return false;
     145             :         }
     146             : 
     147           5 :         m_mapping.clear();
     148             :         try
     149             :         {
     150           5 :             constexpr int MAX_CHARS_PER_LINE = 1000 * 1000;
     151           5 :             constexpr size_t MAX_MAPPING_SIZE = 10 * 1000 * 1000;
     152             :             while (const char *line =
     153        1034 :                        CPLReadLine2L(f.get(), MAX_CHARS_PER_LINE, nullptr))
     154             :             {
     155        1200 :                 while (isspace(*line))
     156             :                 {
     157         170 :                     line++;
     158             :                 }
     159             : 
     160        1030 :                 if (line[0])
     161             :                 {
     162        1024 :                     if (!m_mapping.empty())
     163             :                     {
     164        1018 :                         m_mapping.append(";");
     165             :                     }
     166             : 
     167        1024 :                     const char *comment = strchr(line, '#');
     168        1024 :                     if (!comment)
     169             :                     {
     170        1018 :                         m_mapping.append(line);
     171             :                     }
     172             :                     else
     173             :                     {
     174             :                         m_mapping.append(line,
     175           6 :                                          static_cast<size_t>(comment - line));
     176             :                     }
     177        1024 :                     if (m_mapping.size() > MAX_MAPPING_SIZE)
     178             :                     {
     179           1 :                         ReportError(CE_Failure, CPLE_AppDefined,
     180             :                                     "Too large mapping size");
     181           1 :                         return false;
     182             :                     }
     183             :                 }
     184        1029 :             }
     185             :         }
     186           0 :         catch (const std::exception &)
     187             :         {
     188           0 :             ReportError(CE_Failure, CPLE_OutOfMemory,
     189             :                         "Out of memory while ingesting mapping file");
     190             :         }
     191             :     }
     192          13 :     if (nErrorCount == CPLGetErrorCounter())
     193             :     {
     194          12 :         if (!GDALReclassifyValidateMappings(*poSrcDS, m_mapping, eDstType))
     195             :         {
     196           1 :             return false;
     197             :         }
     198             : 
     199          11 :         m_outputDataset.Set(GDALReclassifyCreateVRTDerived(
     200          11 :             *poSrcDS, m_mapping, eDstType, m_keepColorTable));
     201             :     }
     202          12 :     return m_outputDataset.GetDatasetRef() != nullptr;
     203             : }
     204             : 
     205             : GDALRasterReclassifyAlgorithmStandalone::
     206             :     ~GDALRasterReclassifyAlgorithmStandalone() = default;
     207             : 
     208             : //! @endcond

Generated by: LCOV version 1.14