bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
AggregationUtil.cc
1
2// This file is part of the "NcML Module" project, a BES module designed
3// to allow NcML files to be used to be used as a wrapper to add
4// AIS to existing datasets of any format.
5//
6// Copyright (c) 2009 OPeNDAP, Inc.
7// Author: Michael Johnson <m.johnson@opendap.org>
8//
9// For more information, please also see the main website: http://opendap.org/
10//
11// This library is free software; you can redistribute it and/or
12// modify it under the terms of the GNU Lesser General Public
13// License as published by the Free Software Foundation; either
14// version 2.1 of the License, or (at your option) any later version.
15//
16// This library is distributed in the hope that it will be useful,
17// but WITHOUT ANY WARRANTY; without even the implied warranty of
18// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19// Lesser General Public License for more details.
20//
21// You should have received a copy of the GNU Lesser General Public
22// License along with this library; if not, write to the Free Software
23// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24//
25// Please see the files COPYING and COPYRIGHT for more information on the GLPL.
26//
27// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
29#include "config.h"
30
31#include "AggregationUtil.h"
32
33// agg_util includes
34#include "AggMemberDataset.h"
35#include "AggregationException.h"
36#include "Dimension.h"
37
38// libdap includes
39#include <libdap/Array.h> // libdap
40#include <libdap/AttrTable.h>
41#include <libdap/BaseType.h>
42#include <libdap/DataDDS.h>
43#include <libdap/DDS.h>
44#include <libdap/Grid.h>
45#include "BESDebug.h"
46#include "BESStopWatch.h"
47
48// Outside includes (MINIMIZE THESE!)
49#include "NCMLDebug.h" // This the ONLY dependency on NCML Module I want in this class since the macros there are general it's ok...
50
51using libdap::Array;
52using libdap::AttrTable;
53using libdap::BaseType;
54using libdap::Constructor;
55using libdap::DataDDS;
56using libdap::DDS;
57using libdap::Grid;
58using libdap::Vector;
59using std::string;
60using std::vector;
61
62#define MODULE "agg_util"
63
64// AggregationUtil
65#define prolog_au string("AggregationUtil::").append(__func__).append("() - ")
66// TopLevelArrayGetter
67#define prolog_tlag string("TopLevelArrayGetter::").append(__func__).append("() - ")
68// TopLevelGridDataArrayGetter
69#define prolog_tlgdag string("TopLevelGridDataArrayGetter::").append(__func__).append("() - ")
70// TopLevelGridMapArrayGetter
71#define prolog_tlgmag string("TopLevelGridMapArrayGetter::").append(__func__).append("() - ")
72
73namespace agg_util {
74// Static class member used to track the position of the last CVs insertion
75// when building a JoinExisting aggregation.
76int AggregationUtil::d_last_added_cv_position = 0;
77
79// ArrayGetterInterface impls
80
81/* virtual */
82ArrayGetterInterface::~ArrayGetterInterface()
83{
84}
85
87// TopLevelArrayGetter impl
88
89TopLevelArrayGetter::TopLevelArrayGetter() :
91{
92}
93
94/* virtual */
95TopLevelArrayGetter::~TopLevelArrayGetter()
96{
97}
98
99/* virtual */
100TopLevelArrayGetter*
102{
103 return new TopLevelArrayGetter(*this);
104}
105
106/* virtual */
107libdap::Array*
108TopLevelArrayGetter::readAndGetArray(const std::string& name, const libdap::DDS& dds,
109 const libdap::Array* const pConstraintTemplate, const std::string& debugChannel) const
110{
111
112 BES_STOPWATCH_START(MODULE, prolog_tlag + "Timing");
113
114 // First, look up the BaseType
115 BaseType* pBT = AggregationUtil::getVariableNoRecurse(dds, name);
116
117 // Next, if it's not there, throw exception.
118 if (!pBT) {
119 throw AggregationException("TopLevelArrayGetter: "
120 "Did not find a variable named \"" + name + "\" at the top-level of the DDS!");
121 }
122
123 // Next, make sure it's an Array before we cast it
124 // Prefer using the enum type for speed rather than RTTI
125 if (pBT->type() != libdap::dods_array_c) {
126 throw AggregationException("TopLevelArrayGetter: "
127 "The top-level DDS variable named \"" + name + "\" was not of the expected type!"
128 " Expected:Array Found:" + pBT->type_name());
129 }
130
131 libdap::Array* pDatasetArray = static_cast<libdap::Array*>(pBT);
132
133 // If given, copy the constraints over to the found Array
134 if (pConstraintTemplate) {
135 agg_util::AggregationUtil::transferArrayConstraints(pDatasetArray, // into this dataset array to be read
136 *pConstraintTemplate, // from this template
137 false, // same rank Array's in template and loaded, don't skip first dim
138 false, // or the to dimension. copy whole thing.
139 !(debugChannel.empty()), // printDebug
140 debugChannel);
141 }
142
143 // Force a read() perhaps with constraints
144 pDatasetArray->set_send_p(true);
145 pDatasetArray->set_in_selection(true);
146 pDatasetArray->read();
147
148 return pDatasetArray;
149}
150
152// TopLevelGridDataArrayGetter impl
153
154TopLevelGridDataArrayGetter::TopLevelGridDataArrayGetter() :
156{
157}
158
159/* virtual */
160TopLevelGridDataArrayGetter::~TopLevelGridDataArrayGetter()
161{
162}
163
164/* virtual */
167{
168 return new TopLevelGridDataArrayGetter(*this);
169}
170
171/* virtual */
172libdap::Array*
173TopLevelGridDataArrayGetter::readAndGetArray(const std::string& name, const libdap::DDS& dds,
174 const libdap::Array* const pConstraintTemplate, const std::string& debugChannel) const
175{
176 BES_STOPWATCH_START(MODULE, prolog_tlgdag + "Timing");
177
178 // First, look up the BaseType
179 BaseType* pBT = AggregationUtil::getVariableNoRecurse(dds, name);
180
181 // Next, if it's not there, throw exception.
182 if (!pBT) {
183 throw AggregationException("TopLevelGridArrayGetter: "
184 "Did not find a variable named \"" + name + "\" at the top-level of the DDS!");
185 }
186
187 // Next, make sure it's a Grid before we cast it
188 // Prefer using the enum type for speed rather than RTTI
189 if (pBT->type() != libdap::dods_grid_c) {
190 throw AggregationException("TopLevelGridArrayGetter: "
191 "The top-level DDS variable named \"" + name + "\" was not of the expected type!"
192 " Expected:Grid Found:" + pBT->type_name());
193 }
194
195 // Grab the array and return it.
196 Grid* pDataGrid = static_cast<Grid*>(pBT);
197 Array* pDataArray = static_cast<Array*>(pDataGrid->array_var());
198 if (!pDataArray) {
199 throw AggregationException("TopLevelGridArrayGetter: "
200 "The data Array var for variable name=\"" + name + "\" was unexpectedly null!");
201 }
202
203 // If given, copy the constraints over to the found Array
204 if (pConstraintTemplate) {
205 agg_util::AggregationUtil::transferArrayConstraints(pDataArray, // into this data array to be read
206 *pConstraintTemplate, // from this template
207 false, // same rank Array's in template and loaded, don't skip first dim
208 false, // also don't skip in the to array
209 !(debugChannel.empty()), // printDebug
210 debugChannel);
211 }
212
213 // Force the read() on the Grid level since some handlers
214 // cannot handle a read on a subobject unless read() is called
215 // on the parent object. We have given the constraints to the
216 // data Array already.
217 // TODO make an option on whether to load the Grid's map
218 // vectors or not! I think for these cases we do not want them ever!
219 pDataGrid->set_send_p(true);
220 pDataGrid->set_in_selection(true);
221 pDataGrid->read();
222
223 // Also make sure the Array was read and if not call it as well.
224 if (!pDataArray->read_p()) {
225 pDataArray->set_send_p(true);
226 pDataArray->set_in_selection(true);
227 pDataArray->read();
228 }
229
230 return pDataArray;
231}
232
234// TopLevelGridMapArrayGetter impl
235
236TopLevelGridMapArrayGetter::TopLevelGridMapArrayGetter(const std::string& gridName) :
237 ArrayGetterInterface(), _gridName(gridName)
238{
239}
240
241/* virtual */
242TopLevelGridMapArrayGetter::~TopLevelGridMapArrayGetter()
243{
244}
245
246/* virtual */
249{
250 return new TopLevelGridMapArrayGetter(*this);
251}
252
253/* virtual */
254libdap::Array*
255TopLevelGridMapArrayGetter::readAndGetArray(const std::string& arrayName, const libdap::DDS& dds,
256 const libdap::Array* const pConstraintTemplate, const std::string& debugChannel) const
257{
258 BES_STOPWATCH_START(MODULE, prolog_tlgmag + "Timing");
259
260 // First, look up the Grid the map is in
261 BaseType* pBT = AggregationUtil::getVariableNoRecurse(dds, _gridName);
262
263 // Next, if it's not there, throw exception.
264 if (!pBT) {
265 throw AggregationException("Did not find a variable named \"" + _gridName + "\" at the top-level of the DDS!");
266 }
267
268 // Next, make sure it's a Grid before we cast it
269 // Prefer using the enum type for speed rather than RTTI
270 if (pBT->type() != libdap::dods_grid_c) {
272 "The top-level DDS variable named \"" + _gridName + "\" was not of the expected type!"
273 " Expected:Grid Found:" + pBT->type_name());
274 }
275
276 // Find the correct map
277 Grid* pDataGrid = static_cast<Grid*>(pBT);
278 Array* pMap = const_cast<Array*>(AggregationUtil::findMapByName(*pDataGrid, arrayName));
279 NCML_ASSERT_MSG(pMap,
280 "Expected to find the map with name " + arrayName + " within the Grid " + _gridName + " but failed to find it!");
281
282 // Prepare it to be read in so we can get the data
283 pMap->set_send_p(true);
284 pMap->set_in_selection(true);
285
286 // If given, copy the constraints over to the found Array
287 if (pConstraintTemplate) {
288 agg_util::AggregationUtil::transferArrayConstraints(pMap, // into this data array to be read
289 *pConstraintTemplate, // from this template
290 false, // same rank Array's in template and loaded, don't skip first dim
291 false, // also don't skip in the to array
292 !(debugChannel.empty()), // printDebug
293 debugChannel);
294 }
295
296 // Do the read
297 pMap->read();
298
299 return pMap;
300}
301
302/*********************************************************************************************************
303 * AggregationUtil Impl
304 */
305void AggregationUtil::performUnionAggregation(DDS* pOutputUnion, const ConstDDSList& datasetsInOrder)
306{
307 VALID_PTR(pOutputUnion);
308
309 // Union any non-aggregated variables from the template dataset into the aggregated dataset
310 // Because we want the joinExistingaggregation to build up the Coordinate Variables (CVs)
311 // in the order they are declared in the NCML file, we need to track the current position
312 // where the last one was inserted. We can do that with a field in the AggregationUtil
313 // class. Here we reset that field so that it starts at position 0. 12.13.11 jhrg
315
316 vector<const DDS*>::const_iterator endIt = datasetsInOrder.end();
318 for (it = datasetsInOrder.begin(); it != endIt; ++it) {
319 const DDS* pDDS = *it;
320 VALID_PTR(pDDS);
321
322 // Merge in the global attribute tables
323 unionAttrsInto(&(pOutputUnion->get_attr_table()),
324 // TODO there really should be const version of this in libdap::DDS
325 const_cast<DDS*>(pDDS)->get_attr_table());
326
327 // Merge in the variables, which carry their tables along with them since the AttrTable is
328 // within the variable's "namespace", or lexical scope.
329 unionAllVariablesInto(pOutputUnion, *pDDS);
330 }
331}
332
333void AggregationUtil::unionAttrsInto(AttrTable* pOut, const AttrTable& fromTableIn)
334{
335 // semantically const
336 AttrTable& fromTable = const_cast<AttrTable&>(fromTableIn);
337 AttrTable::Attr_iter endIt = fromTable.attr_end();
338 AttrTable::Attr_iter it;
339 for (it = fromTable.attr_begin(); it != endIt; ++it) {
340 const string& name = fromTable.get_name(it);
341 AttrTable::Attr_iter attrInOut;
342 bool foundIt = findAttribute(*pOut, name, attrInOut);
343 // If it's already in the output, then skip it
344 if (foundIt) {
345 BESDEBUG("ncml",
346 "Union of AttrTable: an attribute named " << name << " already exist in output, skipping it..." << endl);
347 continue;
348 }
349 else // put a copy of it into the output
350 {
351 // containers need deep copy
352 if (fromTable.is_container(it)) {
353 AttrTable* pOrigAttrContainer = fromTable.get_attr_table(it);
354 NCML_ASSERT_MSG(pOrigAttrContainer,
355 "AggregationUtil::mergeAttrTables(): expected non-null AttrTable for the attribute container: "
356 + name);
357 AttrTable* pClonedAttrContainer = new AttrTable(*pOrigAttrContainer);
358 VALID_PTR(pClonedAttrContainer);
359 pOut->append_container(pClonedAttrContainer, name);
360 BESDEBUG("ncml",
361 "Union of AttrTable: adding a deep copy of attribute=" << name << " to the merged output." << endl);
362 }
363 else // for a simple type
364 {
365 string type = fromTable.get_type(it);
366 vector<string>* pAttrTokens = fromTable.get_attr_vector(it);
367 VALID_PTR(pAttrTokens);
368 // append_attr makes a copy of the vector, so we don't have to do so here.
369 pOut->append_attr(name, type, pAttrTokens);
370 }
371 }
372 }
373}
374
375bool AggregationUtil::findAttribute(const AttrTable& inTable, const string& name, AttrTable::Attr_iter& attr)
376{
377 AttrTable& inTableSemanticConst = const_cast<AttrTable&>(inTable);
378 attr = inTableSemanticConst.simple_find(name);
379 return (attr != inTableSemanticConst.attr_end());
380}
381
382void AggregationUtil::unionAllVariablesInto(libdap::DDS* pOutputUnion, const ConstDDSList& datasetsInOrder)
383{
384 ConstDDSList::const_iterator endIt = datasetsInOrder.end();
385 ConstDDSList::const_iterator it;
386 for (it = datasetsInOrder.begin(); it != endIt; ++it) {
387 unionAllVariablesInto(pOutputUnion, *(*it));
388 }
389}
390
391void AggregationUtil::unionAllVariablesInto(libdap::DDS* pOutputUnion, const libdap::DDS& fromDDS, bool add_at_top)
392{
393 DDS& dds = const_cast<DDS&>(fromDDS); // semantically const
394 DDS::Vars_iter endIt = dds.var_end();
395 DDS::Vars_iter it;
396 for (it = dds.var_begin(); it != endIt; ++it) {
397 BaseType* var = *it;
398 if (var) {
399 bool addedVar = addCopyOfVariableIfNameIsAvailable(pOutputUnion, *var, add_at_top);
400 if (addedVar) {
401 BESDEBUG("ncml", "Variable name=" << var->name() << " wasn't in the union yet and was added." << endl);
402 }
403 else {
404 BESDEBUG("ncml",
405 "Variable name=" << var->name() << " was already in the union and was skipped." << endl);
406 }
407 }
408 }
409}
410
411// This method is used to 'initialize' a new JoinExisting aggregation so that
412// A set of Coordinate Variables (CVs) will be inserted _in the order they are
413// listed_ in the .ncml file.
415{
416 //cerr << "Called resetCVInsertionPosition" << endl;
417 d_last_added_cv_position = 0;
418}
419
420bool AggregationUtil::addCopyOfVariableIfNameIsAvailable(libdap::DDS* pOutDDS, const libdap::BaseType& varProto,
421 bool add_at_top)
422{
423 bool ret = false;
424 BaseType* existingVar = findVariableAtDDSTopLevel(*pOutDDS, varProto.name());
425 if (!existingVar) {
426 // Add the var. add_var does a clone, so we don't need to.
427 BESDEBUG("ncml2", "AggregationUtil::addCopyOfVariableIfNameIsAvailable: " << varProto.name() << endl);
428 if (add_at_top) {
429 // This provides a way to remember where the last CV was inserted and adds
430 // this one after it. That provides the behavior that all of the CVs are
431 // added at the beginning of the DDS but in the order they appear in the NCML.
432 // That will translate into a greater chance of success for users, I think ...
433 //
434 // See also similar code in AggregationElement::createAndAddCoordinateVariableForNewDimensio
435 // jhrg 10/17/11
436 //cerr << "d_last_added_cv_position: " << d_last_added_cv_position << endl;
437 DDS::Vars_iter pos = pOutDDS->var_begin() + d_last_added_cv_position;
438
439 pOutDDS->insert_var(pos, const_cast<BaseType*>(&varProto));
440
441 ++d_last_added_cv_position;
442 }
443 else {
444 pOutDDS->add_var(const_cast<BaseType*>(&varProto));
445 }
446
447 ret = true;
448 }
449 return ret;
450}
451
452void AggregationUtil::addOrReplaceVariableForName(libdap::DDS* pOutDDS, const libdap::BaseType& varProto)
453{
454 BaseType* existingVar = findVariableAtDDSTopLevel(*pOutDDS, varProto.name());
455
456 // If exists, nuke it.
457 if (existingVar) {
458 pOutDDS->del_var(varProto.name());
459 }
460
461 // Add the var. add_var does a clone, so we don't need to clone it here.
462 pOutDDS->add_var(const_cast<BaseType*>(&varProto));
463}
464
465libdap::BaseType*
466AggregationUtil::findVariableAtDDSTopLevel(const libdap::DDS& dds_const, const string& name)
467{
468 BaseType* ret = 0;
469 DDS& dds = const_cast<DDS&>(dds_const); // semantically const
470 DDS::Vars_iter endIt = dds.var_end();
471 DDS::Vars_iter it;
472 for (it = dds.var_begin(); it != endIt; ++it) {
473 BaseType* var = *it;
474 if (var && var->name() == name) {
475 ret = var;
476 break;
477 }
478 }
479 return ret;
480}
481
482template<class LibdapType>
483LibdapType*
484AggregationUtil::findTypedVariableAtDDSTopLevel(const libdap::DDS& dds, const string& name)
485{
486 BaseType* pBT = findVariableAtDDSTopLevel(dds, name);
487 if (pBT) {
488 return dynamic_cast<LibdapType*>(pBT);
489 }
490 else {
491 return 0;
492 }
493}
494
495#if 0
496void AggregationUtil::produceOuterDimensionJoinedArray(Array* pJoinedArray, const std::string& joinedArrayName,
497 const std::string& newOuterDimName, const std::vector<libdap::Array*>& fromVars, bool copyData)
498{
499 string funcName = "AggregationUtil::createOuterDimensionJoinedArray:";
500
501 NCML_ASSERT_MSG(fromVars.size() > 0, funcName + "Must be at least one Array in input!");
502
503 // uses the first one as template for type and shape
504 if (!validateArrayTypesAndShapesMatch(fromVars, true)) {
505 throw AggregationException(
506 funcName + " The input arrays must all have the same data type and dimensions but do not!");
507 }
508
509 // The first will be used to "set up" the pJoinedArray
510 Array* templateArray = fromVars[0];
511 VALID_PTR(templateArray);
512 BaseType* templateVar = templateArray->var();
513 NCML_ASSERT_MSG(templateVar, funcName + "Expected a non-NULL prototype BaseType in the first Array!");
514
515 // Set the template var for the type.
516 pJoinedArray->add_var(templateVar);
517 // and force the name to be the desired one, not the prototype's
518 pJoinedArray->set_name(joinedArrayName);
519
520 // Copy the attribute table from the template over... We're not merging or anything.
521 pJoinedArray->set_attr_table(templateArray->get_attr_table());
522
523 // Create a new outer dimension based on the number of inputs we have.
524 // These append_dim calls go left to right, so we need to add the new dim FIRST.
525 pJoinedArray->append_dim(fromVars.size(), newOuterDimName);
526
527 // Use the template to add inner dimensions to the new array
528 for (Array::Dim_iter it = templateArray->dim_begin(); it != templateArray->dim_end(); ++it) {
529 int dimSize = templateArray->dimension_size(it);
530 string dimName = templateArray->dimension_name(it);
531 pJoinedArray->append_dim(dimSize, dimName);
532 }
533
534 if (copyData) {
535 // Make sure we have capacity for the full length of the up-ranked shape.
536 pJoinedArray->reserve_value_capacity(pJoinedArray->size());
537 // Glom the data together in
538 joinArrayData(pJoinedArray, fromVars, false, // we already reserved the space
539 true); // but please clear the Vector buffers after you use each Array in fromVars to help on memory.
540 }
541}
542#endif
543
544bool AggregationUtil::validateArrayTypesAndShapesMatch(const std::vector<libdap::Array*>& arrays,
545 bool enforceMatchingDimNames)
546{
547 NCML_ASSERT(arrays.size() > 0);
548 bool valid = true;
549 Array* pTemplate = 0;
550 for (vector<Array*>::const_iterator it = arrays.begin(); it != arrays.end(); ++it) {
551 // Set the template from the first one.
552 if (!pTemplate) {
553 pTemplate = *it;
554 VALID_PTR(pTemplate);
555 continue;
556 }
557
558 valid = (valid && doTypesMatch(*pTemplate, **it) && doShapesMatch(*pTemplate, **it, enforceMatchingDimNames));
559 // No use continuing
560 if (!valid) {
561 break;
562 }
563 }
564 return valid;
565}
566
567bool AggregationUtil::doTypesMatch(const libdap::Array& lhsC, const libdap::Array& rhsC)
568{
569 // semantically const
570 Array& lhs = const_cast<Array&>(lhsC);
571 Array& rhs = const_cast<Array&>(rhsC);
572 return (lhs.var() && rhs.var() && lhs.var()->type() == rhs.var()->type());
573}
574
575bool AggregationUtil::doShapesMatch(const libdap::Array& lhsC, const libdap::Array& rhsC, bool checkDimNames)
576{
577 // semantically const
578 Array& lhs = const_cast<Array&>(lhsC);
579 Array& rhs = const_cast<Array&>(rhsC);
580
581 // Check the number of dims matches first.
582 bool valid = true;
583 if (lhs.dimensions() != rhs.dimensions()) {
584 valid = false;
585 }
586 else {
587 // Then iterate on both in sync and compare.
588 Array::Dim_iter rhsIt = rhs.dim_begin();
589 for (Array::Dim_iter lhsIt = lhs.dim_begin(); lhsIt != lhs.dim_end(); (++lhsIt, ++rhsIt)) {
590 valid = (valid && (lhs.dimension_size(lhsIt) == rhs.dimension_size(rhsIt)));
591
592 if (checkDimNames) {
593 valid = (valid && (lhs.dimension_name(lhsIt) == rhs.dimension_name(rhsIt)));
594 }
595 }
596 }
597 return valid;
598}
599
600unsigned int AggregationUtil::collectVariableArraysInOrder(std::vector<Array*>& varArrays,
601 const std::string& collectVarName, const ConstDDSList& datasetsInOrder)
602{
603 unsigned int count = 0;
604 ConstDDSList::const_iterator endIt = datasetsInOrder.end();
605 ConstDDSList::const_iterator it;
606 for (it = datasetsInOrder.begin(); it != endIt; ++it) {
607 DDS* pDDS = const_cast<DDS*>(*it);
608 VALID_PTR(pDDS);
609 Array* pVar = dynamic_cast<Array*>(findVariableAtDDSTopLevel(*pDDS, collectVarName));
610 if (pVar) {
611 varArrays.push_back(pVar);
612 count++;
613 }
614 }
615 return count;
616}
617
619{
620 Array* pArr = dynamic_cast<Array*>(pBT);
621 if (pArr && (pArr->dimensions() == 1)) {
622 // only one dimension, so grab the first and make sure we only got one.
623 Array::Dim_iter it = pArr->dim_begin();
624 bool matches = (pArr->dimension_name(it) == pArr->name());
625 NCML_ASSERT_MSG((++it == pArr->dim_end()),
626 "Logic error: NCMLUtil::isCoordinateVariable(): expected one dimension from Array, but got more!");
627 return matches;
628 }
629 else {
630 return false;
631 }
632}
633
634#if 0
635void AggregationUtil::joinArrayData(Array* pAggArray, const std::vector<Array*>& varArrays,
636 bool reserveStorage/*=true*/, bool clearDataAfterUse/*=false*/)
637{
638 // Make sure we get a pAggArray with a type var we can deal with.
639 VALID_PTR(pAggArray->var());
640 NCML_ASSERT_MSG(pAggArray->var()->is_simple_type(),
641 "AggregationUtil::joinArrayData: the output Array is not of a simple type! Can't aggregate!");
642
643 // If the caller wants us to do it, sum up size() and reserve that much.
644 if (reserveStorage) {
645 // Figure it how much we need...
646 unsigned int totalLength = 0;
647 {
649 vector<Array*>::const_iterator endIt = varArrays.end();
650 for (it = varArrays.begin(); it != endIt; ++it) {
651 Array* pArr = *it;
652 if (pArr) {
653 totalLength += pArr->size();
654 }
655 }
656 }
657 pAggArray->reserve_value_capacity(totalLength);
658 }
659
660 // For each Array, make sure it's read in and copy its data into the output.
661 unsigned int nextElt = 0; // keeps track of where we are to write next in the output
662 vector<Array*>::const_iterator it;
663 vector<Array*>::const_iterator endIt = varArrays.end();
664 for (it = varArrays.begin(); it != endIt; ++it) {
665 Array* pArr = *it;
666 VALID_PTR(pArr);
667 NCML_ASSERT_MSG(pArr->var() && (pArr->var()->type() == pAggArray->var()->type()),
668 "AggregationUtil::joinArrayData: one of the arrays to join has different type than output! Can't aggregate!");
669
670 // Make sure it was read in...
671 if (!pArr->read_p()) {
672 pArr->read();
673 }
674
675 // Copy it in with the Vector call and update our location
676 nextElt += pAggArray->set_value_slice_from_row_major_vector(*pArr, nextElt);
677
678 if (clearDataAfterUse) {
679 pArr->clear_local_data();
680 }
681 }
682
683 // That's all folks!
684}
685#endif
686
688// struct dimension
689// {
690// int size; ///< The unconstrained dimension size.
691// string name; ///< The name of this dimension.
692// int start; ///< The constraint start index
693// int stop; ///< The constraint end index
694// int stride; ///< The constraint stride
695// int c_size; ///< Size of dimension once constrained
696// };
697
699void AggregationUtil::printDimensions(std::ostream& os, const libdap::Array& fromArray)
700{
701 os << "Array dimensions: " << endl;
702 Array& theArray = const_cast<Array&>(fromArray);
703 Array::Dim_iter it;
704 Array::Dim_iter endIt = theArray.dim_end();
705 for (it = theArray.dim_begin(); it != endIt; ++it) {
706 Array::dimension d = *it;
707 os << "Dim = {" << endl;
708 os << "name=" << d.name << endl;
709 os << "size=" << d.size << endl;
710 os << " }" << endl;
711 }
712 os << "End Array dimensions." << endl;
713}
714
715void AggregationUtil::printConstraints(std::ostream& os, const Array& rcArray)
716{
717 os << "Array constraints: " << endl;
718 Array& theArray = const_cast<Array&>(rcArray);
719 Array::Dim_iter it;
720 Array::Dim_iter endIt = theArray.dim_end();
721 for (it = theArray.dim_begin(); it != endIt; ++it) {
722 Array::dimension d = *it;
723 os << "Dim = {" << endl;
724 os << "name=" << d.name << endl;
725 os << "start=" << d.start << endl;
726 os << "stride=" << d.stride << endl;
727 os << "stop=" << d.stop << endl;
728 os << " }" << endl;
729 }
730 os << "End Array constraints" << endl;
731}
732
733void AggregationUtil::printConstraintsToDebugChannel(const std::string& debugChannel, const libdap::Array& fromArray)
734{
735 ostringstream oss;
736 BESDEBUG(debugChannel, "Printing constraints for Array: " << fromArray.name() << ": " << oss.str() << endl);
737 AggregationUtil::printConstraints(oss, fromArray);
738 BESDEBUG(debugChannel, oss.str() << endl);
739}
740
741void AggregationUtil::transferArrayConstraints(Array* pToArray, const Array& fromArrayConst, bool skipFirstFromDim,
742 bool skipFirstToDim, bool printDebug /* = false */, const std::string& debugChannel /* = "agg_util" */)
743{
744 VALID_PTR(pToArray);
745 Array& fromArray = const_cast<Array&>(fromArrayConst);
746
747 // Make sure there's no constraints on output. Shouldn't be, but...
748 pToArray->reset_constraint();
749
750 // Ensure the dimensionalities will work out.
751 int skipDelta = ((skipFirstFromDim) ? (1) : (0));
752 // If skipping output as well, subtract out the delta.
753 // If we go negative, also an error.
754 if (skipFirstToDim) {
755 skipDelta -= 1;
756 }
757 if (skipDelta < 0 || (pToArray->dimensions() + skipDelta != const_cast<Array&>(fromArrayConst).dimensions())) {
758 throw AggregationException("AggregationUtil::transferArrayConstraints: "
759 "Mismatched dimensionalities!");
760 }
761
762 if (printDebug) {
763 BESDEBUG(debugChannel,
764 "Printing constraints on fromArray name= " << fromArray.name() << " before transfer..." << endl);
765 printConstraintsToDebugChannel(debugChannel, fromArray);
766 }
767
768 // Only real way to the constraints is with the iterator,
769 // so we'll iterator on the fromArray and move
770 // to toarray iterator in sync.
771 Array::Dim_iter fromArrIt = fromArray.dim_begin();
772 Array::Dim_iter fromArrEndIt = fromArray.dim_end();
773 Array::Dim_iter toArrIt = pToArray->dim_begin();
774 for (; fromArrIt != fromArrEndIt; ++fromArrIt) {
775 if (skipFirstFromDim && (fromArrIt == fromArray.dim_begin())) {
776 // If we skip first to array as well, increment
777 // before the next call.
778 if (skipFirstToDim) {
779 ++toArrIt;
780 }
781 continue;
782 }
783// If aggregates with renaming dimensions do not match each other. SK 07/26/18
784// NCML_ASSERT_MSG(fromArrIt->name == toArrIt->name, "GAggregationUtil::transferArrayConstraints: "
785// "Expected the dimensions to have the same name but they did not.");
786 pToArray->add_constraint(toArrIt, fromArrIt->start, fromArrIt->stride, fromArrIt->stop);
787 ++toArrIt;
788 }
789
790 if (printDebug) {
791 BESDEBUG(debugChannel, "Printing constrains on pToArray after transfer..." << endl);
792 printConstraintsToDebugChannel(debugChannel, *pToArray);
793 }
794}
795
796BaseType*
797AggregationUtil::getVariableNoRecurse(const libdap::DDS& ddsConst, const std::string& name)
798{
799 BaseType* ret = 0;
800 DDS& dds = const_cast<DDS&>(ddsConst); // semantically const
801 DDS::Vars_iter endIt = dds.var_end();
802 DDS::Vars_iter it;
803 for (it = dds.var_begin(); it != endIt; ++it) {
804 BaseType* var = *it;
805 if (var && var->name() == name) {
806 ret = var;
807 break;
808 }
809 }
810 return ret;
811}
812
813// Ugh cut and pasted from the other...
814// TODO REFACTOR DDS and Constructor really need a common abstract interface,
815// like IVariableContainer that declares the iterators and associated methods.
816BaseType*
817AggregationUtil::getVariableNoRecurse(const libdap::Constructor& varContainerConst, const std::string& name)
818{
819 BaseType* ret = 0;
820 Constructor& varContainer = const_cast<Constructor&>(varContainerConst); // semantically const
821 Constructor::Vars_iter endIt = varContainer.var_end();
822 Constructor::Vars_iter it;
823 for (it = varContainer.var_begin(); it != endIt; ++it) {
824 BaseType* var = *it;
825 if (var && var->name() == name) {
826 ret = var;
827 break;
828 }
829 }
830 return ret;
831}
832
833/*static*/
834Array*
836{
837 if (!pBT) {
838 return 0;
839 }
840
841 // After switch():
842 // if Array, will be cast to Array.
843 // if Grid, will be cast data Array member of Grid.
844 // Other types, will be null.
845 libdap::Array* pArray(0);
846 switch (pBT->type()) {
847 case libdap::dods_array_c:
848 pArray = static_cast<libdap::Array*>(pBT);
849 break;
850
851 case libdap::dods_grid_c:
852 pArray = static_cast<Grid*>(pBT)->get_array();
853 break;
854
855 default:
856 pArray = 0;
857 break;
858 }
859 return pArray;
860}
861
862const Array*
863AggregationUtil::findMapByName(const libdap::Grid& inGrid, const string& findName)
864{
865 Grid& grid = const_cast<Grid&>(inGrid);
866 Array* pRet = 0;
867 Grid::Map_iter it;
868 Grid::Map_iter endIt = grid.map_end();
869 for (it = grid.map_begin(); it != endIt; ++it) {
870 if ((*it)->name() == findName) {
871 pRet = static_cast<Array*>(*it);
872 break;
873 }
874 }
875 return pRet;
876}
877
878Array* AggregationUtil::readDatasetArrayDataForAggregation(const Array& constrainedTemplateArray,
879 const std::string& varName, AggMemberDataset& dataset, const ArrayGetterInterface& arrayGetter,
880 const std::string& debugChannel)
881{
882 BES_STOPWATCH_START(MODULE, prolog_au + "Timing");
883
884 const libdap::DDS* pDDS = dataset.getDDS();
885 NCML_ASSERT_MSG(pDDS, "GridAggregateOnOuterDimension::read(): Got a null DataDDS "
886 "while loading dataset = " + dataset.getLocation());
887
888 // Grab the Array from the DataDDS with the getter
889 Array* pDatasetArray = arrayGetter.readAndGetArray(varName, *pDDS, &constrainedTemplateArray, debugChannel);
890 NCML_ASSERT_MSG(pDatasetArray, "In aggregation member dataset, failed to get the array! "
891 "Dataset location = " + dataset.getLocation());
892
893 // Make sure that the data was read in or I dunno what went on.
894 if (!pDatasetArray->read_p()) {
895 NCML_ASSERT_MSG(pDatasetArray->read_p(),
896 "AggregationUtil::addDatasetArrayDataToAggregationOutputArray: pDatasetArray was not read_p()!");
897 }
898
899 // Make sure it matches the prototype or somthing went wrong
900 if (!AggregationUtil::doTypesMatch(constrainedTemplateArray, *pDatasetArray)) {
902 "Invalid aggregation! "
903 "AggregationUtil::addDatasetArrayDataToAggregationOutputArray: "
904 "We found the aggregation variable name=" + varName
905 + " but it was not of the same type as the prototype variable!");
906 }
907
908 // Make sure the subshapes match! (true means check dimension names too... debate this)
909 if (!AggregationUtil::doShapesMatch(constrainedTemplateArray, *pDatasetArray, true)) {
911 "Invalid aggregation! "
912 "AggregationUtil::addDatasetArrayDataToAggregationOutputArray: "
913 "We found the aggregation variable name=" + varName
914 + " but it was not of the same shape as the prototype!");
915 }
916
917 // Make sure the length of the data array also matches the proto
918 if (constrainedTemplateArray.length() != pDatasetArray->length()) {
919 NCML_ASSERT_MSG(constrainedTemplateArray.length() == pDatasetArray->length(),
920 "AggregationUtil::addDatasetArrayDataToAggregationOutputArray: "
921 "The prototype array and the loaded dataset array size()'s were not equal, even "
922 "though their shapes matched. Logic problem.");
923 }
924
925 return pDatasetArray;
926}
927
928void AggregationUtil::addDatasetArrayDataToAggregationOutputArray(libdap::Array& oOutputArray, unsigned int atIndex,
929 const Array& constrainedTemplateArray, const std::string& varName, AggMemberDataset& dataset,
930 const ArrayGetterInterface& arrayGetter, const std::string& debugChannel)
931{
932 BES_STOPWATCH_START(MODULE, prolog_au + "Timing");
933
934 libdap::Array* pDatasetArray = readDatasetArrayDataForAggregation(constrainedTemplateArray, varName, dataset, arrayGetter,
935 debugChannel);
936
937 // FINALLY, we get to stream the data!
938 oOutputArray.set_value_slice_from_row_major_vector(*pDatasetArray, atIndex);
939
940 // Now that we have copied it - let the memory go! Free! Let the bytes be freed! - ndp 08/12/2015
941 pDatasetArray->clear_local_data();
942}
943
944void AggregationUtil::gatherMetadataChangesFrom(BaseType* pIntoVar, const BaseType& fromVarC)
945{
946 BaseType& fromVar = const_cast<BaseType&>(fromVarC); //semantic const
947 // The output will end up here.
948 AttrTable finalAT;
949
950 // First, seed it with the changes in the fromVar.
951 unionAttrsInto(&finalAT, fromVar.get_attr_table());
952
953 // Then union in the items from the to var
954 unionAttrsInto(&finalAT, pIntoVar->get_attr_table());
955
956 // HACK BUG In the set_attr_table call through AttrTable operator=
957 // means we keep bad memory around. Workaround until fixed!
958 pIntoVar->get_attr_table().erase();
959
960 // Finally, replace the output var's table with the constructed one!
961 pIntoVar->set_attr_table(finalAT);
962}
963
964} // namespace agg_util
virtual const libdap::DDS * getDDS()=0
const std::string & getLocation() const
static void addDatasetArrayDataToAggregationOutputArray(libdap::Array &oOutputArray, unsigned int atIndex, const libdap::Array &constrainedTemplateArray, const string &varName, AggMemberDataset &dataset, const ArrayGetterInterface &arrayGetter, const string &debugChannel)
static void printConstraintsToDebugChannel(const std::string &debugChannel, const libdap::Array &fromArray)
static void gatherMetadataChangesFrom(libdap::BaseType *pIntoVar, const libdap::BaseType &fromVar)
static LibdapType * findTypedVariableAtDDSTopLevel(const libdap::DDS &dds, const string &name)
static bool doTypesMatch(const libdap::Array &lhs, const libdap::Array &rhs)
static unsigned int collectVariableArraysInOrder(std::vector< libdap::Array * > &varArrays, const std::string &collectVarName, const ConstDDSList &datasetsInOrder)
static void transferArrayConstraints(libdap::Array *pToArray, const libdap::Array &fromArray, bool skipFirstFromDim, bool skipFirstToDim, bool printDebug=false, const std::string &debugChannel="agg_util")
static void printDimensions(std::ostream &os, const libdap::Array &fromArray)
static libdap::Array * readDatasetArrayDataForAggregation(const libdap::Array &constrainedTemplateArray, const std::string &varName, AggMemberDataset &dataset, const ArrayGetterInterface &arrayGetter, const std::string &debugChannel)
static void performUnionAggregation(libdap::DDS *pOutputUnion, const ConstDDSList &datasetsInOrder)
static libdap::BaseType * getVariableNoRecurse(const libdap::DDS &dds, const std::string &name)
static void unionAllVariablesInto(libdap::DDS *pOutputUnion, const ConstDDSList &datasetsInOrder)
static libdap::Array * getAsArrayIfPossible(libdap::BaseType *pBT)
static bool couldBeCoordinateVariable(libdap::BaseType *pBT)
static const libdap::Array * findMapByName(const libdap::Grid &inGrid, const std::string &findName)
static bool addCopyOfVariableIfNameIsAvailable(libdap::DDS *pOutDDS, const libdap::BaseType &varProto, bool add_at_top=false)
static void addOrReplaceVariableForName(libdap::DDS *pOutDDS, const libdap::BaseType &varProto)
static void unionAttrsInto(libdap::AttrTable *pOut, const libdap::AttrTable &fromTable)
static bool doShapesMatch(const libdap::Array &lhs, const libdap::Array &rhs, bool checkDimNames)
static bool findAttribute(const libdap::AttrTable &inTable, const string &name, libdap::AttrTable::Attr_iter &attr)
static void resetCVInsertionPosition()
static bool validateArrayTypesAndShapesMatch(const std::vector< libdap::Array * > &arrays, bool enforceMatchingDimNames)
static void printConstraints(std::ostream &os, const libdap::Array &fromArray)
static libdap::BaseType * findVariableAtDDSTopLevel(const libdap::DDS &dds, const string &name)
STL class.
STL iterator class.
Helper class for temporarily hijacking an existing dhi to load a DDX response for one particular file...
virtual libdap::Array * readAndGetArray(const std::string &name, const libdap::DDS &dds, const libdap::Array *const pConstraintTemplate, const std::string &debugChannel) const =0
virtual libdap::Array * readAndGetArray(const std::string &name, const libdap::DDS &dds, const libdap::Array *const pConstraintTemplate, const std::string &debugChannel) const
virtual TopLevelArrayGetter * clone() const
virtual libdap::Array * readAndGetArray(const std::string &name, const libdap::DDS &dds, const libdap::Array *const pConstraintTemplate, const std::string &debugChannel) const
virtual TopLevelGridDataArrayGetter * clone() const
virtual TopLevelGridMapArrayGetter * clone() const
virtual libdap::Array * readAndGetArray(const std::string &name, const libdap::DDS &dds, const libdap::Array *const pConstraintTemplate, const std::string &debugChannel) const