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