bes  Updated for version 3.20.6
BBoxFunction.cc
1 
2 // -*- mode: c++; c-basic-offset:4 -*-
3 
4 // This file is part of bes, A C++ implementation of the OPeNDAP
5 // Hyrax data server
6 
7 // Copyright (c) 2015 OPeNDAP, Inc.
8 // Authors: 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 <cassert>
29 #include <sstream>
30 #include <memory>
31 
32 #include <BaseType.h>
33 #include <Int32.h>
34 #include <Str.h>
35 #include <Array.h>
36 #include <Structure.h>
37 
38 #include <D4RValue.h>
39 #include <Error.h>
40 #include <debug.h>
41 #include <util.h>
42 #include <ServerFunctionsList.h>
43 
44 #include <BESDebug.h>
45 
46 #include "BBoxFunction.h"
47 #include "Odometer.h"
48 #include "roi_util.h"
49 
50 // Set this to 1 to use special code for arrays of rank 1 and 2.
51 // set it to 0 (... comment out, etc.) to use the general code for
52 // all cases. I've run the unit and regression tests both ways.
53 // jhrg 3/2/15
54 #define UNWIND_BBOX_CODE 1
55 
56 using namespace std;
57 using namespace libdap;
58 
59 namespace functions {
60 
61 unique_ptr<Array> bbox_helper(double min_value, double max_value, Array* the_array)
62 {
63  // Get the values as doubles
64  vector<double> the_values;
65  extract_double_array(the_array, the_values); // This function sets the size of the_values
66 
67  // Build the response
68  unsigned int rank = the_array->dimensions();
69  unique_ptr<Array> response = roi_bbox_build_empty_bbox(rank, the_array->name());
70 
71  switch (rank) {
72  case 1: {
73  unsigned int X = the_array->dimension_size(the_array->dim_begin());
74  bool found_start = false;
75  unsigned int start = 0;
76  for (unsigned int i = 0; i < X && !found_start; ++i) {
77  if (the_values[i] >= min_value && the_values[i] <= max_value) {
78  start = i;
79  found_start = true;
80  }
81  }
82  // ! found_start == error?
83  if (!found_start) {
84  ostringstream oss("In function bbox(): No values between ", std::ios::ate);
85  oss << min_value << " and " << max_value << " were found in the array '" << the_array->name() << "'";
86  throw Error(oss.str());
87  }
88  bool found_stop = false;
89  unsigned int stop = X - 1;
90  for (int i = X - 1; i >= 0 && !found_stop; --i) {
91  if (the_values[i] >= min_value && the_values[i] <= max_value) {
92  stop = (unsigned int) (i);
93  found_stop = true;
94  }
95  }
96  // ! found_stop == error?
97  if (!found_stop) throw InternalErr(__FILE__, __LINE__, "In BBoxFunction: Found start but not stop.");
98 
99  Structure* slice = roi_bbox_build_slice(start, stop, the_array->dimension_name(the_array->dim_begin()));
100  response->set_vec_nocopy(0, slice);
101  break;
102  }
103  case 2: {
104  // quick reminder: rows == y == j; cols == x == i
105  Array::Dim_iter rows = the_array->dim_begin(), cols = the_array->dim_begin() + 1;
106  unsigned int Y = the_array->dimension_size(rows);
107  unsigned int X = the_array->dimension_size(cols);
108  unsigned int x_start = X - 1; //= 0;
109  unsigned int y_start = 0;
110  bool found_y_start = false;
111  // Must look at all rows to find the 'left-most' col with value
112  for (unsigned int j = 0; j < Y; ++j) {
113  bool found_x_start = false;
114  for (unsigned int i = 0; i < X && !found_x_start; ++i) {
115  unsigned int ind = j * X + i;
116  if (the_values[ind] >= min_value && the_values[ind] <= max_value) {
117  x_start = min(i, x_start);
118  found_x_start = true;
119  if (!found_y_start) {
120  y_start = j;
121  found_y_start = true;
122  }
123  }
124  }
125  }
126  // ! found_y_start == error?
127  if (!found_y_start) {
128  ostringstream oss("In function bbox(): No values between ", std::ios::ate);
129  oss << min_value << " and " << max_value << " were found in the array '" << the_array->name() << "'";
130  throw Error(oss.str());
131  }
132  unsigned int x_stop = 0;
133  unsigned int y_stop = 0;
134  bool found_y_stop = false;
135  // Must look at all rows to find the 'left-most' col with value
136  for (int j = Y - 1; j >= (int) (y_start); --j) {
137  bool found_x_stop = false;
138  for (int i = X - 1; i >= 0 && !found_x_stop; --i) {
139  unsigned int ind = j * X + i;
140  if (the_values[ind] >= min_value && the_values[ind] <= max_value) {
141  x_stop = max((unsigned int) (i), x_stop);
142  found_x_stop = true;
143  if (!found_y_stop) {
144  y_stop = j;
145  found_y_stop = true;
146  }
147  }
148  }
149  }
150  // ! found_stop == error?
151  if (!found_y_stop) throw InternalErr(__FILE__, __LINE__, "In BBoxFunction: Found start but not stop.");
152 
153  response->set_vec_nocopy(0, roi_bbox_build_slice(y_start, y_stop, the_array->dimension_name(rows)));
154  response->set_vec_nocopy(1, roi_bbox_build_slice(x_start, x_stop, the_array->dimension_name(cols)));
155  break;
156  }
157  default: {
158  Odometer::shape shape(rank); // the shape of 'the_array'
159  int j = 0;
160  for (Array::Dim_iter i = the_array->dim_begin(), e = the_array->dim_end(); i != e; ++i) {
161  shape.at(j++) = the_array->dimension_size(i);
162  }
163  Odometer odometer(shape);
164  Odometer::shape indices(rank); // Holds a given index
165  Odometer::shape min = shape; // Holds the minimum values for each of rank dimensions
166  Odometer::shape max(rank, 0); // ... and the maximum. min and max define the bounding box
167  // NB: shape is initialized with the size of the array
168  do {
169  if (the_values[odometer.offset()] >= min_value && the_values[odometer.offset()] <= max_value) {
170  // record this index
171  odometer.indices(indices);
172  Odometer::shape::iterator m = min.begin();
173  Odometer::shape::iterator x = max.begin();
174  for (Odometer::shape::iterator i = indices.begin(), e = indices.end(); i != e; ++i, ++m, ++x) {
175  if (*i < *m) *m = *i;
176 
177  if (*i > *x) *x = *i;
178  }
179  }
180  } while (odometer.next() != odometer.end());
181  // cheap test for 'did we find any values.' If we did, then the
182  // min index will have to be less than the shape (which is the
183  // size of the array). We only need to test one of the indices.
184  if (min[0] == shape[0]) {
185  ostringstream oss("In function bbox(): No values between ", std::ios::ate);
186  oss << min_value << " and " << max_value << " were found in the array '" << the_array->name() << "'";
187  throw Error(oss.str());
188  }
189  Odometer::shape::iterator m = min.begin();
190  Odometer::shape::iterator x = max.begin();
191  Array::Dim_iter d = the_array->dim_begin();
192  for (unsigned int i = 0; i < rank; ++i, ++m, ++x, ++d) {
193  response->set_vec_nocopy(i, roi_bbox_build_slice(*m, *x, the_array->dimension_name(d)));
194  }
195  break;
196  } // default
197  } // switch
198 
199  response->set_read_p(true);
200  response->set_send_p(true);
201  return response;
202 }
203 
228 void
229 function_dap2_bbox(int argc, BaseType *argv[], DDS &, BaseType **btpp)
230 {
231  const string wrong_args = "Wrong number of arguments to bbox(). Expected an Array and minimum and maximum values (3 arguments)";
232 
233  switch (argc) {
234  case 0:
235  throw Error(malformed_expr, wrong_args);
236  case 3:
237  // correct number of args
238  break;
239  default:
240  throw Error(malformed_expr, wrong_args);
241  }
242 
243  if (argv[0] && argv[0]->type() != dods_array_c)
244  throw Error("In function bbox(): Expected argument 1 to be an Array.");
245  if (!argv[0]->var()->is_simple_type() || argv[0]->var()->type() == dods_str_c || argv[0]->var()->type() == dods_url_c)
246  throw Error("In function bbox(): Expected argument 1 to be an Array of numeric types.");
247 
248  // cast is safe given the above
249  Array *the_array = static_cast<Array*>(argv[0]);
250  BESDEBUG("bbox", "the_array: " << the_array->name() << ": " << (void*)the_array << endl);
251 
252  // Read the variable into memory
253  the_array->read();
254  the_array->set_read_p(true);
255 
256  double min_value = extract_double_value(argv[1]);
257  double max_value = extract_double_value(argv[2]);
258 
259  // Get the values as doubles
260  unique_ptr<Array> response = bbox_helper(min_value, max_value, the_array);
261 
262  *btpp = response.release();
263  return;
264 }
265 
277 BaseType *function_dap4_bbox(D4RValueList * /* args */, DMR & /* dmr */)
278 {
279  throw Error(malformed_expr, "Not yet implemented for DAP4 functions.");
280 
281  return 0; //response.release();
282 }
283 
284 } // namesspace functions
libdap
Definition: BESDapFunctionResponseCache.h:35
Error