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