bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
LinearScaleFunction.cc
1// -*- mode: c++; c-basic-offset:4 -*-
2
3// This file is part of libdap, A C++ implementation of the OPeNDAP Data
4// Access Protocol.
5
6// Copyright (c) 2013 OPeNDAP, Inc.
7// Authors: Nathan Potter <npotter@opendap.org>
8// James Gallagher <jgallagher@opendap.org>
9//
10// This library is free software; you can redistribute it and/or
11// modify it under the terms of the GNU Lesser General Public
12// License as published by the Free Software Foundation; either
13// version 2.1 of the License, or (at your option) any later version.
14//
15// This library is distributed in the hope that it will be useful,
16// but WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18// Lesser General Public License for more details.
19//
20// You should have received a copy of the GNU Lesser General Public
21// License along with this library; if not, write to the Free Software
22// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23//
24// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
25
26#include "config.h"
27
28#include <sstream>
29
30#include <libdap/BaseType.h>
31#include <libdap/Float64.h>
32#include <libdap/Str.h>
33#include <libdap/Array.h>
34#include <libdap/Grid.h>
35#include <libdap/D4RValue.h>
36
37#include <libdap/Error.h>
38#include <libdap/DDS.h>
39
40#include <libdap/debug.h>
41#include <libdap/util.h>
42
43#include "BESDebug.h"
44
45#include "LinearScaleFunction.h"
46
47using namespace libdap;
48
49namespace functions {
50
51string linear_scale_info =
52 string("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
53 + "<function name=\"linear_scale\" version=\"1.0b1\" href=\"http://docs.opendap.org/index.php/Server_Side_Processing_Functions#linear_scale\">\n"
54 + "</function>";
55
56// These static functions could be moved to a class that provides a more
57// general interface for COARDS/CF someday. Assume each BaseType comes bundled
58// with an attribute table.
59
60static double string_to_double(const char *val)
61{
62 istringstream iss(val);
63 double v;
64 iss >> v;
65
66 double abs_val = fabs(v);
67 if (abs_val > DODS_DBL_MAX || (abs_val != 0.0 && abs_val < DODS_DBL_MIN))
68 throw Error(malformed_expr, string("Could not convert the string '") + val + "' to a double.");
69
70 return v;
71}
72
82static double get_attribute_double_value(BaseType *var, vector<string> &attributes)
83{
84 // This code also builds a list of the attribute values that have been
85 // passed in but not found so that an informative message can be returned.
86 AttrTable &attr = var->get_attr_table();
87 string attribute_value = "";
88 string values = "";
89 vector<string>::iterator i = attributes.begin();
90 while (attribute_value == "" && i != attributes.end()) {
91 values += *i;
92 if (!values.empty()) values += ", ";
93 attribute_value = attr.get_attr(*i++);
94 }
95
96 // If the value string is empty, then look at the grid's array (if it's a
97 // grid) or throw an Error.
98 if (attribute_value.empty()) {
99 if (var->type() == dods_grid_c)
100 return get_attribute_double_value(dynamic_cast<Grid&>(*var).get_array(), attributes);
101 else
102 throw Error(malformed_expr,
103 string("No COARDS/CF '") + values.substr(0, values.size() - 2)
104 + "' attribute was found for the variable '" + var->name() + "'.");
105 }
106
107 return string_to_double(remove_quotes(attribute_value).c_str());
108}
109
110static double get_attribute_double_value(BaseType *var, const string &attribute)
111{
112 AttrTable &attr = var->get_attr_table();
113 string attribute_value = attr.get_attr(attribute);
114
115 // If the value string is empty, then look at the grid's array (if it's a
116 // grid or throw an Error.
117 if (attribute_value.empty()) {
118 if (var->type() == dods_grid_c)
119 return get_attribute_double_value(dynamic_cast<Grid&>(*var).get_array(), attribute);
120 else
121 throw Error(malformed_expr,
122 string("No COARDS '") + attribute + "' attribute was found for the variable '" + var->name()
123 + "'.");
124 }
125
126 return string_to_double(remove_quotes(attribute_value).c_str());
127}
128
129static double get_y_intercept(BaseType *var)
130{
131 vector<string> attributes;
132 attributes.push_back("add_offset");
133 attributes.push_back("add_off");
134 return get_attribute_double_value(var, attributes);
135}
136
137static double get_slope(BaseType *var)
138{
139 return get_attribute_double_value(var, "scale_factor");
140}
141
142static double get_missing_value(BaseType *var)
143{
144 return get_attribute_double_value(var, "missing_value");
145}
146
147BaseType *function_linear_scale_worker(BaseType *bt, double m, double b, double missing, bool use_missing)
148{
149 // Read the data, scale and return the result. Must replace the new data
150 // in a constructor (i.e., Array part of a Grid).
151 BaseType *dest = nullptr;
152 double *data;
153 if (bt->type() == dods_grid_c) {
154 // Grab the whole Grid; note that the scaling is done only on the array part
155 Grid &source = dynamic_cast<Grid&>(*bt);
156
157 BESDEBUG("function", "function_linear_scale_worker() - Grid send_p: " << source.send_p() << endl);
158 BESDEBUG("function",
159 "function_linear_scale_worker() - Grid Array send_p: " << source.get_array()->send_p() << endl);
160
161 // Read the grid; set send_p since Grid is a kind of constructor and
162 // read will only be called on it's fields if their send_p flag is set
163 source.set_send_p(true);
164 source.read();
165
166 // Get the Array part and read the values
167 Array *a = source.get_array();
168 //a->read();
169 data = extract_double_array(a);
170
171 // Now scale the data.
172 int length = a->length();
173 for (int i = 0; i < length; ++i)
174 data[i] = data[i] * m + b;
175
176 // Copy source Grid to result Grid. Could improve on this by not using this
177 // trick since it copies all of 'source' to 'dest', including the main Array.
178 // The next bit of code will replace those values with the newly scaled ones.
179 Grid *result = new Grid(source);
180
181 // Now load the transferred values; use Float64 as the new type of the result
182 // Grid Array.
183 result->get_array()->add_var_nocopy(new Float64(source.name()));
184 result->get_array()->set_value(data, length);
185 delete[] data;
186
187 // FIXME result->set_send_p(true);
188 BESDEBUG("function", "function_linear_scale_worker() - Grid send_p: " << source.send_p() << endl);
189 BESDEBUG("function",
190 "function_linear_scale_worker() - Grid Array send_p: " << source.get_array()->send_p() << endl);
191
192 dest = result;
193 }
194 else if (bt->is_vector_type()) {
195 Array &source = dynamic_cast<Array&>(*bt);
196 // If the array is really a map, make sure to read using the Grid
197 // because of the HDF4 handler's odd behavior WRT dimensions.
198 if (source.get_parent() && source.get_parent()->type() == dods_grid_c) {
199 source.get_parent()->set_send_p(true);
200 source.get_parent()->read();
201 }
202 else
203 source.read();
204
205 data = extract_double_array(&source);
206 int length = source.length();
207 for (int i = 0; i < length; ++i)
208 data[i] = data[i] * m + b;
209
210 Array *result = new Array(source);
211
212 result->add_var_nocopy(new Float64(source.name()));
213 result->set_value(data, length);
214
215 delete[] data; // val2buf copies.
216
217 dest = result;
218 }
219 else if (bt->is_simple_type() && !(bt->type() == dods_str_c || bt->type() == dods_url_c)) {
220 double data = extract_double_value(bt);
221 if (!use_missing || !double_eq(data, missing)) data = data * m + b;
222
223 Float64 *fdest = new Float64(bt->name());
224
225 fdest->set_value(data);
226 // dest->val2buf(static_cast<void*> (&data));
227 dest = fdest;
228 }
229 else {
230 throw Error(malformed_expr, "The linear_scale() function works only for numeric Grids, Arrays and scalars.");
231 }
232
233 return dest;
234}
235
248void function_dap2_linear_scale(int argc, BaseType * argv[], DDS &, BaseType **btpp)
249{
250 if (argc == 0) {
251 Str *response = new Str("info");
252 response->set_value(linear_scale_info);
253 *btpp = response;
254 return;
255 }
256
257 // Check for 1 or 3 arguments: 1 --> use attributes; 3 --> m & b supplied
258 DBG(cerr << "argc = " << argc << endl);
259 if (!(argc == 1 || argc == 3 || argc == 4))
260 throw Error(malformed_expr,
261 "Wrong number of arguments to linear_scale(). See linear_scale() for more information");
262
263 // Get m & b
264 bool use_missing = false;
265 double m, b, missing = 0.0;
266 if (argc == 4) {
267 m = extract_double_value(argv[1]);
268 b = extract_double_value(argv[2]);
269 missing = extract_double_value(argv[3]);
270 use_missing = true;
271 }
272 else if (argc == 3) {
273 m = extract_double_value(argv[1]);
274 b = extract_double_value(argv[2]);
275 use_missing = false;
276 }
277 else {
278 m = get_slope(argv[0]);
279
280 // This is really a hack; on a fair number of datasets, the y intercept
281 // is not given and is assumed to be 0. Here the function looks and
282 // catches the error if a y intercept is not found.
283 try {
284 b = get_y_intercept(argv[0]);
285 }
286 catch (Error &e) {
287 b = 0.0;
288 }
289
290 // This is not the best plan; the get_missing_value() function should
291 // do something other than throw, but to do that would require mayor
292 // surgery on get_attribute_double_value().
293 try {
294 missing = get_missing_value(argv[0]);
295 use_missing = true;
296 }
297 catch (Error &e) {
298 use_missing = false;
299 }
300 }
301
302 BESDEBUG("function",
303 "function_dap2_linear_scale() - m: " << m << ", b: " << b << ", use_missing: " << use_missing << ", missing: " << missing << endl);
304
305 *btpp = function_linear_scale_worker(argv[0], m, b, missing, use_missing);
306}
307
320BaseType *function_dap4_linear_scale(D4RValueList *args, DMR &dmr)
321{
322 BESDEBUG("function", "function_dap4_linear_scale() BEGIN " << endl);
323
324 // DAP4 function porting information: in place of 'argc' use 'args.size()'
325 if (args == 0 || args->size() == 0) {
326 Str *response = new Str("info");
327 response->set_value(linear_scale_info);
328 // DAP4 function porting: return a BaseType* instead of using the value-result parameter
329 return response;
330 }
331
332 // Check for 2 arguments
333 DBG(cerr << "args.size() = " << args.size() << endl);
334 if (!(args->size() == 1 || args->size() == 3 || args->size() == 4))
335 throw Error(malformed_expr,
336 "Wrong number of arguments to linear_scale(). See linear_scale() for more information");
337
338 // Get m & b
339 bool use_missing = false;
340 double m, b, missing = 0.0;
341 if (args->size() == 4) {
342 m = extract_double_value(args->get_rvalue(1)->value(dmr));
343 b = extract_double_value(args->get_rvalue(2)->value(dmr));
344 missing = extract_double_value(args->get_rvalue(3)->value(dmr));
345 use_missing = true;
346 }
347 else if (args->size() == 3) {
348 m = extract_double_value(args->get_rvalue(1)->value(dmr));
349 b = extract_double_value(args->get_rvalue(2)->value(dmr));
350 use_missing = false;
351 }
352 else {
353 m = get_slope(args->get_rvalue(0)->value(dmr));
354
355 // This is really a hack; on a fair number of datasets, the y intercept
356 // is not given and is assumed to be 0. Here the function looks and
357 // catches the error if a y intercept is not found.
358 try {
359 b = get_y_intercept(args->get_rvalue(0)->value(dmr));
360 }
361 catch (Error &e) {
362 b = 0.0;
363 }
364
365 // This is not the best plan; the get_missing_value() function should
366 // do something other than throw, but to do that would require mayor
367 // surgery on get_attribute_double_value().
368 try {
369 missing = get_missing_value(args->get_rvalue(0)->value(dmr));
370 use_missing = true;
371 }
372 catch (Error &e) {
373 use_missing = false;
374 }
375 }
376 BESDEBUG("function",
377 "function_dap4_linear_scale() - m: " << m << ", b: " << b << ", use_missing: " << use_missing << ", missing: " << missing << endl);
378
379 BESDEBUG("function", "function_dap4_linear_scale() END " << endl);
380
381 return function_linear_scale_worker(args->get_rvalue(0)->value(dmr), m, b, missing, use_missing);
382}
383
384} // namesspace functions