bes Updated for version 3.20.10
h5commoncfdap.cc
Go to the documentation of this file.
1// This file is part of hdf5_handler: an HDF5 file handler for the OPeNDAP
2// data server.
3
4// Copyright (c) 2011-2016 The HDF Group, Inc. and OPeNDAP, Inc.
5//
6// This is free software; you can redistribute it and/or modify it under the
7// terms of the GNU Lesser General Public License as published by the Free
8// Software Foundation; either version 2.1 of the License, or (at your
9// option) any later version.
10//
11// This software is distributed in the hope that it will be useful, but
12// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14// License for more details.
15//
16// You should have received a copy of the GNU Lesser General Public
17// License along with this library; if not, write to the Free Software
18// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19//
20// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
21// You can contact The HDF Group, Inc. at 1800 South Oak Street,
22// Suite 203, Champaign, IL 61820
23
32
33#include <sys/types.h>
34#include <sys/stat.h>
35#include <fcntl.h>
36#include <unistd.h>
37#include <iostream>
38#include <sstream>
39
40#include <libdap/InternalErr.h>
41#include <BESDebug.h>
42
43#include "HDF5RequestHandler.h"
44#include "h5cfdaputil.h"
45#include "h5gmcfdap.h"
46#include "HDF5CFByte.h"
47#include "HDF5CFInt8.h"
48#include "HDF5CFUInt16.h"
49#include "HDF5CFInt16.h"
50#include "HDF5CFUInt32.h"
51#include "HDF5CFInt32.h"
52#include "HDF5CFFloat32.h"
53#include "HDF5CFFloat64.h"
54#include "HDF5CFInt64.h"
55#include "HDF5CFUInt64.h"
56#include "HDF5CFStr.h"
57#include "HDF5CFArray.h"
58#include "HDF5CFGeoCF1D.h"
59#include "HDF5CFGeoCFProj.h"
60
61//#include "HDF5Int64.h"
62#include "HDF5CFUtil.h"
63
64using namespace std;
65using namespace libdap;
66using namespace HDF5CF;
67
68// Generate DDS from one variable
69void gen_dap_onevar_dds(DDS &dds, const HDF5CF::Var* var, const hid_t file_id, const string & filename)
70{
71
72 BESDEBUG("h5", "Coming to gen_dap_onevar_dds() "<<endl);
73 const vector<HDF5CF::Dimension *>& dims = var->getDimensions();
74
75 if (dims.empty()) {
76 // Adding 64-bit integer support for DMR
77 if (H5INT64 == var->getType() || H5UINT64 == var->getType()){
78 DMR * dmr = HDF5RequestHandler::get_dmr_64bit_int();
79 if(dmr == NULL)
80 return;
81 else {
82 D4Group* root_grp = dmr->root();
83 if(H5INT64 == var->getType()) {
84 HDF5CFInt64 *sca_int64 = NULL;
85 try {
86 sca_int64 = new HDF5CFInt64(var->getNewName(), var->getFullPath(), filename);
87 }
88 catch (...) {
89 string error_message = "Cannot allocate the HDF5CFInt64: " + error_message;
90 throw InternalErr(__FILE__, __LINE__, error_message);
91 }
92 sca_int64->set_is_dap4(true);
93 map_cfh5_var_attrs_to_dap4_int64(var,sca_int64);
94 root_grp->add_var_nocopy(sca_int64);
95
96 }
97 else if(H5UINT64 == var->getType()) {
98 HDF5CFUInt64 *sca_uint64 = NULL;
99 try {
100 sca_uint64 = new HDF5CFUInt64(var->getNewName(), var->getFullPath(), filename);
101 }
102 catch (...) {
103 throw InternalErr(__FILE__, __LINE__, "Cannot allocate the HDF5CFInt64.");
104 }
105 sca_uint64->set_is_dap4(true);
106 map_cfh5_var_attrs_to_dap4_int64(var,sca_uint64);
107 root_grp->add_var_nocopy(sca_uint64);
108
109 }
110
111 }
112 }
113 else if (H5FSTRING == var->getType() || H5VSTRING == var->getType()) {
114 HDF5CFStr *sca_str = NULL;
115 try {
116 sca_str = new HDF5CFStr(var->getNewName(), filename, var->getFullPath());
117 }
118 catch (...) {
119 throw InternalErr(__FILE__, __LINE__, "Cannot allocate the HDF5CFStr.");
120 }
121 dds.add_var(sca_str);
122 delete sca_str;
123 }
124 else {
125 switch (var->getType()) {
126
127 case H5UCHAR: {
128 HDF5CFByte * sca_uchar = NULL;
129 try {
130 sca_uchar = new HDF5CFByte(var->getNewName(), var->getFullPath(), filename);
131 }
132 catch (...) {
133 throw InternalErr(__FILE__, __LINE__, "Cannot allocate the HDF5CFByte.");
134 }
135 dds.add_var(sca_uchar);
136 delete sca_uchar;
137
138 }
139 break;
140 case H5CHAR:
141 case H5INT16: {
142 HDF5CFInt16 * sca_int16 = NULL;
143 try {
144 sca_int16 = new HDF5CFInt16(var->getNewName(), var->getFullPath(), filename);
145 }
146 catch (...) {
147 throw InternalErr(__FILE__, __LINE__, "Cannot allocate the HDF5CFInt16.");
148 }
149 dds.add_var(sca_int16);
150 delete sca_int16;
151 }
152 break;
153 case H5UINT16: {
154 HDF5CFUInt16 * sca_uint16 = NULL;
155 try {
156 sca_uint16 = new HDF5CFUInt16(var->getNewName(), var->getFullPath(), filename);
157 }
158 catch (...) {
159 throw InternalErr(__FILE__, __LINE__, "Cannot allocate the HDF5CFUInt16.");
160 }
161 dds.add_var(sca_uint16);
162 delete sca_uint16;
163 }
164 break;
165 case H5INT32: {
166 HDF5CFInt32 * sca_int32 = NULL;
167 try {
168 sca_int32 = new HDF5CFInt32(var->getNewName(), var->getFullPath(), filename);
169 }
170 catch (...) {
171 throw InternalErr(__FILE__, __LINE__, "Cannot allocate the HDF5CFInt32.");
172 }
173 dds.add_var(sca_int32);
174 delete sca_int32;
175 }
176 break;
177 case H5UINT32: {
178 HDF5CFUInt32 * sca_uint32 = NULL;
179 try {
180 sca_uint32 = new HDF5CFUInt32(var->getNewName(), var->getFullPath(), filename);
181 }
182 catch (...) {
183 throw InternalErr(__FILE__, __LINE__, "Cannot allocate the HDF5CFUInt32.");
184 }
185 dds.add_var(sca_uint32);
186 delete sca_uint32;
187 }
188 break;
189 case H5FLOAT32: {
190 HDF5CFFloat32 * sca_float32 = NULL;
191 try {
192 sca_float32 = new HDF5CFFloat32(var->getNewName(), var->getFullPath(), filename);
193 }
194 catch (...) {
195 throw InternalErr(__FILE__, __LINE__, "Cannot allocate the HDF5CFFloat32.");
196 }
197 dds.add_var(sca_float32);
198 delete sca_float32;
199 }
200 break;
201 case H5FLOAT64: {
202 HDF5CFFloat64 * sca_float64 = NULL;
203 try {
204 sca_float64 = new HDF5CFFloat64(var->getNewName(), var->getFullPath(), filename);
205 }
206 catch (...) {
207 throw InternalErr(__FILE__, __LINE__, "Cannot allocate the HDF5CFFloat64.");
208 }
209 dds.add_var(sca_float64);
210 delete sca_float64;
211
212 }
213 break;
214 default:
215 throw InternalErr(__FILE__, __LINE__, "unsupported data type.");
216 }
217 }
218 }
219
220 else {
221
222 // 64-bit integer support
223 // DMR CHECK
224 bool dap4_int64 = false;
225 if(var->getType() == H5INT64 || var->getType()==H5UINT64) {
226 DMR * dmr = HDF5RequestHandler::get_dmr_64bit_int();
227 if(dmr == NULL)
228 return;
229 else
230 dap4_int64 = true;
231 }
232
233#if 0
234 else {
235 D4Group* root_grp = dmr->root();
236 BaseType *bt = NULL;
237 bt = new(HDF5Int64)(var->getNewName(),var->getFullPath(),filename);
238 bt->transform_to_dap4(root_grp,root_grp);
239 delete bt;
240 return;
241 }
242#endif
243 BaseType *bt = NULL;
244
245 if(true == dap4_int64) {
246 if(var->getType() == H5INT64)
247 bt = new(HDF5CFInt64)(var->getNewName(),var->getFullPath());
248 else if(var->getType() == H5UINT64)
249 bt = new(HDF5CFUInt64)(var->getNewName(),var->getFullPath());
250 }
251
252 else {
253 switch (var->getType()) {
254#define HANDLE_CASE(tid,type) \
255 case tid: \
256 bt = new (type)(var->getNewName(),var->getFullPath()); \
257 break;
258 HANDLE_CASE(H5FLOAT32, HDF5CFFloat32)
259 ;
260 HANDLE_CASE(H5FLOAT64, HDF5CFFloat64)
261 ;
262 HANDLE_CASE(H5CHAR, HDF5CFInt16)
263 ;
264 HANDLE_CASE(H5UCHAR, HDF5CFByte)
265 ;
266 HANDLE_CASE(H5INT16, HDF5CFInt16)
267 ;
268 HANDLE_CASE(H5UINT16, HDF5CFUInt16)
269 ;
270 HANDLE_CASE(H5INT32, HDF5CFInt32)
271 ;
272 HANDLE_CASE(H5UINT32, HDF5CFUInt32)
273 ;
274 HANDLE_CASE(H5FSTRING, Str)
275 ;
276 HANDLE_CASE(H5VSTRING, Str)
277 ;
278 default:
279 throw InternalErr(__FILE__, __LINE__, "unsupported data type.");
280#undef HANDLE_CASE
281 }
282 }
283
284 vector<HDF5CF::Dimension*>::const_iterator it_d;
285 vector<size_t> dimsizes;
286 dimsizes.resize(var->getRank());
287 for (int i = 0; i < var->getRank(); i++)
288 dimsizes[i] = (dims[i])->getSize();
289
290 HDF5CFArray *ar = NULL;
291 try {
292 ar = new HDF5CFArray(var->getRank(), file_id, filename, var->getType(), dimsizes, var->getFullPath(),
293 var->getTotalElems(), CV_UNSUPPORTED, false, var->getCompRatio(), false,var->getNewName(), bt);
294 }
295 catch (...) {
296 delete bt;
297 throw InternalErr(__FILE__, __LINE__, "Cannot allocate the HDF5CFStr.");
298 }
299
300 for (it_d = dims.begin(); it_d != dims.end(); ++it_d) {
301 if ("" == (*it_d)->getNewName())
302 ar->append_dim((*it_d)->getSize());
303 else
304 ar->append_dim((*it_d)->getSize(), (*it_d)->getNewName());
305 }
306
307 // When handling DAP4 CF, we need to generate dmr for 64-bit integer separately.
308 if(dap4_int64 == true) {
309 DMR * dmr = HDF5RequestHandler::get_dmr_64bit_int();
310 D4Group* root_grp = dmr->root();
311 // Dimensions need to be translated.
312 BaseType* d4_var = ar->h5cfdims_transform_to_dap4_int64(root_grp);
313 // Attributes.
314 map_cfh5_var_attrs_to_dap4_int64(var,d4_var);
315 root_grp->add_var_nocopy(d4_var);
316 }
317 else
318 dds.add_var(ar);
319
320 delete bt;
321 delete ar;
322 }
323
324 return;
325
326}
327
328// Currently only when the datatype of fillvalue is not the same as the datatype of the variable,
329// special attribute handling is needed.
330bool need_special_attribute_handling(const HDF5CF::Attribute* attr, const HDF5CF::Var* var)
331{
332 return ((("_FillValue" == attr->getNewName()) && (var->getType() != attr->getType())) ? true : false);
333}
334
335// Currently we only handle the case when the datatype of _FillValue is not the same as the variable datatype.
336void gen_dap_special_oneobj_das(AttrTable*at, const HDF5CF::Attribute* attr, const HDF5CF::Var* var)
337{
338
339 BESDEBUG("h5", "Coming to gen_dap_special_oneobj_das() "<<endl);
340 if (attr->getCount() != 1) throw InternalErr(__FILE__, __LINE__, "FillValue attribute can only have one element.");
341
342 H5DataType var_dtype = var->getType();
343 if ((true == HDF5RequestHandler::get_fillvalue_check())
344 && (false == is_fvalue_valid(var_dtype, attr))) {
345 string msg = "The attribute value is out of the range.\n";
346 msg += "The variable name: " + var->getNewName() + "\n";
347 msg += "The attribute name: " + attr->getNewName() + "\n";
348 msg += "The error occurs inside the gen_dap_special_oneobj_das function in h5commoncfdap.cc.";
349 throw InternalErr(msg);
350 }
351 string print_rep = HDF5CFDAPUtil::print_attr(attr->getType(), 0, (void*) (&(attr->getValue()[0])));
352 at->append_attr(attr->getNewName(), HDF5CFDAPUtil::print_type(var_dtype), print_rep);
353}
354
355// Check if this fillvalue is in the valid datatype range when the fillvalue datatype is changed to follow the CF
356bool is_fvalue_valid(H5DataType var_dtype, const HDF5CF::Attribute* attr)
357{
358
359 BESDEBUG("h5", "Coming to is_fvalue_valid() "<<endl);
360 bool ret_value = true;
361 // We only check 8-bit and 16-bit integers.
362 switch (attr->getType()) {
363 case H5CHAR: {
364 signed char final_fill_value = *((signed char*) ((void*) (&(attr->getValue()[0]))));
365 if ((var_dtype == H5UCHAR) && (final_fill_value<0))
366 ret_value = false;
367 return ret_value;
368
369 }
370 case H5INT16: {
371 short final_fill_value = *((short*) ((void*) (&(attr->getValue()[0]))));
372 if ((var_dtype == H5UCHAR) &&(final_fill_value > 255 || final_fill_value < 0))
373 ret_value = false;
374
375 // No need to check the var_dtype==H5CHAR case since it is mapped to int16.
376 else if ((var_dtype == H5UINT16) && (final_fill_value < 0))
377 ret_value = false;
378 return ret_value;
379 }
380 case H5UINT16: {
381 unsigned short final_fill_value = *((unsigned short*) ((void*) (&(attr->getValue()[0]))));
382 if ((var_dtype == H5UCHAR) &&(final_fill_value > 255)) {
383 ret_value = false;
384 }
385 else if ((var_dtype == H5INT16) && (final_fill_value >32767)){
386 ret_value = false;
387 }
388 return ret_value;
389
390 }
391 // We are supposed to check the case when the datatype of fillvalue is unsigned char.
392 // However, since the variable type signed char is always mapped to int16, so there
393 // will never be an overflow case(the signed char case is the only possible one).
394 // Still the data producer should not do this. We will not check this in the handler.KY 2016-03-04
395#if 0
396 case H5UCHAR:
397 {
398 unsigned char final_fill_value = *((unsigned char*)((void*)(&(attr->getValue()[0]))));
399 if(var_dtype == H5CHAR) {
400 if(final_fill_value >127)
401 ret_value = false;
402 }
403 return ret_value;
404 }
405
406 case H5UCHAR:
407 case H5INT32:
408 case H5UINT32:
409#endif
410
411 default:
412 return ret_value;
413 }
414
415}
416// Leave the old code for the time being. KY 2015-05-07
417#if 0
418void gen_dap_special_oneobj_das(AttrTable*at, const HDF5CF::Attribute* attr,const HDF5CF::Var* var) {
419
420 if (attr->getCount() != 1)
421 throw InternalErr(__FILE__,__LINE__,"FillValue attribute can only have one element.");
422
423 H5DataType var_dtype = var->getType();
424 switch(var_dtype) {
425
426 case H5UCHAR:
427 {
428 unsigned char final_fill_value = *((unsigned char*)((void*)(&(attr->getValue()[0]))));
429 print_rep = HDF5CFDAPUtil::print_attr(var_dtype,0,(void*)&final_fill_value);
430 }
431 break;
432
433 case H5CHAR:
434 {
435 // Notice HDF5 native char maps to DAP int16.
436 short final_fill_value = *((short*)((void*)(&(attr->getValue()[0]))));
437 print_rep = HDF5CFDAPUtil::print_attr(var_dtype,0,(void*)&final_fill_value);
438 }
439 break;
440 case H5INT16:
441 {
442 short final_fill_value = *((short*)((void*)(&(attr->getValue()[0]))));
443 print_rep = HDF5CFDAPUtil::print_attr(var_dtype,0,(void*)&final_fill_value);
444 }
445 break;
446 case H5UINT16:
447 {
448 unsigned short final_fill_value = *((unsigned short*)((void*)(&(attr->getValue()[0]))));
449 print_rep = HDF5CFDAPUtil::print_attr(var_dtype,0,(void*)&final_fill_value);
450 }
451 break;
452
453 case H5INT32:
454 {
455 int final_fill_value = *((int*)((void*)(&(attr->getValue()[0]))));
456 print_rep = HDF5CFDAPUtil::print_attr(var_dtype,0,(void*)&final_fill_value);
457 }
458 break;
459 case H5UINT32:
460 {
461 unsigned int final_fill_value = *((unsigned int*)((void*)(&(attr->getValue()[0]))));
462 print_rep = HDF5CFDAPUtil::print_attr(var_dtype,0,(void*)&final_fill_value);
463 }
464 break;
465 case H5FLOAT32:
466 {
467 float final_fill_value = *((float*)((void*)(&(attr->getValue()[0]))));
468// memcpy(&(attr->getValue()[0]),(void*)(&final_fill_value),sizeof(float));
469//cerr<<"final_fill_value is "<<final_fill_value <<endl;
470 print_rep = HDF5CFDAPUtil::print_attr(var_dtype,0,(void*)&final_fill_value);
471 }
472 break;
473 case H5FLOAT64:
474 {
475 double final_fill_value = *((double*)((void*)(&(attr->getValue()[0]))));
476 print_rep = HDF5CFDAPUtil::print_attr(var_dtype,0,(void*)&final_fill_value);
477 }
478 break;
479 default:
480 throw InternalErr(__FILE__,__LINE__,"unsupported data type.");
481 }
482
483 at->append_attr(attr->getNewName(), HDF5CFDAPUtil::print_type(var_dtype), print_rep);
484}
485#endif
486
487// Generate DAS from one variable
488void gen_dap_oneobj_das(AttrTable*at, const HDF5CF::Attribute* attr, const HDF5CF::Var *var)
489{
490
491 BESDEBUG("h5", "Coming to gen_dap_oneobj_das() "<<endl);
492 // DMR support for 64-bit integer
493 if (H5INT64 == attr->getType() || H5UINT64 == attr->getType()) {
494 // TODO: Add code to tackle DMR for the variable datatype that is not 64-bit integer.
495 return;
496
497 }
498 else if ((H5FSTRING == attr->getType()) || (H5VSTRING == attr->getType())) {
499 gen_dap_str_attr(at, attr);
500 }
501 else {
502
503 if (NULL == var) {
504
505 // HDF5 Native Char maps to DAP INT16(DAP doesn't have the corresponding datatype), so needs to
506 // obtain the mem datatype.
507 size_t mem_dtype_size = (attr->getBufSize()) / (attr->getCount());
508 H5DataType mem_dtype = HDF5CFDAPUtil::get_mem_dtype(attr->getType(), mem_dtype_size);
509
510 for (unsigned int loc = 0; loc < attr->getCount(); loc++) {
511 string print_rep = HDF5CFDAPUtil::print_attr(mem_dtype, loc, (void*) &(attr->getValue()[0]));
512 at->append_attr(attr->getNewName(), HDF5CFDAPUtil::print_type(attr->getType()), print_rep);
513 }
514
515 }
516
517 else {
518
519 // The datatype of _FillValue attribute needs to be the same as the variable datatype for an netCDF C file.
520 // To make OPeNDAP's netCDF file out work, we need to change the attribute datatype of _FillValue to be the
521 // same as the variable datatype if they are not the same. An OMI-Aura_L2-OMUVB file has such a case.
522 // The datatype of "TerrainHeight" is int32 but the datatype of the fillvalue is int16.
523 // KY 2012-11-20
524 bool special_attr_handling = need_special_attribute_handling(attr, var);
525 if (true == special_attr_handling) {
526 gen_dap_special_oneobj_das(at, attr, var);
527 }
528
529 else {
530
531 // HDF5 Native Char maps to DAP INT16(DAP doesn't have the corresponding datatype), so needs to
532 // obtain the mem datatype.
533 size_t mem_dtype_size = (attr->getBufSize()) / (attr->getCount());
534 H5DataType mem_dtype = HDF5CFDAPUtil::get_mem_dtype(attr->getType(), mem_dtype_size);
535
536 for (unsigned int loc = 0; loc < attr->getCount(); loc++) {
537 string print_rep = HDF5CFDAPUtil::print_attr(mem_dtype, loc, (void*) &(attr->getValue()[0]));
538 at->append_attr(attr->getNewName(), HDF5CFDAPUtil::print_type(attr->getType()), print_rep);
539 }
540 }
541 }
542 }
543}
544
545// Generate DMR from one variable
546void gen_dap_onevar_dmr(libdap::D4Group* d4_grp, const HDF5CF::Var* var, const hid_t file_id, const string & filename) {
547
548 BESDEBUG("h5", "Coming to gen_dap_onevar_dmr() "<<endl);
549
550 const vector<HDF5CF::Dimension *>& dims = var->getDimensions();
551
552 if (dims.empty()) {
553
554 if (H5FSTRING == var->getType() || H5VSTRING == var->getType()) {
555 HDF5CFStr *sca_str = NULL;
556 try {
557 sca_str = new HDF5CFStr(var->getNewName(), filename, var->getFullPath());
558 sca_str->set_is_dap4(true);
559 map_cfh5_var_attrs_to_dap4(var,sca_str);
560 }
561 catch (...) {
562 delete sca_str;
563 throw InternalErr(__FILE__, __LINE__, "Cannot allocate the HDF5CFStr.");
564 }
565 d4_grp->add_var_nocopy(sca_str);
566 }
567 else {
568 switch (var->getType()) {
569
570 case H5UCHAR: {
571 HDF5CFByte * sca_uchar = NULL;
572 try {
573 sca_uchar = new HDF5CFByte(var->getNewName(), var->getFullPath(), filename);
574 sca_uchar->set_is_dap4(true);
575 map_cfh5_var_attrs_to_dap4(var,sca_uchar);
576 }
577 catch (...) {
578 delete sca_uchar;
579 throw InternalErr(__FILE__, __LINE__, "Cannot allocate the HDF5CFByte.");
580 }
581 d4_grp->add_var_nocopy(sca_uchar);
582 }
583 break;
584 case H5CHAR: {
585 HDF5CFInt8 * sca_char = NULL;
586 try {
587 sca_char = new HDF5CFInt8(var->getNewName(), var->getFullPath(), filename);
588 sca_char->set_is_dap4(true);
589 map_cfh5_var_attrs_to_dap4(var,sca_char);
590 }
591 catch (...) {
592 delete sca_char;
593 throw InternalErr(__FILE__, __LINE__, "Cannot allocate the HDF5CFByte.");
594 }
595 d4_grp->add_var_nocopy(sca_char);
596 }
597 break;
598
599 case H5INT16: {
600 HDF5CFInt16 * sca_int16 = NULL;
601 try {
602 sca_int16 = new HDF5CFInt16(var->getNewName(), var->getFullPath(), filename);
603 sca_int16->set_is_dap4(true);
604 map_cfh5_var_attrs_to_dap4(var,sca_int16);
605
606 }
607 catch (...) {
608 delete sca_int16;
609 throw InternalErr(__FILE__, __LINE__, "Cannot allocate the HDF5CFInt16.");
610 }
611 d4_grp->add_var_nocopy(sca_int16);
612 }
613 break;
614 case H5UINT16: {
615 HDF5CFUInt16 * sca_uint16 = NULL;
616 try {
617 sca_uint16 = new HDF5CFUInt16(var->getNewName(), var->getFullPath(), filename);
618 sca_uint16->set_is_dap4(true);
619 map_cfh5_var_attrs_to_dap4(var,sca_uint16);
620 }
621 catch (...) {
622 delete sca_uint16;
623 throw InternalErr(__FILE__, __LINE__, "Cannot allocate the HDF5CFUInt16.");
624 }
625 d4_grp->add_var_nocopy(sca_uint16);
626 }
627 break;
628 case H5INT32: {
629 HDF5CFInt32 * sca_int32 = NULL;
630 try {
631 sca_int32 = new HDF5CFInt32(var->getNewName(), var->getFullPath(), filename);
632 sca_int32->set_is_dap4(true);
633 map_cfh5_var_attrs_to_dap4(var,sca_int32);
634 }
635 catch (...) {
636 delete sca_int32;
637 throw InternalErr(__FILE__, __LINE__, "Cannot allocate the HDF5CFInt32.");
638 }
639 d4_grp->add_var_nocopy(sca_int32);
640 }
641 break;
642 case H5UINT32: {
643 HDF5CFUInt32 * sca_uint32 = NULL;
644 try {
645 sca_uint32 = new HDF5CFUInt32(var->getNewName(), var->getFullPath(), filename);
646 sca_uint32->set_is_dap4(true);
647 map_cfh5_var_attrs_to_dap4(var,sca_uint32);
648 }
649 catch (...) {
650 delete sca_uint32;
651 throw InternalErr(__FILE__, __LINE__, "Cannot allocate the HDF5CFUInt32.");
652 }
653 d4_grp->add_var_nocopy(sca_uint32);
654 }
655 break;
656 case H5INT64: {
657 HDF5CFInt64 * sca_int64 = NULL;
658 try {
659 sca_int64 = new HDF5CFInt64(var->getNewName(), var->getFullPath(), filename);
660 sca_int64->set_is_dap4(true);
661 map_cfh5_var_attrs_to_dap4(var,sca_int64);
662 }
663 catch (...) {
664 delete sca_int64;
665 throw InternalErr(__FILE__, __LINE__, "Cannot allocate the HDF5CFInt64.");
666 }
667 d4_grp->add_var_nocopy(sca_int64);
668 }
669 break;
670 case H5UINT64: {
671 HDF5CFUInt64 * sca_uint64 = NULL;
672 try {
673 sca_uint64 = new HDF5CFUInt64(var->getNewName(), var->getFullPath(), filename);
674 sca_uint64->set_is_dap4(true);
675 map_cfh5_var_attrs_to_dap4(var,sca_uint64);
676 }
677 catch (...) {
678 delete sca_uint64;
679 throw InternalErr(__FILE__, __LINE__, "Cannot allocate the HDF5CFUInt64.");
680 }
681 d4_grp->add_var_nocopy(sca_uint64);
682 }
683 break;
684 case H5FLOAT32: {
685 HDF5CFFloat32 * sca_float32 = NULL;
686 try {
687 sca_float32 = new HDF5CFFloat32(var->getNewName(), var->getFullPath(), filename);
688 sca_float32->set_is_dap4(true);
689 map_cfh5_var_attrs_to_dap4(var,sca_float32);
690 }
691 catch (...) {
692 delete sca_float32;
693 throw InternalErr(__FILE__, __LINE__, "Cannot allocate the HDF5CFFloat32.");
694 }
695 d4_grp->add_var_nocopy(sca_float32);
696 }
697 break;
698 case H5FLOAT64: {
699 HDF5CFFloat64 * sca_float64 = NULL;
700 try {
701 sca_float64 = new HDF5CFFloat64(var->getNewName(), var->getFullPath(), filename);
702 sca_float64->set_is_dap4(true);
703 map_cfh5_var_attrs_to_dap4(var,sca_float64);
704 }
705 catch (...) {
706 delete sca_float64;
707 throw InternalErr(__FILE__, __LINE__, "Cannot allocate the HDF5CFFloat64.");
708 }
709 d4_grp->add_var_nocopy(sca_float64);
710 }
711 break;
712 default:
713 throw InternalErr(__FILE__, __LINE__, "unsupported data type.");
714 }
715 }
716 }
717
718 else {
719
720 BaseType *bt = NULL;
721
722 switch (var->getType()) {
723#define HANDLE_CASE(tid,type) \
724 case tid: \
725 bt = new (type)(var->getNewName(),var->getFullPath()); \
726 break;
727 HANDLE_CASE(H5FLOAT32, HDF5CFFloat32)
728 ;
729 HANDLE_CASE(H5FLOAT64, HDF5CFFloat64)
730 ;
731 HANDLE_CASE(H5CHAR, HDF5CFInt8)
732 ;
733 HANDLE_CASE(H5UCHAR, HDF5CFByte)
734 ;
735 HANDLE_CASE(H5INT16, HDF5CFInt16)
736 ;
737 HANDLE_CASE(H5UINT16, HDF5CFUInt16)
738 ;
739 HANDLE_CASE(H5INT32, HDF5CFInt32)
740 ;
741 HANDLE_CASE(H5UINT32, HDF5CFUInt32)
742 ;
743 HANDLE_CASE(H5INT64, HDF5CFInt64)
744 ;
745 HANDLE_CASE(H5UINT64, HDF5CFUInt64)
746 ;
747 HANDLE_CASE(H5FSTRING, Str)
748 ;
749 HANDLE_CASE(H5VSTRING, Str)
750 ;
751 default:
752 throw InternalErr(__FILE__, __LINE__, "unsupported data type.");
753#undef HANDLE_CASE
754 }
755
756 vector<HDF5CF::Dimension*>::const_iterator it_d;
757 vector<size_t> dimsizes;
758 dimsizes.resize(var->getRank());
759 for (int i = 0; i < var->getRank(); i++)
760 dimsizes[i] = (dims[i])->getSize();
761
762 HDF5CFArray *ar = NULL;
763 try {
764 ar = new HDF5CFArray(var->getRank(), file_id, filename, var->getType(), dimsizes, var->getFullPath(),
765 var->getTotalElems(), CV_UNSUPPORTED, false, var->getCompRatio(), true,var->getNewName(), bt);
766 }
767 catch (...) {
768 delete bt;
769 throw InternalErr(__FILE__, __LINE__, "Cannot allocate the HDF5CFStr.");
770 }
771
772 for (it_d = dims.begin(); it_d != dims.end(); ++it_d) {
773 if ("" == (*it_d)->getNewName())
774 ar->append_dim((*it_d)->getSize());
775 else
776 ar->append_dim((*it_d)->getSize(), (*it_d)->getNewName());
777 }
778
779 delete bt;
780 ar->set_is_dap4(true);
781 BaseType* d4_var=ar->h5cfdims_transform_to_dap4(d4_grp);
782 map_cfh5_var_attrs_to_dap4(var,d4_var);
783 d4_grp->add_var_nocopy(d4_var);
784 delete ar;
785
786 }
787
788 return;
789
790
791
792}
793
794
795void gen_dap_str_attr(AttrTable *at, const HDF5CF::Attribute *attr)
796{
797
798 BESDEBUG("h5", "Coming to gen_dap_str_attr() "<<endl);
799 const vector<size_t>& strsize = attr->getStrSize();
800//cerr<<"in DAS strsize is "<<strsize.size() <<endl;
801 unsigned int temp_start_pos = 0;
802 bool is_cset_ascii = attr->getCsetType();
803 for (unsigned int loc = 0; loc < attr->getCount(); loc++) {
804 if (strsize[loc] != 0) {
805 string tempstring(attr->getValue().begin() + temp_start_pos,
806 attr->getValue().begin() + temp_start_pos + strsize[loc]);
807 temp_start_pos += strsize[loc];
808
809 // If the string size is longer than the current netCDF JAVA
810 // string and the "EnableDropLongString" key is turned on,
811 // No string is generated.
812 // The above statement is no longer true. The netCDF Java can handle long string
813 // attributes. The long string can be kept and I do think the
814 // performance penalty should be small. KY 2018-02-26
815 if ((attr->getNewName() != "origname") && (attr->getNewName() != "fullnamepath") && (true == is_cset_ascii))
816 tempstring = HDF5CFDAPUtil::escattr(tempstring);
817 at->append_attr(attr->getNewName(), "String", tempstring);
818 }
819 }
820}
821
822//#if 0
823// This function adds the 1-D horizontal coordinate variables as well as the dummy projection variable to the grid.
824//Note: Since we don't add these artifical CF variables to our main engineering at HDFEOS5CF.cc, the information
825// to handle DAS won't pass to DDS by the file pointer, we need to re-call the routines to check projection
826// and dimension. The time to retrieve these information is trivial compared with the whole translation.
827void add_cf_grid_cvs(DDS & dds, EOS5GridPCType cv_proj_code, float cv_point_lower, float cv_point_upper,
828 float cv_point_left, float cv_point_right, const vector<HDF5CF::Dimension*>& dims)
829{
830
831 //1. Check the projection information: we first just handled the sinusoidal projection.
832 // We also add the LAMAZ and PS support. These 1-D varaibles are the same as the sinusoidal one.
833 if (HE5_GCTP_SNSOID == cv_proj_code || HE5_GCTP_LAMAZ == cv_proj_code || HE5_GCTP_PS == cv_proj_code) {
834
835 //2. Obtain the dimension information from latitude and longitude(fieldtype =1 or fieldtype =2)
836 vector<HDF5CF::Dimension*>::const_iterator it_d;
837 string dim0name = dims[0]->getNewName();
838 int dim0size = dims[0]->getSize();
839 string dim1name = dims[1]->getNewName();
840 int dim1size = dims[1]->getSize();
841
842 //3. Add the 1-D CV variables and the dummy projection variable
843 BaseType *bt_dim0 = NULL;
844 BaseType *bt_dim1 = NULL;
845
846 HDF5CFGeoCF1D * ar_dim0 = NULL;
847 HDF5CFGeoCF1D * ar_dim1 = NULL;
848
849 try {
850
851 bt_dim0 = new (HDF5CFFloat64)(dim0name, dim0name);
852 bt_dim1 = new (HDF5CFFloat64)(dim1name, dim1name);
853
854 // Note ar_dim0 is y, ar_dim1 is x.
855 ar_dim0 = new HDF5CFGeoCF1D(HE5_GCTP_SNSOID, cv_point_upper, cv_point_lower, dim0size, dim0name, bt_dim0);
856 ar_dim0->append_dim(dim0size, dim0name);
857
858 ar_dim1 = new HDF5CFGeoCF1D(HE5_GCTP_SNSOID, cv_point_left, cv_point_right, dim1size, dim1name, bt_dim1);
859 ar_dim1->append_dim(dim1size, dim1name);
860 dds.add_var(ar_dim0);
861 dds.add_var(ar_dim1);
862
863 }
864 catch (...) {
865 if (bt_dim0) delete bt_dim0;
866 if (bt_dim1) delete bt_dim1;
867 if (ar_dim0) delete ar_dim0;
868 if (ar_dim1) delete ar_dim1;
869 throw InternalErr(__FILE__, __LINE__, "Unable to allocate the HDFEOS2GeoCF1D instance.");
870 }
871
872 if (bt_dim0) delete bt_dim0;
873 if (bt_dim1) delete bt_dim1;
874 if (ar_dim0) delete ar_dim0;
875 if (ar_dim1) delete ar_dim1;
876
877 }
878}
879
880// This function adds the grid mapping variables.
881void add_cf_grid_mapinfo_var(DDS & dds, const EOS5GridPCType grid_proj_code, const unsigned short g_suffix)
882{
883
884 //Add the dummy projection variable. The attributes of this variable can be used to store the grid mapping info.
885 // To handle multi-grid cases, we need to add suffixes to distinguish them.
886 string cf_projection_base = "eos_cf_projection";
887
888 HDF5CFGeoCFProj * dummy_proj_cf = NULL;
889 if(HE5_GCTP_SNSOID == grid_proj_code) {
890 // AFAWK, one grid_mapping variable is necessary for multi-grids. So we just leave one grid here.
891 if(g_suffix == 1) {
892 dummy_proj_cf = new HDF5CFGeoCFProj(cf_projection_base, cf_projection_base);
893 dds.add_var(dummy_proj_cf);
894 }
895 }
896 else {
897 stringstream t_suffix_ss;
898 t_suffix_ss << g_suffix;
899 string cf_projection_name = cf_projection_base + "_" + t_suffix_ss.str();
900 dummy_proj_cf = new HDF5CFGeoCFProj(cf_projection_name, cf_projection_name);
901 dds.add_var(dummy_proj_cf);
902 }
903 if (dummy_proj_cf) delete dummy_proj_cf;
904
905}
906
907// This function adds 1D grid mapping CF attributes to CV and data variables.
908#if 0
909void add_cf_grid_cv_attrs(DAS & das, const vector<HDF5CF::Var*>& vars, EOS5GridPCType cv_proj_code,
910 float /*cv_point_lower*/, float /*cv_point_upper*/, float /*cv_point_left*/, float /*cv_point_right*/,
911 const vector<HDF5CF::Dimension*>& dims,const vector<double> &eos5_proj_params,const unsigned short g_suffix)
912#endif
913void add_cf_grid_cv_attrs(DAS & das, const vector<HDF5CF::Var*>& vars, EOS5GridPCType cv_proj_code,
914 const vector<HDF5CF::Dimension*>& dims,const vector<double> &eos5_proj_params,const unsigned short g_suffix)
915{
916
917
918 //1. Check the projection information, now, we handle sinusoidal,PS and LAMAZ projections.
919 if (HE5_GCTP_SNSOID == cv_proj_code || HE5_GCTP_PS == cv_proj_code || HE5_GCTP_LAMAZ== cv_proj_code) {
920
921 string dim0name = (dims[0])->getNewName();
922 int dim0size = dims[0]->getSize();
923 string dim1name = (dims[1])->getNewName();
924 int dim1size = dims[1]->getSize();
925
926 //2. Add 1D CF attributes to the 1-D CV variables and the dummy grid_mapping variable
927 AttrTable *at = das.get_table(dim0name);
928 if (!at)
929 at = das.add_table(dim0name, new AttrTable);
930 at->append_attr("standard_name", "String", "projection_y_coordinate");
931
932 string long_name = "y coordinate of projection ";
933 at->append_attr("long_name", "String", long_name);
934
935 // Change this to meter.
936 at->append_attr("units", "string", "meter");
937
938 at->append_attr("_CoordinateAxisType", "string", "GeoY");
939
940 at = das.get_table(dim1name);
941 if (!at) at = das.add_table(dim1name, new AttrTable);
942
943 at->append_attr("standard_name", "String", "projection_x_coordinate");
944
945 long_name = "x coordinate of projection ";
946 at->append_attr("long_name", "String", long_name);
947
948 // change this to meter.
949 at->append_attr("units", "string", "meter");
950
951 // This is for CDM conventions. Adding doesn't do harm. Same as GeoY.
952 at->append_attr("_CoordinateAxisType", "string", "GeoX");
953
954 // Add the attributes for the dummy grid_mapping variable.
955 string cf_projection_base = "eos_cf_projection";
956 string cf_projection;
957 if(HE5_GCTP_SNSOID == cv_proj_code)
958 cf_projection = cf_projection_base;
959 else {
960 stringstream t_suffix_ss;
961 t_suffix_ss << g_suffix;
962 cf_projection = cf_projection_base + "_" + t_suffix_ss.str();
963 }
964 add_cf_projection_attrs(das,cv_proj_code,eos5_proj_params,cf_projection);
965
966 // Fill in the data fields that contains the dim0name and dim1name dimensions with the grid_mapping
967 // We only apply to >=2D data fields.
968 add_cf_grid_mapping_attr(das, vars, cf_projection, dim0name, dim0size, dim1name, dim1size);
969 }
970
971}
972
973// Add CF projection attribute
974
975void add_cf_projection_attrs(DAS &das,EOS5GridPCType cv_proj_code,const vector<double> &eos5_proj_params,const string& cf_projection) {
976
977 AttrTable* at = das.get_table(cf_projection);
978 if (!at) {// Only append attributes when the table is created.
979 at = das.add_table(cf_projection, new AttrTable);
980
981 if (HE5_GCTP_SNSOID == cv_proj_code) {
982 at->append_attr("grid_mapping_name", "String", "sinusoidal");
983 at->append_attr("longitude_of_central_meridian", "Float64", "0.0");
984 at->append_attr("earth_radius", "Float64", "6371007.181");
985 at->append_attr("_CoordinateAxisTypes", "string", "GeoX GeoY");
986 }
987 else if (HE5_GCTP_PS == cv_proj_code) {
988
989 // The following information is added according to the HDF-EOS5 user's guide and
990 // CF 1.7 grid_mapping requirement.
991
992 // Longitude down below pole of map
993 double vert_lon_pole = HE5_EHconvAng(eos5_proj_params[4],HE5_HDFE_DMS_DEG);
994
995 // Latitude of true scale
996 double lat_true_scale = HE5_EHconvAng(eos5_proj_params[5],HE5_HDFE_DMS_DEG);
997
998 // False easting
999 double fe = eos5_proj_params[6];
1000
1001 // False northing
1002 double fn = eos5_proj_params[7];
1003
1004 at->append_attr("grid_mapping_name", "String", "polar_stereographic");
1005
1006 ostringstream s_vert_lon_pole;
1007 s_vert_lon_pole << vert_lon_pole;
1008
1009 // I did this map is based on my best understanding. I cannot be certain about south pole. KY
1010 // CF: straight_vertical_longitude_from_pole
1011 at->append_attr("straight_vertical_longitude_from_pole", "Float64", s_vert_lon_pole.str());
1012 ostringstream s_lat_true_scale;
1013 s_lat_true_scale << lat_true_scale;
1014
1015 at->append_attr("standard_parallel", "Float64", s_lat_true_scale.str());
1016
1017 if(fe == 0.0)
1018 at->append_attr("false_easting","Float64","0.0");
1019 else {
1020 ostringstream s_fe;
1021 s_fe << fe;
1022 at->append_attr("false_easting","Float64",s_fe.str());
1023 }
1024
1025
1026 if(fn == 0.0)
1027 at->append_attr("false_northing","Float64","0.0");
1028 else {
1029 ostringstream s_fn;
1030 s_fn << fn;
1031 at->append_attr("false_northing","Float64",s_fn.str());
1032 }
1033
1034
1035 if(lat_true_scale >0)
1036 at->append_attr("latitude_of_projection_origin","Float64","+90.0");
1037 else
1038 at->append_attr("latitude_of_projection_origin","Float64","-90.0");
1039
1040
1041 at->append_attr("_CoordinateAxisTypes", "string", "GeoX GeoY");
1042
1043 // From CF, PS has another parameter,
1044 // Either standard_parallel (EPSG 9829) or scale_factor_at_projection_origin (EPSG 9810)
1045 // I cannot find the corresponding parameter from the EOS5.
1046
1047 }
1048 else if(HE5_GCTP_LAMAZ == cv_proj_code) {
1049 double lon_proj_origin = HE5_EHconvAng(eos5_proj_params[4],HE5_HDFE_DMS_DEG);
1050 double lat_proj_origin = HE5_EHconvAng(eos5_proj_params[5],HE5_HDFE_DMS_DEG);
1051 double fe = eos5_proj_params[6];
1052 double fn = eos5_proj_params[7];
1053
1054 at->append_attr("grid_mapping_name", "String", "lambert_azimuthal_equal_area");
1055
1056 ostringstream s_lon_proj_origin;
1057 s_lon_proj_origin << lon_proj_origin;
1058 at->append_attr("longitude_of_projection_origin", "Float64", s_lon_proj_origin.str());
1059
1060 ostringstream s_lat_proj_origin;
1061 s_lat_proj_origin << lat_proj_origin;
1062
1063 at->append_attr("latitude_of_projection_origin", "Float64", s_lat_proj_origin.str());
1064
1065
1066 if(fe == 0.0)
1067 at->append_attr("false_easting","Float64","0.0");
1068 else {
1069 ostringstream s_fe;
1070 s_fe << fe;
1071 at->append_attr("false_easting","Float64",s_fe.str());
1072 }
1073
1074
1075 if(fn == 0.0)
1076 at->append_attr("false_northing","Float64","0.0");
1077 else {
1078 ostringstream s_fn;
1079 s_fn << fn;
1080 at->append_attr("false_northing","Float64",s_fn.str());
1081 }
1082
1083 at->append_attr("_CoordinateAxisTypes", "string", "GeoX GeoY");
1084
1085
1086 }
1087 }
1088
1089}
1090
1091
1092// This function adds the 1-D cf grid projection mapping attribute to data variables
1093// it is called by the function add_cf_grid_attrs.
1094void add_cf_grid_mapping_attr(DAS &das, const vector<HDF5CF::Var*>& vars, const string& cf_projection,
1095 const string & dim0name, hsize_t dim0size, const string &dim1name, hsize_t dim1size)
1096{
1097
1098#if 0
1099 cerr<<"dim0name is "<<dim0name <<endl;
1100 cerr<<"dim1name is "<<dim1name <<endl;
1101 cerr<<"dim0size is "<<dim0size <<endl;
1102 cerr<<"dim1size is "<<dim1size <<endl;
1103#endif
1104
1105 // Check >=2-D fields, check if they hold the dim0name,dim0size etc., yes, add the attribute cf_projection.
1106 vector<HDF5CF::Var *>::const_iterator it_v;
1107 for (it_v = vars.begin(); it_v != vars.end(); ++it_v) {
1108
1109 if ((*it_v)->getRank() > 1) {
1110 bool has_dim0 = false;
1111 bool has_dim1 = false;
1112 const vector<HDF5CF::Dimension*>& dims = (*it_v)->getDimensions();
1113 for (vector<HDF5CF::Dimension *>::const_iterator j = dims.begin(); j != dims.end(); ++j) {
1114 if ((*j)->getNewName() == dim0name && (*j)->getSize() == dim0size)
1115 has_dim0 = true;
1116 else if ((*j)->getNewName() == dim1name && (*j)->getSize() == dim1size)
1117 has_dim1 = true;
1118
1119 }
1120 if (true == has_dim0 && true == has_dim1) { // Need to add the grid_mapping attribute
1121 AttrTable *at = das.get_table((*it_v)->getNewName());
1122 if (!at) at = das.add_table((*it_v)->getNewName(), new AttrTable);
1123
1124 // The dummy projection name is the value of the grid_mapping attribute
1125 at->append_attr("grid_mapping", "String", cf_projection);
1126 }
1127 }
1128 }
1129}
1130// Now this is specially for LAMAZ where the NSIDC EASE-Grid may have points off the earth. So
1131// The calculated lat/lon are set to number out of the normal range. The valid_range attributes
1132// will hopefully constrain the applications not to consider those points.
1133void add_ll_valid_range(AttrTable* at, bool is_lat) {
1134 if(true == is_lat) {
1135 at->append_attr("valid_min", "Float64","-90.0");
1136 at->append_attr("valid_max", "Float64","90.0");
1137 }
1138 else {
1139 at->append_attr("valid_min", "Float64","-180.0");
1140 at->append_attr("valid_max", "Float64","180.0");
1141 }
1142}
1143
1144// This routine is for 64-bit DAP4 CF support: when var type is 64-bit integer.
1145// Note: the main part of DMR still comes from DDS and DAS.
1146bool need_attr_values_for_dap4(const HDF5CF::Var *var) {
1147 bool ret_value = false;
1148 if((HDF5RequestHandler::get_dmr_64bit_int()!=NULL) &&
1149 (H5INT64 == var->getType() || H5UINT64 == var->getType()))
1150 ret_value = true;
1151 return ret_value;
1152}
1153
1154// This routine is for 64-bit DAP4 CF support: map all attributes to DAP4 for 64-bit integers.
1155// Note: the main part of DMR still comes from DDS and DAS.
1156void map_cfh5_var_attrs_to_dap4_int64(const HDF5CF::Var *var,BaseType* d4_var) {
1157
1158 vector<HDF5CF::Attribute *>::const_iterator it_ra;
1159 for (it_ra = var->getAttributes().begin();
1160 it_ra != var->getAttributes().end(); ++it_ra) {
1161 // HDF5 Native Char maps to DAP INT16(DAP doesn't have the corresponding datatype), so needs to
1162 // obtain the mem datatype. Keep this in DAP4 mapping.
1163 size_t mem_dtype_size = ((*it_ra)->getBufSize()) / ((*it_ra)->getCount());
1164 H5DataType mem_dtype = HDF5CFDAPUtil::get_mem_dtype((*it_ra)->getType(), mem_dtype_size);
1165
1166 string dap2_attrtype = HDF5CFDAPUtil::print_type(mem_dtype);
1167 D4AttributeType dap4_attrtype = HDF5CFDAPUtil::daptype_strrep_to_dap4_attrtype(dap2_attrtype);
1168 D4Attribute *d4_attr = new D4Attribute((*it_ra)->getNewName(),dap4_attrtype);
1169 if(dap4_attrtype == attr_str_c) {
1170 if("coordinates" == (*it_ra)->getNewName()) {
1171 bool chg_coor_value = false;
1172 if((true == HDF5RequestHandler::get_enable_coord_attr_add_path())
1173 &&(true == var->getCoorAttrAddPath()))
1174 chg_coor_value = true;
1175 string tempstring;
1176 handle_coor_attr_for_int64_var((*it_ra),var->getFullPath(),tempstring,chg_coor_value);
1177 d4_attr->add_value(tempstring);
1178 }
1179 else {
1180 const vector<size_t>& strsize = (*it_ra)->getStrSize();
1181 unsigned int temp_start_pos = 0;
1182 for (unsigned int loc = 0; loc < (*it_ra)->getCount(); loc++) {
1183 if (strsize[loc] != 0) {
1184 string tempstring((*it_ra)->getValue().begin() + temp_start_pos,
1185 (*it_ra)->getValue().begin() + temp_start_pos + strsize[loc]);
1186 temp_start_pos += strsize[loc];
1187 //The below if is not necessary since the "origname" and "fullnamepath" are not added.KY 2020-02-24
1188 //if (((*it_ra)->getNewName() != "origname") && ((*it_ra)->getNewName() != "fullnamepath"))
1189 tempstring = HDF5CFDAPUtil::escattr(tempstring);
1190 d4_attr->add_value(tempstring);
1191 }
1192 }
1193 }
1194
1195 }
1196 else {
1197 for (unsigned int loc = 0; loc < (*it_ra)->getCount(); loc++) {
1198 string print_rep = HDF5CFDAPUtil::print_attr(mem_dtype, loc, (void*) &((*it_ra)->getValue()[0]));
1199 d4_attr->add_value(print_rep);
1200 }
1201 }
1202 d4_var->attributes()->add_attribute_nocopy(d4_attr);
1203 }
1204 // Here we add the "origname" and "fullnamepath" attributes since they are crucial to DMRPP generation.
1205 D4Attribute *d4_attr = new D4Attribute("origname",attr_str_c);
1206 d4_attr->add_value(var->getName());
1207 d4_var->attributes()->add_attribute_nocopy(d4_attr);
1208 d4_attr = new D4Attribute("fullnamepath",attr_str_c);
1209 d4_attr->add_value(var->getFullPath());
1210 d4_var->attributes()->add_attribute_nocopy(d4_attr);
1211}
1212
1213// A helper function for 64-bit DAP4 CF support
1214// Note: the main part of DMR still comes from DDS and DAS.
1215void check_update_int64_attr(const string & obj_name, const HDF5CF::Attribute * attr) {
1216 if(attr->getType() == H5INT64 || attr->getType() == H5UINT64) {
1217
1218 DMR * dmr = HDF5RequestHandler::get_dmr_64bit_int();
1219 if(dmr != NULL) {
1220 string dap2_attrtype = HDF5CFDAPUtil::print_type(attr->getType());
1221 D4AttributeType dap4_attrtype = HDF5CFDAPUtil::daptype_strrep_to_dap4_attrtype(dap2_attrtype);
1222 D4Attribute *d4_attr = new D4Attribute(attr->getNewName(),dap4_attrtype);
1223 for (unsigned int loc = 0; loc < attr->getCount(); loc++) {
1224 string print_rep = HDF5CFDAPUtil::print_attr(attr->getType(), loc, (void*) &(attr->getValue()[0]));
1225 d4_attr->add_value(print_rep);
1226 }
1227 D4Group * root_grp = dmr->root();
1228 D4Attribute *d4_hg_container;
1229 if(root_grp->attributes()->empty() == true){
1230#if 0
1231 //D4Attribute *d4_hg_container = root_grp->attributes()->find("HDF5_GLOBAL");
1232 //if(d4_hg_container == NULL) {
1233#endif
1234 d4_hg_container = new D4Attribute;
1235 d4_hg_container->set_name("HDF5_GLOBAL_integer_64");
1236 d4_hg_container->set_type(attr_container_c);
1237 root_grp->attributes()->add_attribute_nocopy(d4_hg_container);
1238#if 0
1239 //root_grp->attributes()->add_attribute(d4_hg_container);
1240#endif
1241 }
1242 //else
1243 d4_hg_container = root_grp->attributes()->get("HDF5_GLOBAL_integer_64");
1244 if(obj_name != "") {
1245 string test_obj_name = "HDF5_GLOBAL_integer_64."+obj_name;
1246#if 0
1247 //D4Attribute *d4_container = root_grp->attributes()->find(obj_name);
1248 //D4Attribute *d4_container = root_grp->attributes()->get(obj_name);
1249#endif
1250 D4Attribute *d4_container = root_grp->attributes()->get(test_obj_name);
1251 // ISSUES need to search the attributes
1252 //
1253#if 0
1254 //D4Attribute *d4_container = d4_hg_container->attributes()->find(obj_name);
1255#endif
1256 if(d4_container == NULL) {
1257 d4_container = new D4Attribute;
1258 d4_container->set_name(obj_name);
1259 d4_container->set_type(attr_container_c);
1260
1261#if 0
1262 //if(d4_hg_container->attributes()->empty()==true)
1263 // cerr<<"global container is empty"<<endl;
1264 //d4_hg_container->attributes()->add_attribute_nocopy(d4_container);
1265 //cerr<<"end of d4_container "<<endl;
1266#endif
1267 }
1268 d4_container->attributes()->add_attribute_nocopy(d4_attr);
1269#if 0
1270 //root_grp->attributes()->add_attribute_nocopy(d4_container);
1271#endif
1272//#if 0
1273 if(d4_hg_container->attributes()->get(obj_name)==NULL)
1274 d4_hg_container->attributes()->add_attribute_nocopy(d4_container);
1275//#endif
1276 }
1277 else
1278 d4_hg_container->attributes()->add_attribute_nocopy(d4_attr);
1279 }
1280 }
1281}
1282
1283// Another helper function for 64-bit DAP4 CF support
1284// Note: the main part of DMR still comes from DDS and DAS.
1285void handle_coor_attr_for_int64_var(const HDF5CF::Attribute *attr,const string &var_path,string &tempstring,bool chg_coor_value) {
1286
1287 string tempstring2(attr->getValue().begin(),attr->getValue().end());
1288 if(true == chg_coor_value) {
1289 char sep=' ';
1290 vector<string>cvalue_vec;
1291 HDF5CFUtil::Split_helper(cvalue_vec,tempstring2,sep);
1292 for (int i = 0; i<cvalue_vec.size();i++) {
1293 HDF5CFUtil::cha_co(cvalue_vec[i],var_path);
1294 string t_str = get_cf_string(cvalue_vec[i]);
1295 if(i == 0)
1296 tempstring = t_str;
1297 else
1298 tempstring += sep+t_str;
1299 }
1300 }
1301 else
1302 tempstring = tempstring2;
1303
1304}
1305
1306// This routine is for direct mapping from CF to DAP4. We build DMR not from DDS and DAS.
1307// Hopefully this will be eventually used to build DMR.
1308void map_cfh5_var_attrs_to_dap4(const HDF5CF::Var *var,BaseType* d4_var) {
1309
1310 vector<HDF5CF::Attribute *>::const_iterator it_ra;
1311 for (it_ra = var->getAttributes().begin();
1312 it_ra != var->getAttributes().end(); ++it_ra) {
1313
1314 D4Attribute *d4_attr = gen_dap4_attr((*it_ra));
1315 d4_var->attributes()->add_attribute_nocopy(d4_attr);
1316 }
1317}
1318
1319// This routine is for direct mapping from CF to DAP4. We build DMR not from DDS and DAS.
1320void map_cfh5_grp_attr_to_dap4(libdap::D4Group *d4_grp,const HDF5CF::Attribute *attr) {
1321
1322 D4Attribute *d4_attr = gen_dap4_attr(attr);
1323 d4_grp->attributes()->add_attribute_nocopy(d4_attr);
1324
1325}
1326
1327// This routine is for direct mapping from CF to DAP4. We build DMR not from DDS and DAS.
1328void map_cfh5_attr_container_to_dap4(libdap::D4Attribute *d4_con,const HDF5CF::Attribute *attr) {
1329
1330 D4Attribute *d4_attr = gen_dap4_attr(attr);
1331 d4_con->attributes()->add_attribute_nocopy(d4_attr);
1332
1333}
1334
1335// Helper function to generate a DAP4 attribute.
1336D4Attribute *gen_dap4_attr(const HDF5CF::Attribute *attr) {
1337
1338 D4AttributeType dap4_attrtype = HDF5CFDAPUtil::print_type_dap4(attr->getType());
1339 D4Attribute *d4_attr = new D4Attribute(attr->getNewName(),dap4_attrtype);
1340 if(dap4_attrtype == attr_str_c) {
1341
1342 const vector<size_t>& strsize = attr->getStrSize();
1343#if 0
1344if(strsize.size() == 0)
1345cerr<<"vector string size is 0"<<endl;
1346for(int i = 0; i<strsize.size(); i++)
1347cerr<<"attr size is "<<strsize[i] <<endl;
1348#endif
1349 unsigned int temp_start_pos = 0;
1350 bool is_cset_ascii = attr->getCsetType();
1351 for (unsigned int loc = 0; loc < attr->getCount(); loc++) {
1352 if (strsize[loc] != 0) {
1353 string tempstring(attr->getValue().begin() + temp_start_pos,
1354 attr->getValue().begin() + temp_start_pos + strsize[loc]);
1355 temp_start_pos += strsize[loc];
1356 if ((attr->getNewName() != "origname") && (attr->getNewName() != "fullnamepath") && (true == is_cset_ascii))
1357 tempstring = HDF5CFDAPUtil::escattr(tempstring);
1358 d4_attr->add_value(tempstring);
1359 }
1360 }
1361 }
1362 else {
1363//cerr<<"not a string type "<<endl;
1364 for (unsigned int loc = 0; loc < attr->getCount(); loc++) {
1365 string print_rep = HDF5CFDAPUtil::print_attr(attr->getType(), loc, (void*) &(attr->getValue()[0]));
1366 d4_attr->add_value(print_rep);
1367 }
1368 }
1369 return d4_attr;
1370}
1371
1372// Direct CF to DAP4, add CF grid_mapping attributes of the projection variable to DAP4.
1373// we support sinusodial, polar stereographic and lambert azimuthal equal-area(LAMAZ) projections.
1374void add_gm_oneproj_var_dap4_attrs(BaseType *var,EOS5GridPCType cv_proj_code,const vector<double> &eos5_proj_params) {
1375
1376 if (HE5_GCTP_SNSOID == cv_proj_code) {
1377
1378 add_var_dap4_attr(var,"grid_mapping_name",attr_str_c,"sinusoidal");
1379 add_var_dap4_attr(var,"longitude_of_central_meridian",attr_float64_c,"0.0");
1380 add_var_dap4_attr(var,"earth_radius", attr_float64_c, "6371007.181");
1381 add_var_dap4_attr(var,"_CoordinateAxisTypes", attr_str_c, "GeoX GeoY");
1382
1383 }
1384 else if (HE5_GCTP_PS == cv_proj_code) {
1385
1386 // The following information is added according to the HDF-EOS5 user's guide and
1387 // CF 1.7 grid_mapping requirement.
1388
1389 // Longitude down below pole of map
1390 double vert_lon_pole = HE5_EHconvAng(eos5_proj_params[4],HE5_HDFE_DMS_DEG);
1391
1392 // Latitude of true scale
1393 double lat_true_scale = HE5_EHconvAng(eos5_proj_params[5],HE5_HDFE_DMS_DEG);
1394
1395 // False easting
1396 double fe = eos5_proj_params[6];
1397
1398 // False northing
1399 double fn = eos5_proj_params[7];
1400
1401 add_var_dap4_attr(var,"grid_mapping_name",attr_str_c,"polar_stereographic");
1402
1403 ostringstream s_vert_lon_pole;
1404 s_vert_lon_pole << vert_lon_pole;
1405
1406 // I did this map is based on my best understanding. I cannot be certain about south pole. KY
1407 // CF: straight_vertical_longitude_from_pole
1408 //at->append_attr("straight_vertical_longitude_from_pole", "Float64", s_vert_lon_pole.str());
1409 add_var_dap4_attr(var,"straight_vertical_longitude_from_pole", attr_float64_c, s_vert_lon_pole.str());
1410
1411 ostringstream s_lat_true_scale;
1412 s_lat_true_scale << lat_true_scale;
1413 add_var_dap4_attr(var,"standard_parallel", attr_float64_c, s_lat_true_scale.str());
1414
1415 if(fe == 0.0)
1416 add_var_dap4_attr(var,"false_easting",attr_float64_c,"0.0");
1417 else {
1418 ostringstream s_fe;
1419 s_fe << fe;
1420 add_var_dap4_attr(var,"false_easting",attr_float64_c,s_fe.str());
1421 }
1422
1423 if(fn == 0.0)
1424 add_var_dap4_attr(var,"false_northing",attr_float64_c,"0.0");
1425 else {
1426 ostringstream s_fn;
1427 s_fn << fn;
1428 add_var_dap4_attr(var,"false_northing",attr_float64_c,s_fn.str());
1429 }
1430
1431 if(lat_true_scale >0)
1432 add_var_dap4_attr(var,"latitude_of_projection_origin",attr_float64_c,"+90.0");
1433 else
1434 add_var_dap4_attr(var, "latitude_of_projection_origin",attr_float64_c,"-90.0");
1435
1436 add_var_dap4_attr(var, "_CoordinateAxisTypes", attr_str_c, "GeoX GeoY");
1437
1438 // From CF, PS has another parameter,
1439 // Either standard_parallel (EPSG 9829) or scale_factor_at_projection_origin (EPSG 9810)
1440 // I cannot find the corresponding parameter from the EOS5.
1441
1442 }
1443 else if(HE5_GCTP_LAMAZ == cv_proj_code) {
1444
1445 double lon_proj_origin = HE5_EHconvAng(eos5_proj_params[4],HE5_HDFE_DMS_DEG);
1446 double lat_proj_origin = HE5_EHconvAng(eos5_proj_params[5],HE5_HDFE_DMS_DEG);
1447 double fe = eos5_proj_params[6];
1448 double fn = eos5_proj_params[7];
1449
1450 add_var_dap4_attr(var,"grid_mapping_name", attr_str_c, "lambert_azimuthal_equal_area");
1451
1452 ostringstream s_lon_proj_origin;
1453 s_lon_proj_origin << lon_proj_origin;
1454 add_var_dap4_attr(var,"longitude_of_projection_origin", attr_float64_c, s_lon_proj_origin.str());
1455
1456 ostringstream s_lat_proj_origin;
1457 s_lat_proj_origin << lat_proj_origin;
1458
1459 add_var_dap4_attr(var,"latitude_of_projection_origin", attr_float64_c, s_lat_proj_origin.str());
1460
1461 if(fe == 0.0)
1462 add_var_dap4_attr(var,"false_easting",attr_float64_c,"0.0");
1463 else {
1464 ostringstream s_fe;
1465 s_fe << fe;
1466 add_var_dap4_attr(var,"false_easting",attr_float64_c,s_fe.str());
1467 }
1468
1469 if(fn == 0.0)
1470 add_var_dap4_attr(var,"false_northing",attr_float64_c,"0.0");
1471 else {
1472 ostringstream s_fn;
1473 s_fn << fn;
1474 add_var_dap4_attr(var,"false_northing",attr_float64_c,s_fn.str());
1475 }
1476
1477 add_var_dap4_attr(var,"_CoordinateAxisTypes", attr_str_c, "GeoX GeoY");
1478 }
1479
1480}
1481
1482// Direct CF to DAP4, add the CF "grid_mapping_name" attribute to every variable that uses the grid.
1483void add_cf_grid_cv_dap4_attrs(D4Group *d4_root, const string& cf_projection,
1484 const vector<HDF5CF::Dimension*>& dims)
1485{
1486 // dims are dimensions for a grid. It is always 2-D for the projections we support.t
1487 string dim0name = (dims[0])->getNewName();
1488 hsize_t dim0size = dims[0]->getSize();
1489 string dim1name = (dims[1])->getNewName();
1490 hsize_t dim1size = dims[1]->getSize();
1491
1492 // We only add the attribute to the variables that match the grid dimensions.
1493 Constructor::Vars_iter vi = d4_root->var_begin();
1494 Constructor::Vars_iter ve = d4_root->var_end();
1495 for (; vi != ve; vi++) {
1496 if((*vi)->is_vector_type()) {
1497 Array *t_a = dynamic_cast<Array*>(*vi);
1498 if(t_a->dimensions() >1) {
1499 Array::Dim_iter dim_i = t_a->dim_begin();
1500 Array::Dim_iter dim_e = t_a->dim_end();
1501 bool has_dim0 = false;
1502 bool has_dim1 = false;
1503 for(;dim_i !=dim_e;dim_i++) {
1504 if((*dim_i).name == dim0name && (*dim_i).size == dim0size)
1505 has_dim0 = true;
1506 else if((*dim_i).name == dim1name && (*dim_i).size == dim1size)
1507 has_dim1 = true;
1508 }
1509
1510 if(true == has_dim0 && true == has_dim1)
1511 add_var_dap4_attr((*vi),"grid_mapping",attr_str_c,cf_projection);
1512 }
1513 }
1514 }
1515}
1516
1517
1518// Direct CF to DAP4, add special CF grid_mapping variable to DAP4.
1519// These variables are dimension variables.
1520void add_gm_spcvs(libdap::D4Group *d4_root, EOS5GridPCType cv_proj_code, float cv_point_lower, float cv_point_upper,
1521 float cv_point_left, float cv_point_right, const std::vector<HDF5CF::Dimension*>& dims) {
1522
1523 //1. Check the projection information: we first just handled the sinusoidal projection.
1524 // We also add the LAMAZ and PS support. These 1-D varaibles are the same as the sinusoidal one.
1525 if (HE5_GCTP_SNSOID == cv_proj_code || HE5_GCTP_LAMAZ == cv_proj_code || HE5_GCTP_PS == cv_proj_code) {
1526
1527 //2. Obtain the dimension information from latitude and longitude(fieldtype =1 or fieldtype =2)
1528 vector<HDF5CF::Dimension*>::const_iterator it_d;
1529 string dim0name = dims[0]->getNewName();
1530 int dim0size = dims[0]->getSize();
1531 string dim1name = dims[1]->getNewName();
1532 int dim1size = dims[1]->getSize();
1533
1534 //3. Add the 1-D CV variables and the dummy projection variable
1535 BaseType *bt_dim0 = NULL;
1536 BaseType *bt_dim1 = NULL;
1537
1538 HDF5CFGeoCF1D * ar_dim0 = NULL;
1539 HDF5CFGeoCF1D * ar_dim1 = NULL;
1540
1541 try {
1542
1543 bt_dim0 = new (HDF5CFFloat64)(dim0name, dim0name);
1544 bt_dim1 = new (HDF5CFFloat64)(dim1name, dim1name);
1545
1546 // Note ar_dim0 is y, ar_dim1 is x.
1547 ar_dim0 = new HDF5CFGeoCF1D(HE5_GCTP_SNSOID, cv_point_upper, cv_point_lower, dim0size, dim0name, bt_dim0);
1548 ar_dim0->append_dim(dim0size, dim0name);
1549
1550 ar_dim0->set_is_dap4(true);
1551
1552 add_gm_spcvs_attrs(ar_dim0,true);
1553
1554 ar_dim1 = new HDF5CFGeoCF1D(HE5_GCTP_SNSOID, cv_point_left, cv_point_right, dim1size, dim1name, bt_dim1);
1555 ar_dim1->append_dim(dim1size, dim1name);
1556
1557 ar_dim1->set_is_dap4(true);
1558
1559 add_gm_spcvs_attrs(ar_dim1,false);
1560
1561 d4_root->add_var(ar_dim0);
1562 d4_root->add_var(ar_dim1);
1563
1564 }
1565 catch (...) {
1566 if (bt_dim0) delete bt_dim0;
1567 if (bt_dim1) delete bt_dim1;
1568 if (ar_dim0) delete ar_dim0;
1569 if (ar_dim1) delete ar_dim1;
1570 throw InternalErr(__FILE__, __LINE__, "Unable to allocate the HDFEOS2GeoCF1D instance.");
1571 }
1572
1573 if (bt_dim0) delete bt_dim0;
1574 if (bt_dim1) delete bt_dim1;
1575 if (ar_dim0) delete ar_dim0;
1576 if (ar_dim1) delete ar_dim1;
1577 }
1578}
1579
1580// Direct CF to DAP4,
1581// add CF grid_mapping $attributes for the special dimension variables.
1582void add_gm_spcvs_attrs(libdap::BaseType *var,const bool is_dim0) {
1583
1584 string standard_name;
1585 string long_name;
1586 string COORAxisTypes;
1587
1588 if (true == is_dim0) {
1589 standard_name = "projection_y_coordinate";
1590 long_name = "y coordinate of projection ";
1591 COORAxisTypes = "GeoY";
1592 }
1593 else {
1594 standard_name = "projection_x_coordinate";
1595 long_name = "x coordinate of projection ";
1596 COORAxisTypes = "GeoX";
1597 }
1598
1599 add_var_dap4_attr(var,"standard_name", attr_str_c, standard_name);
1600 add_var_dap4_attr(var,"long_name", attr_str_c, long_name);
1601 add_var_dap4_attr(var,"units", attr_str_c, "meter");
1602 add_var_dap4_attr(var,"_CoordinateAxisType", attr_str_c, COORAxisTypes);
1603
1604}
1605
1606// Direct CF to DAP4, helper function to DAP4 group attributes.
1607void add_grp_dap4_attr(D4Group *d4_grp,const string& attr_name, D4AttributeType attr_type, const string& attr_value){
1608
1609 D4Attribute *d4_attr = new D4Attribute(attr_name,attr_type);
1610 d4_attr->add_value(attr_value);
1611 d4_grp->attributes()->add_attribute_nocopy(d4_attr);
1612
1613}
1614// Direct CF to DAP4, helper function to DAP4 variable attributes.
1615void add_var_dap4_attr(BaseType *var,const string& attr_name, D4AttributeType attr_type, const string& attr_value){
1616
1617 D4Attribute *d4_attr = new D4Attribute(attr_name,attr_type);
1618 d4_attr->add_value(attr_value);
1619 var->attributes()->add_attribute_nocopy(d4_attr);
1620
1621}
1622
1623// Mainly copy from HDF5CF::get_CF_string. Should be
1624// removed if we can generate DMR independently.
1625string get_cf_string(string & s) {
1626
1627 if(s[0] !='/')
1628 return get_cf_string_helper(s);
1629 else {
1630 // The leading underscore should be removed
1631 s.erase(0,1);
1632 return get_cf_string_helper(s);
1633 }
1634
1635}
1636string get_cf_string_helper(string & s) {
1637
1638 if ("" == s) return s;
1639 string insertString(1, '_');
1640
1641 // Always start with _ if the first character is not a letter
1642 if (true == isdigit(s[0])) s.insert(0, insertString);
1643
1644 for (unsigned int i = 0; i < s.length(); i++)
1645 if ((false == isalnum(s[i])) && (s[i] != '_')) s[i] = '_';
1646 return s;
1647}
This class includes the methods to read data array into DAP buffer from an HDF5 dataset for the CF op...
This class provides a way to map HDF5 byte to DAP byte for the CF option.
This class provides a way to map HDF5 float to DAP float for the CF option.
This class provides a way to map HDF5 64-bit floating-point(double) to DAP 64-bit floating-point for ...
This class provides a way to map HDF5 int16 to DAP int16 for the CF option.
This class provides a way to map HDF5 32-bit integer to DAP Int32 for the CF option.
This class provides a way to map HDF5 64-bit integer to DAP4 Int64 for the CF option.
This class provides a way to map HDF5 int8 to DAP int16 for the CF option.
This class provides a way to map HDF5 Str to DAP Str for the CF option.
This class provides a way to map HDF5 unsigned 16-bit integer to DAP uint16 for the CF option.
This class provides a way to map HDF5 unsigned 32-bit integer to DAP uint32 for the CF option.
This class provides a way to map HDF5 64-bit unsigned integer to DAP4 UInt64 for the CF option.
This file includes several helper functions for translating HDF5 to CF-compliant.
include the entry functions to execute the handlers
static string escattr(string s)
Definition: h5cfdaputil.cc:52
static D4AttributeType daptype_strrep_to_dap4_attrtype(std::string s)
Definition: h5cfdaputil.cc:362
This class represents one attribute.
Definition: HDF5CF.h:189
This class represents one HDF5 dataset(CF variable)
Definition: HDF5CF.h:259
int getRank() const
Get the dimension rank of this variable.
Definition: HDF5CF.h:305
const std::string & getFullPath() const
Get the full path of this variable.
Definition: HDF5CF.h:283
const std::string & getName() const
Get the original name of this variable.
Definition: HDF5CF.h:271
H5DataType getType() const
Get the data type of this variable(Not HDF5 datatype id)
Definition: HDF5CF.h:311
const std::vector< Dimension * > & getDimensions() const
Get the list of the dimensions.
Definition: HDF5CF.h:322
int getCompRatio() const
Get the compression ratio of this dataset.
Definition: HDF5CF.h:328
const std::string & getNewName() const
Get the new name of this variable.
Definition: HDF5CF.h:277
Helper functions for generating DAS attributes and a function to check BES Key.
Map and generate DDS and DAS for the CF option for generic HDF5 products.