OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
BBoxFunction.cc
Go to the documentation of this file.
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_utils.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 
58 namespace libdap {
59 
84 void
85 function_dap2_bbox(int argc, BaseType *argv[], DDS &, BaseType **btpp)
86 {
87  const string wrong_args = "Wrong number of arguments to bbox(). Expected an Array and minimum and maximum values (3 arguments)";
88 
89  switch (argc) {
90  case 0:
91  throw Error(malformed_expr, wrong_args);
92  case 3:
93  // correct number of args
94  break;
95  default:
96  throw Error(malformed_expr, wrong_args);
97  }
98 
99  if (argv[0] && argv[0]->type() != dods_array_c)
100  throw Error("In function bbox(): Expected argument 1 to be an Array.");
101  if (!argv[0]->var()->is_simple_type() || argv[0]->var()->type() == dods_str_c || argv[0]->var()->type() == dods_url_c)
102  throw Error("In function bbox(): Expected argument 1 to be an Array of numeric types.");
103 
104  // cast is safe given the above
105  Array *the_array = static_cast<Array*>(argv[0]);
106  BESDEBUG("bbox", "the_array: " << the_array->name() << ": " << (void*)the_array << endl);
107 
108  // Read the variable into memory
109  the_array->read();
110  the_array->set_read_p(true);
111 
112  // Get the values as doubles
113  vector<double> the_values;
114  extract_double_array(the_array, the_values); // This function sets the size of the_values
115 
116  double min_value = extract_double_value(argv[1]);
117  double max_value = extract_double_value(argv[2]);
118 
119  // Build the response
120  unsigned int rank = the_array->dimensions();
121  auto_ptr<Array> response = roi_bbox_build_empty_bbox(rank, the_array->name());
122 
123  switch (rank) {
124  case 1:
125 #if UNWIND_BBOX_CODE
126  {
127  unsigned int X = the_array->dimension_size(the_array->dim_begin());
128 
129  bool found_start = false;
130  unsigned int start = 0;
131  for (unsigned int i = 0; i < X && !found_start; ++i) {
132  if (the_values[i] >= min_value && the_values[i] <= max_value) {
133  start = i;
134  found_start = true;
135  }
136  }
137 
138  // ! found_start == error?
139  if (!found_start) {
140  ostringstream oss("In function bbox(): No values between ", std::ios::ate);
141  oss << min_value << " and " << max_value << " were found in the array '" << the_array->name() << "'";
142  throw Error(oss.str());
143  }
144 
145  bool found_stop = false;
146  unsigned int stop = X-1;
147  for (int i = X - 1; i >= 0 && !found_stop; --i) {
148  if (the_values[i] >= min_value && the_values[i] <= max_value) {
149  stop = (unsigned int)i;
150  found_stop = true;
151  }
152  }
153 
154  // ! found_stop == error?
155  if (!found_stop)
156  throw InternalErr(__FILE__, __LINE__, "In BBoxFunction: Found start but not stop.");
157 
158  Structure *slice = roi_bbox_build_slice(start, stop, the_array->dimension_name(the_array->dim_begin()));
159  response->set_vec_nocopy(0, slice);
160  break;
161  }
162 #endif
163  case 2:
164 #if UNWIND_BBOX_CODE
165  {
166  // quick reminder: rows == y == j; cols == x == i
167  Array::Dim_iter rows = the_array->dim_begin(), cols = the_array->dim_begin()+1;
168  unsigned int Y = the_array->dimension_size(rows);
169  unsigned int X = the_array->dimension_size(cols);
170 
171  unsigned int x_start = X-1; //= 0;
172  unsigned int y_start = 0;
173  bool found_y_start = false;
174  // Must look at all rows to find the 'left-most' col with value
175  for (unsigned int j = 0; j < Y; ++j) {
176  bool found_x_start = false;
177 
178  for (unsigned int i = 0; i < X && !found_x_start; ++i) {
179  unsigned int ind = j * X + i;
180  if (the_values[ind] >= min_value && the_values[ind] <= max_value) {
181  x_start = min(i, x_start);
182  found_x_start = true;
183  if (!found_y_start) {
184  y_start = j;
185  found_y_start = true;
186  }
187  }
188  }
189  }
190 
191  // ! found_y_start == error?
192  if (!found_y_start) {
193  ostringstream oss("In function bbox(): No values between ", std::ios::ate);
194  oss << min_value << " and " << max_value << " were found in the array '" << the_array->name() << "'";
195  throw Error(oss.str());
196  }
197 
198  unsigned int x_stop = 0;
199  unsigned int y_stop = 0;
200  bool found_y_stop = false;
201  // Must look at all rows to find the 'left-most' col with value
202  for (int j = Y - 1; j >= (int)y_start; --j) {
203  bool found_x_stop = false;
204 
205  for (int i = X - 1; i >= 0 && !found_x_stop; --i) {
206  unsigned int ind = j * X + i;
207  if (the_values[ind] >= min_value && the_values[ind] <= max_value) {
208  x_stop = max((unsigned int)i, x_stop);
209  found_x_stop = true;
210  if (!found_y_stop) {
211  y_stop = j;
212  found_y_stop = true;
213  }
214  }
215  }
216  }
217 
218  // ! found_stop == error?
219  if (!found_y_stop)
220  throw InternalErr(__FILE__, __LINE__, "In BBoxFunction: Found start but not stop.");
221 
222  response->set_vec_nocopy(0, roi_bbox_build_slice(y_start, y_stop, the_array->dimension_name(rows)));
223  response->set_vec_nocopy(1, roi_bbox_build_slice(x_start, x_stop, the_array->dimension_name(cols)));
224  break;
225  }
226 #endif
227  default: {
228  Odometer::shape shape(rank); // the shape of 'the_array'
229  int j = 0;
230  for (Array::Dim_iter i = the_array->dim_begin(), e = the_array->dim_end(); i != e; ++i) {
231  shape.at(j++) = the_array->dimension_size(i);
232  }
233  Odometer odometer(shape);
234 
235  Odometer::shape indices(rank); // Holds a given index
236  Odometer::shape min = shape; // Holds the minimum values for each of rank dimensions
237  Odometer::shape max(rank, 0); // ... and the maximum. min and max define the bounding box
238  // NB: shape is initialized with the size of the array
239  do {
240  if (the_values[odometer.offset()] >= min_value && the_values[odometer.offset()] <= max_value) {
241  // record this index
242  odometer.indices(indices);
243  Odometer::shape::iterator m = min.begin();
244  Odometer::shape::iterator x = max.begin();
245 
246  for (Odometer::shape::iterator i = indices.begin(), e = indices.end(); i != e; ++i, ++m, ++x) {
247  if (*i < *m) *m = *i;
248  if (*i > *x) *x = *i;
249  }
250  }
251  } while (odometer.next() != odometer.end());
252 
253  // cheap test for 'did we find any values.' If we did, then the
254  // min index will have to be less than the shape (which is the
255  // size of the array). We only need to test one of the indices.
256  if (min[0] == shape[0]) {
257  ostringstream oss("In function bbox(): No values between ", std::ios::ate);
258  oss << min_value << " and " << max_value << " were found in the array '" << the_array->name() << "'";
259  throw Error(oss.str());
260  }
261 
262  Odometer::shape::iterator m = min.begin();
263  Odometer::shape::iterator x = max.begin();
264  Array::Dim_iter d = the_array->dim_begin();
265  for (unsigned int i = 0; i < rank; ++i, ++m, ++x, ++d) {
266  response->set_vec_nocopy(i, roi_bbox_build_slice(*m, *x, the_array->dimension_name(d)));
267  }
268  break;
269  } // default
270  } // switch
271 
272  response->set_read_p(true);
273  response->set_send_p(true);
274 
275  *btpp = response.release();
276  return;
277 }
278 
290 BaseType *function_dap4_bbox(D4RValueList * /* args */, DMR & /* dmr */)
291 {
292  throw Error(malformed_expr, "Not yet implemented for DAP4 functions.");
293 
294  return 0; //response.release();
295 }
296 
297 } // namesspace libdap
void indices(shape &indices)
Return the current set of indices.
Definition: Odometer.h:134
std::vector< unsigned int > shape
Definition: Odometer.h:41
STL namespace.
unsigned int end()
Return the sentinel value that indicates that the offset (returned by offset()) is at the end of the ...
Definition: Odometer.h:154
Structure * roi_bbox_build_slice(unsigned int start_value, unsigned int stop_value, const string &dim_name)
Build a single element of a bounding box.
Definition: roi_utils.cc:153
static class NCMLUtil overview
BaseType * function_dap4_bbox(D4RValueList *, DMR &)
Return the bounding box for an array.
auto_ptr< Array > roi_bbox_build_empty_bbox(unsigned int num_dim, const string &bbox_name)
Build an empty Bounding Box using DAP variables.
Definition: roi_utils.cc:192
void function_dap2_bbox(int argc, BaseType *argv[], DDS &, BaseType **btpp)
Return the bounding box for an array.
Definition: BBoxFunction.cc:85
#define min(a, b)
Definition: os_utils.h:71
unsigned int offset()
The offset into memory for the current element.
Definition: Odometer.h:142
#define BESDEBUG(x, y)
macro used to send debug information to the debug stream
Definition: BESDebug.h:64
Map the indices of a N-dimensional array to the offset into memory that matches those indices...
Definition: Odometer.h:38
#define max(a, b)
Definition: os_utils.h:67
unsigned int next()
Increment the Odometer to the next element and return the offset value.
Definition: Odometer.h:107