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