bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
TabularFunction.cc
1// -*- mode: c++; c-basic-offset:4 -*-
2
3// This file is part of bes, A C++ implementation of the OPeNDAP
4// Hyrax data server
5
6// Copyright (c) 2015 OPeNDAP, Inc.
7// Authors: James Gallagher <jgallagher@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#include <cassert>
28#include <climits>
29
30#include <sstream>
31#include <memory>
32#include <algorithm>
33
34#include <libdap/BaseType.h>
35#include <libdap/UInt32.h>
36#include <libdap/Array.h>
37#include <libdap/Sequence.h>
38#include <libdap/D4Sequence.h>
39#include <libdap/D4RValue.h>
40#include <libdap/Error.h>
41#include <libdap/debug.h>
42#include <libdap/util.h>
43#include <libdap/ServerFunctionsList.h>
44
45#include "TabularSequence.h"
46#include "TabularFunction.h"
47
48using namespace std;
49using namespace libdap;
50
51namespace functions {
52
59static void read_array_values(Array *a)
60{
61 a->read();
62 a->set_read_p(true);
63}
64
74TabularFunction::Shape TabularFunction::array_shape(Array *a)
75{
76 Shape shape;
77
78 for (Array::Dim_iter i = a->dim_begin(), e = a->dim_end(); i != e; ++i) {
79 shape.push_back(a->dimension_size(i));
80 }
81
82 return shape;
83}
84
93bool TabularFunction::shape_matches(Array *a, const Shape &shape)
94{
95 // Same number of dims
96 if (shape.size() != a->dimensions()) return false;
97
98 // Each dim the same size
99 Array::Dim_iter i = a->dim_begin(), e = a->dim_end();
100 Shape::const_iterator si = shape.begin(), se = shape.end();
101 while (i != e && si != se) {
102 assert(a->dimension_size(i) >= 0);
103 if (*si != (unsigned long) a->dimension_size(i)) return false;
104 ++i;
105 ++si;
106 }
107
108 return true;
109}
110
124bool TabularFunction::dep_indep_match(const Shape &dep_shape, const Shape &indep_shape)
125{
126 // Each of the indep vars dims must match the corresponding dep var dims
127 // Start the comparison with the right-most dims (rbegin())
128 Shape::const_reverse_iterator di = dep_shape.rbegin();
129 for (Shape::const_reverse_iterator i = indep_shape.rbegin(), e = indep_shape.rend(); i != e; ++i) {
130 if (di == dep_shape.rend()) return false; // this was an assert; should be a test. jhrg 11/17/15
131 if (*i != *di++) return false;
132 }
133
134 return true;
135}
136
144unsigned long TabularFunction::number_of_values(const Shape &shape)
145{
146 unsigned long size = 1;
147 Shape::const_iterator si = shape.begin(), se = shape.end();
148 while (si != se) {
149 size *= *si++;
150 }
151 return size;
152}
153
174void TabularFunction::build_columns(unsigned long n, BaseType* btp, vector<Array*>& the_arrays,
175 Shape &shape)
176{
177 if (btp->type() != dods_array_c)
178 throw Error("In tabular(): Expected argument '" + btp->name() + "' to be an Array.");
179
180 // We know it's an Array; cast, test, save and read the values
181 Array *a = static_cast<Array*>(btp);
182 // For the first array, record the number of dims and their sizes
183 // for all subsequent arrays, test for a match
184 if (n == 0)
185 shape = array_shape(a);
186 else if (!shape_matches(a, shape))
187 throw Error("In tabular: Array '" + a->name() + "' does not match the shape of the initial Array.");
188
189 read_array_values(a);
190
191 the_arrays.at(n) = a; // small number of Arrays; use the safe method
192}
193
202void TabularFunction::read_values(const vector<Array*> &arrays)
203{
204 // NB: read_array_values is defined at the very top of this file
205 for_each(arrays.begin(), arrays.end(), read_array_values);
206}
207
224void TabularFunction::build_sequence_values(const vector<Array*> &the_arrays, SequenceValues &sv)
225{
226 // This can be optimized for most cases because we're storing objects for Byte, Int32, ...
227 // values where we could be storing native types. But this is DAP2 code... jhrg 2/3/15
228 //
229 // NB: SequenceValues == vector< vector<BaseType*> *>, and
230 // D4SeqRow, BaseTypeRow == vector<BaseType*>
231 for (SequenceValues::size_type i = 0; i < sv.size(); ++i) {
232
233 BaseTypeRow *row = new BaseTypeRow(the_arrays.size());
234
235 for (BaseTypeRow::size_type j = 0; j < the_arrays.size(); ++j) {
236 DBG(cerr << "the_arrays.at(" << j << ") " << the_arrays.at(j) << endl);
237 // i == row number; j == column (or array) number
238 (*row)[j]/*->at(j)*/= the_arrays[j]/*.at(j)*/->var(i)->ptr_duplicate();
239
240 (*row)[j]->set_send_p(true);
241 (*row)[j]->set_read_p(true);
242 }
243
244 sv[i]/*.at(i)*/= row;
245 }
246}
247
267void TabularFunction::combine_sequence_values(SequenceValues &dep, const SequenceValues &indep)
268{
269 SequenceValues::const_iterator ii = indep.begin(), ie = indep.end();
270 for (SequenceValues::iterator i = dep.begin(), e = dep.end(); i != e; ++i) {
271 // When we get to the end of the indep variables, start over
272 // This test is at the start of the loop so that we can test ii == ie on exit
273 if (ii == ie) ii = indep.begin();
274 // This call to insert() copies the pointers, but that will lead to duplicate
275 // calls to delete when Sequence deletes the SequenceValues object. Could be
276 // replaced with reference counted pointers??? jhrg 3/13/15
277 // (*i)->insert((*i)->end(), (*ii)->begin(), (*ii)->end());
278 for (BaseTypeRow::iterator btr_i = (*ii)->begin(), btr_e = (*ii)->end(); btr_i != btr_e; ++btr_i) {
279 (*i)->push_back((*btr_i)->ptr_duplicate());
280 }
281 ++ii;
282 }
283
284 assert(ii == ie);
285}
286
312void TabularFunction::add_index_column(const Shape &indep_shape, const Shape &dep_shape,
313 vector<Array*> &dep_vars)
314{
315 assert(dep_vars.size() > 0);
316 assert(dep_shape.size() == indep_shape.size() + 1);
317
318 // load a vector with values for the new variable
319 unsigned long num_indep_values = number_of_values(indep_shape);
320 unsigned long num_dep_values = number_of_values(dep_shape);
321 vector<dods_uint32> index_vals(num_dep_values);
322
323 assert(num_dep_values == num_indep_values * dep_shape.at(0));
324
325 // dep_shape.at(0) == the left-most dimension size
326 vector<dods_uint32>::iterator iv = index_vals.begin();
327 for (Shape::size_type i = 0; i < dep_shape.at(0); ++i) {
328 assert(iv != index_vals.end());
329 fill(iv, iv + num_indep_values, i);
330 iv += num_indep_values;
331 }
332
333 // Figure out what to call the new variable/column
334 string new_column_name = dep_vars.at(0)->dimension_name(dep_vars.at(0)->dim_begin());
335 if (new_column_name.empty())
336 new_column_name = "index";
337
338 // Make the new column var
339 Array *a = new Array(new_column_name, new UInt32(new_column_name));
340 a->append_dim(num_dep_values, new_column_name);
341 a->set_value(index_vals, (int)index_vals.size());
342 a->set_read_p(true);
343
344 dep_vars.insert(dep_vars.begin(), a);
345}
346
373void TabularFunction::function_dap2_tabular(int argc, BaseType *argv[], DDS &, BaseType **btpp)
374{
375 vector<Array*> the_arrays;
376 // collect all of the arrays; separates them from other kinds of parameters
377 for (int n = 0; n < argc; ++n) {
378 if (argv[n]->type() != dods_array_c)
379 throw Error("In function tabular(): Expected an array, but argument " + argv[n]->name()
380 + " is a " + argv[n]->type_name() + ".");
381 the_arrays.push_back(static_cast<Array*>(argv[n]));
382 }
383
384 if (the_arrays.size() < 1)
385 throw Error("In function tabular(): Expected at least one Array variable.");
386
387 // every var with dimension == min_dim_size is considered an 'independent' var
388 unsigned long min_dim_size = ULONG_MAX; // <climits>
389 for (vector<Array*>::iterator i = the_arrays.begin(), e = the_arrays.end(); i != e; ++i) {
390 min_dim_size = min((unsigned long) (*i)->dimensions(), min_dim_size);
391 }
392
393 // collect the independent and dependent variables; size _and_ shape must match
394 vector<Array*> indep_vars, dep_vars;
395 for (vector<Array*>::iterator i = the_arrays.begin(), e = the_arrays.end(); i != e; ++i) {
396 if ((*i)->dimensions() == min_dim_size) {
397 indep_vars.push_back(*i);
398 }
399 else {
400 dep_vars.push_back(*i);
401 }
402 }
403
404 Shape indep_shape = array_shape(indep_vars.at(0));
405 // Test that all the indep arrays have the same shape
406 for (vector<Array*>::iterator i = indep_vars.begin()+1, e = indep_vars.end(); i != e; ++i) {
407 if (!shape_matches(*i, indep_shape))
408 throw Error("In function tabular(): Expected all of the 'independent' variables to have the same shape.");
409 }
410
411 // Read the values and load them into a SequenceValues object
412 read_values(indep_vars);
413 unsigned long num_indep_values = number_of_values(indep_shape);
414 SequenceValues indep_sv(num_indep_values);
415 build_sequence_values(indep_vars, indep_sv);
416
417 // Set the reference to the result. If there are any dependent variables,
418 // 'result' will be set to 'dep_vars' once that has been hasked and the
419 // indep_vars merged in.
420 SequenceValues &result = indep_sv;
421
422 // If there are dependent variables, process them
423 if (dep_vars.size() > 0) {
424 Shape dep_shape = array_shape(dep_vars.at(0));
425 // Test that all the dep arrays have the same shape
426 for (vector<Array*>::iterator i = dep_vars.begin()+1, e = dep_vars.end(); i != e; ++i) {
427 if (!shape_matches(*i, dep_shape))
428 throw Error("In function tabular(): Expected all of the 'dependent' variables to have the same shape.");
429 }
430
431 // Test shapes here. My code assumes that deps are like dep_vars[7][x][y]
432 // and indep_vars are [x][y] - the left-most dim is the 'extra' parameter
433 // of the dep_vars.
434 if (dep_shape.size() > indep_shape.size() + 1)
435 throw Error("In function tabular(): The rank of the dependent variables may be at most one more than the rank of the independent variables");
436 if (dep_shape.size() < indep_shape.size())
437 throw Error("In function tabular(): The rank of the dependent variables cannot be less than the rank of the independent variables");
438
439 if (!dep_indep_match(dep_shape, indep_shape))
440 throw Error("In function tabular(): The 'independent' array shapes must match the right-most dimensions of the 'dependent' variables.");
441
442 read_values(dep_vars);
443 unsigned long num_dep_values = number_of_values(dep_shape);
444 SequenceValues dep_sv(num_dep_values);
445
446 // Add and extra variable for extra dimension's index
447 add_index_column(indep_shape, dep_shape, dep_vars);
448
449 build_sequence_values(dep_vars, dep_sv);
450
451 // Now combine the dependent and independent variables; put the
452 // result in the dependent variable vector and assign the 'result'
453 // reference to it.
454 combine_sequence_values(dep_sv, indep_sv);
455 result = dep_sv;
456 }
457
458 unique_ptr<TabularSequence> response(new TabularSequence("table"));
459
460 if (!dep_vars.empty()) {
461 // set the columns of the response
462 for (auto variable: dep_vars) {
463 response->add_var(variable->var());
464 }
465 }
466
467 for (auto variable: indep_vars) {
468 response->add_var(variable->var());
469 }
470
471 // set the values of the response
472 response->set_value(result);
473 response->set_read_p(true);
474
475 *btpp = response.release();
476}
477
478#if 0
479
480// Rework this as time permits. jhrg 3/12/15
481
493BaseType *TabularFunction::function_dap4_tabular(D4RValueList *args, DMR &dmr)
494{
495 // unique_ptr is not avialable on gcc 4.2. jhrg 2/11/15
496 //unique_ptr<D4Sequence> response(new D4Sequence("table"));
497 unique_ptr<D4Sequence> response(new D4Sequence("table"));
498
499 int num_arrays = args->size(); // Might pass in other stuff...
500 vector<unsigned long> shape; // Holds shape info; used to test array sizes for uniformity
501 vector<Array*> the_arrays(num_arrays);
502
503 for (int n = 0; n < num_arrays; ++n) {
504 TabularFunction::build_columns(n, args->get_rvalue(n)->value(dmr), the_arrays, shape);
505 }
506
507 DBG(cerr << "the_arrays.size(): " << the_arrays.size() << endl);
508
509 for (unsigned long n = 0; n < the_arrays.size(); ++n) {
510 response->add_var(the_arrays[n]->var());
511 }
512
513 unsigned long num_values = TabularFunction::number_of_values(shape);
514 D4SeqValues sv(num_values);
515 // sv is a value-result parameter
516 TabularFunction::build_sequence_values(the_arrays, sv);
517
518 response->set_value(sv);
519 response->set_read_p(true);
520
521 return response.release();
522}
523#endif
524
525} // namesspace functions