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