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 #include "D4RValue.h"
37 
38 #include <Error.h>
39 #include <DDS.h>
40 
41 #include <debug.h>
42 #include <util.h>
43 
44 #include "BESDebug.h"
45 
46 #include "LinearScaleFunction.h"
47 
48 namespace libdap {
49 
51 string("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n") +
52 "<function name=\"linear_scale\" version=\"1.0b1\" href=\"http://docs.opendap.org/index.php/Server_Side_Processing_Functions#linear_scale\">\n" +
53 "</function>";
54 
55 // These static functions could be moved to a class that provides a more
56 // general interface for COARDS/CF someday. Assume each BaseType comes bundled
57 // with an attribute table.
58 
59 static double string_to_double(const char *val)
60 {
61 #if 0
62  char *ptr;
63  errno = 0;
64  // Clear previous value. 5/21/2001 jhrg
65 
66 #ifdef WIN32
67  double v = w32strtod(val, &ptr);
68 #else
69  double v = strtod(val, &ptr);
70 #endif
71 
72  if ((v == 0.0 && (val == ptr || errno == HUGE_VAL || errno == ERANGE))
73  || *ptr != '\0') {
74  throw Error(malformed_expr,string("Could not convert the string '") + val + "' to a double.");
75  }
76 #endif
77 
78  istringstream iss(val);
79  double v;
80  iss >> v;
81 
82  double abs_val = fabs(v);
83  if (abs_val > DODS_DBL_MAX || (abs_val != 0.0 && abs_val < DODS_DBL_MIN))
84  throw Error(malformed_expr,string("Could not convert the string '") + val + "' to a double.");
85 
86  return v;
87 }
88 
98 static double get_attribute_double_value(BaseType *var, vector<string> &attributes)
99 {
100  // This code also builds a list of the attribute values that have been
101  // passed in but not found so that an informative message can be returned.
102  AttrTable &attr = var->get_attr_table();
103  string attribute_value = "";
104  string values = "";
105  vector<string>::iterator i = attributes.begin();
106  while (attribute_value == "" && i != attributes.end()) {
107  values += *i;
108  if (!values.empty())
109  values += ", ";
110  attribute_value = attr.get_attr(*i++);
111  }
112 
113  // If the value string is empty, then look at the grid's array (if it's a
114  // grid) or throw an Error.
115  if (attribute_value.empty()) {
116  if (var->type() == dods_grid_c)
117  return get_attribute_double_value(dynamic_cast<Grid&>(*var).get_array(), attributes);
118  else
119  throw Error(malformed_expr,string("No COARDS/CF '") + values.substr(0, values.length() - 2)
120  + "' attribute was found for the variable '"
121  + var->name() + "'.");
122  }
123 
124  return string_to_double(remove_quotes(attribute_value).c_str());
125 }
126 
127 static double get_attribute_double_value(BaseType *var, const string &attribute)
128 {
129  AttrTable &attr = var->get_attr_table();
130  string attribute_value = attr.get_attr(attribute);
131 
132  // If the value string is empty, then look at the grid's array (if it's a
133  // grid or throw an Error.
134  if (attribute_value.empty()) {
135  if (var->type() == dods_grid_c)
136  return get_attribute_double_value(dynamic_cast<Grid&>(*var).get_array(), attribute);
137  else
138  throw Error(malformed_expr,string("No COARDS '") + attribute
139  + "' attribute was found for the variable '"
140  + var->name() + "'.");
141  }
142 
143  return string_to_double(remove_quotes(attribute_value).c_str());
144 }
145 
146 static double get_y_intercept(BaseType *var)
147 {
148  vector<string> attributes;
149  attributes.push_back("add_offset");
150  attributes.push_back("add_off");
151  return get_attribute_double_value(var, attributes);
152 }
153 
154 static double get_slope(BaseType *var)
155 {
156  return get_attribute_double_value(var, "scale_factor");
157 }
158 
159 static double get_missing_value(BaseType *var)
160 {
161  return get_attribute_double_value(var, "missing_value");
162 }
163 
164 BaseType *function_linear_scale_worker(BaseType *bt, double m, double b, double missing, bool use_missing){
165 
166 
167  // Read the data, scale and return the result. Must replace the new data
168  // in a constructor (i.e., Array part of a Grid).
169  BaseType *dest = 0;
170  double *data;
171  if (bt->type() == dods_grid_c) {
172  // Grab the whole Grid; note that the scaling is done only on the array part
173  Grid &source = dynamic_cast<Grid&>(*bt);
174 
175  BESDEBUG("function", "function_linear_scale_worker() - Grid send_p: " << source.send_p() << endl);
176  BESDEBUG("function", "function_linear_scale_worker() - Grid Array send_p: " << source.get_array()->send_p() << endl);
177 
178 
179  // Read the grid; set send_p since Grid is a kind of constructor and
180  // read will only be called on it's fields if their send_p flag is set
181  source.set_send_p(true);
182  source.read();
183 
184  // Get the Array part and read the values
185  Array *a = source.get_array();
186  //a->read();
187  data = extract_double_array(a);
188 
189  // Now scale the data.
190  int length = a->length();
191  for (int i = 0; i < length; ++i)
192  data[i] = data[i] * m + b;
193 
194  // Copy source Grid to result Grid. Could improve on this by not using this
195  // trick since it copies all of 'source' to 'dest', including the main Array.
196  // The next bit of code will replace those values with the newly scaled ones.
197  Grid *result = new Grid(source);
198 
199  // Now load the transferred values; use Float64 as the new type of the result
200  // Grid Array.
201  result->get_array()->add_var_nocopy(new Float64(source.name()));
202  result->get_array()->set_value(data, length);
203  delete[] data;
204 
205  // FIXME result->set_send_p(true);
206  BESDEBUG("function", "function_linear_scale_worker() - Grid send_p: " << source.send_p() << endl);
207  BESDEBUG("function", "function_linear_scale_worker() - Grid Array send_p: " << source.get_array()->send_p() << endl);
208 
209  dest = result;
210  }
211  else if (bt->is_vector_type()) {
212  Array &source = dynamic_cast<Array&>(*bt);
213  // If the array is really a map, make sure to read using the Grid
214  // because of the HDF4 handler's odd behavior WRT dimensions.
215  if (source.get_parent() && source.get_parent()->type() == dods_grid_c) {
216  source.get_parent()->set_send_p(true);
217  source.get_parent()->read();
218  }
219  else
220  source.read();
221 
222  data = extract_double_array(&source);
223  int length = source.length();
224  for (int i = 0; i < length; ++i)
225  data[i] = data[i] * m + b;
226 
227  Array *result = new Array(source);
228 
229  result->add_var_nocopy(new Float64(source.name()));
230  result->set_value(data, length);
231 
232  delete[] data; // val2buf copies.
233 
234  dest = result;
235  }
236  else if (bt->is_simple_type() && !(bt->type() == dods_str_c || bt->type() == dods_url_c)) {
237  double data = extract_double_value(bt);
238  if (!use_missing || !double_eq(data, missing))
239  data = data * m + b;
240 
241  Float64 *fdest = new Float64(bt->name());
242 
243  fdest->set_value(data);
244  // dest->val2buf(static_cast<void*> (&data));
245  dest = fdest;
246  } else {
247  throw Error(malformed_expr,"The linear_scale() function works only for numeric Grids, Arrays and scalars.");
248  }
249 
250  return dest;
251 
252 }
253 
254 
255 
268 void
269 function_dap2_linear_scale(int argc, BaseType * argv[], DDS &, BaseType **btpp)
270 {
271 
272  if (argc == 0) {
273  Str *response = new Str("info");
274  response->set_value(linear_scale_info);
275  *btpp = response;
276  return;
277  }
278 
279  // Check for 1 or 3 arguments: 1 --> use attributes; 3 --> m & b supplied
280  DBG(cerr << "argc = " << argc << endl);
281  if (!(argc == 1 || argc == 3 || argc == 4))
282  throw Error(malformed_expr,"Wrong number of arguments to linear_scale(). See linear_scale() for more information");
283 
284  // Get m & b
285  bool use_missing = false;
286  double m, b, missing = 0.0;
287  if (argc == 4) {
288  m = extract_double_value(argv[1]);
289  b = extract_double_value(argv[2]);
290  missing = extract_double_value(argv[3]);
291  use_missing = true;
292  } else if (argc == 3) {
293  m = extract_double_value(argv[1]);
294  b = extract_double_value(argv[2]);
295  use_missing = false;
296  } else {
297  m = get_slope(argv[0]);
298 
299  // This is really a hack; on a fair number of datasets, the y intercept
300  // is not given and is assumed to be 0. Here the function looks and
301  // catches the error if a y intercept is not found.
302  try {
303  b = get_y_intercept(argv[0]);
304  }
305  catch (Error &e) {
306  b = 0.0;
307  }
308 
309  // This is not the best plan; the get_missing_value() function should
310  // do something other than throw, but to do that would require mayor
311  // surgery on get_attribute_double_value().
312  try {
313  missing = get_missing_value(argv[0]);
314  use_missing = true;
315  }
316  catch (Error &e) {
317  use_missing = false;
318  }
319  }
320 
321  BESDEBUG("function", "function_dap2_linear_scale() - m: " << m << ", b: " << b << ", use_missing: " << use_missing << ", missing: " << missing << endl);
322 
323  *btpp = function_linear_scale_worker(argv[0],m,b,missing,use_missing);
324 }
337 BaseType *function_dap4_linear_scale(D4RValueList *args, DMR &dmr)
338 {
339  BESDEBUG("function", "function_dap4_linear_scale() BEGIN " << endl);
340 
341  // DAP4 function porting information: in place of 'argc' use 'args.size()'
342  if (args==0 || args->size() == 0) {
343  Str *response = new Str("info");
344  response->set_value(linear_scale_info);
345  // DAP4 function porting: return a BaseType* instead of using the value-result parameter
346  return response;
347  }
348 
349 
350  // Check for 2 arguments
351  DBG(cerr << "args.size() = " << args.size() << endl);
352  if (!(args->size() == 1 || args->size() == 3 || args->size() == 4))
353  throw Error(malformed_expr,"Wrong number of arguments to linear_scale(). See linear_scale() for more information");
354 
355  // Get m & b
356  bool use_missing = false;
357  double m, b, missing = 0.0;
358  if (args->size() == 4) {
359  m = extract_double_value(args->get_rvalue(1)->value(dmr));
360  b = extract_double_value(args->get_rvalue(2)->value(dmr));
361  missing = extract_double_value(args->get_rvalue(3)->value(dmr));
362  use_missing = true;
363  } else if (args->size() == 3) {
364  m = extract_double_value(args->get_rvalue(1)->value(dmr));
365  b = extract_double_value(args->get_rvalue(2)->value(dmr));
366  use_missing = false;
367  } else {
368  m = get_slope(args->get_rvalue(0)->value(dmr));
369 
370  // This is really a hack; on a fair number of datasets, the y intercept
371  // is not given and is assumed to be 0. Here the function looks and
372  // catches the error if a y intercept is not found.
373  try {
374  b = get_y_intercept(args->get_rvalue(0)->value(dmr));
375  }
376  catch (Error &e) {
377  b = 0.0;
378  }
379 
380  // This is not the best plan; the get_missing_value() function should
381  // do something other than throw, but to do that would require mayor
382  // surgery on get_attribute_double_value().
383  try {
384  missing = get_missing_value(args->get_rvalue(0)->value(dmr));
385  use_missing = true;
386  }
387  catch (Error &e) {
388  use_missing = false;
389  }
390  }
391  BESDEBUG("function", "function_dap4_linear_scale() - m: " << m << ", b: " << b << ", use_missing: " << use_missing << ", missing: " << missing << endl);
392 
393  BESDEBUG("function", "function_dap4_linear_scale() END " << endl);
394 
395  return function_linear_scale_worker(args->get_rvalue(0)->value(dmr),m,b,missing,use_missing);
396 
397 }
398 
399 } // namesspace libdap
BaseType * function_dap4_linear_scale(D4RValueList *args, DMR &dmr)
Given a BaseType, scale it using 'y = mx + b'.
BaseType * function_linear_scale_worker(BaseType *bt, double m, double b, double missing, bool use_missing)
static class NCMLUtil overview
string linear_scale_info
#define BESDEBUG(x, y)
macro used to send debug information to the debug stream
Definition: BESDebug.h:64
void function_dap2_linear_scale(int argc, BaseType *argv[], DDS &, BaseType **btpp)
Given a BaseType, scale it using 'y = mx + b'.