bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
HE2CF.cc
1
2// This file is part of the hdf4 data handler for the OPeNDAP data server.
3// Copyright (c) The HDF Group
4//
5// Author: Hyo-Kyung Lee <hyoklee@hdfgroup.org>
6//
7// This is free software; you can redistribute it and/or modify it under the
8// terms of the GNU Lesser General Public License as published by the Free
9// Software Foundation; either version 2.1 of the License, or (at your
10// option) any later version.
11//
12// This software is distributed in the hope that it will be useful, but
13// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15// License for more details.
16//
17// You should have received a copy of the GNU Lesser General Public License
18// along with this software; if not, write to the Free Software Foundation,
19// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20//
21// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
23#include "HE2CF.h"
24#include <libdap/escaping.h>
25#include <libdap/InternalErr.h>
26#include "HDFCFUtil.h"
27#include <iomanip>
28
29using namespace libdap;
30using namespace std;
31
32// Private member functions
33bool
34HE2CF::get_vgroup_field_refids(const string& _gname,
35 int32* _ref_df,
36 int32* _ref_gf)
37{
38
39 int32 vrefid = Vfind(file_id, (char*)_gname.c_str());
40 if (FAIL == vrefid) {
41 Vend(file_id);
42 ostringstream error;
43 error <<"cannot obtain the reference number for vgroup "<<_gname;
44 throw_error(error.str());
45 return false;
46 }
47
48 int32 vgroup_id = Vattach(file_id, vrefid, "r");
49 if (FAIL == vgroup_id) {
50 Vend(file_id);
51 ostringstream error;
52 error <<"cannot obtain the group id for vgroup "<<_gname;
53 throw_error(error.str());
54 return false;
55 }
56
57 int32 npairs = Vntagrefs(vgroup_id);
58 int32 ref_df = -1;
59 int32 ref_gf = -1;
60
61 if(npairs < 0){
62 Vdetach(vgroup_id);
63 Vend(file_id);
64 ostringstream error;
65 error << "Got " << npairs
66 << " npairs for " << _gname;
67 throw_error(error.str());
68 return false;
69 }
70
71 for (int i = 0; i < npairs; ++i) {
72
73 int32 tag = 0;
74 int32 ref = 0;
75
76 if (Vgettagref(vgroup_id, i, &tag, &ref) < 0){
77
78 // Previously just output stderr, it doesn't throw an internal error.
79 // I believe this is wrong. Use DAP's internal error to throw an error.KY 2011-4-26
80 Vdetach(vgroup_id);
81 Vend(file_id);
82 ostringstream error;
83 error << "failed to get tag / ref";
84 throw_error(error.str());
85 return false;
86 }
87
88 if(Visvg(vgroup_id, ref)){
89
90 char cvgroup_name[VGNAMELENMAX*4]; // the child vgroup name
91 int32 istat = 0;
92 int32 vgroup_cid = 0; // Vgroup id
93
94 vgroup_cid = Vattach(file_id, ref,"r");
95 if (FAIL == vgroup_cid) {
96 Vdetach(vgroup_id);
97 Vend(file_id);
98 ostringstream error;
99 error << "cannot obtain the vgroup id";
100 throw_error(error.str());
101 return false;
102 }
103
104 istat = Vgetname(vgroup_cid,cvgroup_name);
105 if (FAIL == istat) {
106 Vdetach(vgroup_cid);
107 Vdetach(vgroup_id);
108 Vend(file_id);
109 ostringstream error;
110 error << "cannot obtain the vgroup id";
111 throw_error(error.str());
112 return false;
113 }
114
115 if(strncmp(cvgroup_name, "Data Fields", 11) == 0){
116 ref_df = ref;
117 }
118
119 if(strncmp(cvgroup_name, "Geolocation Fields", 18) == 0){
120 ref_gf = ref;
121 }
122
123 if (FAIL == Vdetach(vgroup_cid)) {
124 Vdetach(vgroup_id);
125 Vend(file_id);
126 ostringstream error;
127 error << "cannot close the vgroup "<< cvgroup_name <<"Successfully";
128 throw_error(error.str());
129 return false;
130 }
131
132 }
133 }
134 *_ref_df = ref_df;
135 *_ref_gf = ref_gf;
136
137 if (FAIL == Vdetach(vgroup_id)) {
138 Vend(file_id);
139 ostringstream error;
140 error << "cannot close the vgroup "<< _gname <<"Successfully";
141 throw_error(error.str());
142 return false;
143 }
144 return true;
145}
146
147bool
148HE2CF::open_sd(const string& _filename,const int sd_id_in)
149{
150 int32 num_datasets = -1;
151 sd_id = sd_id_in;
152 if(SDfileinfo(sd_id, &num_datasets, &num_global_attributes)
153 == FAIL){
154 if(file_id != -1)
155 Vend(file_id);
156 ostringstream error;
157 error << "Failed to call SDfileinfo() on "
158 << _filename
159 << " file.";
160 throw_error(error.str());
161 return false;
162 }
163 return true;
164}
165
166bool
167HE2CF::open_vgroup(const string& _filename,const int file_id_in)
168{
169
170 file_id = file_id_in;
171 if (Vstart(file_id) < 0){
172 ostringstream error;
173 error << "Failed to call Vstart on " << _filename << endl;
174 throw_error(error.str());
175 return false;
176 }
177 return true;
178}
179
180
181void HE2CF::set_DAS(DAS* _das)
182{
183 das = _das;
184}
185
186bool HE2CF::set_non_ecsmetadata_attrs() {
187
188 for(int i = 0; i < num_global_attributes; i++){
189
190 // H4_MAX_NC_NAME is from the user guide example. It's 256.
191 char temp_name[H4_MAX_NC_NAME];
192 int32 attr_type = 0;
193 int32 attr_count = 0;
194 if(SDattrinfo(sd_id, i, temp_name, &attr_type, &attr_count) == FAIL) {
195 Vend(file_id);
196 ostringstream error;
197 error << "Fail to obtain SDS global attribute info." << endl;
198 throw_error(error.str());
199 }
200
201 string attr_namestr(temp_name);
202 // Check if this attribute is an HDF-EOS2 metadata(coremeta etc. ) attribute
203 // If yes, ignore this attribute.
204 if (true == is_eosmetadata(attr_namestr))
205 continue;
206
207 // When DisableStructMetaAttr key is true, StructMetadata.0 is not in the
208 // ECS metadata list. So this routine will pick up this attribute and generate
209 // the DAP output here. Anyhow,
210 // StructMetadata attribute should not be generated here. We will turn it off.
211 if (attr_namestr.compare(0,14, "StructMetadata" )== 0)
212 continue;
213
214 // When DisableECSMetaDataAll key is true, All ECS attributes(Coremetadata etc.)
215 // should not be in the
216 // ECS metadata list. But this routine will pick up those attributes and generate
217 // the DAP output here. Anyhow,
218 // these attributes should not be generated here. We will turn it off.
219 if (attr_namestr.compare(0,12, "CoreMetadata" )== 0)
220 continue;
221 if (attr_namestr.compare(0,12, "coremetadata" )== 0)
222 continue;
223 if (attr_namestr.compare(0,15, "ArchiveMetadata" )== 0)
224 continue;
225 if (attr_namestr.compare(0,15, "archivemetadata" )== 0)
226 continue;
227 if (attr_namestr.compare(0,15, "Productmetadata" )== 0)
228 continue;
229 if (attr_namestr.compare(0,15, "productmetadata" )== 0)
230 continue;
231
232 // USE VECTOR
233 vector<char>attr_data;
234 attr_data.resize((attr_count+1) *DFKNTsize(attr_type));
235
236 if(SDreadattr(sd_id, i, attr_data.data()) == FAIL){
237 Vend(file_id);
238 ostringstream error;
239 error << "Fail to read SDS global attributes" << endl;
240 throw_error(error.str());
241
242 }
243
244 // Handle character type attribute as a string.
245 if (attr_type == DFNT_CHAR || attr_type == DFNT_UCHAR) {
246 attr_data[attr_count] = '\0';
247 attr_count = 1;
248 }
249
250 AttrTable *at = das->get_table("HDF_GLOBAL");
251 if (!at)
252 at = das->add_table("HDF_GLOBAL", new AttrTable);
253
254 attr_namestr = HDFCFUtil::get_CF_string(attr_namestr);
255
256#if 0
257 if(attr_type == DFNT_UCHAR || attr_type == DFNT_CHAR){
258 string tempstring2(attr_data);
259 string tempfinalstr= string(tempstring2.c_str());
260
261 // Using the customized escattr. Don't escape \n,\r and \t. KY 2013-10-14
262 //tempfinalstr=escattr(tempfinalstr);
263 at->append_attr(attr_namestr, "String" , HDFCFUtil::escattr(tempfinalstr));
264 }
265
266#endif
267
268 for (int loc=0; loc < attr_count ; loc++) {
269 string print_rep = HDFCFUtil::print_attr(attr_type, loc, (void*)attr_data.data() );
270 at->append_attr(attr_namestr, HDFCFUtil::print_type(attr_type), print_rep);
271 }
272
273 }
274
275 return true;
276}
277
278
279
280// Combine ECS metadata coremetadata.0,coremetadata.1 etc. into one string.
281bool HE2CF::set_metadata(const string& metadata_basename,vector<string>& non_number_names, vector<string>& no_num_data)
282{
283 bool suffix_is_num_or_null = true;
284 metadata.clear();
285
286 // Metadata like coremetadata,coremetadata.0,coremetadata.1
287 list<string> one_dot_names;
288
289 // Metadata like coremetadata, coremetadata.0, coremetadata.0.1
290 list<string> two_dots_names;
291
292 // Metadata with the suffix as .s, .t, like productmetadta.s, productmetadata.t
293 // is non_number_names passed from the function
294
295 int list_flag = -1;
296
297 for(int i = 0; i < num_global_attributes; i++){
298
299 // H4_MAX_NC_NAME is from the user guide example. It's 256.
300 char temp_name[H4_MAX_NC_NAME];
301 int32 attr_type=0;
302 int32 attr_count = 0;
303 if(SDattrinfo(sd_id, i, temp_name, &attr_type, &attr_count) == FAIL) {
304 Vend(file_id);
305 ostringstream error;
306 error << "Fail to obtain SDS global attribute info." << endl;
307 throw_error(error.str());
308 }
309
310 string temp_name_str(temp_name);
311
312 // Find the basename, arrange the metadata name list.
313 if(temp_name_str.find(metadata_basename)==0) {
314 arrange_list(one_dot_names,two_dots_names,non_number_names,temp_name_str,list_flag);
315 }
316 }
317
318 list<string>::const_iterator lit;
319
320 // list_flag = 0, no suffix
321 // list_flag = 1, only .0, coremetadata.0
322 // list_flag = 2, coremetadata.0, coremetadata.1 etc
323 // list_flag = 3, coremeatadata.0, coremetadata.0.1 etc
324 if ( list_flag >= 0 && list_flag <=2) {
325 for (lit = one_dot_names.begin();lit!=one_dot_names.end();++lit) {
326 set_eosmetadata_namelist(*lit);
327 string cur_data;
328 obtain_SD_attr_value(*lit,cur_data);
329 metadata.append(cur_data);
330 }
331 }
332
333 if ( 3== list_flag) {
334 for (lit = two_dots_names.begin();lit!=two_dots_names.end();++lit){
335 set_eosmetadata_namelist(*lit);
336 string cur_data;
337 obtain_SD_attr_value(*lit,cur_data);
338 metadata.append(cur_data);
339 }
340 }
341
342 if (non_number_names.empty() == false) {
343 suffix_is_num_or_null = false;
344 no_num_data.resize(non_number_names.size());
345 }
346
347 for (unsigned int i =0; i<non_number_names.size();i++) {
348 set_eosmetadata_namelist(non_number_names[i]);
349 obtain_SD_attr_value(non_number_names[i],no_num_data[i]);
350 }
351
352 return suffix_is_num_or_null;
353
354}
355
356// This routine will generate three ECS metadata lists. Note in theory list sl1 and sl2 should be sorted.
357// Since the ECS metadata is always written(sorted) in increasing numeric order, we don't perform this now.
358// Should watch if there are any outliers. KY 2012-08-31
359void HE2CF::arrange_list(list<string> & sl1, list<string>&sl2,vector<string>&v1,const string& name,int& flag) const {
360
361 // No dot in the ECS name
362 if(name.find(".") == string::npos) {
363 sl1.push_front(name);
364 sl2.push_front(name);
365 flag = 0;
366 }
367 else if (name.find_first_of(".") == name.find_last_of(".")) {
368
369 size_t dot_pos = name.find_first_of(".");
370
371 if((dot_pos+1)==name.size())
372 throw InternalErr(__FILE__, __LINE__,"Should have characters or numbers after ." );
373
374 string str_after_dot = name.substr(dot_pos+1);
375 stringstream sstr(str_after_dot);
376
377 int number_after_dot = 0;
378 sstr >> number_after_dot;
379
380 // No dot after ECS metadata
381 if (!sstr)
382 v1.push_back(name);
383 // .0 after the main name of ECS metadata
384 else if(0 == number_after_dot) {
385 sl1.push_back(name);
386 sl2.push_back(name);
387 // For only .0 case, set flag to 1.
388 if(flag!=1)
389 flag =1;
390 }
391 else {// .1 or .2 etc. after the main ECS metadata name
392 sl1.push_back(name);
393 if (3 == flag)
394 throw InternalErr(__FILE__, __LINE__,
395 "ecs metadata suffix .1 and .0.1 cannot exist at the same file" );
396 if (flag !=2)
397 flag = 2;
398 }
399 }
400 else {// We don't distinguish if .0.1 and .0.0.1 will appear.
401 // have two dots in the ECS name.
402 sl2.push_back(name);
403 if (2 == flag)
404 throw InternalErr(__FILE__, __LINE__,"ecs metadata suffix .1 and .0.1 cannot exist at the same file" );
405 if (flag !=3)
406 flag = 3;
407 }
408
409}
410
411// Obtain SD attribute value
412void HE2CF::obtain_SD_attr_value(const string& attrname, string &cur_data) const {
413
414 int32 sds_index = SDfindattr(sd_id, attrname.c_str());
415 if(sds_index == FAIL){
416 Vend(file_id);
417 ostringstream error;
418 error << "Failed to obtain the SDS global attribute" << attrname << endl;
419 throw InternalErr(__FILE__, __LINE__,error.str());
420 }
421
422 // H4_MAX_NC_NAME is from the user guide example. It's 256.
423 char temp_name[H4_MAX_NC_NAME];
424 int32 type = 0;
425 int32 count = 0;
426
427 if(SDattrinfo(sd_id, sds_index, temp_name, &type, &count) == FAIL) {
428 Vend(file_id);
429 ostringstream error;
430 error << "Failed to obtain the SDS global attribute" << attrname << "information" << endl;
431 throw InternalErr(__FILE__, __LINE__,error.str());
432 }
433
434 vector<char> attrvalue;
435 attrvalue.resize((count+1)*DFKNTsize(type));
436
437 if(SDreadattr(sd_id, sds_index, attrvalue.data()) == FAIL){
438 Vend(file_id);
439 ostringstream error;
440 error << "Failed to read the SDS global attribute" << attrname << endl;
441 throw InternalErr(__FILE__, __LINE__,error.str());
442
443 }
444
445 // Leave the following #if 0 #endif block.
446#if 0
447 // Remove the last nullptr character
448 // string temp_data(attrvalue.begin(),attrvalue.end()-1);
449 // cur_data = temp_data;
450#endif
451
452 if(attrvalue[count] != '\0')
453 throw InternalErr(__FILE__,__LINE__,"the last character of the attribute buffer should be nullptr");
454
455 // No need to escape the special characters since they are ECS metadata. Will see. KY 2013-10-14
456 cur_data.resize(attrvalue.size()-1);
457 copy(attrvalue.begin(),attrvalue.end()-1,cur_data.begin());
458}
459
460
461bool HE2CF::set_vgroup_map(int32 _refid,bool isgeo)
462{
463 // Clear existing maps first.
464 // Note: It should only clear the corresponding groups: Geolocation or Data.
465 if(false == isgeo) {
466 vg_dsd_map.clear();
467 vg_dvd_map.clear();
468 }
469 else {
470 vg_gsd_map.clear();
471 vg_gvd_map.clear();
472 }
473
474 int32 vgroup_id = Vattach(file_id, _refid, "r");
475 if (FAIL == vgroup_id) {
476 Vend(file_id);
477 ostringstream error;
478 error << "Fail to attach the vgroup " ;
479 throw_error(error.str());
480 return false;
481 }
482
483 int32 npairs = Vntagrefs(vgroup_id);
484 if (FAIL == npairs) {
485 Vdetach(vgroup_id);
486 Vend(file_id);
487 ostringstream error;
488 error << "Fail to obtain the number of objects in a group " ;
489 throw_error(error.str());
490 return false;
491 }
492
493 for (int i = 0; i < npairs; ++i) {
494
495 int32 tag2 = 0;
496 int32 ref2 = 0;
497 char buf[H4_MAX_NC_NAME];
498
499 if (Vgettagref(vgroup_id, i, &tag2, &ref2) < 0){
500 Vdetach(vgroup_id);
501 Vend(file_id);
502 ostringstream error;
503 error << "Vgettagref failed for vgroup_id=." << vgroup_id;
504 throw_error(error.str());
505 return false;
506 }
507
508 if(tag2 == DFTAG_NDG){
509
510 int32 sds_index = SDreftoindex(sd_id, ref2); // index
511 if (FAIL == sds_index) {
512 Vdetach(vgroup_id);
513 Vend(file_id);
514 ostringstream error;
515 error << "Cannot obtain the SDS index ";
516 throw_error(error.str());
517 return false;
518 }
519
520 int32 sds_id = SDselect(sd_id, sds_index); // sds_id
521 if (FAIL == sds_id) {
522 Vdetach(vgroup_id);
523 Vend(file_id);
524 ostringstream error;
525 error << "Cannot obtain the SDS ID ";
526 throw_error(error.str());
527 return false;
528 }
529
530 int32 rank;
531 int32 dimsizes[H4_MAX_VAR_DIMS];
532 int32 datatype;
533 int32 num_attrs;
534
535 if(FAIL == SDgetinfo(sds_id, buf, &rank, dimsizes, &datatype, &num_attrs)) {
536 Vdetach(vgroup_id);
537 Vend(file_id);
538 ostringstream error;
539 error << "Cannot obtain the SDS info.";
540 throw_error(error.str());
541 return false;
542 }
543 if(false == isgeo)
544 vg_dsd_map[string(buf)] = sds_id;
545 else
546 vg_gsd_map[string(buf)] = sds_id;
547
548 }
549
550 if(tag2 == DFTAG_VH){
551
552 int vid;
553 if ((vid = VSattach(file_id, ref2, "r")) < 0) {
554
555 Vdetach(vgroup_id);
556 Vend(file_id);
557 ostringstream error;
558 error << "VSattach failed for file_id=." << file_id;
559 throw_error(error.str());
560 }
561 if (FAIL == VSgetname(vid, buf)) {
562
563 Vdetach(vgroup_id);
564 Vend(file_id);
565 ostringstream error;
566 error << "VSgetname failed for file_id=." << file_id;
567 throw_error(error.str());
568 }
569 if(false == isgeo)
570 vg_dvd_map[string(buf)] = ref2;
571 else
572 vg_gvd_map[string(buf)] = ref2;
573
574 if (FAIL == VSdetach(vid)) {
575
576 Vdetach(vgroup_id);
577 Vend(file_id);
578 ostringstream error;
579 error << "VSdetach failed for file_id=." << file_id;
580 throw_error(error.str());
581
582 }
583 }// if
584 } // for
585
586 if (FAIL == Vdetach(vgroup_id)){
587 Vend(file_id);
588 ostringstream error;
589 error << "VSdetach failed for file_id=." << file_id;
590 throw_error(error.str());
591 }
592
593 return true;
594}
595
596bool HE2CF::write_attr_long_name(const string& _long_name,
597 const string& _varname,
598 int _fieldtype)
599{
600 AttrTable *at = das->get_table(_varname);
601 if (!at){
602 at = das->add_table(_varname, new AttrTable);
603 }
604 if(_fieldtype > 3){
605 at->append_attr("long_name", "String", _long_name + "(fake)");
606 }
607 else{
608 at->append_attr("long_name", "String", _long_name);
609 }
610 return true;
611}
612bool HE2CF::write_attr_long_name(const string& _group_name,
613 const string& _long_name,
614 const string& _varname,
615 int _fieldtype)
616{
617 AttrTable *at = das->get_table(_varname);
618 if (!at){
619 at = das->add_table(_varname, new AttrTable);
620 }
621 if(_fieldtype > 3){
622 at->append_attr("long_name", "String",
623 _group_name + ":" + _long_name + "(fake)");
624 }
625 else{
626 at->append_attr("long_name", "String",
627 _group_name + ":" + _long_name);
628 }
629 return true;
630}
631
632
633bool
634HE2CF::write_attr_sd(int32 _sds_id, const string& _newfname,int _fieldtype)
635{
636 char buf_var[H4_MAX_NC_NAME];
637 char buf_attr[H4_MAX_NC_NAME];
638 int32 rank;
639 int32 dimsizes[H4_MAX_VAR_DIMS];
640 int32 datatype;
641 int32 num_attrs;
642 int32 n_values;
643 intn status;
644
645 status = SDgetinfo(_sds_id, buf_var,
646 &rank, dimsizes, &datatype, &num_attrs);
647 if (FAIL == status) {
648 Vend(file_id);
649 SDendaccess(_sds_id);
650 ostringstream error;
651 error << "Cannot obtain the SDS info. ";
652 throw_error(error.str());
653
654 }
655 AttrTable *at = das->get_table(_newfname);
656 if (!at){
657 at = das->add_table(_newfname, new AttrTable);
658 }
659
660 //Check if having the "coordinates" attribute when fieldtype is 0
661 bool v_has_coordinates = false;
662 if(0 == _fieldtype) {
663 if(at->simple_find("coordinates")!= at->attr_end())
664 v_has_coordinates = true;
665 }
666
667 //Check if having the "units" attributes when fieldtype is 1(latitude) or 2(longitude)
668 bool llcv_has_units = false;
669
670 // See if we can ignore scale and offset.
671 short llcv_ignore_scale = 0;
672 short llcv_ignore_offset = 0;
673 if(1 == _fieldtype || 2 == _fieldtype) {
674 if(at->simple_find("units")!= at->attr_end())
675 llcv_has_units = true;
676 try {
677 llcv_ignore_scale = check_scale_offset(_sds_id,true);
678 }
679 catch(...) {
680 SDendaccess(_sds_id);
681 Vend(file_id);
682 throw;
683 }
684 try {
685 llcv_ignore_offset = check_scale_offset(_sds_id,false);
686 }
687 catch(...) {
688 SDendaccess(_sds_id);
689 Vend(file_id);
690 throw;
691 }
692
693 // We need to check if we ignore scale_factor and the scale_factor type is an integer.
694 // If that's the case, we need to throw an exception since we cannot retrieve the
695 // integer type scale_factor added by the HDF4 API for the latitude and longitude.
696 // We don't think such a case exists. If any, we throw an exception and hope the
697 // user to report this case to us.
698
699 // Find a case that the variable type is integer,
700 // that the scale_factor and add_offset attributes exist
701 // that the add_offset is not 0.
702 // We don't support this case and highly suspect that such a case doesn't exist.
703 if(-1 == llcv_ignore_offset && 2 == llcv_ignore_scale){
704 SDendaccess(_sds_id);
705 Vend(file_id);
706 ostringstream error;
707 error <<"The latitude or longitude has <scale_factor> and <add_offset> attributes, "
708 <<" the latitude or longitude have integer type and <add_offset> is not 0, "
709 <<" we don't support such a case in the current implementation, "
710 <<" please report to eoshelp@hdfgroup.org if you encounter this situation.";
711 throw_error(error.str());
712 }
713 }
714
715 for (int j=0; j < num_attrs; j++){
716
717 status = SDattrinfo(_sds_id, j, buf_attr, &datatype, &n_values);
718 if (status < 0){
719 Vend(file_id);
720 SDendaccess(_sds_id);
721 ostringstream error;
722 error << "SDattrinfo() failed on " << buf_attr;
723 throw_error(error.str());
724 }
725
726 if(true == v_has_coordinates) {
727 if(!strncmp(buf_attr, "coordinates", H4_MAX_NC_NAME))
728 continue;
729 }
730
731 if(true == llcv_has_units) {
732 if(!strncmp(buf_attr, "units", H4_MAX_NC_NAME))
733 continue;
734 }
735
736 if( 1 == _fieldtype || 2 == _fieldtype) {
737 if(!strncmp(buf_attr, "scale_factor", H4_MAX_NC_NAME))
738 if((1 == llcv_ignore_scale) || (2==llcv_ignore_scale) )
739 continue;
740 if(!strncmp(buf_attr, "add_offset", H4_MAX_NC_NAME))
741 if(1 == llcv_ignore_offset)
742 continue;
743 }
744
745 vector<char> value;
746 value.resize((n_values+1) * DFKNTsize(datatype));
747 status = SDreadattr(_sds_id, j, value.data());
748 if (status < 0){
749 Vend(file_id);
750 SDendaccess(_sds_id);
751 ostringstream error;
752 error << "SDreadattr() failed on " << buf_attr << endl;
753 throw_error(error.str());
754 }
755
756 // Handle character type attribute as a string.
757 if (datatype == DFNT_CHAR || datatype == DFNT_UCHAR) {
758 value[n_values] = '\0';
759 n_values = 1;
760 }
761
762 // Need to check and change if attribute names contain special characters. KY 2012-6-8
763 string attr_cf_name = string(buf_attr,strlen(buf_attr));
764 attr_cf_name = HDFCFUtil::get_CF_string(attr_cf_name);
765 for (int loc=0; loc < n_values ; loc++) {
766 string print_rep = HDFCFUtil::print_attr(datatype, loc, (void *)value.data());
767
768 // Override any existing _FillValue attribute.
769 if (attr_cf_name == "_FillValue") {
770 at->del_attr(attr_cf_name);
771 }
772 // Override any existing long_name attribute.
773 if (attr_cf_name == "long_name") {
774 at->del_attr(attr_cf_name);
775 }
776
777 // No need to escape special characters since print_rep already does that.
778 at->append_attr(attr_cf_name, HDFCFUtil::print_type(datatype), print_rep);
779
780 }
781 }
782
783 status = SDendaccess(_sds_id);
784 if(status < 0) {
785 ostringstream error;
786 error << "SDendaccess failed on variable " << _newfname;
787 throw_error(error.str());
788 }
789
790 return true;
791}
792
793bool HE2CF::write_attr_vdata(int32 _vd_id, const string& _newfname, int _fieldtype)
794{
795 int32 number_type = 0;
796 int32 count = 0;
797 int32 size = 0;
798 char buf[H4_MAX_NC_NAME];
799
800 int vid = 0;
801
802 if ((vid = VSattach(file_id, _vd_id, "r")) < 0) {
803
804 Vend(file_id);
805
806 ostringstream error;
807 error << "VSattach failed.";
808 throw_error(error.str());
809 }
810
811 // Don't use VSnattrs - it returns the TOTAL number of attributes
812 // of a vdata and its fields. We should use VSfnattrs.
813 count = VSfnattrs(vid, _HDF_VDATA);
814 if (FAIL == count) {
815
816 VSdetach(vid);
817 Vend(file_id);
818 ostringstream error;
819 error << "VSfnattrs failed.";
820 throw_error(error.str());
821 }
822
823 AttrTable *at = das->get_table(_newfname);
824 if (!at)
825 at = das->add_table(_newfname, new AttrTable);
826
827
828 //Check if having the "coordinates" attribute when fieldtype is 0
829 bool v_has_coordinates = false;
830 if(0 == _fieldtype) {
831 if(at->simple_find("coordinates")!= at->attr_end())
832 v_has_coordinates = true;
833 }
834
835 //Check if having the "units" attributes when fieldtype is 1 or 2
836 bool llcv_has_units = false;
837 if(1 == _fieldtype || 2 == _fieldtype) {
838 if(at->simple_find("units")!= at->attr_end())
839 llcv_has_units = true;
840 }
841
842
843 for(int i=0; i < count; i++){
844 int32 count_v = 0;
845 if (VSattrinfo(vid, _HDF_VDATA, i, buf,
846 &number_type, &count_v, &size) < 0) {
847 VSdetach(vid);
848 Vend(file_id);
849 ostringstream error;
850 error << "VSattrinfo failed.";
851 throw_error(error.str());
852 }
853
854 if(true == v_has_coordinates) {
855 if(!strncmp(buf, "coordinates", H4_MAX_NC_NAME))
856 continue;
857 }
858
859 else if(true == llcv_has_units) {
860 if(!strncmp(buf, "units", H4_MAX_NC_NAME))
861 continue;
862 }
863
864 vector<char> data;
865 data.resize((count_v+1) * DFKNTsize(number_type));
866 if (VSgetattr(vid, _HDF_VDATA, i, data.data()) < 0) {
867
868 // problem: clean up and throw an exception
869 VSdetach(vid);
870 Vend(file_id);
871 ostringstream error;
872 error << "VSgetattr failed.";
873 throw_error(error.str());
874 }
875 // Handle character type attribute as a string.
876 if (number_type == DFNT_CHAR || number_type == DFNT_UCHAR8) {
877 data[count_v] ='\0';
878 count_v = 1;
879 }
880
881 for(int j=0; j < count_v ; j++){
882
883 string print_rep = HDFCFUtil::print_attr(number_type, j, (void *)data.data());
884
885 // Override any existing _FillValue attribute.
886 if(!strncmp(buf, "_FillValue", H4_MAX_NC_NAME)){
887 at->del_attr(buf);
888 }
889
890 // Override any existing long_name attribute.
891 if(!strncmp(buf, "long_name", H4_MAX_NC_NAME)){
892 at->del_attr(buf);
893 }
894
895 string vdataname(buf);
896 at->append_attr(HDFCFUtil::get_CF_string(buf), HDFCFUtil::print_type(number_type), print_rep);
897
898 }
899 } // for
900 // We need to call VFnfields to process fields as well in near future.
901 VSdetach(vid);
902
903 return true;
904}
905
906void
907HE2CF::throw_error(const string& _error)
908{
909 throw InternalErr(__FILE__, __LINE__,
910 _error);
911}
912
913
914// Public member functions
915HE2CF::HE2CF() = default;
916
917HE2CF::~HE2CF() = default;
918
919bool
921{
922
923 // Close Vgroup interface.
924 int istat = Vend(file_id);
925 if(istat == FAIL){
926 ostringstream error;
927 error << "Failed to call Vend in HE2CF::close.";
928 throw_error(error.str());
929 return false;
930 }
931
932 return true;
933}
934
935string
936HE2CF::get_metadata(const string& _name, bool& suffix_is_number,vector<string>&meta_nonnum_names, vector<string>& meta_nonum_data)
937{
938 suffix_is_number = set_metadata(_name,meta_nonnum_names,meta_nonum_data);
939 return metadata;
940}
941
942bool
943HE2CF::open(const string& _filename,const int hc_sd_id, const int hc_file_id)
944{
945 if(_filename == ""){
946 ostringstream error;
947 error << "=open(): filename is empty.";
948 throw_error(error.str());
949 return false;
950 }
951
952 if(!open_vgroup(_filename,hc_file_id)){
953 ostringstream error;
954 error << "=open(): failed to open vgroup.";
955 throw_error(error.str());
956 return false;
957 }
958
959 if(!open_sd(_filename,hc_sd_id)){
960 ostringstream error;
961 error << "=open(): failed to open sd.";
962 throw_error(error.str());
963 return false;
964 }
965
966 return true;
967}
968
969
970
971
972bool
973HE2CF::write_attribute(const string& _gname,
974 const string& _fname,
975 const string& _newfname,
976 int _n_groups,
977 int _fieldtype)
978{
979
980 if(_n_groups > 1){
981 write_attr_long_name(_gname, _fname, _newfname, _fieldtype);
982 }
983 else{
984 write_attr_long_name(_fname, _newfname, _fieldtype);
985 }
986 int32 ref_df = -1; // ref id for /Data Fields/
987 int32 ref_gf = -1; // ref id for /Geolocaion Fields/
988
989 // This if block effectively constructs the vg_dsd_map and vg_dvd_map at most once
990 // since starting from the second time, gname will be equal to _gname.
991 if(gname != _gname){
992 // Construct field:(SD|Vdata)ref table for faster lookup.
993 gname = _gname;
994 get_vgroup_field_refids(_gname, &ref_df, &ref_gf);
995 if (ref_gf !=-1)
996 set_vgroup_map(ref_gf,true);
997
998 if (ref_df != 1)
999 set_vgroup_map(ref_df,false);
1000 }
1001
1002 // Use a cached table.
1003 int32 id = -1;
1004
1005 // Using SDS name to SDS id table is fine for the HDF-EOS2 case since under one grid/swath,
1006 // the data field name is unique. Generally SDS name cannot be used as a key since one may have
1007 // the same SDS name for two different SDS objects. But in this context(HDF-EOS2 objects), it is okay.
1008 // KY 2012-7-3
1009 // Retrive sds_id for SDS
1010 // Go over all possible maps (data fields and geolocation fields)
1011 id = vg_dsd_map[_fname];
1012 if(id > 0){
1013 write_attr_sd(id, _newfname,_fieldtype);
1014 }
1015 // Retrieve ref_id for Vdata
1016 // The Vdata we retrieve here are HDF-EOS2 objects(Swath 1-D data)
1017 // Not all Vdata from the HDF file.
1018 id = vg_dvd_map[_fname];
1019 if(id > 0){
1020 write_attr_vdata(id, _newfname,_fieldtype);
1021 }
1022
1023 id = vg_gsd_map[_fname];
1024 if(id > 0){
1025 write_attr_sd(id, _newfname,_fieldtype);
1026 }
1027 // Retrieve ref_id for Vdata
1028 // The Vdata we retrieve here are HDF-EOS2 objects(Swath 1-D data)
1029 // Not all Vdata from the HDF file.
1030 id = vg_gvd_map[_fname];
1031 if(id > 0){
1032 write_attr_vdata(id, _newfname,_fieldtype);
1033 }
1034
1035 return true;
1036}
1037
1038// The application will make sure the fill value falls in the valid range of the datatype values.
1039bool
1040HE2CF::write_attribute_FillValue(const string& _varname,
1041 int _type,
1042 float value)
1043{
1044 void* v_ptr = nullptr;
1045 // Casting between pointers of different types may generate unexpected behavior.
1046 // gcc 4.8.2 reveals this problem in one test(SwathFile.HDF).
1047 // So what we should do is to use char* or vector<char> in this case and memcpy the value to the vector<char>.
1048 // Casting the vector pointer to void *. The compiler can correctly interpret the data based on the data type.
1049 vector<char>v_val;
1050
1051 // Cast the value depending on the type.
1052 switch (_type) {
1053
1054 case DFNT_UINT8:
1055 {
1056 auto val = (uint8) value;
1057 v_val.resize(1);
1058 memcpy(v_val.data(),&val,1);
1059 v_ptr = (void*)v_val.data();
1060 }
1061
1062 break;
1063 case DFNT_INT8:
1064 {
1065 auto val = (int8) value;
1066 v_val.resize(1);
1067 memcpy(v_val.data(),&val,1);
1068 v_ptr = (void*)v_val.data();
1069 }
1070 break;
1071 case DFNT_INT16:
1072 {
1073 auto val = (int16) value;
1074 v_val.resize(sizeof(short));
1075 memcpy(v_val.data(),&val,sizeof(short));
1076 v_ptr = (void*)v_val.data();
1077 }
1078 break;
1079
1080 case DFNT_UINT16:
1081 {
1082 auto val = (uint16) value;
1083 v_val.resize(sizeof(unsigned short));
1084 memcpy(v_val.data(),&val,sizeof(unsigned short));
1085 v_ptr = (void*)v_val.data();
1086 }
1087 break;
1088
1089 case DFNT_INT32:
1090 {
1091 auto val = (int32) value;
1092 v_val.resize(sizeof(int));
1093 memcpy(v_val.data(),&val,sizeof(int));
1094 v_ptr = (void*)v_val.data();
1095 }
1096 break;
1097 case DFNT_UINT32:
1098 {
1099 auto val = (uint32) value;
1100 v_val.resize(sizeof(unsigned int));
1101 memcpy(v_val.data(),&val,sizeof(int));
1102 v_ptr = (void*)v_val.data();
1103 }
1104 break;
1105 case DFNT_FLOAT:
1106 {
1107 v_ptr = (void*)&value;
1108 }
1109 break;
1110 case DFNT_DOUBLE:
1111 {
1112 auto val = (float64) value;
1113 v_val.resize(sizeof(double));
1114 memcpy(v_val.data(),&val,sizeof(double));
1115 v_ptr = (void*)v_val.data();
1116 }
1117 break;
1118 default:
1119 throw_error("Invalid FillValue Type - ");
1120 break;
1121 }
1122
1123 AttrTable *at = das->get_table(_varname);
1124 if (!at){
1125 at = das->add_table(_varname, new AttrTable);
1126 }
1127 string print_rep = HDFCFUtil::print_attr(_type, 0, v_ptr);
1128 at->append_attr("_FillValue", HDFCFUtil::print_type(_type), print_rep);
1129
1130 return true;
1131}
1132
1133bool
1134HE2CF::write_attribute_coordinates(const string& _varname, const string &_coordinates)
1135{
1136
1137 AttrTable *at = das->get_table(_varname);
1138 if (!at){
1139 at = das->add_table(_varname, new AttrTable);
1140 }
1141 at->append_attr("coordinates", "String", _coordinates);
1142
1143 return true;
1144}
1145
1146bool
1147HE2CF::write_attribute_units(const string& _varname, const string &_units)
1148{
1149
1150 AttrTable *at = das->get_table(_varname);
1151 if (!at){
1152 at = das->add_table(_varname, new AttrTable);
1153 }
1154 at->del_attr("units"); // Override any existing units attribute.
1155 at->append_attr("units", "String", _units);
1156
1157 return true;
1158}
1159
1160
1161// Check if the scale_factor or the add_offset can be ignored.
1162// _sds_id is the SDS ID of the variable.
1163// is_scale is true, the attribute is scale_factor.
1164// is_scale is false, the attribute is add_offset.
1165// When the attribute is scale_factor,
1166// the meaning of the returned value:
1167// 0: the scale_factor won't be ignored if exists.
1168// 1 or 2: the scale_factor can be ignored. 2 is the case when the variable type is integer. 1 is the case when the variable type is float.
1169// When the attribute is add_offset,
1170// the meaning of the returned value:
1171// 0: the add_offset won't be ignored if exists. It doesn't check if add_offset attribute exists.
1172// -1: the add_offset won't be ignored and the add_offset definitely exists,the value is not 0.
1173// 1: the add_offset will be ignored.
1174short
1175HE2CF::check_scale_offset(int32 _sds_id, bool is_scale) {
1176
1177 char buf_var[H4_MAX_NC_NAME];
1178 char buf_attr[H4_MAX_NC_NAME];
1179 int32 rank;
1180 int32 dimsizes[H4_MAX_VAR_DIMS];
1181 int32 datatype;
1182 int32 num_attrs;
1183 int32 n_values;
1184 intn status;
1185
1186 short ignore_so = 0;
1187 status = SDgetinfo(_sds_id, buf_var,
1188 &rank, dimsizes, &datatype, &num_attrs);
1189 if (FAIL == status) {
1190 SDendaccess(_sds_id);
1191 ostringstream error;
1192 error << "Cannot obtain the SDS info. ";
1193 throw_error(error.str());
1194 }
1195
1196 bool has_so = false;
1197 int so_index = -1;
1198 int32 so_dtype = -1;
1199 int32 var_dtype = datatype;
1200 string so_name =((true==is_scale)?"scale_factor":"add_offset");
1201
1202 for (int j=0; j < num_attrs; j++){
1203
1204 status = SDattrinfo(_sds_id, j, buf_attr, &datatype, &n_values);
1205 if (status < 0){
1206 SDendaccess(_sds_id);
1207 ostringstream error;
1208 error << "SDattrinfo() failed on " << buf_attr;
1209 throw_error(error.str());
1210 }
1211
1212 if(!strncmp(buf_attr, so_name.c_str(), H4_MAX_NC_NAME)) {
1213 if(1 == n_values) {
1214 has_so = true;
1215 so_index = j;
1216 so_dtype = datatype;
1217 break;
1218 }
1219 }
1220 }
1221
1222 // We find the attribute, now check if we can ignore this attribute.
1223 if(true == has_so) {
1224 vector<char> value;
1225 // We have already checked the number of attribute values. The number is 1.
1226 value.resize(DFKNTsize(so_dtype));
1227 status = SDreadattr(_sds_id, so_index, value.data());
1228 if (status < 0){
1229 SDendaccess(_sds_id);
1230 ostringstream error;
1231 error << "SDreadattr() failed on the attribute scale_factor." << endl;
1232 throw_error(error.str());
1233 }
1234 // If this attribute is "scale_factor",
1235 if(true == is_scale) {
1236 if(DFNT_FLOAT32 == so_dtype) {
1237 float final_scale_value = *((float*)((void*)(value.data())));
1238 if(final_scale_value == 1.0)
1239 ignore_so = 1;
1240 // This is the ugly case that the variable type is integer, the
1241 // scale_factor is handled by the handler reading function.
1242 // However, the add_offset for this case cannot be 0.
1243 // So we need to check this case. We may also need to
1244 // check the scale_factor value.
1245 else if(var_dtype !=DFNT_FLOAT32 && var_dtype != DFNT_FLOAT64)
1246 ignore_so = 2;
1247 }
1248 else if(DFNT_FLOAT64 == so_dtype) {
1249 double final_scale_value = *((double*)((void*)(value.data())));
1250 if(final_scale_value == 1.0)
1251 ignore_so = 1;
1252 else if(var_dtype !=DFNT_FLOAT32 && var_dtype != DFNT_FLOAT64)
1253 ignore_so = 2;
1254 }
1255 }
1256 else {
1257 // has offset, we need to make a mark for the case when scale_factor applies to a varialbe that has an integer type
1258 ignore_so = -1;
1259 string print_rep = HDFCFUtil::print_attr(so_dtype, 0, (void *)value.data());
1260 if(DFNT_FLOAT32 == so_dtype || DFNT_FLOAT64 == so_dtype) {
1261 if(atof(print_rep.c_str()) == 0.0)
1262 ignore_so = 1;
1263 }
1264 else {
1265 if(atoi(print_rep.c_str()) == 0)
1266 ignore_so = 1;
1267 }
1268 }
1269 }
1270 return ignore_so;
1271}
1272
bool open(const std::string &filename, const int sd_id, const int file_id)
openes \afilename HDF4 file.
Definition HE2CF.cc:943
string get_metadata(const std::string &metadataname, bool &suffix_is_num, std::vector< std::string > &non_num_names, std::vector< std::string > &non_num_data)
retrieves the merged metadata.
Definition HE2CF.cc:936
bool write_attribute(const std::string &gname, const std::string &fname, const std::string &newfname, int n_groups, int fieldtype)
Definition HE2CF.cc:973
bool write_attribute_coordinates(const std::string &varname, const std::string &coord)
Definition HE2CF.cc:1134
void set_DAS(libdap::DAS *das)
sets DAS pointer so that we can bulid attribute tables.
Definition HE2CF.cc:181
bool close()
closes the opened file.
Definition HE2CF.cc:920
bool write_attribute_units(const std::string &varname, const std::string &units)
Definition HE2CF.cc:1147
bool write_attribute_FillValue(const std::string &varname, int type, float val)
Definition HE2CF.cc:1040
static std::string print_attr(int32, int, void *)
Print attribute values in string.
Definition HDFCFUtil.cc:202
static std::string print_type(int32)
Print datatype in string.
Definition HDFCFUtil.cc:323
static std::string get_CF_string(std::string s)
Change special characters to "_".
Definition HDFCFUtil.cc:100