Coverage Report

Created: 2026-02-14 09:00

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
    AddOutputDataTypeArg(&m_type);
44
0
}
45
46
/************************************************************************/
47
/*                 GDALRasterReclassifyValidateMappings                 */
48
/************************************************************************/
49
50
static bool GDALReclassifyValidateMappings(GDALDataset &input,
51
                                           const std::string &mappings,
52
                                           GDALDataType eDstType)
53
0
{
54
0
    int hasNoData;
55
0
    std::optional<double> noData =
56
0
        input.GetRasterBand(1)->GetNoDataValue(&hasNoData);
57
0
    if (!hasNoData)
58
0
    {
59
0
        noData.reset();
60
0
    }
61
62
0
    gdal::Reclassifier reclassifier;
63
0
    return reclassifier.Init(mappings.c_str(), noData, eDstType) == CE_None;
64
0
}
65
66
/************************************************************************/
67
/*                 GDALRasterReclassifyCreateVRTDerived                 */
68
/************************************************************************/
69
70
static std::unique_ptr<GDALDataset>
71
GDALReclassifyCreateVRTDerived(GDALDataset &input, const std::string &mappings,
72
                               GDALDataType eDstType)
73
0
{
74
0
    CPLXMLTreeCloser root(CPLCreateXMLNode(nullptr, CXT_Element, "VRTDataset"));
75
76
0
    const auto nX = input.GetRasterXSize();
77
0
    const auto nY = input.GetRasterYSize();
78
79
0
    for (int iBand = 1; iBand <= input.GetRasterCount(); ++iBand)
80
0
    {
81
0
        const GDALDataType srcType =
82
0
            input.GetRasterBand(iBand)->GetRasterDataType();
83
0
        const GDALDataType bandType =
84
0
            eDstType == GDT_Unknown ? srcType : eDstType;
85
0
        const GDALDataType xferType = GDALDataTypeUnion(srcType, bandType);
86
87
0
        CPLXMLNode *band =
88
0
            CPLCreateXMLNode(root.get(), CXT_Element, "VRTRasterBand");
89
0
        CPLAddXMLAttributeAndValue(band, "subClass", "VRTDerivedRasterBand");
90
0
        CPLAddXMLAttributeAndValue(band, "dataType",
91
0
                                   GDALGetDataTypeName(bandType));
92
93
0
        CPLXMLNode *pixelFunctionType =
94
0
            CPLCreateXMLNode(band, CXT_Element, "PixelFunctionType");
95
0
        CPLCreateXMLNode(pixelFunctionType, CXT_Text, "reclassify");
96
0
        CPLXMLNode *arguments =
97
0
            CPLCreateXMLNode(band, CXT_Element, "PixelFunctionArguments");
98
0
        CPLAddXMLAttributeAndValue(arguments, "mapping", mappings.c_str());
99
100
0
        CPLXMLNode *sourceTransferType =
101
0
            CPLCreateXMLNode(band, CXT_Element, "SourceTransferType");
102
0
        CPLCreateXMLNode(sourceTransferType, CXT_Text,
103
0
                         GDALGetDataTypeName(xferType));
104
0
    }
105
106
0
    auto ds = std::make_unique<VRTDataset>(nX, nY);
107
0
    if (ds->XMLInit(root.get(), "") != CE_None)
108
0
    {
109
0
        return nullptr;
110
0
    };
111
112
0
    for (int iBand = 1; iBand <= input.GetRasterCount(); ++iBand)
113
0
    {
114
0
        auto poSrcBand = input.GetRasterBand(iBand);
115
0
        auto poDstBand =
116
0
            cpl::down_cast<VRTDerivedRasterBand *>(ds->GetRasterBand(iBand));
117
0
        GDALCopyNoDataValue(poDstBand, poSrcBand);
118
0
        poDstBand->AddSimpleSource(poSrcBand);
119
0
    }
120
121
0
    GDALGeoTransform gt;
122
0
    if (input.GetGeoTransform(gt) == CE_None)
123
0
        ds->SetGeoTransform(gt);
124
0
    ds->SetSpatialRef(input.GetSpatialRef());
125
126
0
    return ds;
127
0
}
128
129
/************************************************************************/
130
/*               GDALRasterReclassifyAlgorithm::RunStep()               */
131
/************************************************************************/
132
133
bool GDALRasterReclassifyAlgorithm::RunStep(GDALPipelineStepRunContext &)
134
0
{
135
0
    const auto poSrcDS = m_inputDataset[0].GetDatasetRef();
136
0
    CPLAssert(poSrcDS);
137
0
    CPLAssert(m_outputDataset.GetName().empty());
138
0
    CPLAssert(!m_outputDataset.GetDatasetRef());
139
140
    // Already validated by argument parser
141
0
    const GDALDataType eDstType =
142
0
        m_type.empty() ? GDT_Unknown : GDALGetDataTypeByName(m_type.c_str());
143
144
0
    const auto nErrorCount = CPLGetErrorCounter();
145
0
    if (!m_mapping.empty() && m_mapping[0] == '@')
146
0
    {
147
0
        auto f =
148
0
            VSIVirtualHandleUniquePtr(VSIFOpenL(m_mapping.c_str() + 1, "r"));
149
0
        if (!f)
150
0
        {
151
0
            ReportError(CE_Failure, CPLE_FileIO, "Cannot open %s",
152
0
                        m_mapping.c_str() + 1);
153
0
            return false;
154
0
        }
155
156
0
        m_mapping.clear();
157
0
        try
158
0
        {
159
0
            constexpr int MAX_CHARS_PER_LINE = 1000 * 1000;
160
0
            constexpr size_t MAX_MAPPING_SIZE = 10 * 1000 * 1000;
161
0
            while (const char *line =
162
0
                       CPLReadLine2L(f.get(), MAX_CHARS_PER_LINE, nullptr))
163
0
            {
164
0
                while (isspace(*line))
165
0
                {
166
0
                    line++;
167
0
                }
168
169
0
                if (line[0])
170
0
                {
171
0
                    if (!m_mapping.empty())
172
0
                    {
173
0
                        m_mapping.append(";");
174
0
                    }
175
176
0
                    const char *comment = strchr(line, '#');
177
0
                    if (!comment)
178
0
                    {
179
0
                        m_mapping.append(line);
180
0
                    }
181
0
                    else
182
0
                    {
183
0
                        m_mapping.append(line,
184
0
                                         static_cast<size_t>(comment - line));
185
0
                    }
186
0
                    if (m_mapping.size() > MAX_MAPPING_SIZE)
187
0
                    {
188
0
                        ReportError(CE_Failure, CPLE_AppDefined,
189
0
                                    "Too large mapping size");
190
0
                        return false;
191
0
                    }
192
0
                }
193
0
            }
194
0
        }
195
0
        catch (const std::exception &)
196
0
        {
197
0
            ReportError(CE_Failure, CPLE_OutOfMemory,
198
0
                        "Out of memory while ingesting mapping file");
199
0
        }
200
0
    }
201
0
    if (nErrorCount == CPLGetErrorCounter())
202
0
    {
203
0
        if (!GDALReclassifyValidateMappings(*poSrcDS, m_mapping, eDstType))
204
0
        {
205
0
            return false;
206
0
        }
207
208
0
        m_outputDataset.Set(
209
0
            GDALReclassifyCreateVRTDerived(*poSrcDS, m_mapping, eDstType));
210
0
    }
211
0
    return m_outputDataset.GetDatasetRef() != nullptr;
212
0
}
213
214
GDALRasterReclassifyAlgorithmStandalone::
215
0
    ~GDALRasterReclassifyAlgorithmStandalone() = default;
216
217
//! @endcond