bes  Updated for version 3.20.6
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 <BaseType.h>
35 #include <UInt32.h>
36 #include <Array.h>
37 #include <Sequence.h>
38 #include <D4Sequence.h>
39 #include <D4RValue.h>
40 #include <Error.h>
41 #include <debug.h>
42 #include <util.h>
43 #include <ServerFunctionsList.h>
44 
45 #include "TabularSequence.h"
46 #include "TabularFunction.h"
47 
48 using namespace std;
49 using namespace libdap;
50 
51 namespace functions {
52 
59 static void read_array_values(Array *a)
60 {
61  a->read();
62  a->set_read_p(true);
63 }
64 
74 TabularFunction::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 
93 bool 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 
124 bool 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 
144 unsigned 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 
174 void 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 
202 void 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 
224 void 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 
267 void 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 
312 void 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 
373 void 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  auto_ptr<TabularSequence> response(new TabularSequence("table"));
459 
460  if (dep_vars.size() > 0) {
461  // set the columns of the response
462  for (SequenceValues::size_type n = 0; n < dep_vars.size(); ++n) {
463  response->add_var(dep_vars[n]->var());
464  }
465  }
466 
467  for (SequenceValues::size_type n = 0; n < indep_vars.size(); ++n) {
468  response->add_var(indep_vars[n]->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  return;
477 }
478 
479 #if 0
480 
481 // Rework this as time permits. jhrg 3/12/15
482 
494 BaseType *TabularFunction::function_dap4_tabular(D4RValueList *args, DMR &dmr)
495 {
496  // unique_ptr is not avialable on gcc 4.2. jhrg 2/11/15
497  //unique_ptr<D4Sequence> response(new D4Sequence("table"));
498  auto_ptr<D4Sequence> response(new D4Sequence("table"));
499 
500  int num_arrays = args->size(); // Might pass in other stuff...
501  vector<unsigned long> shape; // Holds shape info; used to test array sizes for uniformity
502  vector<Array*> the_arrays(num_arrays);
503 
504  for (int n = 0; n < num_arrays; ++n) {
505  TabularFunction::build_columns(n, args->get_rvalue(n)->value(dmr), the_arrays, shape);
506  }
507 
508  DBG(cerr << "the_arrays.size(): " << the_arrays.size() << endl);
509 
510  for (unsigned long n = 0; n < the_arrays.size(); ++n) {
511  response->add_var(the_arrays[n]->var());
512  }
513 
514  unsigned long num_values = TabularFunction::number_of_values(shape);
515  D4SeqValues sv(num_values);
516  // sv is a value-result parameter
517  TabularFunction::build_sequence_values(the_arrays, sv);
518 
519  response->set_value(sv);
520  response->set_read_p(true);
521 
522  return response.release();
523 }
524 #endif
525 
526 } // namesspace functions
libdap
Definition: BESDapFunctionResponseCache.h:35
Error