/src/gdal/frmts/vrt/vrtreclassifier.h
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: Virtual GDAL Datasets |
4 | | * Purpose: Implementation of Reclassifier |
5 | | * Author: Daniel Baston |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2025, ISciences LLC |
9 | | * |
10 | | * SPDX-License-Identifier: MIT |
11 | | ****************************************************************************/ |
12 | | |
13 | | #pragma once |
14 | | |
15 | | #include "gdal.h" |
16 | | #include "cpl_error.h" |
17 | | |
18 | | #include <map> |
19 | | #include <optional> |
20 | | #include <utility> |
21 | | #include <vector> |
22 | | |
23 | | namespace gdal |
24 | | { |
25 | | |
26 | | /** |
27 | | * Class to manage reclassification of pixel values |
28 | | */ |
29 | | class Reclassifier |
30 | | { |
31 | | public: |
32 | | /// Character separating elements in a list of mapping |
33 | | static constexpr char MAPPING_INTERVAL_SEP_CHAR = ';'; |
34 | | |
35 | | /// Character separating source interval from target value |
36 | | static constexpr char MAPPING_FROMTO_SEP_CHAR = '='; |
37 | | |
38 | | /** |
39 | | * Internal struct to hold an interval of values to be reclassified |
40 | | */ |
41 | | struct Interval |
42 | | { |
43 | | /// minimum value of range |
44 | | double dfMin; |
45 | | |
46 | | /// maximum value of range |
47 | | double dfMax; |
48 | | |
49 | | /// Set the interval to represent a single value [x,x] |
50 | | void SetToConstant(double dfVal); |
51 | | |
52 | | /** Parse an interval. The interval may be either a single constant value, |
53 | | * or two comma-separated values enclosed by parentheses/brackets to |
54 | | * represent open/closed intervals. |
55 | | * |
56 | | * @param pszText string from which to parse an interval |
57 | | * @param end pointer to first non-consumed character |
58 | | * @return CE_None on success, CE_Failure otherwise |
59 | | */ |
60 | | CPLErr Parse(const char *pszText, char **end); |
61 | | |
62 | | /// Returns true of the interval represents a single value [x,x] |
63 | | bool IsConstant() const |
64 | 0 | { |
65 | 0 | return dfMin == dfMax; |
66 | 0 | } |
67 | | |
68 | | /// Returns true if the interval contains a value |
69 | | bool Contains(double x) const |
70 | 0 | { |
71 | 0 | return x >= dfMin && x <= dfMax; |
72 | 0 | } |
73 | | |
74 | | /// Returns true if the intervals overlap |
75 | | bool Overlaps(const Interval &other) const; |
76 | | }; |
77 | | |
78 | | /** Initialize a Reclassifier from text. The text consists of a series of |
79 | | * SOURCE=DEST mappings, separated by a semicolon. |
80 | | * |
81 | | * Each SOURCE element much be one of: |
82 | | * - a constant value |
83 | | * - a range of values, such as (3, 4] or [7, inf] |
84 | | * - the value NO_DATA, for which the provided NoData value will be |
85 | | * substituted |
86 | | * - the value DEFAULT, to define a DEST for any value that does not |
87 | | * match another SOURCE mapping |
88 | | * |
89 | | * Each DEST element must be one of: |
90 | | * - a constant value |
91 | | * - the value NO_DATA, for which the provided NoData value will be |
92 | | * substituted |
93 | | * |
94 | | * An error will be returned if: |
95 | | * - NO_DATA is used by a NoData value is not defined. |
96 | | * - a DEST value does not fit into the destination data type |
97 | | * |
98 | | * @param pszText text to parse |
99 | | * @param noDataValue NoData value |
100 | | * @param eBufType Destination data type |
101 | | * @return CE_None if no errors occurred, CE_Failure otherwise |
102 | | */ |
103 | | CPLErr Init(const char *pszText, std::optional<double> noDataValue, |
104 | | GDALDataType eBufType); |
105 | | |
106 | | /** Set a mapping between an interval and (optionally) a destination value. |
107 | | * If no destination value is provided, values matching the interval |
108 | | * will be passed through unmodified. It will not be verified that these values |
109 | | * fit within the destination data type. |
110 | | */ |
111 | | void AddMapping(const Interval &interval, std::optional<double> dfDstVal); |
112 | | |
113 | | /** Reclassify a value |
114 | | * |
115 | | * @param srcVal the value to reclassify |
116 | | * @param bFoundInterval set to True if the value could be reclassified |
117 | | * @return the reclassified value |
118 | | */ |
119 | | double Reclassify(double srcVal, bool &bFoundInterval) const; |
120 | | |
121 | | /** If true, values not matched by any interval will be |
122 | | * returned unmodified. It will not be verified that these values |
123 | | * fit within the destination data type. |
124 | | */ |
125 | | void SetDefaultPassThrough(bool value) |
126 | 0 | { |
127 | 0 | m_defaultPassThrough = value; |
128 | 0 | } |
129 | | |
130 | | /** Sets a default value for any value not matched by any interval. |
131 | | */ |
132 | | void SetDefaultValue(double value) |
133 | 0 | { |
134 | 0 | m_defaultValue = value; |
135 | 0 | } |
136 | | |
137 | | /** Sets a value for an input NaN value |
138 | | */ |
139 | | void SetNaNValue(double value) |
140 | 0 | { |
141 | 0 | m_NaNValue = value; |
142 | 0 | } |
143 | | |
144 | | /** Prepare reclassifier for use. No more mappings may be added. |
145 | | */ |
146 | | CPLErr Finalize(); |
147 | | |
148 | | private: |
149 | | /// mapping of ranges to outputs |
150 | | std::vector<std::pair<Interval, std::optional<double>>> |
151 | | m_aoIntervalMappings{}; |
152 | | |
153 | | /// output value for NaN inputs |
154 | | std::optional<double> m_NaNValue{}; |
155 | | |
156 | | /// output value for inputs not matching any Interval |
157 | | std::optional<double> m_defaultValue{}; |
158 | | |
159 | | /// whether to pass unmatched inputs through unmodified |
160 | | bool m_defaultPassThrough{false}; |
161 | | }; |
162 | | |
163 | | } // namespace gdal |