ICalculator.hpp
1 #pragma once
2 
3 #include "DM/IGeometry.hpp"
4 
5 DM_NAMESPACE_BEGIN
6 
7 /// \brief Generic calculator for evaluating formulas or manipulating objects based on the OPALS \ref ref_filter_generic "generic filter syntax"
8 class DM_API ICalculator : public ObjectBase
9 {
10 public:
11 
12  /// Choose the supported input data types.
13  enum struct ReadAccess
14  {
15  none, ///< support querying no data types at all. Hence, only constants and functions: pi atan(.23)
16  coordinates, ///< support querying coordinates: x y z
17  attributes = coordinates << 1, ///< support querying attributes: sigmaX _customAttr
18  vectors = coordinates | attributes, ///< shortcut: support querying coordinates or attributes
19  rasters = attributes << 1, ///< support querying input raster data: r[0]
20  neighbors = rasters << 1, ///< support querying neighbors: n[0]
21  full = (neighbors << 1) - 1 ///< support querying all input data types
22  };
23 
24  /// Choose which types of data may be assigned to.
25  enum struct WriteAccess
26  {
27  none, ///< support no assignments at all
28  coordinates, ///< support assignment to coordinates
29  attributes = coordinates << 1, ///< support assignment to attributes
30  rasters = attributes << 1, ///< support assignment to output raster nodes: [0] [red] [](float32)
31  full = (rasters << 1) - 1 ///< support assignment to all output data types
32  };
33 
34  /// The result type of the calculator.
35  /** If custom attributes are to be queried, the result type is unknown beforehand.*/
36  enum struct DataTypeSuperset
37  {
38  arithmetic, string, unknown
39  };
40 
41  /// Copy of GDALDataType, but without complex types.
42  enum struct RasterDataType
43  {
44  unknown, ///< Unknown or unspecified type
45  uint8, ///< 8 bit unsigned integer
46  uint16 , ///< 16 bit unsigned integer
47  int16 , ///< 16 bit signed integer
48  uint32 , ///< 32 bit unsigned integer
49  int32 , ///< 32 bit signed integer
50  float32, ///< 32 bit floating point
51  float64 ///< 64 bit floating point
52  };
53 
54  // Round \op value to \p type. Sets \p value to std::numeric_limits<double>::quiet_NaN() if it is outside the domain of \p type.
55  static void convertOrNaN(double &value, RasterDataType type);
56 
57  static ICalculator* New();
58 
59  /// Construct a calculator.
60  /// \param text Text that defines the calculator.
61  /// \param readAccess Choose which types of data the calculator may query i.e. read from.
62  /// If supported, make sure to provide inRaster and/or neighbors in calls to resultDouble()!
63  /// Throws if \p text would read from an unsupported type.
64  /// \param writeAccess Choose which types of data the calculator may write to i.e. assign to.
65  /// If supported, make sure to provide outRaster in calls to resultDouble()!
66  /// Throws if \p text would assign to an unsupported type.
67  static ICalculator* New(const char *text, ReadAccess readAccess, WriteAccess writeAccess);
68 
69  /// Deep-copy. Use for multi-threading!
70  virtual ICalculator* clone() const = 0;
71 
72  virtual ICalculator* operator*(double factor) const = 0;
73  virtual ICalculator* operator*(const ICalculator& that) const = 0;
74 
75  /// \name Logging of evaluation errors
76  /// Set a functor to be called upon evaluation errors, e.g. division by zero, conversion failure to target type.
77  /// \p msg describes the problem, and it may contain contextual information. Hence, similar problems may have different messages.
78  ///@{
79 
80  /// Abstract functor to derive from.
82  {
83  virtual void operator()(const char *msg) = 0;
84  };
85 
86  /// Make this use your functor instance as callback. Its memory remains managed by you.
87  virtual void evaluationErrorCallback(EvaluationErrorCallback *cb = nullptr) = 0;
88 
89  ///@}
90 
91  /// Get notified if an evaluated geometry has been changed.
92  /** After each call of resultDouble, this flag will be true only if this has changed the coordinates and/or attributes of \p geom .
93  An attribute is considered as changed if:
94  - a new AddInfo has been assigned, or
95  - its layout has been changed (column of same name but different type) or extended (additional column), or
96  - an attribute that was null has been set to non-null, or
97  - a non-null attribute has been set null, or
98  - the value of a non-null attribute has been changed.
99  The memory of \p flag remains managed by you.*/
100  virtual void geometryChanged(bool *flag = nullptr) = 0;
101 
102  /// Initialize internal data. resultDouble calls this implicitly / lazily if some non-const method has been called since the last call to this.
103  /** This does various checks and may throw.
104  Call this explicitly to generate these errors beforehand. */
105  virtual void init() const = 0;
106 
107  /// \name Input rasters
108  /// Calculators process zero or more input raster datasets with zero or more bands.
109  /// Each band has an invalid value, and it may have a name.
110  /// Named bands may be accessed in the \p text by name instead of by index.
111  /// While the number of neighbors may change for each call of resultDouble(),
112  /// the number of input raster datasets, their band counts, and each band's name and nodata value typically do not change
113  /// for many consecutive calls of resultDouble(). Hence, this information must be specified beforehand, if input rasters shall be used.
114  ///@{
115 
116  /// \brief Tells the calculator for how many datasets the argument \p inRaster of resultDouble() will subsequently provide data,
117  /// if the respective datasets are used (see inputDatasetSubset()).
118  /** Datasets by default have a single band.
119  For each dataset, \p inRaster expects an element for each band, sorted first by dataset and second by band index, in ascending order.
120  Hence, with
121 
122  CalculatorHandle calc = ICalculator::New(...);
123  calc->inputDatasetCount(2);
124 
125  resultDouble() will expect \p inRaster to point to 2 elements in total:
126  one for the first dataset, followed by another element for the second dataset (since both datasets are single band).*/
127  virtual void inputDatasetCount(unsigned nDatasets) = 0;
128  /// Tells the calculator that the \p iDataset 'th dataset has \p nBands instead of 1.
129  /** Hence, with
130 
131  CalculatorHandle calc = ICalculator::New(...);
132  calc->inputDatasetCount(3);
133  calc->inputBandCount(0, 2);
134  calc->inputBandCount(2, 4);
135 
136  resultDouble() will expect \p inRaster to point to 7 elements, where
137  - the first 2 elements are associated with the 2 bands of the first dataset,
138  - the third element is associated with the only band of the second dataset (because its band count has been left to its default 1), and
139  - the trailing 4 elements are associated with the four bands of the third dataset.*/
140  virtual void inputBandCount(unsigned iDataset, unsigned nBands) = 0;
141  /// Associates a band index with a band name, so the band can be accessed by name in \p text.
142  virtual void inputBandName(unsigned iDataset, unsigned iBand, char const *bandName) = 0;
143  /// Associates the given value as invalid for the given input raster band. Default: NaN.
144  virtual void inputBandNoData(unsigned iDataset, unsigned iBand, double noData) = 0;
145  /// Temporarily restricts the datasets for which \p inRaster must provide elements to those specified in \p iDatasets.
146  /** Input raster indices (r[<idx>]) and index ranges (mean(r[<iBegin> : <iEnd>])) then index into this reduced list of datasets.
147  \p iDatasets must point to \p nDatasets elements.
148  Input band counts, names, and nodata-values of temporarily de-activated datasets are preserved and will be re-used
149  once a dataset gets re-activated by calling inputDatasetSubset another time.
150  Hence, with
151 
152  CalculatorHandle calc = ICalculator::New(...);
153  calc->inputDatasetCount(3);
154  calc->inputBandCount(0, 2);
155  calc->inputBandName(0, 0, "red");
156  calc->inputBandCount(2, 4);
157  std::vector<unsigned> subset = {1, 2};
158  calc->inputDatasetSubset(subset.data(), subset.size());
159 
160  resultDouble() expects \p inRaster to point to 5 elements, where
161  - the first element is associated with the only band of the second dataset, and
162  - the trailing 4 elements are associated with the four bands of the third dataset.
163 
164  Because the first dataset has been excluded / de-activated, r[0] now refers to the single-band (second) dataset.
165  If later on,
166 
167  subset = {0, 1};
168  calc->inputDatasetSubset(subset.data(), subset.size());
169 
170  gets executed, resultDouble() expects \p inRaster to point to 3 elements, where
171  - the first 2 elements are associated with the 2 bands of the first dataset, and
172  - the third element is associated with the only band of the second dataset.
173 
174  Again, the first band of the first dataset is associated with the name "red".
175  Because the first dataset has been activated again, r[0] refers to the two-band (first) dataset.*/
176  virtual void inputDatasetSubset(unsigned const *iDatasets = 0, unsigned nDatasets = 0) = 0;
177 
178  ///@}
179 
180 
181  /// \name Output raster
182  /// \p text defines the number of output bands, their names and types.
183  /// Use this information to size \p outRaster and create the output raster datasets.
184  ///@{
185 
186  /// Returns the number of elements that \p outRaster must point to.
187  /** Hence, returns 0 if this calculator does not write to any output rasters. */
188  virtual unsigned outputBandCount() const = 0;
189  /// Returns the name of the \p iBand'th output band if specified in \p text, or otherwise an empty string.
190  virtual const char * outputBandName(unsigned iBand) const = 0;
191  /// Returns the actual type assumed for the \p iBand'th output band. If not specified in \p text, returns the one passed to outputBandTypeDefault, or otherwise RasterDataType::float64.
192  virtual RasterDataType outputBandType(unsigned iBand) const = 0;
193  /// Sets a value to be written to the \p iBand'th element of \p outRaster if invalid (aka NO_DATA). Default: std::numeric_limits<output band type>::max().
194  virtual void outputBandNoData(unsigned iBand, double noData) = 0;
195  /// Clears the NO_DATA value for the \p iBand'th element of \p outRaster. Evaluation will throw upon encountering an invalid value!
196  virtual void clearOutputBandNoData(unsigned iBand) = 0;
197  /// Returns true if a NO_DATA value has been set for the \p iBand'th element of \p outRaster.
198  virtual bool hasOutputBandNoData(unsigned iBand) const = 0;
199  /// Returns the NO_DATA value for the \p iBand'th element of \p outRaster. Throws if none has been set.
200  virtual double outputBandNoData(unsigned iBand) const = 0;
201  /// Sets the default data type for output bands, to be used if \p text does not specify one. Default: RasterDataType::float64.
202  virtual void outputBandTypeDefault(RasterDataType) = 0;
203  /// Query the statistics of values assigned so far to the \p iBand'th output band.
204  /** \p nInvalid is the number of times that the value to be assigned has been invalid.
205  \p nNoData is the number of times that the value to be assigned has been valid and could be converted to the band's data type, but happened to be equal to the band's NoData value.
206  \p nOverflow is the number of times that the value to be assigned has been valid, but outside the domain (i.e. too small or large)
207  of the band's data type or not numeric (i.e. ColumnType::string or ColumnType::cstr).
208 
209  nInvalid + nNoData + nOverflow is the total number of times that the NoData value has been assigned to \p outRaster.
210  */
211  virtual void outputBandStats(unsigned iBand, size_t& nInvalid, size_t& nNoData, size_t& nOverflow) const = 0;
212 
213  ///@}
214 
215 
216  /// \name Attribute assignment statistics
217  /// Query the statistics of attributes assigned so far.
218  ///@{
219 
220  /// Query the number of attribute assignment statistics available.
221  virtual size_t nAttributeAssignmentStats() const = 0;
222  /// Query the statistics of values assigned so far to the \p iAttribute'th attribute.
223  /** \p name the name of this attribute
224  \p nInvalid is the number of times that the value to be assigned has been invalid.
225  \p nOverflow is the number of times that the value to be assigned has been valid, but outside the domain of this attribute's data type,
226  i.e. too small or too large, or numeric vs. string.
227  nInvalid + nOverflow is the total number of evaluated geometries for which this attribute is now inexistent
228  i.e. the attribute is either not part of the layout, or it is null.
229  */
230  virtual void attributeAssignmentStats(unsigned iAttribute, char const*& name, size_t& nInvalid, size_t& nOverflow) const = 0;
231 
232  ///@}
233 
234 
235  /// \name Evaluation
236  /// Evaluate this with the given geometry, input rasters, neighbors, and output rasters.
237  /// If this has been created without ReadAccess::rasters, then \p inRaster will not be accessed and may be NULL.
238  /// Otherwise, preceding calls to methods in the group for input rasters
239  /// define the number of elements that \p inRaster must point to, and the dataset and band that each of its elements is associated with.
240  /// If this has been created without ReadAccess::neighbors, then \p neighbors will not be accessed and may be NULL.
241  /// \p neighbors must point to \p nNeighbors elements.
242  /// If this has been created without WriteAccess::rasters, then \p outRaster will not be accessed and may be NULL.
243  /// Otherwise, \p outRaster must point to outputBandCount() elements.
244  ///
245  /// Returns the result of evaluating the last statement.
246  /// Assignments always return 1.
247  /// The following operations yield invalid values:
248  /// - Access to inexistent or null attributes and invalid coordinates (DBL_MAX).
249  /// - Operations on invalid values.
250  /// - Domain errors, e.g. 1/0, acos(2).
251  /// - Conversion of values outside the value range of target types, e.g. uint(-1.), uint8(256.)
252  /// Returns NaN for invalid results.
253  /// Hence,
254  ///
255  /// x
256  ///
257  /// returns x if x is valid (i.e. not DBL_MAX, not NaN), NaN otherwise.
258  ///
259  /// 3 * sigmaX
260  ///
261  /// returns 3*sigmaX if sigmaX is valid (attribute is part of the layout and not NULL), NaN otherwise.
262  ///
263  /// 3 * sigmaX; 5
264  ///
265  /// returns 5.
266  ///
267  /// _val = 3 * sigmaX; sigmaY
268  ///
269  /// Sets _val and returns sigmaY if sigmaY is valid, otherwise NaN.
270  /// If init() has been called just before, then resultDouble only
271  /// throws on input raster dataset / band index out-of-range, e.g.:
272  /// - r[1] while only a single input raster dataset has been specified, or
273  /// - r[0][1] while only a single band has been specified for the first input raster dataset.
274  ///@{
275 
276  /// Evaluate this with a mutable geometry.
277  /** The geometry coordinates and/or attributes may get changed
278  only if this has been created with WriteAccess::coordinates and/or WriteAccess::attributes.*/
279  virtual double resultDouble(IGeometry &geom,
280  double const *inRaster = 0,
281  IGeometry const *const *neighbors = 0, unsigned nNeighbors = 0,
282  double *outRaster = 0) const = 0;
283  /// Evaluate this with a constant geometry.
284  /** Throws if this has been created with WriteAccess::coordinates and/or WriteAccess::attributes.*/
285  virtual double resultDouble(IGeometry const& geom,
286  double const* inRaster = 0,
287  IGeometry const* const* neighbors = 0, unsigned nNeighbors = 0,
288  double* outRaster = 0) const = 0;
289 
290  ///@}
291 
292 
293  /// Returns true iff resultDouble surely does not change any arguments and its return value is always the same, independent of its arguments.
294  /** Returns true for constants and combinations thereof:
295  New("1")->isConstant() == true
296  New("3 * atan2(0.2, pi)")->isConstant() == true
297  New("2 ? 3 : x")->isConstant() == true
298  New("x = 1")->isConstant() == false
299  New("3 || 4")->isConstant() == true
300  New("0 && 4")->isConstant() == true
301 
302  Note that the following is correct output, because the validity of x is unknown beforehand and hence, resultDouble may return 0 or NaN.
303  New("0 * x")->isConstant() == false
304 
305  For operator&& and operator||, this returns certain false negatives, e.g.:
306  New("0 && x")->isConstant() == false
307  New("3 || x")->isConstant() == false */
308  virtual bool isConstant() const = 0;
309 
310  /// returns true iff the calculator reads an input raster.
311  virtual bool readsRasters() const = 0;
312  /// returns true iff this reads one or more input raster datasets specified by a single index or by a slice that defines start and/or stop indices.
313  virtual bool readsRastersByIndexOrLimitedSlice() const = 0;
314  /// returns true iff the calculator reads a neighbor.
315  virtual bool readsNeighbors() const = 0;
316 
317  /// returns the number of (statements that are) assignments.
318  virtual unsigned assignmentCount() const = 0;
319  /// returns the number of statements. Empty statements do not count.
320  virtual unsigned statementCount() const = 0;
321 
322  virtual DataTypeSuperset typeSuperset() const = 0;
323 
324  virtual void print(std::ostream&) const = 0;
325 
326  virtual const char *text() const = 0;
327 };
328 
330 
331 constexpr ICalculator::ReadAccess operator| (const ICalculator::ReadAccess &left, const ICalculator::ReadAccess &right)
332 {
333  return static_cast<ICalculator::ReadAccess>(static_cast<int>(left) | static_cast<int>(right));
334 }
335 
336 constexpr ICalculator::WriteAccess operator| (const ICalculator::WriteAccess &left, const ICalculator::WriteAccess &right)
337 {
338  return static_cast<ICalculator::WriteAccess>(static_cast<int>(left) | static_cast<int>(right));
339 }
340 
341 DM_NAMESPACE_END
Generic calculator for evaluating formulas or manipulating objects based on the OPALS generic filter ...
Definition: ICalculator.hpp:8
WriteAccess
Choose which types of data may be assigned to.
Definition: ICalculator.hpp:25
DataTypeSuperset
The result type of the calculator.
Definition: ICalculator.hpp:36
Abstract functor to derive from.
Definition: ICalculator.hpp:81
RasterDataType
Copy of GDALDataType, but without complex types.
Definition: ICalculator.hpp:42
@ string
name: "string". c string of arbitrary size
Base class of all geometry objects.
Definition: IGeometry.hpp:26
Definition: M/c++_api/inc/DM/ObjectBase.hpp:8
ReadAccess
Choose the supported input data types.
Definition: ICalculator.hpp:13