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