bes  Updated for version 3.20.6
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 <BaseType.h>
31 #include <Float64.h>
32 #include <Str.h>
33 #include <Array.h>
34 #include <Grid.h>
35 #include "D4RValue.h"
36 
37 #include <Error.h>
38 #include <DDS.h>
39 
40 #include <debug.h>
41 #include <util.h>
42 
43 #include "BESDebug.h"
44 
45 #include "LinearScaleFunction.h"
46 
47 using namespace libdap;
48 
49 namespace functions {
50 
51 string 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 
60 static 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 
82 static 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.length() - 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 
110 static 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 
129 static 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 
137 static double get_slope(BaseType *var)
138 {
139  return get_attribute_double_value(var, "scale_factor");
140 }
141 
142 static double get_missing_value(BaseType *var)
143 {
144  return get_attribute_double_value(var, "missing_value");
145 }
146 
147 BaseType *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 = 0;
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 
248 void 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 
320 BaseType *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
libdap
Definition: BESDapFunctionResponseCache.h:35
Error