Line data Source code
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 : { 65 : return dfMin == dfMax; 66 : } 67 : 68 : /// Returns true if the interval contains a value 69 104705 : bool Contains(double x) const 70 : { 71 104705 : return x >= dfMin && x <= dfMax; 72 : } 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 1 : void SetDefaultPassThrough(bool value) 126 : { 127 1 : m_defaultPassThrough = value; 128 1 : } 129 : 130 : /** Sets a default value for any value not matched by any interval. 131 : */ 132 9 : void SetDefaultValue(double value) 133 : { 134 9 : m_defaultValue = value; 135 9 : } 136 : 137 : /** Sets a value for an input NaN value 138 : */ 139 1 : void SetNaNValue(double value) 140 : { 141 1 : m_NaNValue = value; 142 1 : } 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