bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
HDFCFUtil.cc
1#include "config.h"
2#include "config_hdf.h"
3
4#include "HDFCFUtil.h"
5#include <BESDebug.h>
6#include <BESLog.h>
7#include <math.h>
8#include"dodsutil.h"
9#include"HDFSPArray_RealField.h"
10#include"HDFSPArrayMissField.h"
11#include"HDFEOS2GeoCFProj.h"
12#include"HDFEOS2GeoCF1D.h"
13
14#include <libdap/debug.h>
15
16#define SIGNED_BYTE_TO_INT32 1
17
18// HDF datatype headers for both the default and the CF options
19#include "HDFByte.h"
20#include "HDFInt16.h"
21#include "HDFUInt16.h"
22#include "HDFInt32.h"
23#include "HDFUInt32.h"
24#include "HDFFloat32.h"
25#include "HDFFloat64.h"
26#include "HDFStr.h"
27#include "HDF4RequestHandler.h"
28
29//
30using namespace std;
31using namespace libdap;
32
33#define ERR_LOC1(x) #x
34#define ERR_LOC2(x) ERR_LOC1(x)
35#define ERR_LOC __FILE__ " : " ERR_LOC2(__LINE__)
36
37
42static void
43split_helper(vector<string> &tokens, const string &text, const char sep)
44{
45 string::size_type start = 0;
46 string::size_type end = 0;
47
48 while ((end = text.find(sep, start)) != string::npos) {
49 tokens.push_back(text.substr(start, end - start));
50 start = end + 1;
51 }
52 tokens.push_back(text.substr(start));
53}
54
55// From a string separated by a separator to a list of string,
56// for example, split "ab,c" to {"ab","c"}
57void
58HDFCFUtil::Split(const char *s, int len, char sep, std::vector<std::string> &names)
59{
60 names.clear();
61 split_helper(names, string(s, len), sep);
62}
63
64// Assume sz is Null terminated string.
65void
66HDFCFUtil::Split(const char *sz, char sep, std::vector<std::string> &names)
67{
68 names.clear();
69
70 // Split() was showing up in some valgrind runs as having an off-by-one
71 // error. I added this help track it down.
72 DBG(std::cerr << "HDFCFUtil::Split: sz: <" << sz << ">, sep: <" << sep << ">" << std::endl);
73
74 split_helper(names, string(sz), sep);
75}
76
77
78// This is a safer way to insert and update a c++ map value.
79// Otherwise, the local testsuite at The HDF Group will fail for HDF-EOS2 data
80// under iMac machine platform.
81// The implementation replaces the element even if the key exists.
82// This function is equivalent to map[key]=value
83bool
84HDFCFUtil::insert_map(std::map<std::string,std::string>& m, string key, string val)
85{
86 pair<map<string,string>::iterator, bool> ret;
87 ret = m.insert(make_pair(key, val));
88 if(ret.second == false){
89 m.erase(key);
90 ret = m.insert(make_pair(key, val));
91 if(ret.second == false){
92 BESDEBUG("h4","insert_map():insertion failed on Key=" << key << " Val=" << val << endl);
93 }
94 }
95 return ret.second;
96}
97
98// Obtain CF string
99string
101{
102
103 if(""==s)
104 return s;
105 string insertString(1,'_');
106
107 // Always start with _ if the first character is not a letter
108 if (true == isdigit(s[0]))
109 s.insert(0,insertString);
110
111 // New name conventions drop the first '/' from the path.
112 if ('/' ==s[0])
113 s.erase(0,1);
114
115 for(auto &si: s)
116 if((false == isalnum(si)) && (si!='_'))
117 si='_';
118
119 return s;
120
121}
122
123// Obtain the unique name for the clashed names and save it to set namelist.
124// This is a recursive call. A unique name list is represented as a set.
125// If we find that a name already exists in the nameset, we will add a number
126// at the end of the name to form a new name. If the new name still exists
127// in the nameset, we will increase the index number and check again until
128// a unique name is generated.
129void
130HDFCFUtil::gen_unique_name(string &str,set<string>& namelist, int&clash_index) {
131
132 pair<set<string>::iterator,bool> ret;
133 string newstr = "";
134 stringstream sclash_index;
135 sclash_index << clash_index;
136 newstr = str + sclash_index.str();
137
138 ret = namelist.insert(newstr);
139 if (false == ret.second) {
140 clash_index++;
141 gen_unique_name(str,namelist,clash_index);
142 }
143 else
144 str = newstr;
145}
146
147// General routine to handle the name clashing
148// The input parameters include:
149// name vector -> newobjnamelist(The name vector is changed to a unique name list
150// a pre-allocated object name set ->objnameset(can be used to determine if a name exists)
151void
152HDFCFUtil::Handle_NameClashing(vector<string>&newobjnamelist,set<string>&objnameset) {
153
154 pair<set<string>::iterator,bool> setret;
156
157 vector<string> clashnamelist;
158
159 // clash index to original index mapping
160 map<int,int> cl_to_ol;
161 int ol_index = 0;
162 int cl_index = 0;
163
164
165 for (const auto &newobjname:newobjnamelist) {
166 setret = objnameset.insert(newobjname);
167 if (false == setret.second ) {
168 clashnamelist.insert(clashnamelist.end(),newobjname);
169 cl_to_ol[cl_index] = ol_index;
170 cl_index++;
171 }
172 ol_index++;
173 }
174
175 // Now change the clashed elements to unique elements,
176 // Generate the set which has the same size as the original vector.
177 for (auto &clashname:clashnamelist) {
178 int clash_index = 1;
179 string temp_clashname = clashname +'_';
180 HDFCFUtil::gen_unique_name(temp_clashname,objnameset,clash_index);
181 clashname = temp_clashname;
182 }
183
184 // Now go back to the original vector, make it unique.
185 for (unsigned int i =0; i <clashnamelist.size(); i++)
186 newobjnamelist[cl_to_ol[i]] = clashnamelist[i];
187
188}
189
190// General routine to handle the name clashing
191// The input parameter just includes:
192// name vector -> newobjnamelist(The name vector is changed to a unique name list
193void
194HDFCFUtil::Handle_NameClashing(vector<string>&newobjnamelist) {
195
196 set<string> objnameset;
197 Handle_NameClashing(newobjnamelist,objnameset);
198}
199
200// Borrowed codes from ncdas.cc in netcdf_handle4 module.
201string
202HDFCFUtil::print_attr(int32 type, int loc, void *vals)
203{
204 ostringstream rep;
205
206 union {
207 char *cp;
208 unsigned char *ucp;
209 short *sp;
210 unsigned short *usp;
211 int32 /*nclong*/ *lp;
212 unsigned int *ui;
213 float *fp;
214 double *dp;
215 } gp;
216
217 switch (type) {
218
219 // Mapping both DFNT_UINT8 and DFNT_INT8 to unsigned char
220 // may cause overflow. Documented at jira ticket HFRHANDLER-169.
221 case DFNT_UINT8:
222 {
223 unsigned char uc;
224 gp.ucp = (unsigned char *) vals;
225
226 uc = *(gp.ucp+loc);
227 rep << (int)uc;
228 return rep.str();
229 }
230
231 case DFNT_INT8:
232 {
233 char c;
234 gp.cp = (char *) vals;
235
236 c = *(gp.cp+loc);
237 rep << (int)c;
238 return rep.str();
239 }
240
241 case DFNT_UCHAR:
242 case DFNT_CHAR:
243 {
244 string tmp_str = static_cast<const char*>(vals);
245 return tmp_str;
246 }
247
248 case DFNT_INT16:
249 {
250 gp.sp = (short *) vals;
251 rep << *(gp.sp+loc);
252 return rep.str();
253 }
254
255 case DFNT_UINT16:
256 {
257 gp.usp = (unsigned short *) vals;
258 rep << *(gp.usp+loc);
259 return rep.str();
260 }
261
262 case DFNT_INT32:
263 {
264 gp.lp = (int32 *) vals;
265 rep << *(gp.lp+loc);
266 return rep.str();
267 }
268
269 case DFNT_UINT32:
270 {
271 gp.ui = (unsigned int *) vals;
272 rep << *(gp.ui+loc);
273 return rep.str();
274 }
275
276 case DFNT_FLOAT:
277 {
278 float attr_val = *(float*)vals;
279 bool is_a_fin = isfinite(attr_val);
280 gp.fp = (float *) vals;
281 rep << showpoint;
282 // setprecision seeme to cause the one-bit error when
283 // converting from float to string. Watch whether this
284 // is an isue.
285 rep << setprecision(10);
286 rep << *(gp.fp+loc);
287 string tmp_rep_str = rep.str();
288 if (tmp_rep_str.find('.') == string::npos
289 && tmp_rep_str.find('e') == string::npos
290 && tmp_rep_str.find('E') == string::npos) {
291 if(true == is_a_fin)
292 rep << ".";
293 }
294 return rep.str();
295 }
296
297 case DFNT_DOUBLE:
298 {
299
300 double attr_val = *(double*)vals;
301 bool is_a_fin = isfinite(attr_val);
302 gp.dp = (double *) vals;
303 rep << std::showpoint;
304 rep << std::setprecision(17);
305 rep << *(gp.dp+loc);
306 string tmp_rep_str = rep.str();
307 if (tmp_rep_str.find('.') == string::npos
308 && tmp_rep_str.find('e') == string::npos
309 && tmp_rep_str.find('E') == string::npos) {
310 if(true == is_a_fin)
311 rep << ".";
312 }
313 return rep.str();
314 }
315 default:
316 return string("UNKNOWN");
317 }
318
319}
320
321// Print datatype in string. This is used to generate DAS.
322string
324{
325
326 // I expanded the list based on libdap/AttrTable.h.
327 static const char UNKNOWN[]="Unknown";
328 static const char BYTE[]="Byte";
329 static const char INT16[]="Int16";
330 static const char UINT16[]="UInt16";
331 static const char INT32[]="Int32";
332 static const char UINT32[]="UInt32";
333 static const char FLOAT32[]="Float32";
334 static const char FLOAT64[]="Float64";
335 static const char STRING[]="String";
336
337 // I got different cases from hntdefs.h.
338 switch (type) {
339
340 case DFNT_CHAR:
341 return STRING;
342
343 case DFNT_UCHAR8:
344 return STRING;
345
346 case DFNT_UINT8:
347 return BYTE;
348
349 case DFNT_INT8:
350// ADD the macro
351 {
352#ifndef SIGNED_BYTE_TO_INT32
353 return BYTE;
354#else
355 return INT32;
356#endif
357 }
358 case DFNT_UINT16:
359 return UINT16;
360
361 case DFNT_INT16:
362 return INT16;
363
364 case DFNT_INT32:
365 return INT32;
366
367 case DFNT_UINT32:
368 return UINT32;
369
370 case DFNT_FLOAT:
371 return FLOAT32;
372
373 case DFNT_DOUBLE:
374 return FLOAT64;
375
376 default:
377 return UNKNOWN;
378 }
379
380}
381
382// Obtain HDF4 datatype size.
383short
385{
386
387 switch (type) {
388
389 case DFNT_CHAR:
390 return sizeof(char);
391
392 case DFNT_UCHAR8:
393 return sizeof(unsigned char);
394
395 case DFNT_UINT8:
396 return sizeof(unsigned char);
397
398 case DFNT_INT8:
399// ADD the macro
400 {
401#ifndef SIGNED_BYTE_TO_INT32
402 return sizeof(char);
403#else
404 return sizeof(int);
405#endif
406 }
407 case DFNT_UINT16:
408 return sizeof(unsigned short);
409
410 case DFNT_INT16:
411 return sizeof(short);
412
413 case DFNT_INT32:
414 return sizeof(int);
415
416 case DFNT_UINT32:
417 return sizeof(unsigned int);
418
419 case DFNT_FLOAT:
420 return sizeof(float);
421
422 case DFNT_DOUBLE:
423 return sizeof(double);
424
425 default:
426 return -1;
427 }
428
429}
430// Subset of latitude and longitude to follow the parameters from the DAP expression constraint
431// Somehow this function doesn't work. Now it is not used. Still keep it here for the future investigation.
432template < typename T >
433void HDFCFUtil::LatLon2DSubset (T * outlatlon,
434 int majordim,
435 int minordim,
436 T * latlon,
437 const int32 * offset,
438 const int32 * count,
439 const int32 * step)
440{
441
442 T (*templatlonptr)[majordim][minordim] = (T *) latlon;
443 int i;
444 int j;
445 int k;
446
447 // do subsetting
448 // Find the correct index
449 int dim0count = count[0];
450 int dim1count = count[1];
451 vector<int> dim0index(dim0count);
452 vector<int> dim1index(dim1count);
453
454 for (i = 0; i < count[0]; i++) // count[0] is the least changing dimension
455 dim0index[i] = offset[0] + i * step[0];
456
457
458 for (j = 0; j < count[1]; j++)
459 dim1index[j] = offset[1] + j * step[1];
460
461 // Now assign the subsetting data
462 k = 0;
463
464 for (i = 0; i < count[0]; i++) {
465 for (j = 0; j < count[1]; j++) {
466 outlatlon[k] = (*templatlonptr)[dim0index[i]][dim1index[j]];
467 k++;
468
469 }
470 }
471}
472
473// CF requires the _FillValue attribute datatype is the same as the corresponding field datatype.
474// For some NASA files, this is not true.
475// So we need to check if the _FillValue's datatype is the same as the attribute's.
476// If not, we need to correct them.
477void HDFCFUtil::correct_fvalue_type(AttrTable *at,int32 dtype) {
478
479 AttrTable::Attr_iter it = at->attr_begin();
480 bool find_fvalue = false;
481 while (it!=at->attr_end() && false==find_fvalue) {
482 if (at->get_name(it) =="_FillValue")
483 {
484 find_fvalue = true;
485 string fillvalue ="";
486 string fillvalue_type ="";
487 if((*at->get_attr_vector(it)).size() !=1)
488 throw InternalErr(__FILE__,__LINE__,"The number of _FillValue must be 1.");
489 fillvalue = (*at->get_attr_vector(it)->begin());
490 fillvalue_type = at->get_type(it);
491 string var_type = HDFCFUtil::print_type(dtype);
492
493 if(fillvalue_type != var_type){
494
495 at->del_attr("_FillValue");
496
497 if (fillvalue_type == "String") {
498
499 // String fillvalue is always represented as /+octal numbers when its type is forced to
500 // change to string(check HFRHANDLER-223). So we have to retrieve it back.
501 fillvalue = escattr_fvalue(fillvalue);
502 if(fillvalue.size() >1) {
503
504 long int fillvalue_int = 0;
505 vector<char> fillvalue_temp(fillvalue.size());
506 char *pEnd;
507 fillvalue_int = strtol((fillvalue.substr(1)).c_str(),&pEnd,8);
508 stringstream convert_str;
509 convert_str << fillvalue_int;
510 at->append_attr("_FillValue",var_type,convert_str.str());
511 }
512 else {
513
514 // If somehow the fillvalue type is DFNT_CHAR or DFNT_UCHAR, and the size is 1,
515 // that means the fillvalue type is wrongly defined, we treat as a 8-bit integer number.
516 // Note, the code will only assume the value ranges from 0 to 255.(JIRA HFRHANDLER-248).
517 // KY 2014-04-24
518
519 short fillvalue_int = fillvalue.at(0);
520
521 stringstream convert_str;
522 convert_str << fillvalue_int;
523 at->append_attr("_FillValue",var_type,convert_str.str());
524 }
525 }
526
527 else
528 at->append_attr("_FillValue",var_type,fillvalue);
529 }
530 }
531 it++;
532 }
533
534}
535
536// CF requires the scale_factor and add_offset attribute datatypes must be the same as the corresponding field datatype.
537// For some NASA files, this is not true.
538// So we need to check if the _FillValue's datatype is the same as the attribute's.
539// If not, we need to correct them.
541
542 AttrTable::Attr_iter it = at->attr_begin();
543 bool find_scale = false;
544 bool find_offset = false;
545
546 // Declare scale_factor,add_offset, fillvalue and valid_range type in string format.
547 string scale_factor_type;
548 string add_offset_type;
549 string scale_factor_value;
550 string add_offset_value;
551
552 while (it!=at->attr_end() &&((find_scale!=true) ||(find_offset!=true))) {
553 if (at->get_name(it) =="scale_factor")
554 {
555 find_scale = true;
556 scale_factor_value = (*at->get_attr_vector(it)->begin());
557 scale_factor_type = at->get_type(it);
558 }
559
560 if(at->get_name(it)=="add_offset")
561 {
562 find_offset = true;
563 add_offset_value = (*at->get_attr_vector(it)->begin());
564 add_offset_type = at->get_type(it);
565 }
566
567 it++;
568 }
569
570 // Change offset type to the scale type
571 if((true==find_scale) && (true==find_offset)) {
572 if(scale_factor_type != add_offset_type) {
573 at->del_attr("add_offset");
574 at->append_attr("add_offset",scale_factor_type,add_offset_value);
575 }
576 }
577}
578
579
580#ifdef USE_HDFEOS2_LIB
581
582// For MODIS (confirmed by level 1B) products, values between 65500(MIN_NON_SCALE_SPECIAL_VALUE)
583// and 65535(MAX_NON_SCALE_SPECIAL_VALUE) are treated as
584// special values. These values represent non-physical data values caused by various failures.
585// For example, 65533 represents "when Detector is saturated".
586bool HDFCFUtil::is_special_value(int32 dtype, float fillvalue, float realvalue) {
587
588 bool ret_value = false;
589
590 if (DFNT_UINT16 == dtype) {
591
592 auto fillvalue_int = (int)fillvalue;
593 if (MAX_NON_SCALE_SPECIAL_VALUE == fillvalue_int) {
594 auto realvalue_int = (int)realvalue;
595 if (realvalue_int <= MAX_NON_SCALE_SPECIAL_VALUE && realvalue_int >=MIN_NON_SCALE_SPECIAL_VALUE)
596 ret_value = true;
597 }
598 }
599
600 return ret_value;
601
602}
603
604// Check if the MODIS file has dimension map and return the number of dimension maps
605// Note: This routine is only applied to a MODIS geo-location file when the corresponding
606// MODIS swath uses dimension maps and has the MODIS geo-location file under the same
607// file directory. This is also restricted by turning on H4.EnableCheckMODISGeoFile to be true(file h4.conf.in).
608// By default, this key is turned off. Also this function is only used once in one service. So it won't
609// affect performance. KY 2014-02-18
610int HDFCFUtil::check_geofile_dimmap(const string & geofilename) {
611
612 int32 fileid = SWopen(const_cast<char*>(geofilename.c_str()),DFACC_READ);
613 if (fileid < 0)
614 return -1;
615 string swathname = "MODIS_Swath_Type_GEO";
616 int32 datasetid = SWattach(fileid,const_cast<char*>(swathname.c_str()));
617 if (datasetid < 0) {
618 SWclose(fileid);
619 return -1;
620 }
621
622 int32 nummaps = 0;
623 int32 bufsize;
624
625 // Obtain number of dimension maps and the buffer size.
626 if ((nummaps = SWnentries(datasetid, HDFE_NENTMAP, &bufsize)) == -1) {
627 SWdetach(datasetid);
628 SWclose(fileid);
629 return -1;
630 }
631
632 SWdetach(datasetid);
633 SWclose(fileid);
634 return nummaps;
635
636}
637
638// Check if we need to change the datatype for MODIS fields. The datatype needs to be changed
639// mainly because of non-CF scale and offset rules. To avoid violating CF conventions, we apply
640// the non-CF MODIS scale and offset rule to MODIS data. So the final data type may be different
641// than the original one due to this operation. For example, the original datatype may be int16.
642// After applying the scale/offset rule, the datatype may become float32.
643// The following are useful notes about MODIS SCALE OFFSET HANDLING
644// Note: MODIS Scale and offset handling needs to re-organized. But it may take big efforts.
645// Instead, I remove the global variable mtype, and _das; move the old calculate_dtype code
646// back to HDFEOS2.cc. The code is a little better organized. If possible, we may think to overhaul
647// the handling of MODIS scale-offset part. KY 2012-6-19
648//
649bool HDFCFUtil::change_data_type(DAS & das, SOType scaletype, const string &new_field_name)
650{
651
652 AttrTable *at = das.get_table(new_field_name);
653
654 // The following codes do these:
655 // For MODIS level 1B(using the swath name), check radiance,reflectance etc.
656 // If the DISABLESCALE key is true, no need to check scale and offset for other types.
657 // Otherwise, continue checking the scale and offset names.
658 // KY 2013-12-13
659
660 if(scaletype!=SOType::DEFAULT_CF_EQU && at!=nullptr)
661 {
662 AttrTable::Attr_iter it = at->attr_begin();
663 string scale_factor_value="";
664 string add_offset_value="0";
665 string radiance_scales_value="";
666 string radiance_offsets_value="";
667 string reflectance_scales_value="";
668 string reflectance_offsets_value="";
669 string scale_factor_type;
670 string add_offset_type;
671
672 while (it!=at->attr_end())
673 {
674 if(at->get_name(it)=="radiance_scales")
675 radiance_scales_value = *(at->get_attr_vector(it)->begin());
676 if(at->get_name(it)=="radiance_offsets")
677 radiance_offsets_value = *(at->get_attr_vector(it)->begin());
678 if(at->get_name(it)=="reflectance_scales")
679 reflectance_scales_value = *(at->get_attr_vector(it)->begin());
680 if(at->get_name(it)=="reflectance_offsets")
681 reflectance_offsets_value = *(at->get_attr_vector(it)->begin());
682
683 // Ideally may just check if the attribute name is scale_factor.
684 // But don't know if some products use attribut name like "scale_factor "
685 // So now just filter out the attribute scale_factor_err. KY 2012-09-20
686 if(at->get_name(it).find("scale_factor")!=string::npos){
687 string temp_attr_name = at->get_name(it);
688 if (temp_attr_name != "scale_factor_err") {
689 scale_factor_value = *(at->get_attr_vector(it)->begin());
690 scale_factor_type = at->get_type(it);
691 }
692 }
693 if(at->get_name(it).find("add_offset")!=string::npos)
694 {
695 string temp_attr_name = at->get_name(it);
696 if (temp_attr_name !="add_offset_err") {
697 add_offset_value = *(at->get_attr_vector(it)->begin());
698 add_offset_type = at->get_type(it);
699 }
700 }
701 it++;
702 }
703
704 if((radiance_scales_value.size()!=0 && radiance_offsets_value.size()!=0)
705 || (reflectance_scales_value.size()!=0 && reflectance_offsets_value.size()!=0))
706 return true;
707
708 if(scale_factor_value.size()!=0)
709 {
710 if(!(atof(scale_factor_value.c_str())==1 && atof(add_offset_value.c_str())==0))
711 return true;
712 }
713 }
714
715 return false;
716}
717
718// Obtain the MODIS swath dimension map info.
719void HDFCFUtil::obtain_dimmap_info(const string& filename,HDFEOS2::Dataset*dataset,
720 vector<struct dimmap_entry> & dimmaps,
721 string & modis_geofilename, bool& geofile_has_dimmap) {
722
723
724 auto sw = static_cast<HDFEOS2::SwathDataset *>(dataset);
725 const vector<HDFEOS2::SwathDataset::DimensionMap*>& origdimmaps = sw->getDimensionMaps();
726 struct dimmap_entry tempdimmap;
727
728 // if having dimension maps, we need to retrieve the dimension map info.
729 for(const auto & origdimmap:origdimmaps){
730 tempdimmap.geodim = origdimmap->getGeoDimension();
731 tempdimmap.datadim = origdimmap->getDataDimension();
732 tempdimmap.offset = origdimmap->getOffset();
733 tempdimmap.inc = origdimmap->getIncrement();
734 dimmaps.push_back(tempdimmap);
735 }
736
737 // Only when there is dimension map, we need to consider the additional MODIS geolocation files.
738 // Will check if the check modis_geo_location file key is turned on.
739 if((origdimmaps.empty() != false) && (true == HDF4RequestHandler::get_enable_check_modis_geo_file()) ) {
740
741 // Has to use C-style since basename and dirname are not C++ routines.
742 char*tempcstr;
743 tempcstr = new char [filename.size()+1];
744 strncpy (tempcstr,filename.c_str(),filename.size());
745 string basefilename = basename(tempcstr);
746 string dirfilename = dirname(tempcstr);
747 delete [] tempcstr;
748
749 // If the current file is a MOD03 or a MYD03 file, we don't need to check the extra MODIS geolocation file at all.
750 bool is_modis_geofile = false;
751 if(basefilename.size() >5) {
752 if((0 == basefilename.compare(0,5,"MOD03")) || (0 == basefilename.compare(0,5,"MYD03")))
753 is_modis_geofile = true;
754 }
755
756 // This part is implemented specifically for supporting MODIS dimension map data.
757 // MODIS Aqua Swath dimension map geolocation file always starts with MYD03
758 // MODIS Terra Swath dimension map geolocation file always starts with MOD03
759 // We will check the first three characters to see if the dimension map geolocation file exists.
760 // An example MODIS swath filename is MOD05_L2.A2008120.0000.005.2008121182723.hdf
761 // An example MODIS geo-location file name is MOD03.A2008120.0000.005.2010003235220.hdf
762 // Notice that the "A2008120.0000" in the middle of the name is the "Acquistion Date" of the data
763 // So the geo-location file name should have exactly the same string. We will use this
764 // string to identify if a MODIS geo-location file exists or not.
765 // Note the string size is 14(.A2008120.0000).
766 // More information of naming convention, check http://modis-atmos.gsfc.nasa.gov/products_filename.html
767 // KY 2010-5-10
768
769
770 // Obtain string "MYD" or "MOD"
771 // Here we need to consider when MOD03 or MYD03 use the dimension map.
772 if ((false == is_modis_geofile) && (basefilename.size() >3)) {
773
774 string fnameprefix = basefilename.substr(0,3);
775
776 if(fnameprefix == "MYD" || fnameprefix =="MOD") {
777 size_t fnamemidpos = basefilename.find(".A");
778 if(fnamemidpos != string::npos) {
779 string fnamemiddle = basefilename.substr(fnamemidpos,14);
780 if(fnamemiddle.size()==14) {
781 string geofnameprefix = fnameprefix+"03";
782 // geofnamefp will be something like "MOD03.A2008120.0000"
783 string geofnamefp = geofnameprefix + fnamemiddle;
784 DIR *dirp;
785 struct dirent* dirs;
786
787 // Go through the directory to see if we have the corresponding MODIS geolocation file
788 dirp = opendir(dirfilename.c_str());
789 if (nullptr == dirp)
790 throw InternalErr(__FILE__,__LINE__,"opendir fails.");
791
792 while ((dirs = readdir(dirp))!= nullptr){
793 if(strncmp(dirs->d_name,geofnamefp.c_str(),geofnamefp.size())==0){
794 modis_geofilename = dirfilename + "/"+ dirs->d_name;
795 int num_dimmap = HDFCFUtil::check_geofile_dimmap(modis_geofilename);
796 if (num_dimmap < 0) {
797 closedir(dirp);
798 throw InternalErr(__FILE__,__LINE__,"this file is not a MODIS geolocation file.");
799 }
800 geofile_has_dimmap = (num_dimmap >0)?true:false;
801 break;
802 }
803 }
804 closedir(dirp);
805 }
806 }
807 }
808 }
809 }
810}
811
812// This is for the case that the separate MODIS geo-location file is used.
813// Some geolocation names at the MODIS data file are not consistent with
814// the names in the MODIS geo-location file. So need to correct them.
815bool HDFCFUtil::is_modis_dimmap_nonll_field(string & fieldname) {
816
817 bool modis_dimmap_nonll_field = false;
818 vector<string> modis_dimmap_nonll_fieldlist;
819
820 modis_dimmap_nonll_fieldlist.emplace_back("Height");
821 modis_dimmap_nonll_fieldlist.emplace_back("SensorZenith");
822 modis_dimmap_nonll_fieldlist.emplace_back("SensorAzimuth");
823 modis_dimmap_nonll_fieldlist.emplace_back("Range");
824 modis_dimmap_nonll_fieldlist.emplace_back("SolarZenith");
825 modis_dimmap_nonll_fieldlist.emplace_back("SolarAzimuth");
826 modis_dimmap_nonll_fieldlist.emplace_back("Land/SeaMask");
827 modis_dimmap_nonll_fieldlist.emplace_back("gflags");
828 modis_dimmap_nonll_fieldlist.emplace_back("Solar_Zenith");
829 modis_dimmap_nonll_fieldlist.emplace_back("Solar_Azimuth");
830 modis_dimmap_nonll_fieldlist.emplace_back("Sensor_Azimuth");
831 modis_dimmap_nonll_fieldlist.emplace_back("Sensor_Zenith");
832
833 map<string,string>modis_field_to_geofile_field;
834 map<string,string>::iterator itmap;
835 modis_field_to_geofile_field["Solar_Zenith"] = "SolarZenith";
836 modis_field_to_geofile_field["Solar_Azimuth"] = "SolarAzimuth";
837 modis_field_to_geofile_field["Sensor_Zenith"] = "SensorZenith";
838 modis_field_to_geofile_field["Solar_Azimuth"] = "SolarAzimuth";
839
840 for (const auto & modis_dimmap_nonll_f:modis_dimmap_nonll_fieldlist) {
841
842 if (fieldname == modis_dimmap_nonll_f) {
843 itmap = modis_field_to_geofile_field.find(fieldname);
844 if (itmap !=modis_field_to_geofile_field.end())
845 fieldname = itmap->second;
846 modis_dimmap_nonll_field = true;
847 break;
848 }
849 }
850
851 return modis_dimmap_nonll_field;
852}
853
854void HDFCFUtil::add_scale_offset_attrs(AttrTable*at,const std::string& s_type, float svalue_f, double svalue_d, bool add_offset_found,
855 const std::string& o_type, float ovalue_f, double ovalue_d) {
856 at->del_attr("scale_factor");
857 string print_rep;
858 if(s_type!="Float64") {
859 print_rep = HDFCFUtil::print_attr(DFNT_FLOAT32,0,(void*)(&svalue_f));
860 at->append_attr("scale_factor", "Float32", print_rep);
861 }
862 else {
863 print_rep = HDFCFUtil::print_attr(DFNT_FLOAT64,0,(void*)(&svalue_d));
864 at->append_attr("scale_factor", "Float64", print_rep);
865 }
866
867 if (true == add_offset_found) {
868 at->del_attr("add_offset");
869 if(o_type!="Float64") {
870 print_rep = HDFCFUtil::print_attr(DFNT_FLOAT32,0,(void*)(&ovalue_f));
871 at->append_attr("add_offset", "Float32", print_rep);
872 }
873 else {
874 print_rep = HDFCFUtil::print_attr(DFNT_FLOAT64,0,(void*)(&ovalue_d));
875 at->append_attr("add_offset", "Float64", print_rep);
876 }
877 }
878}
879
880void HDFCFUtil::add_scale_str_offset_attrs(AttrTable*at,const std::string& s_type, const std::string& s_value_str, bool add_offset_found,
881 const std::string& o_type, float ovalue_f, double ovalue_d) {
882 at->del_attr("scale_factor");
883 string print_rep;
884 if(s_type!="Float64")
885 at->append_attr("scale_factor", "Float32", s_value_str);
886 else
887 at->append_attr("scale_factor", "Float64", s_value_str);
888
889 if (true == add_offset_found) {
890 at->del_attr("add_offset");
891 if(o_type!="Float64") {
892 print_rep = HDFCFUtil::print_attr(DFNT_FLOAT32,0,(void*)(&ovalue_f));
893 at->append_attr("add_offset", "Float32", print_rep);
894 }
895 else {
896 print_rep = HDFCFUtil::print_attr(DFNT_FLOAT64,0,(void*)(&ovalue_d));
897 at->append_attr("add_offset", "Float64", print_rep);
898 }
899 }
900}
902void HDFCFUtil::handle_modis_special_attrs_disable_scale_comp(AttrTable *at,
903 const string &filename,
904 bool is_grid,
905 const string & newfname,
906 SOType sotype){
907
908 // Declare scale_factor,add_offset, fillvalue and valid_range type in string format.
909 string scale_factor_type;
910 string add_offset_type;
911
912 // Scale and offset values
913 string scale_factor_value="";
914 float orig_scale_value_float = 1;
915 double orig_scale_value_double = 1;
916 string add_offset_value="0";
917 float orig_offset_value_float = 0;
918 double orig_offset_value_double = 0;
919 bool add_offset_found = false;
920
921
922 // Go through all attributes to find scale_factor,add_offset,_FillValue,valid_range
923 // Number_Type information
924 AttrTable::Attr_iter it = at->attr_begin();
925 while (it!=at->attr_end())
926 {
927 if(at->get_name(it)=="scale_factor")
928 {
929 scale_factor_value = (*at->get_attr_vector(it)->begin());
930 scale_factor_type = at->get_type(it);
931 if(scale_factor_type =="Float64")
932 orig_scale_value_double=atof(scale_factor_value.c_str());
933 else
934 orig_scale_value_float = (float)(atof(scale_factor_value.c_str()));
935 }
936
937 if(at->get_name(it)=="add_offset")
938 {
939 add_offset_value = (*at->get_attr_vector(it)->begin());
940 add_offset_type = at->get_type(it);
941
942 if(add_offset_type == "Float64")
943 orig_offset_value_double = atof(add_offset_value.c_str());
944 else
945 orig_offset_value_float = (float)(atof(add_offset_value.c_str()));
946 add_offset_found = true;
947 }
948
949 it++;
950 }
951
952 // According to our observations, it seems that MODIS products ALWAYS use the "scale" factor to
953 // make bigger values smaller.
954 // So for MODIS_MUL_SCALE products, if the scale of some variable is greater than 1,
955 // it means that for this variable, the MODIS type for this variable may be MODIS_DIV_SCALE.
956 // For the similar logic, we may need to change MODIS_DIV_SCALE to MODIS_MUL_SCALE and MODIS_EQ_SCALE
957 // to MODIS_DIV_SCALE.
958 // We indeed find such a case. HDF-EOS2 Grid MODIS_Grid_1km_2D of MOD(or MYD)09GA is a MODIS_EQ_SCALE.
959 // However,
960 // the scale_factor of the variable Range_1 in the MOD09GA product is 25. According to our observation,
961 // this variable should be MODIS_DIV_SCALE.We verify this is true according to MODIS 09 product document
962 // http://modis-sr.ltdri.org/products/MOD09_UserGuide_v1_3.pdf.
963 // Since this conclusion is based on our observation, we would like to add a BESlog to detect if we find
964 // the similar cases so that we can verify with the corresponding product documents to see if this is true.
965 // More information,
966 // We just verified with the data producer, the scale_factor for Range_1 and Range_c is 25 but the
967 // equation is still multiplication instead of division. So we have to make this as a special case that
968 // we don't want to change the scale and offset settings.
969 // KY 2014-01-13
970
971
972 if(scale_factor_value.size()!=0) {
973 if (SOType::MODIS_EQ_SCALE == sotype || SOType::MODIS_MUL_SCALE == sotype) {
974 if (orig_scale_value_float > 1 || orig_scale_value_double >1) {
975
976 bool need_change_scale = true;
977 if(true == is_grid) {
978 if ((filename.size() >5) && ((filename.compare(0,5,"MOD09") == 0)|| (filename.compare(0,5,"MYD09")==0))) {
979 if ((newfname.size() >5) && newfname.find("Range") != string::npos)
980 need_change_scale = false;
981 }
982 else if((filename.size() >7)&&((filename.compare(0,7,"MOD16A2") == 0)|| (filename.compare(0,7,"MYD16A2")==0) ||
983 (filename.compare(0,7,"MOD16A3")==0) || (filename.compare(0,7,"MYD16A3")==0)))
984 need_change_scale = false;
985 }
986 if(true == need_change_scale) {
987 sotype = SOType::MODIS_DIV_SCALE;
988 INFO_LOG("The field " + newfname + " scale factor is "+ scale_factor_value
989 + " But the original scale factor type is MODIS_MUL_SCALE or MODIS_EQ_SCALE. "
990 + " Now change it to MODIS_DIV_SCALE. ");
991 }
992 }
993 }
994
995 if (SOType::MODIS_DIV_SCALE == sotype) {
996 if (orig_scale_value_float < 1 || orig_scale_value_double<1) {
997 sotype = SOType::MODIS_MUL_SCALE;
998 INFO_LOG("The field " + newfname + " scale factor is "+ scale_factor_value
999 + " But the original scale factor type is MODIS_DIV_SCALE. "
1000 + " Now change it to MODIS_MUL_SCALE. ");
1001 }
1002 }
1003
1004
1005 if ((SOType::MODIS_MUL_SCALE == sotype) &&(true == add_offset_found)) {
1006
1007 float new_offset_value_float=0;
1008 double new_offset_value_double=0;
1009 if(add_offset_type!="Float64")
1010 new_offset_value_float = (orig_offset_value_float ==0)?0:(-1 * orig_offset_value_float *orig_scale_value_float);
1011 else
1012 new_offset_value_double = (orig_offset_value_double ==0)?0:(-1 * orig_offset_value_double *orig_scale_value_double);
1013
1014 // May need to use another function to avoid the rounding error by atof.
1015 add_scale_str_offset_attrs(at,scale_factor_type,scale_factor_value,add_offset_found,
1016 add_offset_type,new_offset_value_float,new_offset_value_double);
1017
1018 }
1019
1020 if (SOType::MODIS_DIV_SCALE == sotype) {
1021
1022 float new_scale_value_float=1;
1023 double new_scale_value_double=1;
1024 float new_offset_value_float=0;
1025 double new_offset_value_double=0;
1026
1027
1028 if(scale_factor_type !="Float64") {
1029 new_scale_value_float = 1.0f/orig_scale_value_float;
1030 if (true == add_offset_found) {
1031 if(add_offset_type !="Float64")
1032 new_offset_value_float = (orig_offset_value_float==0)?0:(-1 * orig_offset_value_float *new_scale_value_float);
1033 else
1034 new_offset_value_double = (orig_offset_value_double==0)?0:(-1 * orig_offset_value_double *new_scale_value_float);
1035 }
1036 }
1037
1038 else {
1039 new_scale_value_double = 1.0/orig_scale_value_double;
1040 if (true == add_offset_found) {
1041 if(add_offset_type !="Float64")
1042 new_offset_value_float = (orig_offset_value_float==0)?0:(-1.0f * orig_offset_value_float *((float)new_scale_value_double));
1043 else
1044 new_offset_value_double = (orig_offset_value_double==0)?0:(-1 * orig_offset_value_double *new_scale_value_double);
1045 }
1046 }
1047
1048 add_scale_offset_attrs(at,scale_factor_type,new_scale_value_float,new_scale_value_double,add_offset_found,
1049 add_offset_type,new_offset_value_float,new_offset_value_double);
1050
1051 }
1052
1053 }
1054
1055}
1056
1057// These routines will handle scale_factor,add_offset,valid_min,valid_max and other attributes
1058// such as Number_Type to make sure the CF is followed.
1059// For example, For the case that the scale and offset rule doesn't follow CF, the scale_factor and add_offset attributes are renamed
1060// to orig_scale_factor and orig_add_offset to keep the original field info.
1061// Note: This routine should only be called when MODIS Scale and offset rules don't follow CF.
1062// Input parameters:
1063// AttrTable at - DAS attribute table
1064// string newfname - field name: this is used to identify special MODIS level 1B emissive and refSB fields
1065// SOType sotype - MODIS scale and offset type
1066// bool gridname_change_valid_range - Flag to check if this is the special MODIS VIP product
1067// bool changedtype - Flag to check if the datatype of this field needs to be changed
1068// bool change_fvtype - Flag to check if the attribute _FillValue needs to change to data type
1069
1071// Divide this function into smaller functions:
1072//
1073void HDFCFUtil::handle_modis_special_attrs(AttrTable *at, const string & filename,
1074 bool is_grid,const string & newfname,
1075 SOType sotype, bool gridname_change_valid_range,
1076 bool changedtype, bool & change_fvtype){
1077
1078 // Declare scale_factor,add_offset, fillvalue and valid_range type in string format.
1079 string scale_factor_type;
1080 string add_offset_type;
1081 string fillvalue_type;
1082 string valid_range_type;
1083
1084
1085 // Scale and offset values
1086 string scale_factor_value="";
1087 float orig_scale_value = 1;
1088 string add_offset_value="0";
1089 float orig_offset_value = 0;
1090 bool add_offset_found = false;
1091
1092 // Fillvalue
1093 string fillvalue="";
1094
1095 // Valid range value
1096 string valid_range_value="";
1097
1098 bool has_valid_range = false;
1099
1100 // We may need to change valid_range to valid_min and valid_max. Initialize them here.
1101 float orig_valid_min = 0;
1102 float orig_valid_max = 0;
1103
1104 // Number_Type also needs to be adjusted when datatype is changed
1105 string number_type_value="";
1106 string number_type_dap_type="";
1107
1108 // Go through all attributes to find scale_factor,add_offset,_FillValue,valid_range
1109 // Number_Type information
1110 AttrTable::Attr_iter it = at->attr_begin();
1111 while (it!=at->attr_end())
1112 {
1113 if(at->get_name(it)=="scale_factor")
1114 {
1115 scale_factor_value = (*at->get_attr_vector(it)->begin());
1116 orig_scale_value = (float)(atof(scale_factor_value.c_str()));
1117 scale_factor_type = at->get_type(it);
1118 }
1119
1120 if(at->get_name(it)=="add_offset")
1121 {
1122 add_offset_value = (*at->get_attr_vector(it)->begin());
1123 orig_offset_value = (float)(atof(add_offset_value.c_str()));
1124 add_offset_type = at->get_type(it);
1125 add_offset_found = true;
1126 }
1127
1128 if(at->get_name(it)=="_FillValue")
1129 {
1130 fillvalue = (*at->get_attr_vector(it)->begin());
1131 fillvalue_type = at->get_type(it);
1132 }
1133
1134 if(at->get_name(it)=="valid_range")
1135 {
1136 vector<string> *avalue = at->get_attr_vector(it);
1137 auto ait = avalue->begin();
1138 while(ait!=avalue->end())
1139 {
1140 valid_range_value += *ait;
1141 ait++;
1142 if(ait!=avalue->end())
1143 valid_range_value += ", ";
1144 }
1145 valid_range_type = at->get_type(it);
1146 if (false == gridname_change_valid_range) {
1147 orig_valid_min = (float)(atof((avalue->at(0)).c_str()));
1148 orig_valid_max = (float)(atof((avalue->at(1)).c_str()));
1149 }
1150 has_valid_range = true;
1151 }
1152
1153 if(true == changedtype && (at->get_name(it)=="Number_Type"))
1154 {
1155 number_type_value = (*at->get_attr_vector(it)->begin());
1156 number_type_dap_type= at->get_type(it);
1157 }
1158
1159 it++;
1160 }
1161
1162 // Rename scale_factor and add_offset attribute names. Otherwise, they will be
1163 // misused by CF tools to generate wrong data values based on the CF scale and offset rule.
1164 if(scale_factor_value.size()!=0)
1165 {
1166 if(!(atof(scale_factor_value.c_str())==1 && atof(add_offset_value.c_str())==0)) //Rename them.
1167 {
1168 at->del_attr("scale_factor");
1169 at->append_attr("orig_scale_factor", scale_factor_type, scale_factor_value);
1170 if(add_offset_found)
1171 {
1172 at->del_attr("add_offset");
1173 at->append_attr("orig_add_offset", add_offset_type, add_offset_value);
1174 }
1175 }
1176 }
1177
1178 // Change _FillValue datatype
1179 if(true == changedtype && fillvalue.size()!=0 && fillvalue_type!="Float32" && fillvalue_type!="Float64")
1180 {
1181 change_fvtype = true;
1182 at->del_attr("_FillValue");
1183 at->append_attr("_FillValue", "Float32", fillvalue);
1184 }
1185
1186 float valid_max = 0;
1187 float valid_min = 0;
1188
1189 it = at->attr_begin();
1190 bool handle_modis_l1b = false;
1191
1192 // MODIS level 1B's Emissive and RefSB fields' scale_factor and add_offset
1193 // get changed according to different vertical levels.
1194 // So we need to handle them specifically.
1195 if (sotype == SOType::MODIS_MUL_SCALE && true ==changedtype) {
1196
1197 // Emissive should be at the end of the field name such as "..._Emissive"
1198 string emissive_str = "Emissive";
1199 string RefSB_str = "RefSB";
1200 bool is_emissive_field = false;
1201 bool is_refsb_field = false;
1202 if(newfname.find(emissive_str)!=string::npos) {
1203 if(0 == newfname.compare(newfname.size()-emissive_str.size(),emissive_str.size(),emissive_str))
1204 is_emissive_field = true;
1205 }
1206
1207 if(newfname.find(RefSB_str)!=string::npos) {
1208 if(0 == newfname.compare(newfname.size()-RefSB_str.size(),RefSB_str.size(),RefSB_str))
1209 is_refsb_field = true;
1210 }
1211
1212 // We will calculate the maximum and minimum scales and offsets within all the vertical levels.
1213 if ((true == is_emissive_field) || (true== is_refsb_field)){
1214
1215 float scale_max = 0;
1216 float scale_min = 100000;
1217
1218 float offset_max = 0;
1219 float offset_min = 0;
1220
1221 float temp_var_val = 0;
1222
1223 string orig_long_name_value;
1224 string modify_long_name_value;
1225 string str_removed_from_long_name=" Scaled Integers";
1226 string radiance_units_value;
1227
1228 while (it!=at->attr_end())
1229 {
1230 // Radiance field(Emissive field)
1231 if(true == is_emissive_field) {
1232
1233 if ("radiance_scales" == (at->get_name(it))) {
1234 vector<string> *avalue = at->get_attr_vector(it);
1235 for (vector<string>::const_iterator ait = avalue->begin();ait !=avalue->end();++ait) {
1236 temp_var_val = (float)(atof((*ait).c_str()));
1237 if (temp_var_val > scale_max)
1238 scale_max = temp_var_val;
1239 if (temp_var_val < scale_min)
1240 scale_min = temp_var_val;
1241 }
1242 }
1243
1244 if ("radiance_offsets" == (at->get_name(it))) {
1245 vector<string> *avalue = at->get_attr_vector(it);
1246 for (vector<string>::const_iterator ait = avalue->begin();ait !=avalue->end();++ait) {
1247 temp_var_val = (float)(atof((*ait).c_str()));
1248 if (temp_var_val > offset_max)
1249 offset_max = temp_var_val;
1250 if (temp_var_val < scale_min)
1251 offset_min = temp_var_val;
1252 }
1253 }
1254
1255 if ("long_name" == (at->get_name(it))) {
1256 orig_long_name_value = (*at->get_attr_vector(it)->begin());
1257 if (orig_long_name_value.find(str_removed_from_long_name)!=string::npos) {
1258 if(0 == orig_long_name_value.compare(orig_long_name_value.size()-str_removed_from_long_name.size(),
1259 str_removed_from_long_name.size(),str_removed_from_long_name)) {
1260
1261 modify_long_name_value =
1262 orig_long_name_value.substr(0,orig_long_name_value.size()-str_removed_from_long_name.size());
1263 at->del_attr("long_name");
1264 at->append_attr("long_name","String",modify_long_name_value);
1265 at->append_attr("orig_long_name","String",orig_long_name_value);
1266 }
1267 }
1268 }
1269
1270 if ("radiance_units" == (at->get_name(it)))
1271 radiance_units_value = (*at->get_attr_vector(it)->begin());
1272 }
1273
1274 if (true == is_refsb_field) {
1275 if ("reflectance_scales" == (at->get_name(it))) {
1276 vector<string> *avalue = at->get_attr_vector(it);
1277 for (vector<string>::const_iterator ait = avalue->begin();ait !=avalue->end();++ait) {
1278 temp_var_val = (float)(atof((*ait).c_str()));
1279 if (temp_var_val > scale_max)
1280 scale_max = temp_var_val;
1281 if (temp_var_val < scale_min)
1282 scale_min = temp_var_val;
1283 }
1284 }
1285
1286 if ("reflectance_offsets" == (at->get_name(it))) {
1287
1288 vector<string> *avalue = at->get_attr_vector(it);
1289 for (vector<string>::const_iterator ait = avalue->begin();ait !=avalue->end();++ait) {
1290 temp_var_val = (float)(atof((*ait).c_str()));
1291 if (temp_var_val > offset_max)
1292 offset_max = temp_var_val;
1293 if (temp_var_val < scale_min)
1294 offset_min = temp_var_val;
1295 }
1296 }
1297
1298 if ("long_name" == (at->get_name(it))) {
1299 orig_long_name_value = (*at->get_attr_vector(it)->begin());
1300 if (orig_long_name_value.find(str_removed_from_long_name)!=string::npos) {
1301 if(0 == orig_long_name_value.compare(orig_long_name_value.size()-str_removed_from_long_name.size(),
1302 str_removed_from_long_name.size(),str_removed_from_long_name)) {
1303
1304 modify_long_name_value =
1305 orig_long_name_value.substr(0,orig_long_name_value.size()-str_removed_from_long_name.size());
1306 at->del_attr("long_name");
1307 at->append_attr("long_name","String",modify_long_name_value);
1308 at->append_attr("orig_long_name","String",orig_long_name_value);
1309 }
1310 }
1311 }
1312 }
1313 it++;
1314 }
1315
1316 // Calculate the final valid_max and valid_min.
1317 if (scale_min <= 0)
1318 throw InternalErr(__FILE__,__LINE__,"the scale factor should always be greater than 0.");
1319
1320 if (orig_valid_max > offset_min)
1321 valid_max = (orig_valid_max-offset_min)*scale_max;
1322 else
1323 valid_max = (orig_valid_max-offset_min)*scale_min;
1324
1325 if (orig_valid_min > offset_max)
1326 valid_min = (orig_valid_min-offset_max)*scale_min;
1327 else
1328 valid_min = (orig_valid_min -offset_max)*scale_max;
1329
1330 // These physical variables should be greater than 0
1331 if (valid_min < 0)
1332 valid_min = 0;
1333
1334 string print_rep = HDFCFUtil::print_attr(DFNT_FLOAT32,0,(void*)(&valid_min));
1335 at->append_attr("valid_min","Float32",print_rep);
1336 print_rep = HDFCFUtil::print_attr(DFNT_FLOAT32,0,(void*)(&valid_max));
1337 at->append_attr("valid_max","Float32",print_rep);
1338 at->del_attr("valid_range");
1339 handle_modis_l1b = true;
1340
1341 // Change the units for the emissive field
1342 if (true == is_emissive_field && radiance_units_value.size() >0) {
1343 at->del_attr("units");
1344 at->append_attr("units","String",radiance_units_value);
1345 }
1346 }
1347 }
1348
1349 // Handle other valid_range attributes
1350 if(true == changedtype && true == has_valid_range && false == handle_modis_l1b) {
1351
1352 // If the gridname_change_valid_range is true, call a special function to handle this.
1353 if (true == gridname_change_valid_range)
1354 HDFCFUtil::handle_modis_vip_special_attrs(valid_range_value,scale_factor_value,valid_min,valid_max);
1355 else if(scale_factor_value.size()!=0) {
1356
1357 // We found MODIS products always scale to a smaller value. If somehow the original scale factor
1358 // is smaller than 1, the scale/offset should be the multiplication rule.(new_data =scale*(old_data-offset))
1359 // If the original scale factor is greater than 1, the scale/offset rule should be division rule.
1360 // New_data = (old_data-offset)/scale.
1361 // We indeed find such a case. HDF-EOS2 Grid MODIS_Grid_1km_2D of MOD(or MYD)09GA is a MODIS_EQ_SCALE.
1362 // However,
1363 // the scale_factor of the variable Range_1 in the MOD09GA product is 25. According to our observation,
1364 // this variable should be MODIS_DIV_SCALE.We verify this is true according to MODIS 09 product document
1365 // http://modis-sr.ltdri.org/products/MOD09_UserGuide_v1_3.pdf.
1366 // Since this conclusion is based on our observation, we would like to add a BESlog to detect if we find
1367 // the similar cases so that we can verify with the corresponding product documents to see if this is true.
1368 // More information,
1369 // We just verified with the data producer, the scale_factor for Range_1 and Range_c is 25 but the
1370 // equation is still multiplication instead of division. So we have to make this as a special case that
1371 // we don't want to change the scale and offset settings.
1372 // KY 2014-01-13
1373
1374 if (SOType::MODIS_EQ_SCALE == sotype || SOType::MODIS_MUL_SCALE == sotype) {
1375 if (orig_scale_value > 1) {
1376
1377 bool need_change_scale = true;
1378 if(true == is_grid) {
1379 if ((filename.size() >5) && ((filename.compare(0,5,"MOD09") == 0)|| (filename.compare(0,5,"MYD09")==0))) {
1380 if ((newfname.size() >5) && newfname.find("Range") != string::npos)
1381 need_change_scale = false;
1382 }
1383
1384 else if((filename.size() >7)&&((filename.compare(0,7,"MOD16A2") == 0)|| (filename.compare(0,7,"MYD16A2")==0) ||
1385 (filename.compare(0,7,"MOD16A3")==0) || (filename.compare(0,7,"MYD16A3")==0)))
1386 need_change_scale = false;
1387 }
1388 if(true == need_change_scale) {
1389 sotype = SOType::MODIS_DIV_SCALE;
1390 INFO_LOG("The field " + newfname + " scale factor is "+ std::to_string(orig_scale_value)
1391 + " But the original scale factor type is MODIS_MUL_SCALE or MODIS_EQ_SCALE."
1392 + " Now change it to MODIS_DIV_SCALE. ");
1393 }
1394 }
1395 }
1396
1397 if (SOType::MODIS_DIV_SCALE == sotype) {
1398 if (orig_scale_value < 1) {
1399 sotype = SOType::MODIS_MUL_SCALE;
1400 INFO_LOG("The field " + newfname + " scale factor is " + std::to_string(orig_scale_value)
1401 + " But the original scale factor type is MODIS_DIV_SCALE."
1402 + " Now change it to MODIS_MUL_SCALE.");
1403 }
1404 }
1405
1406 if(sotype == SOType::MODIS_MUL_SCALE) {
1407 valid_min = (orig_valid_min - orig_offset_value)*orig_scale_value;
1408 valid_max = (orig_valid_max - orig_offset_value)*orig_scale_value;
1409 }
1410 else if (sotype == SOType::MODIS_DIV_SCALE) {
1411 valid_min = (orig_valid_min - orig_offset_value)/orig_scale_value;
1412 valid_max = (orig_valid_max - orig_offset_value)/orig_scale_value;
1413 }
1414 else if (sotype == SOType::MODIS_EQ_SCALE) {
1415 valid_min = orig_valid_min * orig_scale_value + orig_offset_value;
1416 valid_max = orig_valid_max * orig_scale_value + orig_offset_value;
1417 }
1418 }
1419 else {// This is our current assumption.
1420 if (SOType::MODIS_MUL_SCALE == sotype || SOType::MODIS_DIV_SCALE == sotype) {
1421 valid_min = orig_valid_min - orig_offset_value;
1422 valid_max = orig_valid_max - orig_offset_value;
1423 }
1424 else if (SOType::MODIS_EQ_SCALE == sotype) {
1425 valid_min = orig_valid_min + orig_offset_value;
1426 valid_max = orig_valid_max + orig_offset_value;
1427 }
1428 }
1429
1430 // Append the corrected valid_min and valid_max. (valid_min,valid_max) is equivalent to valid_range.
1431 string print_rep = HDFCFUtil::print_attr(DFNT_FLOAT32,0,(void*)(&valid_min));
1432 at->append_attr("valid_min","Float32",print_rep);
1433 print_rep = HDFCFUtil::print_attr(DFNT_FLOAT32,0,(void*)(&valid_max));
1434 at->append_attr("valid_max","Float32",print_rep);
1435 at->del_attr("valid_range");
1436 }
1437
1438 // We also find that there is an attribute called "Number_Type" that will stores the original attribute
1439 // datatype. If the datatype gets changed, the attribute is confusion. here we can change the attribute
1440 // name to "Number_Type_Orig"
1441 if(true == changedtype && number_type_dap_type !="" ) {
1442 at->del_attr("Number_Type");
1443 at->append_attr("Number_Type_Orig",number_type_dap_type,number_type_value);
1444 }
1445}
1446
1447 // This routine makes the MeaSUREs VIP attributes follow CF.
1448// valid_range attribute uses char type instead of the raw data's datatype.
1449void HDFCFUtil::handle_modis_vip_special_attrs(const std::string& valid_range_value,
1450 const std::string& scale_factor_value,
1451 float& valid_min, float &valid_max) {
1452
1453 int16 vip_orig_valid_min = 0;
1454 int16 vip_orig_valid_max = 0;
1455
1456 size_t found = valid_range_value.find_first_of(",");
1457 size_t found_from_end = valid_range_value.find_last_of(",");
1458 if (string::npos == found)
1459 throw InternalErr(__FILE__,__LINE__,"should find the separator ,");
1460 if (found != found_from_end)
1461 throw InternalErr(__FILE__,__LINE__,"There should be only one separator.");
1462
1463
1464 vip_orig_valid_min = (short) (atoi((valid_range_value.substr(0,found)).c_str()));
1465 vip_orig_valid_max = (short) (atoi((valid_range_value.substr(found+1)).c_str()));
1466
1467 int16 scale_factor_number = 1;
1468
1469 scale_factor_number = (short)(atoi(scale_factor_value.c_str()));
1470
1471 if(scale_factor_number !=0) {
1472 valid_min = (float)(vip_orig_valid_min/scale_factor_number);
1473 valid_max = (float)(vip_orig_valid_max/scale_factor_number);
1474 }
1475 else
1476 throw InternalErr(__FILE__,__LINE__,"The scale_factor_number should not be zero.");
1477}
1478
1479// Make AMSR-E attributes follow CF.
1480// Change SCALE_FACTOR and OFFSET to CF names: scale_factor and add_offset.
1481void HDFCFUtil::handle_amsr_attrs(AttrTable *at) {
1482
1483 AttrTable::Attr_iter it = at->attr_begin();
1484
1485 string scale_factor_value="";
1486 string add_offset_value="0";
1487
1488 string scale_factor_type;
1489 string add_offset_type;
1490
1491 bool OFFSET_found = false;
1492 bool Scale_found = false;
1493 bool SCALE_FACTOR_found = false;
1494
1495 while (it!=at->attr_end())
1496 {
1497 if(at->get_name(it)=="SCALE_FACTOR")
1498 {
1499 scale_factor_value = (*at->get_attr_vector(it)->begin());
1500 scale_factor_type = at->get_type(it);
1501 SCALE_FACTOR_found = true;
1502 }
1503
1504 if(at->get_name(it)=="Scale")
1505 {
1506 scale_factor_value = (*at->get_attr_vector(it)->begin());
1507 scale_factor_type = at->get_type(it);
1508 Scale_found = true;
1509 }
1510
1511 if(at->get_name(it)=="OFFSET")
1512 {
1513 add_offset_value = (*at->get_attr_vector(it)->begin());
1514 add_offset_type = at->get_type(it);
1515 OFFSET_found = true;
1516 }
1517 it++;
1518 }
1519
1520 if (true == SCALE_FACTOR_found) {
1521 at->del_attr("SCALE_FACTOR");
1522 at->append_attr("scale_factor",scale_factor_type,scale_factor_value);
1523 }
1524
1525 if (true == Scale_found) {
1526 at->del_attr("Scale");
1527 at->append_attr("scale_factor",scale_factor_type,scale_factor_value);
1528 }
1529
1530 if (true == OFFSET_found) {
1531 at->del_attr("OFFSET");
1532 at->append_attr("add_offset",add_offset_type,add_offset_value);
1533 }
1534
1535}
1536
1537//This function obtains the latitude and longitude dimension info. of an
1538//HDF-EOS2 grid after the handler translates the HDF-EOS to CF.
1539// Dimension info. includes dimension name and dimension size.
1540void HDFCFUtil::obtain_grid_latlon_dim_info(const HDFEOS2::GridDataset* gdset,
1541 string & dim0name,
1542 int32 & dim0size,
1543 string & dim1name,
1544 int32& dim1size){
1545
1546 const vector<HDFEOS2::Field*>gfields = gdset->getDataFields();
1547 for (const auto &gf:gfields) {
1548
1549 // Check the dimensions for Latitude
1550 if(1 == gf->getFieldType()) {
1551 const vector<HDFEOS2::Dimension*>& dims= gf->getCorrectedDimensions();
1552
1553 //2-D latitude
1554 if(2 == dims.size()) {
1555 // Most time, it is YDim Major. We will see if we have a real case to
1556 // check if the handling is right for the XDim Major case.
1557 if(true == gf->getYDimMajor()) {
1558 dim0name = dims[0]->getName();
1559 dim0size = dims[0]->getSize();
1560 dim1name = dims[1]->getName();
1561 dim1size = dims[1]->getSize();
1562 }
1563 else {
1564 dim0name = dims[1]->getName();
1565 dim0size = dims[1]->getSize();
1566 dim1name = dims[0]->getName();
1567 dim1size = dims[0]->getSize();
1568 }
1569 break;
1570 }
1571
1572 //1-D latitude
1573 if( 1 == dims.size()) {
1574 dim0name = dims[0]->getName();
1575 dim0size = dims[0]->getSize();
1576 }
1577 }
1578
1579 // Longitude, if longitude is checked first, it goes here.
1580 if(2 == gf->getFieldType()) {
1581 const vector<HDFEOS2::Dimension*>& dims= gf->getCorrectedDimensions();
1582 if(2 == dims.size()) {
1583
1584 // Most time, it is YDim Major. We will see if we have a real case to
1585 // check if the handling is right for the XDim Major case.
1586 if(true == gf->getYDimMajor()) {
1587 dim0name = dims[0]->getName();
1588 dim0size = dims[0]->getSize();
1589 dim1name = dims[1]->getName();
1590 dim1size = dims[1]->getSize();
1591 }
1592 else {
1593 dim0name = dims[1]->getName();
1594 dim0size = dims[1]->getSize();
1595 dim1name = dims[0]->getName();
1596 dim1size = dims[0]->getSize();
1597 }
1598 break;
1599 }
1600 if( 1 == dims.size()) {
1601 dim1name = dims[0]->getName();
1602 dim1size = dims[0]->getSize();
1603
1604 }
1605 }
1606 }
1607 if(""==dim0name || ""==dim1name || dim0size<0 || dim1size <0)
1608 throw InternalErr (__FILE__, __LINE__,"Fail to obtain grid lat/lon dimension info.");
1609
1610}
1611
1612// This function adds the 1-D cf grid projection mapping attribute to data variables
1613// it is called by the function add_cf_grid_attrs.
1614void HDFCFUtil::add_cf_grid_mapping_attr(DAS &das, const HDFEOS2::GridDataset*gdset,const string& cf_projection,
1615 const string & dim0name,int32 dim0size,const string &dim1name,int32 dim1size) {
1616
1617 // Check >=2-D fields, check if they hold the dim0name,dim0size etc., yes, add the attribute cf_projection.
1618 const vector<HDFEOS2::Field*>gfields = gdset->getDataFields();
1619
1620 for (const auto &gf:gfields) {
1621 if(0 == gf->getFieldType() && gf->getRank() >1) {
1622 bool has_dim0 = false;
1623 bool has_dim1 = false;
1624 const vector<HDFEOS2::Dimension*>& dims= gf->getCorrectedDimensions();
1625 for (const auto &dim:dims) {
1626 if(dim->getName()== dim0name && dim->getSize() == dim0size)
1627 has_dim0 = true;
1628 else if(dim->getName()== dim1name && dim->getSize() == dim1size)
1629 has_dim1 = true;
1630
1631 }
1632 if(true == has_dim0 && true == has_dim1) {// Need to add the grid_mapping attribute
1633 AttrTable *at = das.get_table(gf->getNewName());
1634 if (!at)
1635 at = das.add_table(gf->getNewName(), new AttrTable);
1636
1637 // The dummy projection name is the value of the grid_mapping attribute
1638 at->append_attr("grid_mapping","String",cf_projection);
1639 }
1640 }
1641 }
1642}
1643
1644//This function adds 1D grid mapping CF attributes to CV and data variables.
1645void HDFCFUtil::add_cf_grid_cv_attrs(DAS & das, const HDFEOS2::GridDataset *gdset) {
1646
1647 //1. Check the projection information, now, we only handle sinusoidal now
1648 if(GCTP_SNSOID == gdset->getProjection().getCode()) {
1649
1650 //2. Obtain the dimension information from latitude and longitude(fieldtype =1 or fieldtype =2)
1651 string dim0name;
1652 string dim1name;
1653 int32 dim0size = -1;
1654 int32 dim1size = -1;
1655
1656 HDFCFUtil::obtain_grid_latlon_dim_info(gdset,dim0name,dim0size,dim1name,dim1size);
1657
1658 //3. Add 1D CF attributes to the 1-D CV variables and the dummy projection variable
1659 AttrTable *at = das.get_table(dim0name);
1660 if (!at)
1661 at = das.add_table(dim0name, new AttrTable);
1662 at->append_attr("standard_name","String","projection_y_coordinate");
1663
1664 string long_name="y coordinate of projection for grid "+ gdset->getName();
1665 at->append_attr("long_name","String",long_name);
1666 // Change this to meter.
1667 at->append_attr("units","string","meter");
1668
1669 at->append_attr("_CoordinateAxisType","string","GeoY");
1670
1671 at = das.get_table(dim1name);
1672 if (!at)
1673 at = das.add_table(dim1name, new AttrTable);
1674
1675 at->append_attr("standard_name","String","projection_x_coordinate");
1676 long_name="x coordinate of projection for grid "+ gdset->getName();
1677 at->append_attr("long_name","String",long_name);
1678
1679 // change this to meter.
1680 at->append_attr("units","string","meter");
1681 at->append_attr("_CoordinateAxisType","string","GeoX");
1682
1683 // Add the attributes for the dummy projection variable.
1684 string cf_projection_base = "eos_cf_projection";
1685 string cf_projection = HDFCFUtil::get_CF_string(gdset->getName()) +"_"+cf_projection_base;
1686 at = das.get_table(cf_projection);
1687 if (!at)
1688 at = das.add_table(cf_projection, new AttrTable);
1689
1690 at->append_attr("grid_mapping_name","String","sinusoidal");
1691 at->append_attr("longitude_of_central_meridian","Float64","0.0");
1692 at->append_attr("earth_radius","Float64","6371007.181");
1693
1694 at->append_attr("_CoordinateAxisTypes","string","GeoX GeoY");
1695
1696 // Fill in the data fields that contains the dim0name and dim1name dimensions with the grid_mapping
1697 // We only apply to >=2D data fields.
1698 HDFCFUtil::add_cf_grid_mapping_attr(das,gdset,cf_projection,dim0name,dim0size,dim1name,dim1size);
1699 }
1700
1701}
1702
1703// This function adds the 1-D horizontal coordinate variables as well as the dummy projection variable to the grid.
1704//Note: Since we don't add these artifical CF variables to our main engineering at HDFEOS2.cc, the information
1705// to handle DAS won't pass to DDS by the file pointer, we need to re-call the routines to check projection
1706// and dimension. The time to retrieve these information is trivial compared with the whole translation.
1707void HDFCFUtil::add_cf_grid_cvs(DDS & dds, const HDFEOS2::GridDataset *gdset) {
1708
1709 //1. Check the projection information, now, we only handle sinusoidal now
1710 if(GCTP_SNSOID == gdset->getProjection().getCode()) {
1711
1712 //2. Obtain the dimension information from latitude and longitude(fieldtype =1 or fieldtype =2)
1713 string dim0name;
1714 string dim1name;
1715 int32 dim0size = -1;
1716 int32 dim1size = -1;
1717 HDFCFUtil::obtain_grid_latlon_dim_info(gdset,dim0name,dim0size,dim1name,dim1size);
1718
1719 //3. Add the 1-D CV variables and the dummy projection variable
1720 // Note: we just need to pass the parameters that calculate 1-D cv to the data reading function,
1721 // in that way, we save the open cost of HDF-EOS2.
1722 BaseType *bt_dim0 = nullptr;
1723 BaseType *bt_dim1 = nullptr;
1724
1725 HDFEOS2GeoCF1D * ar_dim0 = nullptr;
1726 HDFEOS2GeoCF1D * ar_dim1 = nullptr;
1727
1728 const float64 *upleft = nullptr;
1729 const float64 *lowright = nullptr;
1730
1731 try {
1732
1733 bt_dim0 = new(HDFFloat64)(dim0name,gdset->getName());
1734 bt_dim1 = new(HDFFloat64)(dim1name,gdset->getName());
1735
1736 // Obtain the upleft and lowright coordinates
1737 upleft = gdset->getInfo().getUpLeft();
1738 lowright = gdset->getInfo().getLowRight();
1739
1740 // Note ar_dim0 is y, ar_dim1 is x.
1741 ar_dim0 = new HDFEOS2GeoCF1D(GCTP_SNSOID,
1742 upleft[1],lowright[1],dim0size,dim0name,bt_dim0);
1743 ar_dim0->append_dim(dim0size,dim0name);
1744
1745 ar_dim1 = new HDFEOS2GeoCF1D(GCTP_SNSOID,
1746 upleft[0],lowright[0],dim1size,dim1name,bt_dim1);
1747 ar_dim1->append_dim(dim1size,dim1name);
1748 dds.add_var(ar_dim0);
1749 dds.add_var(ar_dim1);
1750
1751 }
1752 catch(...) {
1753 if(bt_dim0)
1754 delete bt_dim0;
1755 if(bt_dim1)
1756 delete bt_dim1;
1757 if(ar_dim0)
1758 delete ar_dim0;
1759 if(ar_dim1)
1760 delete ar_dim1;
1761 throw InternalErr(__FILE__,__LINE__,"Unable to allocate the HDFEOS2GeoCF1D instance.");
1762 }
1763
1764 if(bt_dim0)
1765 delete bt_dim0;
1766 if(bt_dim1)
1767 delete bt_dim1;
1768 if(ar_dim0)
1769 delete ar_dim0;
1770 if(ar_dim1)
1771 delete ar_dim1;
1772
1773 // Also need to add the dummy projection variable.
1774 string cf_projection_base = "eos_cf_projection";
1775
1776 // To handle multi-grid cases, we need to add the grid name.
1777 string cf_projection = HDFCFUtil::get_CF_string(gdset->getName()) +"_"+cf_projection_base;
1778
1779 HDFEOS2GeoCFProj * dummy_proj_cf = new HDFEOS2GeoCFProj(cf_projection,gdset->getName());
1780 dds.add_var(dummy_proj_cf);
1781 if(dummy_proj_cf)
1782 delete dummy_proj_cf;
1783
1784 }
1785
1786}
1787#endif
1788
1789 // Check OBPG attributes. Specifically, check if slope and intercept can be obtained from the file level.
1790 // If having global slope and intercept, obtain OBPG scaling, slope and intercept values.
1791void HDFCFUtil::check_obpg_global_attrs(HDFSP::File *f, std::string & scaling,
1792 float & slope,bool &global_slope_flag,
1793 float & intercept, bool & global_intercept_flag){
1794
1795 HDFSP::SD* spsd = f->getSD();
1796
1797 for (const auto &attr:spsd->getAttributes()) {
1798
1799 //We want to add two new attributes, "scale_factor" and "add_offset" to data fields if the scaling equation is linear.
1800 // OBPG products use "Slope" instead of "scale_factor", "intercept" instead of "add_offset". "Scaling" describes if the equation is linear.
1801 // Their values will be copied directly from File attributes. This addition is for OBPG L3 only.
1802 // We also add OBPG L2 support since all OBPG level 2 products(CZCS,MODISA,MODIST,OCTS,SeaWiFS) we evaluate use Slope,intercept and linear equation
1803 // for the final data. KY 2012-09-06
1804 if(f->getSPType()==OBPGL3 || f->getSPType() == OBPGL2)
1805 {
1806 if(attr->getName()=="Scaling")
1807 {
1808 string tmpstring(attr->getValue().begin(), attr->getValue().end());
1809 scaling = tmpstring;
1810 }
1811 if(attr->getName()=="Slope" || attr->getName()=="slope")
1812 {
1813 global_slope_flag = true;
1814
1815 switch(attr->getType())
1816 {
1817#define GET_SLOPE(TYPE, CAST) \
1818 case DFNT_##TYPE: \
1819 { \
1820 CAST tmpvalue = *(CAST*)&(attr->getValue()[0]); \
1821 slope = (float)tmpvalue; \
1822 } \
1823 break;
1824 GET_SLOPE(INT16, int16)
1825 GET_SLOPE(INT32, int32)
1826 GET_SLOPE(FLOAT32, float)
1827 GET_SLOPE(FLOAT64, double)
1828 default:
1829 throw InternalErr(__FILE__,__LINE__,"unsupported data type.");
1830#undef GET_SLOPE
1831 }
1832#if 0
1833 //slope = *(float*)&((*i)->getValue()[0]);
1834#endif
1835 }
1836 if(attr->getName()=="Intercept" || attr->getName()=="intercept")
1837 {
1838 global_intercept_flag = true;
1839 switch(attr->getType())
1840 {
1841#define GET_INTERCEPT(TYPE, CAST) \
1842 case DFNT_##TYPE: \
1843 { \
1844 CAST tmpvalue = *(CAST*)&(attr->getValue()[0]); \
1845 intercept = (float)tmpvalue; \
1846 } \
1847 break;
1848 GET_INTERCEPT(INT16, int16)
1849 GET_INTERCEPT(INT32, int32)
1850 GET_INTERCEPT(FLOAT32, float)
1851 GET_INTERCEPT(FLOAT64, double)
1852 default:
1853 throw InternalErr(__FILE__,__LINE__,"unsupported data type.");
1854#undef GET_INTERCEPT
1855 }
1856#if 0
1857 //intercept = *(float*)&((*i)->getValue()[0]);
1858#endif
1859 }
1860 }
1861 }
1862}
1863
1864// For some OBPG files that only provide slope and intercept at the file level,
1865// global slope and intercept are needed to add to all fields and their names are needed to be changed to scale_factor and add_offset.
1866// For OBPG files that provide slope and intercept at the field level, slope and intercept are needed to rename to scale_factor and add_offset.
1867void HDFCFUtil::add_obpg_special_attrs(const HDFSP::File*f,DAS &das,
1868 const HDFSP::SDField *onespsds,
1869 const string& scaling, float& slope,
1870 const bool& global_slope_flag,
1871 float& intercept,
1872 const bool & global_intercept_flag) {
1873
1874 AttrTable *at = das.get_table(onespsds->getNewName());
1875 if (!at)
1876 at = das.add_table(onespsds->getNewName(), new AttrTable);
1877
1878 //For OBPG L2 and L3 only.Some OBPG products put "slope" and "Intercept" etc. in the field attributes
1879 // We still need to handle the scale and offset here.
1880 bool scale_factor_flag = false;
1881 bool add_offset_flag = false;
1882 bool slope_flag = false;
1883 bool intercept_flag = false;
1884
1885 if(f->getSPType()==OBPGL3 || f->getSPType() == OBPGL2) {// Begin OPBG CF attribute handling(Checking "slope" and "intercept")
1886#if 0
1887 for(vector<HDFSP::Attribute *>::const_iterator i=onespsds->getAttributes().begin();
1888 i!=onespsds->getAttributes().end();i++) {
1889#endif
1890 for (const auto &attr:onespsds->getAttributes()) {
1891
1892 if(global_slope_flag != true && (attr->getName()=="Slope" || attr->getName()=="slope"))
1893 {
1894 slope_flag = true;
1895
1896 switch(attr->getType())
1897 {
1898#define GET_SLOPE(TYPE, CAST) \
1899 case DFNT_##TYPE: \
1900 { \
1901 CAST tmpvalue = *(CAST*)&(attr->getValue()[0]); \
1902 slope = (float)tmpvalue; \
1903 } \
1904 break;
1905
1906 GET_SLOPE(INT16, int16)
1907 GET_SLOPE(INT32, int32)
1908 GET_SLOPE(FLOAT32, float)
1909 GET_SLOPE(FLOAT64, double)
1910 default:
1911 throw InternalErr(__FILE__,__LINE__,"unsupported data type.");
1912
1913#undef GET_SLOPE
1914 }
1915#if 0
1916 //slope = *(float*)&((*i)->getValue()[0]);
1917#endif
1918 }
1919 if(global_intercept_flag != true && (attr->getName()=="Intercept" || attr->getName()=="intercept"))
1920 {
1921 intercept_flag = true;
1922 switch(attr->getType())
1923 {
1924#define GET_INTERCEPT(TYPE, CAST) \
1925 case DFNT_##TYPE: \
1926 { \
1927 CAST tmpvalue = *(CAST*)&(attr->getValue()[0]); \
1928 intercept = (float)tmpvalue; \
1929 } \
1930 break;
1931 GET_INTERCEPT(INT16, int16)
1932 GET_INTERCEPT(INT32, int32)
1933 GET_INTERCEPT(FLOAT32, float)
1934 GET_INTERCEPT(FLOAT64, double)
1935 default:
1936 throw InternalErr(__FILE__,__LINE__,"unsupported data type.");
1937
1938#undef GET_INTERCEPT
1939 }
1940#if 0
1941 //intercept = *(float*)&((*i)->getValue()[0]);
1942#endif
1943 }
1944 }
1945 } // End of checking "slope" and "intercept"
1946
1947 // Checking if OBPG has "scale_factor" ,"add_offset", generally checking for "long_name" attributes.
1948 for (const auto& attr:onespsds->getAttributes()) {
1949
1950 if((f->getSPType()==OBPGL3 || f->getSPType() == OBPGL2) && attr->getName()=="scale_factor")
1951 scale_factor_flag = true;
1952
1953 if((f->getSPType()==OBPGL3 || f->getSPType() == OBPGL2) && attr->getName()=="add_offset")
1954 add_offset_flag = true;
1955 }
1956
1957 // Checking if the scale and offset equation is linear, this is only for OBPG level 3.
1958 // Also checking if having the fill value and adding fill value if not having one for some OBPG data type
1959 if((f->getSPType() == OBPGL2 || f->getSPType()==OBPGL3 )&& onespsds->getFieldType()==0)
1960 {
1961
1962 if((OBPGL3 == f->getSPType() && (scaling.find("linear")!=string::npos)) || OBPGL2 == f->getSPType() ) {
1963
1964 if(false == scale_factor_flag && (true == slope_flag || true == global_slope_flag))
1965 {
1966 string print_rep = HDFCFUtil::print_attr(DFNT_FLOAT32, 0, (void*)&slope);
1967 at->append_attr("scale_factor", HDFCFUtil::print_type(DFNT_FLOAT32), print_rep);
1968 }
1969
1970 if(false == add_offset_flag && (true == intercept_flag || true == global_intercept_flag))
1971 {
1972 string print_rep = HDFCFUtil::print_attr(DFNT_FLOAT32, 0, (void*)&intercept);
1973 at->append_attr("add_offset", HDFCFUtil::print_type(DFNT_FLOAT32), print_rep);
1974 }
1975 }
1976
1977 bool has_fill_value = false;
1978 for(const auto &attr:onespsds->getAttributes()) {
1979 if ("_FillValue" == attr->getNewName()){
1980 has_fill_value = true;
1981 break;
1982 }
1983 }
1984
1985
1986 // Add fill value to some type of OBPG data. 16-bit integer, fill value = -32767, unsigned 16-bit integer fill value = 65535
1987 // This is based on the evaluation of the example files. KY 2012-09-06
1988 if ((false == has_fill_value) &&(DFNT_INT16 == onespsds->getType())) {
1989 short fill_value = -32767;
1990 string print_rep = HDFCFUtil::print_attr(DFNT_INT16,0,(void*)&fill_value);
1991 at->append_attr("_FillValue",HDFCFUtil::print_type(DFNT_INT16),print_rep);
1992 }
1993
1994 if ((false == has_fill_value) &&(DFNT_UINT16 == onespsds->getType())) {
1995 unsigned short fill_value = 65535;
1996 string print_rep = HDFCFUtil::print_attr(DFNT_UINT16,0,(void*)&fill_value);
1997 at->append_attr("_FillValue",HDFCFUtil::print_type(DFNT_UINT16),print_rep);
1998 }
1999
2000 }// Finish OBPG handling
2001
2002}
2003
2004// Handle HDF4 OTHERHDF products that follow SDS dimension scale model.
2005// The special handling of AVHRR data is also included.
2006void HDFCFUtil::handle_otherhdf_special_attrs(const HDFSP::File*f,DAS &das) {
2007
2008 // For some HDF4 files that follow HDF4 dimension scales, P.O. DAAC's AVHRR files.
2009 // The "otherHDF" category can almost make AVHRR files work, except
2010 // that AVHRR uses the attribute name "unit" instead of "units" for latitude and longitude,
2011 // I have to correct the name to follow CF conventions(using "units"). I won't check
2012 // the latitude and longitude values since latitude and longitude values for some files(LISO files)
2013 // are not in the standard range(0-360 for lon and 0-180 for lat). KY 2011-3-3
2014 const vector<HDFSP::SDField *>& spsds = f->getSD()->getFields();
2015
2016 if(f->getSPType() == OTHERHDF) {
2017
2018 bool latflag = false;
2019 bool latunitsflag = false; //CF conventions use "units" instead of "unit"
2020 bool lonflag = false;
2021 bool lonunitsflag = false; // CF conventions use "units" instead of "unit"
2022 int llcheckoverflag = 0;
2023
2024 // Here I try to condense the code within just two for loops
2025 // The outer loop: Loop through all SDS objects
2026 // The inner loop: Loop through all attributes of this SDS
2027 // Inside two inner loops(since "units" are common for other fields),
2028 // inner loop 1: when an attribute's long name value is L(l)atitude,mark it.
2029 // inner loop 2: when an attribute's name is units, mark it.
2030 // Outside the inner loop, when latflag is true, and latunitsflag is false,
2031 // adding a new attribute called units and the value should be "degrees_north".
2032 // doing the same thing for longitude.
2033
2034 for (const auto &fd:spsds){
2035
2036 // Ignore ALL coordinate variables if this is "OTHERHDF" case and some dimensions
2037 // don't have dimension scale data.
2038 if ( true == f->Has_Dim_NoScale_Field() && (fd->getFieldType() !=0) && (fd->IsDimScale() == false))
2039 continue;
2040
2041 // Ignore the empty(no data) dimension variable.
2042 if (OTHERHDF == f->getSPType() && true == fd->IsDimNoScale())
2043 continue;
2044
2045 AttrTable *at = das.get_table(fd->getNewName());
2046 if (!at)
2047 at = das.add_table(fd->getNewName(), new AttrTable);
2048
2049 for (const auto& attr:fd->getAttributes()) {
2050 if(attr->getType()==DFNT_UCHAR || attr->getType() == DFNT_CHAR){
2051
2052 if(attr->getName() == "long_name") {
2053 string tempstring2(attr->getValue().begin(),attr->getValue().end());
2054 string tempfinalstr= string(tempstring2.c_str());// This may remove some garbage characters
2055 if(tempfinalstr=="latitude" || tempfinalstr == "Latitude") // Find long_name latitude
2056 latflag = true;
2057 if(tempfinalstr=="longitude" || tempfinalstr == "Longitude") // Find long_name latitude
2058 lonflag = true;
2059 }
2060 }
2061 }
2062
2063 if(latflag) {
2064 for (const auto& attr:fd->getAttributes()) {
2065 if (attr->getName() == "units")
2066 latunitsflag = true;
2067 }
2068 }
2069
2070 if(lonflag) {
2071 for(const auto& attr:fd->getAttributes()) {
2072 if(attr->getName() == "units")
2073 lonunitsflag = true;
2074 }
2075 }
2076 if(latflag && !latunitsflag){ // No "units" for latitude, add "units"
2077 at->append_attr("units","String","degrees_north");
2078 latflag = false;
2079 latunitsflag = false;
2080 llcheckoverflag++;
2081 }
2082
2083 if(lonflag && !lonunitsflag){ // No "units" for latitude, add "units"
2084 at->append_attr("units","String","degrees_east");
2085 lonflag = false;
2086 lonunitsflag = false;
2087 llcheckoverflag++;
2088 }
2089 if(llcheckoverflag ==2) break;
2090
2091 }
2092
2093 }
2094
2095}
2096
2097// Add missing CF attributes for non-CV varibles
2098void
2099HDFCFUtil::add_missing_cf_attrs(const HDFSP::File*f,DAS &das) {
2100
2101 const vector<HDFSP::SDField *>& spsds = f->getSD()->getFields();
2102
2103 // TRMM level 3 grid
2104 if(TRMML3A_V6== f->getSPType() || TRMML3C_V6==f->getSPType() || TRMML3S_V7 == f->getSPType() || TRMML3M_V7 == f->getSPType()) {
2105
2106 for(const auto &fd:spsds){
2107 if(fd->getFieldType() == 0 && fd->getType()==DFNT_FLOAT32) {
2108
2109 AttrTable *at = das.get_table(fd->getNewName());
2110 if (!at)
2111 at = das.add_table(fd->getNewName(), new AttrTable);
2112 string print_rep = "-9999.9";
2113 at->append_attr("_FillValue",HDFCFUtil::print_type(DFNT_FLOAT32),print_rep);
2114
2115 }
2116 }
2117
2118 for(const auto &fd:spsds){
2119 if(fd->getFieldType() == 0 && ((fd->getType()==DFNT_INT32) || (fd->getType()==DFNT_INT16))) {
2120
2121 AttrTable *at = das.get_table(fd->getNewName());
2122 if (!at)
2123 at = das.add_table(fd->getNewName(), new AttrTable);
2124 string print_rep = "-9999";
2125 if(fd->getType()==DFNT_INT32)
2126 at->append_attr("_FillValue",HDFCFUtil::print_type(DFNT_INT32),print_rep);
2127 else
2128 at->append_attr("_FillValue",HDFCFUtil::print_type(DFNT_INT16),print_rep);
2129
2130 }
2131 }
2132
2133 // nlayer for TRMM single grid version 7, the units should be "km"
2134 if(TRMML3S_V7 == f->getSPType()) {
2135 for(const auto &fd:spsds){
2136 if(fd->getFieldType() == 6 && fd->getNewName()=="nlayer") {
2137
2138 AttrTable *at = das.get_table(fd->getNewName());
2139 if (!at)
2140 at = das.add_table(fd->getNewName(), new AttrTable);
2141 at->append_attr("units","String","km");
2142
2143 }
2144 else if(fd->getFieldType() == 4) {
2145
2146 if (fd->getNewName()=="nh3" ||
2147 fd->getNewName()=="ncat3" ||
2148 fd->getNewName()=="nthrshZO" ||
2149 fd->getNewName()=="nthrshHB" ||
2150 fd->getNewName()=="nthrshSRT")
2151 {
2152
2153 string references =
2154 "http://pps.gsfc.nasa.gov/Documents/filespec.TRMM.V7.pdf";
2155 string comment;
2156
2157 AttrTable *at = das.get_table(fd->getNewName());
2158 if (!at)
2159 at = das.add_table(fd->getNewName(), new AttrTable);
2160
2161 if(fd->getNewName()=="nh3") {
2162 comment="Index number to represent the fixed heights above the earth ellipsoid,";
2163 comment= comment + " at 2, 4, 6 km plus one for path-average.";
2164 }
2165
2166 else if(fd->getNewName()=="ncat3") {
2167 comment="Index number to represent catgories for probability distribution functions.";
2168 comment=comment + "Check more information from the references.";
2169 }
2170
2171 else if(fd->getNewName()=="nthrshZO")
2172 comment="Q-thresholds for Zero order used for probability distribution functions.";
2173
2174 else if(fd->getNewName()=="nthrshHB")
2175 comment="Q-thresholds for HB used for probability distribution functions.";
2176
2177 else if(fd->getNewName()=="nthrshSRT")
2178 comment="Q-thresholds for SRT used for probability distribution functions.";
2179
2180 at->append_attr("comment","String",comment);
2181 at->append_attr("references","String",references);
2182
2183 }
2184
2185 }
2186
2187 }
2188
2189 // 3A26 use special values such as -666, -777,-999 in their fields.
2190 // Although the document doesn't provide range for some fields, the meaning of those fields should be greater than 0.
2191 // So add valid_min = 0 and fill_value = -999 .
2192 string base_filename;
2193 size_t last_slash_pos = f->getPath().find_last_of("/");
2194 if(last_slash_pos != string::npos)
2195 base_filename = f->getPath().substr(last_slash_pos+1);
2196 if(""==base_filename)
2197 base_filename = f->getPath();
2198 bool t3a26_flag = ((base_filename.find("3A26")!=string::npos)?true:false);
2199
2200 if(true == t3a26_flag) {
2201
2202 for(const auto &fd:spsds){
2203
2204 if(fd->getFieldType() == 0 && (fd->getType()==DFNT_FLOAT32)) {
2205 AttrTable *at = das.get_table(fd->getNewName());
2206 if (!at)
2207 at = das.add_table(fd->getNewName(), new AttrTable);
2208 at->del_attr("_FillValue");
2209 at->append_attr("_FillValue","Float32","-999");
2210 at->append_attr("valid_min","Float32","0");
2211
2212 }
2213 }
2214 }
2215
2216 }
2217
2218 // nlayer for TRMM single grid version 7, the units should be "km"
2219 if(TRMML3M_V7 == f->getSPType()) {
2220 for(const auto &fd:spsds){
2221
2222 if(fd->getFieldType() == 4 ) {
2223
2224 string references ="http://pps.gsfc.nasa.gov/Documents/filespec.TRMM.V7.pdf";
2225 if (fd->getNewName()=="nh1") {
2226
2227 AttrTable *at = das.get_table(fd->getNewName());
2228 if (!at)
2229 at = das.add_table(fd->getNewName(), new AttrTable);
2230
2231 string comment="Number of fixed heights above the earth ellipsoid,";
2232 comment= comment + " at 2, 4, 6, 10, and 15 km plus one for path-average.";
2233
2234 at->append_attr("comment","String",comment);
2235 at->append_attr("references","String",references);
2236
2237 }
2238 if (fd->getNewName()=="nh3") {
2239
2240 AttrTable *at = das.get_table(fd->getNewName());
2241 if (!at)
2242 at = das.add_table(fd->getNewName(), new AttrTable);
2243
2244 string comment="Number of fixed heights above the earth ellipsoid,";
2245 comment= comment + " at 2, 4, 6 km plus one for path-average.";
2246
2247 at->append_attr("comment","String",comment);
2248 at->append_attr("references","String",references);
2249
2250 }
2251
2252 if (fd->getNewName()=="nang") {
2253
2254 AttrTable *at = das.get_table(fd->getNewName());
2255 if (!at)
2256 at = das.add_table(fd->getNewName(), new AttrTable);
2257
2258 string comment="Number of fixed incidence angles, at 0, 5, 10 and 15 degree and all angles.";
2259 references = "http://pps.gsfc.nasa.gov/Documents/ICSVol4.pdf";
2260
2261 at->append_attr("comment","String",comment);
2262 at->append_attr("references","String",references);
2263
2264 }
2265
2266 if (fd->getNewName()=="ncat2") {
2267
2268 AttrTable *at = das.get_table(fd->getNewName());
2269 if (!at)
2270 at = das.add_table(fd->getNewName(), new AttrTable);
2271
2272 string comment="Second number of categories for histograms (30). ";
2273 comment=comment + "Check more information from the references.";
2274
2275 at->append_attr("comment","String",comment);
2276 at->append_attr("references","String",references);
2277
2278 }
2279 }
2280 }
2281 }
2282 }
2283
2284 // TRMM level 2 swath
2285 else if(TRMML2_V7== f->getSPType()) {
2286
2287 string base_filename;
2288 size_t last_slash_pos = f->getPath().find_last_of("/");
2289 if(last_slash_pos != string::npos)
2290 base_filename = f->getPath().substr(last_slash_pos+1);
2291 if(""==base_filename)
2292 base_filename = f->getPath();
2293 bool t2b31_flag = ((base_filename.find("2B31")!=string::npos)?true:false);
2294 bool t2a21_flag = ((base_filename.find("2A21")!=string::npos)?true:false);
2295 bool t2a12_flag = ((base_filename.find("2A12")!=string::npos)?true:false);
2296
2297 // 2A23 is temporarily not supported perhaps due to special fill values
2298 bool t2a25_flag = ((base_filename.find("2A25")!=string::npos)?true:false);
2299 bool t1c21_flag = ((base_filename.find("1C21")!=string::npos)?true:false);
2300 bool t1b21_flag = ((base_filename.find("1B21")!=string::npos)?true:false);
2301 bool t1b11_flag = ((base_filename.find("1B11")!=string::npos)?true:false);
2302 bool t1b01_flag = ((base_filename.find("1B01")!=string::npos)?true:false);
2303
2304 // Handle scale and offset
2305
2306 // group 1: 2B31,2A12,2A21
2307 if(t2b31_flag || t2a12_flag || t2a21_flag) {
2308
2309 // special for 2B31
2310 if(t2b31_flag) {
2311
2312 for (const auto &fd:spsds){
2313
2314 if(fd->getFieldType() == 0 && fd->getType()==DFNT_INT16) {
2315
2316 AttrTable *at = das.get_table(fd->getNewName());
2317 if (!at)
2318 at = das.add_table(fd->getNewName(), new AttrTable);
2319
2320 AttrTable::Attr_iter it = at->attr_begin();
2321 while (it!=at->attr_end()) {
2322 if(at->get_name(it)=="scale_factor")
2323 {
2324 // Scale type and value
2325 string scale_factor_value="";
2326 string scale_factor_type;
2327
2328 scale_factor_value = (*at->get_attr_vector(it)->begin());
2329 scale_factor_type = at->get_type(it);
2330
2331 if(scale_factor_type == "Float64") {
2332 double new_scale = 1.0/strtod(scale_factor_value.c_str(),nullptr);
2333 at->del_attr("scale_factor");
2334 string print_rep = HDFCFUtil::print_attr(DFNT_FLOAT64,0,(void*)(&new_scale));
2335 at->append_attr("scale_factor", scale_factor_type,print_rep);
2336
2337 }
2338
2339 if(scale_factor_type == "Float32") {
2340 float new_scale = 1.0f/strtof(scale_factor_value.c_str(),nullptr);
2341 at->del_attr("scale_factor");
2342 string print_rep = HDFCFUtil::print_attr(DFNT_FLOAT32,0,(void*)(&new_scale));
2343 at->append_attr("scale_factor", scale_factor_type,print_rep);
2344
2345 }
2346
2347 break;
2348 }
2349 ++it;
2350 }
2351 }
2352 }
2353 }
2354
2355 // Special for 2A12
2356 if (t2a12_flag==true) {
2357
2358 for (const auto &fd:spsds){
2359
2360 if (fd->getFieldType() == 6 && fd->getNewName()=="nlayer") {
2361
2362 AttrTable *at = das.get_table(fd->getNewName());
2363 if (!at)
2364 at = das.add_table(fd->getNewName(), new AttrTable);
2365 at->append_attr("units","String","km");
2366
2367 }
2368
2369 // signed char maps to int32, so use int32 for the fillvalue.
2370 if (fd->getFieldType() == 0 && fd->getType()==DFNT_INT8) {
2371
2372 AttrTable *at = das.get_table(fd->getNewName());
2373 if (!at)
2374 at = das.add_table(fd->getNewName(), new AttrTable);
2375 at->append_attr("_FillValue","Int32","-99");
2376
2377 }
2378 }
2379 }
2380
2381 // for all 2A12,2A21 and 2B31
2382 // Add fillvalues for float32 and int32.
2383 for (const auto & fd:spsds){
2384 if (fd->getFieldType() == 0 && fd->getType()==DFNT_FLOAT32) {
2385
2386 AttrTable *at = das.get_table(fd->getNewName());
2387 if (!at)
2388 at = das.add_table(fd->getNewName(), new AttrTable);
2389 string print_rep = "-9999.9";
2390 at->append_attr("_FillValue",HDFCFUtil::print_type(DFNT_FLOAT32),print_rep);
2391
2392 }
2393 }
2394
2395 for (const auto &fd:spsds){
2396
2397 if(fd->getFieldType() == 0 && fd->getType()==DFNT_INT16) {
2398
2399 AttrTable *at = das.get_table(fd->getNewName());
2400 if (!at)
2401 at = das.add_table(fd->getNewName(), new AttrTable);
2402
2403 string print_rep = "-9999";
2404 at->append_attr("_FillValue",HDFCFUtil::print_type(DFNT_INT32),print_rep);
2405
2406 }
2407 }
2408 }
2409
2410 // group 2: 2A21 and 2A25.
2411 else if(t2a21_flag == true || t2a25_flag == true) {
2412
2413 // 2A25: handle reflectivity and rain rate scales
2414 if (t2a25_flag == true) {
2415
2416 unsigned char handle_scale = 0;
2417
2418 for (const auto &fd:spsds){
2419
2420 if (fd->getFieldType() == 0 && fd->getType()==DFNT_INT16) {
2421 bool has_dBZ = false;
2422 bool has_rainrate = false;
2423 bool has_scale = false;
2424 string scale_factor_value;
2425 string scale_factor_type;
2426
2427 AttrTable *at = das.get_table(fd->getNewName());
2428 if (!at)
2429 at = das.add_table(fd->getNewName(), new AttrTable);
2430 AttrTable::Attr_iter it = at->attr_begin();
2431 while (it!=at->attr_end()) {
2432 if(at->get_name(it)=="units"){
2433 string units_value = *at->get_attr_vector(it)->begin();
2434 if("dBZ" == units_value) {
2435 has_dBZ = true;
2436 }
2437
2438 else if("mm/hr" == units_value){
2439 has_rainrate = true;
2440 }
2441 }
2442 if(at->get_name(it)=="scale_factor")
2443 {
2444 scale_factor_value = *at->get_attr_vector(it)->begin();
2445 scale_factor_type = at->get_type(it);
2446 has_scale = true;
2447 }
2448 ++it;
2449
2450 }
2451
2452 if((true == has_rainrate || true == has_dBZ) && true == has_scale) {
2453
2454 handle_scale++;
2455 short valid_min = 0;
2456 short valid_max = 0;
2457
2458 // Here just use 32-bit floating-point for the scale_factor, should be okay.
2459 if(true == has_rainrate)
2460 valid_max = (short)(300*strtof(scale_factor_value.c_str(),nullptr));
2461 else if(true == has_dBZ)
2462 valid_max = (short)(80*strtof(scale_factor_value.c_str(),nullptr));
2463
2464 string print_rep1 = HDFCFUtil::print_attr(DFNT_INT16,0,(void*)(&valid_min));
2465 at->append_attr("valid_min","Int16",print_rep1);
2466 print_rep1 = HDFCFUtil::print_attr(DFNT_INT16,0,(void*)(&valid_max));
2467 at->append_attr("valid_max","Int16",print_rep1);
2468
2469 at->del_attr("scale_factor");
2470 if(scale_factor_type == "Float64") {
2471 double new_scale = 1.0/strtod(scale_factor_value.c_str(),nullptr);
2472 string print_rep2 = HDFCFUtil::print_attr(DFNT_FLOAT64,0,(void*)(&new_scale));
2473 at->append_attr("scale_factor", scale_factor_type,print_rep2);
2474
2475 }
2476
2477 if(scale_factor_type == "Float32") {
2478 float new_scale = 1.0/strtof(scale_factor_value.c_str(),nullptr);
2479 string print_rep3 = HDFCFUtil::print_attr(DFNT_FLOAT32,0,(void*)(&new_scale));
2480 at->append_attr("scale_factor", scale_factor_type,print_rep3);
2481
2482 }
2483 }
2484
2485 if(2 == handle_scale)
2486 break;
2487
2488 }
2489 }
2490 }
2491 }
2492
2493 // 1B21,1C21 and 1B11
2494 else if (t1b21_flag || t1c21_flag || t1b11_flag) {
2495
2496 // 1B21,1C21 scale_factor to CF and valid_range for dBm and dBZ.
2497 if (t1b21_flag || t1c21_flag) {
2498
2499 for (const auto &fd:spsds){
2500
2501 if(fd->getFieldType() == 0 && fd->getType()==DFNT_INT16) {
2502
2503 bool has_dBm = false;
2504 bool has_dBZ = false;
2505
2506 AttrTable *at = das.get_table(fd->getNewName());
2507 if (!at)
2508 at = das.add_table(fd->getNewName(), new AttrTable);
2509 AttrTable::Attr_iter it = at->attr_begin();
2510
2511 while (it!=at->attr_end()) {
2512 if(at->get_name(it)=="units"){
2513
2514 string units_value = *at->get_attr_vector(it)->begin();
2515 if("dBm" == units_value) {
2516 has_dBm = true;
2517 break;
2518 }
2519
2520 else if("dBZ" == units_value){
2521 has_dBZ = true;
2522 break;
2523 }
2524 }
2525 ++it;
2526 }
2527
2528 if(has_dBm == true || has_dBZ == true) {
2529 it = at->attr_begin();
2530 while (it!=at->attr_end()) {
2531 if(at->get_name(it)=="scale_factor")
2532 {
2533
2534 string scale_value = *at->get_attr_vector(it)->begin();
2535
2536 if(true == has_dBm) {
2537 auto valid_min = (short)(-120 *strtof(scale_value.c_str(),nullptr));
2538 auto valid_max = (short)(-20 *strtof(scale_value.c_str(),nullptr));
2539 string print_rep = HDFCFUtil::print_attr(DFNT_INT16,0,(void*)(&valid_min));
2540 at->append_attr("valid_min","Int16",print_rep);
2541 print_rep = HDFCFUtil::print_attr(DFNT_INT16,0,(void*)(&valid_max));
2542 at->append_attr("valid_max","Int16",print_rep);
2543 break;
2544
2545 }
2546
2547 else if(true == has_dBZ){
2548 auto valid_min = (short)(-20 *strtof(scale_value.c_str(),nullptr));
2549 auto valid_max = (short)(80 *strtof(scale_value.c_str(),nullptr));
2550 string print_rep = HDFCFUtil::print_attr(DFNT_INT16,0,(void*)(&valid_min));
2551 at->append_attr("valid_min","Int16",print_rep);
2552 print_rep = HDFCFUtil::print_attr(DFNT_INT16,0,(void*)(&valid_max));
2553 at->append_attr("valid_max","Int16",print_rep);
2554 break;
2555
2556 }
2557 }
2558 ++it;
2559 }
2560
2561 }
2562 }
2563 }
2564 }
2565
2566 // For all 1B21,1C21 and 1B11 int16-bit products,change scale to follow CF
2567 // I find that one 1B21 variable binStormHeight has fillvalue -9999,
2568 // so add _FillValue -9999 for int16-bit variables.
2569 for(const auto &fd:spsds){
2570
2571 if(fd->getFieldType() == 0 && fd->getType()==DFNT_INT16) {
2572
2573 AttrTable *at = das.get_table(fd->getNewName());
2574 if (!at)
2575 at = das.add_table(fd->getNewName(), new AttrTable);
2576 AttrTable::Attr_iter it = at->attr_begin();
2577
2578 while (it!=at->attr_end()) {
2579
2580 if(at->get_name(it)=="scale_factor")
2581 {
2582 // Scale type and value
2583 string scale_factor_value="";
2584 string scale_factor_type;
2585
2586 scale_factor_value = (*at->get_attr_vector(it)->begin());
2587 scale_factor_type = at->get_type(it);
2588
2589 if(scale_factor_type == "Float64") {
2590 double new_scale = 1.0/strtod(scale_factor_value.c_str(),nullptr);
2591 at->del_attr("scale_factor");
2592 string print_rep = HDFCFUtil::print_attr(DFNT_FLOAT64,0,(void*)(&new_scale));
2593 at->append_attr("scale_factor", scale_factor_type,print_rep);
2594
2595 }
2596
2597 if(scale_factor_type == "Float32") {
2598 float new_scale = 1.0f/strtof(scale_factor_value.c_str(),nullptr);
2599 at->del_attr("scale_factor");
2600 string print_rep = HDFCFUtil::print_attr(DFNT_FLOAT32,0,(void*)(&new_scale));
2601 at->append_attr("scale_factor", scale_factor_type,print_rep);
2602 }
2603
2604 break;
2605
2606 }
2607 ++it;
2608
2609 }
2610
2611 at->append_attr("_FillValue","Int16","-9999");
2612
2613 }
2614 }
2615 }
2616
2617 // For 1B01 product, just add the fillvalue.
2618 else if (t1b01_flag == true) {
2619
2620 for (const auto &fd:spsds){
2621
2622 if(fd->getFieldType() == 0 && fd->getType()==DFNT_FLOAT32) {
2623
2624 AttrTable *at = das.get_table(fd->getNewName());
2625 if (!at)
2626 at = das.add_table(fd->getNewName(), new AttrTable);
2627
2628 at->append_attr("_FillValue","Float32","-9999.9");
2629 }
2630 }
2631 }
2632
2633 AttrTable *at = das.get_table("HDF_GLOBAL");
2634 if (!at)
2635 at = das.add_table("HDF_GLOBAL", new AttrTable);
2636 string references ="http://pps.gsfc.nasa.gov/Documents/filespec.TRMM.V7.pdf";
2637 string comment="The HDF4 OPeNDAP handler adds _FillValue, valid_min and valid_max for some TRMM level 1 and level 2 products.";
2638 comment= comment + " It also changes scale_factor to follow CF conventions. ";
2639
2640 at->append_attr("comment","String",comment);
2641 at->append_attr("references","String",references);
2642
2643 }
2644
2645}
2646
2647//
2648// Many CERES products compose of multiple groups
2649// There are many fields in CERES data(a few hundred) and the full name(with the additional path)
2650// is very long. It causes Java clients choken since Java clients append names in the URL
2651// To improve the performance and to make Java clients access the data, we will ignore
2652// the path of these fields. Users can turn off this feature by commenting out the line: H4.EnableCERESMERRAShortName=true
2653// or can set the H4.EnableCERESMERRAShortName=false
2654// We still preserve the full path as an attribute in case users need to check them.
2655// Kent 2012-6-29
2656
2657void HDFCFUtil::handle_merra_ceres_attrs_with_bes_keys(const HDFSP::File *f, DAS &das,const string& filename) {
2658
2659 string base_filename = filename.substr(filename.find_last_of("/")+1);
2660
2661#if 0
2662 string check_ceres_merra_short_name_key="H4.EnableCERESMERRAShortName";
2663 bool turn_on_ceres_merra_short_name_key= false;
2664
2665 turn_on_ceres_merra_short_name_key = HDFCFUtil::check_beskeys(check_ceres_merra_short_name_key);
2666#endif
2667
2668 bool merra_is_eos2 = false;
2669 if (0== (base_filename.compare(0,5,"MERRA"))) {
2670
2671 for (const auto & fd:f->getSD()->getAttributes()) {
2672 // Check if this MERRA file is an HDF-EOS2 or not.
2673 if((fd->getName().compare(0, 14, "StructMetadata" )== 0) ||
2674 (fd->getName().compare(0, 14, "structmetadata" )== 0)) {
2675 merra_is_eos2 = true;
2676 break;
2677 }
2678
2679 }
2680 }
2681
2682 if (true == HDF4RequestHandler::get_enable_ceres_merra_short_name() && (CER_ES4 == f->getSPType() || CER_SRB == f->getSPType()
2683 || CER_CDAY == f->getSPType() || CER_CGEO == f->getSPType()
2684 || CER_SYN == f->getSPType() || CER_ZAVG == f->getSPType()
2685 || CER_AVG == f->getSPType() || (true == merra_is_eos2))) {
2686
2687 const vector<HDFSP::SDField *>& spsds = f->getSD()->getFields();
2688 for (const auto & fd:spsds){
2689
2690 AttrTable *at = das.get_table(fd->getNewName());
2691 if (!at)
2692 at = das.add_table(fd->getNewName(), new AttrTable);
2693
2694 at->append_attr("fullpath","String",fd->getSpecFullPath());
2695
2696 }
2697 }
2698}
2699
2700
2701// Handle the attributes when the BES key EnableVdataDescAttr is enabled..
2702void HDFCFUtil::handle_vdata_attrs_with_desc_key(const HDFSP::File*f,libdap::DAS &das) {
2703
2704 // Check the EnableVdataDescAttr key. If this key is turned on, the handler-added attribute VDdescname and
2705 // the attributes of vdata and vdata fields will be outputed to DAS. Otherwise, these attributes will
2706 // not outputed to DAS. The key will be turned off by default to shorten the DAP output. KY 2012-09-18
2707
2708#if 0
2709 string check_vdata_desc_key="H4.EnableVdataDescAttr";
2710 bool turn_on_vdata_desc_key= false;
2711
2712 turn_on_vdata_desc_key = HDFCFUtil::check_beskeys(check_vdata_desc_key);
2713#endif
2714
2715 string VDdescname = "hdf4_vd_desc";
2716 string VDdescvalue = "This is an HDF4 Vdata.";
2717 string VDfieldprefix = "Vdata_field_";
2718 string VDattrprefix = "Vdata_attr_";
2719 string VDfieldattrprefix ="Vdata_field_attr_";
2720
2721 // To speed up the performance for handling CERES data, we turn off some CERES vdata fields, this should be resumed in the future version with BESKeys.
2722#if 0
2723 string check_ceres_vdata_key="H4.EnableCERESVdata";
2724 bool turn_on_ceres_vdata_key= false;
2725 turn_on_ceres_vdata_key = HDFCFUtil::check_beskeys(check_ceres_vdata_key);
2726#endif
2727
2728 bool output_vdata_flag = true;
2729 if (false == HDF4RequestHandler::get_enable_ceres_vdata() &&
2730 (CER_AVG == f->getSPType() ||
2731 CER_ES4 == f->getSPType() ||
2732 CER_SRB == f->getSPType() ||
2733 CER_ZAVG == f->getSPType()))
2734 output_vdata_flag = false;
2735
2736 if (true == output_vdata_flag) {
2737
2738 for (const auto &vd:f->getVDATAs()) {
2739
2740 AttrTable *at = das.get_table(vd->getNewName());
2741 if(!at)
2742 at = das.add_table(vd->getNewName(),new AttrTable);
2743
2744 if (true == HDF4RequestHandler::get_enable_vdata_desc_attr()) {
2745 // Add special vdata attributes
2746 bool emptyvddasflag = true;
2747 if (!(vd->getAttributes().empty()))
2748 emptyvddasflag = false;
2749 if (vd->getTreatAsAttrFlag())
2750 emptyvddasflag = false;
2751 else {
2752 for (const auto &vfd:vd->getFields()) {
2753 if(!(vfd->getAttributes().empty())) {
2754 emptyvddasflag = false;
2755 break;
2756 }
2757 }
2758 }
2759
2760 if(emptyvddasflag)
2761 continue;
2762 at->append_attr(VDdescname, "String" , VDdescvalue);
2763
2764 for(const auto &va:vd->getAttributes()) {
2765
2766 if(va->getType()==DFNT_UCHAR || va->getType() == DFNT_CHAR){
2767
2768 string tempstring2(va->getValue().begin(),va->getValue().end());
2769 string tempfinalstr= string(tempstring2.c_str());
2770 at->append_attr(VDattrprefix+va->getNewName(), "String" , tempfinalstr);
2771 }
2772 else {
2773 for (int loc=0; loc < va->getCount() ; loc++) {
2774 string print_rep = HDFCFUtil::print_attr(va->getType(), loc, (void*) &(va->getValue()[0]));
2775 at->append_attr(VDattrprefix+va->getNewName(), HDFCFUtil::print_type(va->getType()), print_rep);
2776 }
2777 }
2778 }
2779 }
2780
2781 if(false == (vd->getTreatAsAttrFlag())){
2782
2783 if (true == HDF4RequestHandler::get_enable_vdata_desc_attr()) {
2784
2785 //NOTE: for vdata field, we assume that no special characters are found. We need to escape the special characters when the data type is char.
2786 // We need to create a DAS container for each field so that the attributes can be put inside.
2787 for (const auto &vdf:vd->getFields()) {
2788
2789 // This vdata field will NOT be treated as attributes, only save the field attribute as the attribute
2790 // First check if the field has attributes, if it doesn't have attributes, no need to create a container.
2791
2792 if (vdf->getAttributes().empty() ==false) {
2793
2794 AttrTable *at_v = das.get_table(vdf->getNewName());
2795 if(!at_v)
2796 at_v = das.add_table(vdf->getNewName(),new AttrTable);
2797
2798 for (const auto &va:vdf->getAttributes()) {
2799
2800 if(va->getType()==DFNT_UCHAR || va->getType() == DFNT_CHAR){
2801
2802 string tempstring2(va->getValue().begin(),va->getValue().end());
2803 string tempfinalstr= string(tempstring2.c_str());
2804 at_v->append_attr(va->getNewName(), "String" , tempfinalstr);
2805 }
2806 else {
2807 for (int loc=0; loc < va->getCount() ; loc++) {
2808 string print_rep = HDFCFUtil::print_attr(va->getType(), loc, (void*) &(va->getValue()[0]));
2809 at_v->append_attr(va->getNewName(), HDFCFUtil::print_type(va->getType()), print_rep);
2810 }
2811 }
2812
2813 }
2814 }
2815 }
2816 }
2817
2818 }
2819
2820 else {
2821
2822 for(const auto & vdf:vd->getFields()) {
2823
2824 if(vdf->getFieldOrder() == 1) {
2825 if(vdf->getType()==DFNT_UCHAR || vdf->getType() == DFNT_CHAR){
2826 string tempfinalstr;
2827 tempfinalstr.resize(vdf->getValue().size());
2828 copy(vdf->getValue().begin(),vdf->getValue().end(),tempfinalstr.begin());
2829 at->append_attr(VDfieldprefix+vdf->getNewName(), "String" , tempfinalstr);
2830 }
2831 else {
2832 for ( int loc=0; loc < vdf->getNumRec(); loc++) {
2833 string print_rep = HDFCFUtil::print_attr(vdf->getType(), loc, (void*) &(vdf->getValue()[0]));
2834 at->append_attr(VDfieldprefix+vdf->getNewName(), HDFCFUtil::print_type(vdf->getType()), print_rep);
2835 }
2836 }
2837 }
2838 else {//When field order is greater than 1,we want to print each record in group with single quote,'0 1 2','3 4 5', etc.
2839
2840 if (vdf->getValue().size() != (unsigned int)(DFKNTsize(vdf->getType())*(vdf->getFieldOrder())*(vdf->getNumRec()))){
2841 throw InternalErr(__FILE__,__LINE__,"the vdata field size doesn't match the vector value");
2842 }
2843
2844 if(vdf->getNumRec()==1){
2845 if(vdf->getType()==DFNT_UCHAR || vdf->getType() == DFNT_CHAR){
2846 string tempstring2(vdf->getValue().begin(),vdf->getValue().end());
2847 auto tempfinalstr= string(tempstring2.c_str());
2848 at->append_attr(VDfieldprefix+vdf->getNewName(),"String",tempfinalstr);
2849 }
2850 else {
2851 for (int loc=0; loc < vdf->getFieldOrder(); loc++) {
2852 string print_rep = HDFCFUtil::print_attr(vdf->getType(), loc, (void*) &(vdf->getValue()[0]));
2853 at->append_attr(VDfieldprefix+vdf->getNewName(), HDFCFUtil::print_type(vdf->getType()), print_rep);
2854 }
2855 }
2856
2857 }
2858
2859 else {
2860 if(vdf->getType()==DFNT_UCHAR || vdf->getType() == DFNT_CHAR){
2861
2862 for(int tempcount = 0; tempcount < vdf->getNumRec()*DFKNTsize(vdf->getType());tempcount ++) {
2863 vector<char>::const_iterator tempit;
2864 tempit = vdf->getValue().begin()+tempcount*(vdf->getFieldOrder());
2865 string tempstring2(tempit,tempit+vdf->getFieldOrder());
2866 auto tempfinalstr= string(tempstring2.c_str());
2867 string tempoutstring = "'"+tempfinalstr+"'";
2868 at->append_attr(VDfieldprefix+vdf->getNewName(),"String",tempoutstring);
2869 }
2870 }
2871
2872 else {
2873 for(int tempcount = 0; tempcount < vdf->getNumRec();tempcount ++) {
2874 at->append_attr(VDfieldprefix+vdf->getNewName(),HDFCFUtil::print_type(vdf->getType()),"'");
2875 for (int loc=0; loc < vdf->getFieldOrder(); loc++) {
2876 string print_rep = HDFCFUtil::print_attr(vdf->getType(), loc, (void*) &(vdf->getValue()[tempcount*(vdf->getFieldOrder())]));
2877 at->append_attr(VDfieldprefix+vdf->getNewName(), HDFCFUtil::print_type(vdf->getType()), print_rep);
2878 }
2879 at->append_attr(VDfieldprefix+vdf->getNewName(),HDFCFUtil::print_type(vdf->getType()),"'");
2880 }
2881 }
2882 }
2883 }
2884
2885
2886 if (true == HDF4RequestHandler::get_enable_vdata_desc_attr()) {
2887 for(const auto &va:vdf->getAttributes()) {
2888
2889 if(va->getType()==DFNT_UCHAR || va->getType() == DFNT_CHAR){
2890
2891 string tempstring2(va->getValue().begin(),va->getValue().end());
2892 auto tempfinalstr= string(tempstring2.c_str());
2893 at->append_attr(VDfieldattrprefix+va->getNewName(), "String" , tempfinalstr);
2894 }
2895 else {
2896 for (int loc=0; loc < va->getCount() ; loc++) {
2897 string print_rep = HDFCFUtil::print_attr(va->getType(), loc, (void*) &(va->getValue()[0]));
2898 at->append_attr(VDfieldattrprefix+va->getNewName(), HDFCFUtil::print_type(va->getType()), print_rep);
2899 }
2900 }
2901 }
2902 }
2903 }
2904 }
2905
2906 }
2907 }
2908
2909}
2910
2911void HDFCFUtil::map_eos2_objects_attrs(libdap::DAS &das,const string &filename) {
2912
2913 intn status_n =-1;
2914 int32 status_32 = -1;
2915 int32 file_id = -1;
2916 int32 vgroup_id = -1;
2917 int32 num_of_lones = 0;
2918 uint16 name_len = 0;
2919
2920 file_id = Hopen (filename.c_str(), DFACC_READ, 0);
2921 if(file_id == FAIL)
2922 throw InternalErr(__FILE__,__LINE__,"Hopen failed.");
2923
2924 status_n = Vstart (file_id);
2925 if(status_n == FAIL) {
2926 Hclose(file_id);
2927 throw InternalErr(__FILE__,__LINE__,"Vstart failed.");
2928 }
2929
2930 string err_msg;
2931 bool unexpected_fail = false;
2932 //Get and print the names and class names of all the lone vgroups.
2933 // First, call Vlone with num_of_lones set to 0 to get the number of
2934 // lone vgroups in the file, but not to get their reference numbers.
2935 num_of_lones = Vlone (file_id, nullptr, num_of_lones );
2936
2937 //
2938 // Then, if there are any lone vgroups,
2939 if (num_of_lones > 0)
2940 {
2941 // Use the num_of_lones returned to allocate sufficient space for the
2942 // buffer ref_array to hold the reference numbers of all lone vgroups,
2943 vector<int32> ref_array;
2944 ref_array.resize(num_of_lones);
2945
2946
2947 // and call Vlone again to retrieve the reference numbers into
2948 // the buffer ref_array.
2949
2950 num_of_lones = Vlone (file_id, ref_array.data(), num_of_lones);
2951
2952 // Loop the name and class of each lone vgroup.
2953 for (int lone_vg_number = 0; lone_vg_number < num_of_lones;
2954 lone_vg_number++)
2955 {
2956
2957 // Attach to the current vgroup then get and display its
2958 // name and class. Note: the current vgroup must be detached before
2959 // moving to the next.
2960 vgroup_id = Vattach(file_id, ref_array[lone_vg_number], "r");
2961 if(vgroup_id == FAIL) {
2962 unexpected_fail = true;
2963 err_msg = string(ERR_LOC) + " Vattach failed. ";
2964 goto cleanFail;
2965 }
2966
2967 status_32 = Vgetnamelen(vgroup_id, &name_len);
2968 if(status_32 == FAIL) {
2969 unexpected_fail = true;
2970 Vdetach(vgroup_id);
2971 err_msg = string(ERR_LOC) + " Vgetnamelen failed. ";
2972 goto cleanFail;
2973 }
2974
2975 vector<char> vgroup_name;
2976 vgroup_name.resize(name_len+1);
2977 status_32 = Vgetname (vgroup_id, vgroup_name.data());
2978 if(status_32 == FAIL) {
2979 unexpected_fail = true;
2980 Vdetach(vgroup_id);
2981 err_msg = string(ERR_LOC) + " Vgetname failed. ";
2982 goto cleanFail;
2983 }
2984
2985 status_32 = Vgetclassnamelen(vgroup_id, &name_len);
2986 if(status_32 == FAIL) {
2987 unexpected_fail = true;
2988 Vdetach(vgroup_id);
2989 err_msg = string(ERR_LOC) + " Vgetclassnamelen failed. ";
2990 goto cleanFail;
2991 }
2992
2993 vector<char>vgroup_class;
2994 vgroup_class.resize(name_len+1);
2995 status_32 = Vgetclass (vgroup_id, vgroup_class.data());
2996 if(status_32 == FAIL) {
2997 unexpected_fail = true;
2998 Vdetach(vgroup_id);
2999 err_msg = string(ERR_LOC) + " Vgetclass failed. ";
3000 goto cleanFail;
3001 }
3002
3003 string vgroup_name_str(vgroup_name.begin(),vgroup_name.end());
3004 vgroup_name_str = vgroup_name_str.substr(0,vgroup_name_str.size()-1);
3005
3006 string vgroup_class_str(vgroup_class.begin(),vgroup_class.end());
3007 vgroup_class_str = vgroup_class_str.substr(0,vgroup_class_str.size()-1);
3008 try {
3009 if(vgroup_class_str =="GRID")
3010 map_eos2_one_object_attrs_wrapper(das,file_id,vgroup_id,vgroup_name_str,true);
3011 else if(vgroup_class_str =="SWATH")
3012 map_eos2_one_object_attrs_wrapper(das,file_id,vgroup_id,vgroup_name_str,false);
3013 }
3014 catch(...) {
3015 Vdetach(vgroup_id);
3016 Vend(file_id);
3017 Hclose(file_id);
3018 throw InternalErr(__FILE__,__LINE__,"map_eos2_one_object_attrs_wrapper failed.");
3019 }
3020 Vdetach (vgroup_id);
3021 }// for
3022 }// if
3023
3024 //Terminate access to the V interface and close the file.
3025cleanFail:
3026 Vend (file_id);
3027 Hclose (file_id);
3028 if(true == unexpected_fail)
3029 throw InternalErr(__FILE__,__LINE__,err_msg);
3030
3031 return;
3032
3033}
3034
3035void HDFCFUtil::map_eos2_one_object_attrs_wrapper(libdap:: DAS &das,int32 file_id,int32 vgroup_id, const string& vgroup_name,bool is_grid) {
3036
3037 int32 num_gobjects = Vntagrefs (vgroup_id);
3038 if(num_gobjects < 0)
3039 throw InternalErr(__FILE__,__LINE__,"Cannot obtain the number of objects under a vgroup.");
3040
3041 for(int i = 0; i<num_gobjects;i++) {
3042
3043 int32 obj_tag;
3044 int32 obj_ref;
3045 if (Vgettagref (vgroup_id, i, &obj_tag, &obj_ref) == FAIL)
3046 throw InternalErr(__FILE__,__LINE__,"Failed to obtain the tag and reference of an object under a vgroup.");
3047
3048 if (Visvg (vgroup_id, obj_ref) == TRUE) {
3049
3050 int32 object_attr_vgroup = Vattach(file_id,obj_ref,"r");
3051 if(object_attr_vgroup == FAIL)
3052 throw InternalErr(__FILE__,__LINE__,"Failed to attach an EOS2 vgroup.");
3053
3054 uint16 name_len = 0;
3055 int32 status_32 = Vgetnamelen(object_attr_vgroup, &name_len);
3056 if(status_32 == FAIL) {
3057 Vdetach(object_attr_vgroup);
3058 throw InternalErr(__FILE__,__LINE__,"Failed to obtain an EOS2 vgroup name length.");
3059 }
3060 vector<char> attr_vgroup_name;
3061 attr_vgroup_name.resize(name_len+1);
3062 status_32 = Vgetname (object_attr_vgroup, attr_vgroup_name.data());
3063 if(status_32 == FAIL) {
3064 Vdetach(object_attr_vgroup);
3065 throw InternalErr(__FILE__,__LINE__,"Failed to obtain an EOS2 vgroup name. ");
3066 }
3067
3068 string attr_vgroup_name_str(attr_vgroup_name.begin(),attr_vgroup_name.end());
3069 attr_vgroup_name_str = attr_vgroup_name_str.substr(0,attr_vgroup_name_str.size()-1);
3070
3071 try {
3072 if(true == is_grid && attr_vgroup_name_str=="Grid Attributes"){
3073 map_eos2_one_object_attrs(das,file_id,object_attr_vgroup,vgroup_name);
3074 Vdetach(object_attr_vgroup);
3075 break;
3076 }
3077 else if(false == is_grid && attr_vgroup_name_str=="Swath Attributes") {
3078 map_eos2_one_object_attrs(das,file_id,object_attr_vgroup,vgroup_name);
3079 Vdetach(object_attr_vgroup);
3080 break;
3081 }
3082 }
3083 catch(...) {
3084 Vdetach(object_attr_vgroup);
3085 throw InternalErr(__FILE__,__LINE__,"Cannot map eos2 object attributes to DAP2.");
3086 }
3087 Vdetach(object_attr_vgroup);
3088
3089 }
3090
3091 }
3092}
3093
3094void HDFCFUtil::map_eos2_one_object_attrs(libdap:: DAS &das,int32 file_id, int32 obj_attr_group_id, const string& vgroup_name) {
3095
3096 int32 num_gobjects = Vntagrefs(obj_attr_group_id);
3097 if(num_gobjects < 0)
3098 throw InternalErr(__FILE__,__LINE__,"Cannot obtain the number of objects under a vgroup.");
3099
3100 string vgroup_cf_name = HDFCFUtil::get_CF_string(vgroup_name);
3101 AttrTable *at = das.get_table(vgroup_cf_name);
3102 if(!at)
3103 at = das.add_table(vgroup_cf_name,new AttrTable);
3104
3105 for(int i = 0; i<num_gobjects;i++) {
3106
3107 int32 obj_tag;
3108 int32 obj_ref;
3109
3110 if (Vgettagref(obj_attr_group_id, i, &obj_tag, &obj_ref) == FAIL)
3111 throw InternalErr(__FILE__,__LINE__,"Failed to obtain the tag and reference of an object under a vgroup.");
3112
3113 if(Visvs(obj_attr_group_id,obj_ref)) {
3114
3115 int32 vdata_id = VSattach(file_id,obj_ref,"r");
3116 if(vdata_id == FAIL)
3117 throw InternalErr(__FILE__,__LINE__,"Failed to attach a vdata.");
3118
3119 // EOS2 object vdatas are actually EOS2 object attributes.
3120 if(VSisattr(vdata_id)) {
3121
3122 // According to EOS2 library, EOS2 number of field and record must be 1.
3123 int32 num_field = VFnfields(vdata_id);
3124 if(num_field !=1) {
3125 VSdetach(vdata_id);
3126 throw InternalErr(__FILE__,__LINE__,"Number of vdata field for an EOS2 object must be 1.");
3127 }
3128 int32 num_record = VSelts(vdata_id);
3129 if(num_record !=1){
3130 VSdetach(vdata_id);
3131 throw InternalErr(__FILE__,__LINE__,"Number of vdata record for an EOS2 object must be 1.");
3132 }
3133 char vdata_name[VSNAMELENMAX];
3134 if(VSQueryname(vdata_id,vdata_name) == FAIL) {
3135 VSdetach(vdata_id);
3136 throw InternalErr(__FILE__,__LINE__,"Failed to obtain EOS2 object vdata name.");
3137 }
3138 string vdatanamestr(vdata_name);
3139 string vdataname_cfstr = HDFCFUtil::get_CF_string(vdatanamestr);
3140 int32 fieldsize = VFfieldesize(vdata_id,0);
3141 if(fieldsize == FAIL) {
3142 VSdetach(vdata_id);
3143 throw InternalErr(__FILE__,__LINE__,"Failed to obtain EOS2 object vdata field size.");
3144 }
3145
3146 const char* fieldname = VFfieldname(vdata_id,0);
3147 if(fieldname == nullptr) {
3148 VSdetach(vdata_id);
3149 throw InternalErr(__FILE__,__LINE__,"Failed to obtain EOS2 object vdata field name.");
3150 }
3151 int32 fieldtype = VFfieldtype(vdata_id,0);
3152 if(fieldtype == FAIL) {
3153 VSdetach(vdata_id);
3154 throw InternalErr(__FILE__,__LINE__,"Failed to obtain EOS2 object vdata field type.");
3155 }
3156
3157 if(VSsetfields(vdata_id,fieldname) == FAIL) {
3158 VSdetach(vdata_id);
3159 throw InternalErr(__FILE__,__LINE__,"EOS2 object vdata: VSsetfields failed.");
3160 }
3161
3162 vector<char> vdata_value;
3163 vdata_value.resize(fieldsize);
3164 if(VSread(vdata_id,(uint8*)vdata_value.data(),1,FULL_INTERLACE) == FAIL) {
3165 VSdetach(vdata_id);
3166 throw InternalErr(__FILE__,__LINE__,"EOS2 object vdata: VSread failed.");
3167 }
3168
3169 // Map the attributes to DAP2.
3170 if(fieldtype == DFNT_UCHAR || fieldtype == DFNT_CHAR){
3171 string tempstring(vdata_value.begin(),vdata_value.end());
3172 // Remove the nullptr term
3173 auto tempstring2 = string(tempstring.c_str());
3174 at->append_attr(vdataname_cfstr,"String",tempstring2);
3175 }
3176 else {
3177 string print_rep = HDFCFUtil::print_attr(fieldtype, 0, (void*) vdata_value.data());
3178 at->append_attr(vdataname_cfstr, HDFCFUtil::print_type(fieldtype), print_rep);
3179 }
3180
3181 }
3182 VSdetach(vdata_id);
3183
3184 }
3185 }
3186
3187 return;
3188}
3189
3190// The function that escapes the special characters is no longer needed after we move that functionality to libdap4.
3191// Will keep the following function as an #if 0/#endif block for a while and then the code should be removed. KY 2022-11-22
3192#if 0
3193// Part of a large fix for attributes. Escaping the values of the attributes
3194// may have been a bad idea. It breaks using JSON, for example. If this is a
3195// bad idea - to turn of escaping - then we'll have to figure out how to store
3196// 'serialized JSON' in attributes because it's being used in netcdf/hdf files.
3197// If we stick with this, there's clearly a more performant solution - eliminate
3198// the calls to this code.
3199// jhrg 6/25/21
3200#define ESCAPE_STRING_ATTRIBUTES 0
3201
3202string HDFCFUtil::escattr(string s)
3203{
3204// We should not handle any special characters here. These will be handled by libdap4. Still leave the code here for the time being.
3205// Eventually this function will be removed. KY 2022-08-25
3206#if ESCAPE_STRING_ATTRIBUTES
3207 const string printable = " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~`!@#$%^&*()_-+={[}]|\\:;<,>.?/'\"\n\t\r";
3208 const string ESC = "\\";
3209 const string DOUBLE_ESC = ESC + ESC;
3210 const string QUOTE = "\"";
3211 const string ESCQUOTE = ESC + QUOTE;
3212
3213 // escape \ with a second backslash
3214 size_t ind = 0;
3215 while ((ind = s.find(ESC, ind)) != string::npos) {
3216 s.replace(ind, 1, DOUBLE_ESC);
3217 ind += DOUBLE_ESC.size();
3218 }
3219
3220 // escape " with backslash
3221 ind = 0;
3222 while ((ind = s.find(QUOTE, ind)) != string::npos) {
3223 //comment out the following line since it wastes the CPU operation.
3224 s.replace(ind, 1, ESCQUOTE);
3225 ind += ESCQUOTE.size();
3226 }
3227
3228 size_t ind = 0;
3229 while ((ind = s.find_first_not_of(printable, ind)) != string::npos) {
3230 s.replace(ind, 1, ESC + octstring(s[ind]));
3231 }
3232#endif
3233
3234 return s;
3235}
3236#endif
3237
3238// This function is necessary since char is represented as string. For fillvalue, this has to be resumed.
3239string HDFCFUtil::escattr_fvalue(string s)
3240{
3241 const string printable = " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~`!@#$%^&*()_-+={[}]|\\:;<,>.?/'\"\n\t\r";
3242 const string ESC = "\\";
3243 size_t ind = 0;
3244 while ((ind = s.find_first_not_of(printable, ind)) != string::npos) {
3245 s.replace(ind, 1, ESC + octstring(s[ind]));
3246 }
3247
3248 return s;
3249}
3250
3251
3252void HDFCFUtil::parser_trmm_v7_gridheader(const vector<char>& value,
3253 int& latsize, int&lonsize,
3254 float& lat_start, float& lon_start,
3255 float& lat_res, float& lon_res,
3256 bool check_reg_orig ){
3257
3258 float lat_north = 0.;
3259 float lat_south = 0.;
3260 float lon_east = 0.;
3261 float lon_west = 0.;
3262
3263 vector<string> ind_elems;
3264 char sep='\n';
3265 HDFCFUtil::Split(value.data(),sep,ind_elems);
3266 // The number of elements in the GridHeader is 9.
3267 //The string vector will add a leftover. So the size should be 10.
3268 // For the MacOS clang compiler, the string vector size may become 11.
3269 // So we change the condition to be "<9" is wrong.
3270 if(ind_elems.size()<9)
3271 throw InternalErr(__FILE__,__LINE__,"The number of elements in the TRMM level 3 GridHeader is not right.");
3272
3273 if(false == check_reg_orig) {
3274 if (0 != ind_elems[1].find("Registration=CENTER"))
3275 throw InternalErr(__FILE__,__LINE__,"The TRMM grid registration is not center.");
3276 }
3277
3278 if (0 == ind_elems[2].find("LatitudeResolution")){
3279
3280 size_t equal_pos = ind_elems[2].find_first_of('=');
3281 if(string::npos == equal_pos)
3282 throw InternalErr(__FILE__,__LINE__,"Cannot find latitude resolution for TRMM level 3 products");
3283
3284 size_t scolon_pos = ind_elems[2].find_first_of(';');
3285 if(string::npos == scolon_pos)
3286 throw InternalErr(__FILE__,__LINE__,"Cannot find latitude resolution for TRMM level 3 products");
3287 if (equal_pos < scolon_pos){
3288
3289 string latres_str = ind_elems[2].substr(equal_pos+1,scolon_pos-equal_pos-1);
3290 lat_res = strtof(latres_str.c_str(),nullptr);
3291 }
3292 else
3293 throw InternalErr(__FILE__,__LINE__,"latitude resolution is not right for TRMM level 3 products");
3294 }
3295 else
3296 throw InternalErr(__FILE__,__LINE__,"The TRMM grid LatitudeResolution doesn't exist.");
3297
3298 if (0 == ind_elems[3].find("LongitudeResolution")){
3299
3300 size_t equal_pos = ind_elems[3].find_first_of('=');
3301 if(string::npos == equal_pos)
3302 throw InternalErr(__FILE__,__LINE__,"Cannot find longitude resolution for TRMM level 3 products");
3303
3304 size_t scolon_pos = ind_elems[3].find_first_of(';');
3305 if(string::npos == scolon_pos)
3306 throw InternalErr(__FILE__,__LINE__,"Cannot find longitude resolution for TRMM level 3 products");
3307 if (equal_pos < scolon_pos){
3308 string lonres_str = ind_elems[3].substr(equal_pos+1,scolon_pos-equal_pos-1);
3309 lon_res = strtof(lonres_str.c_str(),nullptr);
3310 }
3311 else
3312 throw InternalErr(__FILE__,__LINE__,"longitude resolution is not right for TRMM level 3 products");
3313 }
3314 else
3315 throw InternalErr(__FILE__,__LINE__,"The TRMM grid LongitudeResolution doesn't exist.");
3316
3317 if (0 == ind_elems[4].find("NorthBoundingCoordinate")){
3318
3319 size_t equal_pos = ind_elems[4].find_first_of('=');
3320 if(string::npos == equal_pos)
3321 throw InternalErr(__FILE__,__LINE__,"Cannot find latitude resolution for TRMM level 3 products");
3322
3323 size_t scolon_pos = ind_elems[4].find_first_of(';');
3324 if(string::npos == scolon_pos)
3325 throw InternalErr(__FILE__,__LINE__,"Cannot find latitude resolution for TRMM level 3 products");
3326 if (equal_pos < scolon_pos){
3327 string north_bounding_str = ind_elems[4].substr(equal_pos+1,scolon_pos-equal_pos-1);
3328 lat_north = strtof(north_bounding_str.c_str(),nullptr);
3329 }
3330 else
3331 throw InternalErr(__FILE__,__LINE__,"NorthBoundingCoordinate is not right for TRMM level 3 products");
3332
3333 }
3334 else
3335 throw InternalErr(__FILE__,__LINE__,"The TRMM grid NorthBoundingCoordinate doesn't exist.");
3336
3337 if (0 == ind_elems[5].find("SouthBoundingCoordinate")){
3338
3339 size_t equal_pos = ind_elems[5].find_first_of('=');
3340 if(string::npos == equal_pos)
3341 throw InternalErr(__FILE__,__LINE__,"Cannot find south bound coordinate for TRMM level 3 products");
3342
3343 size_t scolon_pos = ind_elems[5].find_first_of(';');
3344 if(string::npos == scolon_pos)
3345 throw InternalErr(__FILE__,__LINE__,"Cannot find south bound coordinate for TRMM level 3 products");
3346 if (equal_pos < scolon_pos){
3347 string lat_south_str = ind_elems[5].substr(equal_pos+1,scolon_pos-equal_pos-1);
3348 lat_south = strtof(lat_south_str.c_str(),nullptr);
3349 }
3350 else
3351 throw InternalErr(__FILE__,__LINE__,"south bound coordinate is not right for TRMM level 3 products");
3352
3353 }
3354 else
3355 throw InternalErr(__FILE__,__LINE__,"The TRMM grid SouthBoundingCoordinate doesn't exist.");
3356
3357 if (0 == ind_elems[6].find("EastBoundingCoordinate")){
3358
3359 size_t equal_pos = ind_elems[6].find_first_of('=');
3360 if(string::npos == equal_pos)
3361 throw InternalErr(__FILE__,__LINE__,"Cannot find south bound coordinate for TRMM level 3 products");
3362
3363 size_t scolon_pos = ind_elems[6].find_first_of(';');
3364 if(string::npos == scolon_pos)
3365 throw InternalErr(__FILE__,__LINE__,"Cannot find south bound coordinate for TRMM level 3 products");
3366 if (equal_pos < scolon_pos){
3367 string lon_east_str = ind_elems[6].substr(equal_pos+1,scolon_pos-equal_pos-1);
3368 lon_east = strtof(lon_east_str.c_str(),nullptr);
3369 }
3370 else
3371 throw InternalErr(__FILE__,__LINE__,"south bound coordinate is not right for TRMM level 3 products");
3372
3373 }
3374 else
3375 throw InternalErr(__FILE__,__LINE__,"The TRMM grid EastBoundingCoordinate doesn't exist.");
3376
3377 if (0 == ind_elems[7].find("WestBoundingCoordinate")){
3378
3379 size_t equal_pos = ind_elems[7].find_first_of('=');
3380 if(string::npos == equal_pos)
3381 throw InternalErr(__FILE__,__LINE__,"Cannot find south bound coordinate for TRMM level 3 products");
3382
3383 size_t scolon_pos = ind_elems[7].find_first_of(';');
3384 if(string::npos == scolon_pos)
3385 throw InternalErr(__FILE__,__LINE__,"Cannot find south bound coordinate for TRMM level 3 products");
3386 if (equal_pos < scolon_pos){
3387 string lon_west_str = ind_elems[7].substr(equal_pos+1,scolon_pos-equal_pos-1);
3388 lon_west = strtof(lon_west_str.c_str(),nullptr);
3389 }
3390 else
3391 throw InternalErr(__FILE__,__LINE__,"south bound coordinate is not right for TRMM level 3 products");
3392
3393 }
3394 else
3395 throw InternalErr(__FILE__,__LINE__,"The TRMM grid WestBoundingCoordinate doesn't exist.");
3396
3397 if (false == check_reg_orig) {
3398 if (0 != ind_elems[8].find("Origin=SOUTHWEST"))
3399 throw InternalErr(__FILE__,__LINE__,"The TRMM grid origin is not SOUTHWEST.");
3400 }
3401
3402 // Since we only treat the case when the Registration is center, so the size should be the
3403 // regular number size - 1.
3404 latsize =(int)((lat_north-lat_south)/lat_res);
3405 lonsize =(int)((lon_east-lon_west)/lon_res);
3406 lat_start = lat_south;
3407 lon_start = lon_west;
3408}
3409
3410// Somehow the conversion of double to c++ string with sprintf causes the memory error in
3411// the testing code. I used the following code to do the conversion. Most part of the code
3412// in reverse, int_to_str and dtoa are adapted from geeksforgeeks.org
3413
3414// reverses a string 'str' of length 'len'
3415void HDFCFUtil::rev_str(char *str, int len)
3416{
3417 int i=0;
3418 int j=len-1;
3419 char temp = 0;
3420 while (i<j)
3421 {
3422 temp = str[i];
3423 str[i] = str[j];
3424 str[j] = temp;
3425 i++;
3426 j--;
3427 }
3428}
3429
3430// Converts a given integer x to string str[]. d is the number
3431// of digits required in output. If d is more than the number
3432// of digits in x, then 0s are added at the beginning.
3433int HDFCFUtil::int_to_str(int x, char str[], int d)
3434{
3435 int i = 0;
3436 while (x)
3437 {
3438 str[i++] = (x%10) + '0';
3439 x = x/10;
3440 }
3441
3442 // If number of digits required is more, then
3443 // add 0s at the beginning
3444 while (i < d)
3445 str[i++] = '0';
3446
3447 rev_str(str, i);
3448 str[i] = '\0';
3449 return i;
3450}
3451
3452// Converts a double floating point number to string.
3453void HDFCFUtil::dtoa(double n, char *res, int afterpoint)
3454{
3455 // Extract integer part
3456 auto ipart = (int)n;
3457
3458 // Extract the double part
3459 double fpart = n - (double)ipart;
3460
3461 // convert integer part to string
3462 int i = int_to_str(ipart, res, 0);
3463
3464 // check for display option after point
3465 if (afterpoint != 0)
3466 {
3467 res[i] = '.'; // add dot
3468
3469 // Get the value of fraction part upto given no.
3470 // of points after dot. The third parameter is needed
3471 // to handle cases like 233.007
3472 fpart = fpart * pow(10, afterpoint);
3473
3474 // A round-error of 1 is found when casting to the integer for some numbers.
3475 // We need to correct it.
3476 auto final_fpart = (int)fpart;
3477 if(fpart -(int)fpart >0.5)
3478 final_fpart = (int)fpart +1;
3479 int_to_str(final_fpart, res + i + 1, afterpoint);
3480 }
3481}
3482
3483
3484string HDFCFUtil::get_double_str(double x,int total_digit,int after_point) {
3485
3486 string str;
3487 if(x!=0) {
3488 vector<char> res;
3489 res.resize(total_digit);
3490 for(int i = 0; i<total_digit;i++)
3491 res[i] = '\0';
3492 if (x<0) {
3493 str.push_back('-');
3494 dtoa(-x,res.data(),after_point);
3495 for(int i = 0; i<total_digit;i++) {
3496 if(res[i] != '\0')
3497 str.push_back(res[i]);
3498 }
3499 }
3500 else {
3501 dtoa(x, res.data(), after_point);
3502 for(int i = 0; i<total_digit;i++) {
3503 if(res[i] != '\0')
3504 str.push_back(res[i]);
3505 }
3506 }
3507
3508 }
3509 else
3510 str.push_back('0');
3511
3512 return str;
3513
3514}
3515
3516string HDFCFUtil::get_int_str(int x) {
3517
3518 string str;
3519 if(x > 0 && x <10)
3520 str.push_back((char)x+'0');
3521
3522 else if (x >10 && x<100) {
3523 str.push_back((char)(x/10)+'0');
3524 str.push_back((char)(x%10)+'0');
3525 }
3526 else {
3527 int num_digit = 0;
3528 int abs_x = (x<0)?-x:x;
3529 while(abs_x/=10)
3530 num_digit++;
3531 if(x<=0)
3532 num_digit++;
3533 vector<char> buf;
3534 buf.resize(num_digit);
3535 snprintf(buf.data(),num_digit,"%d",x);
3536 str.assign(buf.data());
3537
3538 }
3539
3540 return str;
3541
3542}
3543
3544ssize_t HDFCFUtil::read_vector_from_file(int fd, vector<double> &val, size_t dtypesize) {
3545
3546 ssize_t ret_val;
3547 ret_val = read(fd,val.data(),val.size()*dtypesize);
3548
3549 return ret_val;
3550}
3551// Need to wrap a 'read buffer' from a pure file call here since read() is also a DAP function to read DAP data.
3552ssize_t HDFCFUtil::read_buffer_from_file(int fd, void*buf, size_t total_read) {
3553
3554 ssize_t ret_val;
3555 ret_val = read(fd,buf,total_read);
3556
3557 return ret_val;
3558}
3559void HDFCFUtil::close_fileid(int32 sdfd, int32 fileid,int32 gridfd, int32 swathfd,bool pass_fileid) {
3560
3561 if(false == pass_fileid) {
3562 if(sdfd != -1)
3563 SDend(sdfd);
3564 if(fileid != -1)
3565 Hclose(fileid);
3566#ifdef USE_HDFEOS2_LIB
3567 if(gridfd != -1)
3568 GDclose(gridfd);
3569 if(swathfd != -1)
3570 SWclose(swathfd);
3571
3572#endif
3573 }
3574
3575}
3576
3577// Obtain the cache name. Since AIRS version 6 level 3 all share the same latitude and longitude,
3578// we provide one set of latitude and longitude cache files for all AIRS level 3 version 6 products.
3579string HDFCFUtil::obtain_cache_fname(const string & fprefix, const string &fname, const string &vname) {
3580
3581 string cache_fname = fprefix;
3582 string base_file_name = basename(fname);
3583 // Identify this file from product name: AIRS, product level: .L3. or .L2. and version .v6.
3584 if((base_file_name.size() >12)
3585 && (base_file_name.compare(0,4,"AIRS") == 0)
3586 && (base_file_name.find(".L3.")!=string::npos)
3587 && (base_file_name.find(".v6.")!=string::npos)
3588 && ((vname == "Latitude") ||(vname == "Longitude")))
3589 cache_fname = cache_fname +"AIRS"+".L3."+".v6."+vname;
3590 else
3591 cache_fname = cache_fname + base_file_name +"_"+vname;
3592
3593 return cache_fname;
3594}
3595
3596// The current DDS cache is only for some products of which object information
3597// can be retrieved via HDF4 SDS APIs. Currently only AIRS version 6 products are supported.
3598size_t HDFCFUtil::obtain_dds_cache_size(const HDFSP::File*spf) {
3599
3600 size_t total_bytes_written = 0;
3601 const vector<HDFSP::SDField *>& spsds = spf->getSD()->getFields();
3602 for (const auto &fd:spsds){
3603
3604 // We will not handle when the SDS datatype is DFNT_CHAR now.
3605 if(DFNT_CHAR == fd->getType()) {
3606 total_bytes_written = 0;
3607 break;
3608 }
3609 else {
3610 // We need to store dimension names and variable names.
3611 int temp_rank = fd->getRank();
3612 for (const auto & dim:fd->getDimensions())
3613 total_bytes_written += (dim->getName()).size()+1;
3614
3615 total_bytes_written +=(fd->getName()).size()+1;
3616
3617 // Many a time variable new name is the same as variable name,
3618 // so we may just save one byte('\0') for such as a case.
3619 if((fd->getName()) != (fd->getNewName()))
3620 total_bytes_written +=(fd->getNewName()).size()+1;
3621 else
3622 total_bytes_written +=1;
3623
3624 // We need to store 4 integers: rank, variable datatype, SDS reference number, fieldtype
3625 total_bytes_written +=(temp_rank+4)*sizeof(int);
3626 }
3627 }
3628
3629 if(total_bytes_written != 0)
3630 total_bytes_written +=1;
3631
3632 return total_bytes_written;
3633
3634}
3635
3636// Write the DDS of the special SDS-only HDF to a cache.
3637void HDFCFUtil::write_sp_sds_dds_cache(const HDFSP::File* spf,FILE*dds_file,size_t total_bytes_dds_cache,const string &dds_filename) {
3638
3639 BESDEBUG("h4"," Coming to write SDS DDS to a cache" << endl);
3640 char delimiter = '\0';
3641 char cend = '\n';
3642 size_t total_written_bytes_count = 0;
3643
3644 // The buffer to hold information to write to a DDS cache file.
3645 vector<char>temp_buf;
3646 temp_buf.resize(total_bytes_dds_cache);
3647 char* temp_pointer = temp_buf.data();
3648
3649 const vector<HDFSP::SDField *>& spsds = spf->getSD()->getFields();
3650
3651 //Read SDS
3652 for(const auto &fd:spsds){
3653
3654 // First, rank, fieldtype, SDS reference number, variable datatype, dimsize(rank)
3655 int sds_rank = fd->getRank();
3656 int sds_ref = fd->getFieldRef();
3657 int sds_dtype = fd->getType();
3658 int sds_ftype = fd->getFieldType();
3659
3660 vector<int32>dimsizes;
3661 dimsizes.resize(sds_rank);
3662
3663 // Size for each dimension
3664 const vector<HDFSP::Dimension*>& dims= fd->getDimensions();
3665 for(int i = 0; i < sds_rank; i++)
3666 dimsizes[i] = dims[i]->getSize();
3667
3668 memcpy((void*)temp_pointer,(void*)&sds_rank,sizeof(int));
3669 temp_pointer +=sizeof(int);
3670 memcpy((void*)temp_pointer,(void*)&sds_ref,sizeof(int));
3671 temp_pointer +=sizeof(int);
3672 memcpy((void*)temp_pointer,(void*)&sds_dtype,sizeof(int));
3673 temp_pointer +=sizeof(int);
3674 memcpy((void*)temp_pointer,(void*)&sds_ftype,sizeof(int));
3675 temp_pointer +=sizeof(int);
3676
3677 memcpy((void*)temp_pointer,(void*)dimsizes.data(),sds_rank*sizeof(int));
3678 temp_pointer +=sds_rank*sizeof(int);
3679
3680 // total written bytes so far
3681 total_written_bytes_count +=(sds_rank+4)*sizeof(int);
3682
3683 // Second, variable name,variable new name and SDS dim name(rank)
3684 // we need to a delimiter to distinguish each name.
3685 string temp_varname = fd->getName();
3686 vector<char>temp_val1(temp_varname.begin(),temp_varname.end());
3687 memcpy((void*)temp_pointer,(void*)temp_val1.data(),temp_varname.size());
3688 temp_pointer +=temp_varname.size();
3689 memcpy((void*)temp_pointer,&delimiter,1);
3690 temp_pointer +=1;
3691
3692 total_written_bytes_count =total_written_bytes_count +(1+temp_varname.size());
3693
3694 // To save the dds cache size and the speed to retrieve variable new name
3695 // we only save variable cf name when the variable cf name is not the
3696 // same as the variable name. When they are the same, a delimiter is saved for
3697 // variable cf name.
3698 if(fd->getName() == fd->getNewName()){
3699 memcpy((void*)temp_pointer,&delimiter,1);
3700 temp_pointer +=1;
3701 total_written_bytes_count +=1;
3702 }
3703 else {
3704 string temp_varnewname = fd->getNewName();
3705 vector<char>temp_val2(temp_varnewname.begin(),temp_varnewname.end());
3706 memcpy((void*)temp_pointer,(void*)temp_val2.data(),temp_varnewname.size());
3707 temp_pointer +=temp_varnewname.size();
3708 memcpy((void*)temp_pointer,&delimiter,1);
3709 temp_pointer +=1;
3710 total_written_bytes_count =total_written_bytes_count +(1+temp_varnewname.size());
3711 }
3712
3713 // Now the name for each dimensions.
3714 for(int i = 0; i < sds_rank; i++) {
3715 string temp_dimname = dims[i]->getName();
3716 vector<char>temp_val3(temp_dimname.begin(),temp_dimname.end());
3717 memcpy((void*)temp_pointer,(void*)temp_val3.data(),temp_dimname.size());
3718 temp_pointer +=temp_dimname.size();
3719 memcpy((void*)temp_pointer,&delimiter,1);
3720 temp_pointer +=1;
3721 total_written_bytes_count =total_written_bytes_count +(1+temp_dimname.size());
3722 }
3723 }
3724
3725 memcpy((void*)temp_pointer,&cend,1);
3726 total_written_bytes_count +=1;
3727
3728 if(total_written_bytes_count != total_bytes_dds_cache) {
3729 stringstream s_total_written_count;
3730 s_total_written_count << total_written_bytes_count;
3731 stringstream s_total_bytes_dds_cache;
3732 s_total_bytes_dds_cache << total_bytes_dds_cache;
3733 string msg = "DDs cached file "+ dds_filename +" buffer size should be " + s_total_bytes_dds_cache.str() ;
3734 msg = msg + ". But the real size written in the buffer is " + s_total_written_count.str();
3735 throw InternalErr (__FILE__, __LINE__,msg);
3736 }
3737
3738 size_t bytes_really_written = fwrite((const void*)temp_buf.data(),1,total_bytes_dds_cache,dds_file);
3739
3740 if(bytes_really_written != total_bytes_dds_cache) {
3741 stringstream s_expected_bytes;
3742 s_expected_bytes << total_bytes_dds_cache;
3743 stringstream s_really_written_bytes;
3744 s_really_written_bytes << bytes_really_written;
3745 string msg = "DDs cached file "+ dds_filename +" size should be " + s_expected_bytes.str() ;
3746 msg += ". But the real size written to the file is " + s_really_written_bytes.str();
3747 throw InternalErr (__FILE__, __LINE__,msg);
3748 }
3749
3750}
3751
3752// Read DDS of a special SDS-only HDF file from a cache.
3753void HDFCFUtil::read_sp_sds_dds_cache(FILE* dds_file,libdap::DDS * dds_ptr,
3754 const std::string &cache_filename, const std::string &hdf4_filename) {
3755
3756 BESDEBUG("h4"," Coming to read SDS DDS from a cache" << endl);
3757
3758 // Check the cache file size.
3759 struct stat sb;
3760 if(stat(cache_filename.c_str(),&sb)!=0) {
3761 string err_mesg="The DDS cache file " + cache_filename;
3762 err_mesg = err_mesg + " doesn't exist. ";
3763 throw InternalErr(__FILE__,__LINE__,err_mesg);
3764 }
3765
3766 auto bytes_expected_read = (size_t)sb.st_size;
3767
3768 // Allocate the buffer size based on the file size.
3769 vector<char> temp_buf;
3770 temp_buf.resize(bytes_expected_read);
3771 size_t bytes_really_read = fread((void*)temp_buf.data(),1,bytes_expected_read,dds_file);
3772
3773 // Now bytes_really_read should be the same as bytes_expected_read if the element size is 1.
3774 if(bytes_really_read != bytes_expected_read) {
3775 stringstream s_bytes_really_read;
3776 s_bytes_really_read << bytes_really_read ;
3777 stringstream s_bytes_expected_read;
3778 s_bytes_expected_read << bytes_expected_read;
3779 string msg = "The expected bytes to read from DDS cache file " + cache_filename +" is " + s_bytes_expected_read.str();
3780 msg = msg + ". But the real read size from the buffer is " + s_bytes_really_read.str();
3781 throw InternalErr (__FILE__, __LINE__,msg);
3782 }
3783 char* temp_pointer = temp_buf.data();
3784
3785 char delimiter = '\0';
3786 // The end of the whole string.
3787 char cend = '\n';
3788 bool end_file_flag = false;
3789
3790
3791 do {
3792 int sds_rank = *((int *)(temp_pointer));
3793 temp_pointer = temp_pointer + sizeof(int);
3794
3795 int sds_ref = *((int *)(temp_pointer));
3796 temp_pointer = temp_pointer + sizeof(int);
3797
3798 int sds_dtype = *((int *)(temp_pointer));
3799 temp_pointer = temp_pointer + sizeof(int);
3800
3801 int sds_ftype = *((int *)(temp_pointer));
3802 temp_pointer = temp_pointer + sizeof(int);
3803
3804 vector<int32>dimsizes;
3805 if(sds_rank <=0)
3806 throw InternalErr (__FILE__, __LINE__,"SDS rank must be >0");
3807
3808 dimsizes.resize(sds_rank);
3809 for (int i = 0; i <sds_rank;i++) {
3810 dimsizes[i] = *((int *)(temp_pointer));
3811 temp_pointer = temp_pointer + sizeof(int);
3812 }
3813
3814 vector<string>dimnames;
3815 dimnames.resize(sds_rank);
3816 string varname;
3817 string varnewname;
3818 for (int i = 0; i <sds_rank+2;i++) {
3819 vector<char> temp_vchar;
3820 char temp_char = *temp_pointer;
3821
3822 // Only apply when varnewname is stored as the delimiter.
3823 if(temp_char == delimiter)
3824 temp_vchar.push_back(temp_char);
3825 while(temp_char !=delimiter) {
3826 temp_vchar.push_back(temp_char);
3827 temp_pointer++;
3828 temp_char = *temp_pointer;
3829 //temp_char = *(++temp_pointer); work
3830 //temp_char = *(temp_pointer++); not working
3831 }
3832 string temp_string(temp_vchar.begin(),temp_vchar.end());
3833 if(i == 0)
3834 varname = temp_string;
3835 else if( i == 1)
3836 varnewname = temp_string;
3837 else
3838 dimnames[i-2] = temp_string;
3839 temp_pointer++;
3840 }
3841
3842 // If varnewname is only the delimiter, varname and varnewname is the same.
3843 if(varnewname[0] == delimiter)
3844 varnewname = varname;
3845 // Assemble DDS.
3846 // 1. Create a basetype
3847 BaseType *bt = nullptr;
3848 switch(sds_dtype) {
3849#define HANDLE_CASE(tid, type) \
3850 case tid: \
3851 bt = new (type)(varnewname,hdf4_filename); \
3852 break;
3853 HANDLE_CASE(DFNT_FLOAT32, HDFFloat32)
3854 HANDLE_CASE(DFNT_FLOAT64, HDFFloat64)
3855 HANDLE_CASE(DFNT_CHAR, HDFStr)
3856#ifndef SIGNED_BYTE_TO_INT32
3857 HANDLE_CASE(DFNT_INT8, HDFByte)
3858#else
3859 HANDLE_CASE(DFNT_INT8,HDFInt32)
3860#endif
3861 HANDLE_CASE(DFNT_UINT8, HDFByte)
3862 HANDLE_CASE(DFNT_INT16, HDFInt16)
3863 HANDLE_CASE(DFNT_UINT16, HDFUInt16)
3864 HANDLE_CASE(DFNT_INT32, HDFInt32)
3865 HANDLE_CASE(DFNT_UINT32, HDFUInt32)
3866 HANDLE_CASE(DFNT_UCHAR8, HDFByte)
3867 default:
3868 throw InternalErr(__FILE__,__LINE__,"unsupported data type.");
3869#undef HANDLE_CASE
3870 }
3871
3872 if(nullptr == bt)
3873 throw InternalErr(__FILE__,__LINE__,"Cannot create the basetype when creating DDS from a cache file.");
3874
3875 SPType sptype = OTHERHDF;
3876
3877 // sds_ftype indicates if this is a general data field or geolocation field.
3878 // 4 indicates this is a missing non-latlon geo-location fields.
3879 if(sds_ftype != 4){
3880 HDFSPArray_RealField *ar = nullptr;
3881 try {
3882 // pass sds id as 0 since the sds id will be retrieved from SDStart if necessary.
3883 ar = new HDFSPArray_RealField(
3884 sds_rank,
3885 hdf4_filename,
3886 0,
3887 sds_ref,
3888 sds_dtype,
3889 sptype,
3890 varname,
3891 dimsizes,
3892 varnewname,
3893 bt);
3894 }
3895 catch(...) {
3896 delete bt;
3897 throw InternalErr(__FILE__,__LINE__,"Unable to allocate the HDFSPArray_RealField instance.");
3898 }
3899
3900 for(int i = 0; i <sds_rank; i++)
3901 ar->append_dim(dimsizes[i],dimnames[i]);
3902 dds_ptr->add_var(ar);
3903 delete bt;
3904 delete ar;
3905 }
3906 else {
3907 HDFSPArrayMissGeoField *ar = nullptr;
3908 if(sds_rank == 1) {
3909 try {
3910 ar = new HDFSPArrayMissGeoField(
3911 sds_rank,
3912 dimsizes[0],
3913 varnewname,
3914 bt);
3915 }
3916 catch(...) {
3917 delete bt;
3918 throw InternalErr(__FILE__,__LINE__,"Unable to allocate the HDFSPArray_RealField instance.");
3919 }
3920
3921 ar->append_dim(dimsizes[0],dimnames[0]);
3922 dds_ptr->add_var(ar);
3923 delete bt;
3924 delete ar;
3925 }
3926 else
3927 throw InternalErr(__FILE__,__LINE__,"SDS rank must be 1 for the missing coordinate.");
3928 }
3929
3930 if(*temp_pointer == cend)
3931 end_file_flag = true;
3932 if((temp_pointer - temp_buf.data()) > (int)bytes_expected_read) {
3933 string msg = cache_filename + " doesn't have the end-line character at the end. The file may be corrupted.";
3934 throw InternalErr (__FILE__, __LINE__,msg);
3935 }
3936 } while(false == end_file_flag);
3937
3938 dds_ptr->set_dataset_name(basename(hdf4_filename));
3939}
3940
3941
const std::vector< Attribute * > & getAttributes() const
Get the attributes of this field.
Definition HDFSP.h:300
int32 getType() const
Get the data type of this field.
Definition HDFSP.h:294
const std::string & getNewName() const
Get the CF name(special characters replaced by underscores) of this field.
Definition HDFSP.h:282
const std::vector< VDATA * > & getVDATAs() const
Public interface to Obtain Vdata.
Definition HDFSP.h:727
bool Has_Dim_NoScale_Field() const
This file has a field that is a SDS dimension but no dimension scale.
Definition HDFSP.h:706
SD * getSD() const
Public interface to Obtain SD.
Definition HDFSP.h:721
SPType getSPType() const
Obtain special HDF4 product type.
Definition HDFSP.h:699
const std::string & getPath() const
Obtain the path of the file.
Definition HDFSP.h:715
One instance of this class represents one SDS object.
Definition HDFSP.h:331
const std::vector< Attribute * > & getAttributes() const
Public interface to obtain the SD(file) attributes.
Definition HDFSP.h:549
const std::vector< SDField * > & getFields() const
Public interface to obtain information of all SDS vectors(objects).
Definition HDFSP.h:543
STL iterator class.
static short obtain_type_size(int32)
Obtain datatype size.
Definition HDFCFUtil.cc:384
static bool insert_map(std::map< std::string, std::string > &m, std::string key, std::string val)
Definition HDFCFUtil.cc:84
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 void gen_unique_name(std::string &str, std::set< std::string > &namelist, int &clash_index)
Obtain the unique name for the clashed names and save it to set namelist.
Definition HDFCFUtil.cc:130
static void Handle_NameClashing(std::vector< std::string > &newobjnamelist)
General routines to handle name clashings.
Definition HDFCFUtil.cc:194
static void correct_scale_offset_type(libdap::AttrTable *at)
Definition HDFCFUtil.cc:540
static std::string get_CF_string(std::string s)
Change special characters to "_".
Definition HDFCFUtil.cc:100
static void Split(const char *s, int len, char sep, std::vector< std::string > &names)
Definition HDFCFUtil.cc:58
static void correct_fvalue_type(libdap::AttrTable *at, int32 dtype)
Definition HDFCFUtil.cc:477
static void close_fileid(int32 sdfd, int32 file_id, int32 gridfd, int32 swathfd, bool pass_fileid_key)