@openeo/js-client 2.5.1 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,211 +1,211 @@
1
- const TapDigit = require("./tapdigit");
2
- const Parameter = require("./parameter");
3
- const BuilderNode = require('./node');
4
-
5
- /**
6
- * This converts a mathematical formula into a openEO process for you.
7
- *
8
- * Operators: - (subtract), + (add), / (divide), * (multiply), ^ (power)
9
- *
10
- * It supports all mathematical functions (i.e. expects a number and returns a number) the back-end implements, e.g. `sqrt(x)`.
11
- * For namespaced processes, use for example `process@namespace(x)` - EXPERIMENTAL!
12
- *
13
- * Only available if a builder is specified in the constructor:
14
- * You can refer to output from processes with a leading `#`, e.g. `#loadco1` if the node to refer to has the key `loadco1`.
15
- *
16
- * Only available if a parent node is set via `setNode()`:
17
- * Parameters can be accessed simply by name.
18
- * If the first parameter is a (labeled) array, the value for a specific index or label can be accessed by typing the numeric index or textual label with a `$` in front, for example `$B1` for the label `B1` or `$0` for the first element in the array. Numeric labels are not supported.
19
- * You can access subsequent parameters by adding additional `$` at the beginning, e.g. `$$0` to access the first element of an array in the second parameter, `$$$0` for the same in the third parameter etc.
20
- *
21
- * An example that computes an EVI (assuming the labels for the bands are `NIR`, `RED` and `BLUE`): `2.5 * ($NIR - $RED) / (1 + $NIR + 6 * $RED + (-7.5 * $BLUE))`
22
- */
23
- class Formula {
24
-
25
- /**
26
- * Creates a math formula object.
27
- *
28
- * @param {string} formula - A mathematical formula to parse.y
29
- */
30
- constructor(formula) {
31
- let parser = new TapDigit.Parser();
32
- /**
33
- * @type {object.<string, *>}
34
- */
35
- this.tree = parser.parse(formula);
36
- /**
37
- * @type {Builder | null}
38
- */
39
- this.builder = null;
40
- }
41
-
42
- /**
43
- * The builder instance to use.
44
- *
45
- * @param {Builder} builder - The builder instance to add the formula to.
46
- */
47
- setBuilder(builder) {
48
- this.builder = builder;
49
- }
50
-
51
- /**
52
- * Generates the processes for the formula specified in the constructor.
53
- *
54
- * Returns the last node that computes the result.
55
- *
56
- * @param {boolean} setResultNode - Set the `result` flag to `true`.
57
- * @returns {BuilderNode}
58
- * @throws {Error}
59
- */
60
- generate(setResultNode = true) {
61
- let finalNode = this.parseTree(this.tree);
62
- if (!(finalNode instanceof BuilderNode)) {
63
- throw new Error('Invalid formula specified.');
64
- }
65
- // Set result node
66
- if (setResultNode) {
67
- finalNode.result = true;
68
- }
69
- return finalNode;
70
- }
71
-
72
- /**
73
- * Walks through the tree generated by the TapDigit parser and generates process nodes.
74
- *
75
- * @protected
76
- * @param {object.<string, *>} tree
77
- * @returns {object.<string, *>}
78
- * @throws {Error}
79
- */
80
- parseTree(tree) {
81
- let key = Object.keys(tree)[0]; // There's never more than one property so no loop required
82
- switch(key) {
83
- case 'Number':
84
- return parseFloat(tree.Number);
85
- case 'Identifier':
86
- return this.getRef(tree.Identifier);
87
- case 'Expression':
88
- return this.parseTree(tree.Expression);
89
- case 'FunctionCall': {
90
- let args = [];
91
- for(let i in tree.FunctionCall.args) {
92
- args.push(this.parseTree(tree.FunctionCall.args[i]));
93
- }
94
- return this.builder.process(tree.FunctionCall.name, args);
95
- }
96
- case 'Binary':
97
- return this.addOperatorProcess(
98
- tree.Binary.operator,
99
- this.parseTree(tree.Binary.left),
100
- this.parseTree(tree.Binary.right)
101
- );
102
- case 'Unary': {
103
- let val = this.parseTree(tree.Unary.expression);
104
- if (tree.Unary.operator === '-') {
105
- if (typeof val === 'number') {
106
- return -val;
107
- }
108
- else {
109
- return this.addOperatorProcess('*', -1, val);
110
- }
111
- }
112
- else {
113
- return val;
114
- }
115
- }
116
- default:
117
- throw new Error('Operation ' + key + ' not supported.');
118
- }
119
- }
120
-
121
- /**
122
- * Gets the reference for a value, e.g. from_node or from_parameter.
123
- *
124
- * @protected
125
- * @param {*} value
126
- * @returns {*}
127
- */
128
- getRef(value) {
129
- // Convert native data types
130
- if (value === 'true') {
131
- return true;
132
- }
133
- else if (value === 'false') {
134
- return false;
135
- }
136
- else if (value === 'null') {
137
- return null;
138
- }
139
-
140
- // Output of a process
141
- if (typeof value === 'string' && value.startsWith('#')) {
142
- let nodeId = value.substring(1);
143
- if (nodeId in this.builder.nodes) {
144
- return { from_node: nodeId };
145
- }
146
- }
147
-
148
- let callbackParams = this.builder.getParentCallbackParameters();
149
- // Array labels / indices
150
- if (typeof value === 'string' && callbackParams.length > 0) {
151
- let prefix = value.match(/^\$+/);
152
- let count = prefix ? prefix[0].length : 0;
153
- if (count > 0 && callbackParams.length >= count) {
154
- let ref = value.substring(count);
155
- return callbackParams[count-1][ref];
156
- }
157
- }
158
-
159
- // Everything else is a parameter
160
- let parameter = new Parameter(value);
161
- // Add new parameter if it doesn't exist
162
- this.builder.addParameter(parameter);
163
- return parameter;
164
- }
165
-
166
- /**
167
- * Adds a process node for an operator like +, -, *, / etc.
168
- *
169
- * @param {string} operator - The operator.
170
- * @param {number|object.<string, *>} left - The left part for the operator.
171
- * @param {number|object.<string, *>} right - The right part for the operator.
172
- * @returns {BuilderNode}
173
- * @throws {Error}
174
- */
175
- addOperatorProcess(operator, left, right) {
176
- let processName = Formula.operatorMapping[operator];
177
- let process = this.builder.spec(processName);
178
- if (processName && process) {
179
- let args = {};
180
- if (!Array.isArray(process.parameters) || process.parameters.length < 2) {
181
- throw new Error("Process for operator " + operator + " must have at least two parameters");
182
- }
183
- args[process.parameters[0].name || 'x'] = left;
184
- args[process.parameters[1].name || 'y'] = right;
185
- return this.builder.process(processName, args);
186
- }
187
- else {
188
- throw new Error('Operator ' + operator + ' not supported');
189
- }
190
- }
191
-
192
- }
193
-
194
- /**
195
- * List of supported operators.
196
- *
197
- * All operators must have the parameters be name x and y.
198
- *
199
- * The key is the mathematical operator, the value is the process identifier.
200
- *
201
- * @type {object.<string, string>}
202
- */
203
- Formula.operatorMapping = {
204
- "-": "subtract",
205
- "+": "add",
206
- "/": "divide",
207
- "*": "multiply",
208
- "^": "power"
209
- };
210
-
211
- module.exports = Formula;
1
+ const TapDigit = require("./tapdigit");
2
+ const Parameter = require("./parameter");
3
+ const BuilderNode = require('./node');
4
+
5
+ /**
6
+ * This converts a mathematical formula into a openEO process for you.
7
+ *
8
+ * Operators: - (subtract), + (add), / (divide), * (multiply), ^ (power)
9
+ *
10
+ * It supports all mathematical functions (i.e. expects a number and returns a number) the back-end implements, e.g. `sqrt(x)`.
11
+ * For namespaced processes, use for example `process@namespace(x)` - EXPERIMENTAL!
12
+ *
13
+ * Only available if a builder is specified in the constructor:
14
+ * You can refer to output from processes with a leading `#`, e.g. `#loadco1` if the node to refer to has the key `loadco1`.
15
+ *
16
+ * Only available if a parent node is set via `setNode()`:
17
+ * Parameters can be accessed simply by name.
18
+ * If the first parameter is a (labeled) array, the value for a specific index or label can be accessed by typing the numeric index or textual label with a `$` in front, for example `$B1` for the label `B1` or `$0` for the first element in the array. Numeric labels are not supported.
19
+ * You can access subsequent parameters by adding additional `$` at the beginning, e.g. `$$0` to access the first element of an array in the second parameter, `$$$0` for the same in the third parameter etc.
20
+ *
21
+ * An example that computes an EVI (assuming the labels for the bands are `NIR`, `RED` and `BLUE`): `2.5 * ($NIR - $RED) / (1 + $NIR + 6 * $RED + (-7.5 * $BLUE))`
22
+ */
23
+ class Formula {
24
+
25
+ /**
26
+ * Creates a math formula object.
27
+ *
28
+ * @param {string} formula - A mathematical formula to parse.y
29
+ */
30
+ constructor(formula) {
31
+ let parser = new TapDigit.Parser();
32
+ /**
33
+ * @type {object.<string, *>}
34
+ */
35
+ this.tree = parser.parse(formula);
36
+ /**
37
+ * @type {Builder | null}
38
+ */
39
+ this.builder = null;
40
+ }
41
+
42
+ /**
43
+ * The builder instance to use.
44
+ *
45
+ * @param {Builder} builder - The builder instance to add the formula to.
46
+ */
47
+ setBuilder(builder) {
48
+ this.builder = builder;
49
+ }
50
+
51
+ /**
52
+ * Generates the processes for the formula specified in the constructor.
53
+ *
54
+ * Returns the last node that computes the result.
55
+ *
56
+ * @param {boolean} setResultNode - Set the `result` flag to `true`.
57
+ * @returns {BuilderNode}
58
+ * @throws {Error}
59
+ */
60
+ generate(setResultNode = true) {
61
+ let finalNode = this.parseTree(this.tree);
62
+ if (!(finalNode instanceof BuilderNode)) {
63
+ throw new Error('Invalid formula specified.');
64
+ }
65
+ // Set result node
66
+ if (setResultNode) {
67
+ finalNode.result = true;
68
+ }
69
+ return finalNode;
70
+ }
71
+
72
+ /**
73
+ * Walks through the tree generated by the TapDigit parser and generates process nodes.
74
+ *
75
+ * @protected
76
+ * @param {object.<string, *>} tree
77
+ * @returns {object.<string, *>}
78
+ * @throws {Error}
79
+ */
80
+ parseTree(tree) {
81
+ let key = Object.keys(tree)[0]; // There's never more than one property so no loop required
82
+ switch(key) {
83
+ case 'Number':
84
+ return parseFloat(tree.Number);
85
+ case 'Identifier':
86
+ return this.getRef(tree.Identifier);
87
+ case 'Expression':
88
+ return this.parseTree(tree.Expression);
89
+ case 'FunctionCall': {
90
+ let args = [];
91
+ for(let i in tree.FunctionCall.args) {
92
+ args.push(this.parseTree(tree.FunctionCall.args[i]));
93
+ }
94
+ return this.builder.process(tree.FunctionCall.name, args);
95
+ }
96
+ case 'Binary':
97
+ return this.addOperatorProcess(
98
+ tree.Binary.operator,
99
+ this.parseTree(tree.Binary.left),
100
+ this.parseTree(tree.Binary.right)
101
+ );
102
+ case 'Unary': {
103
+ let val = this.parseTree(tree.Unary.expression);
104
+ if (tree.Unary.operator === '-') {
105
+ if (typeof val === 'number') {
106
+ return -val;
107
+ }
108
+ else {
109
+ return this.addOperatorProcess('*', -1, val);
110
+ }
111
+ }
112
+ else {
113
+ return val;
114
+ }
115
+ }
116
+ default:
117
+ throw new Error('Operation ' + key + ' not supported.');
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Gets the reference for a value, e.g. from_node or from_parameter.
123
+ *
124
+ * @protected
125
+ * @param {*} value
126
+ * @returns {*}
127
+ */
128
+ getRef(value) {
129
+ // Convert native data types
130
+ if (value === 'true') {
131
+ return true;
132
+ }
133
+ else if (value === 'false') {
134
+ return false;
135
+ }
136
+ else if (value === 'null') {
137
+ return null;
138
+ }
139
+
140
+ // Output of a process
141
+ if (typeof value === 'string' && value.startsWith('#')) {
142
+ let nodeId = value.substring(1);
143
+ if (nodeId in this.builder.nodes) {
144
+ return { from_node: nodeId };
145
+ }
146
+ }
147
+
148
+ let callbackParams = this.builder.getParentCallbackParameters();
149
+ // Array labels / indices
150
+ if (typeof value === 'string' && callbackParams.length > 0) {
151
+ let prefix = value.match(/^\$+/);
152
+ let count = prefix ? prefix[0].length : 0;
153
+ if (count > 0 && callbackParams.length >= count) {
154
+ let ref = value.substring(count);
155
+ return callbackParams[count-1][ref];
156
+ }
157
+ }
158
+
159
+ // Everything else is a parameter
160
+ let parameter = new Parameter(value);
161
+ // Add new parameter if it doesn't exist
162
+ this.builder.addParameter(parameter);
163
+ return parameter;
164
+ }
165
+
166
+ /**
167
+ * Adds a process node for an operator like +, -, *, / etc.
168
+ *
169
+ * @param {string} operator - The operator.
170
+ * @param {number|object.<string, *>} left - The left part for the operator.
171
+ * @param {number|object.<string, *>} right - The right part for the operator.
172
+ * @returns {BuilderNode}
173
+ * @throws {Error}
174
+ */
175
+ addOperatorProcess(operator, left, right) {
176
+ let processName = Formula.operatorMapping[operator];
177
+ let process = this.builder.spec(processName);
178
+ if (processName && process) {
179
+ let args = {};
180
+ if (!Array.isArray(process.parameters) || process.parameters.length < 2) {
181
+ throw new Error("Process for operator " + operator + " must have at least two parameters");
182
+ }
183
+ args[process.parameters[0].name || 'x'] = left;
184
+ args[process.parameters[1].name || 'y'] = right;
185
+ return this.builder.process(processName, args);
186
+ }
187
+ else {
188
+ throw new Error('Operator ' + operator + ' not supported');
189
+ }
190
+ }
191
+
192
+ }
193
+
194
+ /**
195
+ * List of supported operators.
196
+ *
197
+ * All operators must have the parameters be name x and y.
198
+ *
199
+ * The key is the mathematical operator, the value is the process identifier.
200
+ *
201
+ * @type {object.<string, string>}
202
+ */
203
+ Formula.operatorMapping = {
204
+ "-": "subtract",
205
+ "+": "add",
206
+ "/": "divide",
207
+ "*": "multiply",
208
+ "^": "power"
209
+ };
210
+
211
+ module.exports = Formula;