OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
TabularFunction.cc
Go to the documentation of this file.
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 
50 namespace libdap {
51 
58 static void read_array_values(Array *a)
59 {
60  a->read();
61  a->set_read_p(true);
62 }
63 
72 TabularFunction::Shape TabularFunction::array_shape(Array *a)
73 {
74  Shape shape;
75 
76  for (Array::Dim_iter i = a->dim_begin(), e = a->dim_end(); i != e; ++i) {
77  shape.push_back(a->dimension_size(i));
78  }
79 
80  return shape;
81 }
82 
91 bool TabularFunction::shape_matches(Array *a, const Shape &shape)
92 {
93  // Same number of dims
94  if (shape.size() != a->dimensions()) return false;
95 
96  // Each dim the same size
97  Array::Dim_iter i = a->dim_begin(), e = a->dim_end();
98  Shape::const_iterator si = shape.begin(), se = shape.end();
99  while (i != e && si != se) {
100  assert(a->dimension_size(i) >= 0);
101  if (*si != (unsigned long) a->dimension_size(i)) return false;
102  ++i;
103  ++si;
104  }
105 
106  return true;
107 }
108 
122 bool TabularFunction::dep_indep_match(const Shape &dep_shape, const Shape &indep_shape)
123 {
124  // Each of the indep vars dims must match the corresponding dep var dims
125  // Start the comparison with the right-most dims (rbegin())
126  Shape::const_reverse_iterator di = dep_shape.rbegin();
127  for (Shape::const_reverse_iterator i = indep_shape.rbegin(), e = indep_shape.rend(); i != e; ++i) {
128  assert(di != dep_shape.rend());
129  if (*i != *di++) return false;
130  }
131 
132  return true;
133 }
134 
142 unsigned long TabularFunction::number_of_values(const Shape &shape)
143 {
144  unsigned long size = 1;
145  Shape::const_iterator si = shape.begin(), se = shape.end();
146  while (si != se) {
147  size *= *si++;
148  }
149  return size;
150 }
151 
172 void TabularFunction::build_columns(unsigned long n, BaseType* btp, vector<Array*>& the_arrays,
173  Shape &shape)
174 {
175  if (btp->type() != dods_array_c)
176  throw Error("In tabular(): Expected argument '" + btp->name() + "' to be an Array.");
177 
178  // We know it's an Array; cast, test, save and read the values
179  Array *a = static_cast<Array*>(btp);
180  // For the first array, record the number of dims and their sizes
181  // for all subsequent arrays, test for a match
182  if (n == 0)
183  shape = array_shape(a);
184  else if (!shape_matches(a, shape))
185  throw Error("In tabular: Array '" + a->name() + "' does not match the shape of the initial Array.");
186 
187  read_array_values(a);
188 
189  the_arrays.at(n) = a; // small number of Arrays; use the safe method
190 }
191 
200 void TabularFunction::read_values(const vector<Array*> &arrays)
201 {
202  // NB: read_array_values is defined at the very top of this file
203  for_each(arrays.begin(), arrays.end(), read_array_values);
204 }
205 
222 void TabularFunction::build_sequence_values(const vector<Array*> &the_arrays, SequenceValues &sv)
223 {
224  // This can be optimized for most cases because we're storing objects for Byte, Int32, ...
225  // values where we could be storing native types. But this is DAP2 code... jhrg 2/3/15
226  //
227  // NB: SequenceValues == vector< vector<BaseType*> *>, and
228  // D4SeqRow, BaseTypeRow == vector<BaseType*>
229  for (SequenceValues::size_type i = 0; i < sv.size(); ++i) {
230 
231  BaseTypeRow *row = new BaseTypeRow(the_arrays.size());
232 
233  for (BaseTypeRow::size_type j = 0; j < the_arrays.size(); ++j) {
234  DBG(cerr << "the_arrays.at(" << j << ") " << the_arrays.at(j) << endl);
235  // i == row number; j == column (or array) number
236  (*row)[j]/*->at(j)*/= the_arrays[j]/*.at(j)*/->var(i)->ptr_duplicate();
237 
238  (*row)[j]->set_send_p(true);
239  (*row)[j]->set_read_p(true);
240  }
241 
242  sv[i]/*.at(i)*/= row;
243  }
244 }
245 
265 void TabularFunction::combine_sequence_values(SequenceValues &dep, const SequenceValues &indep)
266 {
267  SequenceValues::const_iterator ii = indep.begin(), ie = indep.end();
268  for (SequenceValues::iterator i = dep.begin(), e = dep.end(); i != e; ++i) {
269  // When we get to the end of the indep variables, start over
270  // This test is at the start of the loop so that we can test ii == ie on exit
271  if (ii == ie) ii = indep.begin();
272  // This call to insert() copies the pointers, but that will lead to duplicate
273  // calls to delete when Sequence deletes the SequenceValues object. Could be
274  // replaced with reference counted pointers??? jhrg 3/13/15
275  // (*i)->insert((*i)->end(), (*ii)->begin(), (*ii)->end());
276  for (BaseTypeRow::iterator btr_i = (*ii)->begin(), btr_e = (*ii)->end(); btr_i != btr_e; ++btr_i) {
277  (*i)->push_back((*btr_i)->ptr_duplicate());
278  }
279  ++ii;
280  }
281 
282  assert(ii == ie);
283 }
284 
310 void TabularFunction::add_index_column(const Shape &indep_shape, const Shape &dep_shape,
311  vector<Array*> &dep_vars)
312 {
313  assert(dep_vars.size() > 0);
314  assert(dep_shape.size() == indep_shape.size() + 1);
315 
316  // load a vector with values for the new variable
317  unsigned long num_indep_values = number_of_values(indep_shape);
318  unsigned long num_dep_values = number_of_values(dep_shape);
319  vector<dods_uint32> index_vals(num_dep_values);
320 
321  assert(num_dep_values == num_indep_values * dep_shape.at(0));
322 
323  // dep_shape.at(0) == the left-most dimension size
324  vector<dods_uint32>::iterator iv = index_vals.begin();
325  for (Shape::size_type i = 0; i < dep_shape.at(0); ++i) {
326  assert(iv != index_vals.end());
327  fill(iv, iv + num_indep_values, i);
328  iv += num_indep_values;
329  }
330 
331  // Figure out what to call the new variable/column
332  string new_column_name = dep_vars.at(0)->dimension_name(dep_vars.at(0)->dim_begin());
333  if (new_column_name.empty())
334  new_column_name = "index";
335 
336  // Make the new column var
337  Array *a = new Array(new_column_name, new UInt32(new_column_name));
338  a->append_dim(num_dep_values, new_column_name);
339  a->set_value(index_vals, (int)index_vals.size());
340  a->set_read_p(true);
341 
342  dep_vars.insert(dep_vars.begin(), a);
343 }
344 
364 void TabularFunction::function_dap2_tabular_2(int argc, BaseType *argv[], DDS &, BaseType **btpp)
365 {
366  // unique_ptr is not available on gcc 4.2. jhrg 2/11/15
367  // unique_ptr<TabularSequence> response(new TabularSequence("table"));
368  auto_ptr<TabularSequence> response(new TabularSequence("table"));
369 
370  int num_arrays = argc; // Might pass in other stuff...
371  Shape shape; // Holds shape info; used to test array sizes for uniformity
372  vector<Array*> the_arrays(num_arrays);
373 
374  // Read each array passed to tabular(), check that its shape matches
375  // the first array's shape, store the array in a vector and read in
376  // it's values (into the Array object's internal store).
377  for (int n = 0; n < num_arrays; ++n) {
378  build_columns(n, argv[n], the_arrays, shape);
379  }
380 
381  DBG(cerr << "the_arrays.size(): " << the_arrays.size() << endl);
382 
383  // Now build the response Sequence so it has columns that match the
384  // Array element types
385  for (unsigned long n = 0; n < the_arrays.size(); ++n) {
386  response->add_var(the_arrays[n]->var());
387  }
388 
389  unsigned long num_values = number_of_values(shape);
390  SequenceValues sv(num_values);
391  // Transfer the data from the array variables held in the vector of
392  // Arrays into the Sequence using the SequenceValues object.
393  // sv is a value-result parameter
394  build_sequence_values(the_arrays, sv);
395 
396  response->set_value(sv);
397  response->set_read_p(true);
398 
399  *btpp = response.release();
400  return;
401 }
402 
429 void TabularFunction::function_dap2_tabular(int argc, BaseType *argv[], DDS &, BaseType **btpp)
430 {
431  vector<Array*> the_arrays;
432  // collect all of the arrays; separates them from other kinds of parameters
433  for (int n = 0; n < argc; ++n) {
434  if (argv[n]->type() != dods_array_c)
435  throw Error("In function tabular(): Expected an array, but argument " + argv[n]->name()
436  + " is a " + argv[n]->type_name() + ".");
437  the_arrays.push_back(static_cast<Array*>(argv[n]));
438  }
439 
440  if (the_arrays.size() < 1)
441  throw Error("In function tabular(): Expected at least one Array variable.");
442 
443  // every var with dimension == min_dim_size is considered an 'independent' var
444  unsigned long min_dim_size = ULONG_MAX; // <climits>
445  for (vector<Array*>::iterator i = the_arrays.begin(), e = the_arrays.end(); i != e; ++i) {
446  min_dim_size = min((unsigned long) (*i)->dimensions(), min_dim_size);
447  }
448 
449  // collect the independent and dependent variables; size _and_ shape must match
450  vector<Array*> indep_vars, dep_vars;
451  for (vector<Array*>::iterator i = the_arrays.begin(), e = the_arrays.end(); i != e; ++i) {
452  if ((*i)->dimensions() == min_dim_size) {
453  indep_vars.push_back(*i);
454  }
455  else {
456  dep_vars.push_back(*i);
457  }
458  }
459 
460  Shape indep_shape = array_shape(indep_vars.at(0));
461  // Test that all the indep arrays have the same shape
462  for (vector<Array*>::iterator i = indep_vars.begin()+1, e = indep_vars.end(); i != e; ++i) {
463  if (!shape_matches(*i, indep_shape))
464  throw Error("In function tabular(): Expected all of the 'independent' variables to have the same shape.");
465  }
466 
467  // Read the values and load them into a SequenceValues object
468  read_values(indep_vars);
469  unsigned long num_indep_values = number_of_values(indep_shape);
470  SequenceValues indep_sv(num_indep_values);
471  build_sequence_values(indep_vars, indep_sv);
472 
473  // Set the reference to the result. If there are any dependent variables,
474  // 'result' will be set to 'dep_vars' once that has been hasked and the
475  // indep_vars merged in.
476  SequenceValues &result = indep_sv;
477 
478  // If there are dependent variables, process them
479  if (dep_vars.size() > 0) {
480  Shape dep_shape = array_shape(dep_vars.at(0));
481  // Test that all the dep arrays have the same shape
482  for (vector<Array*>::iterator i = dep_vars.begin()+1, e = dep_vars.end(); i != e; ++i) {
483  if (!shape_matches(*i, dep_shape))
484  throw Error("In function tabular(): Expected all of the 'dependent' variables to have the same shape.");
485  }
486 
487  // Test shapes here. My code assumes that deps are like dep_vars[7][x][y]
488  // and indep_vars are [x][y] - the left-most dim is the 'extra' parameter
489  // of the dep_vars.
490  if (dep_shape.size() > indep_shape.size() + 1)
491  throw Error("In function tabular(): The rank of the dependent variables may be at most one more than the rank of the independent variables");
492  if (dep_shape.size() < indep_shape.size())
493  throw Error("In function tabular(): The rank of the dependent variables cannot be less than the rank of the independent variables");
494 
495  if (!dep_indep_match(dep_shape, indep_shape))
496  throw Error("In function tabular(): The 'independent' array shapes must match the right-most dimensions of the 'dependent' variables.");
497 
498  read_values(dep_vars);
499  unsigned long num_dep_values = number_of_values(dep_shape);
500  SequenceValues dep_sv(num_dep_values);
501 
502  // Add and extra variable for extra dimension's index
503  add_index_column(indep_shape, dep_shape, dep_vars);
504 
505  build_sequence_values(dep_vars, dep_sv);
506 
507  // Now combine the dependent and independent variables; put the
508  // result in the dependent variable vector and assign the 'result'
509  // reference to it.
510  combine_sequence_values(dep_sv, indep_sv);
511  result = dep_sv;
512  }
513 
514  auto_ptr<TabularSequence> response(new TabularSequence("table"));
515 
516  if (dep_vars.size() > 0) {
517  // set the columns of the response
518  for (SequenceValues::size_type n = 0; n < dep_vars.size(); ++n) {
519  response->add_var(dep_vars[n]->var());
520  }
521  }
522 
523  for (SequenceValues::size_type n = 0; n < indep_vars.size(); ++n) {
524  response->add_var(indep_vars[n]->var());
525  }
526 
527  // set the values of the response
528  response->set_value(result);
529  response->set_read_p(true);
530 
531  *btpp = response.release();
532  return;
533 }
534 
535 #if 0
536 
537 // Rework this as time permits. jhrg 3/12/15
538 
550 BaseType *TabularFunction::function_dap4_tabular(D4RValueList *args, DMR &dmr)
551 {
552  // unique_ptr is not avialable on gcc 4.2. jhrg 2/11/15
553  //unique_ptr<D4Sequence> response(new D4Sequence("table"));
554  auto_ptr<D4Sequence> response(new D4Sequence("table"));
555 
556  int num_arrays = args->size(); // Might pass in other stuff...
557  vector<unsigned long> shape; // Holds shape info; used to test array sizes for uniformity
558  vector<Array*> the_arrays(num_arrays);
559 
560  for (int n = 0; n < num_arrays; ++n) {
561  TabularFunction::build_columns(n, args->get_rvalue(n)->value(dmr), the_arrays, shape);
562  }
563 
564  DBG(cerr << "the_arrays.size(): " << the_arrays.size() << endl);
565 
566  for (unsigned long n = 0; n < the_arrays.size(); ++n) {
567  response->add_var(the_arrays[n]->var());
568  }
569 
570  unsigned long num_values = TabularFunction::number_of_values(shape);
571  D4SeqValues sv(num_values);
572  // sv is a value-result parameter
573  TabularFunction::build_sequence_values(the_arrays, sv);
574 
575  response->set_value(sv);
576  response->set_read_p(true);
577 
578  return response.release();
579 }
580 #endif
581 
582 } // namesspace libdap
STL namespace.
#define assert(exp)
Definition: freeform.h:735
static class NCMLUtil overview
#define min(a, b)
Definition: os_utils.h:71