bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
h5dmr.cc
Go to the documentation of this file.
1// This file is part of hdf5_handler a HDF5 file handler for the OPeNDAP
2// data server.
3
4// Copyright (c) 2007-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 401 E University Avenue,
22// Suite 200, Champaign, IL 61820
23
36
37#include <sstream>
38#include <memory>
39
40#include <libdap/InternalErr.h>
41#include <libdap/mime_util.h>
42#include <libdap/D4Maps.h>
43
44#include <BESDebug.h>
45
46#include "hdf5_handler.h"
47#include "HDF5Int32.h"
48#include "HDF5UInt32.h"
49#include "HDF5UInt16.h"
50#include "HDF5Int16.h"
51#include "HDF5Array.h"
52#include "HDF5Float32.h"
53#include "HDF5Float64.h"
54#include "HDF5Url.h"
55#include "HDF5Structure.h"
56#include "h5dmr.h"
57
58// The HDF5CFUtil.h includes the utility function obtain_string_after_lastslash.
59#include "HDF5CFUtil.h"
60#include "h5commoncfdap.h"
61
62// For HDF-EOS5 handling
63#include "HE5Parser.h"
64#include "HE5Checker.h"
65#include "HDF5CFProj.h"
66#include "HDF5CFProj1D.h"
67#include "HDF5MissLLArray.h"
68
69using namespace std;
70using namespace libdap;
71
74
76static DS_t dt_inst;
77
78struct yy_buffer_state;
79
80yy_buffer_state *he5dds_scan_string(const char *str);
81int he5ddsparse(HE5Parser *he5parser);
82int he5ddslex_destroy();
83
84void array_add_dimensions_dimscale(HDF5Array* ar);
85bool array_add_dimensions_non_dimscale(HDF5Array *ar, const string &varname, const eos5_dim_info_t &eos5_dim_info);
86void read_objects_basetype_add_eos5_grid_mapping(const eos5_dim_info_t &eos5_dim_info, BaseType *new_var,const HDF5Array *ar);
87void write_dap4_attr(hid_t attr_id, libdap::D4Attribute *d4_attr, hid_t ty_id, const DSattr_t &attr_inst);
88void write_dap4_attr_value(D4Attribute *d4_attr, hid_t ty_id, hsize_t nelmts, char *tempvalue,
89 size_t elesize = 0);
90
115
116bool breadth_first(hid_t file_id, hid_t pid, const char *gname,
117 D4Group* par_grp, const char *fname,
118 bool use_dimscale,bool is_eos5,
119 vector<link_info_t> & hdf5_hls,
120 eos5_dim_info_t & eos5_dim_info,
121 vector<string> & handled_cv_names)
122{
123 BESDEBUG("h5",
124 ">breadth_first() for dmr "
125 << " pid: " << pid
126 << " gname: " << gname
127 << " fname: " << fname
128 << endl);
129
131 int slinkindex = 0;
132
133 // Obtain the number of objects in this group
134 H5G_info_t g_info;
135 hsize_t nelems = 0;
136 if (H5Gget_info(pid,&g_info) < 0) {
137 string msg =
138 "h5_dmr: counting hdf5 group elements error for ";
139 msg += gname;
140 throw InternalErr(__FILE__, __LINE__, msg);
141 }
142
143 nelems = g_info.nlinks;
144
145 // First iterate through the HDF5 datasets under the group.
146 for (hsize_t i = 0; i < nelems; i++) {
147
148 vector <char> oname;
149 obtain_hdf5_object_name(pid, i, gname, oname);
150 if (true == check_soft_external_links(par_grp, pid, slinkindex,gname, oname,true))
151 continue;
152
153 // Obtain the object type, such as group or dataset.
154 H5O_info_t oinfo;
155 if (H5OGET_INFO_BY_IDX(pid, ".", H5_INDEX_NAME, H5_ITER_NATIVE,
156 i, &oinfo, H5P_DEFAULT) <0 ) {
157 string msg = "h5_dmr handler: Error obtaining the info for the object";
158 msg += string(oname.begin(),oname.end());
159 throw InternalErr(__FILE__, __LINE__, msg);
160 }
161
162 H5O_type_t obj_type = oinfo.type;
163
164 if (H5O_TYPE_DATASET == obj_type) {
165
166 // Obtain the absolute path of the HDF5 dataset
167 string full_path_name = string(gname) + string(oname.begin(),oname.end()-1);
168
169 // Obtain the hdf5 dataset handle stored in the structure dt_inst.
170 // All the metadata information in the handler is stored in dt_inst.
171 // Dimension scale is handled in this routine. KY 2020-06-10
172 bool is_pure_dim = false;
173 get_dataset_dmr(file_id, pid, full_path_name, &dt_inst,use_dimscale,is_eos5,
174 is_pure_dim,hdf5_hls,handled_cv_names);
175
176 // pure dimensions are netCDF-4's dimensions only. They are not variables in the netCDF-4 term.
177 if (false == is_pure_dim) {
178 handle_actual_dataset(par_grp, pid, full_path_name, fname, use_dimscale,
179 is_eos5, eos5_dim_info);
180 }
181 else
182 handle_pure_dimension(par_grp, pid, oname, is_eos5, full_path_name);
183 }
184 }
185
186 // The attributes of this group. Doing this order to follow ncdump's way (variable,attribute then groups)
187 map_h5_attrs_to_dap4(pid,par_grp,nullptr,nullptr,0);
188
189 if (is_eos5 && !use_dimscale)
190 handle_eos5_datasets(par_grp, gname, eos5_dim_info);
191
192 // The fullnamepath of the group is not necessary since dmrpp only needs the dataset path to retrieve info.
193 // It only increases the dmr file size. So comment out for now. KY 2022-10-13
194#if 0
195 if (is_eos5)
196 map_h5_varpath_to_dap4_attr(par_grp,nullptr,nullptr,gname,0);
197#endif
198
199 // Then HDF5 child groups
200 for (hsize_t i = 0; i < nelems; i++) {
201
202 vector <char>oname;
203 obtain_hdf5_object_name(pid, i, gname, oname);
204 if (true == check_soft_external_links(par_grp, pid, slinkindex,gname, oname,false))
205 continue;
206
207 // Obtain the object type, such as group or dataset.
208 H5O_info_t oinfo;
209
210 if (H5OGET_INFO_BY_IDX(pid, ".", H5_INDEX_NAME, H5_ITER_NATIVE,
211 i, &oinfo, H5P_DEFAULT) < 0 ) {
212 string msg = "h5_dmr handler: Error obtaining the info for the object in the breadth_first.";
213 throw InternalErr(__FILE__, __LINE__, msg);
214 }
215
216 H5O_type_t obj_type = oinfo.type;
217
218 if (obj_type == H5O_TYPE_GROUP) {
219 handle_child_grp(file_id, pid, gname, par_grp, fname, use_dimscale, is_eos5, hdf5_hls, eos5_dim_info,
220 handled_cv_names, oname);
221 }
222 } // for i is 0 ... nelems
223
224 BESDEBUG("h5", "<breadth_first() " << endl);
225 return true;
226}
227
228void obtain_hdf5_object_name(hid_t pid, hsize_t obj_index, const char *gname, vector<char> &oname) {
229
230 ssize_t oname_size;
231
232 // Query the length of this object name.
233 oname_size = H5Lget_name_by_idx(pid,".",H5_INDEX_NAME,H5_ITER_NATIVE,obj_index,
234 nullptr,(size_t)DODS_NAMELEN, H5P_DEFAULT);
235 if (oname_size <= 0) {
236 string msg = "h5_dmr handler: Error getting the size of the hdf5 object from the group: ";
237 msg += gname;
238 throw InternalErr(__FILE__, __LINE__, msg);
239 }
240
241 // Obtain the name of the object
242 oname.resize((size_t) oname_size + 1);
243
244 if (H5Lget_name_by_idx(pid,".",H5_INDEX_NAME,H5_ITER_NATIVE,obj_index,
245 oname.data(),(size_t)(oname_size+1), H5P_DEFAULT) < 0) {
246 string msg = "h5_dmr handler: Error getting the hdf5 object name from the group: ";
247 msg += gname;
248 throw InternalErr(__FILE__, __LINE__, msg);
249 }
250}
251
252bool check_soft_external_links(D4Group *par_grp, hid_t pid, int & slinkindex,
253 const char *gname, const vector<char> &oname, bool handle_softlink) {
254
255 bool ret_value = false;
256
257 // Check the HDF5 link info.
258 H5L_info_t linfo;
259 if (H5Lget_info(pid,oname.data(),&linfo,H5P_DEFAULT) < 0) {
260 string msg = "hdf5 link name error from: ";
261 msg += gname;
262 throw InternalErr(__FILE__, __LINE__, msg);
263 }
264
265 // Information of soft links are stored as attributes
266 if (linfo.type == H5L_TYPE_SOFT) {
267 ret_value = true;
268 if (handle_softlink == false) {
269 slinkindex++;
270 // Size of a soft link value
271 size_t val_size = linfo.u.val_size;
272 get_softlink(par_grp, pid, oname.data(), slinkindex, val_size);
273 }
274 }
275 else if (linfo.type == H5L_TYPE_EXTERNAL) // Ignore external links
276 ret_value = true;
277
278 return ret_value;
279}
280
281void handle_actual_dataset(D4Group *par_grp, hid_t pid, const string &full_path_name, const string &fname,
282 bool use_dimscale, bool is_eos5, eos5_dim_info_t &eos5_dim_info) {
283
284 hid_t dset_id = -1;
285 if ((dset_id = H5Dopen(pid,full_path_name.c_str(),H5P_DEFAULT)) < 0) {
286 string msg = "cannot open the HDF5 dataset ";
287 msg += full_path_name;
288 throw InternalErr(__FILE__, __LINE__, msg);
289 }
290
291 try {
292 read_objects(par_grp, pid, full_path_name, fname,dset_id,use_dimscale,is_eos5,eos5_dim_info);
293 }
294 catch(...) {
295 H5Dclose(dset_id);
296 throw;
297 }
298 if (H5Dclose(dset_id) < 0) {
299 string msg = "cannot close the HDF5 dataset ";
300 msg += full_path_name;
301 throw InternalErr(__FILE__, __LINE__, msg);
302 }
303
304}
305
306void handle_pure_dimension(D4Group *par_grp, hid_t pid, const vector<char>& oname, bool is_eos5,
307 const string &full_path_name) {
308
309 // Need to add this pure dimension to the corresponding DAP4 group
310 D4Dimensions *d4_dims = par_grp->dims();
311 string d4dim_name;
312 if (is_eos5) {
313 auto tempdim_name = string(oname.begin(),oname.end()-1);
314 d4dim_name = handle_string_special_characters(tempdim_name);
315 }
316 else
317 d4dim_name = string(oname.begin(),oname.end()-1);
318
319 const D4Dimension *d4_dim = d4_dims->find_dim(d4dim_name);
320 if (d4_dim == nullptr) {
321
322 hsize_t nelmts = dt_inst.nelmts;
323
324 // For pure dimension, if the dimension size is 0,
325 // the dimension is unlimited or a NULL data space, so we need to use the object reference
326 // to retrieve the variable this dimension is attached and then for the same variable retrieve
327 // the size of this dimension.
328 if (dt_inst.nelmts == 0)
329 nelmts = obtain_unlim_pure_dim_size(pid,full_path_name);
330
331 auto d4_dim_unique = make_unique<D4Dimension>(d4dim_name, nelmts);
332 d4_dims->add_dim_nocopy(d4_dim_unique.release());
333 }
334
335 BESDEBUG("h5", "<h5dmr.cc: pure dimension: dataset name." << d4dim_name << endl);
336
337 if (H5Tclose(dt_inst.type)<0) {
338 throw InternalErr(__FILE__, __LINE__, "Cannot close the HDF5 datatype.");
339 }
340
341}
342
343void handle_eos5_datasets(D4Group* par_grp, const char *gname, eos5_dim_info_t &eos5_dim_info) {
344
345 // To support HDF-EOS5 grids, we have to add extra variables.
346 // These variables are geo-location related variables such as latitude and longitude.
347 // These geo-location variables are DAP4 coverage map variable candidates.
348 // And to follow the DAP4 coverage specification, we need to define map variables.
349 // The map variables need to be in *front* of all the variables that use the map variables.
350 // So here we have to insert these extra variables if an HDF-EOS5 grid is found.
351 // We may need to remember the full path of these extra variables. These will be
352 // used as the coordinates of this group's data variables. For the geographic projection,
353 // this is not necessary.
354 add_possible_eos5_grid_vars(par_grp, eos5_dim_info);
355
356 //Also need to add DAP4 dimensions to this group if there are HDF-EOS5 dimensions.
357 unordered_map<string,vector<HE5Dim>> grppath_to_dims = eos5_dim_info.grppath_to_dims;
358 vector<string> dim_names;
359 auto par_grp_name = string(gname);
360 if (par_grp_name.size() > 1)
361 par_grp_name = par_grp_name.substr(0,par_grp_name.size()-1);
362
363 BESDEBUG("h5", "<h5dmr.cc: eos5 handling - parent group name: " << par_grp_name << endl);
364
365 // We need to ensure the special characters are handled.
366 bool is_eos5_dims = obtain_eos5_grp_dim(par_grp_name,grppath_to_dims,dim_names);
367
368 if (is_eos5_dims) {
369
370 vector<HE5Dim> grp_eos5_dim = grppath_to_dims[par_grp_name];
371 D4Dimensions *d4_dims = par_grp->dims();
372 for (unsigned grp_dim_idx = 0; grp_dim_idx < dim_names.size(); grp_dim_idx++) {
373
374 const D4Dimension *d4_dim = d4_dims->find_dim(dim_names[grp_dim_idx]);
375 if (d4_dim == nullptr) {
376 auto d4_dim_unique =
377 make_unique<D4Dimension>(dim_names[grp_dim_idx],grp_eos5_dim[grp_dim_idx].size);
378 d4_dims->add_dim_nocopy(d4_dim_unique.release());
379 }
380 }
381 }
382}
383
384void handle_child_grp(hid_t file_id, hid_t pid, const char *gname,
385 D4Group* par_grp, const char *fname,
386 bool use_dimscale,bool is_eos5,
387 vector<link_info_t> & hdf5_hls,
388 eos5_dim_info_t & eos5_dim_info,
389 vector<string> & handled_cv_names,
390 const vector<char>& oname) {
391
392 // Obtain the full path name
393 string full_path_name =
394 string(gname) + string(oname.begin(),oname.end()-1) + "/";
395
396 BESDEBUG("h5", "=breadth_first dmr ():H5G_GROUP " << full_path_name
397 << endl);
398
399 vector <char>t_fpn;
400 t_fpn.resize(full_path_name.size() + 1);
401 copy(full_path_name.begin(),full_path_name.end(),t_fpn.begin());
402 t_fpn[full_path_name.size()] = '\0';
403
404 hid_t cgroup = H5Gopen(pid, t_fpn.data(),H5P_DEFAULT);
405 if (cgroup < 0){
406 throw InternalErr(__FILE__, __LINE__, "h5_dmr handler: H5Gopen() failed.");
407 }
408
409 auto grp_name = string(oname.begin(),oname.end()-1);
410
411 // Check the hard link loop and break the loop if it exists.
412 string oid = get_hardlink_dmr(cgroup, full_path_name);
413 if (oid.empty()) {
414
415 try {
416 if (is_eos5)
417 grp_name = handle_string_special_characters(grp_name);
418 auto tem_d4_cgroup_ptr = make_unique<D4Group>(grp_name);
419 auto tem_d4_cgroup = tem_d4_cgroup_ptr.release();
420
421 // Add this new DAP4 group
422 par_grp->add_group_nocopy(tem_d4_cgroup);
423
424 // Continue searching the objects under this group
425 breadth_first(file_id,cgroup, t_fpn.data(), tem_d4_cgroup,fname,
426 use_dimscale,is_eos5,hdf5_hls,eos5_dim_info,handled_cv_names);
427 }
428 catch(...) {
429 H5Gclose(cgroup);
430 throw;
431 }
432 }
433 else {
434 // This group has been visited.
435 // Add the attribute table with the attribute name as HDF5_HARDLINK.
436 // The attribute value is the name of the group when it is first visited.
437 auto tem_d4_cgroup_unique = make_unique<D4Group>(grp_name);
438 auto tem_d4_cgroup = tem_d4_cgroup_unique.release();
439
440 // Note attr_str_c is the DAP4 attribute string datatype
441 auto d4_hlinfo_unique = make_unique<D4Attribute>("HDF5_HARDLINK",attr_str_c);
442 auto d4_hlinfo = d4_hlinfo_unique.release();
443
444 d4_hlinfo->add_value(obj_paths.get_name(oid));
445 tem_d4_cgroup->attributes()->add_attribute_nocopy(d4_hlinfo);
446 par_grp->add_group_nocopy(tem_d4_cgroup);
447 }
448
449 if (H5Gclose(cgroup) < 0){
450 throw InternalErr(__FILE__, __LINE__, "Could not close the group.");
451 }
452}
476//
477void
478read_objects( D4Group * d4_grp, hid_t pid, const string &varname, const string &filename, hid_t dset_id,bool use_dimscale,
479 bool is_eos5, eos5_dim_info_t & eos5_dim_info) {
480
481 // NULL space data, ignore.
482 if (dt_inst.ndims == -1 && dt_inst.nelmts == 0)
483 return;
484
485 switch (H5Tget_class(dt_inst.type)) {
486
487 // HDF5 compound maps to DAP structure.
488 case H5T_COMPOUND:
489 read_objects_structure(d4_grp, varname, filename,dset_id,use_dimscale,is_eos5);
490 break;
491
492 case H5T_ARRAY:
493 H5Tclose(dt_inst.type);
494 throw InternalErr(__FILE__, __LINE__,
495 "Currently don't support accessing data of Array datatype when array datatype is not inside the compound.");
496
497 default:
498 read_objects_base_type(d4_grp,pid,varname, filename,dset_id,use_dimscale,is_eos5,eos5_dim_info);
499 break;
500 }
501 // Close the datatype obtained in the get_dataset_dmr() since the datatype is no longer used.
502 if (H5Tclose(dt_inst.type) < 0) {
503 throw InternalErr(__FILE__, __LINE__, "Cannot close the HDF5 datatype.");
504 }
505}
506
507void array_add_dimensions_dimscale(HDF5Array *ar){
508
509 if (dt_inst.dimnames.empty() == true) {
510 for (int dim_index = 0; dim_index < dt_inst.ndims; dim_index++)
511 ar->append_dim_ll(dt_inst.size[dim_index]);
512 }
513 else {
514 for (int dim_index = 0; dim_index < dt_inst.ndims; dim_index++) {
515 if (dt_inst.dimnames[dim_index].empty() == false)
516 ar->append_dim_ll(dt_inst.size[dim_index], dt_inst.dimnames[dim_index]);
517 else
518 ar->append_dim_ll(dt_inst.size[dim_index]);
519 }
520 }
521
522 if (dt_inst.dimnames.empty() == false)
523 dt_inst.dimnames.clear();
524
525}
526
527bool array_add_dimensions_non_dimscale(HDF5Array *ar, const string &varname, const eos5_dim_info_t &eos5_dim_info) {
528
529 // Without using the dimension scales, the HDF5 file may still have dimension names(HDF-EOS5 etc.).
530 // We search the file to see if there are dimension names. If yes, add them here.
531 vector<string> dim_names;
532 bool is_eos5_dims = obtain_eos5_dim(varname, eos5_dim_info.varpath_to_dims, dim_names);
533
534 if (is_eos5_dims) {
535 for (int dim_index = 0; dim_index < dt_inst.ndims; dim_index++)
536 ar->append_dim_ll(dt_inst.size[dim_index], dim_names[dim_index]);
537 } else {
538 for (int dim_index = 0; dim_index < dt_inst.ndims; dim_index++)
539 ar->append_dim_ll(dt_inst.size[dim_index]);
540 }
541 return is_eos5_dims;
542}
543
544void read_objects_basetype_add_eos5_grid_mapping(const eos5_dim_info_t &eos5_dim_info,
545 BaseType *new_var,const HDF5Array *ar) {
546
547 if ((eos5_dim_info.dimpath_to_cvpath.empty() == false) && (ar->get_numdim() > 1))
548 add_possible_var_cv_info(new_var, eos5_dim_info);
549 if (eos5_dim_info.gridname_to_info.empty() == false)
550 make_attributes_to_cf(new_var, eos5_dim_info);
551
552}
553
573//
574
575void
576read_objects_base_type(D4Group * d4_grp, hid_t pid, const string & varname, const string & filename, hid_t dset_id,
577 bool use_dimscale, bool is_eos5, eos5_dim_info_t &eos5_dim_info)
578{
579
580 string newvarname = obtain_new_varname(varname, use_dimscale, is_eos5);
581
582 hid_t datatype = H5Dget_type(dset_id);
583
584 // If this is an HDF5 variable length float/integer type, we need to handle it differently.
585 if (H5Tget_class(datatype) == H5T_VLEN) {
586 handle_vlen_int_float(d4_grp, pid, newvarname, varname, filename,dset_id);
587 H5Tclose(datatype);
588 return;
589 }
590
591 H5Tclose(datatype);
592 // Get a base type. It should be an HDF5 atomic datatype.
593 BaseType *bt = Get_bt_enhanced(d4_grp,pid, newvarname,varname, filename, dt_inst.type);
594 if (!bt)
595 throw InternalErr(__FILE__, __LINE__,"Unable to convert hdf5 datatype to dods basetype");
596
597 // First deal with scalar data.
598 if (dt_inst.ndims == 0) {
599
600 // Mark this base type as an DAP4 object
601 bt->set_is_dap4(true);
602 read_objects_basetype_attr_hl(varname, bt, dset_id, is_eos5);
603
604 // Attach this object to the DAP4 group
605 d4_grp->add_var_nocopy(bt);
606 }
607 else {
608
609 // Next, deal with Array data. This 'else clause' runs to
610 // the end of the method.
611 auto ar_unique = make_unique<HDF5Array>(newvarname, filename, bt);
612 HDF5Array *ar = ar_unique.get();
613 delete bt;
614 bt = nullptr;
615
616 // set number of elements and variable name values.
617 // This essentially stores in the struct.
618 ar->set_memneed(dt_inst.need);
619 ar->set_numdim(dt_inst.ndims);
620 ar->set_numelm((dt_inst.nelmts));
621 ar->set_varpath(varname);
622
623 // If we have dimension names(dimension scale is used.),we will see if we can add the names.
624 int dimnames_size = 0;
625 if ((unsigned int) ((int) (dt_inst.dimnames.size())) != dt_inst.dimnames.size())
626 throw InternalErr(__FILE__, __LINE__,"number of dimensions: overflow");
627
628 dimnames_size = (int) (dt_inst.dimnames.size());
629
630 bool is_eos5_dims = false;
631 if (dimnames_size == dt_inst.ndims)
632 array_add_dimensions_dimscale(ar);
633 else {
634 // With using the dimension scales, the HDF5 file may still have dimension names such as HDF-EOS5.
635 // We search if there are dimension names. If yes, add them here.
636 is_eos5_dims = array_add_dimensions_non_dimscale(ar, varname, eos5_dim_info);
637 }
638
639 // We need to transform dimension info. to DAP4 group
640 BaseType *new_var = nullptr;
641
642 if (is_eos5_dims)
643 new_var = ar->h5dims_transform_to_dap4(d4_grp, eos5_dim_info.varpath_to_dims.at(varname));
644 else
645 new_var = ar->h5dims_transform_to_dap4(d4_grp, dt_inst.dimnames_path);
646
647 // clear DAP4 dimnames_path vector
648 dt_inst.dimnames_path.clear();
649
650 read_objects_basetype_attr_hl(varname, new_var, dset_id, is_eos5);
651
652 // Here we need to add grid_mapping information if necessary.
653 if (is_eos5_dims && !use_dimscale)
654 read_objects_basetype_add_eos5_grid_mapping(eos5_dim_info, new_var, ar);
655
656 // Add this var to DAP4 group.
657 d4_grp->add_var_nocopy(new_var);
658 }
659 BESDEBUG("h5", "<read_objects_base_type(dmr)" << endl);
660}
661
662
663void read_objects_basetype_attr_hl(const string &varname, BaseType *bt, hid_t dset_id, bool is_eos5) {
664
665 // Mark this base type as an DAP4 object
666 bt->set_is_dap4(true);
667
668 // Map the HDF5 dataset attributes to DAP4
669 map_h5_attrs_to_dap4(dset_id,nullptr,bt,nullptr,1);
670
671 // If this variable is a hardlink, stores the HARDLINK info. as an attribute.
672 map_h5_dset_hardlink_to_d4(dset_id,varname,bt,nullptr,1);
673
674 // If this is an HDF-EOS5 object, we need to map the full path of this object to a DAP4 attribute.
675 if (is_eos5)
676 map_h5_varpath_to_dap4_attr(nullptr,bt,nullptr,varname,1);
677
678}
679
680
695void
696read_objects_structure(D4Group *d4_grp, const string & varname,
697 const string & filename,hid_t dset_id,bool use_dimscale,bool is_eos5)
698{
699 string newvarname = obtain_new_varname(varname, use_dimscale, is_eos5);
700
701 // Map HDF5 compound datatype to Structure
702 Structure *structure = Get_structure(newvarname, varname,filename, dt_inst.type,true);
703
704 // TODO: compound datatype should not be used by HDF-EOS5. Still we may add those support.
705 try {
706 BESDEBUG("h5", "=read_objects_structure(): Dimension is "
707 << dt_inst.ndims << endl);
708
709 if (dt_inst.ndims != 0) // Array of Structure
710 read_objects_structure_arrays(d4_grp, structure, varname,newvarname, filename, dset_id, is_eos5);
711 else // A scalar structure
712 read_objects_structure_scalar(d4_grp, structure, varname,dset_id, is_eos5);
713 } // try Structure
714 catch (...) {
715 delete structure;
716 throw;
717 }
718}
719
720void read_objects_structure_arrays(D4Group *d4_grp, Structure *structure, const string & varname,
721 const string &newvarname, const string & filename, hid_t dset_id, bool is_eos5)
722{
723 BESDEBUG("h5", "=read_objects_structure(): array of size " << dt_inst.nelmts << endl);
724 BESDEBUG("h5", "=read_objects_structure(): memory needed = " << dt_inst.need << endl);
725
726 // Create the Array of structure.
727 auto ar_unique = make_unique<HDF5Array>(newvarname, filename, structure);
728 HDF5Array *ar = ar_unique.get();
729 delete structure; structure = nullptr;
730
731 // These parameters are used in the data read function.
732 ar->set_memneed(dt_inst.need);
733 ar->set_numdim(dt_inst.ndims);
734 ar->set_numelm(dt_inst.nelmts);
735 ar->set_length(dt_inst.nelmts);
736 ar->set_varpath(varname);
737
738 // If having dimension names, add the dimension names to DAP.
739 int dimnames_size = 0;
740 if((unsigned int)((int)(dt_inst.dimnames.size())) != dt_inst.dimnames.size())
741 {
742 throw InternalErr(__FILE__, __LINE__,
743 "number of dimensions: overflow");
744 }
745 dimnames_size = (int)(dt_inst.dimnames.size());
746
747 if (dimnames_size ==dt_inst.ndims) {
748 for (int dim_index = 0; dim_index < dt_inst.ndims; dim_index++) {
749 if (dt_inst.dimnames[dim_index].empty() ==false)
750 ar->append_dim_ll(dt_inst.size[dim_index],dt_inst.dimnames[dim_index]);
751 else
752 ar->append_dim_ll(dt_inst.size[dim_index]);
753 }
754 dt_inst.dimnames.clear();
755 }
756 else {
757 for (int dim_index = 0; dim_index < dt_inst.ndims; dim_index++)
758 ar->append_dim_ll(dt_inst.size[dim_index]);
759 }
760
761 // We need to transform dimension info. to DAP4 group
762 BaseType* new_var = ar->h5dims_transform_to_dap4(d4_grp,dt_inst.dimnames_path);
763 dt_inst.dimnames_path.clear();
764
765 // Map HDF5 dataset attributes to DAP4
766 map_h5_attrs_to_dap4(dset_id,nullptr,new_var,nullptr,1);
767
768 // If this is a hardlink, map the Hardlink info. as an DAP4 attribute.
769 map_h5_dset_hardlink_to_d4(dset_id,varname,new_var,nullptr,1);
770 if (is_eos5)
771 map_h5_varpath_to_dap4_attr(nullptr,new_var,nullptr,varname,1);
772
773 // Add this var to DAP4 group
774 if(new_var)
775 d4_grp->add_var_nocopy(new_var);
776
777}
778
779void read_objects_structure_scalar(D4Group *d4_grp, Structure *structure, const string & varname,
780 hid_t dset_id, bool is_eos5)
781{
782 structure->set_is_dap4(true);
783 map_h5_attrs_to_dap4(dset_id,nullptr,nullptr,structure,2);
784 map_h5_dset_hardlink_to_d4(dset_id,varname,nullptr,structure,2);
785 if (is_eos5)
786 map_h5_varpath_to_dap4_attr(nullptr,nullptr,structure,varname,2);
787 if(structure)
788 d4_grp->add_var_nocopy(structure);
789}
790
791string obtain_new_varname(const string &varname, bool use_dimscale, bool is_eos5) {
792
793 // Obtain the relative path of the variable name under the leaf group
794 string newvarname = HDF5CFUtil::obtain_string_after_lastslash(varname);
795 if (use_dimscale) {
796 const string nc4_non_coord="_nc4_non_coord_";
797 size_t nc4_non_coord_size= nc4_non_coord.size();
798 if (newvarname.find(nc4_non_coord) == 0)
799 newvarname = newvarname.substr(nc4_non_coord_size,newvarname.size()-nc4_non_coord_size);
800 }
801 if (is_eos5)
802 newvarname = handle_string_special_characters(newvarname);
803 return newvarname;
804}
805
819//
820
821void map_h5_attrs_to_dap4(hid_t h5_objid,D4Group* d4g,BaseType* d4b,Structure * d4s,int flag) {
822
823 // Get the object info
824 H5O_info_t obj_info;
825 if (H5OGET_INFO(h5_objid, &obj_info) <0) {
826 string msg = "Fail to obtain the HDF5 object info. .";
827 throw InternalErr(__FILE__, __LINE__, msg);
828 }
829
830 // Obtain the number of attributes
831 auto num_attrs = (int)(obj_info.num_attrs);
832 if (num_attrs < 0 ) {
833 string msg = "Fail to get the number of attributes for the HDF5 object. ";
834 throw InternalErr(__FILE__, __LINE__,msg);
835 }
836
837 bool ignore_attr = false;
838 hid_t attr_id = -1;
839
840 for (int j = 0; j < num_attrs; j++) {
841
842 // Obtain attribute information.
843 DSattr_t attr_inst;
844
845 // Ignore the attributes of which the HDF5 datatype
846 // cannot be mapped to DAP4. The ignored attribute datatypes can be found
847 // at function get_attr_info in h5get.cc.
848 attr_id = get_attr_info(h5_objid, j, true,&attr_inst, &ignore_attr);
849 if (true == ignore_attr) {
850 H5Aclose(attr_id);
851 continue;
852 }
853
854 // Get the corresponding DAP data type of the HDF5 datatype.
855 // The following line doesn't work in HDF5 1.10.
856#if 0
857 //hid_t ty_id = attr_inst.type;
858#endif
859 hid_t ty_id = H5Aget_type(attr_id);
860 if (ty_id < 0) {
861 H5Aclose(attr_id);
862 throw InternalErr(__FILE__, __LINE__, "Cannot retrieve HDF5 attribute datatype successfully.");
863 }
864
865 string dap_type = get_dap_type(ty_id,true);
866
867 // Need to have DAP4 representation of the attribute type
868 D4AttributeType dap4_attr_type = daptype_strrep_to_dap4_attrtype(dap_type);
869
870 // We encounter an unsupported DAP4 attribute type.
871 if(attr_null_c == dap4_attr_type) {
872 H5Tclose(ty_id);
873 H5Aclose(attr_id);
874 throw InternalErr(__FILE__, __LINE__, "unsupported DAP4 attribute type");
875 }
876
877 string attr_name = attr_inst.name;
878 BESDEBUG("h5", "attr_name= " << attr_name << endl);
879
880 // Create the DAP4 attribute mapped from HDF5
881 //auto d4_attr = new D4Attribute(attr_name,dap4_attr_type);
882 auto d4_attr_unique = make_unique<D4Attribute>(attr_name, dap4_attr_type);
883 D4Attribute *d4_attr = d4_attr_unique.release();
884
885 // Check if utf8 string.
886 if (dap4_attr_type == attr_str_c && check_if_utf8_str(ty_id) )
887 d4_attr->set_utf8_str_flag(true);
888
889 // We have to handle variable length string differently.
890 if (H5Tis_variable_str(ty_id))
891 write_vlen_str_attrs(attr_id,ty_id,&attr_inst,d4_attr,nullptr,true);
892 else
893 write_dap4_attr(attr_id, d4_attr, ty_id, attr_inst);
894
895 H5Tclose(ty_id);
896 H5Aclose(attr_id);
897
898 if(0 == flag) // D4group
899 d4g->attributes()->add_attribute_nocopy(d4_attr);
900 else if (1 == flag) // HDF5 dataset with atomic datatypes
901 d4b->attributes()->add_attribute_nocopy(d4_attr);
902 else if ( 2 == flag) // HDF5 dataset with compound datatype
903 d4s->attributes()->add_attribute_nocopy(d4_attr);
904 else {
905 stringstream sflag;
906 sflag << flag;
907 string msg = "The add_dap4_attr flag has to be either 0,1 or 2.";
908 msg +="The current flag is "+sflag.str();
909 delete d4_attr;
910 throw InternalErr(__FILE__, __LINE__, msg);
911 }
912 } // for (int j = 0; j < num_attr; j++)
913
914}
915
916void write_dap4_attr(hid_t attr_id, libdap::D4Attribute *d4_attr, hid_t ty_id, const DSattr_t &attr_inst) {
917
918 vector<char> value;
919 value.resize(attr_inst.need);
920 BESDEBUG("h5", "arttr_inst.need=" << attr_inst.need << endl);
921
922 // Need to obtain the memtype since we still find BE data.
923 hid_t memtype = H5Tget_native_type(ty_id, H5T_DIR_ASCEND);
924
925 // Read HDF5 attribute data.
926 if (H5Aread(attr_id, memtype, (void *) (value.data())) < 0) {
927 H5Tclose(ty_id);
928 H5Aclose(attr_id);
929 delete d4_attr;
930 throw InternalErr(__FILE__, __LINE__, "unable to read HDF5 attribute data");
931 }
932 H5Aclose(memtype);
933
934 // Due to the implementation of print_attr, the attribute value will be
935 // written one by one.
936 char *tempvalue = value.data();
937
938 // For scalar data, just read data once.
939 if (attr_inst.ndims == 0)
940 write_dap4_attr_value(d4_attr,ty_id,1,tempvalue);
941 else {// The number of dimensions is > 0
942
943 // Get the attribute datatype size
944 auto elesize = (int) H5Tget_size(ty_id);
945 if (elesize == 0) {
946 H5Tclose(ty_id);
947 H5Aclose(attr_id);
948 delete d4_attr;
949 throw InternalErr(__FILE__, __LINE__, "unable to get attibute size");
950 }
951
952 write_dap4_attr_value(d4_attr,ty_id,attr_inst.nelmts,tempvalue, elesize);
953 } // if attr_inst.ndims != 0
954}
955
956void write_dap4_attr_value(D4Attribute *d4_attr, hid_t ty_id, hsize_t nelmts, char *tempvalue, size_t elesize) {
957
958 // Write this value. the "loc" can always be set to 0 since
959 // tempvalue will be moved to the next value.
960 string print_rep;
961 for (hsize_t temp_index = 0; temp_index < nelmts; temp_index++) {
962
963 print_rep = print_attr(ty_id, 0, tempvalue);
964 if (print_rep.c_str() != nullptr) {
965
966 BESDEBUG("h5", "print_rep= " << print_rep << endl);
967 d4_attr->add_value(print_rep);
968 tempvalue = tempvalue + elesize;
969 BESDEBUG("h5",
970 "tempvalue= " << tempvalue
971 << "elesize=" << elesize
972 << endl);
973 } else {
974 H5Tclose(ty_id);
975 delete d4_attr;
976 throw InternalErr(__FILE__, __LINE__, "unable to convert attribute value to DAP");
977 }
978 }
979}
993
994
995void map_h5_dset_hardlink_to_d4(hid_t h5_dsetid,const string & full_path, BaseType* d4b,Structure * d4s,int flag) {
996
997 // Obtain the unique object number info. If no hardlinks, empty string will return.
998 string oid = get_hardlink_dmr(h5_dsetid, full_path);
999
1000 // Find that this is a hardlink,add the hardlink info to a DAP4 attribute.
1001 if(false == oid.empty()) {
1002
1003 auto d4_hlinfo_unique = make_unique<D4Attribute>("HDF5_HARDLINK",attr_str_c);
1004 auto d4_hlinfo = d4_hlinfo_unique.release();
1005 d4_hlinfo->add_value(obj_paths.get_name(oid));
1006
1007 if (1 == flag)
1008 d4b->attributes()->add_attribute_nocopy(d4_hlinfo);
1009 else if ( 2 == flag)
1010 d4s->attributes()->add_attribute_nocopy(d4_hlinfo);
1011 else
1012 delete d4_hlinfo;
1013 }
1014
1015}
1016
1017
1031//
1032
1033void map_h5_varpath_to_dap4_attr(D4Group* d4g,BaseType* d4b,Structure * d4s,const string & varpath, short flag) {
1034
1035 auto d4_attr_unique = make_unique<D4Attribute>("fullnamepath",attr_str_c);
1036 auto d4_attr = d4_attr_unique.release();
1037 d4_attr->add_value(varpath);
1038
1039 if (0 == flag) // D4group
1040 d4g->attributes()->add_attribute_nocopy(d4_attr);
1041 else if (1 == flag) // HDF5 dataset with atomic datatypes
1042 d4b->attributes()->add_attribute_nocopy(d4_attr);
1043 else if ( 2 == flag) // HDF5 dataset with compound datatype
1044 d4s->attributes()->add_attribute_nocopy(d4_attr);
1045 else {
1046 stringstream sflag;
1047 sflag << flag;
1048 string msg ="The add_dap4_attr flag has to be either 0,1 or 2.";
1049 msg+="The current flag is "+sflag.str();
1050 delete d4_attr;
1051 throw InternalErr(__FILE__, __LINE__, msg);
1052 }
1053
1054}
1055
1056
1069void get_softlink(D4Group* par_grp, hid_t h5obj_id, const string & oname, int index, size_t val_size)
1070{
1071 BESDEBUG("h5", "dap4 get_softlink():" << oname << endl);
1072
1073 ostringstream oss;
1074 oss << string("HDF5_SOFTLINK");
1075 oss << "_";
1076 oss << index;
1077 string temp_varname = oss.str();
1078
1079 BESDEBUG("h5", "dap4->get_softlink():link name " << temp_varname << endl);
1080
1081 auto d4_slinfo_unique = make_unique<D4Attribute>();
1082 auto d4_slinfo = d4_slinfo_unique.release();
1083 d4_slinfo->set_name(temp_varname);
1084
1085 // Make the type as a container
1086 d4_slinfo->set_type(attr_container_c);
1087
1088 string softlink_name = "linkname";
1089
1090 auto softlink_src_unique = make_unique<D4Attribute>(softlink_name, attr_str_c);
1091 auto softlink_src = softlink_src_unique.release();
1092 softlink_src->add_value(oname);
1093
1094 d4_slinfo->attributes()->add_attribute_nocopy(softlink_src);
1095 string softlink_value_name ="LINKTARGET";
1096
1097 vector<char> buf;
1098 buf.resize(val_size + 1);
1099
1100 // get link target name
1101 if (H5Lget_val(h5obj_id, oname.c_str(), (void*) buf.data(), val_size + 1, H5P_DEFAULT) < 0) {
1102 throw InternalErr(__FILE__, __LINE__, "unable to get link value");
1103 }
1104 auto softlink_tgt_unique = make_unique<D4Attribute>(softlink_value_name, attr_str_c);
1105 auto softlink_tgt = softlink_tgt_unique.release();
1106
1107 auto link_target_name = string(buf.begin(), buf.end());
1108 softlink_tgt->add_value(link_target_name);
1109
1110 d4_slinfo->attributes()->add_attribute_nocopy(softlink_tgt);
1111 par_grp->attributes()->add_attribute_nocopy(d4_slinfo);
1112}
1113
1114
1127string get_hardlink_dmr( hid_t h5obj_id, const string & oname) {
1128
1129 BESDEBUG("h5", "dap4->get_hardlink_dmr():" << oname << endl);
1130
1131 // Get the object info
1132 H5O_info_t obj_info;
1133 if (H5OGET_INFO(h5obj_id, &obj_info) <0)
1134 throw InternalErr(__FILE__, __LINE__, "H5OGET_INFO() failed.");
1135
1136 // If the reference count is greater than 1,that means
1137 // hard links are found. return the original object name this
1138 // hard link points to.
1139
1140 if (obj_info.rc >1) {
1141
1142 string objno;
1143
1144#if (H5_VERS_MAJOR == 1 && ((H5_VERS_MINOR == 12) || (H5_VERS_MINOR == 13) || (H5_VERS_MINOR == 14)))
1145 char *obj_tok_str = nullptr;
1146 if(H5Otoken_to_str(h5obj_id, &(obj_info.token), &obj_tok_str) <0) {
1147 throw InternalErr(__FILE__, __LINE__, "H5Otoken_to_str failed.");
1148 }
1149 objno.assign(obj_tok_str,obj_tok_str+strlen(obj_tok_str));
1150 H5free_memory(obj_tok_str);
1151
1152#else
1153 ostringstream oss;
1154 oss << hex << obj_info.addr;
1155 objno = oss.str();
1156#endif
1157
1158 BESDEBUG("h5", "dap4->get_hardlink_dmr() objno=" << objno << endl);
1159
1160 // Add this hard link to the map.
1161 // obj_paths is a global variable defined at the beginning of this file.
1162 // it is essentially an id to obj name map. See HDF5PathFinder.h.
1163 if (!obj_paths.add(objno, oname)) {
1164 return objno;
1165 }
1166 else {
1167 return "";
1168 }
1169 }
1170 else {
1171 return "";
1172 }
1173
1174}
1175
1176// This function is to retrieve structmetadata from an HDF-EOS5 file.
1177string read_struct_metadata(hid_t s_file_id) {
1178
1179 BESDEBUG("h5","Coming to read_struct_metadata() "<<endl);
1180
1181 string total_strmeta_value;
1182 string ecs_group = "/HDFEOS INFORMATION";
1183 hid_t ecs_grp_id = -1;
1184 if ((ecs_grp_id = H5Gopen(s_file_id, ecs_group.c_str(),H5P_DEFAULT)) < 0) {
1185 string msg = "h5_ecs_meta: unable to open the HDF5 group ";
1186 msg += ecs_group;
1187 throw InternalErr(__FILE__, __LINE__, msg);
1188 }
1189
1190 H5G_info_t g_info;
1191 hsize_t nelems = 0;
1192
1193 if (H5Gget_info(ecs_grp_id,&g_info) < 0) {
1194 string msg = "h5_ecs_meta: unable to obtain the HDF5 group info. for ";
1195 msg +=ecs_group;
1196 H5Gclose(ecs_grp_id);
1197 throw InternalErr(__FILE__, __LINE__, msg);
1198 }
1199
1200 nelems = g_info.nlinks;
1201
1202 // Initialize the total number of structure metadata.
1203 int strmeta_num_total = 0;
1204 bool strmeta_no_suffix = true;
1205
1206 // Define a vector of string to hold all dataset names.
1207 vector<string> s_oname(nelems);
1208
1209 // Define an EOSMetadata array that can describe the metadata type for each object
1210 // We initialize the value to OtherMeta.
1211 vector<bool> smetatype(nelems,false);
1212
1213 obtain_struct_metadata_info(ecs_grp_id, s_oname, smetatype,
1214 strmeta_num_total, strmeta_no_suffix, nelems);
1215
1216 // Define a vector of string to hold StructMetadata.
1217 // StructMetadata must exist for a valid HDF-EOS5 file.
1218 vector<string> strmeta_value;
1219 if (strmeta_num_total <= 0) {
1220 string msg = "hdf5 object name error from: ";
1221 H5Gclose(ecs_grp_id);
1222 throw InternalErr(__FILE__, __LINE__, msg);
1223 }
1224 else {
1225 strmeta_value.resize(strmeta_num_total);
1226 for (int i = 0; i < strmeta_num_total; i++)
1227 strmeta_value[i]="";
1228 }
1229
1230 int strmeta_num = obtain_struct_metadata_value(ecs_grp_id, s_oname,smetatype, nelems,
1231 strmeta_value, total_strmeta_value);
1232
1233 // Now we need to handle the concatenation of the metadata
1234 if ((strmeta_num_total > 0) && (strmeta_num != -1) ) {
1235 // The no suffix one has been taken care.
1236 for (int i = 0; i <strmeta_num_total; i++)
1237 total_strmeta_value +=strmeta_value[i];
1238 }
1239
1240 return total_strmeta_value;
1241}
1242
1243void obtain_struct_metadata_info(hid_t ecs_grp_id, vector<string> &s_oname, vector<bool> &smetatype,
1244 int &strmeta_num_total, bool &strmeta_no_suffix, hsize_t nelems) {
1245
1246 string ecs_group = "/HDFEOS INFORMATION";
1247 ssize_t oname_size = 0;
1248 for (hsize_t i = 0; i < nelems; i++) {
1249
1250 // Query the length of the object name.
1251 oname_size = H5Lget_name_by_idx(ecs_grp_id, ".", H5_INDEX_NAME, H5_ITER_NATIVE,
1252 i, nullptr,0, H5P_DEFAULT);
1253 if (oname_size <= 0) {
1254 string msg = "hdf5 object name error from: ";
1255 msg += ecs_group;
1256 H5Gclose(ecs_grp_id);
1257 throw InternalErr(__FILE__, __LINE__, msg);
1258 }
1259
1260 // Obtain the name of the object.
1261 vector<char> oname(oname_size + 1);
1262 if (H5Lget_name_by_idx(ecs_grp_id, ".", H5_INDEX_NAME, H5_ITER_NATIVE, i,
1263 oname.data(),(size_t) (oname_size + 1), H5P_DEFAULT) < 0) {
1264 string msg = "hdf5 object name error from: ";
1265 msg += ecs_group;
1266 H5Gclose(ecs_grp_id);
1267 throw InternalErr(__FILE__, __LINE__, msg);
1268 }
1269
1270 // Check if this object is an HDF5 dataset, not, throw an error.
1271 // First, check if it is the hard link or the soft link
1272 H5L_info_t linfo;
1273 if (H5Lget_info(ecs_grp_id, oname.data(), &linfo, H5P_DEFAULT) < 0) {
1274 string msg = "hdf5 link name error from: ";
1275 msg += ecs_group;
1276 H5Gclose(ecs_grp_id);
1277 throw InternalErr(__FILE__, __LINE__, msg);
1278 }
1279
1280 // This is the soft link.
1281 if (linfo.type == H5L_TYPE_SOFT) {
1282 string msg = "hdf5 link name error from: ";
1283 msg += ecs_group;
1284 H5Gclose(ecs_grp_id);
1285 throw InternalErr(__FILE__, __LINE__, msg);
1286 }
1287
1288 // Obtain the object type
1289 H5O_info_t oinfo;
1290 if (H5OGET_INFO_BY_IDX(ecs_grp_id, ".", H5_INDEX_NAME, H5_ITER_NATIVE,
1291 i, &oinfo, H5P_DEFAULT) < 0) {
1292 string msg = "Cannot obtain the object info ";
1293 msg += ecs_group;
1294 H5Gclose(ecs_grp_id);
1295 throw InternalErr(__FILE__, __LINE__, msg);
1296 }
1297
1298 if (oinfo.type != H5O_TYPE_DATASET) {
1299 string msg = "hdf5 link name error from: ";
1300 msg += ecs_group;
1301 H5Gclose(ecs_grp_id);
1302 throw InternalErr(__FILE__, __LINE__, msg);
1303 }
1304
1305 // We want to remove the last '\0' character added by C .
1306 string s_one_oname(oname.begin(), oname.end() - 1);
1307 s_oname[i] = s_one_oname;
1308
1309 // Calculate how many elements we have for each category(StructMetadata, CoreMetadata, etc.)
1310 if (((s_one_oname.find("StructMetadata")) == 0) ||
1311 ((s_one_oname.find("structmetadata")) == 0)) {
1312
1313 smetatype[i] = true;
1314
1315 // Do we have suffix for the metadata?
1316 // If this metadata doesn't have any suffix, it should only come to this loop once.
1317 // That's why, when checking the first time, no_suffix is always true.
1318 // If we have already found that it doesn't have any suffix,
1319 // it should not go into this loop. throw an error.
1320 if (false == strmeta_no_suffix) {
1321 string msg = "StructMetadata/structmetadata without suffix should only appear once. ";
1322 H5Gclose(ecs_grp_id);
1323 throw InternalErr(__FILE__, __LINE__, msg);
1324 } else if (strmeta_num_total > 0)
1325 strmeta_num_total++;
1326 // either no suffix or the first time to loop the one having the suffix.
1327 else if ((0 == s_one_oname.compare("StructMetadata")) ||
1328 (0 == s_one_oname.compare("structmetadata")))
1329 strmeta_no_suffix = false;
1330 else strmeta_num_total++;
1331 }
1332 oname.clear();
1333 s_one_oname.clear();
1334 }
1335}
1336
1337int obtain_struct_metadata_value(hid_t ecs_grp_id, const vector<string> &s_oname, const vector<bool> &smetatype,
1338 hsize_t nelems, vector<string> &strmeta_value, string &total_strmeta_value) {
1339
1340 int strmeta_num = -1;
1341 // Now we want to retrieve the metadata value and combine them into one string.
1342 // Here we have to remember the location of every element of the metadata if
1343 // this metadata has a suffix.
1344 for (hsize_t i = 0; i < nelems; i++) {
1345
1346 // We only read StructMetadata string.
1347 // Struct Metadata is generated by the HDF-EOS5 library, so the
1348 // name "StructMetadata.??" won't change for real struct metadata.
1349 //However, we still assume that somebody may not use the HDF-EOS5
1350 // library to add StructMetadata, the name may be "structmetadata".
1351 if (((s_oname[i].find("StructMetadata"))!=0) && ((s_oname[i].find("structmetadata"))!=0))
1352 continue;
1353
1354 // Open the dataset, dataspace, datatype, number of elements etc. for this metadata
1355 hid_t s_dset_id = -1;
1356 hid_t s_space_id = -1;
1357 hid_t s_ty_id = -1;
1358 hssize_t s_nelms = -1;
1359 size_t dtype_size = -1;
1360
1361 if ((s_dset_id = H5Dopen(ecs_grp_id,s_oname[i].c_str(),H5P_DEFAULT))<0){
1362 string msg = "Cannot open HDF5 dataset ";
1363 msg += s_oname[i];
1364 H5Gclose(ecs_grp_id);
1365 throw InternalErr(__FILE__, __LINE__, msg);
1366 }
1367
1368 if ((s_space_id = H5Dget_space(s_dset_id))<0) {
1369 string msg = "Cannot open the data space of HDF5 dataset ";
1370 msg += s_oname[i];
1371 H5Dclose(s_dset_id);
1372 H5Gclose(ecs_grp_id);
1373 throw InternalErr(__FILE__, __LINE__, msg);
1374 }
1375
1376 if ((s_ty_id = H5Dget_type(s_dset_id)) < 0) {
1377 string msg = "Cannot get the data type of HDF5 dataset ";
1378 msg += s_oname[i];
1379 H5Sclose(s_space_id);
1380 H5Dclose(s_dset_id);
1381 H5Gclose(ecs_grp_id);
1382 throw InternalErr(__FILE__, __LINE__, msg);
1383 }
1384
1385 if ((s_nelms = H5Sget_simple_extent_npoints(s_space_id))<0) {
1386 string msg = "Cannot get the number of points of HDF5 dataset ";
1387 msg += s_oname[i];
1388 H5Tclose(s_ty_id);
1389 H5Sclose(s_space_id);
1390 H5Dclose(s_dset_id);
1391 H5Gclose(ecs_grp_id);
1392 throw InternalErr(__FILE__, __LINE__, msg);
1393 }
1394
1395 if ((dtype_size = H5Tget_size(s_ty_id))==0) {
1396
1397 string msg = "Cannot get the data type size of HDF5 dataset ";
1398 msg += s_oname[i];
1399 H5Tclose(s_ty_id);
1400 H5Sclose(s_space_id);
1401 H5Dclose(s_dset_id);
1402 H5Gclose(ecs_grp_id);
1403 throw InternalErr(__FILE__, __LINE__, msg);
1404 }
1405
1406 // Obtain the real value of the metadata
1407 vector<char> s_buf(dtype_size*s_nelms +1);
1408
1409 if ((H5Dread(s_dset_id,s_ty_id,H5S_ALL,H5S_ALL,H5P_DEFAULT,
1410 s_buf.data()))<0) {
1411
1412 string msg = "Cannot read HDF5 dataset ";
1413 msg += s_oname[i];
1414 H5Tclose(s_ty_id);
1415 H5Sclose(s_space_id);
1416 H5Dclose(s_dset_id);
1417 H5Gclose(ecs_grp_id);
1418 throw InternalErr(__FILE__, __LINE__, msg);
1419 }
1420
1421 // Now we can safely close datatype, data space and dataset IDs.
1422 H5Tclose(s_ty_id);
1423 H5Sclose(s_space_id);
1424 H5Dclose(s_dset_id);
1425
1426 // Convert from the vector<char> to a C++ string.
1427 string tempstr(s_buf.begin(),s_buf.end());
1428 s_buf.clear();
1429 size_t temp_null_pos = tempstr.find_first_of('\0');
1430
1431 // temp_null_pos returns the position of nullptr,which is the last character of the string.
1432 // so the length of string before null is EQUAL to
1433 // temp_null_pos since pos starts at 0.
1434 string finstr = tempstr.substr(0,temp_null_pos);
1435
1436 if (true == smetatype[i]) {
1437 strmeta_num = obtain_struct_metadata_value_internal(ecs_grp_id, s_oname, strmeta_value,total_strmeta_value,
1438 finstr, i);
1439 }
1440 tempstr.clear();
1441 finstr.clear();
1442 }
1443 return strmeta_num;
1444}
1445
1446int obtain_struct_metadata_value_internal(hid_t ecs_grp_id, const vector<string> &s_oname,
1447 vector<string> &strmeta_value, string &total_strmeta_value,
1448 const string &finstr, hsize_t i)
1449{
1450 int strmeta_num = -1;
1451
1452 // Now obtain the corresponding value in integer type for the suffix. '0' to 0 etc.
1453 try {
1454 strmeta_num = get_strmetadata_num(s_oname[i]);
1455 }
1456 catch(...) {
1457 H5Gclose(ecs_grp_id);
1458 throw InternalErr(__FILE__,__LINE__,"Obtain structmetadata suffix error.");
1459
1460 }
1461 // This is probably not necessary, since structmetadata may always have a suffix.
1462 // Leave here just in case the rules change or a special non-HDF-EOS5 library generated file.
1463 // when strmeta_num is -1, it means no suffix for this metadata. So the total structmetadata
1464 // is this string only.
1465 if (-1 == strmeta_num)
1466 total_strmeta_value = finstr;
1467 // strmeta_value at this point should be empty before assigning any value.
1468 else if (strmeta_value[strmeta_num].empty() == false) {
1469 string msg = "The structmeta value array at this index should be empty string ";
1470 H5Gclose(ecs_grp_id);
1471 throw InternalErr(__FILE__, __LINE__, msg);
1472 }
1473 // assign the string vector to this value.
1474 else
1475 strmeta_value[strmeta_num] = finstr;
1476
1477 return strmeta_num;
1478}
1479
1480// Helper function for read_ecs_metadata. Get the number after metadata.
1481int get_strmetadata_num(const string & meta_str) {
1482
1483 size_t dot_pos = meta_str.find(".");
1484 if (dot_pos == string::npos) // No dot
1485 return -1;
1486 else {
1487 string num_str = meta_str.substr(dot_pos+1);
1488 stringstream ssnum(num_str);
1489 int num;
1490 ssnum >> num;
1491 if (ssnum.fail())
1492 throw InternalErr(__FILE__,__LINE__,"Suffix after dots is not a number.");
1493 return num;
1494 }
1495}
1496
1497void obtain_eos5_dims(hid_t fileid, eos5_dim_info_t &eos5_dim_info) {
1498
1499 unordered_map<string, vector<string>> varpath_to_dims;
1500 unordered_map<string, vector<HE5Dim>> grppath_to_dims;
1501 unordered_map<string, eos5_grid_info_t> gridname_to_info;
1502
1503 string st_str = read_struct_metadata(fileid);
1504
1505 // Parse the structmetadata
1506 HE5Parser p;
1507 HE5Checker c;
1508 he5dds_scan_string(st_str.c_str());
1509 he5ddsparse(&p);
1510 he5ddslex_destroy();
1511
1512 // Retrieve ProjParams from StructMetadata
1513 p.add_projparams(st_str);
1514
1515#if 0
1516 p.print();
1517#endif
1518
1519 // Check if the HDF-EOS5 grid has the valid parameters, projection codes.
1520 if (c.check_grids_unknown_parameters(&p)) {
1521 throw InternalErr("Unknown HDF-EOS5 grid paramters found in the file");
1522 }
1523
1524 if (c.check_grids_missing_projcode(&p)) {
1525 throw InternalErr("The HDF-EOS5 is missing project code ");
1526 }
1527
1528 // We gradually add the support of different projection codes
1529 if (c.check_grids_support_projcode(&p)) {
1530 throw InternalErr("The current project code is not supported");
1531 }
1532
1533 // HDF-EOS5 provides default pixel and origin values if they are not defined.
1534 c.set_grids_missing_pixreg_orig(&p);
1535
1536 for (const auto &sw:p.swath_list)
1537 build_grp_dim_path(sw.name,sw.dim_list,grppath_to_dims,HE5_TYPE::SW);
1538
1539 for (const auto &sw:p.swath_list)
1540 build_var_dim_path(sw.name,sw.data_var_list,varpath_to_dims,HE5_TYPE::SW,false);
1541
1542 for (const auto &sw:p.swath_list)
1543 build_var_dim_path(sw.name,sw.geo_var_list,varpath_to_dims,HE5_TYPE::SW,true);
1544
1545 for (const auto &gd:p.grid_list)
1546 build_grp_dim_path(gd.name,gd.dim_list,grppath_to_dims,HE5_TYPE::GD);
1547
1548 for (const auto &gd:p.grid_list)
1549 build_var_dim_path(gd.name,gd.data_var_list,varpath_to_dims,HE5_TYPE::GD,false);
1550
1551 for (const auto &gd:p.grid_list)
1552 build_gd_info(gd, gridname_to_info);
1553
1554 for (const auto &za:p.za_list)
1555 build_grp_dim_path(za.name,za.dim_list,grppath_to_dims,HE5_TYPE::ZA);
1556
1557 for (const auto &za:p.za_list)
1558 build_var_dim_path(za.name,za.data_var_list,varpath_to_dims,HE5_TYPE::ZA,false);
1559
1560
1561#if 0
1562for (const auto& it:varpath_to_dims) {
1563 cout<<"var path is "<<it.first <<endl;
1564 for (const auto &sit:it.second)
1565 cout<<"var dimension name is "<<sit <<endl;
1566}
1567
1568for (const auto& it:grppath_to_dims) {
1569 cout<<"grp path is "<<it.first <<endl;
1570 for (const auto &sit:it.second) {
1571 cout<<"grp dimension name is "<<sit.name<<endl;
1572 cout<<"grp dimension size is "<<sit.size<<endl;
1573 }
1574}
1575
1576for (const auto &it:gridname_to_info) {
1577 cout<<"grid name is "<<it.first <<endl;
1578 cout<<"grid x dimension name is "<<it.second.xdim_fqn << " and the size is: "<< it.second.xdim_size << endl;
1579 cout<<"grid y dimension name is "<<it.second.ydim_fqn << " and the size is: "<< it.second.ydim_size << endl;
1580}
1581#endif
1582
1583 eos5_dim_info.varpath_to_dims = varpath_to_dims;
1584 eos5_dim_info.grppath_to_dims = grppath_to_dims;
1585 eos5_dim_info.gridname_to_info = gridname_to_info;
1586}
1587
1588void build_grp_dim_path(const string & eos5_obj_name, const vector<HE5Dim>& dim_list, unordered_map<string,
1589 vector<HE5Dim>>& grppath_to_dims, HE5_TYPE e5_type) {
1590
1591 string eos_name_prefix = "/HDFEOS/";
1592 string eos5_grp_path;
1593
1594 switch (e5_type) {
1595 case HE5_TYPE::SW:
1596 eos5_grp_path = eos_name_prefix + "SWATHS/" + eos5_obj_name;
1597 break;
1598 case HE5_TYPE::GD:
1599 eos5_grp_path = eos_name_prefix + "GRIDS/" + eos5_obj_name;
1600 break;
1601 case HE5_TYPE::ZA:
1602 eos5_grp_path = eos_name_prefix + "ZAS/" + eos5_obj_name;
1603 break;
1604 default:
1605 break;
1606 }
1607
1608#if 0
1609 for (const auto & eos5dim:dim_list) {
1610 cout << "EOS5 Dim Name=" << eos5dim.name << endl;
1611 cout << "EOS5 Dim Size=" << eos5dim.size << endl;
1612 }
1613#endif
1614
1615 vector <HE5Dim> grp_dims;
1616 for (const auto &eos5dim:dim_list) {
1617
1618 // Here we need to remove the Unlimited dimension if the size is -1 since we currently don't support unlimited dimension.
1619 // TODO: need to rehandle this when adding the unlimited dimension support.
1620 if (eos5dim.name =="Unlimited" && eos5dim.size == -1)
1621 continue;
1622
1623 HE5Dim eos5_dimp;
1624 string new_eos5dim_name = eos5dim.name;
1625
1626 string dim_fpath = eos5_grp_path +"/" + handle_string_special_characters(new_eos5dim_name);
1627 eos5_dimp.name = dim_fpath;
1628 eos5_dimp.size = eos5dim.size;
1629 grp_dims.push_back(eos5_dimp);
1630 }
1631
1632 pair<string,vector<HE5Dim>> gtod = make_pair(eos5_grp_path,grp_dims);
1633 grppath_to_dims.insert(gtod);
1634
1635}
1636
1637void build_var_dim_path(const string & eos5_obj_name, const vector<HE5Var>& var_list, unordered_map<string,
1638 vector<string>>& varpath_to_dims, HE5_TYPE e5_type, bool is_geo) {
1639
1640 string eos_name_prefix = "/HDFEOS/";
1641 string eos5_data_grp_name = "/Data Fields/";
1642 string eos5_geo_grp_name = "/Geolocation Fields/";
1643 string eos5_dim_name_prefix;
1644 string new_eos5_obj_name = eos5_obj_name;
1645
1646 switch (e5_type) {
1647 case HE5_TYPE::SW:
1648 eos5_dim_name_prefix = eos_name_prefix + "SWATHS/"+handle_string_special_characters(new_eos5_obj_name) +"/";
1649 break;
1650 case HE5_TYPE::GD:
1651 eos5_dim_name_prefix = eos_name_prefix + "GRIDS/"+handle_string_special_characters(new_eos5_obj_name) +"/";
1652 break;
1653 case HE5_TYPE::ZA:
1654 eos5_dim_name_prefix = eos_name_prefix + "ZAS/"+handle_string_special_characters(new_eos5_obj_name) +"/";
1655 break;
1656 default:
1657 break;
1658 }
1659
1660 // Note: in this routine, we should handle the special characters of var path because we need to remember
1661 // the path for the dmrpp module to correctly open the HDF5 dataset.
1662 for (const auto & eos5var:var_list) {
1663
1664 string var_path;
1665 vector<string> var_dim_names;
1666
1667 switch (e5_type) {
1668
1669 case HE5_TYPE::SW:
1670 {
1671 if (is_geo)
1672 var_path = eos_name_prefix + "SWATHS/"+eos5_obj_name + eos5_geo_grp_name + eos5var.name;
1673 else
1674 var_path = eos_name_prefix + "SWATHS/"+eos5_obj_name + eos5_data_grp_name + eos5var.name;
1675 break;
1676 }
1677
1678 case HE5_TYPE::GD:
1679 {
1680 var_path = eos_name_prefix + "GRIDS/"+eos5_obj_name + eos5_data_grp_name + eos5var.name;
1681 break;
1682 }
1683
1684 case HE5_TYPE::ZA:
1685 {
1686 var_path = eos_name_prefix + "ZAS/"+eos5_obj_name + eos5_data_grp_name + eos5var.name;
1687 break;
1688 }
1689 default:
1690 break;
1691 }
1692
1693#if 0
1694cout <<"var_path is "<<var_path <<endl;
1695 for (const auto &eos5dim:eos5var.dim_list) {
1696 cout << "EOS Var Dim Name=" << eos5dim.name << endl;
1697 }
1698#endif
1699 for (const auto &eos5dim:eos5var.dim_list) {
1700 string new_eos5dim_name = eos5dim.name;
1701 string dim_fpath = eos5_dim_name_prefix + handle_string_special_characters(new_eos5dim_name);
1702 var_dim_names.push_back(dim_fpath);
1703 }
1704 pair<string,vector<string>> vtod = make_pair(var_path,var_dim_names);
1705 varpath_to_dims.insert(vtod);
1706 }
1707}
1708
1709// Obtain the dimension names of the variable with the path of varname.
1710bool obtain_eos5_dim(const string & varname, const unordered_map<string, vector<string>>& varpath_to_dims,
1711 vector<string> & dimnames) {
1712
1713 bool ret_value = false;
1714 unordered_map<string,vector<string>>::const_iterator vit = varpath_to_dims.find(varname);
1715 if (vit != varpath_to_dims.end()){
1716 for (const auto &sit:vit->second)
1717 dimnames.push_back(HDF5CFUtil::obtain_string_after_lastslash(sit));
1718 ret_value = true;
1719 }
1720 return ret_value;
1721}
1722
1723// Obtain the dimension names of DAP4 group with the path of grpname.
1724bool obtain_eos5_grp_dim(const string & grpname, const unordered_map<string, vector<HE5Dim>>& grppath_to_dims,
1725 vector<string> & dimnames) {
1726
1727 bool ret_value = false;
1728 unordered_map<string,vector<HE5Dim>>::const_iterator vit = grppath_to_dims.find(grpname);
1729 if (vit != grppath_to_dims.end()){
1730 for (const auto &sit:vit->second)
1731 dimnames.push_back(HDF5CFUtil::obtain_string_after_lastslash(sit.name));
1732 ret_value = true;
1733 }
1734 return ret_value;
1735}
1736
1737hsize_t obtain_unlim_pure_dim_size(hid_t pid, const string &dname) {
1738
1739 hsize_t ret_value = 0;
1740
1741 hid_t dset_id = -1;
1742 if((dset_id = H5Dopen(pid,dname.c_str(),H5P_DEFAULT)) <0) {
1743 string msg = "cannot open the HDF5 dataset ";
1744 msg += dname;
1745 throw InternalErr(__FILE__, __LINE__, msg);
1746 }
1747
1748 htri_t has_reference_list = -1;
1749 string reference_name= "REFERENCE_LIST";
1750 has_reference_list = H5Aexists(dset_id,reference_name.c_str());
1751
1752 if (has_reference_list > 0)
1753 ret_value = obtain_unlim_pure_dim_size_internal(dset_id, dname, reference_name);
1754
1755 if (H5Dclose(dset_id) <0) {
1756 string msg = "cannot close the HDF5 dataset ";
1757 msg += dname;
1758 throw InternalErr(__FILE__, __LINE__, msg);
1759 }
1760
1761 return ret_value;
1762}
1763
1764hsize_t obtain_unlim_pure_dim_size_internal(hid_t dset_id, const string &dname, const string &reference_name) {
1765
1766 hsize_t ret_value = 0;
1767
1768 hid_t attr_id = H5Aopen(dset_id,reference_name.c_str(),H5P_DEFAULT);
1769 if (attr_id <0 ) {
1770 H5Dclose(dset_id);
1771 string msg = "Cannot open the attribute " + reference_name + " of HDF5 dataset "+ dname;
1772 throw InternalErr(__FILE__, __LINE__, msg);
1773 }
1774
1775 hid_t atype_id = H5Aget_type(attr_id);
1776 if (atype_id <0) {
1777 H5Aclose(attr_id);
1778 H5Dclose(dset_id);
1779 string msg = "Cannot get the datatype of the attribute " + reference_name + " of HDF5 dataset "+ dname;
1780 throw InternalErr(__FILE__, __LINE__, msg);
1781 }
1782
1783 if (H5T_COMPOUND == H5Tget_class(atype_id))
1784 ret_value = obtain_unlim_pure_dim_size_internal_value(dset_id, attr_id, atype_id, reference_name, dname);
1785
1786 H5Aclose(attr_id);
1787 H5Tclose(atype_id);
1788
1789 return ret_value;
1790}
1791
1792hsize_t obtain_unlim_pure_dim_size_internal_value(hid_t dset_id, hid_t attr_id, hid_t atype_id,
1793 const string &reference_name, const string &dname) {
1794
1795 typedef struct s_t {
1796 hobj_ref_t s_ref;
1797 int s_index;
1798 } s_t;
1799
1800 hsize_t ret_value = 0;
1801 hid_t aspace_id = H5Aget_space(attr_id);
1802 if (aspace_id < 0) {
1803 H5Aclose(attr_id);
1804 H5Tclose(atype_id);
1805 H5Dclose(dset_id);
1806 string msg = "Cannot obtain the data space ID for the attribute " + reference_name;
1807 throw InternalErr(__FILE__, __LINE__, msg);
1808 }
1809
1810 hssize_t num_ele_ref = H5Sget_simple_extent_npoints(aspace_id);
1811 if (num_ele_ref < 0) {
1812 H5Aclose(attr_id);
1813 H5Tclose(atype_id);
1814 H5Dclose(dset_id);
1815 string msg = "Cannot obtain the number of elements for space of the attribute " + reference_name;
1816 throw InternalErr(__FILE__, __LINE__, msg);
1817 }
1818
1819 size_t ele_size = H5Tget_size(atype_id);
1820 if (ele_size == 0) {
1821 H5Aclose(attr_id);
1822 H5Tclose(atype_id);
1823 H5Dclose(dset_id);
1824 string msg = "Cannot obtain the datatype size of the attribute " + reference_name;
1825 throw InternalErr(__FILE__, __LINE__, msg);
1826 }
1827
1828 if (sizeof(s_t)!=ele_size) {
1829 H5Aclose(attr_id);
1830 H5Tclose(atype_id);
1831 H5Dclose(dset_id);
1832 string msg = "The data type size is not the same as the struct. ";
1833 throw InternalErr(__FILE__, __LINE__, msg);
1834 }
1835 vector<s_t> ref_list(num_ele_ref);
1836
1837 if (H5Aread(attr_id,atype_id,ref_list.data()) <0) {
1838 H5Aclose(attr_id);
1839 H5Tclose(atype_id);
1840 H5Dclose(dset_id);
1841 string msg = "Cannot obtain the referenced object for the variable " + dname;
1842 throw InternalErr(__FILE__, __LINE__, msg);
1843 }
1844
1845 // To obtain the dimension size, we only need to grab the first referred object.
1846 // The size is the same for all objects. KY 2022-12-14
1847
1848 H5O_type_t obj_type;
1849 if (H5Rget_obj_type2(dset_id, H5R_OBJECT, &((ref_list[0]).s_ref),&obj_type) <0) {
1850 H5Aclose(attr_id);
1851 H5Tclose(atype_id);
1852 H5Dclose(dset_id);
1853 string msg = "Cannot obtain the referenced object for the variable " + dname;
1854 throw InternalErr(__FILE__, __LINE__, msg);
1855 }
1856
1857 if (obj_type == H5O_TYPE_DATASET) {
1858
1859 hid_t did_ref = H5Rdereference2(dset_id, H5P_DEFAULT, H5R_OBJECT, &((ref_list[0]).s_ref));
1860 if (did_ref < 0) {
1861 H5Aclose(attr_id);
1862 H5Tclose(atype_id);
1863 H5Dclose(dset_id);
1864 string msg = "Cannot de-reference the object for the variable " + dname;
1865 throw InternalErr(__FILE__, __LINE__, msg);
1866 }
1867
1868 hid_t did_space = H5Dget_space(did_ref);
1869 if (did_space < 0) {
1870 H5Aclose(attr_id);
1871 H5Tclose(atype_id);
1872 H5Dclose(dset_id);
1873 H5Dclose(did_ref);
1874 string msg = "Cannot open the space of the de-referenced object for the variable " + dname;
1875 throw InternalErr(__FILE__, __LINE__, msg);
1876 }
1877
1878 // Check if this is a simple
1879 if (H5Sget_simple_extent_type(did_space) != H5S_SIMPLE) {
1880 H5Aclose(attr_id);
1881 H5Tclose(atype_id);
1882 H5Sclose(did_space);
1883 H5Dclose(dset_id);
1884 H5Dclose(did_ref);
1885 string msg = "The dataspace must be a simple HDF5 dataspace for the variable " + dname;
1886 throw InternalErr(__FILE__, __LINE__, msg);
1887 }
1888
1889 int did_space_num_dims = H5Sget_simple_extent_ndims(did_space);
1890 if (did_space_num_dims <0) {
1891 H5Aclose(attr_id);
1892 H5Tclose(atype_id);
1893 H5Sclose(did_space);
1894 H5Dclose(dset_id);
1895 H5Dclose(did_ref);
1896 string msg = "The number of dimensions must be > 0 for the variable " + dname;
1897 throw InternalErr(__FILE__, __LINE__, msg);
1898 }
1899 vector<hsize_t> did_dims(did_space_num_dims);
1900 vector<hsize_t> did_max_dims(did_space_num_dims);
1901
1902 if (H5Sget_simple_extent_dims(did_space,did_dims.data(),did_max_dims.data()) <0) {
1903 H5Aclose(attr_id);
1904 H5Tclose(atype_id);
1905 H5Sclose(did_space);
1906 H5Dclose(dset_id);
1907 H5Dclose(did_ref);
1908 string msg = "Cannot obtain the dimension information for the variable " + dname;
1909 throw InternalErr(__FILE__, __LINE__, msg);
1910 }
1911
1912 hsize_t cur_unlimited_dim_size = 0;
1913
1914 int num_unlimited_dims = 0;
1915 for (unsigned i = 0; i<did_space_num_dims; i++) {
1916 if (did_max_dims[i] == H5S_UNLIMITED) {
1917 cur_unlimited_dim_size = did_dims[i];
1918 num_unlimited_dims++;
1919 }
1920 }
1921 if (num_unlimited_dims >1)
1922 throw InternalErr(__FILE__,__LINE__,"This variable has more than 1 unlimited dimensions. This is not supported.");
1923
1924 ret_value = cur_unlimited_dim_size;
1925
1926 H5Dclose(did_ref);
1927 H5Sclose(did_space);
1928 }
1929
1930 H5Sclose(aspace_id);
1931
1932 return ret_value;
1933}
1934// The main routine to handle the coverage support. The handled_all_cv_names should be detected before
1935// calling this routine. It includes all valid dimension scale variables. that is: the pure netCDF4-like
1936// dimensions are not included.
1937void add_dap4_coverage_default(D4Group* d4_root, const vector<string>& handled_all_cv_names) {
1938
1939 // We need to construct the var name to Array map,using unordered_map for quick search.
1940 // Dimension scale path to array maps(Grid and non-coordinate dimensions)
1941 unordered_map<string, Array*> dsname_array_maps;
1942 obtain_ds_name_array_maps(d4_root,dsname_array_maps, handled_all_cv_names);
1943
1944
1945 // Coordinate to array(Swath or other cases)
1946 unordered_map<string, Array*> coname_array_maps;
1947
1948 Constructor::Vars_iter vi = d4_root->var_begin();
1949 Constructor::Vars_iter ve = d4_root->var_end();
1950
1951 for (; vi != ve; vi++) {
1952
1953 const BaseType *v = *vi;
1954
1955 // Only Array can have maps.
1956 if (libdap::dods_array_c == v->type()) {
1957
1958 auto t_a = dynamic_cast<Array *>(*vi);
1959
1960 vector<string> coord_names;
1961 unordered_set<string> handled_dim_names;
1962 obtain_coord_names(t_a,coord_names);
1963
1964 // Having the coordinate attributes,
1965 if (coord_names.empty()==false) {
1966 make_coord_names_fpath(d4_root,coord_names);
1967 remove_empty_coord_names(coord_names);
1968 add_coord_maps(d4_root,t_a,coord_names,coname_array_maps,handled_dim_names);
1969 add_dimscale_maps(t_a,dsname_array_maps,handled_dim_names);
1970 }
1971 else // Just use the dimension scales.
1972 add_dimscale_maps(t_a,dsname_array_maps,handled_dim_names);
1973
1974 }
1975 }
1976
1977 // Go over the children groups.
1978 for (D4Group::groupsIter gi = d4_root->grp_begin(), ge = d4_root->grp_end(); gi != ge; ++gi)
1979 add_dap4_coverage_default_internal(*gi, dsname_array_maps,coname_array_maps);
1980
1981 // Now we need to remove the maps from the coordinate variables and the dimension scales.
1982 // First dimension scales.
1983 for (auto &ds_map:dsname_array_maps) {
1984
1985 D4Maps *d4_maps = (ds_map.second)->maps();
1986 size_t d4map_size = d4_maps->size();
1987 while (d4map_size != 0) {
1988 D4Map * d4_map = d4_maps->get_map(0);
1989 d4_maps->remove_map(d4_map);
1990 delete d4_map;
1991 d4map_size = d4_maps->size();
1992 }
1993 }
1994
1995 // Then coordinates
1996 for (auto &cv_map:coname_array_maps) {
1997
1998 D4Maps *d4_maps = (cv_map.second)->maps();
1999 size_t d4map_size = d4_maps->size();
2000 while (d4map_size != 0) {
2001 D4Map * d4_map = d4_maps->get_map(0);
2002 d4_maps->remove_map(d4_map);
2003 delete d4_map;
2004 d4map_size = d4_maps->size();
2005 }
2006
2007 }
2008
2009 // Reorder the variables so that the map variables are at the front.
2010 // We decide to use map instead of unordered_map since now the order of variables is important.
2011 map<string,Array*> ordered_dc_co_array_maps;
2012 map<string,Array*> ordered_coname_array_maps;
2013
2014 // Loop through dsname_array_maps, then search coname_array_maps, if this element is not in the coname_array_maps,
2015 // add this to dc_co_array_maps.
2016 for (const auto &dsname_array_map:dsname_array_maps) {
2017
2018 bool found_coname = false;
2019 for (const auto &coname_array_map:coname_array_maps) {
2020 if (coname_array_map.first == dsname_array_map.first) {
2021 found_coname = true;
2022 break;
2023 }
2024 }
2025
2026 if (found_coname == false)
2027 ordered_dc_co_array_maps.insert(dsname_array_map);
2028 }
2029
2030 for (const auto &coname_array_map:coname_array_maps)
2031 ordered_coname_array_maps.insert(coname_array_map);
2032
2033 reorder_vars(d4_root,ordered_coname_array_maps,ordered_dc_co_array_maps);
2034
2035}
2036
2037void add_dap4_coverage_default_internal(D4Group* d4_grp, unordered_map<string, Array*> &dsname_array_maps,
2038 unordered_map<string, Array*> &coname_array_maps) {
2039
2040
2041 Constructor::Vars_iter vi = d4_grp->var_begin();
2042 Constructor::Vars_iter ve = d4_grp->var_end();
2043
2044 for (; vi != ve; vi++) {
2045
2046 const BaseType *v = *vi;
2047
2048 // Only Arrays can have maps.
2049 if (libdap::dods_array_c == v->type()) {
2050
2051 auto t_a = dynamic_cast<Array *>(*vi);
2052
2053 vector<string> coord_names;
2054 unordered_set<string> handled_dim_names;
2055
2056 // Obtain the coordinate names if having the coordinates attribute.
2057 obtain_coord_names(t_a,coord_names);
2058
2059 if (coord_names.empty()==false) {
2060
2061 // Obtain FQN of all coordinates
2062 make_coord_names_fpath(d4_grp,coord_names);
2063 remove_empty_coord_names(coord_names);
2064
2065 // Add coordinates to the coordinate-array map. Also need to return handled dimension names.
2066 add_coord_maps(d4_grp,t_a,coord_names,coname_array_maps,handled_dim_names);
2067
2068 // For the dimensions not covered by the found coordinates attribute, check dimension scales.
2069 add_dimscale_maps(t_a,dsname_array_maps,handled_dim_names);
2070 }
2071 else
2072 add_dimscale_maps(t_a,dsname_array_maps,handled_dim_names);
2073
2074 }
2075
2076 }
2077
2078 // Go over children groups.
2079 for (D4Group::groupsIter gi = d4_grp->grp_begin(), ge = d4_grp->grp_end(); gi != ge; ++gi)
2080 add_dap4_coverage_default_internal(*gi, dsname_array_maps, coname_array_maps);
2081
2082}
2083
2084void obtain_coord_names(Array* ar, vector<string> & coord_names) {
2085
2086 D4Attributes *d4_attrs = ar->attributes();
2087 D4Attribute *d4_attr = d4_attrs->find("coordinates");
2088 if (d4_attr != nullptr && d4_attr->type() == attr_str_c) {
2089 if (d4_attr->num_values() == 1) {
2090 string tempstring = d4_attr->value(0);
2091 char sep=' ';
2092 HDF5CFUtil::Split_helper(coord_names,tempstring,sep);
2093 }
2094 // From our observations, the coordinates attribute is always just one string.
2095 // So this else block may never be executed.
2096 else
2097 obtain_multi_string_coord_names(d4_attr, coord_names);
2098 }
2099}
2100
2101void obtain_multi_string_coord_names(D4Attribute *d4_attr, vector<string> & coord_names) {
2102
2103 for (D4Attribute::D4AttributeIter av_i = d4_attr->value_begin(), av_e = d4_attr->value_end(); av_i != av_e; av_i++) {
2104 vector <string> tempstr_vec;
2105 char sep=' ';
2106 HDF5CFUtil::Split_helper(tempstr_vec,*av_i,sep);
2107 for (const auto &tve:tempstr_vec)
2108 coord_names.push_back(tve);
2109 }
2110}
2111// Generate absolute path(FQN) for all coordinate variables. Note the output is the coord_names.
2112void make_coord_names_fpath(D4Group* d4_grp, vector<string> &coord_names) {
2113
2114 for (auto &cname:coord_names) {
2115
2116 // No path inside the coordinate name.
2117 if (cname.find('/')==string::npos) {
2118 if (false == obtain_no_path_cv(d4_grp,cname))
2119 cname ="";
2120 }
2121 else if(cname[0] == '/') // the absolute path is specified
2122 handle_absolute_path_cv(d4_grp,cname);
2123 else // The relative path is specified.
2124 handle_relative_path_cv(d4_grp, cname);
2125 }
2126
2127}
2128
2129// This is for the case when the coordinates attribute is something like "lat lon", no path is provided.
2130// We then search the variable names at this group and the ancestor groups until we find them.
2131// Note this function only applies to one coordinate at each time.
2132bool obtain_no_path_cv(D4Group *d4_grp, string &coord_name) {
2133
2134 bool found_cv = false;
2135
2136 Constructor::Vars_iter vi = d4_grp->var_begin();
2137 Constructor::Vars_iter ve = d4_grp->var_end();
2138
2139 for (; vi != ve; vi++) {
2140
2141 const BaseType *v = *vi;
2142
2143 // Currently we only consider the cv that is an array.
2144 if (libdap::dods_array_c == v->type()) {
2145
2146 auto t_a = dynamic_cast<Array *>(*vi);
2147 if (coord_name == t_a->name()) {
2148 // Find the coordinate variable, But We need to return the absolute path of the variable.
2149 coord_name = t_a->FQN();
2150 found_cv = true;
2151 break;
2152 }
2153 }
2154 }
2155
2156 if (found_cv == false && (d4_grp->get_parent())){
2157 auto d4_grp_par = dynamic_cast<D4Group*>(d4_grp->get_parent());
2158 found_cv = obtain_no_path_cv(d4_grp_par,coord_name);
2159 }
2160 return found_cv;
2161}
2162
2163void handle_absolute_path_cv(const D4Group *d4_grp, string &coord_name) {
2164
2165 // For the time being, we don't check if this cv with absolute path exists.
2166 // However, we need to check if the coordinates are under the current group or the ancestor groups.
2167 string d4_grp_fqn = d4_grp->FQN();
2168 string cv_path = HDF5CFUtil::obtain_string_before_lastslash(coord_name);
2169 if (d4_grp_fqn.find(cv_path) != 0)
2170 coord_name="";
2171
2172}
2173
2174// Handle the coordinate attribute that includes relative paths such as coordinates={../../foo etc}
2175void handle_relative_path_cv(const D4Group *d4_grp, string &coord_name) {
2176
2177 // The only valid relative path is among the path of the ancestor groups according to CF.
2178 // So if the identified coordinate variable (cv) names are not under the ancestor groups, we
2179 // don't consider a valid coordinate and skip it. Also we assume the "../" is used
2180 // in the relative path as a way to go to the parent group.
2181
2182 bool find_coord = true;
2183 string sep = "../";
2184 unsigned short sep_count = 0;
2185 size_t pos = coord_name.find(sep, 0);
2186 if (pos != 0)
2187 find_coord = false;
2188 else {
2189 while(pos != string::npos)
2190 {
2191 sep_count++;
2192 size_t temp_pos = pos;
2193 pos = coord_name.find(sep,pos+1);
2194 // If we find something not like ../../../??,this is invalid.
2195 if ((pos !=string::npos) && (pos !=(temp_pos+3))) {
2196 find_coord = false;
2197 break;
2198 }
2199 }
2200 }
2201
2202 // Now we need to find the absolute path of the coordinate variable.
2203 if (find_coord)
2204 handle_relative_path_cvname_internal(d4_grp, coord_name, sep_count);
2205
2206}
2207
2208void handle_relative_path_cvname_internal(const D4Group *d4_grp, string &coord_name, unsigned short sep_count) {
2209
2210 // Obtain variable's name and FQN
2211 string grp_fqn = d4_grp->FQN();
2212
2213 size_t co_path_pos = 0;
2214
2215 size_t var_back_st_pos = grp_fqn.size()-1;
2216 if (var_back_st_pos >0)
2217 var_back_st_pos--;
2218
2219 // To find the coordinate variable path, we need to search backward and then reduce the number of "../".
2220 for (size_t i =var_back_st_pos; i >=0;i--) {
2221 if (grp_fqn[i] == '/') {
2222 sep_count--;
2223 if(sep_count == 0) {
2224 co_path_pos = i;
2225 break;
2226 }
2227 }
2228 }
2229
2230 if (sep_count > 0) { // Invalid relative paths.
2231 //We should not include this coordinate in the map.
2232 coord_name="";
2233 }
2234 else {//build up the coordinate variable full path
2235
2236 // The path includes the '/' at the end.
2237 string the_path = grp_fqn.substr(0,co_path_pos+1);
2238 string the_name = HDF5CFUtil::obtain_string_after_lastslash(coord_name);
2239 coord_name = the_path + the_name;
2240 }
2241}
2242
2243void remove_empty_coord_names(vector<string> & coord_names) {
2244
2245 for (auto it = coord_names.begin(); it !=coord_names.end();) {
2246 if (*it =="")
2247 it = coord_names.erase(it);
2248 else
2249 ++it;
2250 }
2251}
2252
2253// Obtain global dimension scale to array maps for this group for the coverage support.
2254// Note: the handled_all_cv_names are the coordinte variables based on dimension scales detected before adding DAP4 maps.
2255// This vector is const and should not be changed.
2256void obtain_ds_name_array_maps(D4Group *d4_grp,unordered_map<string,Array*>&dsn_array_maps,
2257 const vector<string>& handled_all_cv_names) {
2258
2259 // Loop through all the variables in this group.
2260 Constructor::Vars_iter vi = d4_grp->var_begin();
2261 Constructor::Vars_iter ve = d4_grp->var_end();
2262
2263 for (; vi != ve; vi++) {
2264 const BaseType *v = *vi;
2265
2266 // Only Array can have maps.
2267 if (libdap::dods_array_c == v->type())
2268 obtain_ds_name_array_maps_internal(*vi, dsn_array_maps, handled_all_cv_names);
2269 }
2270
2271 for (D4Group::groupsIter gi = d4_grp->grp_begin(), ge = d4_grp->grp_end(); gi != ge; ++gi) {
2272 BESDEBUG("h5", "In group: " << (*gi)->name() << endl);
2273 obtain_ds_name_array_maps(*gi, dsn_array_maps, handled_all_cv_names);
2274 }
2275
2276}
2277
2278void obtain_ds_name_array_maps_internal(BaseType *v, unordered_map<string,Array*>&dsn_array_maps,
2279 const vector<string>& handled_all_cv_names)
2280{
2281 auto t_a = dynamic_cast<Array *>(v);
2282
2283 // Dimension scales must be 1-dimension. So we save many unnecessary operations.
2284 // Find the dimension scale, insert to the global unordered_map.
2285 if (t_a->dimensions() == 1) {
2286 string t_a_fqn = t_a->FQN();
2287 if (find(handled_all_cv_names.begin(),handled_all_cv_names.end(),t_a_fqn) != handled_all_cv_names.end())
2288 dsn_array_maps.emplace(t_a_fqn,t_a);
2289 }
2290}
2291
2292
2293// Add the valid coordinate variables(via CF's coordinates attribute of this variable) to the var's DAP4 maps.
2294void add_coord_maps(D4Group *d4_grp, Array *var, vector<string> &coord_names,
2295 unordered_map<string,Array*> & coname_array_maps,
2296 unordered_set<string> & handled_dim_names) {
2297
2298 // Search if the coord name(in full path) can be found in the current coname_array_maps.
2299 // If the coord name is found, add it to the DAP4 map.
2300 for (auto cv_it =coord_names.begin(); cv_it != coord_names.end();) {
2301
2302 unordered_map<string, Array*>::const_iterator it_ma = coname_array_maps.find(*cv_it);
2303 if (it_ma != coname_array_maps.end()) {
2304
2305 auto d4_map_unique = make_unique<D4Map>(it_ma->first, it_ma->second);
2306 D4Map *d4_map = d4_map_unique.release();
2307
2308 var->maps()->add_map(d4_map);
2309
2310 // Obtain the dimension full paths. These dimensions are handled dimensions. The dimension scales of
2311 // these dimensions should NOT be included in the final coverage maps.
2312 obtain_handled_dim_names(it_ma->second,handled_dim_names);
2313
2314 // Also need to remove this coordinate name from the coord_names vector since this coordinate is handled.
2315 // Note the coord_names is only for this variable.
2316 cv_it = coord_names.erase(cv_it);
2317 }
2318 else
2319 ++cv_it;
2320 }
2321
2322 // Now we need to search the rest of this variable's coordinates.
2323 // Note the array of coname_array_maps is the map array. It is gradually built.
2324 for (auto cv_it =coord_names.begin(); cv_it != coord_names.end();) {
2325
2326 bool found_cv = false;
2327 Constructor::Vars_iter vi = d4_grp->var_begin();
2328 Constructor::Vars_iter ve = d4_grp->var_end();
2329
2330 for (; vi != ve; vi++) {
2331
2332 const BaseType *v = *vi;
2333
2334 // DAP4 requires the maps to be Arrays. So we only consider the cv that is an array.
2335 // Note the DSG(Discrete Sample Geometry) data may contain scalar coordinates. DAP4
2336 // cannot handle such a case now. So no maps will be generated for scalar coordiates.
2337 if (libdap::dods_array_c == v->type()) {
2338
2339 auto t_a = dynamic_cast<Array *>(*vi);
2340
2341 // Find it.
2342 if (*cv_it == t_a->FQN()) {
2343
2344 // Add the maps
2345 auto d4_map = new D4Map(t_a->FQN(), t_a);
2346 var->maps()->add_map(d4_map);
2347
2348 // Need to add this coordinate to the coname_array_maps
2349 coname_array_maps.emplace(t_a->FQN(),t_a);
2350
2351 // Obtain the dimension full paths. These dimensions are handled dimensions. The dimension scales of
2352 // these dimensions should NOT be included in the final coverage maps.
2353 obtain_handled_dim_names(t_a,handled_dim_names);
2354
2355 found_cv = true;
2356 break;
2357 }
2358 }
2359 }
2360
2361 // Done with this coordinate, erase it from the coordinate names vector, search the next.
2362 if (found_cv)
2363 cv_it = coord_names.erase(cv_it);
2364 else
2365 ++cv_it;
2366 }
2367
2368 // Need to check the parent group and call recursively for the coordinates not found in this group.
2369 if (coord_names.empty() == false && d4_grp->get_parent()) {
2370 auto d4_grp_par = dynamic_cast<D4Group*>(d4_grp->get_parent());
2371 add_coord_maps(d4_grp_par,var,coord_names,coname_array_maps,handled_dim_names);
2372 }
2373}
2374
2375
2376// Add the valid coordinate variables(via dimension scales) to the var's DAP4 maps.
2377// Loop through the handled dimension names(by the coordinate variables) set
2378// Then check this var's dimensions. Only add this var's unhandled coordinates(dimension scales) if these dimension scales exist.
2379void add_dimscale_maps(libdap::Array* var, std::unordered_map<std::string,libdap::Array*> & dc_array_maps,
2380 const std::unordered_set<std::string> & handled_dim_names) {
2381
2382 BESDEBUG("h5","Coming to add_dimscale_maps() "<<endl);
2383
2384 Array::Dim_iter di = var->dim_begin();
2385 Array::Dim_iter de = var->dim_end();
2386
2387 for (; di != de; di++) {
2388
2389 const D4Dimension * d4_dim = var->dimension_D4dim(di);
2390
2391 // DAP4 dimension may not exist, so need to check.
2392 if (d4_dim) {
2393
2394 // Fully Qualified name(absolute path) needs to be used as a key for search.
2395 string dim_fqn = d4_dim->fully_qualified_name();
2396
2397 // Find that this dimension is not handled, we need to check if there is a coordinate variable via dimension scale
2398 // for this dimension.
2399 if (handled_dim_names.find(dim_fqn) == handled_dim_names.end()) {
2400
2401 unordered_map<string, Array*>::const_iterator it_ma = dc_array_maps.find(dim_fqn);
2402
2403 // Find a valid coordinate variable for this dimension, insert this coordinate variable as a DAP4 map to this var.
2404 if (it_ma != dc_array_maps.end()) {
2405 auto d4_map = new D4Map(it_ma->first, it_ma->second);
2406 var->maps()->add_map(d4_map);
2407 }
2408 }
2409 }
2410 }
2411}
2412
2413
2414// Obtain handled dimension names of this variable. An unordered set is used for quick search.
2415void obtain_handled_dim_names(Array *var, unordered_set<string> & handled_dim_names) {
2416
2417 Array::Dim_iter di = var->dim_begin();
2418 Array::Dim_iter de = var->dim_end();
2419
2420 for (; di != de; di++) {
2421 const D4Dimension * d4_dim = var->dimension_D4dim(di);
2422 if (d4_dim)
2423 handled_dim_names.insert(d4_dim->fully_qualified_name());
2424 }
2425}
2426
2427void reorder_vars(D4Group *d4_grp, const map<string,Array*> &coname_array_maps,
2428 const map<string,Array*> & dc_array_maps) {
2429
2430 Constructor::Vars_iter vi = d4_grp->var_begin();
2431 Constructor::Vars_iter ve = d4_grp->var_end();
2432
2433 vector<int> cv_pos;
2434 vector<BaseType *> cv_obj_ptr;
2435
2436 int v_index = 0;
2437 for (; vi != ve; vi++) {
2438
2439 BaseType *v = *vi;
2440
2441 // We only need to re-order arrays.
2442 if (libdap::dods_array_c == v->type()) {
2443
2444 // We need to remember the coordinate variable positions and remember the corresponding arrays.
2445 for (const auto &coname_array_map:coname_array_maps) {
2446 if (coname_array_map.first == v->FQN()) {
2447 cv_pos.push_back(v_index);
2448 cv_obj_ptr.push_back(v);
2449 }
2450 }
2451 for (const auto &dc_array_map:dc_array_maps) {
2452 if (dc_array_map.first == v->FQN()) {
2453 cv_pos.push_back(v_index);
2454 cv_obj_ptr.push_back(v);
2455 }
2456 }
2457 }
2458 v_index++;
2459 }
2460
2461// Leave the following #if 0 block for debugging.
2462#if 0
2463for (const auto &cv_p:cv_pos)
2464cerr<< ": "<<cv_p <<endl;
2465
2466for (const auto &cv_obj_p:cv_obj_ptr)
2467cerr<< "name: "<<cv_obj_p->FQN() <<endl;
2468#endif
2469
2470 // Obtain the front variables. The number of front variables is set to be the same as the coordinate variables
2471 // and dimension scales.
2472 // We also need to remember the positions since it is possible that these front variables contain
2473 // the coordinate/dimension variables.
2474 // We will not move those coordinate/dimension variables in the front.
2475 vector<BaseType *>front_v_ptr;
2476 auto stop_index = (int)(cv_pos.size());
2477
2478 // If we do have coordinate/dimension variables,find those variables and re-order.
2479 // (We cannot assume that we always have these variables).
2480 if (stop_index > 0)
2481 reorder_vars_internal(d4_grp, cv_pos, cv_obj_ptr, stop_index);
2482
2483 // Now go the children groups. Because the coordinate variables is always on the ancestor groups or this group,
2484 // the re-ordering routine guarantees that a map variable is in front of all other variables that use this map.
2485 for (D4Group::groupsIter gi = d4_grp->grp_begin(), ge = d4_grp->grp_end(); gi != ge; ++gi)
2486 reorder_vars(*gi, coname_array_maps, dc_array_maps);
2487
2488}
2489
2490void reorder_vars_internal(D4Group* d4_grp, const vector<int> &cv_pos, const vector<BaseType *>& cv_obj_ptr, int stop_index) {
2491 Constructor::Vars_iter vi = d4_grp->var_begin();
2492 Constructor::Vars_iter ve = d4_grp->var_end();
2493 vi = d4_grp->var_begin();
2494 ve = d4_grp->var_end();
2495
2496 int v_index = 0;
2497 vector<BaseType *> front_v_ptr;
2498
2499 for (; vi != ve; vi++) {
2500 BaseType *v = *vi;
2501 front_v_ptr.push_back(v);
2502 v_index++;
2503 if (v_index == stop_index)
2504 break;
2505 }
2506
2507 // Check the overlaps of cvs with the front variables.
2508 // For example, if there are 3 coordinate variables, c1,c2,c3; the first 3 variables
2509 // are v1,v2,c1. In this case, c1 doesn't need to move. We only switch v1 with c2 and v2 with c3.
2510 // Usually there are only a few coordinate variables. So even the nested loops are not costly.
2511
2512 // First, obtain the overlapped cv and front v positions.
2513 vector<int> overlap_cv_pos;
2514 vector<int> overlap_front_pos;
2515 for (int i = 0; i < stop_index; i++) {
2516 for (int j = 0; j < stop_index; j++) {
2517 if (i == cv_pos[j]) {
2518 overlap_cv_pos.push_back(cv_pos[j]);
2519 overlap_front_pos.push_back(i);
2520 break;
2521 }
2522 }
2523 }
2524
2525 // No need to move a cv if this cv is in the front(overlapped with the first few vars) already.
2526 // Now we need to find the cv and front variables that need to move.
2527
2528 // The cvs that need to be moved.
2529 vector<int> mov_cv_pos;
2530 vector<BaseType *> mov_cv_ptr;
2531 for (int i = 0; i < stop_index; i++) {
2532 bool overlapped_cv = false;
2533 for (const auto &overlap_cv_p: overlap_cv_pos) {
2534 if (cv_pos[i] == overlap_cv_p) {
2535 overlapped_cv = true;
2536 break;
2537 }
2538 }
2539 if (overlapped_cv == false) {
2540 mov_cv_pos.push_back(cv_pos[i]);
2541 mov_cv_ptr.push_back(cv_obj_ptr[i]);
2542 }
2543 }
2544
2545 // The front non-cv variables that need to be moved.
2546 vector<int> mov_front_pos;
2547 vector<BaseType *> mov_front_v_ptr;
2548 for (int i = 0; i < stop_index; i++) {
2549 bool overlapped_front_cv = false;
2550 for (const auto &overlap_front_p: overlap_front_pos) {
2551 if (i == overlap_front_p) {
2552 overlapped_front_cv = true;
2553 break;
2554 }
2555 }
2556 if (overlapped_front_cv == false) {
2557 mov_front_pos.push_back(i);
2558 mov_front_v_ptr.push_back(front_v_ptr[i]);
2559 }
2560 }
2561
2562 reorder_vars_internal_final_phase(d4_grp, mov_cv_pos, mov_front_pos, mov_front_v_ptr, mov_cv_ptr);
2563
2564}
2565
2566void reorder_vars_internal_final_phase(D4Group* d4_grp, const vector<int> &mov_cv_pos,
2567 const vector<int> &mov_front_pos, const vector<BaseType *> &mov_front_v_ptr,
2568 const vector<BaseType *> &mov_cv_ptr) {
2569
2570 // sanity check
2571 if (mov_cv_pos.size() != mov_front_pos.size()) {
2572 string err_msg = "The number of moved coordinate variables is not the same as ";
2573 err_msg += "the number of moved non-coordinate variables";
2574 throw InternalErr(__FILE__, __LINE__, err_msg);
2575 }
2576
2577// Leave the following #if 0 for the time being. This is for debugging purpose. KY 2023-04-13
2578#if 0
2579 for (int i = 0; i <mov_cv_pos.size();i++) {
2580 cerr<<"mov_front_pos: "<<mov_front_pos[i] <<endl;
2581 cerr<<"mov_cv_pos: "<<mov_cv_pos[i] <<endl;
2582 }
2583#endif
2584
2585 // Move the map variables to the front, move the front non-coordinate variables to the original map variable location.
2586 for (unsigned int i = 0; i < mov_front_pos.size(); i++) {
2587 d4_grp->set_var_index(mov_cv_ptr[i], mov_front_pos[i]);
2588 d4_grp->set_var_index(mov_front_v_ptr[i], mov_cv_pos[i]);
2589 }
2590
2591}
2592
2593#if 0
2594bool is_cvar(const BaseType *v, const unordered_map<string,Array*> &coname_array_maps, const unordered_map<string,Array*> & dc_array_maps) {
2595
2596 bool ret_value = false;
2597 unordered_map<string, Array*>::const_iterator it_ma = coname_array_maps.find(v->FQN());
2598 if (it_ma != coname_array_maps.end())
2599 ret_value = true;
2600 else {
2601 it_ma = dc_array_maps.find(v->FQN());
2602 if (it_ma != dc_array_maps.end())
2603 ret_value = true;
2604 }
2605 return ret_value;
2606}
2607#endif
2608
2609void add_possible_eos5_grid_vars(D4Group* d4_grp, eos5_dim_info_t &eos5_dim_info) {
2610
2611 BESDEBUG("h5","coming to add_possible_eos5_grid_vars"<<endl);
2612
2613 eos5_grid_info_t eg_info;
2614
2615#if 0
2616for (const auto & ed_info:eos5_dim_info.gridname_to_info) {
2617 cerr<<"grid name: "<<ed_info.first <<endl;
2618 cerr<<" projection: "<<ed_info.second.projection <<endl;
2619 cerr<<" "<<"xdim fqn:" << ed_info.second.xdim_fqn <<endl;
2620 cerr<<" "<<"ydim fqn:" << ed_info.second.ydim_fqn <<endl;
2621 cerr<<" "<<"xdim size:" << ed_info.second.xdim_size <<endl;
2622 cerr<<" "<<"ydim size:" << ed_info.second.ydim_size <<endl;
2623 cerr<<" "<<"xdim point_lower:" << ed_info.second.point_lower <<endl;
2624 cerr<<" "<<"xdim point_upper:" << ed_info.second.point_upper <<endl;
2625 cerr<<" "<<"xdim point_left:" << ed_info.second.point_lower <<endl;
2626 cerr<<" "<<"xdim point_right:" << ed_info.second.point_right <<endl;
2627
2628}
2629
2630for (const auto & d_v_info:eos5_dim_info.dimpath_to_cvpath) {
2631 cerr<<" dimension name 1" <<d_v_info.first.dpath0 <<endl;
2632 cerr<<" dimension name 2" <<d_v_info.first.dpath1 <<endl;
2633 cerr<<" cv name 1" <<d_v_info.second.vpath0 <<endl;
2634 cerr<<" cv name 2" <<d_v_info.second.vpath1 <<endl;
2635 cerr<<" cv name 3" <<d_v_info.second.cf_gmap_path <<endl;
2636
2637
2638}
2639#endif
2640
2641 bool add_grid_var = is_eos5_grid_grp(d4_grp,eos5_dim_info,eg_info);
2642
2643 if (add_grid_var && eg_info.projection == HE5_GCTP_GEO)
2644 add_eos5_grid_vars_geo(d4_grp, eg_info);
2645 else if (add_grid_var && (eg_info.projection == HE5_GCTP_SNSOID ||
2646 eg_info.projection == HE5_GCTP_PS ||
2647 eg_info.projection == HE5_GCTP_LAMAZ))
2648 add_eos5_grid_vars_non_geo(d4_grp, eos5_dim_info, eg_info);
2649
2650}
2651
2652void add_eos5_grid_vars_geo(D4Group* d4_grp, const eos5_grid_info_t & eg_info) {
2653
2654 BaseType *ar_bt_lat = nullptr;
2655 BaseType *ar_bt_lon = nullptr;
2656 HDF5MissLLArray *ar_lat = nullptr;
2657 HDF5MissLLArray *ar_lon = nullptr;
2658
2659 try {
2660
2661 auto ar_bt_lat_unique = make_unique<Float32>("YDim");
2662 ar_bt_lat = ar_bt_lat_unique.get();
2663 auto ar_lat_unique = make_unique<HDF5MissLLArray>(true,
2664 1,
2665 eg_info,
2666 "YDim",
2667 ar_bt_lat);
2668 ar_lat = ar_lat_unique.release();
2669
2670 string ydimpath = d4_grp->FQN() + "YDim";
2671 ar_lat->append_dim_ll(eg_info.ydim_size, ydimpath);
2672
2673 auto d4_dim0_unique = make_unique<D4Dimension>("YDim", eg_info.ydim_size);
2674 auto d4_dim0 = d4_dim0_unique.release();
2675 (ar_lat->dim_begin())->dim = d4_dim0;
2676
2677 // The DAP4 group needs also to store these dimensions.
2678 D4Dimensions *dims = d4_grp->dims();
2679 dims->add_dim_nocopy(d4_dim0);
2680
2681 auto ar_bt_lon_unique = make_unique<Float32>("XDim");
2682
2683 ar_bt_lon = ar_bt_lon_unique.get();
2684 auto ar_lon_unique = make_unique<HDF5MissLLArray>(
2685 false,
2686 1,
2687 eg_info,
2688 "XDim",
2689 ar_bt_lon);
2690 ar_lon = ar_lon_unique.release();
2691
2692 string xdimpath = d4_grp->FQN() + "XDim";
2693 ar_lon->append_dim_ll(eg_info.xdim_size, xdimpath);
2694
2695 auto d4_dim1_unique = make_unique<D4Dimension>("XDim", eg_info.xdim_size);
2696 auto d4_dim1 = d4_dim1_unique.release();
2697
2698 (ar_lon->dim_begin())->dim = d4_dim1;
2699
2700 // The DAP4 group needs also to store these dimensions.
2701 dims = d4_grp->dims();
2702 dims->add_dim_nocopy(d4_dim1);
2703
2704 // Set this variable to DAP4 is critical for DAP4 dimensions and attributes handling.
2705 ar_lat->set_is_dap4(true);
2706 ar_lon->set_is_dap4(true);
2707
2708 // Add the CF units attribute to ar_lat and ar_lon.
2709 add_var_dap4_attr(ar_lat, "units", attr_str_c, "degrees_north");
2710 add_var_dap4_attr(ar_lon, "units", attr_str_c, "degrees_east");
2711 d4_grp->add_var_nocopy(ar_lat);
2712 d4_grp->add_var_nocopy(ar_lon);
2713 }
2714 catch (...) {
2715 delete ar_lat;
2716 delete ar_lon;
2717 throw InternalErr(__FILE__, __LINE__, "Unable to allocate the HDFMissLLArray instance.");
2718 }
2719}
2720
2721void add_eos5_grid_vars_non_geo(D4Group* d4_grp, eos5_dim_info_t &eos5_dim_info, const eos5_grid_info_t & eg_info) {
2722
2723 HDF5CFProj *dummy_proj_cf = nullptr;
2724 BaseType *ar_bt_dim0 = nullptr;
2725 BaseType *ar_bt_dim1 = nullptr;
2726 HDF5CFProj1D *ar_dim0 = nullptr;
2727 HDF5CFProj1D *ar_dim1 = nullptr;
2728
2729 BaseType *ar_bt_lat = nullptr;
2730 BaseType *ar_bt_lon = nullptr;
2731 HDF5MissLLArray *ar_lat = nullptr;
2732 HDF5MissLLArray *ar_lon = nullptr;
2733
2734 try {
2735 string dummy_proj_cf_name = "eos5_cf_projection";
2736 auto dummy_proj_cf_unique = make_unique<HDF5CFProj>(dummy_proj_cf_name, dummy_proj_cf_name);
2737 dummy_proj_cf = dummy_proj_cf_unique.release();
2738 dummy_proj_cf->set_is_dap4(true);
2739
2740 if (eg_info.projection == HE5_GCTP_SNSOID) {
2741
2742 add_var_dap4_attr(dummy_proj_cf, "grid_mapping_name", attr_str_c, "sinusoidal");
2743 add_var_dap4_attr(dummy_proj_cf, "longitude_of_central_meridian", attr_float64_c, "0.0");
2744 add_var_dap4_attr(dummy_proj_cf, "earth_radius", attr_float64_c, "6371007.181");
2745 add_var_dap4_attr(dummy_proj_cf, "_CoordinateAxisTypes", attr_str_c, "GeoX GeoY");
2746 } else if (eg_info.projection == HE5_GCTP_PS)
2747 add_ps_cf_grid_mapping_attrs(dummy_proj_cf, eg_info);
2748 else if (eg_info.projection == HE5_GCTP_LAMAZ)
2749 add_lamaz_cf_grid_mapping_attrs(dummy_proj_cf, eg_info);
2750
2751 d4_grp->add_var_nocopy(dummy_proj_cf);
2752
2753 auto ar_bt_dim1_unique = make_unique<Float64>("XDim");
2754 ar_bt_dim1 = ar_bt_dim1_unique.get();
2755
2756 auto ar_dim1_unique = make_unique<HDF5CFProj1D>(eg_info.point_left, eg_info.point_right,
2757 eg_info.xdim_size, "XDim", ar_bt_dim1);
2758 ar_dim1 = ar_dim1_unique.release();
2759
2760 // Handle dimensions
2761 string xdimpath = d4_grp->FQN() + "XDim";
2762 ar_dim1->append_dim_ll(eg_info.xdim_size, xdimpath);
2763
2764 // Need to add DAP4 dimensions
2765 auto d4_dim1_unique = make_unique<D4Dimension>("XDim", eg_info.xdim_size);
2766 auto d4_dim1 = d4_dim1_unique.release();
2767
2768 (ar_dim1->dim_begin())->dim = d4_dim1;
2769
2770 // The DAP4 group needs also to store these dimensions.
2771 D4Dimensions *dims = d4_grp->dims();
2772 dims->add_dim_nocopy(d4_dim1);
2773
2774 auto ar_bt_dim0_unique = make_unique<Float64>("YDim");
2775 ar_bt_dim0 = ar_bt_dim0_unique.get();
2776
2777 auto ar_dim0_unique = make_unique<HDF5CFProj1D>
2778 (eg_info.point_upper, eg_info.point_lower, eg_info.ydim_size, "XDim", ar_bt_dim0);
2779 ar_dim0 = ar_dim0_unique.release();
2780
2781 string ydimpath = d4_grp->FQN() + "YDim";
2782 ar_dim0->append_dim_ll(eg_info.ydim_size, ydimpath);
2783
2784 // Need to add DAP4 dimensions
2785 auto d4_dim0_unique = make_unique<D4Dimension>("YDim", eg_info.ydim_size);
2786 auto d4_dim0 = d4_dim0_unique.release();
2787
2788 (ar_dim0->dim_begin())->dim = d4_dim0;
2789
2790 // The DAP4 group needs also to store these dimensions.
2791 dims = d4_grp->dims();
2792 dims->add_dim_nocopy(d4_dim0);
2793
2794 ar_dim1->set_is_dap4(true);
2795 ar_dim0->set_is_dap4(true);
2796
2797 add_gm_spcvs_attrs(ar_dim0, true);
2798 add_gm_spcvs_attrs(ar_dim1, false);
2799
2800 d4_grp->add_var_nocopy(ar_dim1);
2801 d4_grp->add_var_nocopy(ar_dim0);
2802
2803 auto ar_bt_lat_unique = make_unique<Float64>("Latitude");
2804 ar_bt_lat = ar_bt_lat_unique.get();
2805
2806 auto ar_lat_unique = make_unique<HDF5MissLLArray>(true, 2, eg_info, "Latitude", ar_bt_lat);
2807 ar_lat = ar_lat_unique.release();
2808 ar_lat->append_dim_ll(eg_info.ydim_size, ydimpath);
2809 ar_lat->append_dim_ll(eg_info.xdim_size, xdimpath);
2810
2811 // Need to add DAP4 dimensions for this 2-D var.
2812 Array::Dim_iter d = ar_lat->dim_begin();
2813 d->dim = d4_dim0;
2814 d++;
2815 d->dim = d4_dim1;
2816 add_var_dap4_attr(ar_lat, "units", attr_str_c, "degrees_north");
2817
2818 auto ar_bt_lon_unique = make_unique<Float64>("Longitude");
2819 ar_bt_lon = ar_bt_lon_unique.get();
2820
2821 auto ar_lon_unique = make_unique<HDF5MissLLArray>(false,
2822 2,
2823 eg_info,
2824 "Longitude",
2825 ar_bt_lon);
2826 ar_lon = ar_lon_unique.release();
2827 ar_lon->append_dim_ll(eg_info.ydim_size, ydimpath);
2828 ar_lon->append_dim_ll(eg_info.xdim_size, xdimpath);
2829
2830 add_var_dap4_attr(ar_lon, "units", attr_str_c, "degrees_east");
2831
2832 d = ar_lon->dim_begin();
2833 d->dim = d4_dim0;
2834 d++;
2835 d->dim = d4_dim1;
2836
2837 ar_lat->set_is_dap4(true);
2838 ar_lon->set_is_dap4(true);
2839
2840 d4_grp->add_var_nocopy(ar_lat);
2841 d4_grp->add_var_nocopy(ar_lon);
2842
2843 // Now we need to add eos5 grid mapping, dimension and coordinate info to eos5_dim_info.
2844 eos5_dname_info_t edname_info;
2845 eos5_cname_info_t ecname_info;
2846 edname_info.dpath0 = ydimpath;
2847 edname_info.dpath1 = xdimpath;
2848 ecname_info.vpath0 = d4_grp->FQN() + "Latitude";
2849 ecname_info.vpath1 = d4_grp->FQN() + "Longitude";
2850 ecname_info.cf_gmap_path = d4_grp->FQN() + dummy_proj_cf_name;
2851
2852 pair<eos5_dname_info_t, eos5_cname_info_t> t_pair;
2853 t_pair = make_pair(edname_info, ecname_info);
2854 eos5_dim_info.dimpath_to_cvpath.push_back(t_pair);
2855
2856#if 0
2857 for (const auto & d_v_info:eos5_dim_info.dimpath_to_cvpath) {
2858 cerr<<" dimension name 1: " <<d_v_info.first.dpath0 <<endl;
2859 cerr<<" dimension name 2: " <<d_v_info.first.dpath1 <<endl;
2860 cerr<<" cv name 1: " <<d_v_info.second.vpath0 <<endl;
2861 cerr<<" cv name 2: " <<d_v_info.second.vpath1 <<endl;
2862 cerr<<" dummy cf projection var name: " <<d_v_info.second.cf_gmap_path <<endl;
2863 }
2864#endif
2865
2866 }
2867 catch (...) {
2868 delete dummy_proj_cf;
2869 delete ar_dim0;
2870 delete ar_dim1;
2871 delete ar_lat;
2872 delete ar_lon;
2873 throw InternalErr(__FILE__, __LINE__, "Unable to allocate the HDFMissLLArray instance.");
2874 }
2875}
2876
2877bool is_eos5_grid_grp(D4Group *d4_group,const eos5_dim_info_t &eos5_dim_info, eos5_grid_info_t &eg_info) {
2878
2879 bool ret_value = false;
2880 string grp_fqn = d4_group->FQN();
2881
2882 for (const auto & ed_info:eos5_dim_info.gridname_to_info) {
2883 string eos_mod_path = handle_string_special_characters_in_path(ed_info.first);
2884 if (grp_fqn == (eos_mod_path + "/")) {
2885 eg_info = ed_info.second;
2886 ret_value = true;
2887 break;
2888 }
2889 }
2890
2891 if (ret_value == true)
2892 ret_value = no_eos5_grid_vars_in_grp(d4_group, eg_info);
2893
2894 return ret_value;
2895}
2896
2897bool no_eos5_grid_vars_in_grp(D4Group *d4_group, const eos5_grid_info_t &eg_info) {
2898
2899 bool ret_value = true;
2900
2901 // Even if we find the correct eos group, we need to ensure that the variables we want to add
2902 // do not exist. That is: we need to check the variable names like Latitude/Longitude etc. don't exist in
2903 // this group. This seems unnecessary, but we do observe that data producers may add CF variables by themselves.
2904 // The handler needs to ensure that it will keep using the variables added by the data producers first.
2905
2906 Constructor::Vars_iter vi = d4_group->var_begin();
2907 Constructor::Vars_iter ve = d4_group->var_end();
2908
2909 for (; vi != ve; vi++) {
2910
2911 const BaseType *v = *vi;
2912 string vname = v->name();
2913 if (eg_info.projection == HE5_GCTP_GEO) {
2914 if (vname == "YDim" || vname == "XDim") {
2915 ret_value = false;
2916 break;
2917 }
2918
2919 }
2920 else {
2921 if (vname == "YDim" || vname == "XDim" || vname =="Latitude" || vname == "Longitude"
2922 || vname == "eos5_cf_projection") {
2923 ret_value = false;
2924 break;
2925 }
2926 }
2927 }
2928 return ret_value;
2929}
2930
2931void build_gd_info(const HE5Grid &gd,unordered_map<string,eos5_grid_info_t>& gridname_to_info) {
2932
2933 string grid_name = "/HDFEOS/GRIDS/"+gd.name;
2934 eos5_grid_info_t eg_info;
2935 eg_info.xdim_fqn = grid_name+"/XDim";
2936 eg_info.ydim_fqn = grid_name+"/YDim";
2937
2938 bool find_xdim = false;
2939 bool find_ydim = false;
2940
2941 // I have to use the grid dimension name and size information since the struct metadata
2942 // doesn't contain the dimension size information of a variable. It has to be deduced from
2943 // the grid dimension information. Note: application can choose their own dimension names in
2944 // the grid. This is a flaw in the HDF-EOS5 library since the variable's dimension names
2945 // are always XDim or YDim. So far I only see one variation in the NASA products. Use Xdim rather than Ydim.
2946 // An error will be generated if neither XDim/Xdim nor YDim/Ydim is found.
2947
2948 for (const auto &dim:gd.dim_list) {
2949
2950 if ((dim.name == "XDim" || dim.name == "Xdim") && find_xdim == false) {
2951 eg_info.xdim_size = dim.size;
2952 find_xdim = true;
2953 }
2954 else if ((dim.name == "YDim" || dim.name == "Ydim") && find_ydim == false) {
2955 eg_info.ydim_size = dim.size;
2956 find_ydim = true;
2957 }
2958
2959 if (find_xdim == true && find_ydim == true)
2960 break;
2961
2962 }
2963
2964 if (find_xdim == true && find_ydim == true) {
2965
2966 eg_info.point_lower = gd.point_lower;
2967 eg_info.point_upper = gd.point_upper;
2968 eg_info.point_left = gd.point_left;
2969 eg_info.point_right = gd.point_right;
2970 eg_info.pixelregistration = gd.pixelregistration;
2971 eg_info.gridorigin = gd.gridorigin;
2972 eg_info.projection = gd.projection;
2973
2974 for (int i = 0; i <13;i++)
2975 eg_info.param[i] = gd.param[i];
2976
2977 eg_info.zone = gd.zone;
2978 eg_info.sphere = gd.sphere;
2979 gridname_to_info[grid_name] = eg_info;
2980
2981 }
2982
2983 else {
2984
2985 string dimname_list;
2986 for (const auto &dim:gd.dim_list) {
2987 dimname_list += dim.name;
2988 dimname_list += " ";
2989 }
2990 string err_msg = "This HDF-EOS5 grid dimension list doesn't contain XDim, Xdim, YDim or Ydim.";
2991 err_msg += " The dimension names of this grid are: "+dimname_list;
2992 throw InternalErr(__FILE__,__LINE__,err_msg);
2993
2994 }
2995
2996}
2997
2998void add_ps_cf_grid_mapping_attrs(libdap::BaseType *var, const eos5_grid_info_t & eg_info) {
2999
3000 // The following information is added according to the HDF-EOS5 user's guide and
3001 // CF 1.7 grid_mapping requirement.
3002
3003 // Longitude down below pole of map
3004 double vert_lon_pole = HE5_EHconvAng(eg_info.param[4],HE5_HDFE_DMS_DEG);
3005
3006 // Latitude of true scale
3007 double lat_true_scale = HE5_EHconvAng(eg_info.param[5],HE5_HDFE_DMS_DEG);
3008
3009 // False easting
3010 double fe = eg_info.param[6];
3011
3012 // False northing
3013 double fn = eg_info.param[7];
3014
3015 add_var_dap4_attr(var,"grid_mapping_name",attr_str_c,"polar_stereographic");
3016
3017 ostringstream s_vert_lon_pole;
3018 s_vert_lon_pole << vert_lon_pole;
3019
3020 // I did this map is based on my best understanding. KY
3021 // CF: straight_vertical_longitude_from_pole
3022 add_var_dap4_attr(var,"straight_vertical_longitude_from_pole", attr_float64_c, s_vert_lon_pole.str());
3023
3024 ostringstream s_lat_true_scale;
3025 s_lat_true_scale << lat_true_scale;
3026 add_var_dap4_attr(var,"standard_parallel", attr_float64_c, s_lat_true_scale.str());
3027
3028 if(fe == 0.0)
3029 add_var_dap4_attr(var,"false_easting",attr_float64_c,"0.0");
3030 else {
3031 ostringstream s_fe;
3032 s_fe << fe;
3033 add_var_dap4_attr(var,"false_easting",attr_float64_c,s_fe.str());
3034 }
3035
3036 if(fn == 0.0)
3037 add_var_dap4_attr(var,"false_northing",attr_float64_c,"0.0");
3038 else {
3039 ostringstream s_fn;
3040 s_fn << fn;
3041 add_var_dap4_attr(var,"false_northing",attr_float64_c,s_fn.str());
3042 }
3043
3044 if(lat_true_scale >0)
3045 add_var_dap4_attr(var,"latitude_of_projection_origin",attr_float64_c,"+90.0");
3046 else
3047 add_var_dap4_attr(var, "latitude_of_projection_origin",attr_float64_c,"-90.0");
3048
3049 add_var_dap4_attr(var, "_CoordinateAxisTypes", attr_str_c, "GeoX GeoY");
3050
3051 // From CF, PS has another parameter,
3052 // Either standard_parallel (EPSG 9829) or scale_factor_at_projection_origin (EPSG 9810)
3053 // I cannot find the corresponding parameter from the EOS5.
3054
3055}
3056
3057void add_lamaz_cf_grid_mapping_attrs(libdap::BaseType *var, const eos5_grid_info_t & eg_info) {
3058
3059 double lon_proj_origin = HE5_EHconvAng(eg_info.param[4],HE5_HDFE_DMS_DEG);
3060 double lat_proj_origin = HE5_EHconvAng(eg_info.param[5],HE5_HDFE_DMS_DEG);
3061 double fe = eg_info.param[6];
3062 double fn = eg_info.param[7];
3063
3064 add_var_dap4_attr(var,"grid_mapping_name", attr_str_c, "lambert_azimuthal_equal_area");
3065
3066 ostringstream s_lon_proj_origin;
3067 s_lon_proj_origin << lon_proj_origin;
3068 add_var_dap4_attr(var,"longitude_of_projection_origin", attr_float64_c, s_lon_proj_origin.str());
3069
3070 ostringstream s_lat_proj_origin;
3071 s_lat_proj_origin << lat_proj_origin;
3072
3073 add_var_dap4_attr(var,"latitude_of_projection_origin", attr_float64_c, s_lat_proj_origin.str());
3074
3075 if(fe == 0.0)
3076 add_var_dap4_attr(var,"false_easting",attr_float64_c,"0.0");
3077 else {
3078 ostringstream s_fe;
3079 s_fe << fe;
3080 add_var_dap4_attr(var,"false_easting",attr_float64_c,s_fe.str());
3081 }
3082
3083 if(fn == 0.0)
3084 add_var_dap4_attr(var,"false_northing",attr_float64_c,"0.0");
3085 else {
3086 ostringstream s_fn;
3087 s_fn << fn;
3088 add_var_dap4_attr(var,"false_northing",attr_float64_c,s_fn.str());
3089 }
3090
3091 add_var_dap4_attr(var,"_CoordinateAxisTypes", attr_str_c, "GeoX GeoY");
3092}
3093
3094// coordinates and grid_mapping attributes may be added to this HDF-EOS5 var
3095void add_possible_var_cv_info(libdap::BaseType *var, const eos5_dim_info_t &eos5_dim_info) {
3096
3097 bool have_cv_dim0 = false;
3098 bool have_cv_dim1 = false;
3099 string dim0_cv_name1;
3100 string dim0_cv_name2;
3101 string dim0_gm_name;
3102 string dim1_cv_name1;
3103 string dim1_cv_name2;
3104 string dim1_gm_name;
3105
3106 auto t_a = dynamic_cast<Array*>(var);
3107
3108 Array::Dim_iter di = t_a->dim_begin();
3109 Array::Dim_iter de = t_a->dim_end();
3110
3111 for (; di != de; di++) {
3112
3113 const D4Dimension * d4_dim = t_a->dimension_D4dim(di);
3114
3115 // DAP4 dimension may not exist, so need to check.
3116 if(d4_dim) {
3117
3118 // Fully Qualified name(absolute path) of the dimension
3119 // Both "XDim" and "YDim" in the EOS grid should appear in this var.
3120 string dim_fqn = d4_dim->fully_qualified_name();
3121
3122 for (const auto &dim_to_cv:eos5_dim_info.dimpath_to_cvpath) {
3123 if (dim_fqn == dim_to_cv.first.dpath0) {
3124
3125 dim0_cv_name1 = dim_to_cv.second.vpath0;
3126 dim0_cv_name2 = dim_to_cv.second.vpath1;
3127 dim0_gm_name = dim_to_cv.second.cf_gmap_path;
3128
3129 have_cv_dim0 = true;
3130 }
3131 else if (dim_fqn == dim_to_cv.first.dpath1) {
3132
3133 dim1_cv_name1 = dim_to_cv.second.vpath0;
3134 dim1_cv_name2 = dim_to_cv.second.vpath1;
3135 dim1_gm_name = dim_to_cv.second.cf_gmap_path;
3136
3137 have_cv_dim1 = true;
3138 }
3139
3140 if (have_cv_dim0 && have_cv_dim1)
3141 break;
3142 }
3143 }
3144
3145 if (have_cv_dim0 && have_cv_dim1)
3146 break;
3147 }
3148
3149 // We know the dimension names in each grid are different.
3150 // We can have only one set of match in each grid.
3151 if (have_cv_dim0 && have_cv_dim1) {
3152 if (dim0_cv_name1 != dim1_cv_name1 || dim0_cv_name2 !=dim1_cv_name2 || dim0_gm_name !=dim1_gm_name)
3153 throw InternalErr(__FILE__,__LINE__,"Inconsistent coordinates for this EOS5 Grid");
3154 else {// Add the CV attributes.
3155 string coord_value = dim0_cv_name1 + " "+dim0_cv_name2;
3156 add_var_dap4_attr(var,"coordinates",attr_str_c,coord_value);
3157 add_var_dap4_attr(var,"grid_mapping",attr_str_c,dim0_gm_name);
3158 }
3159 }
3160
3161}
3162
3163void make_attributes_to_cf(BaseType *var, const eos5_dim_info_t &eos5_dim_info) {
3164
3165 bool check_attr = false;
3166 for (const auto & ed_info:eos5_dim_info.gridname_to_info) {
3167 if (ed_info.second.projection == HE5_GCTP_GEO) {
3168 check_attr = true;
3169 break;
3170 }
3171 }
3172
3173 if (check_attr == true) {
3174
3175 D4Attributes *d4_attrs = var->attributes();
3176 bool have_scale_factor = false;
3177 bool have_add_offset = false;
3178 for (auto ii = d4_attrs->attribute_begin(), ee = d4_attrs->attribute_end(); ii != ee; ++ii) {
3179 if ((*ii)->name() == "ScaleFactor") {
3180 (*ii)->set_name("scale_factor");
3181 have_scale_factor = true;
3182 }
3183 else if ((*ii)->name() == "Offset") {
3184 (*ii)->set_name("add_offset");
3185 have_add_offset = true;
3186 }
3187 if (have_scale_factor && have_add_offset)
3188 break;
3189 }
3190 }
3191}
3192
3193void handle_vlen_int_float(D4Group *d4_grp, hid_t pid, const string &vname, const string &var_path,
3194 const string &filename, hid_t dset_id) {
3195
3196 hid_t vlen_type = H5Dget_type(dset_id);
3197 hid_t vlen_basetype = H5Tget_super(vlen_type);
3198 if (H5Tget_class(vlen_basetype) != H5T_INTEGER && H5Tget_class(vlen_basetype) != H5T_FLOAT)
3199 throw InternalErr(__FILE__, __LINE__,"Only support float or intger variable-length datatype.");
3200
3201 hid_t vlen_base_memtype = H5Tget_native_type(vlen_basetype, H5T_DIR_ASCEND);
3202 hid_t vlen_memtype = H5Tvlen_create(vlen_base_memtype);
3203
3204 // Will not support the scalar type.
3205 hid_t vlen_space = H5Dget_space(dset_id);
3206 if (H5Sget_simple_extent_type(vlen_space) != H5S_SIMPLE)
3207 throw InternalErr(__FILE__, __LINE__,"Only support array of float or intger variable-length datatype.");
3208
3209 hssize_t vlen_number_elements = H5Sget_simple_extent_npoints(vlen_space);
3210 vector<hvl_t> vlen_data(vlen_number_elements);
3211 if (H5Dread(dset_id, vlen_memtype, H5S_ALL, H5S_ALL, H5P_DEFAULT, vlen_data.data()) <0) {
3212 H5Dclose(dset_id);
3213 throw InternalErr(__FILE__, __LINE__,"Cannot read variable-length datatype data.");
3214 }
3215
3216 size_t max_vlen_length = 0;
3217 for (ssize_t i = 0; i<vlen_number_elements; i++) {
3218 if (max_vlen_length<vlen_data[i].len)
3219 max_vlen_length = vlen_data[i].len;
3220 }
3221
3222 H5Dvlen_reclaim(vlen_memtype, vlen_space, H5P_DEFAULT, (void*)(vlen_data.data()));
3223 H5Sclose(vlen_space);
3224
3225 // Now we need to create two variables: one to store vlen data, another to store vlen index.
3226 // First: vlen data.
3227 BaseType *bt = Get_bt_enhanced(d4_grp,pid, vname,var_path, filename, vlen_basetype);
3228 if (!bt)
3229 throw InternalErr(__FILE__, __LINE__,"Unable to convert hdf5 datatype to dods basetype");
3230
3231 H5Tclose(vlen_base_memtype);
3232 H5Tclose(vlen_basetype);
3233 H5Tclose(vlen_type);
3234 H5Tclose(vlen_memtype);
3235
3236 auto ar_unique = make_unique<HDF5Array>(vname, filename, bt);
3237 HDF5Array *ar = ar_unique.get();
3238
3239 // set number of elements and variable name values.
3240 // This essentially stores in the struct.
3241 ar->set_varpath(var_path);
3242 auto dimnames_size = (int)(dt_inst.dimnames.size());
3243 vector<string> dimnames;
3244 if (dimnames_size == dt_inst.ndims) {
3245 for (const auto &dimname:dt_inst.dimnames)
3246 dimnames.push_back(dimname);
3247 array_add_dimensions_dimscale(ar);
3248 }
3249 else {
3250 for (int dim_index = 0; dim_index < dt_inst.ndims; dim_index++)
3251 ar->append_dim_ll(dt_inst.size[dim_index]);
3252 }
3253
3254 string vlen_length_dimname = vname + "_vlen";
3255 string vlen_length_dimpath = var_path + "_vlen";
3256
3257 if (dimnames.empty())
3258 ar->append_dim_ll(max_vlen_length);
3259 else
3260 ar->append_dim_ll(max_vlen_length, vlen_length_dimname);
3261
3262 // We need to transform dimension info. to DAP4 group
3263 BaseType *new_var = nullptr;
3264 vector<string> temp_dimnames_path;
3265 if (dimnames.empty() == false) {
3266 for (const auto & dimname:dt_inst.dimnames_path)
3267 temp_dimnames_path.push_back(dimname);
3268 temp_dimnames_path.push_back(vlen_length_dimpath);
3269 }
3270
3271 new_var = ar->h5dims_transform_to_dap4(d4_grp,temp_dimnames_path);
3272
3273 read_objects_basetype_attr_hl(var_path, new_var, dset_id, false);
3274
3275 auto vlen_d4_attr_unique = make_unique<D4Attribute>("orig_datatype",attr_str_c);
3276 auto vlen_d4_attr = vlen_d4_attr_unique.get();
3277 vlen_d4_attr->add_value("VLEN");
3278 new_var->attributes()->add_attribute_nocopy(vlen_d4_attr_unique.release());
3279
3280 auto vlen_d4_attr2_unique = make_unique<D4Attribute>("vlen_description",attr_str_c);
3281 auto vlen_d4_attr2 = vlen_d4_attr2_unique.get();
3282 string desc_str = "The original variable-length array data is stored as the regular";
3283 desc_str +=" array data that has an extra dimension. The data gap is filled with 0.";
3284 desc_str +=" The actual length of each original variable-length element is stored in another array. The";
3285 desc_str +=" variable name of this array is " + vname +"_vlen_index" +".";
3286 vlen_d4_attr2->add_value(desc_str);
3287 new_var->attributes()->add_attribute_nocopy(vlen_d4_attr2_unique.release());
3288
3289 d4_grp->add_var_nocopy(new_var);
3290 delete bt;
3291
3292 // We need to create another variable to store the index of the vlen
3293 string vname_idx = vname + "_vlen_index";
3294 auto hdf5_int32 = make_unique<HDF5Int32>(vname_idx,var_path,filename);
3295
3296 auto ar_index_unique = make_unique<HDF5Array>(vname_idx, filename, hdf5_int32.get());
3297 HDF5Array *ar_index = ar_index_unique.get();
3298
3299 // set number of elements and variable name values.
3300 // This essentially stores in the struct.
3301 ar_index->set_varpath(var_path);
3302 if (dimnames.empty()==false) {
3303 for (int dim_index = 0; dim_index < dt_inst.ndims; dim_index++) {
3304 if (dimnames[dim_index].empty() == false)
3305 ar_index->append_dim_ll(dt_inst.size[dim_index], dimnames[dim_index]);
3306 else
3307 ar_index->append_dim_ll(dt_inst.size[dim_index]);
3308 }
3309
3310 }
3311 else {
3312 for (int dim_index = 0; dim_index < dt_inst.ndims; dim_index++)
3313 ar_index->append_dim_ll(dt_inst.size[dim_index]);
3314 }
3315
3316 // We need to transform dimension info. to DAP4 group
3317 BaseType *new_var_index = nullptr;
3318 new_var_index = ar_index->h5dims_transform_to_dap4(d4_grp,dt_inst.dimnames_path);
3319
3320 // clear DAP4 dimnames_path vector
3321 dt_inst.dimnames_path.clear();
3322
3323 auto vlen_index_d4_attr_unique = make_unique<D4Attribute>("orig_datatype",attr_str_c);
3324 auto vlen_index_d4_attr = vlen_index_d4_attr_unique.get();
3325 vlen_index_d4_attr->add_value("VLEN_INDEX");
3326 new_var_index->attributes()->add_attribute_nocopy(vlen_index_d4_attr_unique.release());
3327
3328 d4_grp->add_var_nocopy(new_var_index);
3329
3330}
3331
A class for handling all types of array in HDF5 for the default option.
This file includes several helper functions for translating HDF5 to CF-compliant.
A class for mapping HDF5 32-bit float to DAP for the default option.
A class for mapping HDF5 64-bit float to DAP for the default option.
A class for HDF5 signed 16 bit integer type.
This class provides a way to map HDF5 32 bit integer to DAP Int32 for the default option.
This class converts HDF5 compound type into DAP structure for the default option.
This class provides a way to map unsigned HDF5 16 bit integer to DAP UInt16 for the default option.
This class provides a way to map unsigned HDF5 32 bit integer to DAP UInt32.
This class generates DAP URL type for the default option.
A class for parsing NASA HDF-EOS5 StructMetadata.
A class for parsing NASA HDF-EOS5 StructMetadata.
void set_numelm(hsize_t nelms)
remembers number of elements in this array.
void set_numdim(int ndims)
remembers number of dimensions of this array.
void set_memneed(size_t need)
remembers memory size needed.
int get_numdim() const
obtain the number of dimensions of this array.
Definition HDF5Array.h:153
void print() const
Print the information about the members of the Vector list.
Definition HE5Parser.cc:38
Functions to generate DDS and DAS for one object(variable).
void read_objects(DAS &das, const string &varname, hid_t oid, int num_attr)
Definition h5das.cc:299
HDF5PathFinder obj_paths
A variable for remembering visited paths to break cyclic HDF5 groups.
Definition h5dmr.cc:73
string read_struct_metadata(hid_t s_file_id)
EOS5 handling.
Definition h5dmr.cc:1177
bool breadth_first(hid_t file_id, hid_t pid, const char *gname, D4Group *par_grp, const char *fname, bool use_dimscale, bool is_eos5, vector< link_info_t > &hdf5_hls, eos5_dim_info_t &eos5_dim_info, vector< string > &handled_cv_names)
Definition h5dmr.cc:116
void read_objects_structure(D4Group *d4_grp, const string &varname, const string &filename, hid_t dset_id, bool use_dimscale, bool is_eos5)
Definition h5dmr.cc:696
void add_dap4_coverage_default(D4Group *d4_root, const vector< string > &handled_all_cv_names)
Add DAP4 coverage.
Definition h5dmr.cc:1937
void get_softlink(D4Group *par_grp, hid_t h5obj_id, const string &oname, int index, size_t val_size)
Definition h5dmr.cc:1069
void read_objects_base_type(D4Group *d4_grp, hid_t pid, const string &varname, const string &filename, hid_t dset_id, bool use_dimscale, bool is_eos5, eos5_dim_info_t &eos5_dim_info)
Definition h5dmr.cc:576
Data structure and retrieval processing header for the default option.
The main header of the HDF5 OPeNDAP handler.
const int DODS_NAMELEN
Maximum length of variable or attribute name(default option only).
struct DS DS_t
A structure for DDS generation.
struct DSattr DSattr_t
A structure for DAS generation.
char name[DODS_NAMELEN]
Name of HDF5 group or dataset.
int ndims
Number of dimensions.
hsize_t nelmts
Number of elements.
hsize_t need
Memory space needed to hold nelmts type.
Definition HE5Dim.h:7
double point_right
The rightmost coordinate value of a Grid.
Definition HE5Grid.h:25
double point_upper
The top coordinate value of a Grid.
Definition HE5Grid.h:21
double point_left
The leftmost coordinate value of a Grid.
Definition HE5Grid.h:23
double point_lower
The bottom coordinate value of a Grid.
Definition HE5Grid.h:19
double point_right
The rightmost coordinate value of a Grid.
Definition h5dmr.h:95
double point_upper
The top coordinate value of a Grid.
Definition h5dmr.h:91
double point_lower
The bottom coordinate value of a Grid.
Definition h5dmr.h:89
double point_left
The leftmost coordinate value of a Grid.
Definition h5dmr.h:93