bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
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 <libdap/BaseType.h>
31#include <libdap/Float64.h>
32#include <libdap/Byte.h>
33#include <libdap/Str.h>
34#include <libdap/Structure.h>
35#include <libdap/Array.h>
36#include <libdap/Grid.h>
37#include <libdap/D4RValue.h>
38
39#include <libdap/Error.h>
40#include <libdap/DDS.h>
41
42//#include <libdap/dods-limits.h>
43#include <libdap/debug.h>
44#include <libdap/util.h>
45
46#include "BESDebug.h"
47
48#include "RangeFunction.h"
49
50using namespace libdap;
51
52namespace functions {
53
54string 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
68static 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
83Not Used
84
94static 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.size() - 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
135static 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
154static double get_missing_value(BaseType *var)
155{
156 return get_attribute_double_value(var, "missing_value");
157}
158
168min_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
245BaseType *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
334void 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
387BaseType *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