bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
MakeMaskFunction.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) 2015 OPeNDAP, Inc.
7// Authors: Dan Holloway <dholloway@opendap.org>
8//
9// This library is free software; you can redistribute it and/or
10// modify it under the terms of the GNU Lesser General Public
11// License as published by the Free Software Foundation; either
12// version 2.1 of the License, or (at your option) any later version.
13//
14// This library is distributed in the hope that it will be useful,
15// but WITHOUT ANY WARRANTY; without even the implied warranty of
16// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17// Lesser General Public License for more details.
18//
19// You should have received a copy of the GNU Lesser General Public
20// License along with this library; if not, write to the Free Software
21// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22//
23// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
24
25#include "config.h"
26
27//#define DODS_DEBUG 1
28
29#include <cassert>
30
31#include <sstream>
32#include <vector>
33#include <algorithm>
34
35#include <libdap/Type.h>
36#include <libdap/BaseType.h>
37#include <libdap/Byte.h>
38#include <libdap/Str.h>
39
40#include <libdap/Array.h>
41#include <libdap/Error.h>
42#include <libdap/DDS.h>
43
44#if 0
45// No DAP4 support yet...
46#include <libdap/DMR.h>
47#include <libdap/D4Group.h>
48#include <libdap/D4RValue.h>
49#endif
50
51#include <libdap/debug.h>
52#include <libdap/util.h>
53
54#include <BESDebug.h>
55
56#include "MakeMaskFunction.h"
57#include "Odometer.h"
58#include "functions_util.h"
59
60using namespace libdap;
61using namespace std;
62
63namespace functions {
64
65vector<int> parse_dims(const string &shape); // defined in MakeArrayFunction.cc
66
67string make_mask_info =
68 string("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
69 + "<function name=\"make_array\" version=\"1.0\" href=\"http://docs.opendap.org/index.php/Server_Side_Processing_Functions#make_mask\">\n"
70 + "</function>";
71
79int
80find_value_index(double value, const vector<double> &map)
81{
82 // If C++ hadn't borked passing functions to stl algorithms, we could use...
83 //vector<double>::iterator loc = find_if(map.begin(), map.end(), bind2nd(ptr_fun(double_eq), value));
84
85 for (vector<double>::const_iterator i = map.begin(), e = map.end(); i != e; ++i) {
86 if (double_eq(*i, value, 0.1)) { // FIXME Hack: 0.1 epsilon is a hack. jhrg 5/25/15
87 return i - map.begin(); // there's an official iterator diff function somewhere...
88 }
89 }
90
91 return -1;
92}
93
105vector<int>
106find_value_indices(const vector<double> &values, const vector< vector<double> > &maps)
107{
108 assert(values.size() == maps.size());
109
110 vector<int> indices;
111 vector <vector<double> >::const_iterator m = maps.begin();
112 for (vector<double>::const_iterator d = values.begin(), e = values.end(); d != e; ++d) {
113 indices.push_back(find_value_index(*d, *m++));
114 }
115
116 return indices;
117}
118
124bool all_indices_valid(vector<int> indices)
125{
126 return find(indices.begin(), indices.end(), -1) == indices.end();
127}
128
142template<typename T>
143void make_mask_helper(const vector<Array*> dims, Array *tuples, vector<dods_byte> &mask)
144{
145 vector< vector<double> > dim_value_vecs(dims.size());
146 int i = 0; // index the dim_value_vecs vector of vectors;
147 for (vector<Array*>::const_iterator d = dims.begin(), e = dims.end(); d != e; ++d) {
148 // This version of extract...() takes the vector<double> by reference:
149 // In util.cc/h: void extract_double_array(Array *a, vector<double> &dest)
150 extract_double_array(*d, dim_value_vecs.at(i++));
151 }
152
153 // Construct and Odometer used to calculate offsets
154 Odometer::shape shape(dims.size());
155
156 int j = 0; // index the shape vector for an Odometer;
157 for (vector<Array*>::const_iterator d = dims.begin(), e = dims.end(); d != e; ++d) {
158 shape[j++] = (*d)->length();
159 }
160
161 Odometer odometer(shape);
162
163 // Copy the 'tuple' data to a simple vector<T>
164 vector<T> data(tuples->length());
165 tuples->value(data.data());
166
167 // Iterate over the tuples, searching the dimensions for their values
168 int nDims = dims.size();
169 int nTuples = data.size() / nDims;
170
171 // NB: 'data' holds the tuple values
172
173 // unsigned int tuple_offset = 0; // an optimization...
174 for (int n = 0; n < nTuples; ++n) {
175 vector<double> tuple(nDims);
176 // Build the next tuple
177 for (int dim = 0; dim < nDims; ++dim) {
178 // could replace 'tuple * nDims' with 'tuple_offset'
179 tuple[dim] = data[n * nDims + dim];
180 }
181
182 DBG(cerr << "tuple: ");
183 DBGN(copy(tuple.begin(), tuple.end(), ostream_iterator<int>(std::cerr, " ")));
184 DBGN(cerr << endl);
185
186 // find indices for tuple-values in the specified
187 // target-grid dimensions
188 vector<int> indices = find_value_indices(tuple, dim_value_vecs);
189 DBG(cerr << "indices: ");
190 DBGN(copy(indices.begin(), indices.end(), ostream_iterator<int>(std::cerr, " ")));
191 DBGN(cerr << endl);
192
193 // if all of the indices are >= 0, then add this point to the mask
194 if (all_indices_valid(indices)) {
195
196 // Pass identified indices to Odometer, it will automatically
197 // calculate offset within defined 'shape' using those index values.
198 // Result of set_indices() will update d_offset value, accessible
199 // using the Odometer::offset() accessor.
200 odometer.set_indices(indices);
201 DBG(cerr << "odometer.offset(): " << odometer.offset() << endl);
202 mask[odometer.offset()] = 1;
203 }
204 }
205}
206
216void function_dap2_make_mask(int argc, BaseType * argv[], DDS &, BaseType **btpp)
217{
218 if (argc == 0) {
219 Str *response = new Str("info");
220 response->set_value(make_mask_info);
221 *btpp = response;
222 return;
223 }
224
225 // Check for three args or more. The first two must be strings.
226 DBG(cerr << "argc = " << argc << endl);
227 if (argc < 3)
228 throw Error(malformed_expr,
229 "make_mask(shape_string,[dim1,...],$TYPE(dim1_value0,dim2_value0,...)) requires at least four arguments.");
230
231 if (argv[0]->type() != dods_str_c)
232 throw Error(malformed_expr, "make_mask(): first argument must point to a string variable.");
233
234 string shape_str = extract_string_argument(argv[0]);
235 vector<int> shape = parse_dims(shape_str);
236
237 // Create the 'mask' array using the shape of the target grid variable's array.
238 int length = 1;
239 for (vector<int>::iterator i = shape.begin(), e = shape.end(); i != e; ++i)
240 length *= *i;
241 vector<dods_byte> mask(length, 0); // Create 'mask', initialized with zero's
242 unsigned int nDims = shape.size();
243
244 // read argv[1] -> argv[1+numberOfDims]; the grid dimensions where we will find the values
245 // of the mask tuples. Also note that the number of dims (shape.size() above) should be the
246 // same as argc-2.
247 assert(nDims == (unsigned int)argc-2);
248
249 vector<Array*> dims;
250 for (unsigned int i = 0; i < nDims; i++) {
251
252 BaseType *btp = argv[1 + i];
253 if (btp->type() != dods_array_c) {
254 throw Error(malformed_expr,
255 "make_mask(): dimension-name arguments must point to Grid variable dimensions.");
256 }
257
258 Array *a = static_cast<Array*>(btp);
259
260 // Check that each map size matches the 'shape' info passed in the first arg.
261 // This might not be the case for DAP4 (or if we change this to support level
262 // 2 swath data).
263 assert(a->dimension_size(a->dim_begin()) == shape.at(i));
264
265 a->read();
266 a->set_read_p(true);
267 dims.push_back(a);
268 }
269
270 BaseType *btp = argv[argc - 1];
271 if (btp->type() != dods_array_c) {
272 throw Error(malformed_expr, "make_mask(): last argument must be an array.");
273 }
274
275 check_number_type_array(btp); // Throws an exception if not a numeric type.
276
277 Array *tuples = static_cast<Array*>(btp);
278
279 switch (tuples->var()->type()) {
280 // All mask values are stored in Byte DAP variables by the stock argument parser
281 // except values too large; those are stored in a UInt32
282 case dods_byte_c:
283 make_mask_helper<dods_byte>(dims, tuples, mask);
284 break;
285
286 case dods_int16_c:
287 make_mask_helper<dods_int16>(dims, tuples, mask);
288 break;
289
290 case dods_uint16_c:
291 make_mask_helper<dods_uint16>(dims, tuples, mask);
292 break;
293
294 case dods_int32_c:
295 make_mask_helper<dods_int32>(dims, tuples, mask);
296 break;
297
298 case dods_uint32_c:
299 make_mask_helper<dods_uint32>(dims, tuples, mask);
300 break;
301
302 case dods_float32_c:
303 make_mask_helper<dods_float32>(dims, tuples, mask);
304 break;
305
306 case dods_float64_c:
307 make_mask_helper<dods_float64>(dims, tuples, mask);
308 break;
309
310 case dods_str_c:
311 case dods_url_c:
312 default:
313 throw InternalErr(__FILE__, __LINE__,
314 "make_mask(): Expect an array of mask points (numbers) but found something else instead.");
315 }
316
317 Array *dest = new Array("mask", 0); // The ctor for Array copies the prototype pointer...
318 BaseTypeFactory btf;
319 dest->add_var_nocopy(new Byte("mask")); // ... so use add_var_nocopy() to add it instead
320
321 for (vector<int>::iterator i = shape.begin(), e = shape.end(); i != e; ++i)
322 dest->append_dim(*i);
323
324 dest->set_value(mask, length);
325
326 dest->set_read_p(true);
327
328 *btpp = dest;
329}
330
331} // namespace functions