bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
hdfdesc.cc
Go to the documentation of this file.
1
5// This file is part of the hdf4 data handler for the OPeNDAP data server.
6// The code includes the support of HDF-EOS2 and NASA HDF4 files that follow CF.
7// Copyright (c) The HDF Group.
8// Author: Kent Yang <myang6@hdfgroup.org>
9// Author: Hyo-Kyung Lee <hyoklee@hdfgroup.org>
10//
11// Copyright (c) 2005 OPeNDAP, Inc.
12// Author: Kent Yang <myang6@hdfgroup.org> (CF option and direct dmr modules)
13// Author: James Gallagher <jgallagher@opendap.org>
14//
15// This is free software; you can redistribute it and/or modify it under the
16// terms of the GNU Lesser General Public License as published by the Free
17// Software Foundation; either version 2.1 of the License, or (at your
18// option) any later version.
19//
20// This software is distributed in the hope that it will be useful, but
21// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
22// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
23// License for more details.
24//
25// You should have received a copy of the GNU Lesser General Public License
26// along with this software; if not, write to the Free Software Foundation,
27// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
28//
29// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
30
32// Copyright 1996, by the California Institute of Technology.
33// ALL RIGHTS RESERVED. United States Government Sponsorship
34// acknowledged. Any commercial use must be negotiated with the
35// Office of Technology Transfer at the California Institute of
36// Technology. This software may be subject to U.S. export control
37// laws and regulations. By accepting this software, the user
38// agrees to comply with all applicable U.S. export laws and
39// regulations. User has the responsibility to obtain export
40// licenses, or other export authority as may be required before
41// exporting such information to foreign countries or providing
42// access to foreign persons.
43
44// Author: Todd Karakashian, NASA/Jet Propulsion Laboratory
45// Todd.K.Karakashian@jpl.nasa.gov
46//
48
49#include "config.h"
50#include "config_hdf.h"
51
52#include <cstdio>
53#include <cassert>
54#include <cmath>
55#include <libgen.h>
56
57// STL includes
58#include <string>
59#include <fstream>
60#include <iostream>
61#include <sstream>
62#include <algorithm>
63#include <numeric>
64#include <functional>
65#include <unordered_set>
66#include <set>
67
68
69// Include this on linux to suppress an annoying warning about multiple
70// definitions of MIN and MAX.
71#ifdef HAVE_SYS_PARAM_H
72#include <sys/param.h>
73#endif
74
75#include <unistd.h>
76#include <sys/types.h>
77#include <dirent.h>
78#include <iomanip>
79#include <cerrno>
80
81
82// HDF and HDFClass includes
83#include <mfhdf.h>
84
85// DODS includes
86#include <libdap/DMR.h>
87#include <libdap/D4Group.h>
88#include <libdap/D4Attributes.h>
89
90#include <libdap/DDS.h>
91#include <libdap/DAS.h>
92#include <libdap/escaping.h>
93#include <libdap/parser.h>
94#include <libdap/InternalErr.h>
95#include <libdap/debug.h>
96
97#include <libdap/Array.h>
98
99#include <BESDebug.h>
100#include <BESLog.h>
101
102#include "HDF4RequestHandler.h"
103// DODS/HDF includes for the default option only
104#include "hcstream.h"
105#include "hdfclass.h"
106#include "hcerr.h"
107#include "dhdferr.h"
108#include "HDFArray.h"
109#include "HDFSequence.h"
110#include "HDFTypeFactory.h"
111#include "HDFGrid.h"
112#include "dodsutil.h"
113#include "hdf-maps.h"
114
115// DAP2 doesn't have signed char type, the signed char will be converted to int32 with this macro.
116#define SIGNED_BYTE_TO_INT32 1
117
118// HDF datatype headers for both the default and the CF options
119#include "HDFByte.h"
120#include "HDFInt8.h"
121#include "HDFInt16.h"
122#include "HDFUInt16.h"
123#include "HDFInt32.h"
124#include "HDFUInt32.h"
125#include "HDFFloat32.h"
126#include "HDFFloat64.h"
127#include "HDFStr.h"
128
129// Routines that handle SDS and Vdata attributes for the HDF-EOS2 objects in a hybrid HDF-EOS2 file for the CF option
130#include "HE2CF.h"
131
132// HDF4 for the CF option(EOS2 will treat as pure HDF4 objects if the HDF-EOS2 library is not configured in)
133#include "HDFSP.h"
134#include "HDFSPArray_RealField.h"
135#include "HDFSPArrayGeoField.h"
136#include "HDFSPArrayMissField.h"
137#include "HDFSPArrayAddCVField.h"
138#include "HDFSPArray_VDField.h"
139#include "HDFCFStrField.h"
140#include "HDFCFStr.h"
141#include "HDFCFUtil.h"
142
143// HDF4 for direct DAP4(DMR)
144#include "HDFDMRArray_VD.h"
145#include "HDFDMRArray_SDS.h"
146#include "HDFDMRArray_EOS2LL.h"
147#include "HDFDMRArray_SPLL.h"
148
149// HDF-EOS2 (including the hybrid) will be handled as HDF-EOS2 objects if the HDF-EOS2 library is configured in
150#ifdef USE_HDFEOS2_LIB
151#include "HDFEOS2.h"
152#include "HDFEOS2GeoCFProj.h"
153#include "HDFEOS2GeoCF1D.h"
154#include "HDFEOS2Array_RealField.h"
155#include "HDFEOS2ArrayGridGeoField.h"
156#include "HDFEOS2ArraySwathGeoField.h"
157#include "HDFEOS2ArrayMissField.h"
158#include "HDFEOS2ArraySwathDimMapField.h"
159#include "HDFEOS2ArraySwathGeoMultiDimMapField.h"
160#include "HDFEOS2ArraySwathGeoDimMapExtraField.h"
161#include "HDFEOS2CFStr.h"
162#include "HDFEOS2CFStrField.h"
163#include "HDFEOS2HandleType.h"
164#endif
165
166using namespace std;
167using namespace libdap;
168
169// Added 5/7/09; This bug (#1163) was fixed in July 2008 except for this
170// handler. jhrg
171#define ATTR_STRING_QUOTE_FIX
172
173template < class T > string num2string(T n)
174{
175 ostringstream oss;
176 oss << n;
177 return oss.str();
178}
179
180// Glue routines declared in hdfeos.lex
181void hdfeos_switch_to_buffer(void *new_buffer);
182void hdfeos_delete_buffer(void * buffer);
183void *hdfeos_string(const char *yy_str);
184
185struct yy_buffer_state;
186yy_buffer_state *hdfeos_scan_string(const char *str);
187extern int hdfeosparse(libdap::parser_arg *arg); // defined in hdfeos.tab.c
188
189typedef struct eos2_grid {
190 bool oned_latlon;
191 int32 ydim;
192 int32 xdim;
193 string grid_name;
194
195}eos2_grid_t;
196
197typedef struct eos2_grid_info {
198 int32 projcode;
199 int32 zone;
200 int32 sphere;
201 float64 upleft[2];
202 float64 lowright[2];
203 float64 params[16];
204}eos2_grid_info_t;
205
206
207// Functions for the default option
208void AddHDFAttr(DAS & das, const string & varname,
209 const vector < hdf_attr > &hav);
210void AddHDFAttr(DAS & das, const string & varname,
211 const vector < string > &anv);
212
213static void build_descriptions(DDS & dds, DAS & das,
214 const string & filename);
215static void SDS_descriptions(sds_map & map, DAS & das,
216 const string & filename);
217static void Vdata_descriptions(vd_map & map, DAS & das,
218 const string & filename);
219static void Vgroup_descriptions(DDS & dds, DAS & das,
220 const string & filename, sds_map & sdmap,
221 vd_map & vdmap, gr_map & grmap);
222static void GR_descriptions(gr_map & map, DAS & das,
223 const string & filename);
224static void FileAnnot_descriptions(DAS & das, const string & filename);
225static vector < hdf_attr > Pals2Attrs(const vector < hdf_palette > palv);
226static vector < hdf_attr > Dims2Attrs(const hdf_dim dim);
227
228// Start the declaration of HDF4 to DMR direct mapping functions
229// 1. dmr and root level object handlings
230void read_dmr(DMR *dmr, const string &filename);
231void read_sd_attrs(D4Group *root_grp, int32 fileid, int32 sdfd);
232void handle_sds_dims(D4Group *root_grp, int32 fileid, int32 sdfd);
233void read_lone_sds(D4Group *root_grp, int32 file_id, int32 sdfd, const string &filename);
234void read_lone_vdata(D4Group *root_grp, int32 file_id, int32 sdfd, const string &filename);
235void read_dmr_vlone_groups(D4Group *root_grp, int32 file_id, int32 sdfd, const string &filename);
236
237// 2. vgroup handlings
238void convert_vgroup_objects(int32 vgroup_id,int32 file_id, int32 sdfd, D4Group* d4g, D4Group *root_grp, const string &vgroupname,const string & filename, bool is_eos2_grid, bool is_eos2_grid_cf_mapping);
239void convert_vgroup_attrs(int32 vgroup_id,D4Group* d4g, const string &vgroupname);
240void map_vgroup_attr(D4Group *d4g, const string &dap4_attrname,int32 attr_type, int32 attr_count, vector<char> & attr_value);
241bool reserved_vgroups(const vector<char> &vgroup_class);
242
243// 3. SDS handlings
244#if 0
245void vgroup_convert_sds_objects(int32 vgroup_id,int32 file_id,int32 sdfd,D4Group* d4g,const string& filename);
246#endif
247void convert_sds(int32 file_id, int32 sdfd,int32 vgroup_id, int32 obj_ref, D4Group* d4g,D4Group *root_grp, const string &filename, bool is_eos2_grid, bool is_eos2_grid_cf_mapping);
248void obtain_all_sds_refs(int32 file_id, int32 sdfd, unordered_set<int32>& sds_ref);
249void exclude_all_sds_refs_in_vgroups(int32 file_id, int32 sdfd, unordered_set<int32>&sds_ref);
250void exclude_sds_refs_in_vgroup(int32 file_id, int32 sdfd, int32 vgroup_id, unordered_set<int32>&sds_ref);
251void map_sds_var_dap4_attrs(HDFDMRArray_SDS *ar, int32 sds_id, int32 obj_ref, int32 n_sds_attrs);
252void map_sds_vdata_attr(BaseType *d4b, const string &attr_name,int32 attr_type, int32 attr_count, vector<char> & attr_value);
253
254// 4. Vdata handlings
255void convert_vdata(int32 fileid, int32 sdfd, int32 vgroup_id,int32 obj_ref ,D4Group* d4g,const string& filename);
256void map_vdata_to_dap4_structure_array(int32 vdata_id, int32 num_elms, int32 nflds, int32 obj_ref, D4Group *d4g, const string &filename);
257void map_vdata_to_dap4_atomic_array(int32 vdata_id, int32 num_elms, int32 obj_ref, D4Group *d4g, const string &filename);
258void map_vdata_to_dap4_attrs(HDFDMRArray_VD *ar, int32 vdata_id, int32 obj_ref);
259
260// 5. HDF-EOS2 handlings(Not need the hdf-eos2 library)
261int is_group_eos2_grid(const string& vgroup_name, vector<eos2_grid_t>& eos2_grid_lls);
262void add_eos2_latlon_info(D4Group *d4_grp, D4Group *root_grp, const eos2_grid_t &eos2_grid, const eos2_grid_info_t &eos2_grid_info, const string&filename);
263void add_dummy_grid_cv(D4Group *d4_grp, const eos2_grid_t &eos2_grid, const eos2_grid_info_t &eos2_grid_info);
264void add_ps_cf_grid_mapping_attrs(libdap::BaseType *var, const eos2_grid_info_t & eg_info);
265void add_lamaz_cf_grid_mapping_attrs(libdap::BaseType *var, const eos2_grid_info_t & eg_info);
266void add_CF_1D_cvs(D4Group *d4_grp, D4Group *root_grp, const eos2_grid_t &eos2_grid, const eos2_grid_info_t &eos2_grid_info, const string& xdim_path, const string &ydim_path);
267void add_CF_1D_cv_attrs(libdap::BaseType *var, bool is_ydim);
268
269// 6. Special HDF4 handlings
270
271bool add_sp_hdf4_info(D4Group *d4_grp, const string &filename, string & err_msg);
272bool add_sp_hdf4_trmm_info(D4Group *d4_grp, const string &filename, const D4Attribute *d4_attr, string &err_msg);
273void add_sp_hdf4_additional_info(D4Group *d4_grp);
274
275// 7. Helper functions
276void add_obj_ref_attr(BaseType * d4b, bool is_sds, int32 obj_ref);
277void add_sds_fvalue_attr(BaseType *d4b, int32 sds_id);
278BaseType * gen_dap_var(int32 h4_type, const string & h4_str, const string & filename);
279D4AttributeType h4type_to_dap4_attrtype(int32 h4_type);
280string print_dap4_attr(int32 type, int loc, void *vals);
281void close_vgroup_fileids(int32 fileid, int32 sdfd, int32 vgroup_id);
282void add_var_dap4_attr(BaseType *d4_var,const string& attr_name, D4AttributeType attr_type, const string& attr_value);
283void dims_transform_to_dap4(Array *ar,D4Group *root_grp, bool missing_vars);
284
285// End of HDF4 to DMR direct mapping functions.
286
287
288void read_das(DAS & das, const string & filename);
289void read_dds(DDS & dds, const string & filename);
290
291// For the CF option
292// read_dds for HDF4 files. Some NASA non-eos2 HDF4 products are handled specifially to follow the CF conventions.
293bool read_dds_hdfsp(DDS & dds, const string & filename,int32 sdfd, int32 fileid,const HDFSP::File*h4file);
294bool read_das_hdfsp(DAS & das, const string & filename,int32 sdfd, int32 fileid,HDFSP::File**h4filepptr);
295
296// read_dds for special NASA HDF-EOS2 hybrid(non-EOS2) objects
297bool read_dds_hdfhybrid(DDS & dds, const string & filename,int32 sdfd, int32 fileid,const HDFSP::File*h4file);
298bool read_das_hdfhybrid(DAS & das, const string & filename,int32 sdfd, int32 fileid,HDFSP::File**h4filepptr);
299
300// Functions to read special 1-d HDF-EOS2 grid. This grid can be built up quickly.
301bool read_dds_special_1d_grid(DDS &dds, const HDFSP::File *spf, const string & filename,int32 sdfd,bool can_cache);
302bool read_das_special_eos2(DAS &das,const string & filename,int32 sdid, int32 fileid,bool ecs_metadata,HDFSP::File**h4filepptr);
303bool read_das_special_eos2_core(DAS &das, const HDFSP::File *spf, const string & filename,bool ecs_metadata);
304
305void read_das_sds(DAS & das, const string & filename,int32 sdfd, bool ecs_metadata,HDFSP::File**h4fileptr);
306void read_dds_sds(DDS &dds, const string & filename,int32 sdfd, HDFSP::File*h4file,bool dds_set_cache);
307
308void read_das_simple_cf(DAS &das, int32 sdfd, int32 fileid);
309void read_dds_simple_cf(DDS &dds,const string & filename, int32 sdfd, int32 fileid, short cf_simple_type);
310void obtain_cf_simple_lat_lon(int32 sdfd,string &lat_name, string &lon_name, int &lat_size, int &lon_size);
311
312void change_das_mod08_scale_offset(DAS & das, const HDFSP::File *spf);
313
314// Functions to handle SDS fields for the CF option.
315void read_dds_spfields(DDS &dds,const string& filename,const int sdfd,const HDFSP::SDField *spsds, SPType sptype);
316
317// Functions to handle Vdata fields for the CF option.
318void read_dds_spvdfields(DDS &dds,const string& filename, const int fileid,int32 vdref, int32 numrec,HDFSP::VDField *spvd);
319
320// Check if this is a special HDF-EOS2 file that can be handled by HDF4 directly. Using HDF4 only can improve performance.
321int check_special_eosfile(const string&filename,string&grid_name,int32 sdfd);
322
323// The following blocks only handle HDF-EOS2 objects based on HDF-EOS2 libraries.
324#ifdef USE_HDFEOS2_LIB
325
326bool obtain_eos2_gd_ll_info(const string & fname, const string &grid_name, int32 &ydim, int32 &xdim, bool & oned_ll, eos2_grid_info_t &eos2_grid_info);
327bool check_eos2_grids(const string &filename, int32 sdfd,vector<eos2_grid_t>& eos2_grid_lls, vector<eos2_grid_info_t>& eos2_grids_info);
328
329// Parse HDF-EOS2's ECS metadata(coremetadata etc.)
330void parse_ecs_metadata(DAS &das,const string & metaname, const string &metadata);
331
332// read_dds for HDF-EOS2
333// We find some special HDF-EOS2(MOD08_M3) products that provides coordinate variables
334// without using the dimension scales. We will handle this in a special way.
335// So change the return value of read_dds_hdfeos2 to represent different cases
336// 0: general non-EOS2 pure HDF4
337// 1: HDF-EOS2 hybrid
338// 2: MOD08_M3
339// HDF-EOS2 but no need to use HDF-EOS2 lib: no real dimension scales but have CVs for every dimension, treat differently
340// 3: AIRS version 6
341// HDF-EOS2 but no need to use HDF-EOS2 lib:
342// have dimension scales but don’t have CVs for every dimension, also need to condense dimensions, treat differently
343// 4: Expected AIRS level 3 or level 2
344// HDF-EOS2 but no need to use HDF-EOS2 lib: Have dimension scales for all dimensions
345// 5: MERRA
346// Special handling for MERRA file
347int read_dds_hdfeos2(DDS & dds, const string & filename,int32 sdfd, int32 gridfd, int32 swathfd,const HDFSP::File*h4file,HDFEOS2::File*eosfile);
348
349// reas das for HDF-EOS2
350int read_das_hdfeos2(DAS & das, const string & filename,int32 sdfd,int32 fileid, int32 gridfd, int32 swathfd,bool ecs_metadata,HDFSP::File**h4filepptr,HDFEOS2::File**eosfilepptr);
351
352// read_dds for one grid or swath
353void read_dds_hdfeos2_grid_swath(DDS &dds, const string&filename, HDFEOS2::Dataset *dataset, int grid_or_swath,
354 bool ownll, SOType sotype,bool multi_dmap,int32 sdfd, int32 gridfd,int32 swathfd)
355{
356
357 BESDEBUG("h4","Coming to read_dds_hdfeos2_grid_swath "<<endl);
358
359 // grid_or_swath - 0: grid, 1: swath
360 if (grid_or_swath < 0 || grid_or_swath > 1)
361 throw InternalErr(__FILE__, __LINE__, "The current type should be either grid or swath");
362
364
365 // Declare dim. map entry. The defination of dimmap_entry can be found at HDFCFUtil.h.
366 vector<struct dimmap_entry> dimmaps;
367
368 //The extra dim map file name(lat/lon of swath with dim. map can be found in a separate HDF file.
369 string modis_geofilename="";
370 bool geofile_has_dimmap = false;
371
372 // 1. Obtain dimension map info and stores the info. to dimmaps.
373 // 2. Check if a MODIS swath geo-location HDF-EOS2 file exists for the dimension map case of MODIS Swath
374 if (grid_or_swath == 1)
375 HDFCFUtil::obtain_dimmap_info(filename,dataset,dimmaps,modis_geofilename,geofile_has_dimmap);
376
378
379
381 const vector<HDFEOS2::Field*>& fields = dataset->getDataFields();
382 vector<HDFEOS2::Field*> all_fields = fields;
384
385 if (1 == grid_or_swath) {
386 auto sw = static_cast<HDFEOS2::SwathDataset *>(dataset);
387 const vector<HDFEOS2::Field*>geofields = sw->getGeoFields();
388 for (it_f = geofields.begin(); it_f != geofields.end(); it_f++)
389 all_fields.push_back(*it_f);
390 }
392
394 for(it_f = all_fields.begin(); it_f != all_fields.end(); it_f++)
395 {
396 BESDEBUG("h4","New field Name " <<(*it_f)->getNewName()<<endl);
397
398 BaseType *bt=nullptr;
399
400 // Whether the field is real field,lat/lon field or missing Z-dimension field
401 int fieldtype = (*it_f)->getFieldType();
402
403 // Check if the datatype needs to be changed. This is for MODIS data that needs to apply scale and offset.
404 // ctype_field_namelist is assigned in the function read_das_hdfeos2.
405 bool changedtype = false;
406 for (auto const &cf:ctype_field_namelist){
407 if (cf == (*it_f)->getNewName()){
408 changedtype = true;
409 break;
410 }
411 }
412
413 switch((*it_f)->getType())
414 {
415
416#define HANDLE_CASE2(tid, type) \
417 case tid: \
418 if(true == changedtype && fieldtype==0) \
419 bt = new (HDFFloat32) ((*it_f)->getNewName(), (dataset)->getName()); \
420 else \
421 bt = new (type)((*it_f)->getNewName(), (dataset)->getName()); \
422 break;
423
424#define HANDLE_CASE(tid, type)\
425 case tid: \
426 bt = new (type)((*it_f)->getNewName(), (dataset)->getName()); \
427 break;
428 HANDLE_CASE(DFNT_FLOAT32, HDFFloat32)
429 HANDLE_CASE(DFNT_FLOAT64, HDFFloat64)
430 HANDLE_CASE(DFNT_CHAR8,HDFStr)
431#ifndef SIGNED_BYTE_TO_INT32
432 HANDLE_CASE2(DFNT_INT8, HDFByte)
433#else
434 HANDLE_CASE2(DFNT_INT8,HDFInt32)
435#endif
436 HANDLE_CASE2(DFNT_UINT8, HDFByte)
437 HANDLE_CASE2(DFNT_INT16, HDFInt16)
438 HANDLE_CASE2(DFNT_UINT16,HDFUInt16)
439 HANDLE_CASE2(DFNT_INT32, HDFInt32)
440 HANDLE_CASE2(DFNT_UINT32, HDFUInt32)
441 HANDLE_CASE2(DFNT_UCHAR8, HDFByte)
442 default:
443 throw InternalErr(__FILE__,__LINE__,"unsupported data type.");
444#undef HANDLE_CASE
445#undef HANDLE_CASE2
446 }
447
448 if(bt)
449 {
450 const vector<HDFEOS2::Dimension*>& dims= (*it_f)->getCorrectedDimensions();
452
453 // Char array maps to DAP string.
454 if(DFNT_CHAR == (*it_f)->getType()) {
455
456 if((*it_f)->getRank() >1) {
457
458 HDFEOS2CFStrField * ar = nullptr;
459
460 try {
461
462 ar = new HDFEOS2CFStrField(
463 (*it_f)->getRank() -1,
464 (grid_or_swath ==0)?gridfd:swathfd,
465 filename,
466 dataset->getName(),
467 (*it_f)->getName(),
468 grid_or_swath,
469 (*it_f)->getNewName(),
470 bt);
471 }
472 catch(...) {
473 delete bt;
474 throw InternalErr(__FILE__,__LINE__,"Unable to allocate the HDFCFStr instance.");
475 }
476 for(it_d = dims.begin(); it_d != dims.begin()+dims.size()-1; it_d++){
477 ar->append_dim((*it_d)->getSize(), (*it_d)->getName());
478 }
479
480 dds.add_var(ar);
481 delete bt;
482 if(ar != nullptr)
483 delete ar;
484
485 }
486
487 else {
488 HDFEOS2CFStr * sca_str = nullptr;
489 try {
490
491 sca_str = new HDFEOS2CFStr(
492 (grid_or_swath ==0)?gridfd:swathfd,
493 filename,
494 dataset->getName(),
495 (*it_f)->getName(),
496 (*it_f)->getNewName(),
497 grid_or_swath);
498 }
499 catch(...) {
500 delete bt;
501 throw InternalErr(__FILE__,__LINE__,"Unable to allocate the HDFCFStr instance.");
502 }
503 dds.add_var(sca_str);
504 delete bt;
505 delete sca_str;
506 }
507
508 }
509
510 // For general variables and non-lat/lon existing coordinate variables
511 else if(fieldtype == 0 || fieldtype == 3 || fieldtype == 5) {
512
513 // grid
514 if(grid_or_swath==0){
515 HDFEOS2Array_RealField *ar = nullptr;
516 ar = new HDFEOS2Array_RealField(
517 (*it_f)->getRank(),
518 filename,false,sdfd,gridfd,
519 dataset->getName(), "", (*it_f)->getName(),
520 sotype,
521 (*it_f)->getNewName(), bt);
522 for(it_d = dims.begin(); it_d != dims.end(); it_d++)
523 ar->append_dim((*it_d)->getSize(), (*it_d)->getName());
524 dds.add_var(ar);
525 delete bt;
526 delete ar;
527 }
528 // swath
529 else if(grid_or_swath==1){
530
531 string tempfieldname = (*it_f)->getName();
532
533 // This swath uses the dimension map, but not the multi-dim. map we can handle.
534 if((*it_f)->UseDimMap() && false == multi_dmap) {
535
536 // We also find that a separate geolocation file exists
537 if (!modis_geofilename.empty()) {
538
539 // This field can be found in the geo-location file. The field name may be corrected.
540 if (true == HDFCFUtil::is_modis_dimmap_nonll_field(tempfieldname)) {
541
542 if(false == geofile_has_dimmap) {
543
544 // Here we have to use HDFEOS2Array_RealField since the field may
545 // need to apply scale and offset equation.
546 // MODIS geolocation swath name is always MODIS_Swath_Type_GEO.
547 // We can improve the handling of this by not hard-coding the swath name
548 // in the future. KY 2012-08-16
549 HDFEOS2Array_RealField *ar = nullptr;
550 ar = new HDFEOS2Array_RealField(
551 (*it_f)->getRank(),
552 modis_geofilename,
553 true,
554 sdfd,
555 swathfd,
556 "",
557 "MODIS_Swath_Type_GEO",
558 tempfieldname,
559 sotype,
560 (*it_f)->getNewName(),
561 bt);
562
563 for(it_d = dims.begin(); it_d != dims.end(); it_d++)
564 ar->append_dim((*it_d)->getSize(), (*it_d)->getName());
565 dds.add_var(ar);
566 delete bt;
567 delete ar;
568 }
569 else {// Use dimension maps in the dimension map file
570
571 HDFEOS2ArraySwathDimMapField * ar = nullptr;
572
573 // SET dimmaps to empty.
574 // This is the key since we are using the geolocation file for the new information.
575 // The dimension map info. will be obtained when the data is reading. KY 2013-03-13
576
577 dimmaps.clear();
578 ar = new HDFEOS2ArraySwathDimMapField(
579 (*it_f)->getRank(),
580 modis_geofilename,
581 true,
582 sdfd,
583 swathfd,
584 "",
585 "MODIS_Swath_Type_GEO",
586 tempfieldname,
587 dimmaps,
588 sotype,
589 (*it_f)->getNewName(),
590 bt);
591 for(it_d = dims.begin(); it_d != dims.end(); it_d++)
592 ar->append_dim((*it_d)->getSize(), (*it_d)->getName());
593 dds.add_var(ar);
594 delete bt;
595 delete ar;
596 }
597 }
598 else { // This field cannot be found in the dimension map file.
599
600 HDFEOS2ArraySwathDimMapField * ar = nullptr;
601
602 // Even if the dimension map file exists, it only applies to some
603 // specific data fields, if this field doesn't belong to these fields,
604 // we should still apply the dimension map rule to these fields.
605
606 ar = new HDFEOS2ArraySwathDimMapField(
607 (*it_f)->getRank(),
608 filename,
609 false,
610 sdfd,
611 swathfd,
612 "",
613 dataset->getName(),
614 tempfieldname,
615 dimmaps,
616 sotype,
617 (*it_f)->getNewName(),
618 bt);
619
620 for (it_d = dims.begin(); it_d != dims.end(); it_d++)
621 ar->append_dim((*it_d)->getSize(), (*it_d)->getName());
622 dds.add_var(ar);
623 delete bt;
624 delete ar;
625 }
626 }
627 else {// No dimension map file
628
629 HDFEOS2ArraySwathDimMapField * ar = nullptr;
630 ar = new HDFEOS2ArraySwathDimMapField(
631 (*it_f)->getRank(),
632 filename,
633 false,
634 sdfd,
635 swathfd,
636 "",
637 dataset->getName(),
638 tempfieldname,
639 dimmaps,
640 sotype,
641 (*it_f)->getNewName(),
642 bt);
643 for(it_d = dims.begin(); it_d != dims.end(); it_d++)
644 ar->append_dim((*it_d)->getSize(), (*it_d)->getName());
645 dds.add_var(ar);
646 delete bt;
647 delete ar;
648 }
649 }
650 else { // No dimension map
651
652 HDFEOS2Array_RealField * ar = nullptr;
653 ar = new HDFEOS2Array_RealField(
654 (*it_f)->getRank(),
655 filename,
656 false,
657 sdfd,
658 swathfd,
659 "",
660 dataset->getName(),
661 tempfieldname,
662 sotype,
663 (*it_f)->getNewName(),
664 bt);
665 for(it_d = dims.begin(); it_d != dims.end(); it_d++)
666 ar->append_dim((*it_d)->getSize(), (*it_d)->getName());
667 dds.add_var(ar);
668 delete bt;
669 delete ar;
670 }
671 }
672 else {
673 delete bt;
674 throw InternalErr(__FILE__, __LINE__, "The current type should be either grid or swath");
675 }
676 }
677
678 // For latitude and longitude
679 else if(fieldtype == 1 || fieldtype == 2) {
680
681 // For grid
682 if (grid_or_swath==0) {
683
684 HDFEOS2ArrayGridGeoField *ar = nullptr;
685 bool ydimmajor = (*it_f)->getYDimMajor();
686 bool condenseddim = (*it_f)->getCondensedDim();
687 bool speciallon = (*it_f)->getSpecialLon();
688 int specialformat = (*it_f)->getSpecialLLFormat();
689
690 ar = new HDFEOS2ArrayGridGeoField(
691 (*it_f)->getRank(),
692 fieldtype,
693 ownll,
694 ydimmajor,
695 condenseddim,
696 speciallon,
697 specialformat,
698 /*fieldcache,*/
699 filename,
700 gridfd,
701 dataset->getName(),
702 (*it_f)->getName(),
703 (*it_f)->getNewName(),
704 bt);
705
706 for(it_d = dims.begin(); it_d != dims.end(); it_d++)
707 ar->append_dim((*it_d)->getSize(), (*it_d)->getName());
708 dds.add_var(ar);
709 delete bt;
710 delete ar;
711 }
712
713 // We encounter a very special MODIS case (MOD/MYD ATML2 files),
714 // Latitude and longitude fields are located under data fields.
715 // So include this case. KY 2010-7-12
716 // We also encounter another special case(MOD11_L2.A2012248.2355.041.2012249083024.hdf),
717 // the latitude/longitude with dimension maps is under the "data fields".
718 // So we have to consider this. KY 2012-09-24
719
720 else if(grid_or_swath ==1) {
721
722 if (true == multi_dmap) {
723 if ((*it_f)->getRank() !=2)
724 throw InternalErr(__FILE__, __LINE__, "For the multi-dimmap case, the field rank must be 2.");
725 int dim0size = (dims[0])->getSize();
726 int dim1size = (dims[1])->getSize();
727 int dim0offset = (*it_f)->getLLDim0Offset();
728 int dim1offset = (*it_f)->getLLDim1Offset();
729 int dim0inc = (*it_f)->getLLDim0Inc();
730 int dim1inc = (*it_f)->getLLDim1Inc();
731 string fieldname;
732 if(fieldtype == 1)
733 fieldname = "Latitude";
734 else
735 fieldname = "Longitude";
736#if 0
737cerr<<"hdfdesc: newfieldname is "<<(*it_f)->getNewName() <<endl;
738cerr<<"hdfdesc: dim0size "<<dim0size <<endl;
739cerr<<"hdfdesc: dim1size "<<dim1size <<endl;
740cerr<<"hdfdesc: dim0offset "<<dim0offset <<endl;
741cerr<<"hdfdesc: dim1offset "<<dim1offset <<endl;
742cerr<<"hdfdesc: dim0inc "<<dim0inc <<endl;
743cerr<<"hdfdesc: dim1inc "<<dim1inc <<endl;
744#endif
745
746 HDFEOS2ArraySwathGeoMultiDimMapField * ar = nullptr;
747
748 ar = new HDFEOS2ArraySwathGeoMultiDimMapField(
749 (*it_f)->getRank(),
750 filename,
751 swathfd,
752 dataset->getName(),
753 fieldname,
754 dim0size,
755 dim0offset,
756 dim0inc,
757 dim1size,
758 dim1offset,
759 dim1inc,
760 (*it_f)->getNewName(),
761 bt);
762
763 for(it_d = dims.begin(); it_d != dims.end(); it_d++)
764 ar->append_dim((*it_d)->getSize(), (*it_d)->getName());
765
766 dds.add_var(ar);
767 delete bt;
768 delete ar;
769 }
770 else {
771
772 // Use Swath dimension map
773 if((*it_f)->UseDimMap()) {
774
775 // Have an extra HDF-EOS file for latitude and longtiude
776 if(!modis_geofilename.empty()) {
777
778 if (false == geofile_has_dimmap) {
779 HDFEOS2ArraySwathGeoDimMapExtraField *ar = nullptr;
780 ar = new HDFEOS2ArraySwathGeoDimMapExtraField(
781 (*it_f)->getRank(),
782 modis_geofilename,
783 (*it_f)->getName(),
784 (*it_f)->getNewName(),
785 bt);
786 for(it_d = dims.begin(); it_d != dims.end(); it_d++)
787 ar->append_dim((*it_d)->getSize(), (*it_d)->getName());
788 dds.add_var(ar);
789 delete bt;
790 delete ar;
791 }
792 else {
793
794 HDFEOS2ArraySwathDimMapField * ar = nullptr;
795
796 // SET dimmaps to empty.
797 // This is essential since we are using the geolocation file for the new information.
798 // The dimension map info. will be obtained when the data is read. KY 2013-03-13
799 dimmaps.clear();
800 ar = new HDFEOS2ArraySwathDimMapField(
801 (*it_f)->getRank(),
802 modis_geofilename,
803 true,
804 sdfd,
805 swathfd,
806 "",
807 "MODIS_Swath_Type_GEO",
808 (*it_f)->getName(),
809 dimmaps,
810 sotype,
811 (*it_f)->getNewName(),
812 bt);
813 for(it_d = dims.begin(); it_d != dims.end(); it_d++)
814 ar->append_dim((*it_d)->getSize(), (*it_d)->getName());
815
816 dds.add_var(ar);
817 delete bt;
818 delete ar;
819 }
820 }
821 // Will interpolate by the handler
822 else {
823
824 HDFEOS2ArraySwathDimMapField * ar = nullptr;
825 ar = new HDFEOS2ArraySwathDimMapField(
826 (*it_f)->getRank(),
827 filename,
828 false,
829 sdfd,
830 swathfd,
831 "",
832 dataset->getName(),
833 (*it_f)->getName(),
834 dimmaps,
835 sotype,
836 (*it_f)->getNewName(),
837 bt);
838 for(it_d = dims.begin(); it_d != dims.end(); it_d++)
839 ar->append_dim((*it_d)->getSize(), (*it_d)->getName());
840
841 dds.add_var(ar);
842 delete bt;
843 delete ar;
844 }
845 }
846 else {// No Dimension map
847
848 HDFEOS2ArraySwathGeoField * ar = nullptr;
849 ar = new HDFEOS2ArraySwathGeoField(
850 (*it_f)->getRank(),
851 filename,
852 swathfd,
853 dataset->getName(),
854 (*it_f)->getName(),
855 (*it_f)->getNewName(),
856 bt);
857
858 for(it_d = dims.begin(); it_d != dims.end(); it_d++)
859 ar->append_dim((*it_d)->getSize(), (*it_d)->getName());
860 dds.add_var(ar);
861 delete bt;
862 delete ar;
863 }
864 }
865 }
866 else {
867 delete bt;
868 throw InternalErr(__FILE__, __LINE__, "The current type should be either grid or swath");
869 }
870
871 }
872
873 //Missing Z dimensional field
874 else if(fieldtype == 4) {
875
876 if((*it_f)->getRank()!=1){
877 delete bt;
878 throw InternalErr(__FILE__, __LINE__, "The rank of missing Z dimension field must be 1");
879 }
880
881 int nelem = ((*it_f)->getCorrectedDimensions()[0])->getSize();
882 HDFEOS2ArrayMissGeoField *ar = nullptr;
883 ar = new HDFEOS2ArrayMissGeoField(
884 (*it_f)->getRank(),
885 nelem,
886 (*it_f)->getNewName(),
887 bt);
888
889 for(it_d = dims.begin(); it_d != dims.end(); it_d++)
890 ar->append_dim((*it_d)->getSize(), (*it_d)->getName());
891
892 dds.add_var(ar);
893 delete bt;
894 delete ar;
895 }
896 else {
897 delete bt;
898 throw InternalErr(__FILE__, __LINE__, "Encounter unsupported datatype or The field type should be between 0 and 5. ");
899 }
900
901 }
902 }
903
904}
905
906// Build DDS for HDF-EOS2 only.
907int read_dds_hdfeos2(DDS & dds, const string & filename,int32 sdfd, int32 gridfd, int32 swathfd,const HDFSP::File*spf,HDFEOS2::File*f)
908{
909
910 BESDEBUG("h4","Coming to read_dds_hdfeos2 "<<endl);
911
912 // Set DDS dataset.
913 dds.set_dataset_name(basename(filename));
914
915 // There are some HDF-EOS2 files(MERRA) that should be treated
916 // exactly like HDF4 SDS files. We don't need to use HDF-EOS2 APIs to
917 // retrieve any information. In fact, treating them as HDF-EOS2 files
918 // will cause confusions and we may get wrong information.
919 // A quick fix is to check if the file name contains MERRA. KY 2011-3-4
920 // Find MERRA data, return 5, then just use HDF4 SDS code.
921 if ((basename(filename).size() >=5) && ((basename(filename)).compare(0,5,"MERRA")==0))
922 return 5;
923
924 if (true == HDF4RequestHandler::get_enable_special_eos()) {
925
926 string grid_name;
927 int ret_val = check_special_eosfile(filename,grid_name,sdfd);
928
929 // These are AIRS-like products that use HDF4 SDS dimension scale perfectly.
930 // We originally thought that the AIRS version 6 products fall into this category, so we added this case.
931 // However, the current AIRS version 6 products still miss some dimension scales. So currently we don't
932 // find any products that support this case. Leave it for future use. KY 2015-06-03
933 if (4== ret_val)
934 return ret_val;
935
936
937 // Case 2 or 3 are MOD08M3 or AIRS version 6
938 if (2 == ret_val || 3 == ret_val) {
939
940 try {
941 read_dds_special_1d_grid(dds,spf,filename,sdfd,false);
942 } catch (...)
943 {
944 throw;
945 }
946 return ret_val;
947 }
948
949 }
950
951 // Special HDF-EOS2 file, doesn't use HDF-EOS2 file structure. so
952 // the file pointer passed from DAS is Null. return 0.
953 if (f == nullptr)
954 return 0;
955
956 //Some grids have one shared lat/lon pair. For this case,"onelatlon" is true.
957 // Other grids have their individual grids. We have to handle them differently.
958 // ownll is the flag to distinguish "one lat/lon pair" and multiple lat/lon pairs.
959 const vector<HDFEOS2::GridDataset *>& grids = f->getGrids();
960 bool ownll = false;
961 bool onelatlon = f->getOneLatLon();
962
963 // Set scale and offset type to the DEFAULT one.
964 SOType sotype = SOType::DEFAULT_CF_EQU;
965
966 // Iterate all the grids of this file and map them to DAP DDS.
967 for (const auto &gd:grids){
968
969 // Check if this grid provides its own lat/lon.
970 ownll = onelatlon?onelatlon:gd->getLatLonFlag();
971
972 // Obtain Scale and offset type. This is for MODIS products who use non-CF scale/offset rules.
973 sotype = gd->getScaleType();
974 try {
975 read_dds_hdfeos2_grid_swath(
976 dds, filename, static_cast<HDFEOS2::Dataset*>(gd), 0,ownll,sotype,false,sdfd,gridfd,swathfd);
977 // Add 1-D CF grid projection required coordinate variables.
978 // Currently only supports sinusoidal projection.
979 HDFCFUtil::add_cf_grid_cvs(dds,gd);
980 }
981 catch(...) {
982 throw;
983 }
984 }
985
986 // Obtain the multi dimmap flag.
987 bool multi_dmap = f->getMultiDimMaps();
988
989
990 // Iterate all the swaths of this file and map them to DAP DDS.
991 const vector<HDFEOS2::SwathDataset *>& swaths= f->getSwaths();
992 for (const auto &swath:swaths) {
993
994 // Obtain Scale and offset type. This is for MODIS products who use non-CF scale/offset rules.
995 sotype = swath->getScaleType();
996 try {
997 //No global lat/lon for multiple swaths
998 read_dds_hdfeos2_grid_swath(
999 dds, filename, static_cast<HDFEOS2::Dataset*>(swath), 1,false,sotype,multi_dmap,sdfd,gridfd,swathfd);
1000 }
1001 catch(...) {
1002 throw;
1003 }
1004 }
1005
1006 // Clear the field name list of which the datatype is changed. KY 2012-8-1
1007 // ctype_field_namelist is a global vector(see HDFEOS2HandleType.h for more description)
1008 // Since the handler program is a continuously running service, this values of this global vector may
1009 // change from one file to another. So clearing this vector each time when mapping DDS is over.
1010 ctype_field_namelist.clear();
1011
1012 return 1;
1013}
1014
1015
1016// The wrapper of building DDS of non-EOS fields and attributes in a hybrid HDF-EOS2 file.
1017//bool read_dds_hdfhybrid(DDS & dds, const string & filename,int32 sdfd, int32 fileid,int32 gridfd,int32 swathfd)
1018bool read_dds_hdfhybrid(DDS & dds, const string & filename,int32 sdfd, int32 fileid,const HDFSP::File*f)
1019
1020{
1021
1022 BESDEBUG("h4","Coming to read_dds_hdfhybrid "<<endl);
1023
1024 // Set DDS dataset.
1025 dds.set_dataset_name(basename(filename));
1026
1027 // Obtain non-EOS SDS fields.
1028 const vector<HDFSP::SDField *>& spsds = f->getSD()->getFields();
1029
1030 // Read SDS
1031 for(const auto &sdfield:spsds){
1032 try {
1033 read_dds_spfields(dds,filename,sdfd,sdfield,f->getSPType());
1034 }
1035 catch(...) {
1036 throw;
1037 }
1038 }
1039
1040 // Read Vdata fields.
1041 // To speed up the performance for CERES data, we turn off some CERES vdata fields.
1042
1043 // Many MODIS and MISR products use Vdata intensively. To make the output CF compliant, we map
1044 // each vdata field to a DAP array. However, this may cause the generation of many DAP fields. So
1045 // we use the BES keys for users to turn on/off as they choose. By default, the key is turned on. KY 2012-6-26
1046
1047 if (true == HDF4RequestHandler::get_enable_hybrid_vdata()) {
1048
1049 for (const auto &vd:f->getVDATAs()) {
1050 if (false == vd->getTreatAsAttrFlag()){
1051 for (const auto &vdf:vd->getFields()) {
1052 try {
1053 read_dds_spvdfields(dds,filename,fileid, vd->getObjRef(),vdf->getNumRec(),vdf);
1054 }
1055 catch(...) {
1056 throw;
1057 }
1058 }
1059 }
1060 }
1061 }
1062
1063 return true;
1064}
1065
1066
1067// Build DAS for non-EOS objects in a hybrid HDF-EOS2 file.
1068bool read_das_hdfhybrid(DAS & das, const string & filename,int32 sdfd, int32 fileid,HDFSP::File**fpptr)
1069{
1070
1071 BESDEBUG("h4","Coming to read_das_hdfhybrid "<<endl);
1072 // Declare a non-EOS file pointer
1073 HDFSP::File *f = nullptr;
1074 try {
1075 // Read non-EOS objects in a hybrid HDF-EOS2 file.
1076 f = HDFSP::File::Read_Hybrid(filename.c_str(), sdfd,fileid);
1077 }
1078 catch (HDFSP::Exception &e)
1079 {
1080 if (f!=nullptr)
1081 delete f;
1082 throw InternalErr(e.what());
1083 }
1084
1085 // Remember the file pointer
1086 *fpptr = f;
1087
1088 // First Added non-HDFEOS2 SDS attributes.
1089 const vector<HDFSP::SDField *>& spsds = f->getSD()->getFields();
1090
1091 for (const auto &spfield:spsds) {
1092
1093 // Use CF field name as the DAS table name.
1094 AttrTable *at = das.get_table(spfield->getNewName());
1095 if (!at)
1096 at = das.add_table(spfield->getNewName(), new AttrTable);
1097
1098 // Some fields have "long_name" attributes,so we have to use this attribute rather than creating our own "long_name"
1099 bool long_name_flag = false;
1100
1101 for (const auto &attr:spfield->getAttributes()) {
1102
1103 if(attr->getName() == "long_name") {
1104 long_name_flag = true;
1105 break;
1106 }
1107 }
1108
1109 if(false == long_name_flag)
1110 at->append_attr("long_name", "String", spfield->getName());
1111
1112 // Map all attributes to DAP DAS.
1113 for (const auto& attr:spfield->getAttributes()) {
1114
1115 // Handle string first.
1116 if(attr->getType()==DFNT_UCHAR || attr->getType() == DFNT_CHAR){
1117
1118 // Questionable use of string. KY 2014-02-12
1119 string tempstring2(attr->getValue().begin(),attr->getValue().end());
1120 auto tempfinalstr= string(tempstring2.c_str());
1121
1122 // We want to escape the possible special characters except the fullpath attribute.
1123 // The fullpath is only added for some CERES and MERRA data. People use fullpath to keep their
1124 // original names even their original name includes special characters. KY 2014-02-12
1125 // The function to escape the special characters is moved to the libdap. So the handler just needs to pass the attribute values.
1126 // KY 2023-10-5
1127#if 0
1128 at->append_attr(attr->getNewName(), "String" , (attr->getNewName()=="fullpath")?tempfinalstr:HDFCFUtil::escattr(tempfinalstr));
1129#endif
1130 at->append_attr(attr->getNewName(), "String" , tempfinalstr);
1131 }
1132 else {
1133 for (int loc=0; loc < attr->getCount() ; loc++) {
1134 string print_rep = HDFCFUtil::print_attr(attr->getType(), loc, (void*) &(attr->getValue()[0]));
1135 at->append_attr(attr->getNewName(), HDFCFUtil::print_type(attr->getType()), print_rep);
1136 }
1137 }
1138 }
1139
1140 // Check if having _FillValue. If having _FillValue, compare the datatype of _FillValue
1141 // with the variable datatype. Correct the fillvalue datatype if necessary.
1142 if(at != nullptr) {
1143 int32 var_type = spfield->getType();
1144 try {
1145 HDFCFUtil::correct_fvalue_type(at,var_type);
1146 }
1147 catch(...) {
1148 throw;
1149 }
1150 }
1151
1152 // If H4.EnableCheckScaleOffsetType BES key is true,
1153 // if yes, check if having scale_factor and add_offset attributes;
1154 // if yes, check if scale_factor and add_offset attribute types are the same;
1155 // if no, make add_offset's datatype be the same as the datatype of scale_factor.
1156 // (CF requires the type of scale_factor and add_offset the same).
1157 if (true == HDF4RequestHandler::get_enable_check_scale_offset_type() && at !=nullptr)
1159
1160 }
1161
1162 // Handle vdata attributes.
1163 try {
1164 HDFCFUtil::handle_vdata_attrs_with_desc_key(f,das);
1165 }
1166 catch(...) {
1167 throw;
1168 }
1169
1170 return true;
1171
1172}
1173
1176void read_dds_use_eos2lib(DDS & dds, const string & filename,int32 sdfd,int32 fileid, int32 gridfd, int32 swathfd,
1177 HDFSP::File*h4file,HDFEOS2::File*eosfile)
1178{
1179
1180 BESDEBUG("h4","Coming to read_dds_use_eos2lib" <<endl);
1181 if((basename(filename).size() >=7) && ((basename(filename)).compare(0,7,"MCD43GF")==0)) {
1182 short cf_simple_type = 1;
1183 read_dds_simple_cf(dds, filename, sdfd, fileid,cf_simple_type);
1184 return;
1185 }
1186
1187 int ret_value = read_dds_hdfeos2(dds,filename,sdfd,gridfd,swathfd,h4file,eosfile);
1188
1189 BESDEBUG("h4","ret_value of read_dds_hdfeos2 is "<<ret_value<<endl);
1190
1191 // read_dds_hdfeos2 return value description:
1192 // 0: general non-EOS2 pure HDF4
1193 // 1: HDF-EOS2 hybrid
1194 // 2: MOD08_M3
1195 // HDF-EOS2 but no need to use HDF-EOS2 lib: no real dimension scales but have CVs for every dimension, treat differently
1196 // 3: AIRS version 6
1197 // HDF-EOS2 but no need to use HDF-EOS2 lib:
1198 // have dimension scales but don’t have CVs for every dimension, also need to condense dimensions, treat differently
1199 // 4. Ideal(Expected) AIRS version 6(No real products yet)
1200 // HDF-EOS2 but no need to use HDF-EOS2 lib: Have dimension scales for all dimensions
1201 // 5. MERRA
1202 // Special handling for MERRA file
1203
1204
1205 // Treat MERRA and non-HDFEOS2 HDF4 products as pure HDF4 objects
1206 // For Ideal AIRS version 6 products, we temporarily handle them in a generic HDF4 way.
1207 if (0 == ret_value || 5 == ret_value || 4 == ret_value ) {
1208 if (true == read_dds_hdfsp(dds, filename,sdfd,fileid,h4file))
1209 return;
1210 }
1211 // Special handling
1212 else if ( 1 == ret_value ) {
1213
1214 // Map non-EOS2 objects to DDS
1215 if (true ==read_dds_hdfhybrid(dds,filename,sdfd,fileid,h4file))
1216 return;
1217 }
1218 else {// ret_value = 2 and 3 are handled already in the read_dds_hdfeos2 calls. Just return.
1219 return;
1220 }
1221
1222// leave this code block for performance comparison.
1223#if 0
1224 // first map HDF-EOS2 objects to DDS
1225 if(true == read_dds_hdfeos2(dds, filename)){
1226
1227 // Map non-EOS2 objects to DDS
1228 if(true == read_dds_hdfhybrid(dds,filename))
1229 return;
1230 }
1231
1232 // Map HDF4 objects in pure HDF4 files to DDS
1233 if(read_dds_hdfsp(dds, filename)){
1234 return;
1235 }
1236#endif
1237
1238 // Call the default mapping of HDF4 to DDS. It should never reach here.
1239 // We add this line to ensure the HDF4 objects mapped to DDS even if the above routines return false.
1240 read_dds(dds, filename);
1241
1242}
1243
1244// Map other HDF global attributes, this routine must be called after all ECS metadata are handled.
1245void write_non_ecsmetadata_attrs(HE2CF& cf) {
1246
1247 cf.set_non_ecsmetadata_attrs();
1248
1249}
1250
1251// Map HDF-EOS2's ECS attributes to DAS. ECS attributes include coremetadata, structmetadata etc.
1252void write_ecsmetadata(DAS& das, HE2CF& cf, const string& _meta)
1253{
1254
1255 // There is a maximum length for each ECS metadata if one uses ECS toolkit to add the metadata.
1256 // For some products of which the metadata size is huge, one metadata may be stored in several
1257 // ECS attributes such as coremetadata.0, coremetadata.1 etc.
1258 // When mapping the ECS metadata, we need to merge such metadata attributes into one attribute(coremetadata)
1259 // so that end users can easily understand this metadata.
1260 // ECS toolkit recommends data producers to use the format shown in the following coremetadata example:
1261 // coremetadata.0, coremetadata.1, etc.
1262 // Most NASA HDF-EOS2 products follow this naming convention.
1263 // However, the toolkit also allows data producers to freely name its metadata.
1264 // So we also find the following slightly different format:
1265 // (1) No suffix: coremetadata
1266 // (2) only have one such ECS attribute: coremetadata.0
1267 // (3) have several ECS attributes with two dots after the name: coremetadata.0, coremetadata.0.1 etc.
1268 // (4) Have non-number suffix: productmetadata.s, productmetadata.t etc.
1269 // We handle the above case in the function set_metadata defined in HE2CF.cc. KY 2013-07-06
1270
1271 bool suffix_is_number = true;
1272 vector<string> meta_nonum_names;
1273 vector<string> meta_nonum_data;
1274
1275 string meta = cf.get_metadata(_meta,suffix_is_number,meta_nonum_names, meta_nonum_data);
1276
1277 if(""==meta && true == suffix_is_number){
1278 return; // No _meta metadata exists.
1279 }
1280
1281 BESDEBUG("h4",meta << endl);
1282
1283 if (false == suffix_is_number) {
1284 // For the case when the suffix is like productmetadata.s, productmetadata.t,
1285 // we will not merge the metadata since we are not sure about the order.
1286 // We just parse each attribute individually.
1287 for (unsigned int i = 0; i <meta_nonum_names.size(); i++)
1288 parse_ecs_metadata(das,meta_nonum_names[i],meta_nonum_data[i]);
1289 }
1290 else
1291 parse_ecs_metadata(das,_meta,meta);
1292
1293}
1294
1295void parse_ecs_metadata(DAS &das,const string & metaname, const string &metadata) {
1296
1297 AttrTable *at = das.get_table(metaname);
1298 if (!at)
1299 at = das.add_table(metaname, new AttrTable);
1300
1301 // tell lexer to scan attribute string
1302 void *buf = hdfeos_string(metadata.c_str());
1303 parser_arg arg(at);
1304
1305 if (hdfeosparse(&arg) != 0) {
1306 hdfeos_delete_buffer(buf);
1307 throw Error("HDF-EOS parse error while processing a " + metadata + " HDFEOS attribute.");
1308 }
1309
1310 if (arg.status() == false) {
1311 INFO_LOG("HDF-EOS parse error while processing a " + metadata + " HDFEOS attribute. (2)");
1312#if 0
1313// for debugging
1314 << arg.error()->get_error_message() << endl;
1315#endif
1316 }
1317
1318 hdfeos_delete_buffer(buf);
1319}
1320
1321// Build DAS for HDFEOS2 files.
1322int read_das_hdfeos2(DAS & das, const string & filename,int32 sdfd,int32 fileid, int32 gridfd, int32 swathfd,
1323 bool ecs_metadata,HDFSP::File**spfpptr,HDFEOS2::File **fpptr)
1324{
1325
1326 BESDEBUG("h4","Coming to read_das_hdfeos2 " << endl);
1327
1328 // There are some HDF-EOS2 files(MERRA) that should be treated
1329 // exactly like HDF4 SDS files. We don't need to use HDF-EOS2 APIs to
1330 // retrieve any information. In fact, treating them as HDF-EOS2 files
1331 // will cause confusions and retrieve wrong information, though may not be essential.
1332 // So far, we've only found that the MERRA product has this problem.
1333 // A quick fix is to check if the file name contains MERRA. KY 2011-3-4
1334 // Actually, AIRS version 6 and MODO8M3 also fall into this category,
1335 // they are also specially handled, check read_das_special_eos2_core. KY 2015-06-04
1336
1337 // Find MERRA data, return 5.
1338 if((basename(filename).size() >=5) && ((basename(filename)).compare(0,5,"MERRA")==0)) {
1339 return 5;
1340 }
1341
1342 // We will check if the handler wants to turn on the special EOS key checking
1343 if (true == HDF4RequestHandler::get_enable_special_eos()) {
1344
1345 string grid_name;
1346 int ret_val = check_special_eosfile(filename,grid_name,sdfd);
1347
1348 // Expected AIRS level 2 or 3
1349 if (4== ret_val)
1350 return ret_val;
1351
1352 bool airs_l2_l3_v6 = false;
1353 bool special_1d_grid = false;
1354
1355 // AIRS level 2,3 version 6 or MOD08_M3-like products
1356 if (2 == ret_val || 3 == ret_val) {
1357
1358 HDFSP::File *spf = nullptr;
1359 try {
1360 spf = HDFSP::File::Read(filename.c_str(),sdfd,fileid);
1361 }
1362 catch (HDFSP::Exception &e)
1363 {
1364 if (spf != nullptr)
1365 delete spf;
1366 throw InternalErr(e.what());
1367 }
1368
1369 try {
1370 if (2 == ret_val) {
1371
1372 // More check and build the relations if this is a special MOD08_M3-like file
1373 if (spf->Check_update_special(grid_name)== true){
1374
1375 special_1d_grid = true;
1376
1377 // Building the normal HDF4 DAS here.
1378 read_das_special_eos2_core(das,spf,filename,ecs_metadata);
1379
1380 // Need to handle MOD08M3 product
1381 if (grid_name =="mod08") {
1382 change_das_mod08_scale_offset(das,spf);
1383 }
1384 }
1385 }
1386 else {
1387
1388 airs_l2_l3_v6 = true;
1389 spf->Handle_AIRS_L23();
1390 read_das_special_eos2_core(das,spf,filename,ecs_metadata);
1391 }
1392
1393 }
1394 catch (...)
1395 {
1396 delete spf;
1397 throw;
1398 }
1399
1400 // If this is MOD08M3 or AIRS version 6,we just need to return the file pointer.
1401 if (true == special_1d_grid || true == airs_l2_l3_v6) {
1402 *spfpptr = spf;
1403 return ret_val;
1404 }
1405
1406 }
1407 }
1408
1409 HDFEOS2::File *f = nullptr;
1410
1411 try {
1412 // Read all the information of EOS objects from an HDF-EOS2 file
1413 f= HDFEOS2::File::Read(filename.c_str(),gridfd,swathfd);
1414 }
1415 catch (HDFEOS2::Exception &e){
1416
1417 if (f != nullptr)
1418 delete f;
1419
1420 // If this file is not an HDF-EOS2 file, return 0.
1421 if (!e.getFileType()){
1422 return 0;
1423 }
1424 else
1425 {
1426 throw InternalErr(e.what());
1427 }
1428 }
1429
1430 try {
1431 // Generate CF coordinate variables(including auxiliary coordinate variables) and dimensions
1432 // All the names follow CF.
1433 f->Prepare(filename.c_str());
1434 }
1435
1436 catch (HDFEOS2:: Exception &e) {
1437 if (f!=nullptr)
1438 delete f;
1439 throw InternalErr(e.what());
1440 }
1441
1442 *fpptr = f;
1443
1444 // HE2CF cf is used to handle hybrid SDS and SD attributes.
1445 HE2CF cf;
1446
1447 try {
1448 cf.open(filename,sdfd,fileid);
1449 }
1450 catch(...) {
1451 throw;
1452 }
1453 cf.set_DAS(&das);
1454
1455 SOType sotype = SOType::DEFAULT_CF_EQU;
1456
1457 // A flag not to generate structMetadata for the MOD13C2 file.
1458 // MOD13C2's structMetadata has wrong values. It couldn't pass the parser.
1459 // So we want to turn it off. KY 2010-8-10
1460 bool tempstrflag = false;
1461
1462#if 0
1463 // AMSR_E may stop using "SCALE_FACTOR", so the following "if block" is empty. Still leave it here for future reference. KY 2022-06-16
1464 // Product name(AMSR_E) that needs to change attribute from "SCALE FACTOR" to scale_factor etc. to follow the CF conventions
1465 if (f->getSwaths().empty() == false) {
1466 string temp_fname = basename(filename);
1467 string temp_prod_prefix = "AMSR_E";
1468 if ((temp_fname.size() > temp_prod_prefix.size()) &&
1469 (0 == (temp_fname.compare(0,temp_prod_prefix.size(),temp_prod_prefix)))) {
1470 }
1471
1472 }
1473#endif
1474
1475 // Obtain information to identify MEaSURES VIP. This product needs to be handled properly.
1476 bool gridname_change_valid_range = false;
1477 if (1 == f->getGrids().size()) {
1478 string gridname = f->getGrids()[0]->getName();
1479 if ("VIP_CMG_GRID" == gridname)
1480 gridname_change_valid_range = true;
1481 }
1482
1483 // Obtain information to identify MODIS_SWATH_Type_L1B product. This product's scale and offset need to be handled properly.
1484 bool is_modis_l1b = false;
1485
1486 // Since this is a swath product, we check swath only.
1487 for (int i = 0; i<(int) f->getSwaths().size(); i++) {
1488 const HDFEOS2::SwathDataset* swath = f->getSwaths()[i];
1489 string sname = swath->getName();
1490 if ("MODIS_SWATH_Type_L1B" == sname){
1491 is_modis_l1b = true;
1492 break;
1493 }
1494 }
1495
1496 try {
1497
1498 // MAP grids to DAS.
1499 for (int i = 0; i < (int) f->getGrids().size(); i++) {
1500
1501 const HDFEOS2::GridDataset* grid = f->getGrids()[i];
1502 string gname = grid->getName();
1503 sotype = grid->getScaleType();
1504
1505 const vector<HDFEOS2::Field*>gfields = grid->getDataFields();
1506
1507 for (const auto &gf:gfields) {
1508
1509 bool change_fvtype = false;
1510
1511 // original field name
1512 string fname = gf->getName();
1513
1514 // new field name that follows CF
1515 string newfname = gf->getNewName();
1516
1517 BESDEBUG("h4","Original field name: " << fname << endl);
1518 BESDEBUG("h4","Corrected field name: " << newfname << endl);
1519
1520 // whether coordinate variable or data variables
1521 int fieldtype = gf->getFieldType();
1522
1523 // 0 means that the data field is NOT a coordinate variable.
1524 if (fieldtype == 0){
1525
1526 // If you don't find any _FillValue through generic APIs.
1527 if (gf->haveAddedFillValue()) {
1528 BESDEBUG("h4","Has an added fill value." << endl);
1529 float addedfillvalue =
1530 gf->getAddedFillValue();
1531 int type =
1532 gf->getType();
1533 BESDEBUG("h4","Added fill value = "<<addedfillvalue);
1534 cf.write_attribute_FillValue(newfname,
1535 type, addedfillvalue);
1536 }
1537 string coordinate = gf->getCoordinate();
1538 BESDEBUG("h4","Coordinate attribute: " << coordinate <<endl);
1539 if (coordinate != "")
1540 cf.write_attribute_coordinates(newfname, coordinate);
1541 }
1542
1543 // This will override _FillValue if it's defined on the field.
1544 cf.write_attribute(gname, fname, newfname,
1545 (int)(f->getGrids().size()), fieldtype);
1546
1547 // For fieldtype values:
1548 // 0 is general fields
1549 // 1 is latitude.
1550 // 2 is longtitude.
1551 // 3 is the existing 3rd-dimension coordinate variable
1552 // 4 is the dimension that misses the coordinate variable,use natural number
1553 // 5 is time
1554 if (fieldtype > 0){
1555
1556 // MOD13C2 is treated specially.
1557 if (fieldtype == 1 && (gf->getSpecialLLFormat())==3)
1558 tempstrflag = true;
1559
1560 // Don't change the units if the 3-rd dimension field exists.(fieldtype =3)
1561 // KY 2013-02-15
1562 if (fieldtype !=3) {
1563 string tempunits = gf->getUnits();
1564 BESDEBUG("h4",
1565 "fieldtype " << fieldtype
1566 << " units" << tempunits
1567 << endl);
1568 cf.write_attribute_units(newfname, tempunits);
1569 }
1570 }
1571
1572 //Rename attributes of MODIS products.
1573 AttrTable *at = das.get_table(newfname);
1574
1575 // No need for the case that follows the CF scale and offset .
1576 if (sotype!=SOType::DEFAULT_CF_EQU && at!=nullptr)
1577 {
1578 bool has_Key_attr = false;
1579 AttrTable::Attr_iter it = at->attr_begin();
1580 while (it!=at->attr_end())
1581 {
1582 if (at->get_name(it)=="Key")
1583 {
1584 has_Key_attr = true;
1585 break;
1586 }
1587 it++;
1588 }
1589
1590 if ((false == is_modis_l1b) && (false == gridname_change_valid_range)&&(false == has_Key_attr) &&
1591 (true == HDF4RequestHandler::get_disable_scaleoffset_comp()))
1592 HDFCFUtil::handle_modis_special_attrs_disable_scale_comp(at,basename(filename), true, newfname,sotype);
1593 else {
1594
1595 // Check if the datatype of this field needs to be changed.
1596 bool changedtype = HDFCFUtil::change_data_type(das,sotype,newfname);
1597
1598 // Build up the field name list if the datatype of the field needs to be changed.
1599 if (true == changedtype)
1600 ctype_field_namelist.push_back(newfname);
1601
1602 HDFCFUtil::handle_modis_special_attrs(at,basename(filename),true, newfname,sotype,gridname_change_valid_range,changedtype,change_fvtype);
1603
1604 }
1605 }
1606
1607 // Handle AMSR-E attributes.
1608 HDFCFUtil::handle_amsr_attrs(at);
1609
1610 // Check if having _FillValue. If having _FillValue, compare the datatype of _FillValue
1611 // with the variable datatype. Correct the fillvalue datatype if necessary.
1612 if ((false == change_fvtype) && at != nullptr) {
1613 int32 var_type = gf->getType();
1614 HDFCFUtil::correct_fvalue_type(at,var_type);
1615 }
1616
1617 // if h4.enablecheckscaleoffsettype bes key is true,
1618 // if yes, check if having scale_factor and add_offset attributes;
1619 // if yes, check if scale_factor and add_offset attribute types are the same;
1620 // if no, make add_offset's datatype be the same as the datatype of scale_factor.
1621 // (cf requires the type of scale_factor and add_offset the same).
1622 if (true == HDF4RequestHandler::get_enable_check_scale_offset_type() && at!=nullptr)
1624
1625 }
1626
1627 // Add possible 1-D CV CF attributes to identify projection info. for CF.
1628 // Currently only the Sinusoidal projection is supported.
1629 HDFCFUtil::add_cf_grid_cv_attrs(das,grid);
1630
1631 }
1632 }
1633 catch(...) {
1634 //delete f;
1635 throw;
1636 }
1637
1638 try {
1639 // MAP Swath attributes to DAS.
1640 for (int i = 0; i < (int) f->getSwaths().size(); i++) {
1641
1642 const HDFEOS2::SwathDataset* swath = f->getSwaths()[i];
1643
1644 // Swath includes two parts: "Geolocation Fields" and "Data Fields".
1645 // The all_fields vector includes both.
1646 const vector<HDFEOS2::Field*> geofields = swath->getGeoFields();
1647 vector<HDFEOS2::Field*> all_fields = geofields;
1648
1649 const vector<HDFEOS2::Field*> datafields = swath->getDataFields();
1650 for (const auto &df:datafields)
1651 all_fields.push_back(df);
1652
1653 auto total_geofields = (int)(geofields.size());
1654
1655 string gname = swath->getName();
1656 BESDEBUG("h4","Swath name: " << gname << endl);
1657
1658 sotype = swath->getScaleType();
1659
1660 // field_counter is only used to separate the geo field from the data field.
1661 int field_counter = 0;
1662
1663 for (const auto &af:all_fields)
1664 {
1665 bool change_fvtype = false;
1666 string fname = af->getName();
1667 string newfname = af->getNewName();
1668 BESDEBUG("h4","Original Field name: " << fname << endl);
1669 BESDEBUG("h4","Corrected Field name: " << newfname << endl);
1670
1671 int fieldtype = af->getFieldType();
1672 if (fieldtype == 0){
1673 string coordinate = af->getCoordinate();
1674 BESDEBUG("h4","Coordinate attribute: " << coordinate <<endl);
1675 if (coordinate != "")
1676 cf.write_attribute_coordinates(newfname, coordinate);
1677 }
1678
1679 // 1 is latitude.
1680 // 2 is longitude.
1681 // Don't change "units" if a non-latlon coordinate variable exists.
1682 if(fieldtype >0 && fieldtype !=3){
1683 string tempunits = af->getUnits();
1684 BESDEBUG("h4",
1685 "fieldtype " << fieldtype
1686 << " units" << tempunits << endl);
1687 cf.write_attribute_units(newfname, tempunits);
1688
1689 }
1690 BESDEBUG("h4","Field Name: " << fname << endl);
1691
1692 // coordinate "fillvalue" attribute
1693 // This operation should only apply to data fields.
1694 if (field_counter >=total_geofields) {
1695 if(af->haveAddedFillValue()){
1696 float addedfillvalue =
1697 af->getAddedFillValue();
1698 int type =
1699 af->getType();
1700 BESDEBUG("h4","Added fill value = "<<addedfillvalue);
1701 cf.write_attribute_FillValue(newfname, type, addedfillvalue);
1702 }
1703 }
1704 cf.write_attribute(gname, fname, newfname,
1705 (int)(f->getSwaths().size()), fieldtype);
1706
1707 AttrTable *at = das.get_table(newfname);
1708
1709 // No need for CF scale and offset equation.
1710 if (sotype!=SOType::DEFAULT_CF_EQU && at!=nullptr)
1711 {
1712
1713 bool has_Key_attr = false;
1714 AttrTable::Attr_iter it = at->attr_begin();
1715 while (it!=at->attr_end())
1716 {
1717 if (at->get_name(it)=="Key")
1718 {
1719 has_Key_attr = true;
1720 break;
1721 }
1722 it++;
1723 }
1724
1725 if ((false == is_modis_l1b) && (false == gridname_change_valid_range) &&(false == has_Key_attr) &&
1726 (true == HDF4RequestHandler::get_disable_scaleoffset_comp()))
1727 HDFCFUtil::handle_modis_special_attrs_disable_scale_comp(at,basename(filename),false,newfname,sotype);
1728 else {
1729
1730 // Check if the datatype of this field needs to be changed.
1731 bool changedtype = HDFCFUtil::change_data_type(das,sotype,newfname);
1732
1733 // Build up the field name list if the datatype of the field needs to be changed.
1734 if (true == changedtype)
1735
1736 ctype_field_namelist.push_back(newfname);
1737
1738 // Handle MODIS special attributes such as valid_range, scale_factor and add_offset etc.
1739 // Need to catch the exception since this function calls handle_modis_vip_special_attrs that may
1740 // throw an exception.
1741 HDFCFUtil::handle_modis_special_attrs(at,basename(filename), false,newfname,sotype,gridname_change_valid_range,changedtype,change_fvtype);
1742 }
1743 }
1744
1745 // Handle AMSR-E attributes
1746 if (at!=nullptr)
1747 HDFCFUtil::handle_amsr_attrs(at);
1748
1749 // Check if having _FillValue. If having _FillValue, compare the datatype of _FillValue
1750 // with the variable datatype. Correct the fillvalue datatype if necessary.
1751 if ((false == change_fvtype) && at != nullptr) {
1752 int32 var_type = af->getType();
1753 HDFCFUtil::correct_fvalue_type(at,var_type);
1754 }
1755
1756 // If H4.EnableCheckScaleOffsetType BES key is true,
1757 // if yes, check if having scale_factor and add_offset attributes;
1758 // if yes, check if scale_factor and add_offset attribute types are the same;
1759 // if no, make add_offset's datatype be the same as the datatype of scale_factor.
1760 // (CF requires the type of scale_factor and add_offset the same).
1761 if (true == HDF4RequestHandler::get_enable_check_scale_offset_type() && at !=nullptr)
1763
1764 field_counter++;
1765 }
1766 }
1767 }
1768 catch(...) {
1769 throw;
1770 }
1771
1772
1773 try {
1774
1775 if(ecs_metadata == true) {
1776
1777 // Handle ECS metadata. The following metadata are what we found so far.
1778 write_ecsmetadata(das,cf, "CoreMetadata");
1779
1780 write_ecsmetadata(das,cf, "coremetadata");
1781
1782 write_ecsmetadata(das,cf,"ArchiveMetadata");
1783
1784 write_ecsmetadata(das,cf,"archivemetadata");
1785
1786 write_ecsmetadata(das,cf,"ProductMetadata");
1787
1788 write_ecsmetadata(das,cf,"productmetadata");
1789 }
1790
1791 // This cause a problem for a MOD13C2 file, So turn it off temporarily. KY 2010-6-29
1792 if (false == tempstrflag) {
1793
1794 if (false == HDF4RequestHandler::get_disable_structmeta() ) {
1795 write_ecsmetadata(das, cf, "StructMetadata");
1796 }
1797 }
1798
1799 // Write other HDF global attributes, this routine must be called after all ECS metadata are handled.
1800 write_non_ecsmetadata_attrs(cf);
1801
1802 cf.close();
1803 }
1804 catch(...) {
1805 throw;
1806 }
1807
1808 try {
1809
1810 // Check if swath or grid object (like vgroup) attributes should be mapped to DAP2. If yes, start mapping.
1811 if (true == HDF4RequestHandler::get_enable_swath_grid_attr()) {
1812
1813 // MAP grid attributes to DAS.
1814 for (int i = 0; i < (int) f->getGrids().size(); i++) {
1815
1816 HDFEOS2::GridDataset* grid = f->getGrids()[i];
1817
1818 string gname = HDFCFUtil::get_CF_string(grid->getName());
1819
1820 AttrTable*at = nullptr;
1821
1822 // Create a "grid" DAS table if this grid has attributes.
1823 if (grid->getAttributes().empty() == false){
1824 at = das.get_table(gname);
1825 if (!at)
1826 at = das.add_table(gname, new AttrTable);
1827 }
1828 if (at!= nullptr) {
1829
1830 // Process grid attributes
1831 const vector<HDFEOS2::Attribute *> grid_attrs = grid->getAttributes();
1832 for (const auto &attr:grid_attrs) {
1833
1834 int attr_type = attr->getType();
1835
1836 // We treat string differently. DFNT_UCHAR and DFNT_CHAR are treated as strings.
1837 if(attr_type==DFNT_UCHAR || attr_type == DFNT_CHAR){
1838 string tempstring2(attr->getValue().begin(),attr->getValue().end());
1839 auto tempfinalstr= string(tempstring2.c_str());
1840 at->append_attr(attr->getNewName(), "String" , tempfinalstr);
1841 }
1842
1843
1844 else {
1845 for (int loc=0; loc < attr->getCount() ; loc++) {
1846 string print_rep = HDFCFUtil::print_attr(attr->getType(), loc, (void*) &(attr->getValue()[0]));
1847 at->append_attr(attr->getNewName(), HDFCFUtil::print_type(attr->getType()), print_rep);
1848 }
1849 }
1850 }
1851 }
1852 }
1853
1854 //
1855 // MAP swath attributes to DAS.
1856 for (int i = 0; i < (int) f->getSwaths().size(); i++) {
1857
1858 const HDFEOS2::SwathDataset* swath = f->getSwaths()[i];
1859 string sname = swath->getName();
1860 AttrTable*at = nullptr;
1861
1862 // Create a "swath" DAS table if this swath has attributes.
1863 if(swath->getAttributes().empty() == false) {
1864 at = das.get_table(sname);
1865 if (!at)
1866 at = das.add_table(sname, new AttrTable);
1867 }
1868
1869 if(at != nullptr) {
1870 const vector<HDFEOS2::Attribute *> swath_attrs = swath->getAttributes();
1871 for (const auto &attr:swath_attrs) {
1872
1873 int attr_type = attr->getType();
1874
1875 // We treat string differently. DFNT_UCHAR and DFNT_CHAR are treated as strings.
1876 if(attr_type==DFNT_UCHAR || attr_type == DFNT_CHAR){
1877 string tempstring2(attr->getValue().begin(),attr->getValue().end());
1878 auto tempfinalstr= string(tempstring2.c_str());
1879 at->append_attr(attr->getNewName(), "String" , tempfinalstr);
1880 }
1881 else {
1882 for (int loc=0; loc < attr->getCount() ; loc++) {
1883 string print_rep = HDFCFUtil::print_attr(attr->getType(), loc, (void*) &(attr->getValue()[0]));
1884 at->append_attr(attr->getNewName(), HDFCFUtil::print_type(attr->getType()), print_rep);
1885 }
1886
1887 }
1888 }
1889 }
1890 }
1891 }// end of mapping swath and grid object attributes to DAP2
1892 }
1893 catch(...) {
1894 throw;
1895 }
1896
1897 return 1;
1898}
1899
1900//The wrapper of building HDF-EOS2 and special HDF4 files.
1901void read_das_use_eos2lib(DAS & das, const string & filename,
1902 int32 sdfd,int32 fileid, int32 gridfd, int32 swathfd,bool ecs_metadata,
1903 HDFSP::File**h4filepptr,HDFEOS2::File**eosfilepptr)
1904{
1905
1906 BESDEBUG("h4","Coming to read_das_use_eos2lib" << endl);
1907
1908 if((basename(filename).size() >=7) && ((basename(filename)).compare(0,7,"MCD43GF")==0)) {
1909 read_das_simple_cf(das, sdfd, fileid);
1910 return;
1911 }
1912 int ret_value = read_das_hdfeos2(das,filename,sdfd,fileid, gridfd, swathfd,ecs_metadata,h4filepptr,eosfilepptr);
1913
1914 BESDEBUG("h4","ret_value of read_das_hdfeos2 is "<<ret_value <<endl);
1915
1916 // read_das_hdfeos2 return value description:
1917 // 0: general non-EOS2 pure HDF4
1918 // 1: HDF-EOS2 hybrid
1919 // 2: MOD08_M3
1920 // HDF-EOS2 but no need to use HDF-EOS2 lib: no real dimension scales but have CVs for every dimension, treat differently
1921 // 3: AIRS version 6 level 3 and level 2
1922 // HDF-EOS2 but no need to use HDF-EOS2 lib:
1923 // have dimension scales but don’t have CVs for every dimension, also need to condense dimensions, treat differently
1924 // 4. Expected AIRS version 6 level 3 and level 2
1925 // HDF-EOS2 but no need to use HDF-EOS2 lib: Have dimension scales for all dimensions
1926 // 5. MERRA
1927 // Special handling for MERRA products.
1928
1929 // Treat as pure HDF4 objects
1930 if (ret_value == 4) {
1931 if(true == read_das_special_eos2(das, filename,sdfd,fileid,ecs_metadata,h4filepptr))
1932 return;
1933 }
1934 // Special handling, already handled
1935 else if (ret_value == 2 || ret_value == 3) {
1936 return;
1937 }
1938 else if (ret_value == 1) {
1939
1940 // Map non-EOS2 objects to DDS
1941 if(true == read_das_hdfhybrid(das,filename,sdfd,fileid,h4filepptr))
1942 return;
1943 }
1944 else {// ret_value is 0(pure HDF4) or 5(Merra)
1945 if(true == read_das_hdfsp(das, filename,sdfd, fileid,h4filepptr))
1946 return;
1947 }
1948
1949
1950// Leave the original code that don't pass the file pointers.
1951#if 0
1952 // First map HDF-EOS2 attributes to DAS
1953 if(true == read_das_hdfeos2(das, filename)){
1954
1955 // Map non-EOS2 attributes to DAS
1956 if (true == read_das_hdfhybrid(das,filename))
1957 return;
1958 }
1959
1960 // Map HDF4 attributes in pure HDF4 files to DAS
1961 if(true == read_das_hdfsp(das, filename)){
1962 return;
1963 }
1964#endif
1965
1966 // Call the default mapping of HDF4 to DAS. It should never reach here.
1967 // We add this line to ensure the HDF4 attributes mapped to DAS even if the above routines return false.
1968 read_das(das, filename);
1969}
1970
1971
1972#endif // end of #ifdef USE_HDFEOS2_LIB #endif block
1973
1974// The wrapper of building DDS function.
1975bool read_dds_hdfsp(DDS & dds, const string & filename,int32 sdfd, int32 fileid,const HDFSP::File*f)
1976{
1977
1978 BESDEBUG("h4","Coming to read_dds_sp "<<endl);
1979 dds.set_dataset_name(basename(filename));
1980
1981 // Obtain SDS fields
1982 const vector<HDFSP::SDField *>& spsds = f->getSD()->getFields();
1983
1984 // Read SDS
1985 for (const auto& spf:spsds){
1986
1987 // Although the following line's logic needs to improve, it is right.
1988 // When Has_Dim_NoScale_Field is false, it only happens to the OTHERHDF case.
1989 // For the OTHERHDF case, we will not map the dimension_no_dim_scale (empty) field. This is equivalent to
1990 if (false == f->Has_Dim_NoScale_Field() || (0 == spf->getFieldType()) || (true == spf->IsDimScale())){
1991 try {
1992 read_dds_spfields(dds,filename,sdfd,spf,f->getSPType());
1993 }
1994 catch(...) {
1995 throw;
1996 }
1997 }
1998 }
1999
2000 // Read Vdata fields.
2001 // 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.
2002
2003 bool output_vdata_flag = true;
2004 if (false == HDF4RequestHandler::get_enable_ceres_vdata() &&
2005 (CER_AVG == f->getSPType() ||
2006 CER_ES4 == f->getSPType() ||
2007 CER_SRB == f->getSPType() ||
2008 CER_ZAVG == f->getSPType()))
2009 output_vdata_flag = false;
2010
2011 if(true == output_vdata_flag) {
2012 for (const auto &vd:f->getVDATAs()) {
2013 if(!vd->getTreatAsAttrFlag()){
2014 for(const auto &vdf:vd->getFields()) {
2015 try {
2016 read_dds_spvdfields(dds,filename,fileid,vd->getObjRef(),vdf->getNumRec(),vdf);
2017 }
2018 catch(...) {
2019 throw;
2020 }
2021 }
2022 }
2023 }
2024 }
2025
2026 return true;
2027}
2028
2029// Follow CF to build DAS for non-HDFEOS2 HDF4 products. This routine also applies
2030// to all HDF4 products when HDF-EOS2 library is not configured in.
2031bool read_das_hdfsp(DAS & das, const string & filename, int32 sdfd, int32 fileid,HDFSP::File**fpptr)
2032{
2033
2034 BESDEBUG("h4","Coming to read_das_sp "<<endl);
2035
2036 // Define a file pointer
2037 HDFSP::File *f = nullptr;
2038 try {
2039 // Obtain all the necesary information from HDF4 files.
2040 f = HDFSP::File::Read(filename.c_str(), sdfd,fileid);
2041 }
2042 catch (HDFSP::Exception &e)
2043 {
2044 if (f != nullptr)
2045 delete f;
2046 throw InternalErr(e.what());
2047 }
2048
2049 try {
2050 // Generate CF coordinate variables(including auxiliary coordinate variables) and dimensions
2051 // All the names follow CF.
2052 f->Prepare();
2053 }
2054 catch (HDFSP::Exception &e) {
2055 delete f;
2056 throw InternalErr(e.what());
2057 }
2058
2059 *fpptr = f;
2060
2061 // Check if mapping vgroup attribute key is turned on, if yes, mapping vgroup attributes.
2062
2063 if (true == HDF4RequestHandler::get_enable_vgroup_attr()) {
2064
2065 // Obtain vgroup attributes if having vgroup attributes.
2066 vector<HDFSP::AttrContainer *>vg_container = f->getVgattrs();
2067 for (const auto &vgattr_c:f->getVgattrs()) {
2068 AttrTable *vgattr_at = das.get_table(vgattr_c->getName());
2069 if (!vgattr_at)
2070 vgattr_at = das.add_table(vgattr_c->getName(), new AttrTable);
2071
2072 for (const auto &attr:vgattr_c->getAttributes()) {
2073
2074 // Handle string first.
2075 if(attr->getType()==DFNT_UCHAR || attr->getType() == DFNT_CHAR){
2076 string tempstring2(attr->getValue().begin(),attr->getValue().end());
2077 string tempfinalstr= string(tempstring2.c_str());
2078 vgattr_at->append_attr(attr->getNewName(), "String" , tempfinalstr);
2079 }
2080 else {
2081 for (int loc=0; loc < attr->getCount() ; loc++) {
2082
2083 string print_rep = HDFCFUtil::print_attr(attr->getType(), loc, (void*) &(attr->getValue()[0]));
2084 vgattr_at->append_attr(attr->getNewName(), HDFCFUtil::print_type(attr->getType()), print_rep);
2085 }
2086 }
2087 }
2088 }
2089 }// end of mapping vgroup attributes.
2090
2091 // Initialize ECS metadata
2092 string core_metadata = "";
2093 string archive_metadata = "";
2094 string struct_metadata = "";
2095
2096 // Obtain SD pointer, this is used to retrieve the file attributes associated with the SD interface
2097 const HDFSP::SD* spsd = f->getSD();
2098
2099 // Except for TRMM, we don't find ECS metadata in other non-EOS products. For the option to treat EOS2 as pure HDF4, we
2100 // kind of relax the support of merging metadata as we do for the EOS2 case(read_das_hdfeos2). We will see if we have the user
2101 // request to make them consistent in the future. KY 2013-07-08
2102 for (const auto &sp_attr:spsd->getAttributes()) {
2103
2104 // Here we try to combine ECS metadata into a string.
2105 if((sp_attr->getName().compare(0, 12, "CoreMetadata" )== 0) ||
2106 (sp_attr->getName().compare(0, 12, "coremetadata" )== 0)){
2107
2108 // We assume that CoreMetadata.0, CoreMetadata.1, ..., CoreMetadata.n attribures
2109 // are processed in the right order during HDFSP::Attribute vector iteration.
2110 // Otherwise, this won't work.
2111 string tempstring(sp_attr->getValue().begin(),sp_attr->getValue().end());
2112
2113 // Temporarily turn off CERES data since there are so many fields in CERES. It will choke clients KY 2010-7-9
2114 if(f->getSPType() != CER_AVG &&
2115 f->getSPType() != CER_ES4 &&
2116 f->getSPType() !=CER_SRB &&
2117 f->getSPType() != CER_ZAVG)
2118 core_metadata.append(tempstring);
2119 }
2120 else if((sp_attr->getName().compare(0, 15, "ArchiveMetadata" )== 0) ||
2121 (sp_attr->getName().compare(0, 16, "ArchivedMetadata")==0) ||
2122 (sp_attr->getName().compare(0, 15, "archivemetadata" )== 0)){
2123
2124 string tempstring(sp_attr->getValue().begin(),sp_attr->getValue().end());
2125 // Currently some TRMM "swath" archivemetadata includes special characters that cannot be handled by OPeNDAP
2126 // So turn it off.
2127 // Turn off CERES data since it may choke JAVA clients KY 2010-7-9
2128 if(f->getSPType() != TRMML2_V6 && f->getSPType() != CER_AVG && f->getSPType() != CER_ES4 && f->getSPType() !=CER_SRB && f->getSPType() != CER_ZAVG)
2129 archive_metadata.append(tempstring);
2130 }
2131 else if((sp_attr->getName().compare(0, 14, "StructMetadata" )== 0) ||
2132 (sp_attr->getName().compare(0, 14, "structmetadata" )== 0)){
2133
2134 if (false == HDF4RequestHandler::get_disable_structmeta()) {
2135
2136 string tempstring(sp_attr->getValue().begin(),sp_attr->getValue().end());
2137
2138 // Turn off TRMM "swath" verison 6 level 2 productsCERES data since it may choke JAVA clients KY 2010-7-9
2139 if(f->getSPType() != TRMML2_V6 &&
2140 f->getSPType() != CER_AVG &&
2141 f->getSPType() != CER_ES4 &&
2142 f->getSPType() !=CER_SRB &&
2143 f->getSPType() != CER_ZAVG)
2144 struct_metadata.append(tempstring);
2145
2146 }
2147 }
2148 else {
2149 // Process gloabal attributes
2150 AttrTable *at = das.get_table("HDF_GLOBAL");
2151 if (!at)
2152 at = das.add_table("HDF_GLOBAL", new AttrTable);
2153
2154 // We treat string differently. DFNT_UCHAR and DFNT_CHAR are treated as strings.
2155 if (sp_attr->getType()==DFNT_UCHAR || sp_attr->getType() == DFNT_CHAR){
2156
2157 string tempstring2(sp_attr->getValue().begin(),sp_attr->getValue().end());
2158 auto tempfinalstr= string(tempstring2.c_str());
2159 at->append_attr(sp_attr->getNewName(), "String" , tempfinalstr);
2160 }
2161
2162 else {
2163 for (int loc=0; loc < sp_attr->getCount() ; loc++) {
2164 string print_rep = HDFCFUtil::print_attr(sp_attr->getType(), loc, (void*) &(sp_attr->getValue()[0]));
2165 at->append_attr(sp_attr->getNewName(), HDFCFUtil::print_type(sp_attr->getType()), print_rep);
2166 }
2167 }
2168 }
2169 }
2170
2171 // The following code may be condensed in the future. KY 2012-09-19
2172 // Coremetadata, structmetadata and archive metadata need special parsers.
2173
2174 // Write coremetadata.
2175 if(core_metadata.size() > 0){
2176 AttrTable *at = das.get_table("CoreMetadata");
2177 if (!at)
2178 at = das.add_table("CoreMetadata", new AttrTable);
2179 // tell lexer to scan attribute string
2180 void *buf = hdfeos_string(core_metadata.c_str());
2181 parser_arg arg(at);
2182
2183 if (hdfeosparse(&arg) != 0) {
2184 hdfeos_delete_buffer(buf);
2185 throw Error("Parse error while processing a CoreMetadata attribute.");
2186 }
2187
2188 // Errors returned from here are ignored.
2189 if (arg.status() == false) {
2190 ERROR_LOG("Parse error while processing a CoreMetadata attribute. (2)");
2191#if 0
2192 // << arg.error()->get_error_message() << endl;
2193#endif
2194 }
2195
2196 hdfeos_delete_buffer(buf);
2197 }
2198
2199 // Write archive metadata.
2200 if(archive_metadata.size() > 0){
2201 AttrTable *at = das.get_table("ArchiveMetadata");
2202 if (!at)
2203 at = das.add_table("ArchiveMetadata", new AttrTable);
2204 // tell lexer to scan attribute string
2205 void *buf = hdfeos_string(archive_metadata.c_str());
2206 parser_arg arg(at);
2207 if (hdfeosparse(&arg) != 0){
2208 hdfeos_delete_buffer(buf);
2209 throw Error("Parse error while processing an ArchiveMetadata attribute.");
2210 }
2211
2212 // Errors returned from here are ignored.
2213 if (arg.status() == false) {
2214 ERROR_LOG("Parse error while processing an ArchiveMetadata attribute. (2) ");
2215#if 0
2216 // << arg.error()->get_error_message() << endl;
2217#endif
2218 }
2219
2220 hdfeos_delete_buffer(buf);
2221 }
2222
2223 // Write struct metadata.
2224 if(struct_metadata.size() > 0){
2225 AttrTable *at = das.get_table("StructMetadata");
2226 if (!at)
2227 at = das.add_table("StructMetadata", new AttrTable);
2228 // tell lexer to scan attribute string
2229 void *buf = hdfeos_string(struct_metadata.c_str());
2230 parser_arg arg(at);
2231 if (hdfeosparse(&arg) != 0){
2232 hdfeos_delete_buffer(buf);
2233 throw Error("Parse error while processing a StructMetadata attribute.");
2234 }
2235
2236 if (arg.status() == false) {
2237 ERROR_LOG("Parse error while processing a StructMetadata attribute. (2)");
2238 }
2239
2240 // Errors returned from here are ignored.
2241#if 0
2242 if (arg.status() == false) {
2243 (*BESLog::TheLog())<< "Parse error while processing a StructMetadata attribute. (2)" << endl
2244 << arg.error()->get_error_message() << endl;
2245 }
2246#endif
2247
2248 hdfeos_delete_buffer(buf);
2249 }
2250
2251 // The following code checks the special handling of scale and offset of the OBPG products.
2252 //Store value of "Scaling" attribute.
2253 string scaling;
2254
2255 //Store value of "Slope" attribute.
2256 float slope = 0.;
2257 bool global_slope_flag = false;
2258 float intercept = 0.;
2259 bool global_intercept_flag = false;
2260
2261 // Check OBPG attributes. Specifically, check if slope and intercept can be obtained from the file level.
2262 // If having global slope and intercept, obtain OBPG scaling, slope and intercept values.
2263 HDFCFUtil::check_obpg_global_attrs(f,scaling,slope,global_slope_flag,intercept,global_intercept_flag);
2264
2265 // Handle individual fields
2266 const vector<HDFSP::SDField *>& spsds = f->getSD()->getFields();
2268 for(it_g = spsds.begin(); it_g != spsds.end(); it_g++){
2269
2270 // The following two if-statements are double secure checks. It will
2271 // make sure no-dimension-scale dimension variables and the associated coordinate variables(if any) are ignored.
2272 // Ignore ALL coordinate variables if this is "OTHERHDF" case and some dimensions
2273 // don't have dimension scale data.
2274 if ( true == f->Has_Dim_NoScale_Field() &&
2275 ((*it_g)->getFieldType() !=0)&&
2276 ((*it_g)->IsDimScale() == false))
2277 continue;
2278
2279 // Ignore the empty(no data) dimension variable.
2280 if (OTHERHDF == f->getSPType() && true == (*it_g)->IsDimNoScale())
2281 continue;
2282
2283 AttrTable *at = das.get_table((*it_g)->getNewName());
2284 if (!at)
2285 at = das.add_table((*it_g)->getNewName(), new AttrTable);
2286
2287 // Some fields have "long_name" attributes,so we have to use this attribute rather than creating our own
2288 bool long_name_flag = false;
2289
2290 for (const auto& attr:(*it_g)->getAttributes()) {
2291 if(attr->getName() == "long_name") {
2292 long_name_flag = true;
2293 break;
2294 }
2295 }
2296
2297 if(false == long_name_flag) {
2298 if (f->getSPType() == TRMML2_V7) {
2299 if((*it_g)->getFieldType() == 1)
2300 at->append_attr("standard_name","String","latitude");
2301 else if ((*it_g)->getFieldType() == 2) {
2302 at->append_attr("standard_name","String","longitude");
2303
2304 }
2305
2306 }
2307 else if (f->getSPType() == TRMML3S_V7 || f->getSPType() == TRMML3M_V7) {
2308 if((*it_g)->getFieldType() == 1) {
2309 at->append_attr("long_name","String","latitude");
2310 at->append_attr("standard_name","String","latitude");
2311
2312 }
2313 else if ((*it_g)->getFieldType() == 2) {
2314 at->append_attr("long_name","String","longitude");
2315 at->append_attr("standard_name","String","longitude");
2316 }
2317
2318 }
2319 else
2320 at->append_attr("long_name", "String", (*it_g)->getName());
2321 }
2322
2323 // For some OBPG files that only provide slope and intercept at the file level,
2324 // we need to add the global slope and intercept to all fields and change their names to scale_factor and add_offset.
2325 // For OBPG files that provide slope and intercept at the field level, we need to rename those attribute names to scale_factor and add_offset.
2326 HDFCFUtil::add_obpg_special_attrs(f,das,*it_g,scaling,slope,global_slope_flag,intercept,global_intercept_flag);
2327
2328 // MAP individual SDS field to DAP DAS
2329 for (const auto &attr:(*it_g)->getAttributes()) {
2330
2331 // Handle string first.
2332 if(attr->getType()==DFNT_UCHAR || attr->getType() == DFNT_CHAR){
2333 string tempstring2(attr->getValue().begin(),attr->getValue().end());
2334 string tempfinalstr= string(tempstring2.c_str());
2335 at->append_attr(attr->getNewName(), "String" ,tempfinalstr);
2336 }
2337 else {
2338 for (int loc=0; loc < attr->getCount() ; loc++) {
2339 string print_rep = HDFCFUtil::print_attr(attr->getType(), loc, (void*) &(attr->getValue()[0]));
2340 at->append_attr(attr->getNewName(), HDFCFUtil::print_type(attr->getType()), print_rep);
2341 }
2342 }
2343 }
2344
2345 // MAP dimension info. to DAS(Currently this should only affect the OTHERHDF case when no dimension scale for some dimensions)
2346 // KY 2012-09-19
2347 // For the type DFNT_CHAR, one dimensional char array is mapped to a scalar DAP string,
2348 // N dimensional char array is mapped to N-1 dimensional DAP string,
2349 // So the number of dimension info stored in the attribute container should be reduced by 1.
2350 // KY 2014-04-11
2351
2352 bool has_dim_info = true;
2353 vector<HDFSP::AttrContainer *>::const_iterator it_end = (*it_g)->getDimInfo().end();
2354 if ((*it_g)->getType() == DFNT_CHAR) {
2355 if ((*it_g)->getRank() >1 && (*it_g)->getDimInfo().size() >1)
2356 it_end = (*it_g)->getDimInfo().begin()+(*it_g)->getDimInfo().size() -1;
2357 else
2358 has_dim_info = false;
2359 }
2360
2361 if( true == has_dim_info) {
2362
2363 // Note: the following loop cannot be changed to range-loop because the last element is not the end of the iterator.
2364 for(vector<HDFSP::AttrContainer *>::const_iterator i=(*it_g)->getDimInfo().begin();i!=it_end;i++) {
2365
2366 // Here a little surgory to add the field path(including) name before dim0, dim1, etc.
2367 string attr_container_name = (*it_g)->getNewName() + (*i)->getName();
2368 AttrTable *dim_at = das.get_table(attr_container_name);
2369 if (!dim_at)
2370 dim_at = das.add_table(attr_container_name, new AttrTable);
2371
2372 for (const auto &attr:(*i)->getAttributes()) {
2373
2374 // Handle string first.
2375 if (attr->getType()==DFNT_UCHAR || attr->getType() == DFNT_CHAR){
2376 string tempstring2(attr->getValue().begin(),attr->getValue().end());
2377 string tempfinalstr= string(tempstring2.c_str());
2378 dim_at->append_attr(attr->getNewName(), "String" , tempfinalstr);
2379 }
2380 else {
2381 for (int loc=0; loc < attr->getCount() ; loc++) {
2382
2383 string print_rep = HDFCFUtil::print_attr(attr->getType(), loc, (void*) &(attr->getValue()[0]));
2384 dim_at->append_attr(attr->getNewName(), HDFCFUtil::print_type(attr->getType()), print_rep);
2385 }
2386 }
2387 }
2388 }
2389 }
2390
2391 // Handle special CF attributes such as units, valid_range and coordinates
2392 // Overwrite units if fieldtype is latitude.
2393 if ((*it_g)->getFieldType() == 1){
2394
2395 at->del_attr("units"); // Override any existing units attribute.
2396 at->append_attr("units", "String",(*it_g)->getUnits());
2397 if (f->getSPType() == CER_ES4) // Drop the valid_range attribute since the value will be interpreted wrongly by CF tools
2398 at->del_attr("valid_range");
2399
2400 }
2401 // Overwrite units if fieldtype is longitude
2402 if ((*it_g)->getFieldType() == 2){
2403 at->del_attr("units"); // Override any existing units attribute.
2404 at->append_attr("units", "String",(*it_g)->getUnits());
2405 if (f->getSPType() == CER_ES4) // Drop the valid_range attribute since the value will be interpreted wrongly by CF tools
2406 at->del_attr("valid_range");
2407
2408 }
2409
2410 // The following if-statement may not be necessary since fieldtype=4 is the missing CV.
2411 // This missing CV is added by the handler and the units is always level.
2412 if ((*it_g)->getFieldType() == 4){
2413 at->del_attr("units"); // Override any existing units attribute.
2414 at->append_attr("units", "String",(*it_g)->getUnits());
2415 }
2416
2417 // Overwrite coordinates if fieldtype is neither lat nor lon.
2418 if ((*it_g)->getFieldType() == 0){
2419 at->del_attr("coordinates"); // Override any existing units attribute.
2420
2421 // If no "dimension scale" dimension exists, delete the "coordinates" attributes
2422 if (false == f->Has_Dim_NoScale_Field()) {
2423 string coordinate = (*it_g)->getCoordinate();
2424 if (coordinate !="")
2425 at->append_attr("coordinates", "String", coordinate);
2426 }
2427 }
2428 }
2429
2430
2431 // For OTHERHDF products, add units for latitude and longitude; also change unit to units.
2432 HDFCFUtil::handle_otherhdf_special_attrs(f,das);
2433
2434 // For NASA products, add missing CF attributes if possible
2435 HDFCFUtil::add_missing_cf_attrs(f,das);
2436
2437 // Check if having _FillValue. If having _FillValue, compare the datatype of _FillValue
2438 // with the variable datatype. Correct the fillvalue datatype if necessary.
2439 for (it_g = spsds.begin(); it_g != spsds.end(); it_g++){
2440
2441 AttrTable *at = das.get_table((*it_g)->getNewName());
2442 if (at != nullptr) {
2443 int32 var_type = (*it_g)->getType();
2444 try {
2445 HDFCFUtil::correct_fvalue_type(at,var_type);
2446 }
2447 catch(...) {
2448 throw;
2449 }
2450 }
2451
2452 // If H4.EnableCheckScaleOffsetType BES key is true,
2453 // if yes, check if having scale_factor and add_offset attributes;
2454 // if yes, check if scale_factor and add_offset attribute types are the same;
2455 // if no, make add_offset's datatype be the same as the datatype of scale_factor.
2456 // (CF requires the type of scale_factor and add_offset the same).
2457 if (true == HDF4RequestHandler::get_enable_check_scale_offset_type() && at !=nullptr)
2459 }
2460
2461 // Optimization for users to tune the DAS output.
2462 HDFCFUtil::handle_merra_ceres_attrs_with_bes_keys(f,das,filename);
2463
2464 // Check the EnableVdataDescAttr key. If this key is turned on, the handler-added attribute VDdescname and
2465 // the attributes of vdata and vdata fields will be outputed to DAS. Otherwise, these attributes will
2466 // not output to DAS. The key will be turned off by default to shorten the DAP output. KY 2012-09-18
2467 try {
2468 HDFCFUtil::handle_vdata_attrs_with_desc_key(f,das);
2469 }
2470 catch(...) {
2471 throw;
2472 }
2473
2474 return true;
2475}
2476
2477// This routine is for case 4 of the cases returned by read_das_hdfeos2.
2478// Creating this routine is for performance reasons. Structmetadata is
2479// turned off because the information has been retrieved and presented
2480// by DDS and DAS.
2481// Currently we don't have a user case for this routine and also
2482// this code is not used. We still keep it for the future usage.
2483// KY 2014-01-29
2484
2485bool read_das_special_eos2(DAS &das,const string& filename,int32 sdfd,int32 fileid,bool ecs_metadata,HDFSP::File**fpptr) {
2486
2487 BESDEBUG("h4","Coming to read_das_special_eos2 " << endl);
2488
2489 // Define a file pointer
2490 HDFSP::File *f = nullptr;
2491
2492 try {
2493 // Obtain all the necesary information from HDF4 files.
2494 f = HDFSP::File::Read(filename.c_str(), sdfd,fileid);
2495 }
2496 catch (HDFSP::Exception &e)
2497 {
2498 if (f!= nullptr)
2499 delete f;
2500 throw InternalErr(e.what());
2501 }
2502
2503 try {
2504 // Generate CF coordinate variables(including auxiliary coordinate variables) and dimensions
2505 // All the names follow CF.
2506 f->Prepare();
2507 }
2508 catch (HDFSP::Exception &e) {
2509 delete f;
2510 throw InternalErr(e.what());
2511 }
2512
2513 *fpptr = f;
2514
2515 try {
2516 read_das_special_eos2_core(das, f, filename,ecs_metadata);
2517 }
2518 catch(...) {
2519 throw;
2520 }
2521
2522 // The return value is a dummy value, not used.
2523 return true;
2524}
2525
2526// This routine is for special EOS2 that can be tuned to build up DAS and DDS quickly.
2527// We also turn off the generation of StructMetadata for the performance reason.
2528bool read_das_special_eos2_core(DAS &das,const HDFSP::File* f,const string& filename,bool ecs_metadata) {
2529
2530 BESDEBUG("h4","Coming to read_das_special_eos2_core "<<endl);
2531
2532 // Initialize ECS metadata
2533 string core_metadata = "";
2534 string archive_metadata = "";
2535 string struct_metadata = "";
2536
2537 // Obtain SD pointer, this is used to retrieve the file attributes associated with the SD interface
2538 const HDFSP::SD* spsd = f->getSD();
2539
2540 //Ignore StructMetadata to improve performance
2541 for (const auto &attr:spsd->getAttributes()) {
2542
2543 // Here we try to combine ECS metadata into a string.
2544 if ((attr->getName().compare(0, 12, "CoreMetadata" )== 0) ||
2545 (attr->getName().compare(0, 12, "coremetadata" )== 0)){
2546
2547 if (ecs_metadata == true) {
2548
2549 // We assume that CoreMetadata.0, CoreMetadata.1, ..., CoreMetadata.n attribures
2550 // are processed in the right order during HDFSP::Attribute vector iteration.
2551 // Otherwise, this won't work.
2552 string tempstring(attr->getValue().begin(),attr->getValue().end());
2553 core_metadata.append(tempstring);
2554 }
2555 }
2556 else if((attr->getName().compare(0, 15, "ArchiveMetadata" )== 0) ||
2557 (attr->getName().compare(0, 16, "ArchivedMetadata")==0) ||
2558 (attr->getName().compare(0, 15, "archivemetadata" )== 0)){
2559 if (ecs_metadata == true) {
2560 string tempstring(attr->getValue().begin(),attr->getValue().end());
2561 archive_metadata.append(tempstring);
2562 }
2563 }
2564 else if((attr->getName().compare(0, 14, "StructMetadata" )== 0) ||
2565 (attr->getName().compare(0, 14, "structmetadata" )== 0))
2566 ; // Ignore StructMetadata for performance
2567 else {
2568 // Process gloabal attributes
2569 AttrTable *at = das.get_table("HDF_GLOBAL");
2570 if (!at)
2571 at = das.add_table("HDF_GLOBAL", new AttrTable);
2572
2573 // We treat string differently. DFNT_UCHAR and DFNT_CHAR are treated as strings.
2574 if(attr->getType()==DFNT_UCHAR || attr->getType() == DFNT_CHAR){
2575 string tempstring2(attr->getValue().begin(),attr->getValue().end());
2576 auto tempfinalstr= string(tempstring2.c_str());
2577 at->append_attr(attr->getNewName(), "String" , tempfinalstr);
2578 }
2579
2580 else {
2581 for (int loc=0; loc < attr->getCount() ; loc++) {
2582 string print_rep = HDFCFUtil::print_attr(attr->getType(), loc, (void*) &(attr->getValue()[0]));
2583 at->append_attr(attr->getNewName(), HDFCFUtil::print_type(attr->getType()), print_rep);
2584 }
2585 }
2586 }
2587 }
2588
2589 // The following code may be condensed in the future. KY 2012-09-19
2590 // Coremetadata, structmetadata and archive metadata need special parsers.
2591 if (ecs_metadata == true) {
2592
2593 // Write coremetadata.
2594 if (core_metadata.size() > 0){
2595 AttrTable *at = das.get_table("CoreMetadata");
2596 if (!at)
2597 at = das.add_table("CoreMetadata", new AttrTable);
2598 // tell lexer to scan attribute string
2599 void *buf = hdfeos_string(core_metadata.c_str());
2600 parser_arg arg(at);
2601
2602 if (hdfeosparse(&arg) != 0) {
2603 hdfeos_delete_buffer(buf);
2604 throw Error("Parse error while processing a CoreMetadata attribute.");
2605 }
2606
2607 // Errors returned from here are ignored.
2608 if (arg.status() == false) {
2609 ERROR_LOG("Parse error while processing a CoreMetadata attribute. (2)");
2610#if 0
2611 //for debugging
2612 << arg.error()->get_error_message() << endl;
2613#endif
2614 }
2615
2616 hdfeos_delete_buffer(buf);
2617
2618 }
2619
2620 // Write archive metadata.
2621 if (archive_metadata.size() > 0){
2622 AttrTable *at = das.get_table("ArchiveMetadata");
2623 if (!at)
2624 at = das.add_table("ArchiveMetadata", new AttrTable);
2625 // tell lexer to scan attribute string
2626 void *buf = hdfeos_string(archive_metadata.c_str());
2627 parser_arg arg(at);
2628 if (hdfeosparse(&arg) != 0) {
2629 hdfeos_delete_buffer(buf);
2630 throw Error("Parse error while processing an ArchiveMetadata attribute.");
2631 }
2632
2633 // Errors returned from here are ignored.
2634 if (arg.status() == false)
2635 ERROR_LOG("Parse error while processing an ArchiveMetadata attribute. (2)");
2636
2637 hdfeos_delete_buffer(buf);
2638 }
2639 }
2640
2641 // Handle individual fields
2642 const vector<HDFSP::SDField *>& spsds = f->getSD()->getFields();
2643
2644 for (const auto &sdf:spsds){
2645
2646 if(sdf->getFieldType() != 0){
2647
2648 AttrTable *at = das.get_table(sdf->getNewName());
2649 if (!at)
2650 at = das.add_table(sdf->getNewName(), new AttrTable);
2651
2652 string tempunits = sdf->getUnits();
2653 if(at->simple_find("units")== at->attr_end() && tempunits!="")
2654 at->append_attr("units", "String" ,tempunits);
2655 if(sdf->getFieldType() == 1){
2656 if(at->simple_find("long_name")== at->attr_end())
2657 at->append_attr("long_name","String","Latitude");
2658 }
2659 else if(sdf->getFieldType() == 2) {
2660 if(at->simple_find("long_name")== at->attr_end())
2661 at->append_attr("long_name","String","Longitude");
2662 }
2663 }
2664 else {// We will check if having the coordinates attribute.
2665
2666 AttrTable *at = das.get_table(sdf->getNewName());
2667 if (!at)
2668 at = das.add_table(sdf->getNewName(), new AttrTable);
2669 string tempcoors = sdf->getCoordinate();
2670
2671 // If we add the coordinates attribute, any existing coordinates attribute will be removed.
2672 if (tempcoors!=""){
2673 at->del_attr("coordinates");
2674 at->append_attr("coordinates","String",tempcoors);
2675 }
2676 }
2677
2678 // Ignore variables that don't have attributes.
2679 if (sdf->getAttributes().empty())
2680 continue;
2681
2682 AttrTable *at = das.get_table(sdf->getNewName());
2683 if (!at)
2684 at = das.add_table(sdf->getNewName(), new AttrTable);
2685
2686 // MAP individual SDS field to DAP DAS
2687 for (const auto &attr:sdf->getAttributes()) {
2688
2689 // Handle string first.
2690 if (attr->getType()==DFNT_UCHAR || attr->getType() == DFNT_CHAR){
2691 string tempstring2(attr->getValue().begin(),attr->getValue().end());
2692 auto tempfinalstr= string(tempstring2.c_str());
2693 at->append_attr(attr->getNewName(), "String" ,tempfinalstr);
2694 }
2695 else {
2696 for (int loc=0; loc < attr->getCount() ; loc++) {
2697 string print_rep = HDFCFUtil::print_attr(attr->getType(), loc, (void*) &(attr->getValue()[0]));
2698 at->append_attr(attr->getNewName(), HDFCFUtil::print_type(attr->getType()), print_rep);
2699 }
2700 }
2701 }
2702 }
2703
2704 // Handle HDF-EOS2 object attributes. These are found in AIRS version 6.
2705 HDFCFUtil::map_eos2_objects_attrs(das,filename);
2706
2707 return true;
2708}
2709
2710
2711// MOD/MYD08M3 follows the no-CF scale/offset rulea,we need to change the add_offset value when add_offset is 0.
2712void change_das_mod08_scale_offset(DAS &das, const HDFSP::File *f) {
2713
2714 // Handle individual fields
2715 // Check HDFCFUtil::handle_modis_special_attrs_disable_scale_comp
2716
2717 const vector<HDFSP::SDField *>& spsds = f->getSD()->getFields();
2718 for (const auto &sdf:spsds) {
2719 if (sdf->getFieldType() == 0){
2720 AttrTable *at = das.get_table(sdf->getNewName());
2721 if (!at)
2722 at = das.add_table(sdf->getNewName(), new AttrTable);
2723
2724 // Declare add_offset type in string format.
2725 string add_offset_type;
2726
2727 // add_offset values
2728 string add_offset_value="0";
2729 double orig_offset_value = 0;
2730 bool add_offset_modify = false;
2731
2732
2733 // Go through all attributes to find add_offset
2734 // If add_offset is 0 or add_offset is not found, we don't need
2735 // to modify the add_offset value.
2736 AttrTable::Attr_iter it = at->attr_begin();
2737 while (it!=at->attr_end())
2738 {
2739 if (at->get_name(it)=="add_offset")
2740 {
2741 add_offset_value = (*at->get_attr_vector(it)->begin());
2742 orig_offset_value = atof(add_offset_value.c_str());
2743 add_offset_type = at->get_type(it);
2744 if(add_offset_value == "0.0" || orig_offset_value == 0)
2745 add_offset_modify = false;
2746 else
2747 add_offset_modify = true;
2748 break;
2749 }
2750 it++;
2751
2752 }
2753
2754 // We need to modify the add_offset value if the add_offset exists.
2755 if (true == add_offset_modify) {
2756
2757 // Declare scale_factor type in string format.
2758 string scale_factor_type;
2759
2760 // Scale values
2761 string scale_factor_value="";
2762 double orig_scale_value = 1;
2763
2764 it = at->attr_begin();
2765 while (it!=at->attr_end())
2766 {
2767 if(at->get_name(it)=="scale_factor")
2768 {
2769 scale_factor_value = (*at->get_attr_vector(it)->begin());
2770 orig_scale_value = atof(scale_factor_value.c_str());
2771 scale_factor_type = at->get_type(it);
2772 }
2773 it++;
2774 }
2775
2776 if (scale_factor_value.size() !=0) {
2777 double new_offset_value = -1 * orig_scale_value*orig_offset_value;
2778 string print_rep = HDFCFUtil::print_attr(DFNT_FLOAT64,0,(void*)(&new_offset_value));
2779 at->del_attr("add_offset");
2780 at->append_attr("add_offset", HDFCFUtil::print_type(DFNT_FLOAT64), print_rep);
2781 }
2782 }
2783 }
2784 }
2785}
2786
2787// Function to build special AIRS version 6 and MOD08_M3 DDS. Doing this way is for improving performance.
2788bool read_dds_special_1d_grid(DDS &dds,const HDFSP::File* spf,const string& filename, int32 sdid,bool check_cache) {
2789
2790 BESDEBUG("h4","Coming to read_dds_special_1d_grid "<<endl);
2791 bool dds_cache = false;
2792 size_t total_bytes_dds_cache = 0;
2793
2794 // Only support AIRS version 6 level 2 or level 3 KY 2015-06-07
2795 if (true == check_cache) {
2796
2797 total_bytes_dds_cache = HDFCFUtil::obtain_dds_cache_size(spf);
2798 BESDEBUG("h4","Total DDS cache file size is "<< total_bytes_dds_cache<<endl);
2799 if(total_bytes_dds_cache !=0)
2800 dds_cache = true;
2801
2802 }
2803
2804 SPType sptype = OTHERHDF;
2805 const vector<HDFSP::SDField *>& spsds = spf->getSD()->getFields();
2806
2807 // Read SDS
2808 for (const auto &spsdsf:spsds) {
2809
2810 BaseType *bt=nullptr;
2811 switch(spsdsf->getType()) {
2812#define HANDLE_CASE(tid, type) \
2813 case tid: \
2814 bt = new (type)(spsdsf->getNewName(),filename); \
2815 break;
2816 HANDLE_CASE(DFNT_FLOAT32, HDFFloat32)
2817 HANDLE_CASE(DFNT_FLOAT64, HDFFloat64)
2818 HANDLE_CASE(DFNT_CHAR, HDFStr)
2819#ifndef SIGNED_BYTE_TO_INT32
2820 HANDLE_CASE(DFNT_INT8, HDFByte)
2821#else
2822 HANDLE_CASE(DFNT_INT8,HDFInt32)
2823#endif
2824 HANDLE_CASE(DFNT_UINT8, HDFByte)
2825 HANDLE_CASE(DFNT_INT16, HDFInt16)
2826 HANDLE_CASE(DFNT_UINT16, HDFUInt16)
2827 HANDLE_CASE(DFNT_INT32, HDFInt32)
2828 HANDLE_CASE(DFNT_UINT32, HDFUInt32)
2829 HANDLE_CASE(DFNT_UCHAR8, HDFByte)
2830 default:
2831 throw InternalErr(__FILE__,__LINE__,"unsupported data type.");
2832#undef HANDLE_CASE
2833 }
2834
2835 if(bt)
2836 {
2837
2838 const vector<HDFSP::Dimension*>& dims= spsdsf->getDimensions();
2839
2841
2842 // Char will be mapped to DAP string.
2843 if(DFNT_CHAR == spsdsf->getType()) {
2844 if(1 == spsdsf->getRank()) {
2845 HDFCFStr * sca_str = nullptr;
2846 try {
2847 sca_str = new HDFCFStr(
2848 sdid,
2849 spsdsf->getFieldRef(),
2850 filename,
2851 spsdsf->getName(),
2852 spsdsf->getNewName(),
2853 false
2854 );
2855 }
2856 catch(...) {
2857 delete bt;
2858 throw InternalErr(__FILE__,__LINE__,"Unable to allocate the HDFCFStr instance.");
2859 }
2860 dds.add_var(sca_str);
2861 delete bt;
2862 delete sca_str;
2863 }
2864
2865 else {
2866 HDFCFStrField *ar = nullptr;
2867 try {
2868
2869 ar = new HDFCFStrField(
2870 spsdsf->getRank() -1 ,
2871 filename,
2872 false,
2873 sdid,
2874 spsdsf->getFieldRef(),
2875 0,
2876 spsdsf->getName(),
2877 spsdsf->getNewName(),
2878 bt);
2879
2880 }
2881 catch(...) {
2882 delete bt;
2883 throw InternalErr(__FILE__,__LINE__,"Unable to allocate the HDFCFStrField instance.");
2884 }
2885
2886 for(it_d = dims.begin(); it_d != dims.begin()+dims.size()-1; it_d++)
2887 ar->append_dim((*it_d)->getSize(), (*it_d)->getName());
2888 dds.add_var(ar);
2889 delete bt;
2890 delete ar;
2891 }
2892
2893 }
2894
2895 else {// Other datatypes
2896
2897 // Non missing fields
2898 if(spsdsf->getFieldType()!= 4) {
2899 HDFSPArray_RealField *ar = nullptr;
2900
2901 try {
2902
2903 vector<int32>dimsizes;
2904
2905 dimsizes.resize(spsdsf->getRank());
2906 for(int i = 0; i <spsdsf->getRank();i++)
2907 dimsizes[i] = (dims[i])->getSize();
2908 ar = new HDFSPArray_RealField(
2909 spsdsf->getRank(),
2910 filename,
2911 sdid,
2912 spsdsf->getFieldRef(),
2913 spsdsf->getType(),
2914 sptype,
2915 spsdsf->getName(),
2916 dimsizes,
2917 spsdsf->getNewName(),
2918 bt);
2919 }
2920 catch(...) {
2921 delete bt;
2922 throw InternalErr(__FILE__,__LINE__,"Unable to allocate the HDFSPArray_RealField instance.");
2923 }
2924 for(it_d = dims.begin(); it_d != dims.end(); it_d++)
2925 ar->append_dim((*it_d)->getSize(), (*it_d)->getName());
2926 dds.add_var(ar);
2927 delete bt;
2928 delete ar;
2929 }
2930 else {
2931 if(spsdsf->getRank()!=1){
2932 delete bt;
2933 throw InternalErr(__FILE__, __LINE__, "The rank of missing Z dimension field must be 1");
2934 }
2935 int nelem = (spsdsf->getDimensions()[0])->getSize();
2936
2937 HDFSPArrayMissGeoField *ar = nullptr;
2938
2939 try {
2940 ar = new HDFSPArrayMissGeoField(
2941 spsdsf->getRank(),
2942 nelem,
2943 spsdsf->getNewName(),
2944 bt);
2945 }
2946 catch(...) {
2947 delete bt;
2948 throw InternalErr(__FILE__,__LINE__,
2949 "Unable to allocate the HDFSPArrayMissGeoField instance.");
2950 }
2951
2952
2953 for(it_d = dims.begin(); it_d != dims.end(); it_d++)
2954 ar->append_dim((*it_d)->getSize(), (*it_d)->getName());
2955 dds.add_var(ar);
2956 delete bt;
2957 delete ar;
2958
2959 }
2960 }
2961 }
2962 }
2963
2964 // If we need to generate a DDS cache file,
2965 if (true == dds_cache) {
2966
2967 // Check the file path
2968 string md_cache_dir;
2969 string key = "H4.Cache.metadata.path";
2970 bool found = false;
2971 TheBESKeys::TheKeys()->get_value(key,md_cache_dir,found);
2972
2973 if (true == found) {
2974
2975 // Create the DDS cache file name.
2976 string base_file_name = basename(filename);
2977 string dds_filename = md_cache_dir + "/"+base_file_name +"_dds";
2978
2979 // DDS cache file is a binary file, this makes the file size smaller.
2980 FILE* dds_file =fopen(dds_filename.c_str(),"wb");
2981 if(nullptr == dds_file) {
2982 string msg = "Cannot create the cache file. " + dds_filename + get_errno();
2983 throw InternalErr(__FILE__,__LINE__,msg);
2984 }
2985 int fd = fileno(dds_file);
2986 struct flock *l= lock(F_WRLCK);
2987 if (fcntl(fd, F_SETLKW, l) == -1) {
2988 fclose(dds_file);
2989 string msg = "Cannot hold the write lock for dds cached file "+ dds_filename;
2990 throw InternalErr (__FILE__, __LINE__,msg);
2991 }
2992 // TRY CATCH to close fclose.
2993 try {
2994 HDFCFUtil::write_sp_sds_dds_cache(spf,dds_file,total_bytes_dds_cache,dds_filename);
2995 }
2996 catch(...) {
2997 if (fcntl(fd, F_SETLK, lock(F_UNLCK)) == -1) {
2998 fclose(dds_file);
2999 string msg = "Cannot release the write lock for dds cached file "+ dds_filename;
3000 throw InternalErr (__FILE__, __LINE__,msg);
3001 }
3002
3003 fclose(dds_file);
3004 throw InternalErr(__FILE__,__LINE__,"Fail to generate a dds cache file.");
3005 }
3006 if (fcntl(fd, F_SETLK, lock(F_UNLCK)) == -1) {
3007 fclose(dds_file);
3008 string msg = "Cannot release the write lock for dds cached file "+ dds_filename;
3009 throw InternalErr (__FILE__, __LINE__,msg);
3010 }
3011 fclose(dds_file);
3012
3013 }
3014
3015 else {
3016 throw InternalErr (__FILE__, __LINE__,
3017 "DDS/DAS metadata cache path cannot be found when 'H4.EnableMetaDataCacheFile' key is set to be true.");
3018 }
3019 }
3020
3021 return true;
3022
3023}
3024
3025// Read SDS fields
3026void read_dds_spfields(DDS &dds,const string& filename,const int sdfd,const HDFSP::SDField *spsds, SPType sptype) {
3027
3028 BESDEBUG("h4","Coming to read_dds_spfields "<<endl);
3029
3030 // Ignore the dimension variable that is empty for non-special handling NASA HDF products
3031 if(OTHERHDF == sptype && (true == spsds->IsDimNoScale()))
3032 return;
3033
3034 BaseType *bt=nullptr;
3035 switch(spsds->getType()) {
3036
3037#define HANDLE_CASE(tid, type) \
3038 case tid: \
3039 bt = new (type)(spsds->getNewName(),filename); \
3040 break;
3041 HANDLE_CASE(DFNT_FLOAT32, HDFFloat32)
3042 HANDLE_CASE(DFNT_FLOAT64, HDFFloat64)
3043 HANDLE_CASE(DFNT_CHAR, HDFStr)
3044#ifndef SIGNED_BYTE_TO_INT32
3045 HANDLE_CASE(DFNT_INT8, HDFByte)
3046 //HANDLE_CASE(DFNT_CHAR, HDFByte);
3047#else
3048 HANDLE_CASE(DFNT_INT8,HDFInt32)
3049 //HANDLE_CASE(DFNT_CHAR, HDFInt32);
3050#endif
3051 HANDLE_CASE(DFNT_UINT8, HDFByte)
3052 HANDLE_CASE(DFNT_INT16, HDFInt16)
3053 HANDLE_CASE(DFNT_UINT16, HDFUInt16)
3054 HANDLE_CASE(DFNT_INT32, HDFInt32)
3055 HANDLE_CASE(DFNT_UINT32, HDFUInt32)
3056 HANDLE_CASE(DFNT_UCHAR, HDFByte)
3057 default:
3058 throw InternalErr(__FILE__,__LINE__,"unsupported data type.");
3059#undef HANDLE_CASE
3060 }
3061 int fieldtype = spsds->getFieldType();// Whether the field is real field,lat/lon field or missing Z-dimension field
3062
3063 if(bt)
3064 {
3065
3066 const vector<HDFSP::Dimension*>& dims= spsds->getCorrectedDimensions();
3068
3069 if(DFNT_CHAR == spsds->getType()) {
3070
3071 if(1 == spsds->getRank()) {
3072
3073 HDFCFStr * sca_str = nullptr;
3074
3075 try {
3076
3077 sca_str = new HDFCFStr(
3078 sdfd,
3079 spsds->getFieldRef(),
3080 filename,
3081 spsds->getName(),
3082 spsds->getNewName(),
3083 false
3084 );
3085 }
3086 catch(...) {
3087 delete bt;
3088 throw InternalErr(__FILE__,__LINE__,"Unable to allocate the HDFCFStr instance.");
3089 }
3090 dds.add_var(sca_str);
3091 delete bt;
3092 delete sca_str;
3093 }
3094 else {
3095 HDFCFStrField *ar = nullptr;
3096 try {
3097
3098 ar = new HDFCFStrField(
3099 spsds->getRank() -1 ,
3100 filename,
3101 false,
3102 sdfd,
3103 spsds->getFieldRef(),
3104 0,
3105 spsds->getName(),
3106 spsds->getNewName(),
3107 bt);
3108
3109 }
3110 catch(...) {
3111 delete bt;
3112 throw InternalErr(__FILE__,__LINE__,"Unable to allocate the HDFCFStrField instance.");
3113 }
3114
3115 for(it_d = dims.begin(); it_d != dims.begin()+dims.size()-1; it_d++)
3116 ar->append_dim((*it_d)->getSize(), (*it_d)->getName());
3117 dds.add_var(ar);
3118 delete bt;
3119 delete ar;
3120 }
3121
3122 }
3123
3124 // For non-CV variables and the existing non-lat/lon CV variables
3125 else if(fieldtype == 0 || fieldtype == 3 ) {
3126
3127 HDFSPArray_RealField *ar = nullptr;
3128
3129 try {
3130 vector<int32>dimsizes;
3131 dimsizes.resize(spsds->getRank());
3132 for(int i = 0; i <spsds->getRank();i++)
3133 dimsizes[i] = (dims[i])->getSize();
3134
3135 ar = new HDFSPArray_RealField(
3136 spsds->getRank(),
3137 filename,
3138 sdfd,
3139 spsds->getFieldRef(),
3140 spsds->getType(),
3141 sptype,
3142 spsds->getName(),
3143 dimsizes,
3144 spsds->getNewName(),
3145 bt);
3146 }
3147 catch(...) {
3148 delete bt;
3149 throw InternalErr(__FILE__,__LINE__,"Unable to allocate the HDFSPArray_RealField instance.");
3150 }
3151
3152 for(it_d = dims.begin(); it_d != dims.end(); it_d++)
3153 ar->append_dim((*it_d)->getSize(), (*it_d)->getName());
3154 dds.add_var(ar);
3155 delete bt;
3156 delete ar;
3157 }
3158
3159 // For latitude and longitude
3160 else if(fieldtype == 1 || fieldtype == 2) {
3161
3162 if (sptype == MODISARNSS || sptype == TRMML2_V7) {
3163 HDFSPArray_RealField *ar = nullptr;
3164 try {
3165
3166 vector<int32>dimsizes;
3167
3168 dimsizes.resize(spsds->getRank());
3169 for(int i = 0; i <spsds->getRank();i++)
3170 dimsizes[i] = (dims[i])->getSize();
3171
3172 ar = new HDFSPArray_RealField(
3173 spsds->getRank(),
3174 filename,
3175 sdfd,
3176 spsds->getFieldRef(),
3177 spsds->getType(),
3178 sptype,
3179 spsds->getName(),
3180 dimsizes,
3181 spsds->getNewName(),
3182 bt);
3183 }
3184 catch(...) {
3185 delete bt;
3186 throw InternalErr(__FILE__,__LINE__,
3187 "Unable to allocate the HDFSPArray_RealField instance.");
3188 }
3189
3190
3191 for(it_d = dims.begin(); it_d != dims.end(); it_d++)
3192 ar->append_dim((*it_d)->getSize(), (*it_d)->getName());
3193 dds.add_var(ar);
3194 delete bt;
3195 delete ar;
3196
3197 }
3198 else {
3199
3200 HDFSPArrayGeoField *ar = nullptr;
3201
3202 try {
3203 ar = new HDFSPArrayGeoField(
3204 spsds->getRank(),
3205 filename,
3206 sdfd,
3207 spsds->getFieldRef(),
3208 spsds->getType(),
3209 sptype,
3210 fieldtype,
3211 spsds->getName(),
3212 spsds->getNewName(),
3213 bt);
3214 }
3215 catch(...) {
3216 delete bt;
3217 throw InternalErr(__FILE__,__LINE__,
3218 "Unable to allocate the HDFSPArray_RealField instance.");
3219 }
3220
3221 for(it_d = dims.begin(); it_d != dims.end(); it_d++)
3222 ar->append_dim((*it_d)->getSize(), (*it_d)->getName());
3223 dds.add_var(ar);
3224 delete bt;
3225 delete ar;
3226 }
3227 }
3228
3229
3230 else if(fieldtype == 4) { //missing Z dimensional field(or coordinate variables with missing values)
3231 if(spsds->getRank()!=1){
3232 delete bt;
3233 throw InternalErr(__FILE__, __LINE__, "The rank of missing Z dimension field must be 1");
3234 }
3235 int nelem = (spsds->getDimensions()[0])->getSize();
3236
3237 HDFSPArrayMissGeoField *ar = nullptr;
3238
3239 try {
3240 ar = new HDFSPArrayMissGeoField(
3241 spsds->getRank(),
3242 nelem,
3243 spsds->getNewName(),
3244 bt);
3245 }
3246 catch(...) {
3247 delete bt;
3248 throw InternalErr(__FILE__,__LINE__,
3249 "Unable to allocate the HDFSPArrayMissGeoField instance.");
3250 }
3251
3252
3253 for(it_d = dims.begin(); it_d != dims.end(); it_d++)
3254 ar->append_dim((*it_d)->getSize(), (*it_d)->getName());
3255 dds.add_var(ar);
3256 delete bt;
3257 delete ar;
3258 }
3259 // fieldtype =5 originally keeps for time. Still keep it for a while.
3260
3261 else if(fieldtype == 6) { //Coordinate variables added from the product specification
3262
3263 if(spsds->getRank()!=1){
3264 delete bt;
3265 throw InternalErr(__FILE__, __LINE__, "The rank of added coordinate variable must be 1");
3266 }
3267 int nelem = (spsds->getDimensions()[0])->getSize();
3268
3269 HDFSPArrayAddCVField *ar = nullptr;
3270 try {
3271 ar = new HDFSPArrayAddCVField(
3272 spsds->getType(),
3273 sptype,
3274 spsds->getName(),
3275 nelem,
3276 spsds->getNewName(),
3277 bt);
3278 }
3279 catch(...) {
3280 delete bt;
3281 throw InternalErr(__FILE__,__LINE__,
3282 "Unable to allocate the HDFSPArrayAddCVField instance.");
3283 }
3284
3285
3286 for(it_d = dims.begin(); it_d != dims.end(); it_d++)
3287 ar->append_dim((*it_d)->getSize(), (*it_d)->getName());
3288 dds.add_var(ar);
3289 delete bt;
3290 delete ar;
3291 }
3292 else {
3293 delete bt;
3294 throw InternalErr(__FILE__, __LINE__, "The field type should be one of 0,1,2,3,4 or 6.");
3295
3296 }
3297 }
3298
3299}
3300
3301// Read Vdata fields.
3302void read_dds_spvdfields(DDS &dds,const string & filename, const int fileid,int32 objref,int32 numrec,HDFSP::VDField *spvd) {
3303
3304 BESDEBUG("h4","Coming to read_dds_spvdfields "<<endl);
3305
3306 // First map the HDF4 datatype to DAP2
3307 BaseType *bt=nullptr;
3308 switch(spvd->getType()) {
3309#define HANDLE_CASE(tid, type) \
3310 case tid: \
3311 bt = new (type)(spvd->getNewName(),filename); \
3312 break;
3313 HANDLE_CASE(DFNT_FLOAT32, HDFFloat32)
3314 HANDLE_CASE(DFNT_FLOAT64, HDFFloat64)
3315 HANDLE_CASE(DFNT_CHAR8,HDFStr)
3316#ifndef SIGNED_BYTE_TO_INT32
3317 HANDLE_CASE(DFNT_INT8, HDFByte)
3318#else
3319 HANDLE_CASE(DFNT_INT8,HDFInt32)
3320#endif
3321 HANDLE_CASE(DFNT_UINT8, HDFByte)
3322 HANDLE_CASE(DFNT_INT16, HDFInt16)
3323 HANDLE_CASE(DFNT_UINT16, HDFUInt16)
3324 HANDLE_CASE(DFNT_INT32, HDFInt32)
3325 HANDLE_CASE(DFNT_UINT32, HDFUInt32)
3326 HANDLE_CASE(DFNT_UCHAR8, HDFByte)
3327 //HANDLE_CASE(DFNT_CHAR8, HDFByte)
3328 //HANDLE_CASE(DFNT_CHAR8, HDFByte)
3329 default:
3330 throw InternalErr(__FILE__,__LINE__,"unsupported data type.");
3331#undef HANDLE_CASE
3332 }
3333
3334 if(bt)
3335 {
3336
3337 if(DFNT_CHAR == spvd->getType()) {
3338
3339 // If the field order is >1, the vdata field will be 2-D array
3340 // with the number of elements along the fastest changing dimension
3341 // as the field order.
3342 int vdrank = ((spvd->getFieldOrder())>1)?2:1;
3343 if (1 == vdrank) {
3344
3345 HDFCFStr * sca_str = nullptr;
3346 try {
3347 sca_str = new HDFCFStr(
3348 fileid,
3349 objref,
3350 filename,
3351 spvd->getName(),
3352 spvd->getNewName(),
3353 true
3354 );
3355 }
3356 catch(...) {
3357 delete bt;
3358 throw InternalErr(__FILE__,__LINE__,"Unable to allocate the HDFCFStr instance.");
3359 }
3360 dds.add_var(sca_str);
3361 delete bt;
3362 delete sca_str;
3363 }
3364
3365 else {
3366
3367 HDFCFStrField *ar = nullptr;
3368 try {
3369
3370 ar = new HDFCFStrField(
3371 vdrank -1 ,
3372 filename,
3373 true,
3374 fileid,
3375 objref,
3376 spvd->getFieldOrder(),
3377 spvd->getName(),
3378 spvd->getNewName(),
3379 bt);
3380
3381 }
3382 catch(...) {
3383 delete bt;
3384 throw InternalErr(__FILE__,__LINE__,"Unable to allocate the HDFCFStrField instance.");
3385 }
3386
3387 string dimname0 = "VDFDim0_"+spvd->getNewName();
3388 ar->append_dim(numrec, dimname0);
3389 dds.add_var(ar);
3390 delete bt;
3391 delete ar;
3392
3393 }
3394 }
3395 else {
3396 HDFSPArray_VDField *ar = nullptr;
3397
3398 // If the field order is >1, the vdata field will be 2-D array
3399 // with the number of elements along the fastest changing dimension
3400 // as the field order.
3401 int vdrank = ((spvd->getFieldOrder())>1)?2:1;
3402 ar = new HDFSPArray_VDField(
3403 vdrank,
3404 filename,
3405 fileid,
3406 objref,
3407 spvd->getType(),
3408 spvd->getFieldOrder(),
3409 spvd->getName(),
3410 spvd->getNewName(),
3411 bt);
3412
3413 string dimname1 = "VDFDim0_"+spvd->getNewName();
3414
3415 string dimname2 = "VDFDim1_"+spvd->getNewName();
3416 if(spvd->getFieldOrder() >1) {
3417 ar->append_dim(numrec,dimname1);
3418 ar->append_dim(spvd->getFieldOrder(),dimname2);
3419 }
3420 else
3421 ar->append_dim(numrec,dimname1);
3422
3423 dds.add_var(ar);
3424 delete bt;
3425 delete ar;
3426 }
3427 }
3428
3429}
3430
3431// This routine will check if this is a special EOS2 file that we can improve the performance
3432// Currently AIRS level 2 and 3 version 6 and MOD08_M3-like products are what we can serve. KY 2014-01-29
3433int check_special_eosfile(const string & filename, string& grid_name,int32 sdfd) {
3434
3435 int32 sds_id = 0;
3436 int32 n_sds = 0;
3437 int32 n_sd_attrs = 0;
3438 bool is_eos = false;
3439 int ret_val = 0;
3440
3441 // Obtain number of SDS objects and number of SD(file) attributes
3442 if (SDfileinfo (sdfd, &n_sds, &n_sd_attrs) == FAIL){
3443 throw InternalErr (__FILE__,__LINE__,"SDfileinfo failed ");
3444 }
3445
3446 char attr_name[H4_MAX_NC_NAME];
3447 int32 attr_type = -1;
3448 int32 attr_count = -1;
3449 char structmdname[] = "StructMetadata.0";
3450
3451 // Is this an HDF-EOS2 file?
3452 for (int attr_index = 0; attr_index < n_sd_attrs;attr_index++) {
3453 if(SDattrinfo(sdfd,attr_index,attr_name,&attr_type,&attr_count) == FAIL) {
3454 throw InternalErr (__FILE__,__LINE__,"SDattrinfo failed ");
3455 }
3456
3457 if(strcmp(attr_name,structmdname)==0) {
3458 is_eos = true;
3459 break;
3460 }
3461 }
3462
3463 if(true == is_eos) {
3464
3465 int sds_index = 0;
3466 int32 sds_rank = 0;
3467 int32 dim_sizes[H4_MAX_VAR_DIMS];
3468 int32 sds_dtype = 0;
3469 int32 n_sds_attrs = 0;
3470 char sds_name[H4_MAX_NC_NAME];
3471 char xdim_name[] ="XDim";
3472 char ydim_name[] ="YDim";
3473
3474 string temp_grid_name1;
3475 string temp_grid_name2;
3476 bool xdim_is_cv_flag = false;
3477 bool ydim_is_cv_flag = false;
3478
3479
3480 // The following for-loop checks if this is a MOD08_M3-like HDF-EOS2 product.
3481 for (sds_index = 0; sds_index < n_sds; sds_index++) {
3482
3483 sds_id = SDselect (sdfd, sds_index);
3484 if (sds_id == FAIL) {
3485 throw InternalErr (__FILE__,__LINE__,"SDselect failed ");
3486 }
3487
3488 // Obtain object name, rank, size, field type and number of SDS attributes
3489 int status = SDgetinfo (sds_id, sds_name, &sds_rank, dim_sizes,
3490 &sds_dtype, &n_sds_attrs);
3491 if (status == FAIL) {
3492 SDendaccess(sds_id);
3493 throw InternalErr (__FILE__,__LINE__,"SDgetinfo failed ");
3494 }
3495
3496 if (1 == sds_rank) {
3497
3498 // This variable "XDim" exists
3499 if(strcmp(sds_name,xdim_name) == 0) {
3500 int32 sds_dimid = SDgetdimid(sds_id,0);
3501 if(sds_dimid == FAIL) {
3502 SDendaccess(sds_id);
3503 throw InternalErr (__FILE__,__LINE__,"SDgetinfo failed ");
3504 }
3505 char dim_name[H4_MAX_NC_NAME];
3506 int32 dim_size = 0;
3507 int32 dim_type = 0;
3508 int32 num_dim_attrs = 0;
3509 if(SDdiminfo(sds_dimid,dim_name,&dim_size,&dim_type,&num_dim_attrs) == FAIL) {
3510 SDendaccess(sds_id);
3511 throw InternalErr(__FILE__,__LINE__,"SDdiminfo failed ");
3512 }
3513
3514 // No dimension scale and XDim exists
3515 if(0 == dim_type) {
3516 string tempdimname(dim_name);
3517 if(tempdimname.size() >=5) {
3518 if(tempdimname.compare(0,5,"XDim:") == 0) {
3519
3520 // Obtain the grid name.
3521 temp_grid_name1 = tempdimname.substr(5);
3522 xdim_is_cv_flag = true;
3523
3524 }
3525 }
3526 else if("XDim" == tempdimname)
3527 xdim_is_cv_flag = true;
3528 }
3529 }
3530
3531 // The variable "YDim" exists
3532 if(strcmp(sds_name,ydim_name) == 0) {
3533
3534 int32 sds_dimid = SDgetdimid(sds_id,0);
3535 if(sds_dimid == FAIL) {
3536 SDendaccess (sds_id);
3537 throw InternalErr (__FILE__,__LINE__,"SDgetinfo failed ");
3538 }
3539 char dim_name[H4_MAX_NC_NAME];
3540 int32 dim_size = 0;
3541 int32 dim_type = 0;
3542 int32 num_dim_attrs = 0;
3543 if(SDdiminfo(sds_dimid,dim_name,&dim_size,&dim_type,&num_dim_attrs) == FAIL) {
3544 SDendaccess(sds_id);
3545 throw InternalErr(__FILE__,__LINE__,"SDdiminfo failed ");
3546 }
3547
3548 // For this case, the dimension should not have dimension scales.
3549 if(0 == dim_type) {
3550 string tempdimname(dim_name);
3551 if(tempdimname.size() >=5) {
3552 if(tempdimname.compare(0,5,"YDim:") == 0) {
3553 // Obtain the grid name.
3554 temp_grid_name2 = tempdimname.substr(5);
3555 ydim_is_cv_flag = true;
3556 }
3557 }
3558 else if ("YDim" == tempdimname)
3559 ydim_is_cv_flag = true;
3560 }
3561 }
3562 }
3563
3564 SDendaccess(sds_id);
3565 if((true == xdim_is_cv_flag) && (true == ydim_is_cv_flag ))
3566 break;
3567
3568 }
3569
3570 // If one-grid and variable XDim/YDim exist and also they don't have dim. scales,we treat this as MOD08-M3-like products
3571 if ((temp_grid_name1 == temp_grid_name2) && (true == xdim_is_cv_flag) && (true == ydim_is_cv_flag)) {
3572 grid_name = temp_grid_name1;
3573 ret_val = 2;
3574 }
3575
3576 // Check if this is a new AIRS level 2 and 3 product. Since new AIRS level 2 and 3 version 6 products still have dimensions that don't have
3577 // dimension scales and the old way to handle level 2 and 3 dimensions makes the performance suffer. We will see if we can improve
3578 // performance by handling the data with just the HDF4 interfaces.
3579 // At least the file name should have string AIRS.L3. or AIRS.L2..
3580 else if((basename(filename).size() >8) && (basename(filename).compare(0,4,"AIRS") == 0)
3581 && ((basename(filename).find(".L3.")!=string::npos) || (basename(filename).find(".L2.")!=string::npos))){
3582
3583 bool has_dimscale = false;
3584
3585 // Go through the SDS object and check if this file has dimension scales.
3586 for (sds_index = 0; sds_index < n_sds; sds_index++) {
3587
3588 sds_id = SDselect (sdfd, sds_index);
3589 if (sds_id == FAIL) {
3590 throw InternalErr (__FILE__,__LINE__,"SDselect failed ");
3591 }
3592
3593 // Obtain object name, rank, size, field type and number of SDS attributes
3594 int status = SDgetinfo (sds_id, sds_name, &sds_rank, dim_sizes,
3595 &sds_dtype, &n_sds_attrs);
3596 if (status == FAIL) {
3597 SDendaccess(sds_id);
3598 throw InternalErr (__FILE__,__LINE__,"SDgetinfo failed ");
3599 }
3600
3601 for (int dim_index = 0; dim_index<sds_rank; dim_index++) {
3602
3603 int32 sds_dimid = SDgetdimid(sds_id,dim_index);
3604 if(sds_dimid == FAIL) {
3605 SDendaccess(sds_id);
3606 throw InternalErr (__FILE__,__LINE__,"SDgetinfo failed ");
3607 }
3608
3609 char dim_name[H4_MAX_NC_NAME];
3610 int32 dim_size = 0;
3611 int32 dim_type = 0;
3612 int32 num_dim_attrs = 0;
3613 if(SDdiminfo(sds_dimid,dim_name,&dim_size,&dim_type,&num_dim_attrs) == FAIL) {
3614 SDendaccess(sds_id);
3615 throw InternalErr(__FILE__,__LINE__,"SDdiminfo failed ");
3616 }
3617
3618 if(dim_type !=0) {
3619 has_dimscale = true;
3620 break;
3621 }
3622 }
3623 SDendaccess(sds_id);
3624 if( true == has_dimscale)
3625 break;
3626 }
3627
3628 // If having dimension scales, this is an AIRS level 2 or 3 version 6. Treat it differently. Otherwise, this is an old AIRS level 3 product.
3629 if (true == has_dimscale)
3630 ret_val = 3;
3631 }
3632 else
3633 ret_val = 1;
3634#if 0
3635 else {// Check if this is an HDF-EOS2 file but not using HDF-EOS2 at all.
3636 // We turn off this for the time being because
3637 // 1) We need to make sure this is a grid file not swath or point file.
3638 // It will be time consuming to identify grids or swaths and hurts the performance for general case.
3639 // 2) No real NASA files exist. We will handle them later.
3640 // KY 2014-01-29
3641 ;
3642 bool has_dimscale = true;
3643 bool is_grid = false;
3644
3645 // Go through the SDS object
3646 for (sds_index = 0; sds_index < n_sds; sds_index++) {
3647
3648 sds_id = SDselect (sdid, sds_index);
3649 if (sds_id == FAIL) {
3650 SDend(sdid);
3651 throw InternalErr (__FILE__,__LINE__,"SDselect failed ");
3652 }
3653
3654 // Obtain object name, rank, size, field type and number of SDS attributes
3655 int status = SDgetinfo (sds_id, sds_name, &sds_rank, dim_sizes,
3656 &sds_dtype, &n_sds_attrs);
3657 if (status == FAIL) {
3658 SDendaccess(sds_id);
3659 SDend(sdid);
3660 throw InternalErr (__FILE__,__LINE__,"SDgetinfo failed ");
3661 }
3662
3663
3664 for (int dim_index = 0; dim_index<sds_rank; dim_index++) {
3665
3666 int32 sds_dimid = SDgetdimid(sds_id,dim_index);
3667 if(sds_dimid == FAIL) {
3668 SDendaccess(sds_id);
3669 SDend(sdid);
3670 throw InternalErr (__FILE__,__LINE__,"SDgetinfo failed ");
3671 }
3672 char dim_name[H4_MAX_NC_NAME];
3673 int32 dim_size = 0;
3674 int32 dim_type = 0;
3675 int32 num_dim_attrs = 0;
3676 if(SDdiminfo(sds_dimid,dim_name,&dim_size,&dim_type,&num_dim_attrs) == FAIL) {
3677 SDendaccess(sds_id);
3678 SDend(sdid);
3679 throw InternalErr(__FILE__,__LINE__,"SDdiminfo failed ");
3680 }
3681
3682 if(0 == dim_type) {
3683 has_dimscale = false;
3684 }
3685
3686 }
3687 SDendaccess(sds_id);
3688 }
3689 if (true == has_dimscale)
3690 ret_val = 4;
3691 }
3692#endif
3693 }
3694
3695 return ret_val;
3696}
3697
3698// Generate DAS for the file that only use SDS APIs. Currently this routine only applies to AIRS version 6
3699// that can take advantage of the handler's metadata cache feature.
3700void read_das_sds(DAS & das, const string & filename,int32 sdfd, bool ecs_metadata,HDFSP::File**h4fileptr) {
3701
3702 HDFSP::File *spf = nullptr;
3703 try {
3704 spf = HDFSP::File::Read(filename.c_str(),sdfd,-1);
3705 spf->Handle_AIRS_L23();
3706 read_das_special_eos2_core(das,spf,filename,ecs_metadata);
3707 }
3708 catch (HDFSP::Exception &e)
3709 {
3710 if (spf != nullptr)
3711 delete spf;
3712 throw InternalErr(e.what());
3713 }
3714
3715 *h4fileptr = spf;
3716 return;
3717}
3718
3719// Generate DDS for the file that only use SDS APIs. Currently this routine only applies to AIRS version 6
3720// that can take advantage of the handler's metadata cache feature.
3721void read_dds_sds(DDS &dds, const string & filename,int32 sdfd, HDFSP::File*h4file,bool dds_setcache) {
3722
3723 // Set DDS dataset.
3724 dds.set_dataset_name(basename(filename));
3725 read_dds_special_1d_grid(dds,h4file,filename,sdfd,dds_setcache);
3726 return;
3727
3728}
3729
3730void read_das_simple_cf(DAS &das, int32 sdfd, int32 fileid) {
3731
3732 int32 n_sds = 0;
3733 int32 n_sd_attrs = 0;
3734
3735 // Obtain number of SDS objects and number of SD(file) attributes
3736 if (SDfileinfo (sdfd, &n_sds, &n_sd_attrs) == FAIL){
3737 close_vgroup_fileids(fileid,sdfd,-1);
3738 throw InternalErr (__FILE__,__LINE__,"SDfileinfo failed ");
3739 }
3740
3741 AttrTable *at = das.add_table("HDF_GLOBAL", new AttrTable);
3742
3743 for (int attr_index = 0; attr_index < n_sd_attrs;attr_index++) {
3744
3745 char attr_name[H4_MAX_NC_NAME];
3746 int32 attr_type = -1;
3747 int32 attr_count = -1;
3748
3749 if (SDattrinfo(sdfd,attr_index,attr_name,&attr_type,&attr_count) == FAIL) {
3750 close_vgroup_fileids(fileid,sdfd,-1);
3751 throw InternalErr (__FILE__,__LINE__,"SDattrinfo failed ");
3752 }
3753
3754 vector<char> attr_value;
3755 attr_value.resize(attr_count * DFKNTsize(attr_type));
3756 if (SDreadattr (sdfd, attr_index, attr_value.data()) == -1) {
3757 close_vgroup_fileids(fileid,sdfd,-1);
3758 throw InternalErr(__FILE__,__LINE__, "SDreadattr failed ");
3759 }
3760
3761 string das_attrname(attr_name);
3762 das_attrname = HDFCFUtil::get_CF_string(das_attrname);
3763 if (attr_type==DFNT_UCHAR || attr_type == DFNT_CHAR){
3764 string tempstring(attr_value.begin(),attr_value.end());
3765 auto tempfinalstr= string(tempstring.c_str());
3766 at->append_attr(HDFCFUtil::get_CF_string(das_attrname), "String" , tempfinalstr);
3767 }
3768 else {
3769
3770 for (int loc=0; loc < attr_count ; loc++) {
3771 string print_rep = HDFCFUtil::print_attr(attr_type, loc, (void*) (attr_value.data()));
3772 at->append_attr(das_attrname, HDFCFUtil::print_type(attr_type), print_rep);
3773 }
3774
3775 }
3776
3777 }
3778
3779 for (int i = 0; i <n_sds; i++) {
3780
3781 uint16 name_len = 0;
3782 vector<char> sds_name;
3783
3784 int32 sds_id = SDselect(sdfd,i);
3785 if (sds_id == FAIL) {
3786 close_vgroup_fileids(fileid,sdfd,-1);
3787 throw InternalErr (__FILE__,__LINE__,"SDselect failed ");
3788 }
3789
3790 if (SDgetnamelen(sds_id, &name_len) == FAIL) {
3791 SDendaccess(sds_id);
3792 close_vgroup_fileids(fileid,sdfd,-1);
3793 throw InternalErr(__FILE__, __LINE__, "Fail to obtain the SDS name length.");
3794 }
3795
3796 sds_name.resize(name_len+1);
3797
3798 int32 n_sds_attrs = 0;
3799
3800 // Obtain object name, rank, size, field type and number of SDS attributes
3801 if (FAIL == SDgetinfo (sds_id, sds_name.data(), nullptr, nullptr, nullptr, &n_sds_attrs)) {
3802 SDendaccess(sds_id);
3803 close_vgroup_fileids(fileid,sdfd,-1);
3804 throw InternalErr(__FILE__, __LINE__, "Fail to obtain the SDS ID.");
3805 }
3806
3807 string sds_name_str(sds_name.begin(),sds_name.end()-1);
3808
3809 // Handle special characters in the sds_name.
3810 sds_name_str = HDFCFUtil::get_CF_string(sds_name_str);
3811
3812 AttrTable *at_sds = das.get_table(sds_name_str);
3813 if (!at_sds)
3814 at_sds = das.add_table(sds_name_str, new AttrTable);
3815
3816 char attr_name[H4_MAX_NC_NAME];
3817 for (int attrindex = 0; attrindex < n_sds_attrs; attrindex++) {
3818
3819 int32 sds_attr_type = 0;
3820 int32 attr_value_count = 0;
3821 if (FAIL==SDattrinfo (sds_id, attrindex, attr_name, &sds_attr_type, &attr_value_count)) {
3822 SDendaccess(sds_id);
3823 close_vgroup_fileids(fileid,sdfd,-1);
3824 throw InternalErr(__FILE__,__LINE__, "SDattrinfo failed ");
3825 }
3826
3827 vector<char> attr_value;
3828 attr_value.resize(attr_value_count * DFKNTsize(sds_attr_type));
3829 if (SDreadattr (sds_id, attrindex, attr_value.data()) == -1) {
3830 SDendaccess(sds_id);
3831 close_vgroup_fileids(fileid,sdfd,-1);
3832 throw InternalErr(__FILE__,__LINE__, "SDreadattr failed ");
3833 }
3834
3835 string das_attrname (attr_name);
3836 das_attrname = HDFCFUtil::get_CF_string(das_attrname);
3837 if (sds_attr_type==DFNT_UCHAR || sds_attr_type == DFNT_CHAR){
3838 string tempstring(attr_value.begin(),attr_value.end());
3839 auto tempfinalstr= string(tempstring.c_str());
3840 at_sds->append_attr(HDFCFUtil::get_CF_string(das_attrname), "String" , tempfinalstr);
3841 }
3842 else {
3843
3844 for (int loc=0; loc < attr_value_count ; loc++) {
3845 string print_rep = HDFCFUtil::print_attr(sds_attr_type, loc, (void*) (attr_value.data()));
3846 at_sds->append_attr(das_attrname, HDFCFUtil::print_type(sds_attr_type), print_rep);
3847 }
3848 }
3849 }
3850 SDendaccess(sds_id);
3851 }
3852}
3853
3854void obtain_cf_simple_lat_lon(int32 sdfd,int32 fileid,int32 n_sds, string &lat_name, string &lon_name, int &lat_size, int &lon_size) {
3855
3856 bool find_lat = false;
3857 bool find_lon = false;
3858
3859 for (int i = 0; i <n_sds; i++) {
3860
3861 uint16 name_len = 0;
3862 vector<char> sds_name;
3863
3864 int32 sds_id = SDselect(sdfd,i);
3865 if (sds_id == FAIL) {
3866 close_vgroup_fileids(fileid,sdfd,-1);
3867 throw InternalErr (__FILE__,__LINE__,"SDselect failed ");
3868 }
3869
3870 if (SDgetnamelen(sds_id, &name_len) == FAIL) {
3871 SDendaccess(sds_id);
3872 close_vgroup_fileids(fileid,sdfd,-1);
3873 throw InternalErr(__FILE__, __LINE__, "Fail to obtain the SDS name length.");
3874 }
3875
3876 sds_name.resize(name_len+1);
3877
3878 int32 sds_rank = 0;
3879 int32 dim_sizes[H4_MAX_VAR_DIMS];
3880 int32 num_attrs = 0;
3881
3882 // Obtain object name, rank, size, field type and number of SDS attributes
3883 if (FAIL == SDgetinfo (sds_id, sds_name.data(), &sds_rank, dim_sizes, nullptr, &num_attrs)) {
3884 SDendaccess(sds_id);
3885 close_vgroup_fileids(fileid,sdfd,-1);
3886 throw InternalErr(__FILE__, __LINE__, "Fail to obtain the SDS ID.");
3887 }
3888
3889 // This simple CF grid only contains 1-D lat/lon.
3890 if (sds_rank == 1) {
3891
3892 char attr_name[H4_MAX_NC_NAME];
3893
3894 for (int attrindex = 0; attrindex < num_attrs; attrindex++) {
3895
3896 int32 sds_attr_type = 0;
3897 int32 attr_value_count = 0;
3898 if (FAIL==SDattrinfo (sds_id, attrindex, attr_name, &sds_attr_type, &attr_value_count)) {
3899 SDendaccess(sds_id);
3900 close_vgroup_fileids(fileid,sdfd,-1);
3901 throw InternalErr(__FILE__,__LINE__, "SDattrinfo failed ");
3902 }
3903
3904 string attrname_str(attr_name);
3905 if(attrname_str == "units" && sds_attr_type == DFNT_CHAR) {
3906 vector<char> attr_value;
3907 attr_value.resize(attr_value_count * DFKNTsize(sds_attr_type));
3908 if (SDreadattr (sds_id, attrindex, attr_value.data()) == -1) {
3909 SDendaccess(sds_id);
3910 close_vgroup_fileids(fileid,sdfd,-1);
3911 throw InternalErr(__FILE__,__LINE__, "SDreadattr failed ");
3912 }
3913 string tempstring(attr_value.begin(),attr_value.end());
3914 auto tempfinalstr= string(tempstring.c_str());
3915 if (tempfinalstr == "degrees_north") {
3916 find_lat = true;
3917 string sds_name_str(sds_name.begin(),sds_name.end()-1);
3918 lat_name = HDFCFUtil::get_CF_string(sds_name_str);
3919 lat_size = dim_sizes[0];
3920 }
3921 else if (tempfinalstr == "degrees_east") {
3922 find_lon = true;
3923 string sds_name_str(sds_name.begin(),sds_name.end()-1);
3924 lon_name = HDFCFUtil::get_CF_string(sds_name_str);
3925 lon_size = dim_sizes[0];
3926 }
3927 }
3928 if (find_lat && find_lon)
3929 break;
3930 }
3931 }
3932 if (find_lat && find_lon) {
3933 SDendaccess(sds_id);
3934 break;
3935 }
3936 else
3937 SDendaccess(sds_id);
3938 }
3939
3940}
3941
3942void read_dds_simple_cf(DDS &dds,const string & filename, int32 sdfd, int32 fileid, short cf_simple_type) {
3943
3944 dds.set_dataset_name(basename(filename));
3945
3946 int32 n_sds = 0;
3947 int32 n_sd_attrs = 0;
3948
3949 // Obtain number of SDS objects and number of SD(file) attributes
3950 if (SDfileinfo (sdfd, &n_sds, &n_sd_attrs) == FAIL){
3951 close_vgroup_fileids(fileid,sdfd,-1);
3952 throw InternalErr (__FILE__,__LINE__,"SDfileinfo failed ");
3953 }
3954
3955 for (int i = 0; i <n_sds; i++) {
3956
3957 uint16 name_len = 0;
3958 vector<char> sds_name;
3959
3960 int32 sds_id = SDselect(sdfd,i);
3961 if (sds_id == FAIL) {
3962 close_vgroup_fileids(fileid,sdfd,-1);
3963 throw InternalErr (__FILE__,__LINE__,"SDselect failed ");
3964 }
3965
3966 int32 obj_ref = SDidtoref(sds_id);
3967 if (obj_ref == FAIL) {
3968 SDendaccess(sds_id);
3969 close_vgroup_fileids(fileid,sdfd,-1);
3970 throw InternalErr (__FILE__,__LINE__,"SDidtoref failed ");
3971 }
3972
3973 if (SDgetnamelen(sds_id, &name_len) == FAIL) {
3974 SDendaccess(sds_id);
3975 close_vgroup_fileids(fileid,sdfd,-1);
3976 throw InternalErr(__FILE__, __LINE__, "Fail to obtain the SDS name length.");
3977 }
3978
3979 sds_name.resize(name_len+1);
3980
3981 int32 sds_rank = 0;
3982 int32 sds_type = 0;
3983
3984 // Obtain object name, rank, size, field type and number of SDS attributes
3985 if (FAIL == SDgetinfo (sds_id, sds_name.data(), &sds_rank, nullptr, &sds_type,nullptr)) {
3986 SDendaccess(sds_id);
3987 close_vgroup_fileids(fileid,sdfd,-1);
3988 throw InternalErr(__FILE__, __LINE__, "Fail to obtain the SDS ID.");
3989 }
3990
3991 string sds_name_str(sds_name.begin(),sds_name.end()-1);
3992
3993 // Handle special characters in the sds_name.
3994 sds_name_str = HDFCFUtil::get_CF_string(sds_name_str);
3995
3996 vector<string> dimnames;
3997 vector<int32> dimsizes;
3998 for (int dimindex = 0; dimindex <sds_rank; dimindex++) {
3999
4000 int dimid = SDgetdimid (sds_id, dimindex);
4001 if (dimid == FAIL) {
4002 SDendaccess (sds_id);
4003 close_vgroup_fileids(fileid,sdfd,-1);
4004 throw InternalErr(__FILE__, __LINE__, "SDgetdimid failed.");
4005 }
4006 char dim_name[H4_MAX_NC_NAME];
4007 int32 dim_size = 0;
4008 int32 dim_type = 0;
4009
4010 // Number of dimension attributes(This is almost never used)
4011 int32 num_dim_attrs = 0;
4012
4013 intn status = SDdiminfo (dimid, dim_name, &dim_size, &dim_type,
4014 &num_dim_attrs);
4015
4016 if (status == FAIL) {
4017 SDendaccess (sds_id);
4018 close_vgroup_fileids(fileid,sdfd,-1);
4019 throw InternalErr(__FILE__, __LINE__, "SDdiminfo failed.");
4020 }
4021
4022 if (dim_size == 0) {
4023 // This is the unlimited dimension case. Currently we don't support this.
4024 SDendaccess (sds_id);
4025 close_vgroup_fileids(fileid,sdfd,-1);
4026 throw InternalErr(__FILE__, __LINE__, "Currently not support the unlimited dimension for simple CF handling.");
4027 }
4028 string dim_name_str (dim_name);
4029
4030 // Make dimension names to follow the CF rule.
4031 dim_name_str = HDFCFUtil::get_CF_string(dim_name_str);
4032 dimnames.push_back(dim_name_str);
4033 dimsizes.push_back(dim_size);
4034 }
4035
4036 BaseType *bt = gen_dap_var(sds_type,sds_name_str, filename);
4037 auto ar_unique = make_unique<HDFSPArray_RealField>(
4038 sds_rank,
4039 filename,
4040 sdfd,
4041 obj_ref,
4042 sds_type,
4043 OTHERHDF,
4044 sds_name_str,
4045 dimsizes,
4046 sds_name_str,
4047 bt);
4048
4049 auto ar = ar_unique.get();
4050 for (int j = 0; j <sds_rank; j++)
4051 ar->append_dim(dimsizes[j],dimnames[j]);
4052 dds.add_var_nocopy(ar_unique.release());
4053 delete bt;
4054 SDendaccess(sds_id);
4055 }
4056
4057 // Make the dimension names to follow the COARDS
4058 if (cf_simple_type == 1) {
4059
4060 // Since DAP2 DDS is separated from DAS, which contains the attributes. So we need to use the HDF4 API to obtain
4061 // the CF attributes degrees_east and degrees_north, then to identify the CF coordinates.
4062 string lat_name;
4063 string lon_name;
4064 string lat_dim_name;
4065 string lon_dim_name;
4066 int lat_size = 0;
4067 int lon_size = 0;
4068
4069 obtain_cf_simple_lat_lon(sdfd,fileid,n_sds,lat_name,lon_name,lat_size,lon_size);
4070
4071 if (!lat_name.empty() && !lon_name.empty()) {
4072
4073 bool find_lat_dim_name = false;
4074 bool find_lon_dim_name = false;
4075
4076 for (const auto &var: dds.variables()) {
4077
4078 if (var->type() == dods_array_c) {
4079 auto ar = dynamic_cast<Array*>(var);
4080 if (ar->dimensions() == 1) {
4081 string cf_var_name = HDFCFUtil::get_CF_string(ar->name());
4082 if (cf_var_name == lat_name) {
4083 lat_dim_name = ar->dimension_name(ar->dim_begin());
4084 ar->rename_dim(lat_dim_name, lat_name);
4085 find_lat_dim_name = true;
4086 }
4087 else if(cf_var_name == lon_name) {
4088 lon_dim_name = ar->dimension_name(ar->dim_begin());
4089 ar->rename_dim(lon_dim_name, lon_name);
4090 find_lon_dim_name = true;
4091 }
4092 }
4093 }
4094 if (find_lat_dim_name && find_lon_dim_name)
4095 break;
4096 }
4097
4098 if (!find_lat_dim_name || !find_lon_dim_name) {
4099 close_vgroup_fileids(fileid,sdfd,-1);
4100 throw InternalErr(__FILE__, __LINE__, "Cannot find lat or lon dimension names for simple CF handling.");
4101 }
4102
4103 // Since a dimension name of a data variable may just contains the dimension name of lat/lon,
4104 // we will replace such dimension name with the lon/lat's name.
4105 for (const auto &var: dds.variables()) {
4106
4107 if (var->type() == dods_array_c) {
4108
4109 auto ar = dynamic_cast<Array*>(var);
4110
4111 for (Array::Dim_iter p = ar->dim_begin(); p != ar->dim_end(); ++p) {
4112 if (ar->dimension_size(p) == lat_size && ar->dimension_name(p).find(lat_dim_name)==0)
4113 ar->rename_dim(ar->dimension_name(p),lat_name);
4114 else if (ar->dimension_size(p) == lon_size && ar->dimension_name(p).find(lon_dim_name)==0) {
4115 ar->rename_dim(ar->dimension_name(p),lon_name);
4116 }
4117 }
4118 }
4119 }
4120 }
4121 }
4122}
4123// Default option
4124void read_dds(DDS & dds, const string & filename)
4125{
4126 // generate DDS, DAS
4127 DAS das;
4128 dds.set_dataset_name(basename(filename));
4129 build_descriptions(dds, das, filename);
4130
4131 if (!dds.check_semantics()) { // DDS didn't get built right
4132 THROW(dhdferr_ddssem);
4133 }
4134 return;
4135}
4136
4137void read_das(DAS & das, const string & filename)
4138{
4139 // generate DDS, DAS
4140 DDS dds(nullptr);
4141 dds.set_dataset_name(basename(filename));
4142
4143 build_descriptions(dds, das, filename);
4144
4145 if (!dds.check_semantics()) { // DDS didn't get built right
4146 dds.print(cout);
4147 THROW(dhdferr_ddssem);
4148 }
4149 return;
4150}
4151
4152// Scan the HDF file and build the DDS and DAS
4153static void build_descriptions(DDS & dds, DAS & das,
4154 const string & filename)
4155{
4156 sds_map sdsmap;
4157 vd_map vdatamap;
4158 gr_map grmap;
4159
4160 // Build descriptions of SDS items
4161 // If CF option is enabled, StructMetadata will be parsed here.
4162 SDS_descriptions(sdsmap, das, filename);
4163
4164 // Build descriptions of file annotations
4165 FileAnnot_descriptions(das, filename);
4166
4167 // Build descriptions of Vdatas
4168 Vdata_descriptions(vdatamap, das, filename);
4169
4170 // Build descriptions of General Rasters
4171 GR_descriptions(grmap, das, filename);
4172
4173 // Build descriptions of Vgroups and add SDS/Vdata/GR in the correct order
4174 Vgroup_descriptions(dds, das, filename, sdsmap, vdatamap, grmap);
4175 return;
4176}
4177
4178// These two Functor classes are used to look for EOS attributes with certain
4179// base names (is_named) and to accumulate values in in different hdf_attr
4180// objects with the same base names (accum_attr). These are used by
4181// merge_split_eos_attributes() to do just that. Some HDF EOS attributes are
4182// longer than HDF 4's 32,000 character limit. Those attributes are split up
4183// in the HDF 4 files and named `StructMetadata.0', `StructMetadata.1', et
4184// cetera. This code merges those attributes so that they can be processed
4185// correctly by the hdf eos attribute parser (see AddHDFAttr() further down
4186// in this file). 10/29/2001 jhrg
4187
4188struct accum_attr
4189 :public binary_function < hdf_genvec &, hdf_attr, hdf_genvec & > {
4190
4191 string d_named;
4192
4193 explicit accum_attr(const string & named):d_named(named) {
4194 }
4195
4196 hdf_genvec & operator() (hdf_genvec & accum, const hdf_attr & attr) {
4197 // Assume that all fields with the same base name should be combined,
4198 // and assume that they are in order.
4199 BESDEBUG("h4", "attr.name: " << attr.name << endl);
4200 if (attr.name.find(d_named) != string::npos) {
4201#if 0
4202 string stuff;
4203 stuff.assign(attr.values.data(), attr.values.size());
4204 cerr << "Attribute chunk: " << attr.name << endl;
4205 cerr << stuff << endl;
4206#endif
4207 accum.append(attr.values.number_type(), attr.values.data(),
4208 attr.values.size());
4209 return accum;
4210 }
4211 else {
4212 return accum;
4213 }
4214 }
4215};
4216
4217struct is_named:public unary_function < hdf_attr, bool > {
4218 string d_named;
4219
4220 explicit is_named(const string & named):d_named(named) {
4221 }
4222
4223 bool operator() (const hdf_attr & attr) {
4224 return (attr.name.find(d_named) != string::npos);
4225 }
4226};
4227
4228static void
4229merge_split_eos_attributes(vector < hdf_attr > &attr_vec,
4230 const string & attr_name)
4231{
4232 // Only do this if there's more than one part.
4233 if (count_if(attr_vec.begin(), attr_vec.end(), is_named(attr_name)) > 1) {
4234 // Merge all split up parts named `attr_name.' Assume they are in
4235 // order in `attr_vec.'
4236 hdf_genvec attributes;
4237 attributes = accumulate(attr_vec.begin(), attr_vec.end(),
4238 attributes, accum_attr(attr_name));
4239
4240 // When things go south, check out the hdf_genvec...
4241 // BEDEBUG seems not providing a way to handle the following debugging info.
4242 // I can define a vector and call attributes.print(s_m), then use
4243 // BESDEBUG to output the debugging info. The downside is that whether BESDEBUG
4244 // is called, a vector of s_m will always be generated and a chunk of memory is
4245 // always used. So don't change this for the time being. KY 2012-09-13
4246 DBG(vector < string > s_m;
4247 attributes.print(s_m);
4248 cerr << "Accum struct MD: (" << s_m.size() << ") "
4249 << s_m[0] << endl);
4250
4251 // Remove all the parts that have been merged
4252 attr_vec.erase(remove_if(attr_vec.begin(), attr_vec.end(),
4253 is_named(attr_name)), attr_vec.end());
4254
4255 // Make a new hdf_attr and assign it the newly merged attributes...
4256 hdf_attr merged_attr;
4257 merged_attr.name = attr_name;
4258 merged_attr.values = attributes;
4259
4260 // And add it to the vector of attributes.
4261 attr_vec.push_back(merged_attr);
4262 }
4263}
4264
4265// Read SDS's out of filename, build descriptions and put them into dds, das.
4266static void SDS_descriptions(sds_map & map, DAS & das,
4267 const string & filename)
4268{
4269
4270 hdfistream_sds sdsin(filename);
4271 sdsin.setmeta(true);
4272
4273 // Read SDS file attributes attr_iter i = ;
4274
4275 vector < hdf_attr > fileattrs;
4276 sdsin >> fileattrs;
4277
4278 // Read SDS's
4279 sdsin.rewind();
4280 while (!sdsin.eos()) {
4281 sds_info sdi; // add the next sds_info to map
4282 sdsin >> sdi.sds;
4283 sdi.in_vgroup = false; // assume we're not part of a vgroup
4284 map[sdi.sds.ref] = sdi; // assign to map by ref
4285 }
4286
4287 sdsin.close();
4288
4289 // This is the call to combine SDS attributes that have been split up
4290 // into N 32,000 character strings. 10/24/2001 jhrg
4291 merge_split_eos_attributes(fileattrs, "StructMetadata");
4292 merge_split_eos_attributes(fileattrs, "CoreMetadata");
4293 merge_split_eos_attributes(fileattrs, "ProductMetadata");
4294 merge_split_eos_attributes(fileattrs, "ArchiveMetadata");
4295 merge_split_eos_attributes(fileattrs, "coremetadata");
4296 merge_split_eos_attributes(fileattrs, "productmetadata");
4297
4298 // Build DAS, add SDS file attributes
4299 AddHDFAttr(das, string("HDF_GLOBAL"), fileattrs);
4300 // add each SDS's attrs
4301 vector < hdf_attr > dattrs;
4302
4303 // TODO Remove these attributes (name and dimension)? jhrg 8/17/11
4304 // ***
4305 for (SDSI s = map.begin(); s != map.end(); ++s) {
4306 const hdf_sds *sds = &s->second.sds;
4307 AddHDFAttr(das, sds->name, sds->attrs);
4308 for (int k = 0; k < (int) sds->dims.size(); ++k) {
4309 dattrs = Dims2Attrs(sds->dims[k]);
4310 AddHDFAttr(das, sds->name + "_dim_" + num2string(k), dattrs);
4311 }
4312
4313 }
4314
4315 return;
4316}
4317
4318// Read Vdata's out of filename, build descriptions and put them into dds.
4319static void Vdata_descriptions(vd_map & map, DAS & das,
4320 const string & filename)
4321{
4322 hdfistream_vdata vdin(filename);
4323 vdin.setmeta(true);
4324
4325 // Read Vdata's
4326 while (!vdin.eos()) {
4327 vd_info vdi; // add the next vd_info to map
4328 vdin >> vdi.vdata;
4329 vdi.in_vgroup = false; // assume we're not part of a vgroup
4330 map[vdi.vdata.ref] = vdi; // assign to map by ref
4331 }
4332 vdin.close();
4333
4334 // Build DAS
4335 vector < hdf_attr > dattrs;
4336 for (VDI s = map.begin(); s != map.end(); ++s) {
4337 const hdf_vdata *vd = &s->second.vdata;
4338 AddHDFAttr(das, vd->name, vd->attrs);
4339 }
4340
4341 return;
4342}
4343
4344// Read Vgroup's out of filename, build descriptions and put them into dds.
4345static void Vgroup_descriptions(DDS & dds, DAS & das,
4346 const string & filename, sds_map & sdmap,
4347 vd_map & vdmap, gr_map & grmap)
4348{
4349
4350 hdfistream_vgroup vgin(filename);
4351
4352 // Read Vgroup's
4353 vg_map vgmap;
4354 while (!vgin.eos()) {
4355 vg_info vgi; // add the next vg_info to map
4356 vgin >> vgi.vgroup; // read vgroup itself
4357 vgi.toplevel = true; // assume toplevel until we prove otherwise
4358 vgmap[vgi.vgroup.ref] = vgi; // assign to map by vgroup ref
4359 }
4360 vgin.close();
4361 // for each Vgroup
4362 for (VGI v = vgmap.begin(); v != vgmap.end(); ++v) {
4363 const hdf_vgroup *vg = &v->second.vgroup;
4364
4365 // Add Vgroup attributes
4366 AddHDFAttr(das, vg->name, vg->attrs);
4367
4368 // now, assign children
4369 for (uint32 i = 0; i < vg->tags.size(); i++) {
4370 int32 tag = vg->tags[i];
4371 int32 ref = vg->refs[i];
4372 switch (tag) {
4373 case DFTAG_VG:
4374 // Could be a GRI or a Vgroup
4375 if (grmap.find(ref) != grmap.end())
4376 grmap[ref].in_vgroup = true;
4377 else
4378 vgmap[ref].toplevel = false;
4379 break;
4380 case DFTAG_VH:
4381 vdmap[ref].in_vgroup = true;
4382 break;
4383 case DFTAG_NDG:
4384 sdmap[ref].in_vgroup = true;
4385 break;
4386 default:
4387 ERROR_LOG("unknown tag: " + std::to_string(tag) + " ref: " + std::to_string(ref));
4388 // TODO: Make this an exception? jhrg 8/19/11
4389 // Don't make an exception. Possibly you will meet other valid tags. Need to know if it
4390 // is worth to tackle this. KY 09/13/12
4391 // cerr << "unknown tag: " << tag << " ref: " << ref << endl;
4392 break;
4393 }// switch (tag)
4394 } // for (uint32 i = 0; i < vg->tags.size(); i++)
4395 } // for (VGI v = vgmap.begin(); v != vgmap.end(); ++v)
4396 // Build DDS for all toplevel vgroups
4397 BaseType *pbt = nullptr;
4398 for (VGI v = vgmap.begin(); v != vgmap.end(); ++v) {
4399 if (!v->second.toplevel)
4400 continue; // skip over non-toplevel vgroups
4401 pbt = NewStructureFromVgroup(v->second.vgroup,
4402 vgmap, sdmap, vdmap,
4403 grmap, filename);
4404 if (pbt != nullptr) {
4405 dds.add_var(pbt);
4406 delete pbt;
4407 }
4408
4409 }
4410
4411 // add lone SDS's
4412 for (SDSI s = sdmap.begin(); s != sdmap.end(); ++s) {
4413 if (s->second.in_vgroup)
4414 continue; // skip over SDS's in vgroups
4415 if (s->second.sds.has_scale()) // make a grid
4416 pbt = NewGridFromSDS(s->second.sds, filename);
4417 else
4418 pbt = NewArrayFromSDS(s->second.sds, filename);
4419 if (pbt != nullptr) {
4420 dds.add_var(pbt);
4421 delete pbt;
4422 }
4423 }
4424
4425 // add lone Vdata's
4426 for (VDI v = vdmap.begin(); v != vdmap.end(); ++v) {
4427 if (v->second.in_vgroup)
4428 continue; // skip over Vdata in vgroups
4429 pbt = NewSequenceFromVdata(v->second.vdata, filename);
4430 if (pbt != nullptr) {
4431 dds.add_var(pbt);
4432 delete pbt;
4433 }
4434 }
4435 // add lone GR's
4436 for (GRI g = grmap.begin(); g != grmap.end(); ++g) {
4437 if (g->second.in_vgroup)
4438 continue; // skip over GRs in vgroups
4439 pbt = NewArrayFromGR(g->second.gri, filename);
4440 if (pbt != nullptr) {
4441 dds.add_var(pbt);
4442 delete pbt ;
4443 }
4444 }
4445}
4446
4447static void GR_descriptions(gr_map & map, DAS & das,
4448 const string & filename)
4449{
4450
4451 hdfistream_gri grin(filename);
4452 grin.setmeta(true);
4453
4454 // Read GR file attributes
4455 vector < hdf_attr > fileattrs;
4456 grin >> fileattrs;
4457
4458 // Read general rasters
4459 grin.rewind();
4460 while (!grin.eos()) {
4461 gr_info gri; // add the next gr_info to map
4462 grin >> gri.gri;
4463 gri.in_vgroup = false; // assume we're not part of a vgroup
4464 map[gri.gri.ref] = gri; // assign to map by ref
4465 }
4466
4467 grin.close();
4468
4469 // Build DAS
4470 AddHDFAttr(das, string("HDF_GLOBAL"), fileattrs); // add GR file attributes
4471
4472 // add each GR's attrs
4473 vector < hdf_attr > pattrs;
4474 for (GRI g = map.begin(); g != map.end(); ++g) {
4475 const hdf_gri *gri = &g->second.gri;
4476 // add GR attributes
4477 AddHDFAttr(das, gri->name, gri->attrs);
4478
4479 // add palettes as attributes
4480 pattrs = Pals2Attrs(gri->palettes);
4481 AddHDFAttr(das, gri->name, pattrs);
4482
4483 }
4484
4485 return;
4486}
4487
4488// Read file annotations out of filename, put in attribute structure
4489static void FileAnnot_descriptions(DAS & das, const string & filename)
4490{
4491
4492 hdfistream_annot annotin(filename);
4493 vector < string > fileannots;
4494
4495 annotin >> fileannots;
4496 AddHDFAttr(das, string("HDF_GLOBAL"), fileannots);
4497
4498 annotin.close();
4499 return;
4500}
4501
4502// add a vector of hdf_attr to a DAS
4503void AddHDFAttr(DAS & das, const string & varname,
4504 const vector < hdf_attr > &hav)
4505{
4506 if (hav.size() == 0) // nothing to add
4507 return;
4508 // get pointer to the AttrTable for the variable varname (create one if
4509 // necessary)
4510 string tempname = varname;
4511 AttrTable *atp = das.get_table(tempname);
4512 if (atp == nullptr) {
4513 atp = new AttrTable;
4514 atp = das.add_table(tempname, atp);
4515 }
4516 // add the attributes to the DAS
4517 vector < string > attv; // vector of attribute strings
4518 string attrtype; // name of type of attribute
4519 for (int i = 0; i < (int) hav.size(); ++i) { // for each attribute
4520
4521 attrtype = DAPTypeName(hav[i].values.number_type());
4522 // get a vector of strings representing the values of the attribute
4523 attv = vector < string > (); // clear attv
4524 hav[i].values.print(attv);
4525
4526 // add the attribute and its values to the DAS
4527 for (int j = 0; j < (int) attv.size(); ++j) {
4528 // handle HDF-EOS metadata with separate parser
4529 string container_name = hav[i].name;
4530 if (container_name.find("StructMetadata") == 0
4531 || container_name.find("CoreMetadata") == 0
4532 || container_name.find("ProductMetadata") == 0
4533 || container_name.find("ArchiveMetadata") == 0
4534 || container_name.find("coremetadata") == 0
4535 || container_name.find("productmetadata") == 0) {
4536 string::size_type dotzero = container_name.find('.');
4537 if (dotzero != container_name.npos)
4538 container_name.erase(dotzero); // erase .0
4539
4540
4541 AttrTable *at = das.get_table(container_name);
4542 if (!at)
4543 at = das.add_table(container_name, new AttrTable);
4544
4545 // tell lexer to scan attribute string
4546 void *buf = hdfeos_string(attv[j].c_str());
4547
4548 // cerr << "About to print attributes to be parsed..." << endl;
4549 // TODO: remove when done!
4550 // cerr << "attv[" << j << "]" << endl << attv[j].c_str() << endl;
4551
4552 parser_arg arg(at);
4553 // HDF-EOS attribute parsing is complex and some errors are
4554 // tolerated. Thus, if the parser proper returns an error,
4555 // that results in an exception that is fatal. However, if
4556 // the status returned by an otherwise successful parse shows
4557 // an error was encountered but successful parsing continued,
4558 // that's OK, but it should be logged.
4559 //
4560 // Also, HDF-EOS files should be read using the new HDF-EOS
4561 // features and not this older parser. jhrg 8/18/11
4562 //
4563 // TODO: How to log (as opposed to using BESDEBUG)?
4564 if (hdfeosparse(&arg) != 0){
4565 hdfeos_delete_buffer(buf);
4566 throw Error("HDF-EOS parse error while processing a " + container_name + " HDFEOS attribute.");
4567 }
4568
4569 // We don't use the parse_error for this case since it generates memory leaking. KY 2014-02-25
4570 if (arg.status() == false) {
4571 ERROR_LOG("HDF-EOS parse error while processing a " + container_name + " HDFEOS attribute. (2)");
4572 //<< arg.error()->get_error_message() << endl;
4573 }
4574
4575 hdfeos_delete_buffer(buf);
4576 }
4577 else {
4578 // Special characters are handled in libdap4. So the following code block is commented out. KY 2022-11-16
4579#if 0
4580 if (attrtype == "String")
4581#ifdef ATTR_STRING_QUOTE_FIX
4582 attv[j] = escattr(attv[j]);
4583
4584#else
4585 attv[j] = "\"" + escattr(attv[j]) + "\"";
4586#endif
4587#endif
4588
4589 if (atp->append_attr(hav[i].name, attrtype, attv[j]) == 0)
4590 THROW(dhdferr_addattr);
4591 }
4592 }
4593 }
4594
4595 return;
4596}
4597
4598// add a vector of annotations to a DAS. They are stored as attributes. They
4599// are encoded as string values of an attribute named "HDF_ANNOT".
4600void AddHDFAttr(DAS & das, const string & varname,
4601 const vector < string > &anv)
4602{
4603 if (anv.size() == 0) // nothing to add
4604 return;
4605
4606 // get pointer to the AttrTable for the variable varname (create one if
4607 // necessary)
4608 AttrTable *atp = das.get_table(varname);
4609 if (atp == nullptr) {
4610 atp = new AttrTable;
4611 atp = das.add_table(varname, atp);
4612 }
4613 // add the annotations to the DAS
4614 string an;
4615 for (int i = 0; i < (int) anv.size(); ++i) { // for each annotation
4616 // Special characters are handled in libdap4. So the following code block is commented out. KY 2022-11-16
4617 an = anv[i];
4618#if 0
4619#ifdef ATTR_STRING_QUOTE_FIX
4620 an = escattr(anv[i]); // quote strings
4621#else
4622 an = "\"" + escattr(anv[i]) + "\""; // quote strings
4623#endif
4624#endif
4625 if (atp->append_attr(string("HDF_ANNOT"), "String", an) == 0)
4626 THROW(dhdferr_addattr);
4627 }
4628
4629 return;
4630}
4631
4632// Add a vector of palettes as attributes to a GR. Each palette is added as
4633// two attributes: the first contains the palette data; the second contains
4634// the number of components in the palette.
4635static vector < hdf_attr > Pals2Attrs(const vector < hdf_palette > palv)
4636{
4637 vector < hdf_attr > pattrs;
4638
4639 if (palv.size() != 0) {
4640 // for each palette create an attribute with the palette inside, and an
4641 // attribute containing the number of components
4642 hdf_attr pattr;
4643 string palname;
4644 for (int i = 0; i < (int) palv.size(); ++i) {
4645 palname = "hdf_palette_" + num2string(i);
4646 pattr.name = palname;
4647 pattr.values = palv[i].table;
4648 pattrs.push_back(pattr);
4649 pattr.name = palname + "_ncomps";
4650 pattr.values = hdf_genvec(DFNT_INT32,
4651 const_cast <
4652 int32 * >(&palv[i].ncomp), 1);
4653 pattrs.push_back(pattr);
4654 if (palv[i].name.size() != 0) {
4655 pattr.name = palname + "_name";
4656 pattr.values = hdf_genvec(DFNT_CHAR,
4657 const_cast <
4658 char *>(palv[i].name.c_str()),
4659 palv[i].name.size());
4660 pattrs.push_back(pattr);
4661 }
4662 }
4663 }
4664 return pattrs;
4665}
4666
4667// Convert the meta information in a hdf_dim into a vector of
4668// hdf_attr.
4669static vector < hdf_attr > Dims2Attrs(const hdf_dim dim)
4670{
4671 vector < hdf_attr > dattrs;
4672 hdf_attr dattr;
4673 if (dim.name.size() != 0) {
4674 dattr.name = "name";
4675 dattr.values =
4676 hdf_genvec(DFNT_CHAR, const_cast < char *>(dim.name.c_str()),
4677 dim.name.size());
4678 dattrs.push_back(dattr);
4679 }
4680 if (dim.label.size() != 0) {
4681 dattr.name = "long_name";
4682 dattr.values =
4683 hdf_genvec(DFNT_CHAR, const_cast < char *>(dim.label.c_str()),
4684 dim.label.size());
4685 dattrs.push_back(dattr);
4686 }
4687 if (dim.unit.size() != 0) {
4688 dattr.name = "units";
4689 dattr.values =
4690 hdf_genvec(DFNT_CHAR, const_cast < char *>(dim.unit.c_str()),
4691 dim.unit.size());
4692 dattrs.push_back(dattr);
4693 }
4694 if (dim.format.size() != 0) {
4695 dattr.name = "format";
4696 dattr.values =
4697 hdf_genvec(DFNT_CHAR, const_cast < char *>(dim.format.c_str()),
4698 dim.format.size());
4699 dattrs.push_back(dattr);
4700 }
4701 return dattrs;
4702}
4703
4704// The following functions handle the direct mapping from HDF4 to DMR.
4705void read_dmr(DMR *dmr, const string &filename) {
4706
4707 BESDEBUG("h4"," Begin read_dmr()"<<endl);
4708 // H open
4709 int32 fileid = Hopen(filename.c_str(), DFACC_READ,0);
4710 if (-1 == fileid) {
4711 string invalid_file_msg="HDF4 SDstart error for the file ";
4712 invalid_file_msg +=filename;
4713 invalid_file_msg +=". It is very possible that this file is not an HDF4 file. ";
4714 throw InternalErr(__FILE__,__LINE__,invalid_file_msg);
4715 }
4716
4717 int32 sdfd = SDstart(filename.c_str(), DFACC_READ);
4718 if( -1 == sdfd){
4719 Hclose(fileid);
4720 string invalid_file_msg="HDF4 SDstart error for the file ";
4721 invalid_file_msg +=filename;
4722 invalid_file_msg +=". It is very possible that this file is not an HDF4 file. ";
4723 throw InternalErr(__FILE__,__LINE__,invalid_file_msg);
4724 }
4725
4726 D4Group* root_grp = dmr->root();
4727
4728 // It is possible that there are SDS and Vdata objects that don't belong to any vgroups.
4729 // We must handle lone SDS objects first because DAP4 requires a dimension with a name
4730 // to be in the front of a dmr.
4731 // We also need to map SD file attributes to DAP4 root group attributes.
4732 // TODO: possible we need to handle HDF-EOS2 swath dimensions. It needs special care.
4733
4734 // SDS dimensions and lone SDS objects
4735 handle_sds_dims(root_grp,fileid, sdfd);
4736 read_lone_sds(root_grp,fileid,sdfd,filename);
4737
4738 // Lone vdata: We find one NASA file has lone vdata, so we need to handle this case.
4739 read_lone_vdata(root_grp,fileid,sdfd,filename);
4740
4741 // SD file attributes under the root group.
4742 read_sd_attrs(root_grp,fileid, sdfd);
4743
4744 // Check if this file is a special HDF4 file that needs to add more information
4745 // Now the only case is TRMM Level 3B42. We need to add lat/lon fields.
4746 string err_msg;
4747 bool is_sp_hdf4_file = add_sp_hdf4_info(root_grp, filename, err_msg);
4748 if (is_sp_hdf4_file == false) {
4749 close_vgroup_fileids(fileid,sdfd,-1);
4750 throw InternalErr(__FILE__, __LINE__, err_msg);
4751 }
4752
4753 // Now go to handle HDF4 vgroups and the objects attached to these vgroups.
4754 read_dmr_vlone_groups(root_grp, fileid, sdfd, filename);
4755
4756 if (is_sp_hdf4_file)
4757 add_sp_hdf4_additional_info(root_grp);
4758 Hclose(fileid);
4759 SDend(sdfd);
4760
4761 BESDEBUG("h4"," End read_dmr()"<<endl);
4762}
4763
4764void handle_sds_dims(D4Group *root_grp, int32 fileid, int32 sdfd) {
4765
4766 BESDEBUG("h4"," Begin handle_sds_dims() "<<endl);
4767 int32 n_sds = 0;
4768 int32 n_sd_attrs = 0;
4769
4770 // Obtain number of SDS objects and number of SD(file) attributes
4771 if (SDfileinfo (sdfd, &n_sds, &n_sd_attrs) == FAIL){
4772 close_vgroup_fileids(fileid,sdfd,-1);
4773 throw InternalErr (__FILE__,__LINE__,"SDfileinfo failed ");
4774 }
4775
4776 D4Dimensions *dims = root_grp->dims();
4777
4778 // Create an unordered_set for SDS dimension names.
4779 // Unlike SDS, an SDS dimension name is unique.
4780 unordered_set<string> sds_dimname_set;
4781 for (int i = 0; i <n_sds; i++) {
4782
4783 int32 sds_id = SDselect(sdfd,i);
4784
4785 if (sds_id == FAIL) {
4786 close_vgroup_fileids(fileid,sdfd,-1);
4787 throw InternalErr(__FILE__, __LINE__, "Fail to obtain the SDS ID.");
4788 }
4789
4790 // Obtain object name, rank, size, field type and number of attributes of an SDS
4791 int32 dim_sizes[H4_MAX_VAR_DIMS];
4792 int32 sds_rank = 0;
4793 int32 sds_type = 0;
4794 int32 n_sds_attrs = 0;
4795 if (FAIL == SDgetinfo (sds_id, nullptr, &sds_rank, dim_sizes, &sds_type, &n_sds_attrs)) {
4796 SDendaccess(sds_id);
4797 close_vgroup_fileids(fileid,sdfd,-1);
4798 throw InternalErr(__FILE__, __LINE__, "Fail to obtain SDS info.");
4799 }
4800
4801 for (int dimindex = 0; dimindex <sds_rank; dimindex++) {
4802
4803 int dimid = SDgetdimid (sds_id, dimindex);
4804 if (dimid == FAIL) {
4805 SDendaccess (sds_id);
4806 close_vgroup_fileids(fileid,sdfd,-1);
4807 throw InternalErr(__FILE__, __LINE__, "SDgetdimid failed.");
4808 }
4809 char dim_name[H4_MAX_NC_NAME];
4810 int32 dim_size = 0;
4811 int32 dim_type = 0;
4812
4813 // Number of dimension attributes(This is almost never used)
4814 int32 num_dim_attrs = 0;
4815
4816 intn status = SDdiminfo (dimid, dim_name, &dim_size, &dim_type,
4817 &num_dim_attrs);
4818
4819 if (status == FAIL) {
4820 SDendaccess (sds_id);
4821 close_vgroup_fileids(fileid,sdfd,-1);
4822 throw InternalErr(__FILE__, __LINE__, "SDdiminfo failed.");
4823 }
4824
4825 if (dim_size == 0 && dim_sizes[dimindex] == 0)
4826 continue;
4827 string dim_name_str (dim_name);
4828
4829 // Make dimension names to follow the CF rule.
4830 dim_name_str = HDFCFUtil::get_CF_string(dim_name_str);
4831
4832 auto it_set = sds_dimname_set.insert(dim_name_str);
4833 if (it_set.second) {
4834 auto d4_dim0_unique = make_unique<D4Dimension>(dim_name_str,dim_sizes[dimindex]);
4835 auto d4_dim0 = d4_dim0_unique.release();
4836 dims->add_dim_nocopy(d4_dim0);
4837 }
4838 }
4839 SDendaccess(sds_id);
4840 }
4841
4842 BESDEBUG("h4"," End handle_sds_dims() "<<endl);
4843}
4844
4845void read_lone_sds(D4Group *root_grp, int32 file_id,int32 sdfd, const string &filename) {
4846
4847 BESDEBUG("h4"," Begin read_lone_sds() "<<endl);
4848
4849 unordered_set<int32> lone_sds_refs;
4850
4851 obtain_all_sds_refs(file_id,sdfd,lone_sds_refs);
4852 exclude_all_sds_refs_in_vgroups(file_id,sdfd,lone_sds_refs);
4853
4854 // Convert unorder set to set. It is necessary to ensure the same order of objects on different platforms.
4855 set<int32> ordered_lone_sds_refs(lone_sds_refs.begin(),lone_sds_refs.end());
4856
4857 // Map lone SDS objects and put them under DAP4's root group.
4858 for (const auto &sds_ref:ordered_lone_sds_refs) {
4859 convert_sds(file_id, sdfd, -1,sds_ref, root_grp, root_grp, filename,false, false);
4860 }
4861
4862 BESDEBUG("h4"," End read_lone_sds() "<<endl);
4863}
4864
4865void obtain_all_sds_refs(int32 file_id, int32 sdfd, unordered_set<int32>& sds_ref) {
4866
4867 BESDEBUG("h4"," Begin obtain_all_sds_refs() "<<endl);
4868 int32 n_sds = 0;
4869 int32 n_sd_attrs = 0;
4870
4871 // Obtain number of SDS objects and number of SD(file) attributes
4872 if (SDfileinfo (sdfd, &n_sds, &n_sd_attrs) == FAIL){
4873 close_vgroup_fileids(file_id,sdfd,-1);
4874 throw InternalErr (__FILE__,__LINE__,"SDfileinfo failed ");
4875 }
4876
4877 for (int i = 0; i <n_sds; i++) {
4878
4879 int32 sds_id = SDselect(sdfd,i);
4880 if (sds_id == FAIL) {
4881 close_vgroup_fileids(file_id,sdfd,-1);
4882 throw InternalErr (__FILE__,__LINE__,"SDselect failed ");
4883 }
4884 int32 obj_ref = SDidtoref(sds_id);
4885 if (obj_ref == FAIL) {
4886 SDendaccess(sds_id);
4887 close_vgroup_fileids(file_id,sdfd,-1);
4888 throw InternalErr (__FILE__,__LINE__,"SDidtoref failed ");
4889 }
4890 sds_ref.insert(obj_ref);
4891 SDendaccess(sds_id);
4892 }
4893
4894 BESDEBUG("h4"," End obtain_all_sds_refs() "<<endl);
4895}
4896
4897void exclude_all_sds_refs_in_vgroups(int32 file_id, int32 sdfd, unordered_set<int32>& sds_ref) {
4898
4899 BESDEBUG("h4"," Begin exclude_all_sds_refs_in_vgroups() "<<endl);
4900 int num_lonevg = 0;
4901 vector<int> ref_array;
4902 int32 istat = 0;
4903
4904 istat = Vstart(file_id);
4905 if (istat == FAIL) {
4906 close_vgroup_fileids(file_id,sdfd,-1);
4907 throw InternalErr(__FILE__, __LINE__, "unable to start hdf4 V interface.");
4908 }
4909
4910 num_lonevg = Vlone(file_id,nullptr,0);
4911
4912 if (num_lonevg == FAIL) {
4913 close_vgroup_fileids(file_id,sdfd,-1);
4914 throw InternalErr(__FILE__, __LINE__, "error in obtaining lone vgroup number.");
4915 }
4916
4917 /* obtain object reference array. */
4918
4919 /* if no lone vgroup, quit from this function. */
4920 if (num_lonevg == 0) {
4921 Vend(file_id);
4922 return;
4923 }
4924
4925 ref_array.resize(num_lonevg);
4926
4927 num_lonevg = Vlone(file_id,ref_array.data(),num_lonevg);
4928
4929 /* walk through every lone group in the file */
4930
4931 for(int lone_vg_number = 0; lone_vg_number < num_lonevg;
4932 lone_vg_number++) {
4933
4934 int32 vgroup_id = Vattach(file_id,ref_array[lone_vg_number],"r");
4935 if (vgroup_id ==FAIL) {
4936 close_vgroup_fileids(file_id,sdfd,-1);
4937 throw InternalErr(__FILE__, __LINE__, "error in attaching lone vgroup.");
4938 }
4939
4940 uint16 vclassnamelen; /*Vgroup class name length */
4941 vector<char> vclass_name;
4942 if (Vgetclassnamelen(vgroup_id,&vclassnamelen) == FAIL) {
4943 close_vgroup_fileids(file_id,sdfd,vgroup_id);
4944 throw InternalErr(__FILE__, __LINE__, "error in obtaining vgroup class name length.");
4945 }
4946
4947 if (vclassnamelen!=0) {
4948 vclass_name.resize(vclassnamelen+1);
4949 if (Vgetclass(vgroup_id,vclass_name.data()) == FAIL) {
4950 close_vgroup_fileids(file_id,sdfd,vgroup_id);
4951 throw InternalErr(__FILE__, __LINE__, "error in obtaining vgroup class name.");
4952 }
4953 if (reserved_vgroups(vclass_name)) {
4954 Vdetach(vgroup_id);
4955 continue;
4956 }
4957 }
4958
4959 exclude_sds_refs_in_vgroup(file_id,sdfd,vgroup_id,sds_ref);
4960 Vdetach(vgroup_id);
4961 }
4962
4963 Vend(file_id);
4964
4965 BESDEBUG("h4","End exclude_all_sds_refs_in_vgroups() "<<endl);
4966}
4967
4968void exclude_sds_refs_in_vgroup(int32 file_id, int32 sdfd, int32 vgroup_id, unordered_set<int32> &sds_ref) {
4969
4970 int num_gobjects; /* number of global objects */
4971 int32 obj_tag; /* object tag */
4972 int32 obj_ref;/* object reference */
4973
4974 num_gobjects = Vntagrefs(vgroup_id);
4975 if (num_gobjects == FAIL) {
4976 close_vgroup_fileids(file_id,sdfd,vgroup_id);
4977 throw InternalErr(__FILE__, __LINE__, "error in obtaining vgroup class name.");
4978 }
4979
4980 for( int i = 0;i<num_gobjects;i++) {
4981
4982 if (Vgettagref(vgroup_id,i,&obj_tag,&obj_ref)==FAIL) {
4983 close_vgroup_fileids(file_id,sdfd,vgroup_id);
4984 throw InternalErr(__FILE__, __LINE__, "fail to obtain object tag and ref. of Vgroup members");
4985 }
4986
4987 if (obj_tag == DFTAG_NDG || obj_tag == DFTAG_SDG) {
4988
4989 if (0 == sds_ref.erase(obj_ref)) {
4990 close_vgroup_fileids(file_id,sdfd,vgroup_id);
4991 string err_msg = "Unable to remove the SDS object from the sds reference list for the reference number " + to_string(obj_ref);
4992 throw InternalErr(__FILE__, __LINE__, err_msg);
4993 }
4994
4995 }
4996 else if (Visvg(vgroup_id, obj_ref)) {
4997
4998 int32 vgroup_cid = Vattach(file_id,obj_ref,"r");
4999 if (vgroup_cid == FAIL) {
5000 close_vgroup_fileids(file_id,sdfd,vgroup_id);
5001 throw InternalErr(__FILE__, __LINE__, "fail to attach vgroup_cid.");
5002 }
5003
5004 uint16 vclassnamelen; /*Vgroup class name length */
5005 vector<char> vclass_name;
5006 if (Vgetclassnamelen(vgroup_cid,&vclassnamelen) == FAIL) {
5007 Vdetach(vgroup_cid);
5008 close_vgroup_fileids(file_id,sdfd,vgroup_id);
5009 throw InternalErr(__FILE__, __LINE__, "error in obtaining vgroup class name length.");
5010 }
5011
5012 if (vclassnamelen!=0) {
5013 vclass_name.resize(vclassnamelen+1);
5014 if (Vgetclass(vgroup_cid,vclass_name.data()) == FAIL) {
5015 Vdetach(vgroup_cid);
5016 close_vgroup_fileids(file_id,sdfd,vgroup_id);
5017 throw InternalErr(__FILE__, __LINE__, "error in obtaining vgroup class name.");
5018 }
5019 if (reserved_vgroups(vclass_name)) {
5020 Vdetach(vgroup_cid);
5021 continue;
5022 }
5023 }
5024
5025 exclude_sds_refs_in_vgroup(file_id,sdfd,vgroup_cid,sds_ref);
5026 Vdetach(vgroup_cid);
5027
5028 }
5029 }
5030}
5031
5032void read_sd_attrs(D4Group *root_grp, int32 fileid, int32 sdfd) {
5033
5034 int32 n_sds = 0;
5035 int32 n_sd_attrs = 0;
5036
5037 // Obtain number of SDS objects and number of SD(file) attributes
5038 if (SDfileinfo (sdfd, &n_sds, &n_sd_attrs) == FAIL){
5039 Hclose(fileid);
5040 SDend(sdfd);
5041 throw InternalErr (__FILE__,__LINE__,"SDfileinfo failed ");
5042 }
5043
5044 for (int attr_index = 0; attr_index < n_sd_attrs;attr_index++) {
5045
5046 char attr_name[H4_MAX_NC_NAME];
5047 int32 attr_type = -1;
5048 int32 attr_count = -1;
5049
5050 if (SDattrinfo(sdfd,attr_index,attr_name,&attr_type,&attr_count) == FAIL) {
5051 Hclose(fileid);
5052 SDend(sdfd);
5053 throw InternalErr (__FILE__,__LINE__,"SDattrinfo failed ");
5054 }
5055
5056 vector<char> attr_value;
5057 attr_value.resize(attr_count * DFKNTsize(attr_type));
5058 if (SDreadattr (sdfd, attr_index, attr_value.data()) == -1) {
5059 Hclose(fileid);
5060 SDend(sdfd);
5061 throw InternalErr(__FILE__,__LINE__, "SDreadattr failed ");
5062 }
5063
5064 string dap4_attrname (attr_name);
5065 dap4_attrname = HDFCFUtil::get_CF_string(dap4_attrname);
5066 map_vgroup_attr(root_grp, dap4_attrname,attr_type,attr_count, attr_value);
5067
5068 }
5069
5070}
5071
5072
5073void read_lone_vdata(D4Group *root_grp, int32 file_id, int32 sdfd, const string &filename) {
5074
5075 BESDEBUG("h4"," Begin read_lone_vdata()"<<endl);
5076 if (Vstart(file_id) == FAIL) {
5077 Hclose(file_id);
5078 SDend(sdfd);
5079 throw InternalErr(__FILE__, __LINE__, "Fail to start the V interface.");
5080 }
5081 // Obtain number of lone vdata.
5082 int num_lone_vdata = VSlone (file_id, nullptr, 0);
5083
5084 if (num_lone_vdata == FAIL) {
5085 close_vgroup_fileids(file_id,sdfd,-1);
5086 throw InternalErr(__FILE__, __LINE__, "Fail to obtain the number of lone vdatas.");
5087 }
5088
5089 if (num_lone_vdata > 0) {
5090
5091 vector<int32>ref_array;
5092 ref_array.resize(num_lone_vdata);
5093
5094 if (VSlone (file_id, ref_array.data(), num_lone_vdata) == FAIL) {
5095 close_vgroup_fileids(file_id,sdfd,-1);
5096 throw InternalErr(__FILE__, __LINE__, "Cannot obtain lone vdata reference arrays.");
5097 }
5098
5099 for (int i = 0; i < num_lone_vdata; i++)
5100 convert_vdata(file_id,sdfd, -1, ref_array[i],root_grp,filename);
5101 }
5102 Vend(file_id);
5103
5104 BESDEBUG("h4"," End read_lone_vdata()"<<endl);
5105}
5106
5107void read_dmr_vlone_groups(D4Group *root_grp, int32 file_id, int32 sdfd, const string & filename) {
5108
5109 BESDEBUG("h4","Start read_dmr_vlone_groups "<<endl);
5110
5111 int num_lonevg = 0;
5112 vector<int> ref_array;
5113 int32 istat = 0;
5114
5115 istat = Vstart(file_id);
5116 if (istat == FAIL) {
5117 close_vgroup_fileids(file_id,sdfd,-1);
5118 throw InternalErr(__FILE__, __LINE__, "Unable to start hdf4 V interface.");
5119 }
5120
5121 num_lonevg = Vlone(file_id,nullptr,0);
5122
5123 if (num_lonevg == FAIL) {
5124 close_vgroup_fileids(file_id,sdfd,-1);
5125 throw InternalErr(__FILE__, __LINE__, "error in obtaining the total number of lone vgroups.");
5126 }
5127
5128 // Obtain object reference array.
5129
5130 // If no lone vgroups, quit from this function.
5131 if (num_lonevg == 0){
5132 Vend(file_id);
5133 return;
5134 }
5135
5136 // Check if this is an HDF-EOS2 grid.
5137 vector<eos2_grid_t> eos2_grid_lls;
5138 vector<eos2_grid_info_t> eos2_grids_info;
5139
5140
5141#ifdef USE_HDFEOS2_LIB
5142 bool ret_value = check_eos2_grids(filename,sdfd,eos2_grid_lls, eos2_grids_info);
5143 if (ret_value == false) {
5144 close_vgroup_fileids(file_id,sdfd,-1);
5145 throw InternalErr(__FILE__, __LINE__, "error in the check_eos2_grid(). ");
5146 }
5147//HDF-EOS2 DEBUG
5148#if 0
5149for(const auto& eg:eos2_grid_lls) {
5150 cout<<"grid name: "<<eg.grid_name <<endl;
5151if (eg.oned_latlon==true)
5152 cout<<"1d latlon "<<endl;
5153else
5154 cout<<"2d latlon "<<endl;
5155 cout<<"xdim: "<< eg.xdim <<endl;
5156 cout<<"ydim: "<< eg.ydim <<endl;
5157}
5158for(const auto& eg:eos2_grids_info) {
5159 cout<<"eg.proj_code: "<< eg.projcode << endl;
5160
5161
5162}
5163#endif
5164#endif
5165
5166 ref_array.resize(num_lonevg);
5167
5168 num_lonevg = Vlone(file_id,ref_array.data(),num_lonevg);
5169
5170 // Walk through every lone group in the file.
5171
5172 for(int lone_vg_number = 0; lone_vg_number < num_lonevg;
5173 lone_vg_number++) {
5174
5175 int32 vgroup_id = Vattach(file_id,ref_array[lone_vg_number],"r");
5176 if (vgroup_id ==FAIL) {
5177 close_vgroup_fileids(file_id,sdfd,vgroup_id);
5178 throw InternalErr(__FILE__, __LINE__, "error in attaching lone vgroup.");
5179 }
5180
5181 uint16 vclassnamelen = 0; /*Vgroup class name length */
5182 vector<char> vclass_name;
5183 if (Vgetclassnamelen(vgroup_id,&vclassnamelen) == FAIL) {
5184 close_vgroup_fileids(file_id,sdfd,vgroup_id);
5185 throw InternalErr(__FILE__, __LINE__, "error in obtaining vgroup class name length.");
5186 }
5187
5188 if (vclassnamelen!=0) {
5189 vclass_name.resize(vclassnamelen+1);
5190 if (Vgetclass(vgroup_id,vclass_name.data()) == FAIL) {
5191 close_vgroup_fileids(file_id,sdfd,vgroup_id);
5192 throw InternalErr(__FILE__, __LINE__, "error in obtaining vgroup class name.");
5193 }
5194 if (reserved_vgroups(vclass_name)) {
5195 Vdetach(vgroup_id);
5196 continue;
5197 }
5198 }
5199
5200 uint16 vgroupnamelen = 0;
5201 if (Vgetnamelen(vgroup_id,&vgroupnamelen) == FAIL) {
5202 close_vgroup_fileids(file_id,sdfd,vgroup_id);
5203 throw InternalErr(__FILE__, __LINE__, "cannot obtain vgroup name length.");
5204 }
5205
5206 if (vgroupnamelen == 0) {
5207 Vdetach(vgroup_id);
5208 continue;
5209 }
5210 vector<char> vgroup_name;
5211 vgroup_name.resize(vgroupnamelen+1);
5212
5213 if (Vgetname(vgroup_id,vgroup_name.data())==FAIL){
5214 close_vgroup_fileids(file_id,sdfd,vgroup_id);
5215 throw InternalErr(__FILE__, __LINE__, "error in obtaining vgroup name.");
5216 }
5217
5218 string vgroup_name_orig_str(vgroup_name.begin(),vgroup_name.end()-1);
5219
5220 int eos2_grid_lls_index = -1;
5221 if (!eos2_grid_lls.empty())
5222 eos2_grid_lls_index = is_group_eos2_grid(vgroup_name_orig_str,eos2_grid_lls);
5223
5224 string vgroup_name_str = HDFCFUtil::get_CF_string(vgroup_name_orig_str);
5225 auto tem_d4_cgroup_ptr = make_unique<D4Group>(vgroup_name_str);
5226 auto tem_d4_cgroup = tem_d4_cgroup_ptr.release();
5227 root_grp->add_group_nocopy(tem_d4_cgroup);
5228
5229 // Add latitude and longitude fields,plus attributes
5230 bool is_eos2_grid = false;
5231 bool is_eos2_grid_cf_mapping = false;
5232 if (eos2_grid_lls_index >=0) {
5233 add_eos2_latlon_info(tem_d4_cgroup, root_grp, eos2_grid_lls[eos2_grid_lls_index],eos2_grids_info[eos2_grid_lls_index],filename);
5234 is_eos2_grid = true;
5235 int32 proj_code = eos2_grids_info[eos2_grid_lls_index].projcode;
5236 if (proj_code == GCTP_LAMAZ || proj_code == GCTP_PS || proj_code == GCTP_SNSOID)
5237 is_eos2_grid_cf_mapping = true;
5238 }
5239
5240 convert_vgroup_objects(vgroup_id,file_id,sdfd,tem_d4_cgroup,root_grp, vgroup_name_orig_str,filename,is_eos2_grid,is_eos2_grid_cf_mapping);
5241
5242 Vdetach(vgroup_id);
5243
5244 }
5245 Vend(file_id);
5246 BESDEBUG("h4","Finish read_dmr_vlone_groups "<<endl);
5247
5248}
5249
5250bool reserved_vgroups(const vector<char>& vgroup_class) {
5251
5252 if(strcmp(vgroup_class.data(),_HDF_ATTRIBUTE)==0)
5253 return true;
5254 else if(strcmp(vgroup_class.data(),_HDF_VARIABLE) ==0)
5255 return true;
5256 else if(strcmp(vgroup_class.data(),_HDF_DIMENSION)==0)
5257 return true;
5258 else if(strcmp(vgroup_class.data(),_HDF_UDIMENSION)==0)
5259 return true;
5260 else if(strcmp(vgroup_class.data(),_HDF_CDF)==0)
5261 return true;
5262 else if(strcmp(vgroup_class.data(),GR_NAME)==0)
5263 return true;
5264 else if(strcmp(vgroup_class.data(),RI_NAME)==0)
5265 return true;
5266 return false;
5267
5268}
5269
5270#if 0
5271void vgroup_convert_sds_objects(int32 vgroup_id, int32 file_id, int32 sdfd, D4Group *d4g, const string &filename) {
5272
5273 int num_gobjects = 0;
5274 int32 obj_tag = 0;
5275 int32 obj_ref = 0;
5276
5277 num_gobjects = Vntagrefs(vgroup_id);
5278 if (num_gobjects == FAIL) {
5279 close_vgroup_fileids(file_id,sdfd,vgroup_id);
5280 throw InternalErr(__FILE__, __LINE__, "error in obtaining vgroup class name.");
5281 }
5282
5283 for( int i = 0;i<num_gobjects;i++) {
5284
5285 if (Vgettagref(vgroup_id,i,&obj_tag,&obj_ref)==FAIL) {
5286 close_vgroup_fileids(file_id,sdfd,vgroup_id);
5287 throw InternalErr(__FILE__, __LINE__, "fail to obtain object tag and ref. of Vgroup members");
5288 }
5289
5290 if (obj_tag == DFTAG_NDG || obj_tag == DFTAG_SDG) {
5291 convert_sds(file_id, sdfd,vgroup_id, obj_ref, d4g,filename);
5292 }
5293 }
5294
5295}
5296#endif
5297
5298void convert_vgroup_objects(int32 vgroup_id,int32 file_id,int32 sdfd,D4Group *d4g,D4Group *root_grp, const string &vgroupname, const string& filename, bool is_eos2_grid, bool is_eos2_grid_cf_mapping) {
5299
5300 BESDEBUG("h4"," Start convert_vgroup_objects"<<endl);
5301
5302 int num_gobjects = 0;
5303 int32 obj_tag = 0;
5304 int32 obj_ref = 0;
5305
5306 num_gobjects = Vntagrefs(vgroup_id);
5307 if (num_gobjects == FAIL) {
5308 close_vgroup_fileids(file_id,sdfd,vgroup_id);
5309 throw InternalErr(__FILE__, __LINE__, "error in obtaining vgroup class name.");
5310 }
5311
5312 // Need to convert objects first.
5313 for ( int i = 0;i<num_gobjects;i++) {
5314
5315 if (Vgettagref(vgroup_id,i,&obj_tag,&obj_ref)==FAIL) {
5316 close_vgroup_fileids(file_id,sdfd,vgroup_id);
5317 throw InternalErr(__FILE__, __LINE__, "fail to obtain object tag and ref. of Vgroup members");
5318 }
5319
5320
5321 if (obj_tag == DFTAG_NDG || obj_tag == DFTAG_SDG) {
5322 convert_sds(file_id,sdfd,vgroup_id,obj_ref,d4g,root_grp, filename,is_eos2_grid, is_eos2_grid_cf_mapping);
5323 }
5324 else if(Visvs(vgroup_id,obj_ref)) {
5325 convert_vdata(file_id, sdfd, vgroup_id, obj_ref,d4g,filename);
5326 }
5327 }
5328
5329 // Then attributes.
5330 convert_vgroup_attrs(vgroup_id,d4g, vgroupname);
5331
5332 // Then the children groups.
5333 for( int i = 0;i<num_gobjects;i++) {
5334
5335 if (Vgettagref(vgroup_id,i,&obj_tag,&obj_ref)==FAIL) {
5336 close_vgroup_fileids(file_id,sdfd,vgroup_id);
5337 throw InternalErr(__FILE__, __LINE__, "fail to obtain object tag and ref. of Vgroup members");
5338 }
5339
5340 if (Visvg(vgroup_id, obj_ref)) {
5341
5342 int32 vgroup_cid = Vattach(file_id,obj_ref,"r");
5343 if (vgroup_cid == FAIL) {
5344 close_vgroup_fileids(file_id,sdfd,vgroup_id);
5345 throw InternalErr(__FILE__, __LINE__, "fail to attach vgroup_cid.");
5346 }
5347
5348 uint16 vclassnamelen = 0;
5349 vector<char> vclass_name;
5350 if (Vgetclassnamelen(vgroup_cid,&vclassnamelen) == FAIL) {
5351 Vdetach(vgroup_cid);
5352 close_vgroup_fileids(file_id,sdfd,vgroup_id);
5353 throw InternalErr(__FILE__, __LINE__, "Error in obtaining vgroup class name length.");
5354 }
5355
5356 if (vclassnamelen!=0) {
5357 vclass_name.resize(vclassnamelen+1);
5358 if (Vgetclass(vgroup_cid,vclass_name.data()) == FAIL) {
5359 Vdetach(vgroup_cid);
5360 close_vgroup_fileids(file_id,sdfd,vgroup_id);
5361 throw InternalErr(__FILE__, __LINE__, "Error in obtaining vgroup class name.");
5362 }
5363 if (reserved_vgroups(vclass_name)) {
5364 Vdetach(vgroup_cid);
5365 continue;
5366 }
5367 }
5368
5369 uint16 vgroupnamelen = 0;
5370 if (Vgetnamelen(vgroup_cid,&vgroupnamelen) == FAIL) {
5371 Vdetach(vgroup_cid);
5372 close_vgroup_fileids(file_id,sdfd,vgroup_id);
5373 throw InternalErr(__FILE__, __LINE__, "cannot obtain vgroup name length.");
5374 }
5375
5376 vector<char> vgroup_name;
5377 vgroup_name.resize(vgroupnamelen+1);
5378
5379 if (Vgetname(vgroup_cid,vgroup_name.data())==FAIL){
5380 Vdetach(vgroup_cid);
5381 close_vgroup_fileids(file_id,sdfd,vgroup_id);
5382 throw InternalErr(__FILE__, __LINE__, "error in obtaining vgroup name.");
5383 }
5384
5385 // Need to remove the last '/0'.
5386 string vgroup_name_orig_str(vgroup_name.begin(),vgroup_name.end()-1);
5387
5388 string vgroup_name_str = HDFCFUtil::get_CF_string(vgroup_name_orig_str);
5389 auto d4c_g_ptr = make_unique<D4Group>(vgroup_name_str);
5390 auto d4c_g = d4c_g_ptr.release();
5391 d4g->add_group_nocopy(d4c_g);
5392
5393 convert_vgroup_objects(vgroup_cid,file_id,sdfd,d4c_g,root_grp,vgroup_name_orig_str,filename, is_eos2_grid, is_eos2_grid_cf_mapping);
5394
5395 Vdetach(vgroup_cid);
5396 }
5397 }
5398 BESDEBUG("h4"," End convert_vgroup_objects"<<endl);
5399}
5400
5401void convert_vgroup_attrs(int32 vgroup_id,D4Group *d4g, const string &vgroupname) {
5402
5403 // Need to use Vnattrs2 instead of Vnattrs to include all attributes.
5404 intn n_attrs = Vnattrs2(vgroup_id);
5405 if (n_attrs == FAIL) {
5406 Vdetach(vgroup_id);
5407 throw InternalErr(__FILE__,__LINE__,"Vnattrs failed");
5408 }
5409
5410 for(int attr_index = 0; attr_index <n_attrs; attr_index++) {
5411
5412 char attr_name[H4_MAX_NC_NAME];
5413 int attr_value_size = 0;
5414 int32 attr_type = 0;
5415 int32 attr_count = 0;
5416 intn status_n = Vattrinfo2(vgroup_id, attr_index, attr_name, &attr_type,
5417 &attr_count, &attr_value_size, nullptr, nullptr);
5418 if (status_n == FAIL) {
5419 Vdetach(vgroup_id);
5420 throw InternalErr(__FILE__,__LINE__,"Vattrinfo failed");
5421 }
5422
5423 // Create the DAP4 attribute
5424 string tempname (attr_name);
5425
5426 // Here we need to exclude the HDF-EOS2 Grid or Swath specific internal attributes starting from _FV_
5427 // These attributes are represented as field attributes already, so we don't need to duplicate them.
5428 if(vgroupname=="Grid Attributes" || vgroupname=="Swath Attributes") {
5429 if(tempname.size()>4) {
5430 string tempname_f4chars = tempname.substr(0,4);
5431 if (tempname_f4chars=="_FV_")
5432 continue;
5433 }
5434 }
5435
5436 vector<char> attr_value;
5437 attr_value.resize(attr_value_size);
5438 status_n = Vgetattr2(vgroup_id,attr_index,attr_value.data());
5439 if (status_n == FAIL) {
5440 Vdetach(vgroup_id);
5441 throw InternalErr(__FILE__,__LINE__,"Vgetattr failed");
5442 }
5443
5444 string dap4_attrname = HDFCFUtil::get_CF_string(tempname);
5445 map_vgroup_attr(d4g,dap4_attrname,attr_type,attr_count,attr_value);
5446
5447 }
5448
5449}
5450
5451
5452void convert_vdata(int32 fileid, int32 sdfd, int32 vgroup_id,int32 obj_ref ,D4Group* d4g,const string& filename) {
5453
5454 // Obtain vdata ID
5455 int32 vdata_id = VSattach (fileid, obj_ref, "r");
5456 if (vdata_id == FAIL) {
5457 close_vgroup_fileids(fileid,sdfd,vgroup_id);
5458 throw InternalErr(__FILE__, __LINE__, "Cannot attach vdata.");
5459 }
5460
5461 // Vdata class
5462 char vdata_class[VSNAMELENMAX+1];
5463
5464 if (VSgetclass (vdata_id, vdata_class) == FAIL) {
5465 VSdetach (vdata_id);
5466 close_vgroup_fileids(fileid,sdfd,vgroup_id);
5467 throw InternalErr(__FILE__, __LINE__, "VSgetclass failed.");
5468 }
5469
5470 if (VSisattr(vdata_id) == FALSE && VSisinternal(vdata_class) == FALSE) {
5471
5472 int32 vs_nflds = VFnfields(vdata_id);
5473 if (vs_nflds == FAIL) {
5474 VSdetach(vdata_id);
5475 close_vgroup_fileids(fileid,sdfd,vgroup_id);
5476 throw InternalErr(__FILE__, __LINE__, "Cannot get the number of fields of a vdata.");
5477 }
5478
5479 int32 num_elms = VSelts(vdata_id);
5480 if (num_elms == FAIL) {
5481 VSdetach(vdata_id);
5482 close_vgroup_fileids(fileid,sdfd,vgroup_id);
5483 throw InternalErr(__FILE__, __LINE__, "Cannot get the number of records of a vdata.");
5484 }
5485
5486 // We map one field vdata to an atomic array; multi-field vdata to an array of structure.
5487 try {
5488 if (vs_nflds >1)
5489 map_vdata_to_dap4_structure_array(vdata_id, num_elms, vs_nflds, obj_ref, d4g, filename);
5490 else if (vs_nflds == 1)
5491 map_vdata_to_dap4_atomic_array(vdata_id, num_elms, obj_ref, d4g, filename);
5492 }
5493 catch(...) {
5494 VSdetach(vdata_id);
5495 close_vgroup_fileids(fileid,sdfd,vgroup_id);
5496 if (vs_nflds >1)
5497 throw InternalErr(__FILE__, __LINE__, "Cannot map vdata to a dap structure array.");
5498 else
5499 throw InternalErr(__FILE__, __LINE__, "Cannot map vdata to a dap atomic array.");
5500 }
5501 }
5502 VSdetach(vdata_id);
5503}
5504
5505
5506void map_vdata_to_dap4_atomic_array(int32 vdata_id, int32 num_elms, int32 obj_ref, D4Group *d4g, const string &filename) {
5507
5508 // Only one field, so the index is 0.
5509 int32 vdata_field_order = VFfieldorder(vdata_id,0);
5510 if (vdata_field_order == FAIL) {
5511 throw InternalErr(__FILE__, __LINE__, "VFfieldorder failed");
5512 }
5513
5514 char vdata_name[VSNAMELENMAX];
5515 if (VSgetname(vdata_id,vdata_name) == FAIL) {
5516 throw InternalErr(__FILE__, __LINE__, "Cannot get vdata name.");
5517 }
5518
5519 string vdata_name_str(vdata_name);
5520
5521 const char *fieldname = VFfieldname(vdata_id,0);
5522 if (fieldname == nullptr) {
5523 throw InternalErr(__FILE__, __LINE__, "Cannot get vdata field name.");
5524 }
5525
5526 string fieldname_str(fieldname);
5527 string dap4_vdata_str;
5528
5529 // For HDF-EOS2, one field vdata and the field share the same name. So we just keep one.
5530 if (vdata_name_str == fieldname_str)
5531 dap4_vdata_str = fieldname_str;
5532 else
5533 dap4_vdata_str = HDFCFUtil::get_CF_string(vdata_name_str) +"_"+ HDFCFUtil::get_CF_string(fieldname_str);
5534
5535 int32 fieldtype = VFfieldtype(vdata_id,0);
5536
5537 BaseType *bt = gen_dap_var(fieldtype,dap4_vdata_str,filename);
5538 auto ar_unique = make_unique<HDFDMRArray_VD>(filename,obj_ref,dap4_vdata_str,bt);
5539 auto ar = ar_unique.release();
5540 ar->append_dim_ll(num_elms);
5541 if (vdata_field_order != 1) {
5542 ar->append_dim_ll(vdata_field_order);
5543 ar->set_rank(2);
5544 }
5545
5546 // map vdata attributes to dap4. We will ignore vdata field attributes. Haven't seen one in NASA files.
5547 map_vdata_to_dap4_attrs(ar,vdata_id,obj_ref);
5548
5549 ar->set_is_dap4(true);
5550 d4g->add_var_nocopy(ar);
5551
5552 delete bt;
5553
5554}
5555
5556void map_vdata_to_dap4_structure_array(int32 vdata_id, int32 num_elms, int32 nflds, int32 obj_ref, D4Group *d4g, const string &filename) {
5557
5558 char vdata_name[VSNAMELENMAX];
5559 if (VSgetname(vdata_id,vdata_name) == FAIL) {
5560 throw InternalErr(__FILE__, __LINE__, "Cannot get vdata name.");
5561 }
5562 string vdata_name_str(vdata_name);
5563 vdata_name_str = HDFCFUtil::get_CF_string(vdata_name_str);
5564
5565 auto structure_unique_ptr = make_unique<HDFStructure>(vdata_name_str,filename);
5566 auto structure_ptr = structure_unique_ptr.release();
5567
5568 for (int j = 0; j <nflds; j++) {
5569
5570 int32 vdata_field_order = VFfieldorder(vdata_id,j);
5571 if (vdata_field_order == FAIL) {
5572 throw InternalErr(__FILE__, __LINE__, "VFfieldorder failed");
5573 }
5574
5575 const char *fieldname = VFfieldname(vdata_id,j);
5576 if (fieldname == nullptr) {
5577 throw InternalErr(__FILE__, __LINE__, "Cannot get vdata field name.");
5578 }
5579
5580 string fieldname_str(fieldname);
5581 fieldname_str = HDFCFUtil::get_CF_string(fieldname_str);
5582
5583 int32 fieldtype = VFfieldtype(vdata_id,j);
5584
5585 if (vdata_field_order == 1) {
5586 BaseType *bt = gen_dap_var(fieldtype,fieldname_str,filename);
5587 structure_ptr->add_var(bt);
5588 delete bt;
5589 }
5590 else {
5591 BaseType *bt = gen_dap_var(fieldtype,fieldname_str,filename);
5592 auto arf_unique = make_unique<HDFArray>(fieldname_str,filename,bt);
5593 auto arf = arf_unique.release();
5594 arf->append_dim_ll(vdata_field_order);
5595 structure_ptr->add_var(arf);
5596 delete bt;
5597 delete arf;
5598 }
5599 }
5600
5601 auto ar_unique = make_unique<HDFDMRArray_VD>(filename,obj_ref,vdata_name_str,structure_ptr);
5602 auto ar = ar_unique.release();
5603 ar->append_dim_ll(num_elms);
5604
5605 // map vdata attributes to dap4. We will ignore vdata field attributes. Haven't seen one in NASA files.
5606 map_vdata_to_dap4_attrs(ar,vdata_id,obj_ref);
5607 ar->set_is_dap4(true);
5608 d4g->add_var(ar);
5609
5610 delete structure_ptr;
5611 delete ar;
5612}
5613
5614void map_vdata_to_dap4_attrs(HDFDMRArray_VD *ar, int32 vdata_id, int32 obj_ref) {
5615
5616 // Number of attributes
5617 int32 nattrs = 0;
5618
5619 // API status indicator
5620 int32 status = 0;
5621
5622 // Number of vdata attributes,we will not handle field attribute now.
5623 nattrs = VSfnattrs (vdata_id, _HDF_VDATA);
5624
5625 if (nattrs > 0) {
5626
5627 // Obtain number of vdata attributes
5628 for (int i = 0; i < nattrs; i++) {
5629
5630 // Vdata attribute name
5631 char attr_name[H4_MAX_NC_NAME];
5632 int attrsize = 0;
5633 int32 attr_type = 0;
5634 int32 attr_count = 0;
5635
5636 status = VSattrinfo (vdata_id, _HDF_VDATA, i, attr_name,
5637 &attr_type, &attr_count, &attrsize);
5638 if (status == FAIL) {
5639 throw InternalErr(__FILE__, __LINE__, "VSattrinfo failed.");
5640 }
5641
5642 // Checking and handling the special characters for the vdata attribute name.
5643 string tempname(attr_name);
5644 vector<char> attr_value;
5645 attr_value.resize(attrsize);
5646
5647 if (VSgetattr (vdata_id, _HDF_VDATA, i, attr_value.data()) == FAIL) {
5648 throw InternalErr(__FILE__, __LINE__, "VSgetattr failed.");
5649 }
5650 string dap4_attrname = HDFCFUtil::get_CF_string(tempname);
5651
5652 map_sds_vdata_attr(ar,dap4_attrname,attr_type,attr_count,attr_value);
5653 }
5654 }
5655
5656 // This is for dmrpp. The reference number is used to retrieve the data.
5657 add_obj_ref_attr(ar,false,obj_ref);
5658
5659}
5660
5661
5662void convert_sds(int32 file_id, int32 sdfd, int32 vgroup_id, int32 obj_ref, D4Group *d4g, D4Group *root_grp,const string &filename, bool is_eos2_grid, bool is_eos2_grid_cf_mapping) {
5663
5664 int32 sds_index = 0;
5665 int32 sds_id = 0;
5666 vector<char> sds_name;
5667 uint16 name_len = 0;
5668 int32 sds_rank = 0;
5669 int32 sds_type = 0;
5670 int32 dim_sizes[H4_MAX_VAR_DIMS];
5671 int32 n_sds_attrs = 0;
5672
5673 sds_index = SDreftoindex(sdfd,obj_ref);
5674 if (sds_index == FAIL) {
5675 close_vgroup_fileids(file_id,sdfd,vgroup_id);
5676 throw InternalErr(__FILE__, __LINE__, "Fail to obtain the SDS index.");
5677 }
5678
5679 sds_id = SDselect(sdfd,sds_index);
5680 if (sds_id == FAIL) {
5681 close_vgroup_fileids(file_id,sdfd,vgroup_id);
5682 throw InternalErr(__FILE__, __LINE__, "Fail to obtain the SDS ID.");
5683 }
5684
5685 if (SDgetnamelen(sds_id, &name_len) == FAIL) {
5686 SDendaccess(sds_id);
5687 close_vgroup_fileids(file_id,sdfd,vgroup_id);
5688 throw InternalErr(__FILE__, __LINE__, "Fail to obtain the SDS name length.");
5689 }
5690
5691 sds_name.resize(name_len+1);
5692
5693 // Obtain object name, rank, size, field type and number of SDS attributes
5694 if (FAIL == SDgetinfo (sds_id, sds_name.data(), &sds_rank, dim_sizes, &sds_type, &n_sds_attrs)) {
5695 SDendaccess(sds_id);
5696 close_vgroup_fileids(file_id,sdfd,vgroup_id);
5697 throw InternalErr(__FILE__, __LINE__, "Fail to obtain the SDS ID.");
5698 }
5699
5700 bool has_zero_dim_size = false;
5701 for (int j = 0; j <sds_rank; j++) {
5702 if(dim_sizes[j] == 0) {
5703 has_zero_dim_size = true;
5704 break;
5705 }
5706 }
5707
5708 if (has_zero_dim_size) {
5709 SDendaccess(sds_id);
5710 return;
5711 }
5712
5713 string sds_name_str(sds_name.begin(),sds_name.end()-1);
5714
5715 // Handle special characters in the sds_name.
5716 sds_name_str = HDFCFUtil::get_CF_string(sds_name_str);
5717 BaseType *bt = gen_dap_var(sds_type,sds_name_str, filename);
5718 unsigned int dtype_size = bt->width();
5719 auto ar_unique = make_unique<HDFDMRArray_SDS>(filename, obj_ref, sds_rank,dtype_size,sds_name_str,bt);
5720 auto ar = ar_unique.release();
5721
5722 // Obtain dimension info.
5723
5724 // Handle dimensions with original dimension names
5725 for (int dimindex = 0; dimindex < sds_rank; dimindex++) {
5726
5727 int dimid = SDgetdimid (sds_id, dimindex);
5728 if (dimid == FAIL) {
5729 delete bt;
5730 delete ar;
5731 SDendaccess (sds_id);
5732 close_vgroup_fileids(file_id,sdfd,vgroup_id);
5733 throw InternalErr(__FILE__, __LINE__, "SDgetdimid failed.");
5734 }
5735 char dim_name[H4_MAX_NC_NAME];
5736 int32 dim_size = 0;
5737 int32 dim_type = 0;
5738
5739 // Number of dimension attributes(This is almost never used)
5740 int32 num_dim_attrs = 0;
5741
5742 intn status = SDdiminfo (dimid, dim_name, &dim_size, &dim_type,
5743 &num_dim_attrs);
5744
5745 if (status == FAIL) {
5746 delete ar;
5747 delete bt;
5748 SDendaccess (sds_id);
5749 close_vgroup_fileids(file_id,sdfd,vgroup_id);
5750 throw InternalErr(__FILE__, __LINE__, "SDdiminfo failed.");
5751 }
5752
5753 string dim_name_str (dim_name);
5754 dim_name_str = HDFCFUtil::get_CF_string(dim_name_str);
5755
5756 // We add a "/" in front of dim_name_str for the dimension name to make it FQN
5757 // since current build_dmrpp requires this.
5758 ar->append_dim_ll(dim_sizes[dimindex], dim_name_str);
5759
5760 }
5761 dims_transform_to_dap4(ar,root_grp,false);
5762
5763 // map sds var attributes to dap4
5764 try {
5765 map_sds_var_dap4_attrs(ar,sds_id,obj_ref,n_sds_attrs);
5766 }
5767 catch(...){
5768 delete ar;
5769 delete bt;
5770 SDendaccess (sds_id);
5771 close_vgroup_fileids(file_id,sdfd,vgroup_id);
5772 throw InternalErr(__FILE__, __LINE__, "Map SDS attributes to DAP4 failed.");
5773 }
5774
5775 if (is_eos2_grid) {
5776
5777 // If this SDS belongs to an HDF-EOS2 grid. We need to add the coordinates attributes to ensure the CF tools
5778 // to correctly display the data. Not absolutely necessary but highly desireable and useful for end users.
5779 // So add it.
5780 string d4_grp_path = d4g->FQN();
5781
5782 // Obtain the grid name, it should be the first part of the d4_grp_path.
5783 string grid_name = first_part_of_full_path(d4_grp_path);
5784 string coordinates =grid_name+"/Longitude ";
5785 coordinates +=grid_name+"/Latitude";
5786 add_var_dap4_attr(ar,"coordinates",attr_str_c,coordinates);
5787
5788 // Here need to add "grid_mapping" grid_name + /eos_cf_projection", this is for grid_mapping attribute to follow the CF.
5789 // But we only support 3 projections now. So need to check this.
5790 if (is_eos2_grid_cf_mapping) {
5791 string grid_cf_mapping = grid_name+"/eos_cf_projection";
5792 add_var_dap4_attr(ar,"grid_mapping",attr_str_c,grid_cf_mapping);
5793 }
5794 }
5795
5796 ar->set_is_dap4(true);
5797 d4g->add_var_nocopy(ar);
5798
5799 delete bt;
5800 SDendaccess(sds_id);
5801}
5802
5803
5804void map_sds_var_dap4_attrs(HDFDMRArray_SDS *ar, int32 sds_id, int32 obj_ref, int32 n_sds_attrs) {
5805
5806 intn emptySDS = 0;
5807 if (SDcheckempty(sds_id,&emptySDS) == FAIL) {
5808 SDendaccess(sds_id);
5809 throw InternalErr(__FILE__, __LINE__, "SDcheckempty failed.");
5810 }
5811 bool has_fv_attr = false;
5812
5813 char attr_name[H4_MAX_NC_NAME];
5814 for (int attrindex = 0; attrindex < n_sds_attrs; attrindex++) {
5815
5816 int32 sds_attr_type = 0;
5817 int32 attr_value_count = 0;
5818 if (FAIL==SDattrinfo (sds_id, attrindex, attr_name, &sds_attr_type, &attr_value_count))
5819 throw InternalErr(__FILE__,__LINE__, "SDattrinfo failed ");
5820
5821 vector<char> attr_value;
5822 attr_value.resize(attr_value_count * DFKNTsize(sds_attr_type));
5823 if (SDreadattr (sds_id, attrindex, attr_value.data()) == -1)
5824 throw InternalErr(__FILE__,__LINE__, "SDreadattr failed ");
5825
5826 string dap4_attrname (attr_name);
5827 if (emptySDS == 1) {
5828 if (!has_fv_attr) {
5829 if (dap4_attrname == "_FillValue")
5830 has_fv_attr = true;
5831 }
5832 }
5833 dap4_attrname = HDFCFUtil::get_CF_string(dap4_attrname);
5834
5835 map_sds_vdata_attr(ar,dap4_attrname,sds_attr_type,attr_value_count,attr_value);
5836
5837 }
5838
5839 // This empty SDS doesn't have _FillValue attribute. We will add the _FillValue attribute based on the fillvalue of this SDS.
5840 if (!has_fv_attr && emptySDS == 1)
5841 add_sds_fvalue_attr(ar, sds_id);
5842 // Save the reference number since it will be used by the dmrpp module.
5843 add_obj_ref_attr(ar,true,obj_ref);
5844}
5845
5846// Note: HDF4 doesn't support 64-bit integers.
5847BaseType * gen_dap_var(int32 h4_type, const string & h4_str, const string &filename) {
5848
5849 BaseType * ret_bt = nullptr;
5850 switch (h4_type) {
5851
5852 case DFNT_INT16:
5853 case DFNT_NINT16:
5854 ret_bt = new HDFInt16(h4_str,filename);
5855 break;
5856
5857 case DFNT_NINT32:
5858 case DFNT_INT32:
5859 ret_bt = new HDFInt32(h4_str,filename);
5860 break;
5861
5862 case DFNT_UINT16:
5863 case DFNT_NUINT16:
5864 ret_bt = new HDFUInt16(h4_str,filename);
5865 break;
5866
5867 case DFNT_NUINT32:
5868 case DFNT_UINT32:
5869 ret_bt = new HDFUInt32(h4_str,filename);
5870 break;
5871
5872 case DFNT_FLOAT32:
5873 ret_bt = new HDFFloat32(h4_str,filename);
5874 break;
5875
5876 case DFNT_FLOAT64:
5877 ret_bt = new HDFFloat64(h4_str,filename);
5878 break;
5879
5880 case DFNT_INT8:
5881 case DFNT_NINT8:
5882 case DFNT_CHAR8:
5883 ret_bt = new HDFInt8(h4_str,filename);
5884 break;
5885
5886 case DFNT_UINT8:
5887 case DFNT_NUINT8:
5888 case DFNT_UCHAR8:
5889 ret_bt = new HDFByte(h4_str,filename);
5890 break;
5891
5892 default:
5893 throw InternalErr(__FILE__,__LINE__,"unsupported data type.");
5894
5895 }
5896 return ret_bt;
5897}
5898
5899// Note: we map the attributes with DFNT_CHAR(char) to DAP4 string since
5900// this is the common way that users apply.
5901D4AttributeType h4type_to_dap4_attrtype(int32 h4_type) {
5902
5903 D4AttributeType dap4_attr_type = attr_null_c;
5904 switch (h4_type) {
5905
5906 case DFNT_CHAR:
5907 dap4_attr_type = attr_str_c;
5908 break;
5909 case DFNT_INT8:
5910 case DFNT_NINT8:
5911 dap4_attr_type = attr_int8_c;
5912 break;
5913 case DFNT_INT16:
5914 case DFNT_NINT16:
5915 dap4_attr_type = attr_int16_c;
5916 break;
5917 case DFNT_NINT32:
5918 case DFNT_INT32:
5919 dap4_attr_type = attr_int32_c;
5920 break;
5921
5922 case DFNT_UINT16:
5923 case DFNT_NUINT16:
5924 dap4_attr_type = attr_uint16_c;
5925 break;
5926
5927 case DFNT_NUINT32:
5928 case DFNT_UINT32:
5929 dap4_attr_type = attr_uint32_c;
5930 break;
5931
5932 case DFNT_FLOAT32:
5933 dap4_attr_type = attr_float32_c;
5934 break;
5935
5936 case DFNT_FLOAT64:
5937 dap4_attr_type = attr_float64_c;
5938 break;
5939
5940 case DFNT_UINT8:
5941 case DFNT_NUINT8:
5942 case DFNT_UCHAR:
5943 dap4_attr_type = attr_byte_c;
5944 break;
5945
5946 default:
5947 throw InternalErr(__FILE__,__LINE__,"unsupported data type.");
5948
5949 }
5950 return dap4_attr_type;
5951
5952}
5953
5954string
5955print_dap4_attr(int32 type, int loc, void *vals)
5956{
5957 string ret_value;
5958 ostringstream rep;
5959
5960 union {
5961 char *cp;
5962 unsigned char *ucp;
5963 short *sp;
5964 unsigned short *usp;
5965 int32 /*nclong*/ *lp;
5966 unsigned int *ui;
5967 float *fp;
5968 double *dp;
5969 } gp;
5970
5971 switch (type) {
5972
5973 // Mapping both DFNT_UINT8 and DFNT_INT8 to unsigned char
5974 // may cause overflow. Documented at jira ticket HFRHANDLER-169.
5975 case DFNT_UINT8:
5976 case DFNT_UCHAR:
5977 {
5978 unsigned char uc;
5979 gp.ucp = (unsigned char *) vals;
5980 uc = *(gp.ucp+loc);
5981 rep << (int)uc;
5982 return rep.str();
5983 }
5984
5985 case DFNT_INT8:
5986 case DFNT_NINT8:
5987 {
5988 char c;
5989 gp.cp = (char *) vals;
5990 c = *(gp.cp+loc);
5991 rep << (int)c;
5992 return rep.str();
5993 }
5994
5995 case DFNT_CHAR:
5996 {
5997 string tmp_str = static_cast<const char*>(vals);
5998 return tmp_str;
5999 }
6000
6001 case DFNT_INT16:
6002 case DFNT_NINT16:
6003 {
6004 gp.sp = (short *) vals;
6005 rep << *(gp.sp+loc);
6006 return rep.str();
6007 }
6008
6009 case DFNT_UINT16:
6010 case DFNT_NUINT16:
6011 {
6012 gp.usp = (unsigned short *) vals;
6013 rep << *(gp.usp+loc);
6014 return rep.str();
6015 }
6016
6017 case DFNT_INT32:
6018 case DFNT_NINT32:
6019 {
6020 gp.lp = (int32 *) vals;
6021 rep << *(gp.lp+loc);
6022 return rep.str();
6023 }
6024
6025 case DFNT_UINT32:
6026 case DFNT_NUINT32:
6027 {
6028 gp.ui = (unsigned int *) vals;
6029 rep << *(gp.ui+loc);
6030 return rep.str();
6031 }
6032
6033 case DFNT_FLOAT:
6034 {
6035 float attr_val = *(float*)vals;
6036 bool is_a_fin = isfinite(attr_val);
6037 gp.fp = (float *) vals;
6038 rep << showpoint;
6039 // setprecision seeme to cause the one-bit error when
6040 // converting from float to string. Watch whether this
6041 // is an isue.
6042 rep << setprecision(10);
6043 rep << *(gp.fp+loc);
6044 string tmp_rep_str = rep.str();
6045 if (tmp_rep_str.find('.') == string::npos
6046 && tmp_rep_str.find('e') == string::npos
6047 && tmp_rep_str.find('E') == string::npos) {
6048 if(true == is_a_fin)
6049 rep << ".";
6050 }
6051 return rep.str();
6052 }
6053
6054 case DFNT_DOUBLE:
6055 {
6056
6057 double attr_val = *(double*)vals;
6058 bool is_a_fin = isfinite(attr_val);
6059 gp.dp = (double *) vals;
6060 rep << std::showpoint;
6061 rep << std::setprecision(17);
6062 rep << *(gp.dp+loc);
6063 string tmp_rep_str = rep.str();
6064 if (tmp_rep_str.find('.') == string::npos
6065 && tmp_rep_str.find('e') == string::npos
6066 && tmp_rep_str.find('E') == string::npos) {
6067 if(true == is_a_fin)
6068 rep << ".";
6069 }
6070 return rep.str();
6071 }
6072 default:
6073 return ret_value;
6074 }
6075
6076}
6077
6078void map_vgroup_attr(D4Group *d4g, const string &dap4_attrname,int32 attr_type, int32 attr_count, vector<char> & attr_value) {
6079
6080 D4AttributeType dap4_attr_type = h4type_to_dap4_attrtype(attr_type);
6081
6082 // We encounter an unsupported DAP4 attribute type.
6083 if (attr_null_c == dap4_attr_type)
6084 throw InternalErr(__FILE__, __LINE__, "unsupported DAP4 attribute type");
6085
6086 // Create the DAP4 attribute
6087 auto d4_attr_unique = make_unique<D4Attribute>(dap4_attrname, dap4_attr_type);
6088 D4Attribute *d4_attr = d4_attr_unique.release();
6089
6090 // Map characters to string first.
6091 if (attr_type == DFNT_CHAR) {
6092 string t_dap4_attr_value(attr_value.begin(),attr_value.end());
6093 auto dap4_attr_value = string(t_dap4_attr_value.c_str());
6094 d4_attr->add_value(dap4_attr_value);
6095 }
6096 else {
6097 for (int loc=0; loc < attr_count ; loc++) {
6098
6099 string print_rep = print_dap4_attr(attr_type, loc, (void*) (attr_value.data()));
6100 if (print_rep.empty())
6101 throw InternalErr(__FILE__,__LINE__, "Cannot obtain the attribute value for DAP4 ");
6102 else
6103 d4_attr->add_value(print_rep);
6104
6105 }
6106 }
6107 d4g->attributes()->add_attribute_nocopy(d4_attr);
6108
6109}
6110
6111void map_sds_vdata_attr(BaseType *d4b, const string &attr_name,int32 attr_type, int32 attr_count, vector<char> & attr_value) {
6112
6113 D4AttributeType dap4_attr_type = h4type_to_dap4_attrtype(attr_type);
6114
6115 // We encounter an unsupported DAP4 attribute type.
6116 if (attr_null_c == dap4_attr_type)
6117 throw InternalErr(__FILE__, __LINE__, "unsupported DAP4 attribute type");
6118
6119 // Create the DAP4 attribute
6120
6121 string dap4_attrname (attr_name);
6122 auto d4_attr_unique = make_unique<D4Attribute>(dap4_attrname, dap4_attr_type);
6123 D4Attribute *d4_attr = d4_attr_unique.release();
6124
6125 if (attr_type == DFNT_CHAR) {
6126 string t_dap4_attr_value(attr_value.begin(),attr_value.end());
6127 auto dap4_attr_value = string(t_dap4_attr_value.c_str());
6128 d4_attr->add_value(dap4_attr_value);
6129 }
6130 else {
6131 for (int loc=0; loc < attr_count ; loc++) {
6132
6133 string print_rep = print_dap4_attr(attr_type, loc, (void*) (attr_value.data()));
6134 if (print_rep.empty())
6135 throw InternalErr(__FILE__,__LINE__, "Cannot obtain the attribute value for DAP4 ");
6136 else
6137 d4_attr->add_value(print_rep);
6138
6139 }
6140 }
6141 d4b->attributes()->add_attribute_nocopy(d4_attr);
6142
6143}
6144
6145void add_obj_ref_attr(BaseType * d4b, bool is_sds, int32 obj_ref) {
6146
6147 // Add the object ref and tag information later.
6148 string obj_ref_attr_name;
6149 if (is_sds)
6150 obj_ref_attr_name = "dmr_sds_ref";
6151 else
6152 obj_ref_attr_name = "dmr_vdata_ref";
6153
6154 auto d4_obj_ref_attr_unique = make_unique<D4Attribute>(obj_ref_attr_name, attr_int32_c);
6155 D4Attribute *d4_obj_ref_attr = d4_obj_ref_attr_unique.release();
6156 string obj_ref_str = print_dap4_attr(DFNT_INT32, 0,(void*)&obj_ref);
6157 d4_obj_ref_attr->add_value(obj_ref_str);
6158
6159 d4b->attributes()->add_attribute_nocopy(d4_obj_ref_attr);
6160
6161}
6162
6163void add_sds_fvalue_attr(BaseType * d4b, int32 sdsid) {
6164
6165 // Obtain SDS datatype
6166 int32 sds_dtype = 0;
6167 if (FAIL == SDgetinfo (sdsid, nullptr, nullptr, nullptr, &sds_dtype, nullptr)) {
6168 SDendaccess(sdsid);
6169 throw InternalErr(__FILE__, __LINE__, "Fail to obtain the SDS info.");
6170 }
6171
6172 string fvalue_str;
6173 switch (sds_dtype) {
6174
6175 case DFNT_UINT8:
6176 case DFNT_UCHAR: {
6177 uint8_t fvalue;
6178 if (SDgetfillvalue(sdsid, &fvalue) == SUCCEED)
6179 fvalue_str = to_string(fvalue);
6180 else
6181 fvalue_str = to_string(255);
6182 }
6183 break;
6184 case DFNT_INT8:
6185 case DFNT_CHAR: {
6186 int8_t fvalue;
6187 if (SDgetfillvalue(sdsid, &fvalue) == SUCCEED)
6188 fvalue_str = to_string(fvalue);
6189 else
6190 fvalue_str = to_string(FILL_BYTE);
6191 }
6192 break;
6193
6194 case DFNT_INT16: {
6195 int16_t fvalue;
6196 if (SDgetfillvalue(sdsid, &fvalue) == SUCCEED)
6197 fvalue_str = to_string(fvalue);
6198 else
6199 fvalue_str = to_string(FILL_SHORT);
6200 }
6201 break;
6202
6203 case DFNT_UINT16: {
6204 uint16_t fvalue;
6205 if (SDgetfillvalue(sdsid, &fvalue) == SUCCEED)
6206 fvalue_str = to_string(fvalue);
6207 else
6208 fvalue_str = to_string(65535);
6209 }
6210 break;
6211
6212 case DFNT_INT32: {
6213 int32_t fvalue;
6214 if (SDgetfillvalue(sdsid, &fvalue) == SUCCEED)
6215 fvalue_str = to_string(fvalue);
6216 else
6217 fvalue_str = to_string(FILL_LONG);
6218 }
6219 break;
6220
6221 case DFNT_UINT32: {
6222 uint32_t fvalue;
6223 if (SDgetfillvalue(sdsid, &fvalue) == SUCCEED)
6224 fvalue_str = to_string(fvalue);
6225 else
6226 fvalue_str = to_string(4294967295);
6227 }
6228 break;
6229
6230 case DFNT_FLOAT: {
6231 float fvalue;
6232 if (SDgetfillvalue(sdsid, &fvalue) == SUCCEED)
6233 fvalue_str = to_string(fvalue);
6234 else
6235 fvalue_str = to_string(FILL_FLOAT);
6236 }
6237 break;
6238
6239 case DFNT_DOUBLE: {
6240 double fvalue;
6241 if (SDgetfillvalue(sdsid, &fvalue) == SUCCEED)
6242 fvalue_str = to_string(fvalue);
6243 else
6244 fvalue_str = to_string(FILL_DOUBLE);
6245
6246 }
6247 break;
6248 default:
6249 break;
6250 }
6251
6252 D4AttributeType dap4_attr_type = h4type_to_dap4_attrtype(sds_dtype);
6253
6254 // We encounter an unsupported DAP4 attribute type.
6255 if (attr_null_c == dap4_attr_type)
6256 throw InternalErr(__FILE__, __LINE__, "unsupported DAP4 attribute type");
6257
6258 auto d4_fvalue_attr_unique = make_unique<D4Attribute>("_FillValue", dap4_attr_type);
6259 D4Attribute *d4_fvalue_attr = d4_fvalue_attr_unique.get();
6260 d4_fvalue_attr->add_value(fvalue_str);
6261
6262 d4b->attributes()->add_attribute_nocopy(d4_fvalue_attr_unique.release());
6263
6264}
6265
6266
6267void close_vgroup_fileids(int32 fileid, int32 sdfd, int32 vgroup_id) {
6268 if (vgroup_id != FAIL)
6269 Vdetach(vgroup_id);
6270 Vend(fileid);
6271 Hclose(fileid);
6272 SDend(sdfd);
6273}
6274
6275// Direct CF to DAP4, helper function to DAP4 variable attributes.
6276void add_var_dap4_attr(BaseType *var,const string& attr_name, D4AttributeType attr_type, const string& attr_value){
6277
6278 auto d4_attr_unique = make_unique<D4Attribute>(attr_name,attr_type);
6279 auto d4_attr = d4_attr_unique.release();
6280 d4_attr->add_value(attr_value);
6281 var->attributes()->add_attribute_nocopy(d4_attr);
6282
6283}
6284
6285int is_group_eos2_grid(const string& vgroup_name, vector<eos2_grid_t>& eos2_grid_lls) {
6286
6287 int ret_value = -1;
6288 for (int i = 0; i <eos2_grid_lls.size();i++) {
6289 if (vgroup_name==eos2_grid_lls[i].grid_name){
6290 ret_value = i;
6291 break;
6292 }
6293 }
6294
6295 return ret_value;
6296}
6297
6298void add_eos2_latlon_info(D4Group *d4_grp, D4Group *root_grp, const eos2_grid_t &eos2_grid, const eos2_grid_info_t & eos2_grid_info, const string &filename){
6299
6300
6301 string ydim_path = "YDim/"+eos2_grid.grid_name;
6302 ydim_path = HDFCFUtil::get_CF_string(ydim_path);
6303 string xdim_path = "XDim/"+eos2_grid.grid_name;
6304 xdim_path = HDFCFUtil::get_CF_string(xdim_path);
6305
6306 int32 proj_code = eos2_grid_info.projcode;
6307 if (proj_code == GCTP_SNSOID || proj_code == GCTP_PS || proj_code == GCTP_LAMAZ) {
6308 // Add grid_mapping dummy variable
6309 add_dummy_grid_cv(d4_grp, eos2_grid, eos2_grid_info);
6310 // Add 1-D CF grid coordinates
6311 add_CF_1D_cvs(d4_grp,root_grp,eos2_grid,eos2_grid_info,xdim_path,ydim_path);
6312 }
6313 string lat_name ="Latitude";
6314 string lon_name ="Longitude";
6315
6316 // Array
6317 auto ar_bt_lat_unique = make_unique<Float64>(lat_name);
6318 auto ar_bt_lat = ar_bt_lat_unique.get();
6319 auto ar_lat_unique = make_unique<HDFDMRArray_EOS2LL>(filename, eos2_grid.grid_name, true, lat_name,ar_bt_lat);
6320 auto ar_lat = ar_lat_unique.release();
6321
6322 // Dimensions
6323 ar_lat->append_dim_ll(eos2_grid.ydim,ydim_path);
6324 if (!eos2_grid.oned_latlon)
6325 ar_lat->append_dim_ll(eos2_grid.xdim,xdim_path);
6326 else
6327 ar_lat->set_rank(1);
6328
6329 dims_transform_to_dap4(ar_lat,root_grp,true);
6330
6331 // Attributes
6332 add_var_dap4_attr(ar_lat,"units",attr_str_c,"degrees_north");
6333
6334 // Add eos latlon predefined attribute eos_latlon that includes the grid name and the lat/lon.
6335 string eos_latlon_value = eos2_grid.grid_name +" lat";
6336 add_var_dap4_attr(ar_lat,"eos_latlon",attr_str_c,eos_latlon_value);
6337
6338 ar_lat->set_is_dap4(true);
6339 d4_grp->add_var_nocopy(ar_lat);
6340
6341 auto ar_bt_lon_unique = make_unique<Float64>(lon_name);
6342 auto ar_bt_lon = ar_bt_lon_unique.get();
6343 auto ar_lon_unique = make_unique<HDFDMRArray_EOS2LL>(filename,eos2_grid.grid_name, false, lon_name, ar_bt_lon);
6344 auto ar_lon = ar_lon_unique.release();
6345 if (!eos2_grid.oned_latlon)
6346 ar_lon->append_dim_ll(eos2_grid.ydim,ydim_path);
6347 else
6348 ar_lon->set_rank(1);
6349 ar_lon->append_dim_ll(eos2_grid.xdim,xdim_path);
6350
6351 dims_transform_to_dap4(ar_lon,root_grp,true);
6352
6353 add_var_dap4_attr(ar_lon,"units",attr_str_c,"degrees_east");
6354
6355 // Add eos latlon predefined attribute eos_latlon that includes the grid name and the lat/lon.
6356 eos_latlon_value = eos2_grid.grid_name +" lon";
6357 add_var_dap4_attr(ar_lon,"eos_latlon",attr_str_c,eos_latlon_value);
6358
6359 ar_lon->set_is_dap4(true);
6360 d4_grp->add_var_nocopy(ar_lon);
6361
6362}
6363
6364#ifdef USE_HDFEOS2_LIB
6365bool check_eos2_grids(const string &filename, int32 sdfd, vector<eos2_grid_t>& eos2_grid_lls, vector<eos2_grid_info_t>& eos2_grids_info) {
6366
6367 bool ret_value = true;
6368 // Here we need to check if this is an HDF-EOS2 grid file.
6369 string dummy_grid_name;
6370 bool is_eos2_file = false;
6371 if (1 == check_special_eosfile(filename,dummy_grid_name,sdfd))
6372 is_eos2_file = true;
6373
6374 if (is_eos2_file) {
6375
6376 vector<string> eos2_grid_names;
6377 ret_value = HDFEOS2::Utility::ReadNamelist(filename.c_str(),GDinqgrid,eos2_grid_names);
6378 if (ret_value == false)
6379 return ret_value;
6380
6381 for (const auto& gname:eos2_grid_names) {
6382 int32 ydim = 0;
6383 int32 xdim = 0;
6384 bool one_d_ll = false;
6385 eos2_grid_info_t eg_info;
6386 ret_value = obtain_eos2_gd_ll_info(filename,gname,ydim,xdim,one_d_ll,eg_info);
6387 if (ret_value ==false)
6388 break;
6389
6390 eos2_grid_t eg;
6391 eg.oned_latlon = one_d_ll;
6392 eg.ydim = ydim;
6393 eg.xdim = xdim;
6394 eg.grid_name = gname;
6395 eos2_grid_lls.push_back(eg);
6396 eos2_grids_info.push_back(eg_info);
6397
6398 }
6399 }
6400
6401 return ret_value;
6402}
6403
6404bool obtain_eos2_gd_ll_info(const string & fname, const string & grid_name, int32 &ydim, int32 &xdim, bool &oned_ll, eos2_grid_info_t &eg_info) {
6405
6406 int32 gfid = GDopen(const_cast<char*>(fname.c_str()),DFACC_READ);
6407 if (gfid <0)
6408 return false;
6409 int32 gridid = GDattach(gfid, const_cast < char *>(grid_name.c_str ()));
6410 if (gridid <0) {
6411 GDclose(gfid);
6412 return false;
6413 }
6414
6415 /* Declare projection code, zone, etc grid parameters. */
6416 int32 projcode = -1;
6417 int32 zone = -1;
6418 int32 sphere = -1;
6419 float64 params[16];
6420 float64 upleft[2];
6421 float64 lowright[2];
6422
6423 if (GDprojinfo (gridid, &projcode, &zone, &sphere, params) == -1) {
6424 GDdetach(gridid);
6425 GDclose(gfid);
6426 return false;
6427 }
6428
6429 // Retrieve dimensions and X-Y coordinates of corners
6430 if (GDgridinfo(gridid, &xdim, &ydim, upleft,
6431 lowright) == -1) {
6432 GDdetach(gridid);
6433 GDclose(gfid);
6434 return false;
6435 }
6436
6437 if (GCTP_CEA == projcode || GCTP_GEO == projcode)
6438 oned_ll = true;
6439
6440 eg_info.projcode = projcode;
6441 eg_info.zone = zone;
6442 eg_info.sphere = sphere;
6443 for (int i = 0; i<2;i++) {
6444 eg_info.upleft[i] = upleft[i];
6445 eg_info.lowright[i] = lowright[i];
6446 }
6447 for (int i = 0; i<16;i++)
6448 eg_info.params[i] = params[i];
6449
6450 GDdetach(gridid);
6451 GDclose(gfid);
6452
6453 return true;
6454}
6455void add_dummy_grid_cv(D4Group *d4_grp, const eos2_grid_t & eos2_grid, const eos2_grid_info_t &eg_info) {
6456
6457 string dummy_proj_cf_name = "eos_cf_projection";
6458 auto dummy_proj_cf_unique = make_unique<HDFEOS2GeoCFProj>(dummy_proj_cf_name, dummy_proj_cf_name);
6459 HDFEOS2GeoCFProj *dummy_proj_cf = dummy_proj_cf_unique.get();
6460 dummy_proj_cf->set_is_dap4(true);
6461
6462 if (eg_info.projcode == GCTP_SNSOID) {
6463
6464 add_var_dap4_attr(dummy_proj_cf, "grid_mapping_name", attr_str_c, "sinusoidal");
6465 add_var_dap4_attr(dummy_proj_cf, "longitude_of_central_meridian", attr_float64_c, "0.0");
6466 add_var_dap4_attr(dummy_proj_cf, "earth_radius", attr_float64_c, "6371007.181");
6467 add_var_dap4_attr(dummy_proj_cf, "_CoordinateAxisTypes", attr_str_c, "GeoX GeoY");
6468 } else if (eg_info.projcode == GCTP_PS)
6469 add_ps_cf_grid_mapping_attrs(dummy_proj_cf, eg_info);
6470 else if (eg_info.projcode == GCTP_LAMAZ)
6471 add_lamaz_cf_grid_mapping_attrs(dummy_proj_cf, eg_info);
6472
6473 string eos_cf_grid_value = eos2_grid.grid_name + " eos_cf_projection";
6474 add_var_dap4_attr(dummy_proj_cf,"eos_cf_grid_mapping",attr_str_c,eos_cf_grid_value);
6475 d4_grp->add_var_nocopy(dummy_proj_cf_unique.release());
6476
6477}
6478
6479void add_CF_1D_cvs(D4Group *d4_grp, D4Group *root_grp, const eos2_grid_t &eos2_grid, const eos2_grid_info_t &eos2_grid_info, const string& xdim_path, const string &ydim_path) {
6480
6481 auto ar_bt_dim1_unique = make_unique<Float64>(xdim_path);
6482 auto ar_bt_dim1 = ar_bt_dim1_unique.get();
6483 auto ar_dim1_unique = make_unique<HDFEOS2GeoCF1D>(eos2_grid_info.projcode, eos2_grid_info.upleft[0], eos2_grid_info.lowright[0],
6484 eos2_grid.xdim, xdim_path, ar_bt_dim1);
6485 auto ar_dim1 = ar_dim1_unique.get();
6486 ar_dim1->append_dim_ll(eos2_grid.xdim, xdim_path);
6487 dims_transform_to_dap4(ar_dim1,root_grp,true);
6488 ar_dim1->set_is_dap4(true);
6489 add_CF_1D_cv_attrs(ar_dim1,false);
6490
6491 string eos_cf_grid_value = eos2_grid.grid_name + " XDim";
6492 add_var_dap4_attr(ar_dim1,"eos_cf_grid",attr_str_c,eos_cf_grid_value);
6493 d4_grp->add_var_nocopy(ar_dim1_unique.release());
6494
6495 auto ar_bt_dim0_unique = make_unique<Float64>(ydim_path);
6496 auto ar_bt_dim0 = ar_bt_dim0_unique.get();
6497 auto ar_dim0_unique = make_unique<HDFEOS2GeoCF1D>(eos2_grid_info.projcode,
6498 eos2_grid_info.upleft[1], eos2_grid_info.lowright[1],
6499 eos2_grid.ydim, ydim_path, ar_bt_dim0);
6500 auto ar_dim0 = ar_dim0_unique.get();
6501 ar_dim0->append_dim_ll(eos2_grid.ydim, ydim_path);
6502 dims_transform_to_dap4(ar_dim0,root_grp,true);
6503
6504 ar_dim0->set_is_dap4(true);
6505 add_CF_1D_cv_attrs(ar_dim0,true);
6506
6507 eos_cf_grid_value = eos2_grid.grid_name + " YDim";
6508 add_var_dap4_attr(ar_dim0,"eos_cf_grid",attr_str_c,eos_cf_grid_value);
6509
6510 d4_grp->add_var_nocopy(ar_dim0_unique.release());
6511
6512}
6513
6514// Direct CF to DAP4,
6515// add CF grid_mapping $attributes for the special dimension variables.
6516void add_CF_1D_cv_attrs(libdap::BaseType *var, bool is_ydim) {
6517
6518 string standard_name;
6519 string long_name;
6520 string COORAxisTypes;
6521
6522 if (true == is_ydim) {
6523 standard_name = "projection_y_coordinate";
6524 long_name = "y coordinate of projection ";
6525 COORAxisTypes = "GeoY";
6526 }
6527 else {
6528 standard_name = "projection_x_coordinate";
6529 long_name = "x coordinate of projection ";
6530 COORAxisTypes = "GeoX";
6531 }
6532
6533 add_var_dap4_attr(var,"standard_name", attr_str_c, standard_name);
6534 add_var_dap4_attr(var,"long_name", attr_str_c, long_name);
6535 add_var_dap4_attr(var,"units", attr_str_c, "meter");
6536 add_var_dap4_attr(var,"_CoordinateAxisType", attr_str_c, COORAxisTypes);
6537
6538}
6539
6540
6541void add_lamaz_cf_grid_mapping_attrs(libdap::BaseType *var, const eos2_grid_info_t & eg_info) {
6542
6543 double lon_proj_origin = EHconvAng(eg_info.params[4],HDFE_DMS_DEG);
6544 double lat_proj_origin = EHconvAng(eg_info.params[5],HDFE_DMS_DEG);
6545 double fe = eg_info.params[6];
6546 double fn = eg_info.params[7];
6547
6548 add_var_dap4_attr(var,"grid_mapping_name", attr_str_c, "lambert_azimuthal_equal_area");
6549
6550 // Here we don't use to_string since it adds trailing zeros at the end.
6551 ostringstream s_lon_proj_origin;
6552 s_lon_proj_origin << lon_proj_origin;
6553 add_var_dap4_attr(var,"longitude_of_projection_origin", attr_float64_c, s_lon_proj_origin.str());
6554
6555 ostringstream s_lat_proj_origin;
6556 s_lat_proj_origin << lat_proj_origin;
6557
6558 add_var_dap4_attr(var,"latitude_of_projection_origin", attr_float64_c, s_lat_proj_origin.str());
6559
6560 if(fe == 0.0)
6561 add_var_dap4_attr(var,"false_easting",attr_float64_c,"0.0");
6562 else {
6563 ostringstream s_fe;
6564 s_fe << fe;
6565 add_var_dap4_attr(var,"false_easting",attr_float64_c,s_fe.str());
6566 }
6567
6568 if(fn == 0.0)
6569 add_var_dap4_attr(var,"false_northing",attr_float64_c,"0.0");
6570 else {
6571 ostringstream s_fn;
6572 s_fn << fn;
6573 add_var_dap4_attr(var,"false_northing",attr_float64_c,s_fn.str());
6574 }
6575
6576 add_var_dap4_attr(var,"_CoordinateAxisTypes", attr_str_c, "GeoX GeoY");
6577}
6578
6579
6580void add_ps_cf_grid_mapping_attrs(libdap::BaseType *var, const eos2_grid_info_t & eg_info) {
6581
6582 // The following information is added according to the HDF-EOS2 user's guide and
6583 // CF 1.11 grid_mapping requirement.
6584
6585 // Longitude down below pole of map
6586 double vert_lon_pole = EHconvAng(eg_info.params[4],HDFE_DMS_DEG);
6587
6588 // Latitude of true scale
6589 double lat_true_scale = EHconvAng(eg_info.params[5],HDFE_DMS_DEG);
6590
6591 // False easting
6592 double fe = eg_info.params[6];
6593
6594 // False northing
6595 double fn = eg_info.params[7];
6596
6597 add_var_dap4_attr(var,"grid_mapping_name",attr_str_c,"polar_stereographic");
6598
6599 ostringstream s_vert_lon_pole;
6600 s_vert_lon_pole << vert_lon_pole;
6601
6602 // The following mapping is based on my best understanding. KY
6603 // CF: straight_vertical_longitude_from_pole
6604 add_var_dap4_attr(var,"straight_vertical_longitude_from_pole", attr_float64_c, s_vert_lon_pole.str());
6605 // Not using to_string here because it adds trailing zeros at the end.
6606#if 0
6607 add_var_dap4_attr(var,"straight_vertical_longitude_from_pole", attr_float64_c, to_string(vert_lon_pole));
6608#endif
6609
6610 ostringstream s_lat_true_scale;
6611 s_lat_true_scale << lat_true_scale;
6612 add_var_dap4_attr(var,"standard_parallel", attr_float64_c, s_lat_true_scale.str());
6613
6614 if(fe == 0.0)
6615 add_var_dap4_attr(var,"false_easting",attr_float64_c,"0.0");
6616 else {
6617 ostringstream s_fe;
6618 s_fe << fe;
6619 add_var_dap4_attr(var,"false_easting",attr_float64_c,s_fe.str());
6620 }
6621
6622 if(fn == 0.0)
6623 add_var_dap4_attr(var,"false_northing",attr_float64_c,"0.0");
6624 else {
6625 ostringstream s_fn;
6626 s_fn << fn;
6627 add_var_dap4_attr(var,"false_northing",attr_float64_c,s_fn.str());
6628 }
6629
6630 if(lat_true_scale >0)
6631 add_var_dap4_attr(var,"latitude_of_projection_origin",attr_float64_c,"+90.0");
6632 else
6633 add_var_dap4_attr(var, "latitude_of_projection_origin",attr_float64_c,"-90.0");
6634
6635 add_var_dap4_attr(var, "_CoordinateAxisTypes", attr_str_c, "GeoX GeoY");
6636
6637 // From CF, PS has another paramseter,
6638 // Either standard_parallel (EPSG 9829) or scale_factor_at_projection_origin (EPSG 9810)
6639 // I cannot find the corresponding paramseter from the EOS library.
6640
6641}
6642
6643
6644#endif
6645
6646bool add_sp_hdf4_info(D4Group *d4_grp, const string &filename, string &err_msg) {
6647
6648 bool ret_value = true;
6649 BESDEBUG("h4","Coming to add_sp_hdf4_info "<<endl);
6650
6651 D4Attributes *d4_attrs = d4_grp->attributes();
6652 if (d4_attrs->find("FileHeader")) {
6653 const D4Attribute *d4_attr = d4_attrs->find("GridHeader");
6654 if (d4_attr) {
6655 string d_attr_value = d4_attr->value(0);
6656 if (d_attr_value.find("Origin")!=string::npos)
6657 ret_value = add_sp_hdf4_trmm_info(d4_grp,filename,d4_attr, err_msg);
6658 }
6659 }
6660
6661 return ret_value;
6662
6663}
6664
6665
6666bool add_sp_hdf4_trmm_info(D4Group *d4_grp, const string& filename, const D4Attribute *d4_attr, string &err_msg) {
6667
6668 BESDEBUG("h4","Coming to add_sp_hdf4_trmm_info "<<endl);
6669
6670 bool ret_value = true;
6671 // Obtain the dimension names.
6672 D4Dimensions *dims = d4_grp->dims();
6673 string lat_name = "nlat";
6674 int lat_size = 0;
6675 string lon_name = "nlon";
6676 int lon_size = 0;
6677
6678 int num_dims = 0;
6679 for (D4Dimensions::D4DimensionsIter di = dims->dim_begin(), de = dims->dim_end(); di != de; ++di) {
6680 num_dims++;
6681 if ((*di)->name() == "nlat")
6682 lat_size = (int)((*di)->size());
6683 else if ((*di)->name() == "nlon")
6684 lon_size = (int)((*di)->size());
6685 }
6686
6687 if ((lat_size !=0) && (lon_size !=0) && num_dims ==2) {
6688
6689 // Retrieve lat/lon starting point and step information.
6690 string grid_header_value = d4_attr->value(0);
6691 int lat_size_gh = 0;
6692 int lon_size_gh = 0;
6693 float lat_start = 0;
6694 float lon_start = 0;
6695 float lat_res = 0;
6696 float lon_res = 0;
6697
6698 vector<char> grid_header_value_vc(grid_header_value.begin(),grid_header_value.end());
6699 HDFCFUtil::parser_trmm_v7_gridheader(grid_header_value_vc,lat_size_gh,lon_size_gh,
6700 lat_start,lon_start,lat_res,lon_res,false);
6701
6702 if(lat_size != lat_size_gh || lon_size != lon_size_gh) {
6703 err_msg = "Either the latitude size is not the same as the size retrieved from the gridheader";
6704 err_msg = " or the longitude size is not the same as the size retrieved from the gridheader\n";
6705 err_msg = "The lat_size is " + to_string(lat_size) + '\n';
6706 err_msg = "The lat_size retrieved from the header is " + to_string(lat_size_gh) + '\n';
6707 err_msg = "The lon_size is " + to_string(lon_size) + '\n';
6708 err_msg = "The lon_size retrieved from the header is " + to_string(lon_size_gh) + '\n';
6709 ret_value = false;
6710
6711 }
6712
6713 // Array
6714 auto ar_bt_lat_unique = make_unique<Float32>(lat_name);
6715 auto ar_bt_lat = ar_bt_lat_unique.get();
6716
6717 auto ar_lat_unique = make_unique<HDFDMRArray_SPLL>(filename, lat_start, lat_res, lat_name,ar_bt_lat);
6718 auto ar_lat = ar_lat_unique.release();
6719
6720#if 0
6721 auto ar_lat_unique = make_unique<HDFArray>(lat_name,filename,ar_bt_lat);
6722 auto ar_lat = ar_lat_unique.release();
6723#endif
6724
6725 // Dimensions
6726 ar_lat->append_dim_ll(lat_size,lat_name);
6727 dims_transform_to_dap4(ar_lat,d4_grp,true);
6728
6729 // Attributes
6730 add_var_dap4_attr(ar_lat,"units",attr_str_c,"degrees_north");
6731
6732#if 0
6733 // Retrieve lat/lon starting point and step information.
6734 string grid_header_value = d4_attr->value(0);
6735 int lat_size_gh = 0;
6736 int lon_size_gh = 0;
6737 float lat_start = 0;
6738 float lon_start = 0;
6739 float lat_res = 0;
6740 float lon_res = 0;
6741
6742 vector<char> grid_header_value_vc(grid_header_value.begin(),grid_header_value.end());
6743 HDFCFUtil::parser_trmm_v7_gridheader(grid_header_value_vc,lat_size_gh,lon_size_gh,
6744 lat_start,lon_start,lat_res,lon_res,false);
6745
6746 if(lat_size != lat_size_gh || lon_size != lon_size_gh) {
6747 err_msg = "Either the latitude size is not the same as the size retrieved from the gridheader";
6748 err_msg = " or the longitude size is not the same as the size retrieved from the gridheader\n";
6749 err_msg = "The lat_size is " + to_string(lat_size) + '\n';
6750 err_msg = "The lat_size retrieved from the header is " + to_string(lat_size_gh) + '\n';
6751 err_msg = "The lon_size is " + to_string(lon_size) + '\n';
6752 err_msg = "The lon_size retrieved from the header is " + to_string(lon_size_gh) + '\n';
6753 ret_value = false;
6754
6755 }
6756#endif
6757
6758 // Add special HDF4 latlon predefined attribute sp_h4_ll that includes the lat name.
6759 string sp_h4_value = "lat " + to_string(lat_start) + ' '+to_string(lat_res);
6760 add_var_dap4_attr(ar_lat,"sp_h4_ll",attr_str_c,sp_h4_value);
6761 ar_lat->set_is_dap4(true);
6762 d4_grp->add_var_nocopy(ar_lat);
6763
6764 auto ar_bt_lon_unique = make_unique<Float32>(lon_name);
6765 auto ar_bt_lon = ar_bt_lon_unique.get();
6766
6767 auto ar_lon_unique = make_unique<HDFDMRArray_SPLL>(filename, lon_start,lon_res, lon_name,ar_bt_lon);
6768 auto ar_lon = ar_lon_unique.release();
6769
6770#if 0
6771 auto ar_lon_unique = make_unique<HDFArray>(lon_name,filename,ar_bt_lon);
6772 auto ar_lon = ar_lon_unique.release();
6773#endif
6774 ar_lon->append_dim_ll(lon_size,lon_name);
6775 dims_transform_to_dap4(ar_lon,d4_grp,true);
6776
6777 add_var_dap4_attr(ar_lon,"units",attr_str_c,"degrees_east");
6778
6779 // Add special HDF4 latlon predefined attribute sp_h4_ll that includes the lon name.
6780 sp_h4_value = "lon " + to_string(lon_start) + ' '+to_string(lon_res);
6781 add_var_dap4_attr(ar_lon,"sp_h4_ll",attr_str_c,sp_h4_value);
6782 ar_lon->set_is_dap4(true);
6783 d4_grp->add_var_nocopy(ar_lon);
6784 }
6785
6786 return ret_value;
6787
6788}
6789
6790
6791void add_sp_hdf4_additional_info(D4Group *d4_grp) {
6792
6793 // variable HQprecipitation have the fillvalue -9999.9 but it doesn't have the _FillValue attribute.
6794 // Add it here.
6795 D4Group* grid_grp = d4_grp->find_child_grp("Grid");
6796 if (grid_grp) {
6797 BaseType *hq_prec = grid_grp->find_var("HQprecipitation");
6798 if (hq_prec)
6799 add_var_dap4_attr(hq_prec,"_FillValue",attr_float32_c,"-9999.9");
6800 }
6801
6802}
6803
6804void dims_transform_to_dap4(Array *ar,D4Group *root_grp, bool missing_vars) {
6805
6806 D4Dimensions *root_dims = root_grp->dims();
6807 for (Array::Dim_iter d = ar->dim_begin(), e = ar->dim_end(); d != e; ++d) {
6808 if (false == (*d).name.empty()) {
6809 D4Dimension *d4_dim = root_dims->find_dim((*d).name);
6810 if (d4_dim == nullptr) {
6811 if (missing_vars) {
6812 auto d4_dim_unique = make_unique<D4Dimension>((*d).name, (*d).size);
6813 d4_dim = d4_dim_unique.release();
6814 root_dims->add_dim_nocopy(d4_dim);
6815 (*d).dim = d4_dim;
6816 }
6817 else
6818 throw InternalErr(__FILE__,__LINE__, "DAP4 Dimension name should be found. ");
6819 }
6820 else {
6821 (*d).dim = d4_dim;
6822 }
6823 }
6824
6825 }
6826
6827}
6828
This class provides a way to map HDF4 1-D character array to DAP Str for the CF option.
This class provides a way to map HDFEOS2 character >1D array to DAP Str array for the CF option.
This class provides a way to map HDFEOS2 1-D character array to DAP Str for the CF option.
const char * what() const override
Return exception message.
Definition HDFSP.h:107
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
int32 getRank() const
Get the dimension rank of this field.
Definition HDFSP.h:288
const std::string & getName() const
Get the name of this field.
Definition HDFSP.h:276
static File * Read(const char *path, int32 sdid, int32 fileid)
Retrieve SDS and Vdata information from the HDF4 file.
Definition HDFSP.cc:191
void Prepare()
Definition HDFSP.cc:3839
const std::vector< VDATA * > & getVDATAs() const
Public interface to Obtain Vdata.
Definition HDFSP.h:727
const std::vector< AttrContainer * > & getVgattrs() const
Get attributes for all vgroups.
Definition HDFSP.h:733
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
static File * Read_Hybrid(const char *path, int32 sdid, int32 fileid)
Definition HDFSP.cc:234
SPType getSPType() const
Obtain special HDF4 product type.
Definition HDFSP.h:699
One instance of this class represents one SDS object.
Definition HDFSP.h:331
const std::vector< Dimension * > & getCorrectedDimensions() const
Get the list of the corrected dimensions.
Definition HDFSP.h:338
const std::vector< Dimension * > & getDimensions() const
Get the list of dimensions.
Definition HDFSP.h:392
bool IsDimNoScale() const
Is this field a dimension without dimension scale(or empty[no data]dimension variable)
Definition HDFSP.h:405
This class retrieves all SDS objects and SD file attributes.
Definition HDFSP.h:532
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
One instance of this class represents one Vdata field.
Definition HDFSP.h:480
int32 getFieldOrder() const
Get the order of this field.
Definition HDFSP.h:486
Definition HE2CF.h:54
bool open(const std::string &filename, const int sd_id, const int file_id)
openes \afilename HDF4 file.
Definition HE2CF.cc:943
string get_metadata(const std::string &metadataname, bool &suffix_is_num, std::vector< std::string > &non_num_names, std::vector< std::string > &non_num_data)
retrieves the merged metadata.
Definition HE2CF.cc:936
bool write_attribute(const std::string &gname, const std::string &fname, const std::string &newfname, int n_groups, int fieldtype)
Definition HE2CF.cc:973
bool write_attribute_coordinates(const std::string &varname, const std::string &coord)
Definition HE2CF.cc:1134
void set_DAS(libdap::DAS *das)
sets DAS pointer so that we can bulid attribute tables.
Definition HE2CF.cc:181
bool close()
closes the opened file.
Definition HE2CF.cc:920
bool write_attribute_units(const std::string &varname, const std::string &units)
Definition HE2CF.cc:1147
bool write_attribute_FillValue(const std::string &varname, int type, float val)
Definition HE2CF.cc:1040
void get_value(const std::string &s, std::string &val, bool &found)
Retrieve the value of a given key, if set.
static TheBESKeys * TheKeys()
Access to the singleton.
Definition TheBESKeys.cc:85
STL iterator class.
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 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 correct_fvalue_type(libdap::AttrTable *at, int32 dtype)
Definition HDFCFUtil.cc:477