bes  Updated for version 3.20.6
RangeFunction.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 <Byte.h>
33 #include <Str.h>
34 #include <Structure.h>
35 #include <Array.h>
36 #include <Grid.h>
37 #include "D4RValue.h"
38 
39 #include <Error.h>
40 #include <DDS.h>
41 
42 //#include <dods-limits.h>
43 #include <debug.h>
44 #include <util.h>
45 
46 #include "BESDebug.h"
47 
48 #include "RangeFunction.h"
49 
50 using namespace libdap;
51 
52 namespace functions {
53 
54 string range_info =
55  string("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
56  + "<function name=\"linear_scale\" version=\"1.0b1\" href=\"http://docs.opendap.org/index.php/Server_Side_Processing_Functions#range\">\n"
57  + "</function>";
58 
59 // These static functions could be moved to a class that provides a more
60 // general interface for COARDS/CF someday. Assume each BaseType comes bundled
61 // with an attribute table.
62 
68 static double string_to_double(const char *val)
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 
81 #if 0
82 
83 Not Used
84 
94 static double get_attribute_double_value(BaseType *var, vector<string> &attributes)
95 {
96  // This code also builds a list of the attribute values that have been
97  // passed in but not found so that an informative message can be returned.
98  AttrTable &attr = var->get_attr_table();
99  string attribute_value = "";
100  string values = "";
101  vector<string>::iterator i = attributes.begin();
102  while (attribute_value == "" && i != attributes.end()) {
103  values += *i;
104  if (!values.empty()) values += ", ";
105  attribute_value = attr.get_attr(*i++);
106  }
107 
108  // If the value string is empty, then look at the grid's array (if it's a
109  // grid) or throw an Error.
110  if (attribute_value.empty()) {
111  if (var->type() == dods_grid_c)
112  return get_attribute_double_value(dynamic_cast<Grid&>(*var).get_array(), attributes);
113  else
114  throw Error(malformed_expr,
115  string("No COARDS/CF '") + values.substr(0, values.length() - 2)
116  + "' attribute was found for the variable '" + var->name() + "'.");
117  }
118 
119  return string_to_double(remove_quotes(attribute_value).c_str());
120 }
121 #endif
122 
135 static double get_attribute_double_value(BaseType *var, const string &attribute)
136 {
137  AttrTable &attr = var->get_attr_table();
138  string attribute_value = attr.get_attr(attribute);
139 
140  // If the value string is empty, then look at the grid's array (if it's a
141  // grid or throw an Error.
142  if (attribute_value.empty()) {
143  if (var->type() == dods_grid_c)
144  return get_attribute_double_value(dynamic_cast<Grid&>(*var).get_array(), attribute);
145  else
146  throw Error(malformed_expr,
147  string("No COARDS '") + attribute + "' attribute was found for the variable '" + var->name()
148  + "'.");
149  }
150 
151  return string_to_double(remove_quotes(attribute_value).c_str());
152 }
153 
154 static double get_missing_value(BaseType *var)
155 {
156  return get_attribute_double_value(var, "missing_value");
157 }
158 
168 min_max_t find_min_max(double* data, int length, bool use_missing, double missing)
169 {
170  min_max_t v;
171  double previous_value;
172  bool increasing, previously_increasing;
173 
174  v.monotonic = true;
175  previous_value = data[0];
176  if (use_missing) {
177  for (int i = 0; i < length; ++i) {
178  if (!double_eq(data[i], missing)) {
179  if(v.monotonic && i>0){
180  increasing = (data[i] - previous_value) > 0.0;
181  if(i>1){
182  if(previously_increasing!=increasing){
183  v.monotonic = false;
184  }
185  }
186  previously_increasing = increasing;
187  previous_value = data[i];
188  }
189  v.max_val = max(v.max_val, data[i]);
190  v.min_val = min(v.min_val, data[i]);
191  }
192  }
193  }
194  else {
195  for (int i = 0; i < length; ++i) {
196  if(v.monotonic && i>0){
197  increasing = (data[i] - previous_value) > 0.0;
198  if(i>1){
199  if(previously_increasing!=increasing){
200  v.monotonic = false;
201  }
202  }
203  previously_increasing = increasing;
204  previous_value = data[i];
205  }
206  v.max_val = max(v.max_val, data[i]);
207  v.min_val = min(v.min_val, data[i]);
208  }
209  }
210 
211 #if 0
212  for (int i = 0; i < length; ++i) {
213  if (!use_missing || !double_eq(data[i], missing)) {
214  if(v.monotonic && i>0){
215  increasing = (data[i] - previous_value) > 0.0;
216  if(i>1){
217  if(previously_increasing!=increasing){
218  v.monotonic = false;
219  }
220  }
221  previously_increasing = increasing;
222  previous_value = data[i];
223  }
224  v.max_val = max(v.max_val, data[i]);
225  v.min_val = min(v.min_val, data[i]);
226  }
227  }
228 #endif
229  return v;
230 }
231 
232 // TODO Modify this to include information about monotonicity of vectors.
233 // That will be useful for geo operations when we use this to look at lat
234 // and lon extent.
235 
245 BaseType *range_worker(BaseType *bt, double missing, bool use_missing)
246 {
247  // Read the data, determine range and return the result. Must replace the new data
248  // in a constructor (i.e., Array part of a Grid).
249 
250  min_max_t v;
251 
252  if (bt->type() == dods_grid_c) {
253  // Grab the whole Grid; note that the scaling is done only on the array part
254  Grid &source = dynamic_cast<Grid&>(*bt);
255 
256  BESDEBUG("function", "range_worker() - Grid send_p: " << source.send_p() << endl);
257  BESDEBUG("function", "range_worker() - Grid Array send_p: " << source.get_array()->send_p() << endl);
258 
259  // Read the grid; set send_p since Grid is a kind of constructor and
260  // read will only be called on it's fields if their send_p flag is set
261  source.set_send_p(true);
262  source.read();
263 
264  // Get the Array part and read the values
265  Array *a = source.get_array();
266  double *data = extract_double_array(a);
267 
268  // Now determine the range.
269  int length = a->length();
270 
271  v = find_min_max(data, length, use_missing, missing);
272 
273  delete[] data;
274  }
275  else if (bt->is_vector_type()) {
276  Array &source = dynamic_cast<Array&>(*bt);
277  // If the array is really a map, make sure to read using the Grid
278  // because of the HDF4 handler's odd behavior WRT dimensions.
279  if (source.get_parent() && source.get_parent()->type() == dods_grid_c) {
280  source.get_parent()->set_send_p(true);
281  source.get_parent()->read();
282  }
283  else
284  source.read();
285 
286  double *data = extract_double_array(&source);
287 
288  // Now determine the range.
289  int length = source.length();
290 
291  v = find_min_max(data, length, use_missing, missing);
292 
293  delete[] data;
294  }
295  else if (bt->is_simple_type() && !(bt->type() == dods_str_c || bt->type() == dods_url_c)) {
296  double data = extract_double_value(bt);
297  v.max_val = data;
298  v.min_val = data;
299  }
300  else {
301  throw Error(malformed_expr, "The range_worker() function works only for numeric Grids, Arrays and scalars.");
302  }
303 
304  // TODO Move this down to the dap2/4 versions?
305  Structure *rangeResult = new Structure("range_result_unwrap");
306 
307  Float64 *rangeMin = new Float64("min");
308  rangeMin->set_value(v.min_val);
309  rangeResult->add_var_nocopy(rangeMin);
310 
311  Float64 *rangeMax = new Float64("max");
312  rangeMax->set_value(v.max_val);
313  rangeResult->add_var_nocopy(rangeMax);
314 
315  Byte *is_monotonic = new Byte("is_monotonic");
316  is_monotonic->set_value(v.monotonic);
317  rangeResult->add_var_nocopy(is_monotonic);
318 
319  return rangeResult;
320 }
321 
334 void function_dap2_range(int argc, BaseType * argv[], DDS &, BaseType **btpp)
335 {
336  if (argc == 0) {
337  Str *response = new Str("info");
338  response->set_value(range_info);
339  *btpp = response;
340  return;
341  }
342 
343  // Check for 1 or 2 arguments: 1 --> use attributes; 2 --> use missing value
344  DBG(cerr << "argc = " << argc << endl);
345  if (!(argc == 1 || argc == 2 ))
346  throw Error(malformed_expr,
347  "Wrong number of arguments to range(). See range() for more information");
348 
349  // Get m & b
350  bool use_missing = false;
351  double missing = 0.0;
352  if (argc == 2) {
353  missing = extract_double_value(argv[1]);
354  use_missing = true;
355  }
356  else {
357  // This is not the best plan; the get_missing_value() function should
358  // do something other than throw, but to do that would require mayor
359  // surgery on get_attribute_double_value().
360  try {
361  missing = get_missing_value(argv[0]);
362  use_missing = true;
363  }
364  catch (Error &) { // Ignore the libdap::Error thrown (but not other errors). jhrg 6/6/17
365  use_missing = false;
366  }
367  }
368 
369  BESDEBUG("function",
370  "function_dap2_range() - use_missing: " << use_missing << ", missing: " << missing << endl);
371 
372  *btpp = range_worker(argv[0], missing, use_missing);
373 }
374 
387 BaseType *function_dap4_range(D4RValueList *args, DMR &dmr)
388 {
389  BESDEBUG("function", "function_dap4_range() BEGIN " << endl);
390 
391  // DAP4 function porting information: in place of 'argc' use 'args.size()'
392  if (args == 0 || args->size() == 0) {
393  Str *response = new Str("info");
394  response->set_value(range_info);
395  // DAP4 function porting: return a BaseType* instead of using the value-result parameter
396  return response;
397  }
398 
399  // Check for 2 arguments
400  DBG(cerr << "args.size() = " << args.size() << endl);
401  if (!(args->size() == 1 || args->size() == 2))
402  throw Error(malformed_expr,
403  "Wrong number of arguments to linear_scale(). See linear_scale() for more information");
404 
405  // Get m & b
406  bool use_missing = false;
407  double missing = 0.0;
408  if (args->size() == 2) {
409  missing = extract_double_value(args->get_rvalue(3)->value(dmr));
410  use_missing = true;
411  }
412  else {
413  try {
414  missing = get_missing_value(args->get_rvalue(0)->value(dmr));
415  use_missing = true;
416  }
417  catch (Error &) {
418  use_missing = false;
419  }
420  }
421  BESDEBUG("function",
422  "function_dap4_range() - use_missing: " << use_missing << ", missing: " << missing << endl);
423 
424  BESDEBUG("function", "function_dap4_range() END " << endl);
425 
426  return range_worker(args->get_rvalue(0)->value(dmr), missing, use_missing);
427 }
428 
429 } // namesspace functions
libdap
Definition: BESDapFunctionResponseCache.h:35
Error