Coverage Report

Created: 2026-08-14 09:29

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