bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
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 <libdap/BaseType.h>
33#include <libdap/Int32.h>
34#include <libdap/Str.h>
35#include <libdap/Array.h>
36#include <libdap/Structure.h>
37
38#include <libdap/D4RValue.h>
39#include <libdap/Error.h>
40#include <libdap/debug.h>
41#include <libdap/util.h>
42#include <libdap/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
56using namespace std;
57using namespace libdap;
58
59namespace functions {
60
61unique_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
228void
229function_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
277BaseType *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