bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
HDFEOS2.cc
1
2// This file is part of the hdf4 data handler for the OPeNDAP data server.
3//
4// Authors: Kent Yang <myang6@hdfgroup.org> Choonghwan Lee
5// Copyright (c) The HDF Group
7
8#ifdef USE_HDFEOS2_LIB
9
10#include <sstream>
11#include <algorithm>
12#include <functional>
13#include <vector>
14#include <map>
15#include <set>
16#include <cfloat>
17#include <math.h>
18#include <sys/stat.h>
19
20#include "HDFCFUtil.h"
21#include "HDFEOS2.h"
22#include "HDF4RequestHandler.h"
23
24// Names to be searched by
25// get_geodim_x_name()
26// get_geodim_y_name()
27// get_latfield_name()
28// get_lonfield_name()
29// get_geogrid_name()
30
31// Possible XDim names
32const char *HDFEOS2::File::_geodim_x_names[] = {"XDim", "LonDim","nlon"};
33
34// Possible YDim names.
35const char *HDFEOS2::File::_geodim_y_names[] = {"YDim", "LatDim","nlat"};
36
37// Possible latitude names.
38const char *HDFEOS2::File::_latfield_names[] = {
39 "Latitude", "Lat","YDim", "LatCenter"
40};
41
42// Possible longitude names.
43const char *HDFEOS2::File::_lonfield_names[] = {
44 "Longitude", "Lon","XDim", "LonCenter"
45};
46
47// For some grid products, latitude and longitude are put under a special geogrid.
48// One possible name is "location".
49const char *HDFEOS2::File::_geogrid_names[] = {"location"};
50
51using namespace HDFEOS2;
52using namespace std;
53
54// The following is for generating exception messages.
55template<typename T, typename U, typename V, typename W, typename X>
56static void _throw5(const char *fname, int line, int numarg,
57 const T &a1, const U &a2, const V &a3, const W &a4,
58 const X &a5)
59{
61 ss << fname << ":" << line << ":";
62 for (int i = 0; i < numarg; ++i) {
63 ss << " ";
64 switch (i) {
65 case 0: ss << a1; break;
66 case 1: ss << a2; break;
67 case 2: ss << a3; break;
68 case 3: ss << a4; break;
69 case 4: ss << a5; break;
70 default:
71 ss<<" Argument number is beyond 5 ";
72 }
73 }
74 throw Exception(ss.str());
75}
76
78// number of arguments.
80#define throw1(a1) _throw5(__FILE__, __LINE__, 1, a1, 0, 0, 0, 0)
81#define throw2(a1, a2) _throw5(__FILE__, __LINE__, 2, a1, a2, 0, 0, 0)
82#define throw3(a1, a2, a3) _throw5(__FILE__, __LINE__, 3, a1, a2, a3, 0, 0)
83#define throw4(a1, a2, a3, a4) _throw5(__FILE__, __LINE__, 4, a1, a2, a3, a4, 0)
84#define throw5(a1, a2, a3, a4, a5) _throw5(__FILE__, __LINE__, 5, a1, a2, a3, a4, a5)
85
86#define assert_throw0(e) do { if (!(e)) throw1("assertion failure"); } while (false)
87#define assert_range_throw0(e, ge, l) assert_throw0((ge) <= (e) && (e) < (l))
88
89// Convenient function used in destructors.
90struct delete_elem
91{
92 template<typename T> void operator()(T *ptr)
93 {
94 delete ptr;
95 }
96};
97
98// Destructor for class File.
99File::~File()
100{
101 if (gridfd !=-1) {
102 for (auto i:grids)
103 delete i;
104 // Grid file IDs will be closed in HDF4RequestHandler.cc.
105 }
106
107 if (swathfd !=-1) {
108 for (auto i:swaths)
109 delete i;
110 }
111
112}
113
115File * File::Read(const char *path, int32 mygridfd, int32 myswathfd)
116{
117
118 auto file = new File(path);
119 if (file == nullptr)
120 throw1("Memory allocation for file class failed. ");
121
122 file->gridfd = mygridfd;
123 file->swathfd = myswathfd;
124
125 vector<string> gridlist;
126 if (!Utility::ReadNamelist(file->path.c_str(), GDinqgrid, gridlist)) {
127 delete file;
128 throw1("Grid ReadNamelist failed.");
129 }
130
131 try {
132 for (const auto &grid: gridlist)
133 file->grids.push_back(GridDataset::Read(file->gridfd, grid));
134 }
135 catch(...) {
136 delete file;
137 throw1("GridDataset Read failed");
138 }
139
140 vector<string> swathlist;
141 if (!Utility::ReadNamelist(file->path.c_str(), SWinqswath, swathlist)){
142 delete file;
143 throw1("Swath ReadNamelist failed.");
144 }
145
146 try {
147 for (const auto &swath:swathlist)
148 file->swaths.push_back(SwathDataset::Read(file->swathfd, swath));
149 }
150 catch(...) {
151 delete file;
152 throw1("SwathDataset Read failed.");
153 }
154
155
156 // We only obtain the name list of point objects but not don't provide
157 // other information of these objects.
158 // The client will only get the name list of point objects.
159 vector<string> pointlist;
160 if (!Utility::ReadNamelist(file->path.c_str(), PTinqpoint, pointlist)){
161 delete file;
162 throw1("Point ReadNamelist failed.");
163 }
164
165 //See if I can make coverity happy because it doesn't understand throw macro.
166 for (const auto&point: pointlist)
167 file->points.push_back(PointDataset::Read(-1, point));
168
169 // If this is not an HDF-EOS2 file, returns exception as false.
170 if (file->grids.empty() && file->swaths.empty()
171 && file->points.empty()) {
172 Exception e("Not an HDF-EOS2 file");
173 e.setFileType(false);
174 delete file;
175 throw e;
176 }
177 return file;
178}
179
180
181// A grid's X-dimension can have different names: XDim, LatDim, etc.
182// This function returns the name of X-dimension which is used in the given file.
183// For better performance, we check the first grid or swath only.
184string File::get_geodim_x_name()
185{
186 if (!_geodim_x_name.empty())
187 return _geodim_x_name;
188 _find_geodim_names();
189 return _geodim_x_name;
190}
191
192// A grid's Y-dimension can have different names: YDim, LonDim, etc.
193// This function returns the name of Y-dimension which is used in the given file.
194// For better performance, we check the first grid or swath only.
195string File::get_geodim_y_name()
196{
197 if (!_geodim_y_name.empty())
198 return _geodim_y_name;
199 _find_geodim_names();
200 return _geodim_y_name;
201}
202
203// In some cases, values of latitude and longitude are stored in data fields.
204// Since the latitude field and longitude field do not have unique names
205// (e.g., latitude field can be "latitude", "Lat", ...),
206// we need to retrieve the field name.
207// The following two functions does this job.
208// For better performance, we check the first grid or swath only.
209
210string File::get_latfield_name()
211{
212 if (!_latfield_name.empty())
213 return _latfield_name;
214 _find_latlonfield_names();
215 return _latfield_name;
216}
217
218string File::get_lonfield_name()
219{
220 if (!_lonfield_name.empty())
221 return _lonfield_name;
222 _find_latlonfield_names();
223 return _lonfield_name;
224}
225
226// In some cases, a dedicated grid is used to store the values of
227// latitude and longitude. The following function finds the name
228// of the geo grid.
229
230string File::get_geogrid_name()
231{
232 if (!_geogrid_name.empty())
233 return _geogrid_name;
234 _find_geogrid_name();
235 return _geogrid_name;
236}
237
238// Internal function used by
239// get_geodim_x_name and get_geodim_y_name functions.
240// This function is not intended to be used outside the
241// get_geodim_x_name and get_geodim_y_name functions.
242
243void File::_find_geodim_names()
244{
245 set<string> geodim_x_name_set;
246 for(size_t i = 0; i<sizeof(_geodim_x_names) / sizeof(const char *); i++)
247 geodim_x_name_set.emplace(_geodim_x_names[i]);
248
249 set<string> geodim_y_name_set;
250 for (size_t i = 0; i<sizeof(_geodim_y_names) / sizeof(const char *); i++)
251 geodim_y_name_set.emplace(_geodim_y_names[i]);
252
253 const size_t gs = grids.size();
254 // For performance, we're checking this for the first grid
255 if (gs >0)
256 {
257 const Dataset *dataset=nullptr;
258 dataset = static_cast<Dataset*>(grids[0]);
259
260 const vector<Dimension *>& dims = dataset->getDimensions();
261 for (const auto &dim:dims)
262 {
263 // Essentially this code will grab any dimension names that is
264 // NOT predefined "XDim","LonDim","nlon" for geodim_x_name;
265 // any dimension names that is NOT predefined "YDim","LatDim","nlat"
266 // for geodim_y_name. This is in theory not right. Given the
267 // fact that this works with the current HDF-EOS2 products and there
268 // will be no more HDF-EOS2 products. We will leave the code this way.
269 if (geodim_x_name_set.find(dim->getName()) != geodim_x_name_set.end())
270 _geodim_x_name = dim->getName();
271 else if (geodim_y_name_set.find(dim->getName()) != geodim_y_name_set.end())
272 _geodim_y_name = dim->getName();
273 }
274 }
275 if (_geodim_x_name.empty())
276 _geodim_x_name = _geodim_x_names[0];
277 if (_geodim_y_name.empty())
278 _geodim_y_name = _geodim_y_names[0];
279}
280
281// Internal function used by
282// get_latfield_name and get_lonfield_name functions.
283// This function is not intended to be used outside
284// the get_latfield_name and get_lonfield_name functions.
285
286void File::_find_latlonfield_names()
287{
288 set<string> latfield_name_set;
289 for(size_t i = 0; i<sizeof(_latfield_names) / sizeof(const char *); i++)
290 latfield_name_set.emplace(_latfield_names[i]);
291
292 set<string> lonfield_name_set;
293 for(size_t i = 0; i<sizeof(_lonfield_names) / sizeof(const char *); i++)
294 lonfield_name_set.emplace(_lonfield_names[i]);
295
296 const size_t gs = grids.size();
297 const size_t ss = swaths.size();
298 for(size_t i=0;i<1 ;i++)
299 {
300 const Dataset *dataset = nullptr;
301 SwathDataset *sw = nullptr;
302 if (i<gs)
303 dataset = static_cast<Dataset*>(grids[i]);
304 else if (i < gs + ss)
305 {
306 sw = swaths[i-gs];
307 dataset = static_cast<Dataset*>(sw);
308 }
309 else
310 break;
311
312 const vector<Field *>& fields = dataset->getDataFields();
313 for (const auto &field:fields)
314 {
315 if (latfield_name_set.find(field->getName()) != latfield_name_set.end())
316 _latfield_name = field->getName();
317 else if (lonfield_name_set.find(field->getName()) != lonfield_name_set.end())
318 _lonfield_name = field->getName();
319 }
320
321 if (sw)
322 {
323 const vector<Field *>& geofields = dataset->getDataFields();
324 for(const auto &gfield:geofields)
325 {
326 if (latfield_name_set.find(gfield->getName()) != latfield_name_set.end())
327 _latfield_name = gfield->getName();
328 else if (lonfield_name_set.find(gfield->getName()) != lonfield_name_set.end())
329 _lonfield_name = gfield->getName();
330 }
331 }
332 }
333 if (_latfield_name.empty())
334 _latfield_name = _latfield_names[0];
335 if (_lonfield_name.empty())
336 _lonfield_name = _lonfield_names[0];
337
338}
339
340// Internal function used by
341// the get_geogrid_name function.
342// This function is not intended to be used outside the get_geogrid_name function.
343
344void File::_find_geogrid_name()
345{
346 set<string> geogrid_name_set;
347 for(size_t i = 0; i<sizeof(_geogrid_names) / sizeof(const char *); i++)
348 geogrid_name_set.emplace(_geogrid_names[i]);
349
350 const size_t gs = grids.size();
351 const size_t ss = swaths.size();
352 for(size_t i=0; ;i++)
353 {
354 const Dataset *dataset = nullptr;
355 if (i<gs)
356 dataset = static_cast<Dataset*>(grids[i]);
357 else if (i < gs + ss)
358 dataset = static_cast<Dataset*>(swaths[i-gs]);
359 else
360 break;
361
362 if (geogrid_name_set.find(dataset->getName()) != geogrid_name_set.end())
363 _geogrid_name = dataset->getName();
364 }
365 if (_geogrid_name.empty())
366 _geogrid_name = "location";
367}
368
369// Check if we have the dedicated lat/lon grid.
370void File::check_onelatlon_grids() {
371
372 // 0. obtain "Latitude","Longitude" and "location" set.
373 string LATFIELDNAME = this->get_latfield_name();
374 string LONFIELDNAME = this->get_lonfield_name();
375
376 // Now only there is only one geo grid name "location", so don't call it know.
377 string GEOGRIDNAME = "location";
378
379 //only one lat/lon and it is under GEOGRIDNAME
380 int onellcount = 0;
381
382 // Check if lat/lon is found under other grids.
383 int morellcount = 0;
384
385 // Loop through all grids
386 for (const auto &grid:grids){
387
388 // Loop through all fields
389 for (const auto &field:grid->getDataFields()) {
390 if (grid->getName()==GEOGRIDNAME){
391 if (field->getName()==LATFIELDNAME){
392 onellcount++;
393 grid->latfield = field;
394 }
395 if (field->getName()==LONFIELDNAME){
396 onellcount++;
397 grid->lonfield = field;
398 }
399 if (onellcount == 2)
400 break;//Finish this grid
401 }
402 else {// Here we assume that lat and lon are always in pairs.
403 if ((field->getName()==LATFIELDNAME)||(field->getName()==LONFIELDNAME)){
404 grid->ownllflag = true;
405 morellcount++;
406 break;
407 }
408 }
409 }
410 }
411
412 if (morellcount ==0 && onellcount ==2)
413 this->onelatlon = true;
414}
415
416// For one grid, need to handle the third-dimension(both existing and missing) coordinate variables
417void File::handle_one_grid_zdim(GridDataset* gdset) {
418
419 // Obtain "XDim","YDim"
420 string DIMXNAME = this->get_geodim_x_name();
421 string DIMYNAME = this->get_geodim_y_name();
422
423 bool missingfield_unlim_flag = false;
424 const Field *missingfield_unlim = nullptr;
425
426 // This is a big assumption, it may be wrong since not every 1-D field
427 // with the third dimension(name and size) is a coordinate
428 // variable. We have to watch the products we've supported. KY 2012-6-13
429
430 // Unique 1-D field's dimension name list.
431 set<string> tempdimlist;
432 pair<set<string>::iterator,bool> tempdimret;
433
434 for (const auto &field:gdset->getDataFields()) {
435 //We only need to search those 1-D fields
436
437 if (field->getRank()==1){
438
439 // DIMXNAME and DIMYNAME correspond to latitude and longitude.
440 // They should NOT be treated as dimension names missing fields. It will be handled differently.
441 if ((field->getDimensions())[0]->getName()!=DIMXNAME && (field->getDimensions())[0]->getName()!=DIMYNAME){
442
443 tempdimret = tempdimlist.insert((field->getDimensions())[0]->getName());
444
445 // Kent: The following implementation may not be always right. This essentially is the flaw of the
446 // data product if a file encounters this case. Only unique 1-D third-dimension field should be provided.
447 // Only pick up the first 1-D field that the third-dimension
448 if (tempdimret.second == true) {
449
450 HDFCFUtil::insert_map(gdset->dimcvarlist, (field->getDimensions())[0]->getName(),
451 field->getName());
452 field->fieldtype = 3;
453 if (field->getName() == "Time")
454 field->fieldtype = 5;// IDV can handle 4-D fields when the 4th dim is Time.
455 }
456 }
457 }
458 }
459
460 // Add the missing Z-dimension field.
461 // Some dimension name's corresponding fields are missing,
462 // so add the missing Z-dimension fields based on the dimension names. When the real
463 // data is read, nature number 1,2,3,.... will be filled!
464 // NOTE: The latitude and longitude dim names are not handled yet.
465
466 // The above handling is also based on a big assumption. This is the best the
467 // handler can do without having the extra information outside the HDF-EOS2 file. KY 2012-6-12
468 // Loop through all dimensions of this grid.
469 for (const auto &gdim:gdset->getDimensions()) {
470
471 // Don't handle DIMXNAME and DIMYNAME yet.
472 if (gdim->getName()!=DIMXNAME && gdim->getName()!=DIMYNAME){
473
474 // This dimension needs a field
475 if ((tempdimlist.find(gdim->getName())) == tempdimlist.end()){
476
477 // Need to create a new data field vector element with the name and dimension as above.
478 auto missingfield = new Field();
479 missingfield->name = gdim->getName();
480 missingfield->rank = 1;
481
482 //This is an HDF constant.the data type is always integer.
483 missingfield->type = DFNT_INT32;
484
485 // Dimension of the missing field
486 auto dim = new Dimension(gdim->getName(),gdim->getSize());
487
488 // only 1 dimension
489 missingfield->dims.push_back(dim);
490
491 if (0 == gdim->getSize()) {
492 missingfield_unlim_flag = true;
493 missingfield_unlim = missingfield;
494 }
495
496 // Provide information for the missing data, since we need to calculate the data, so
497 // the information is different than a normal field.
498
499 // added Z-dimension coordinate variable with nature number
500 missingfield->fieldtype = 4;
501
502 // input data is empty now. We need to review this approach in the future.
503 // The data will be retrieved in HDFEOS2ArrayMissGeoField.cc. KY 2013-06-14
504 gdset->datafields.push_back(missingfield);
505 HDFCFUtil::insert_map(gdset->dimcvarlist, (missingfield->getDimensions())[0]->getName(),
506 missingfield->name);
507
508 }
509 }
510 }
511
512 //Correct the unlimited dimension size.
513 bool temp_missingfield_unlim_flag = missingfield_unlim_flag;
514 if (true == temp_missingfield_unlim_flag) {
515 for (unsigned int i =0; i<gdset->getDataFields().size(); i++) {
516
517 for (const auto &gdim:gdset->getDimensions()) {
518
519 if (gdim->getName() == (missingfield_unlim->getDimensions())[0]->getName()) {
520 if (gdim->getSize()!= 0) {
521 Dimension *dim = missingfield_unlim->getDimensions()[0];
522
523 // The unlimited dimension size is updated.
524 dim->dimsize = gdim->getSize();
525 missingfield_unlim_flag = false;
526 break;
527 }
528 }
529
530 }
531 if (false == missingfield_unlim_flag)
532 break;
533 }
534 }
535
536}
537
538// For one grid, need to handle lat/lon(both existing lat/lon and calculated lat/lon from EOS2 APIs)
539void File::handle_one_grid_latlon(GridDataset* gdset)
540{
541
542 // Obtain "XDim","YDim","Latitude","Longitude"
543 string DIMXNAME = this->get_geodim_x_name();
544 string DIMYNAME = this->get_geodim_y_name();
545
546 string LATFIELDNAME = this->get_latfield_name();
547 string LONFIELDNAME = this->get_lonfield_name();
548
549
550 // This grid has its own latitude/longitude
551 if (gdset->ownllflag) {
552
553 // Searching the lat/lon field from the grid.
554 for (const auto &field:gdset->getDataFields()) {
555
556 if (field->getName() == LATFIELDNAME) {
557
558 // set the flag to tell if this is lat or lon.
559 // The unit will be different for lat and lon.
560 field->fieldtype = 1;
561
562 // Latitude rank should not be greater than 2.
563 // Here I don't check if the rank of latitude is the same as the longitude.
564 // Hopefully it never happens for HDF-EOS2 cases.
565 // We are still investigating if Java clients work
566 // when the rank of latitude and longitude is greater than 2.
567 if (field->getRank() > 2)
568 throw3("The rank of latitude is greater than 2",
569 gdset->getName(),field->getName());
570
571 if (field->getRank() != 1) {
572
573 // Obtain the major dim. For most cases, it is YDim Major.
574 // But still need to watch out the rare cases.
575 field->ydimmajor = gdset->getCalculated().isYDimMajor();
576
577 // If the 2-D lat/lon can be condensed to 1-D.
578 // For current HDF-EOS2 files, only GEO and CEA can be condensed.
579 // To gain performance,
580 // we don't check the real latitude values.
581 int32 projectioncode = gdset->getProjection().getCode();
582 if (projectioncode == GCTP_GEO || projectioncode ==GCTP_CEA) {
583 field->condenseddim = true;
584 }
585
586 // Now we want to handle the dim and the var lists.
587 // If the lat/lon can be condensed to 1-D array,
588 // COARD convention needs to be followed.
589 // Since COARD requires that the dimension name of lat/lon is the same as the field name of lat/lon,
590 // we still need to handle this case in the later step(in function handle_grid_coards).
591 // Regardless of dimension major, always lat->YDim, lon->XDim;
592 // We don't need to adjust the dimension rank.
593 for (const auto &dim:field->getDimensions()) {
594 if (dim->getName() == DIMYNAME)
595 HDFCFUtil::insert_map(gdset->dimcvarlist, dim->getName(), field->getName());
596 }
597 }
598 // This is the 1-D case, just inserting the dimension, field pair.
599 else {
600 HDFCFUtil::insert_map(gdset->dimcvarlist, ((field->getDimensions())[0])->getName(),
601 field->getName());
602 }
603 }
604 else if (field->getName() == LONFIELDNAME) {
605
606 // set the flag to tell if this is lat or lon. The unit will be different for lat and lon.
607 field->fieldtype = 2;
608
609 // longitude rank should not be greater than 2.
610 // Here I don't check if the rank of latitude and longitude is the same.
611 // Hopefully it never happens for HDF-EOS2 cases.
612 // We are still investigating if Java clients work when the rank of latitude and longitude is greater than 2.
613 if (field->getRank() >2)
614 throw3("The rank of Longitude is greater than 2",gdset->getName(),field->getName());
615
616 if (field->getRank() != 1) {
617
618 // Obtain the major dim. For most cases, it is YDim Major. But still need to check for rare cases.
619 field->ydimmajor = gdset->getCalculated().isYDimMajor();
620
621 // If the 2-D lat/lon can be condensed to 1-D.
622 //For current HDF-EOS2 files, only GEO and CEA can be condensed. To gain performance,
623 // we don't check with real values.
624 int32 projectioncode = gdset->getProjection().getCode();
625 if (projectioncode == GCTP_GEO || projectioncode ==GCTP_CEA) {
626 field->condenseddim = true;
627 }
628
629 // Now we want to handle the dim and the var lists.
630 // If the lat/lon can be condensed to 1-D array, COARD convention needs to be followed.
631 // Since COARD requires that the dimension name of lat/lon is the same as the field name of lat/lon,
632 // we still need to handle this case at last.
633 // When the field can be condensed, regardless of dimension major, the EOS convention is always lat->YDim, lon->XDim;
634 // We don't need to adjust the dimension rank.
635 // For 2-D lat/lon case: since dimension order doesn't matter, so we always assume lon->XDim, lat->YDim.
636 for (const auto &dim:field->getDimensions()) {
637 if (dim->getName() == DIMXNAME)
638 HDFCFUtil::insert_map(gdset->dimcvarlist, dim->getName(), field->getName());
639 }
640 }
641 // This is the 1-D case, just inserting the dimension, field pair.
642 else {
643 HDFCFUtil::insert_map(gdset->dimcvarlist, ((field->getDimensions())[0])->getName(),
644 field->getName());
645 }
646 }
647 }
648 }
649 else { // this grid's lat/lon has to be calculated.
650
651 // Latitude and Longitude
652 auto latfield = new Field();
653 auto lonfield = new Field();
654
655 latfield->name = LATFIELDNAME;
656 lonfield->name = LONFIELDNAME;
657
658 // lat/lon is a 2-D array
659 latfield->rank = 2;
660 lonfield->rank = 2;
661
662 // The data type is always float64. DFNT_FLOAT64 is the equivalent float64 type.
663 latfield->type = DFNT_FLOAT64;
664 lonfield->type = DFNT_FLOAT64;
665
666 // Latitude's fieldtype is 1
667 latfield->fieldtype = 1;
668
669 // Longitude's fieldtype is 2
670 lonfield->fieldtype = 2;
671
672 // Check if YDim is the major order.
673 // Obtain the major dim. For most cases, it is YDim Major. But some cases may be not. Still need to check.
674 latfield->ydimmajor = gdset->getCalculated().isYDimMajor();
675 lonfield->ydimmajor = latfield->ydimmajor;
676
677 // Obtain XDim and YDim size.
678 int xdimsize = gdset->getInfo().getX();
679 int ydimsize = gdset->getInfo().getY();
680
681 // Add dimensions. If it is YDim major,the dimension name list is "YDim XDim", otherwise, it is "XDim YDim".
682 // For LAMAZ projection, Y dimension is always supposed to be major for calculating lat/lon,
683 // but for dimension order, it should be consistent with data fields. (LD -2012/01/16
684 bool dmajor=(gdset->getProjection().getCode()==GCTP_LAMAZ)? gdset->getCalculated().DetectFieldMajorDimension()
685 : latfield->ydimmajor;
686
687 if (dmajor) {
688 auto dimlaty = new Dimension(DIMYNAME,ydimsize);
689 latfield->dims.push_back(dimlaty);
690 auto dimlony = new Dimension(DIMYNAME,ydimsize);
691 lonfield->dims.push_back(dimlony);
692 auto dimlatx = new Dimension(DIMXNAME,xdimsize);
693 latfield->dims.push_back(dimlatx);
694 auto dimlonx = new Dimension(DIMXNAME,xdimsize);
695 lonfield->dims.push_back(dimlonx);
696 }
697 else {
698 auto dimlatx = new Dimension(DIMXNAME,xdimsize);
699 latfield->dims.push_back(dimlatx);
700 auto dimlonx = new Dimension(DIMXNAME,xdimsize);
701 lonfield->dims.push_back(dimlonx);
702 auto dimlaty = new Dimension(DIMYNAME,ydimsize);
703 latfield->dims.push_back(dimlaty);
704 auto dimlony = new Dimension(DIMYNAME,ydimsize);
705 lonfield->dims.push_back(dimlony);
706 }
707
708 // Obtain info upleft and lower right for special longitude.
709
710 const float64* upleft = gdset->getInfo().getUpLeft();
711 const float64* lowright = gdset->getInfo().getLowRight();
712
713 // SOme special longitude is from 0 to 360.We need to check this case.
714 int32 projectioncode = gdset->getProjection().getCode();
715 if (((int)lowright[0]>180000000) && ((int)upleft[0]>-1)) {
716 // We can only handle geographic projection now.
717 // This is the only case we can handle.
718 if (projectioncode == GCTP_GEO) {// Will handle when data is read.
719 lonfield->speciallon = true;
720 // When HDF-EOS2 cache is involved, we have to also set the
721 // speciallon flag for the latfield since the cache file
722 // includes both lat and lon fields, and even the request
723 // is only to generate the lat field, the lon field also needs to
724 // be updated to write the proper cache. KY 2016-03-16
725 if (HDF4RequestHandler::get_enable_eosgeo_cachefile() == true)
726 latfield->speciallon = true;
727 }
728 }
729
730 // Some MODIS MCD files don't follow standard format for lat/lon (DDDMMMSSS);
731 // they simply represent lat/lon as -180.0000000 or -90.000000.
732 // HDF-EOS2 library won't give the correct value based on these value.
733 // These need to be remembered and resumed to the correct format when retrieving the data.
734 // Since so far we haven't found region of satellite files is within 0.1666 degree(1 minute)
735 // so, we divide the corner coordinate by 1000 and see if the integral part is 0.
736 // If it is 0, we know this file uses special lat/lon coordinate.
737
738 if (((int)(lowright[0]/1000)==0) &&((int)(upleft[0]/1000)==0)
739 && ((int)(upleft[1]/1000)==0) && ((int)(lowright[1]/1000)==0)) {
740 if (projectioncode == GCTP_GEO){
741 lonfield->specialformat = 1;
742 latfield->specialformat = 1;
743 }
744 }
745
746 // Some TRMM CERES Grid Data have "default" to be set for the corner coordinate,
747 // which they really mean for the whole globe(-180 - 180 lon and -90 - 90 lat).
748 // We will remember the information and change
749 // those values when we read the lat and lon.
750
751 if (((int)(lowright[0])==0) &&((int)(upleft[0])==0)
752 && ((int)(upleft[1])==0) && ((int)(lowright[1])==0)) {
753 if (projectioncode == GCTP_GEO){
754 lonfield->specialformat = 2;
755 latfield->specialformat = 2;
756 gdset->addfvalueattr = true;
757 }
758 }
759
760 //One MOD13C2 file doesn't provide projection code
761 // The upperleft and lowerright coordinates are all -1
762 // We have to calculate lat/lon by ourselves.
763 // Since it doesn't provide the project code, we double check their information
764 // and find that it covers the whole globe with 0.05 degree resolution.
765 // Lat. is from 90 to -90 and Lon is from -180 to 180.
766
767 if (((int)(lowright[0])==-1) &&((int)(upleft[0])==-1)
768 && ((int)(upleft[1])==-1) && ((int)(lowright[1])==-1)) {
769 lonfield->specialformat = 3;
770 latfield->specialformat = 3;
771 lonfield->condenseddim = true;
772 latfield->condenseddim = true;
773 }
774
775
776 // We need to handle SOM projection in a different way.
777 if (GCTP_SOM == projectioncode) {
778 lonfield->specialformat = 4;
779 latfield->specialformat = 4;
780 }
781
782 // Check if the 2-D lat/lon can be condensed to 1-D.
783 //For current HDF-EOS2 files, only GEO and CEA can be condensed. To gain performance,
784 // we just check the projection code, don't check with real values.
785 if (projectioncode == GCTP_GEO || projectioncode ==GCTP_CEA) {
786 lonfield->condenseddim = true;
787 latfield->condenseddim = true;
788 }
789
790 // Add latitude and longitude fields to the field list.
791 gdset->datafields.push_back(latfield);
792 gdset->datafields.push_back(lonfield);
793
794 // Now we want to handle the dim and the var lists.
795 // If the lat/lon can be condensed to 1-D array, COARD convention needs to be followed.
796 // Since COARD requires that the dimension name of lat/lon is the same as the field name of lat/lon,
797 // we still need to handle this case later(function handle_grid_coards).
798
799 // There are two cases,
800 // 1) lat/lon can be condensed to 1-D array. lat to YDim, Lon to XDim, we don't need to adjust the rank.
801 // 2) 2-D lat./lon. The dimension order doesn't matter. So always assume lon to XDim, lat to YDim.
802 // So we can handle them with one loop.
803 for (const auto &dim:lonfield->getDimensions()) {
804 if (dim->getName() == DIMXNAME)
805 HDFCFUtil::insert_map(gdset->dimcvarlist, dim->getName(), lonfield->getName());
806
807 if (dim->getName() == DIMYNAME)
808 HDFCFUtil::insert_map(gdset->dimcvarlist, dim->getName(), latfield->getName());
809 }
810 }
811}
812
813// For the case of which all grids have one dedicated lat/lon grid,
814// this function shows how to handle lat/lon fields.
815void File::handle_onelatlon_grids() {
816
817 // Obtain "XDim","YDim","Latitude","Longitude" and "location" set.
818 string DIMXNAME = this->get_geodim_x_name();
819 string DIMYNAME = this->get_geodim_y_name();
820 string LATFIELDNAME = this->get_latfield_name();
821 string LONFIELDNAME = this->get_lonfield_name();
822
823 // Now only there is only one geo grid name "location", so don't call it now.
824 // string GEOGRIDNAME = this->get_geogrid_name();
825 string GEOGRIDNAME = "location";
826
827 //Dimension name and the corresponding field name when only one lat/lon is used for all grids.
828 map<string,string>temponelatlondimcvarlist;
829
830 // First we need to obtain dimcvarlist for the grid that contains lat/lon.
831 for (const auto &grid:this->grids) {
832
833 // Set the horizontal dimension name "dimxname" and "dimyname"
834 // This will be used to detect the dimension major order.
835 grid->setDimxName(DIMXNAME);
836 grid->setDimyName(DIMYNAME);
837
838 // Handle lat/lon. Note that other grids need to point to this lat/lon.
839 if (grid->getName()==GEOGRIDNAME) {
840
841 // Figure out dimension order,2D or 1D for lat/lon
842 // if lat/lon field's pointed value is changed, the value of the lat/lon field is also changed.
843 // set the flag to tell if this is lat or lon. The unit will be different for lat and lon.
844 grid->lonfield->fieldtype = 2;
845 grid->latfield->fieldtype = 1;
846
847 // latitude and longitude rank must be equal and should not be greater than 2.
848 if (grid->lonfield->rank >2 || grid->latfield->rank >2)
849 throw2("Either the rank of lat or the lon is greater than 2",grid->getName());
850 if (grid->lonfield->rank !=grid->latfield->rank)
851 throw2("The rank of the latitude is not the same as the rank of the longitude",grid->getName());
852
853 // For 2-D lat/lon arrays
854 if (grid->lonfield->rank != 1) {
855
856 // Obtain the major dim. For most cases, it is YDim Major.
857 //But for some cases it is not. So still need to check.
858 grid->lonfield->ydimmajor = grid->getCalculated().isYDimMajor();
859 grid->latfield->ydimmajor = grid->lonfield->ydimmajor;
860
861 // Check if the 2-D lat/lon can be condensed to 1-D.
862 //For current HDF-EOS2 files, only GEO and CEA can be condensed. To gain performance,
863 // we just check the projection code, don't check the real values.
864 int32 projectioncode = grid->getProjection().getCode();
865 if (projectioncode == GCTP_GEO || projectioncode ==GCTP_CEA) {
866 grid->lonfield->condenseddim = true;
867 grid->latfield->condenseddim = true;
868 }
869
870 // Now we want to handle the dim and the var lists.
871 // If the lat/lon can be condensed to 1-D array, COARD convention needs to be followed.
872 // Since COARD requires that the dimension name of lat/lon is the same as the field name of lat/lon,
873 // we still need to handle this case later(function handle_grid_coards). Now we do the first step.
874
875 // There are two cases,
876 // 1) lat/lon can be condensed to 1-D array. lat to YDim, Lon to XDim, we don't need to adjust the rank.
877 // 2) 2-D lat./lon. The dimension order doesn't matter. So always assume lon to XDim, lat to YDim.
878 // So we can handle them with one loop.
879
880 for (const auto &dim:grid->lonfield->getDimensions()) {
881 if (dim->getName() == DIMXNAME) {
882 HDFCFUtil::insert_map(grid->dimcvarlist, dim->getName(),
883 grid->lonfield->getName());
884 }
885 if (dim->getName() == DIMYNAME) {
886 HDFCFUtil::insert_map(grid->dimcvarlist, dim->getName(),
887 grid->latfield->getName());
888 }
889 }
890 }
891 else { // This is the 1-D case, just inserting the dimension, field pair.
892 HDFCFUtil::insert_map(grid->dimcvarlist, (grid->lonfield->getDimensions())[0]->getName(),
893 grid->lonfield->getName());
894 HDFCFUtil::insert_map(grid->dimcvarlist, (grid->latfield->getDimensions())[0]->getName(),
895 grid->latfield->getName());
896 }
897 temponelatlondimcvarlist = grid->dimcvarlist;
898 break;
899
900 }
901
902 }
903
904 // Now we need to assign the dim->cvar relation for lat/lon(xdim->lon,ydim->lat) to grids that don't contain lat/lon
905 for (const auto &grid:this->grids) {
906
907 string templatlonname1;
908 string templatlonname2;
909
910 if (grid->getName() != GEOGRIDNAME) {
911
913
914 // Find DIMXNAME field
915 tempmapit = temponelatlondimcvarlist.find(DIMXNAME);
916 if (tempmapit != temponelatlondimcvarlist.end())
917 templatlonname1= tempmapit->second;
918 else
919 throw2("cannot find the dimension field of XDim", grid->getName());
920
921 HDFCFUtil::insert_map(grid->dimcvarlist, DIMXNAME, templatlonname1);
922
923 // Find DIMYNAME field
924 tempmapit = temponelatlondimcvarlist.find(DIMYNAME);
925 if (tempmapit != temponelatlondimcvarlist.end())
926 templatlonname2= tempmapit->second;
927 else
928 throw2("cannot find the dimension field of YDim", grid->getName());
929 HDFCFUtil::insert_map(grid->dimcvarlist, DIMYNAME, templatlonname2);
930 }
931 }
932
933}
934
935// Handle the dimension name to coordinate variable map for grid.
936void File::handle_grid_dim_cvar_maps() {
937
938 // Obtain "XDim","YDim","Latitude","Longitude" and "location" set.
939 string DIMXNAME = this->get_geodim_x_name();
940
941 string DIMYNAME = this->get_geodim_y_name();
942
943 string LATFIELDNAME = this->get_latfield_name();
944
945 string LONFIELDNAME = this->get_lonfield_name();
946
947
948 // Now only there is only one geo grid name "location", so don't call it know.
949 // string GEOGRIDNAME = this->get_geogrid_name();
950 string GEOGRIDNAME = "location";
951
955
956 // 1. Handle name clashings
957 // 1.1 build up a temp. name list
958 // Note here: we don't include grid and swath names(simply (*j)->name) due to the products we observe
959 // Adding the grid/swath names makes the names artificially long. Will check user's feedback
960 // and may change them later. KY 2012-6-25
961 // The above assumption is purely for practical reason. Field names for all NASA multi-grid/swath products
962 // (AIRS, AMSR-E, some MODIS, MISR) can all be distinguished regardless of grid/swath names. However,
963 // this needs to be carefully watched out. KY 2013-07-08
964 vector <string> tempfieldnamelist;
965 for (const auto &grid:this->grids) {
966 for (const auto &field:grid->getDataFields())
967 tempfieldnamelist.push_back(HDFCFUtil::get_CF_string(field->name));
968 }
969 HDFCFUtil::Handle_NameClashing(tempfieldnamelist);
970
971 // 2. Create a map for dimension field name <original field name, corrected field name>
972 // Also assure the uniqueness of all field names,save the new field names.
973
974 //the original dimension field name to the corrected dimension field name
975 map<string,string>tempncvarnamelist;
976 string tempcorrectedlatname;
977 string tempcorrectedlonname;
978
979 int total_fcounter = 0;
980
981 for (const auto &grid:this->grids) {
982
983 // Here we can't use getDataFields call since for no lat/lon fields
984 // are created for one global lat/lon case. We have to use the dimcvarnamelist
985 // map we just created.
986 for (const auto &field:grid->getDataFields())
987 {
988 field->newname = tempfieldnamelist[total_fcounter];
989 total_fcounter++;
990
991 // If this field is a dimension field, save the name/new name pair.
992 if (field->fieldtype!=0) {
993
994 tempncvarnamelist.insert(make_pair(field->getName(), field->newname));
995
996 // For one latlon case, remember the corrected latitude and longitude field names.
997 if ((this->onelatlon)&&((grid->getName())==GEOGRIDNAME)) {
998 if (field->getName()==LATFIELDNAME)
999 tempcorrectedlatname = field->newname;
1000 if (field->getName()==LONFIELDNAME)
1001 tempcorrectedlonname = field->newname;
1002 }
1003 }
1004 }
1005
1006 grid->ncvarnamelist = tempncvarnamelist;
1007 tempncvarnamelist.clear();
1008 }
1009
1010 // For one lat/lon case, we have to add the lat/lon field name to other grids.
1011 // We know the original lat and lon names. So just retrieve the corrected lat/lon names from
1012 // the geo grid(GEOGRIDNAME).
1013 if (this->onelatlon) {
1014 for (const auto &grid:this->grids) {
1015 // Lat/lon names must be in this group.
1016 if (grid->getName()!=GEOGRIDNAME){
1017 HDFCFUtil::insert_map(grid->ncvarnamelist, LATFIELDNAME, tempcorrectedlatname);
1018 HDFCFUtil::insert_map(grid->ncvarnamelist, LONFIELDNAME, tempcorrectedlonname);
1019 }
1020 }
1021 }
1022
1023 // 3. Create a map for dimension name < original dimension name, corrected dimension name>
1024 map<string,string>tempndimnamelist;//the original dimension name to the corrected dimension name
1025
1027 vector <string>tempalldimnamelist;
1028 for (const auto &grid:this->grids) {
1030 grid->dimcvarlist.begin(); j!= grid->dimcvarlist.end();++j)
1031 tempalldimnamelist.push_back(HDFCFUtil::get_CF_string((*j).first));
1032 }
1033
1034 HDFCFUtil::Handle_NameClashing(tempalldimnamelist);
1035
1036 // Since DIMXNAME and DIMYNAME are not in the original dimension name list, we use the dimension name,field map
1037 // we just formed.
1038 int total_dcounter = 0;
1039 for (const auto &grid:this->grids) {
1040
1042 grid->dimcvarlist.begin(); j!= grid->dimcvarlist.end();++j){
1043
1044 // We have to handle DIMXNAME and DIMYNAME separately.
1045 if ((DIMXNAME == (*j).first || DIMYNAME == (*j).first) && (true==(this->onelatlon)))
1046 HDFCFUtil::insert_map(tempndimnamelist, (*j).first,(*j).first);
1047 else
1048 HDFCFUtil::insert_map(tempndimnamelist, (*j).first, tempalldimnamelist[total_dcounter]);
1049 total_dcounter++;
1050 }
1051
1052 grid->ndimnamelist = tempndimnamelist;
1053 tempndimnamelist.clear();
1054 }
1055}
1056
1057// Follow COARDS for grids.
1058void File::handle_grid_coards() {
1059
1060 // Obtain "XDim","YDim","Latitude","Longitude" and "location" set.
1061 string DIMXNAME = this->get_geodim_x_name();
1062 string DIMYNAME = this->get_geodim_y_name();
1063 string LATFIELDNAME = this->get_latfield_name();
1064 string LONFIELDNAME = this->get_lonfield_name();
1065
1066 // Now only there is only one geo grid name "location", so don't call it know.
1067#if 0
1068 // string GEOGRIDNAME = this->get_geogrid_name();
1069#endif
1070 string GEOGRIDNAME = "location";
1071
1072
1073 // Revisit the lat/lon fields to check if 1-D COARD convention needs to be followed.
1074 vector<Dimension*> correcteddims;
1075 string tempcorrecteddimname;
1076
1077 // grid name to the corrected latitude field name
1078 map<string,string> tempnewxdimnamelist;
1079
1080 // grid name to the corrected longitude field name
1081 map<string,string> tempnewydimnamelist;
1082
1083 // temporary dimension pointer
1084 Dimension *correcteddim;
1085
1086 for (const auto &grid:this->grids) {
1087 for (const auto &field:grid->getDataFields()) {
1088
1089 // Now handling COARD cases, since latitude/longitude can be either 1-D or 2-D array.
1090 // So we need to correct both cases.
1091 // 2-D lat to 1-D COARD lat
1092 if (field->getName()==LATFIELDNAME && field->getRank()==2 &&field->condenseddim) {
1093
1094 string templatdimname;
1096
1097 // Find the new name of LATFIELDNAME
1098 tempmapit = grid->ncvarnamelist.find(LATFIELDNAME);
1099 if (tempmapit != grid->ncvarnamelist.end())
1100 templatdimname= tempmapit->second;
1101 else
1102 throw2("cannot find the corrected field of Latitude", grid->getName());
1103
1104 for (const auto &dim:field->getDimensions()) {
1105
1106 // Since hhis is the latitude, we create the corrected dimension with the corrected latitude field name
1107 // latitude[YDIM]->latitude[latitude]
1108 if (dim->getName()==DIMYNAME) {
1109 correcteddim = new Dimension(templatdimname,dim->getSize());
1110 correcteddims.push_back(correcteddim);
1111 field->setCorrectedDimensions(correcteddims);
1112 HDFCFUtil::insert_map(tempnewydimnamelist, grid->getName(), templatdimname);
1113 }
1114 }
1115 field->iscoard = true;
1116 grid->iscoard = true;
1117 if (this->onelatlon)
1118 this->iscoard = true;
1119
1120 // Clear the local temporary vector
1121 correcteddims.clear();
1122 }
1123
1124 // 2-D lon to 1-D COARD lon
1125 else if (field->getName()==LONFIELDNAME && field->getRank()==2 &&field->condenseddim){
1126
1127 string templondimname;
1129
1130 // Find the new name of LONFIELDNAME
1131 tempmapit = grid->ncvarnamelist.find(LONFIELDNAME);
1132 if (tempmapit != grid->ncvarnamelist.end())
1133 templondimname= tempmapit->second;
1134 else
1135 throw2("cannot find the corrected field of Longitude", grid->getName());
1136
1137 for (const auto &dim:field->getDimensions()) {
1138
1139 // Since this is the longitude, we create the corrected dimension with the corrected longitude field name
1140 // longitude[XDIM]->longitude[longitude]
1141 if (dim->getName()==DIMXNAME) {
1142 correcteddim = new Dimension(templondimname,dim->getSize());
1143 correcteddims.push_back(correcteddim);
1144 field->setCorrectedDimensions(correcteddims);
1145 HDFCFUtil::insert_map(tempnewxdimnamelist, grid->getName(), templondimname);
1146 }
1147 }
1148
1149 field->iscoard = true;
1150 grid->iscoard = true;
1151 if (this->onelatlon)
1152 this->iscoard = true;
1153 correcteddims.clear();
1154 }
1155 // 1-D lon to 1-D COARD lon
1156 // (this code can be combined with the 2-D lon to 1-D lon case, should handle this later, KY 2013-07-10).
1157 else if ((field->getRank()==1) &&(field->getName()==LONFIELDNAME) ) {
1158
1159 string templondimname;
1161
1162 // Find the new name of LONFIELDNAME
1163 tempmapit = grid->ncvarnamelist.find(LONFIELDNAME);
1164 if (tempmapit != grid->ncvarnamelist.end())
1165 templondimname= tempmapit->second;
1166 else
1167 throw2("cannot find the corrected field of Longitude", grid->getName());
1168
1169 correcteddim = new Dimension(templondimname,(field->getDimensions())[0]->getSize());
1170 correcteddims.push_back(correcteddim);
1171 field->setCorrectedDimensions(correcteddims);
1172 field->iscoard = true;
1173 grid->iscoard = true;
1174 if (this->onelatlon)
1175 this->iscoard = true;
1176 correcteddims.clear();
1177
1178 if (((field->getDimensions())[0]->getName()!=DIMXNAME)
1179 &&(((field->getDimensions())[0]->getName())!=DIMYNAME)){
1180 throw3("the dimension name of longitude should not be ",
1181 (field->getDimensions())[0]->getName(),grid->getName());
1182 }
1183 if (((field->getDimensions())[0]->getName())==DIMXNAME) {
1184 HDFCFUtil::insert_map(tempnewxdimnamelist, grid->getName(), templondimname);
1185 }
1186 else {
1187 HDFCFUtil::insert_map(tempnewydimnamelist, grid->getName(), templondimname);
1188 }
1189 }
1190 // 1-D lat to 1-D COARD lat
1191 // (this case can be combined with the 2-D lat to 1-D lat case, should handle this later. KY 2013-7-10).
1192 else if ((field->getRank()==1) &&(field->getName()==LATFIELDNAME) ) {
1193
1194 string templatdimname;
1196
1197 // Find the new name of LATFIELDNAME
1198 tempmapit = grid->ncvarnamelist.find(LATFIELDNAME);
1199 if (tempmapit != grid->ncvarnamelist.end())
1200 templatdimname= tempmapit->second;
1201 else
1202 throw2("cannot find the corrected field of Latitude", grid->getName());
1203
1204 correcteddim = new Dimension(templatdimname,(field->getDimensions())[0]->getSize());
1205 correcteddims.push_back(correcteddim);
1206 field->setCorrectedDimensions(correcteddims);
1207
1208 field->iscoard = true;
1209 grid->iscoard = true;
1210 if (this->onelatlon)
1211 this->iscoard = true;
1212 correcteddims.clear();
1213
1214 if ((((field->getDimensions())[0]->getName())!=DIMXNAME)
1215 &&(((field->getDimensions())[0]->getName())!=DIMYNAME))
1216 throw3("the dimension name of latitude should not be ",
1217 (field->getDimensions())[0]->getName(),grid->getName());
1218 if (((field->getDimensions())[0]->getName())==DIMXNAME){
1219 HDFCFUtil::insert_map(tempnewxdimnamelist, grid->getName(), templatdimname);
1220 }
1221 else {
1222 HDFCFUtil::insert_map(tempnewydimnamelist, grid->getName(), templatdimname);
1223 }
1224 }
1225 }
1226 }
1227
1228 // If COARDS follows, apply the new DIMXNAME and DIMYNAME name to the ndimnamelist
1229 // One lat/lon for all grids.
1230 if (true == this->onelatlon){
1231
1232 // COARDS is followed.
1233 if (true == this->iscoard){
1234
1235 // For this case, only one pair of corrected XDim and YDim for all grids.
1236 string tempcorrectedxdimname;
1237 string tempcorrectedydimname;
1238
1239 if ((int)(tempnewxdimnamelist.size())!= 1)
1240 throw1("the corrected dimension name should have only one pair");
1241 if ((int)(tempnewydimnamelist.size())!= 1)
1242 throw1("the corrected dimension name should have only one pair");
1243
1244 map<string,string>::iterator tempdimmapit = tempnewxdimnamelist.begin();
1245 tempcorrectedxdimname = tempdimmapit->second;
1246 tempdimmapit = tempnewydimnamelist.begin();
1247 tempcorrectedydimname = tempdimmapit->second;
1248
1249 for (const auto &grid:this->grids) {
1250
1251 // Find the DIMXNAME and DIMYNAME in the dimension name list.
1253 tempmapit = grid->ndimnamelist.find(DIMXNAME);
1254 if (tempmapit != grid->ndimnamelist.end()) {
1255 HDFCFUtil::insert_map(grid->ndimnamelist, DIMXNAME, tempcorrectedxdimname);
1256 }
1257 else
1258 throw2("cannot find the corrected dimension name", grid->getName());
1259 tempmapit = grid->ndimnamelist.find(DIMYNAME);
1260 if (tempmapit != grid->ndimnamelist.end()) {
1261 HDFCFUtil::insert_map(grid->ndimnamelist, DIMYNAME, tempcorrectedydimname);
1262 }
1263 else
1264 throw2("cannot find the corrected dimension name", grid->getName());
1265 }
1266 }
1267 }
1268 else {// We have to search each grid
1269 for (const auto &grid:this->grids) {
1270
1271 if (grid->iscoard){
1272
1273 string tempcorrectedxdimname;
1274 string tempcorrectedydimname;
1275
1276 // Find the DIMXNAME and DIMYNAME in the dimension name list.
1277 map<string,string>::iterator tempdimmapit;
1279 tempdimmapit = tempnewxdimnamelist.find(grid->getName());
1280 if (tempdimmapit != tempnewxdimnamelist.end())
1281 tempcorrectedxdimname = tempdimmapit->second;
1282 else
1283 throw2("cannot find the corrected COARD XDim dimension name", grid->getName());
1284 tempmapit = grid->ndimnamelist.find(DIMXNAME);
1285 if (tempmapit != grid->ndimnamelist.end()) {
1286 HDFCFUtil::insert_map(grid->ndimnamelist, DIMXNAME, tempcorrectedxdimname);
1287 }
1288 else
1289 throw2("cannot find the corrected dimension name", grid->getName());
1290
1291 tempdimmapit = tempnewydimnamelist.find(grid->getName());
1292 if (tempdimmapit != tempnewydimnamelist.end())
1293 tempcorrectedydimname = tempdimmapit->second;
1294 else
1295 throw2("cannot find the corrected COARD YDim dimension name", grid->getName());
1296
1297 tempmapit = grid->ndimnamelist.find(DIMYNAME);
1298 if (tempmapit != grid->ndimnamelist.end()) {
1299 HDFCFUtil::insert_map(grid->ndimnamelist, DIMYNAME, tempcorrectedydimname);
1300 }
1301 else
1302 throw2("cannot find the corrected dimension name", grid->getName());
1303 }
1304 }
1305 }
1306
1307
1308 // For 1-D lat/lon cases, Make the third (other than lat/lon coordinate variable) dimension to follow COARD conventions.
1309
1310 for (const auto &grid:this->grids){
1312 grid->dimcvarlist.begin(); j!= grid->dimcvarlist.end();++j){
1313
1314 // It seems that the condition for onelatlon case is if (this->iscoard) is true instead if
1315 // this->onelatlon is true.So change it. KY 2010-7-4
1316 if ((this->iscoard||grid->iscoard) && (*j).first !=DIMXNAME && (*j).first !=DIMYNAME) {
1317 string tempnewdimname;
1319
1320 // Find the new field name of the corresponding dimennsion name
1321 tempmapit = grid->ncvarnamelist.find((*j).second);
1322 if (tempmapit != grid->ncvarnamelist.end())
1323 tempnewdimname= tempmapit->second;
1324 else
1325 throw3("cannot find the corrected field of ", (*j).second,grid->getName());
1326
1327 // Make the new field name to the correponding dimension name
1328 tempmapit =grid->ndimnamelist.find((*j).first);
1329 if (tempmapit != grid->ndimnamelist.end())
1330 HDFCFUtil::insert_map(grid->ndimnamelist, (*j).first, tempnewdimname);
1331 else
1332 throw3("cannot find the corrected dimension name of ", (*j).first,grid->getName());
1333
1334 }
1335 }
1336 }
1337}
1338
1339// Create the corrected dimension vector for each field when COARDS is not followed.
1340void File::update_grid_field_corrected_dims() {
1341
1342 // Revisit the lat/lon fields to check if 1-D COARD convention needs to be followed.
1343 vector<Dimension*> correcteddims;
1344 string tempcorrecteddimname;
1345 // temporary dimension pointer
1346 Dimension *correcteddim;
1347
1348 for (const auto &grid:this->grids) {
1349
1350 for (const auto &field:grid->getDataFields()) {
1351
1352 // When the corrected dimension name of lat/lon has been updated,
1353 if (field->iscoard == false) {
1354
1355 // Just obtain the corrected dim names and save the corrected dimensions for each field.
1356 for (const auto &dim:field->getDimensions()){
1357
1359
1360 // Find the new name of this field
1361 tempmapit = grid->ndimnamelist.find(dim->getName());
1362 if (tempmapit != grid->ndimnamelist.end())
1363 tempcorrecteddimname= tempmapit->second;
1364 else
1365 throw4("cannot find the corrected dimension name", grid->getName(),field->getName(),dim->getName());
1366 correcteddim = new Dimension(tempcorrecteddimname,dim->getSize());
1367 correcteddims.push_back(correcteddim);
1368 }
1369 field->setCorrectedDimensions(correcteddims);
1370 correcteddims.clear();
1371 }
1372 }
1373 }
1374
1375}
1376
1377void File::handle_grid_cf_attrs() {
1378
1379 // Create "coordinates" ,"units" attributes. The attribute "units" only applies to latitude and longitude.
1380 // This is the last round of looping through everything,
1381 // we will match dimension name list to the corresponding dimension field name
1382 // list for every field.
1383
1384 for (const auto &grid:this->grids) {
1385 for (const auto &field:grid->getDataFields()) {
1386
1387 // Real fields: adding coordinate attributesinate attributes
1388 if (field->fieldtype == 0) {
1389 string tempcoordinates="";
1390 string tempfieldname="";
1391 string tempcorrectedfieldname="";
1392 int tempcount = 0;
1393 for (const auto &dim:field->getDimensions()) {
1394
1395 // Handle coordinates attributes
1398
1399 // Find the dimension field name
1400 tempmapit = (grid->dimcvarlist).find(dim->getName());
1401 if (tempmapit != (grid->dimcvarlist).end())
1402 tempfieldname = tempmapit->second;
1403 else
1404 throw4("cannot find the dimension field name",
1405 grid->getName(),field->getName(),dim->getName());
1406
1407 // Find the corrected dimension field name
1408 tempmapit2 = (grid->ncvarnamelist).find(tempfieldname);
1409 if (tempmapit2 != (grid->ncvarnamelist).end())
1410 tempcorrectedfieldname = tempmapit2->second;
1411 else
1412 throw4("cannot find the corrected dimension field name",
1413 grid->getName(),field->getName(),dim->getName());
1414
1415 if (tempcount == 0)
1416 tempcoordinates= tempcorrectedfieldname;
1417 else
1418 tempcoordinates = tempcoordinates +" "+tempcorrectedfieldname;
1419 tempcount++;
1420 }
1421 field->setCoordinates(tempcoordinates);
1422 }
1423
1424 // Add units for latitude and longitude
1425 if (field->fieldtype == 1) {// latitude,adding the "units" degrees_north.
1426 string tempunits = "degrees_north";
1427 field->setUnits(tempunits);
1428 }
1429 if (field->fieldtype == 2) { // longitude, adding the units degrees_east.
1430 string tempunits = "degrees_east";
1431 field->setUnits(tempunits);
1432 }
1433
1434 // Add units for Z-dimension, now it is always "level"
1435 // This also needs to be corrected since the Z-dimension may not always be "level".
1436 // KY 2012-6-13
1437 // We decide not to touch "units" when the Z-dimension is an existing field(fieldtype =3).
1438 if (field->fieldtype == 4) {
1439 string tempunits ="level";
1440 field->setUnits(tempunits);
1441 }
1442
1443 // The units of the time is not right. KY 2012-6-13(documented at jira HFRHANDLER-167)
1444 if (field->fieldtype == 5) {
1445 string tempunits ="days since 1900-01-01 00:00:00";
1446 field->setUnits(tempunits);
1447 }
1448
1449 // We meet a really special case for CERES TRMM data. We make it to the specialformat 2 case
1450 // since the corner coordinate is set to default in HDF-EOS2 structmetadata. We also find that there are
1451 // values such as 3.4028235E38 that is the maximum single precision floating point value. This value
1452 // is a fill value but the fillvalue attribute is not set. So we add the fillvalue attribute for this case.
1453 // We may find such cases for other products and will tackle them also.
1454 if (true == grid->addfvalueattr) {
1455 if (((field->getFillValue()).empty()) && (field->getType()==DFNT_FLOAT32 )) {
1456 float tempfillvalue = FLT_MAX; // Replaced HUGE with FLT_MAX. jhrg 12/3/20
1457 field->addFillValue(tempfillvalue);
1458 field->setAddedFillValue(true);
1459 }
1460 }
1461 }
1462 }
1463}
1464
1465// Special handling SOM(Space Oblique Mercator) projection files
1466void File::handle_grid_SOM_projection() {
1467
1468 // since the latitude and longitude of the SOM projection are 3-D, so we need to handle this projection in a special way.
1469 // Based on our current understanding, the third dimension size is always 180.
1470 // If the size is not 180, the latitude and longitude will not be calculated correctly.
1471 // This is according to the MISR Lat/lon calculation document
1472 // at http://eosweb.larc.nasa.gov/PRODOCS/misr/DPS/DPS_v50_RevS.pdf
1473 // KY 2012-6-12
1474
1475 for (const auto &grid:this->grids) {
1476 if (GCTP_SOM == grid->getProjection().getCode()) {
1477
1478 // 0. Getting the SOM dimension for latitude and longitude.
1479
1480 // Obtain SOM's dimension name.
1481 string som_dimname;
1482 for (const auto &dim:grid->getDimensions()) {
1483
1484 // NBLOCK is from misrproj.h. It is the number of block that MISR team support for the SOM projection.
1485 if (NBLOCK == dim->getSize()) {
1486
1487 // To make sure we catch the right dimension, check the first three characters of the dim. name
1488 // It should be SOM
1489 if (dim->getName().compare(0,3,"SOM") == 0) {
1490 som_dimname = dim->getName();
1491 break;
1492 }
1493 }
1494 }
1495
1496 if (""== som_dimname)
1497 throw4("Wrong number of block: The number of block of MISR SOM Grid ",
1498 grid->getName()," is not ",NBLOCK);
1499
1501
1502 // Find the corrected (CF) dimension name
1503 string cor_som_dimname;
1504 tempmapit = grid->ndimnamelist.find(som_dimname);
1505 if (tempmapit != grid->ndimnamelist.end())
1506 cor_som_dimname = tempmapit->second;
1507 else
1508 throw2("cannot find the corrected dimension name for ", som_dimname);
1509
1510 // Find the corrected(CF) name of this field
1511 string cor_som_cvname;
1512
1513 // Here we cannot use getDataFields() since the returned elements cannot be modified. KY 2012-6-12
1514 // Here we cannot simply change the vector with for range loop since we need to remove an element. KY 2022-06-17
1515 for (vector<Field *>::iterator j = grid->datafields.begin();
1516 j != grid->datafields.end(); ) {
1517
1518 // Only 6-7 fields, so just loop through
1519 // 1. Set the SOM dimension for latitude and longitude
1520 if (1 == (*j)->fieldtype || 2 == (*j)->fieldtype) {
1521
1522 auto newdim = new Dimension(som_dimname,NBLOCK);
1523 auto newcor_dim = new Dimension(cor_som_dimname,NBLOCK);
1525
1526 it_d = (*j)->dims.begin();
1527 (*j)->dims.insert(it_d,newdim);
1528
1529 it_d = (*j)->correcteddims.begin();
1530 (*j)->correcteddims.insert(it_d,newcor_dim);
1531
1532
1533 }
1534
1535 // 2. Remove the added coordinate variable for the SOM dimension
1536 // The added variable is a variable with the nature number
1537 // Why removing it? Since we cannot follow the general rule to create coordinate variables for MISR products.
1538 // The third-dimension belongs to lat/lon rather than a missing coordinate variable.
1539 if ( 4 == (*j)->fieldtype) {
1540 cor_som_cvname = (*j)->newname;
1541 delete (*j);
1542 j = grid->datafields.erase(j);
1543 }
1544 else {
1545 ++j;
1546 }
1547 }
1548
1549 // 3. Fix the "coordinates" attribute: remove the SOM CV name from the coordinate attribute.
1550 // Notice this is a little inefficient. Since we only have a few fields and non-SOM projection products
1551 // won't be affected, and more importantly, to keep the SOM projection handling in a central place,
1552 // I handle the adjustment of "coordinates" attribute here. KY 2012-6-12
1553
1554 // MISR data cannot be visualized by Panoply and IDV. So the coordinates attribute
1555 // created here reflects the coordinates of this variable more accurately. KY 2012-6-13
1556 for (const auto &field:grid->getDataFields()) {
1557
1558 if ( 0 == field->fieldtype) {
1559
1560 string temp_coordinates = field->coordinates;
1561
1562 size_t found;
1563 found = temp_coordinates.find(cor_som_cvname);
1564
1565 if (0 == found) {
1566 // Need also to remove the space after the SOM CV name.
1567 if (temp_coordinates.size() >cor_som_cvname.size())
1568 temp_coordinates.erase(found,cor_som_cvname.size()+1);
1569 else
1570 temp_coordinates.erase(found,cor_som_cvname.size());
1571 }
1572 else if (found != string::npos)
1573 temp_coordinates.erase(found-1,cor_som_cvname.size()+1);
1574 else
1575 throw4("cannot find the coordinate variable ",cor_som_cvname,
1576 "from ",temp_coordinates);
1577
1578 field->setCoordinates(temp_coordinates);
1579
1580 }
1581 }
1582 }
1583 }
1584}
1585
1586// Check if we need to handle dim. map and set handle_swath_dimmap if necessary.
1587// The input parameter is the number of swath.
1588void File::check_swath_dimmap(int numswath) {
1589
1590 if (HDF4RequestHandler::get_disable_swath_dim_map() == true)
1591 return;
1592
1593 // Check if there are dimension maps and if the num of dim. maps is odd in this case.
1594 int tempnumdm = 0;
1595 int temp_num_map = 0;
1596 bool odd_num_map = false;
1597 for (const auto &swath:this->swaths) {
1598 temp_num_map = swath->get_num_map();
1599 tempnumdm += temp_num_map;
1600 if (temp_num_map%2!=0) {
1601 odd_num_map =true;
1602 break;
1603 }
1604 }
1605
1606 // We only handle even number of dimension maps like MODIS(2-D lat/lon)
1607 if (tempnumdm != 0 && odd_num_map == false)
1608 handle_swath_dimmap = true;
1609
1610 // MODATML2 and MYDATML2 in year 2010 include dimension maps. But the dimension map
1611 // is not used. Furthermore, they provide additional latitude/longtiude
1612 // for 10 KM under the data field. So we have to handle this differently.
1613 // MODATML2 in year 2000 version doesn't include dimension map, so we
1614 // have to consider both with dimension map and without dimension map cases.
1615 // The swath name is atml2.
1616
1617 bool fakedimmap = false;
1618
1619 if (numswath == 1) {// Start special atml2-like handling
1620
1621 if ((this->swaths[0]->getName()).find("atml2")!=string::npos){
1622
1623 if (tempnumdm >0)
1624 fakedimmap = true;
1625 int templlflag = 0;
1626
1627 for (const auto &gfield:this->swaths[0]->getGeoFields()) {
1628 if (gfield->getName() == "Latitude" || gfield->getName() == "Longitude") {
1629 if (gfield->getType() == DFNT_UINT16 ||
1630 gfield->getType() == DFNT_INT16)
1631 gfield->type = DFNT_FLOAT32;
1632 templlflag ++;
1633 if (templlflag == 2)
1634 break;
1635 }
1636 }
1637
1638 templlflag = 0;
1639
1640 for (const auto &dfield:this->swaths[0]->getDataFields()) {
1641
1642 // We meet a very speical MODIS case.
1643 // The latitude and longitude types are int16.
1644 // The number are in thousand. The scale factor
1645 // attribute is 0.01. This attribute cannot be
1646 // retrieved by EOS2 APIs. So we have to hard code this.
1647 // We can only use the swath name to
1648 // identify this case.
1649 // The swath name is atml2. It has only one swath.
1650 // We have to change lat and lon to float type array;
1651 // since after applying the scale factor, the array becomes
1652 // float data.
1653 // KY-2010-7-12
1654
1655 if ((dfield->getName()).find("Latitude") != string::npos){
1656
1657 if (dfield->getType() == DFNT_UINT16 ||
1658 dfield->getType() == DFNT_INT16)
1659 dfield->type = DFNT_FLOAT32;
1660
1661 dfield->fieldtype = 1;
1662
1663 // Also need to link the dimension to the coordinate variable list
1664 if (dfield->getRank() != 2)
1665 throw2("The lat/lon rank must be 2 for Java clients to work",
1666 dfield->getRank());
1667 HDFCFUtil::insert_map(this->swaths[0]->dimcvarlist,
1668 ((dfield->getDimensions())[0])->getName(),dfield->getName());
1669 templlflag ++;
1670 }
1671
1672 if ((dfield->getName()).find("Longitude")!= string::npos) {
1673
1674 if (dfield->getType() == DFNT_UINT16 ||
1675 dfield->getType() == DFNT_INT16)
1676 dfield->type = DFNT_FLOAT32;
1677
1678 dfield->fieldtype = 2;
1679 if (dfield->getRank() != 2)
1680 throw2("The lat/lon rank must be 2 for Java clients to work",
1681 dfield->getRank());
1682 HDFCFUtil::insert_map(this->swaths[0]->dimcvarlist,
1683 ((dfield->getDimensions())[1])->getName(), dfield->getName());
1684 templlflag ++;
1685 }
1686
1687 if (templlflag == 2)
1688 break;
1689 }
1690 }
1691 }// End of special atml2 handling
1692
1693 // Although this file includes dimension maps, it doesn't use it at all. So set
1694 // handle_swath_dimmap to 0.
1695 if (true == fakedimmap)
1696 handle_swath_dimmap = false;
1697 return;
1698
1699}
1700
1701// If dim. map needs to be handled, we need to check if we fall into the case
1702// that backward compatibility of MODIS Level 1B etc. should be supported.
1703void File::check_swath_dimmap_bk_compat(int numswath){
1704
1705 if (true == handle_swath_dimmap) {
1706
1707 if (numswath == 1 && (((this->swaths)[0])->name== "MODIS_SWATH_Type_L1B"))
1708 backward_handle_swath_dimmap = true;
1709 else {
1710 // If the number of dimmaps is 2 for every swath
1711 // and latitude/longitude need to be interpolated,
1712 // this also falls back to the backward compatibility case.
1713 // GeoDim_in_vars needs to be checked first.
1714 bool all_2_dimmaps_no_geodim = true;
1715 for (const auto &swath:this->swaths) {
1716 if (swath->get_num_map() !=2 || swath->GeoDim_in_vars == true) {
1717 all_2_dimmaps_no_geodim = false;
1718 break;
1719 }
1720 }
1721 if (true == all_2_dimmaps_no_geodim)
1722 backward_handle_swath_dimmap = true;
1723 }
1724 }
1725 return;
1726}
1727
1728// Create the dimension name to coordinate variable name map for lat/lon.
1729void File::create_swath_latlon_dim_cvar_map(){
1730
1731 vector<Field*> ori_lats;
1732 vector<Field*> ori_lons;
1733 if (handle_swath_dimmap == true && backward_handle_swath_dimmap == false) {
1734
1735 // We need to check if "Latitude and Longitude" both exist in all swaths under GeoFields.
1736 // The latitude and longitude must be 2-D arrays.
1737 // This is the basic requirement to handle our defined multiple dimension map case.
1738 multi_dimmap = true;
1739
1740 for (const auto &swath:this->swaths) {
1741
1742 bool has_cf_lat = false;
1743 bool has_cf_lon = false;
1744
1745 for (const auto &gfield:swath->getGeoFields()) {
1746
1747 // Here we assume it is always lat[f0][f1] and lon [f0][f1].
1748 // lat[f0][f1] and lon[f1][f0] should not occur.
1749 // So far only "Latitude" and "Longitude" are used as standard names of lat and lon for swath.
1750 if (gfield->getName()=="Latitude" && gfield->getRank() == 2){
1751 has_cf_lat = true;
1752 ori_lats.push_back(gfield);
1753 }
1754 else if (gfield->getName()=="Longitude" && gfield->getRank() == 2){
1755 has_cf_lon = true;
1756 ori_lons.push_back(gfield);
1757 }
1758 if (has_cf_lat == true && has_cf_lon == true)
1759 break;
1760 }
1761 if (has_cf_lat == false || has_cf_lon == false) {
1762 multi_dimmap = false;
1763 break;
1764 }
1765 }
1766 }
1767
1768 // By our best knowledge so far, we know we come to a multiple dimension map case
1769 // that we can handle. We will create dim to coordinate variable map for lat and lon
1770 // with the following block and finish this function.
1771 if (true == multi_dimmap) {
1772
1773 int ll_count = 0;
1774 for (const auto &swath:this->swaths) {
1775 create_swath_latlon_dim_cvar_map_for_dimmap(swath,ori_lats[ll_count],ori_lons[ll_count]);
1776 ll_count++;
1777 }
1778 return;
1779
1780 }
1781
1782 // For the cases that multi_dimmap is not true, do the following:
1783 // 1. Prepare the right dimension name and the dimension field list for each swath.
1784 // The assumption is that within a swath, the dimension name is unique.
1785 // The dimension field name(even with the added Z-like field) is unique.
1786 // A map <dimension name, dimension field name> will be created.
1787 // The name clashing handling for multiple swaths will not be done in this step.
1788
1789 // 1.1 Obtain the dimension names corresponding to the latitude and longitude,
1790 // save them to the <dimname, dimfield> map.
1791
1792 // We found a special MODIS product: the Latitude and Longitude are put under the Data fields
1793 // rather than GeoLocation fields.
1794 // So we need to go to the "Data Fields" to grab the "Latitude and Longitude".
1795
1796 bool lat_in_geofields = false;
1797 bool lon_in_geofields = false;
1798
1799 for (const auto &swath:this->swaths) {
1800
1801 int tempgeocount = 0;
1802 for (const auto &gfield:swath->getGeoFields()) {
1803
1804 // Here we assume it is always lat[f0][f1] and lon [f0][f1]. No lat[f0][f1] and lon[f1][f0] occur.
1805 // So far only "Latitude" and "Longitude" are used as standard names of lat and lon for swath.
1806 if (gfield->getName()=="Latitude" ){
1807 if (gfield->getRank() > 2)
1808 throw2("Currently the lat/lon rank must be 1 or 2 for Java clients to work",
1809 gfield->getRank());
1810
1811 lat_in_geofields = true;
1812
1813 // Since under our assumption, lat/lon are always 2-D for a swath and
1814 // dimension order doesn't matter for Java clients,
1815 // so we always map Latitude to the first dimension and longitude to the second dimension.
1816 // Save this information in the coordinate variable name and field map.
1817 // For rank =1 case, we only handle the cross-section along the same
1818 // longitude line. So Latitude should be the dimension name.
1819 HDFCFUtil::insert_map(swath->dimcvarlist, ((gfield->getDimensions())[0])->getName(), "Latitude");
1820
1821 // Have dimension map, we want to remember the dimension and remove it from the list.
1822 if (handle_swath_dimmap == true) {
1823
1824 // We need to keep the backward compatibility when handling MODIS level 1B etc.
1825 if (true == backward_handle_swath_dimmap) {
1826
1827 // We have to loop through the dimension map
1828 for (const auto &dmap:swath->getDimensionMaps()) {
1829
1830 // This dimension name will be replaced by the mapped dimension name,
1831 // the mapped dimension name can be obtained from the getDataDimension() method.
1832 if ((gfield->getDimensions()[0])->getName() == dmap->getGeoDimension()) {
1833 HDFCFUtil::insert_map(swath->dimcvarlist, dmap->getDataDimension(), "Latitude");
1834 break;
1835 }
1836 }
1837 }
1838 }
1839
1840 gfield->fieldtype = 1;
1841 tempgeocount ++;
1842 }
1843
1844 if (gfield->getName()=="Longitude"){
1845 if (gfield->getRank() > 2)
1846 throw2("Currently the lat/lon rank must be 1 or 2 for Java clients to work",
1847 gfield->getRank());
1848
1849 // Only lat-level cross-section(for Panoply)is supported
1850 // when longitude/latitude is 1-D, so ignore the longitude as the dimension field.
1851 lon_in_geofields = true;
1852 if (gfield->getRank() == 1) {
1853 tempgeocount++;
1854 continue;
1855 }
1856
1857 // Since under our assumption, lat/lon are almost always 2-D for
1858 // a swath and dimension order doesn't matter for Java clients,
1859 // we always map Latitude to the first dimension and longitude to the second dimension.
1860 // Save this information in the dimensiion name and coordinate variable map.
1861 HDFCFUtil::insert_map(swath->dimcvarlist,
1862 ((gfield->getDimensions())[1])->getName(), "Longitude");
1863 if (handle_swath_dimmap == true) {
1864 if (true == backward_handle_swath_dimmap) {
1865
1866 // We have to loop through the dimension map
1867 for (const auto &dmap:swath->getDimensionMaps()) {
1868
1869 // This dimension name will be replaced by the mapped dimension name,
1870 // This name can be obtained by getDataDimension() fuction of
1871 // dimension map class.
1872 if ((gfield->getDimensions()[1])->getName() ==
1873 dmap->getGeoDimension()) {
1874 HDFCFUtil::insert_map(swath->dimcvarlist,
1875 dmap->getDataDimension(), "Longitude");
1876 break;
1877 }
1878 }
1879 }
1880 }
1881 gfield->fieldtype = 2;
1882 tempgeocount++;
1883 }
1884 if (tempgeocount == 2)
1885 break;
1886 }
1887 }// end of creating the <dimname,dimfield> map.
1888
1889 // If lat and lon are not together, throw an error.
1890 if (lat_in_geofields!=lon_in_geofields)
1891 throw1("Latitude and longitude must be both under Geolocation fields or Data fields");
1892
1893 // Check if they are under data fields(The code may be combined with the above, see HFRHANDLER-166)
1894 if (!lat_in_geofields && !lon_in_geofields) {
1895
1896 bool lat_in_datafields = false;
1897 bool lon_in_datafields = false;
1898
1899 for (const auto &swath:this->swaths) {
1900
1901 int tempgeocount = 0;
1902 for (const auto &dfield:swath->getDataFields()) {
1903
1904 // Here we assume it is always lat[f0][f1] and lon [f0][f1].
1905 // No lat[f0][f1] and lon[f1][f0] occur.
1906 // So far only "Latitude" and "Longitude" are used as
1907 // standard names of Lat and lon for swath.
1908 if (dfield->getName()=="Latitude" ){
1909 if (dfield->getRank() > 2) {
1910 throw2("Currently the lat/lon rank must be 1 or 2 for Java clients to work",
1911 dfield->getRank());
1912 }
1913 lat_in_datafields = true;
1914
1915 // Since under our assumption, lat/lon are always 2-D
1916 // for a swath and dimension order doesn't matter for Java clients,
1917 // we always map Latitude the first dimension and longitude the second dimension.
1918 // Save this information in the coordinate variable name and field map.
1919 // For rank =1 case, we only handle the cross-section along the same longitude line.
1920 // So Latitude should be the dimension name.
1921 HDFCFUtil::insert_map(swath->dimcvarlist,
1922 ((dfield->getDimensions())[0])->getName(), "Latitude");
1923
1924 if (handle_swath_dimmap == true) {
1925 if (true == backward_handle_swath_dimmap) {
1926 // We have to loop through the dimension map
1927 for (const auto &dmap:swath->getDimensionMaps()) {
1928 // This dimension name will be replaced by the mapped dimension name,
1929 // the mapped dimension name can be obtained from the getDataDimension() method.
1930 if ((dfield->getDimensions()[0])->getName() == dmap->getGeoDimension()) {
1931 HDFCFUtil::insert_map(swath->dimcvarlist, dmap->getDataDimension(), "Latitude");
1932 break;
1933 }
1934 }
1935 }
1936 }
1937 dfield->fieldtype = 1;
1938 tempgeocount ++;
1939 }
1940
1941 if (dfield->getName()=="Longitude"){
1942
1943 if (dfield->getRank() > 2) {
1944 throw2("Currently the lat/lon rank must be 1 or 2 for Java clients to work",
1945 dfield->getRank());
1946 }
1947
1948 // Only lat-level cross-section(for Panoply)is supported when
1949 // longitude/latitude is 1-D, so ignore the longitude as the dimension field.
1950 lon_in_datafields = true;
1951 if (dfield->getRank() == 1) {
1952 tempgeocount++;
1953 continue;
1954 }
1955
1956 // Since under our assumption,
1957 // lat/lon are almost always 2-D for a swath and dimension order doesn't matter for Java clients,
1958 // we always map Latitude the first dimension and longitude the second dimension.
1959 // Save this information in the dimensiion name and coordinate variable map.
1960 HDFCFUtil::insert_map(swath->dimcvarlist,
1961 ((dfield->getDimensions())[1])->getName(), "Longitude");
1962 if (handle_swath_dimmap == true) {
1963 if (true == backward_handle_swath_dimmap) {
1964 // We have to loop through the dimension map
1965 for (const auto &dmap:swath->getDimensionMaps()) {
1966 // This dimension name will be replaced by the mapped dimension name,
1967 // This name can be obtained by getDataDimension() fuction of dimension map class.
1968 if ((dfield->getDimensions()[1])->getName() == dmap->getGeoDimension()) {
1969 HDFCFUtil::insert_map(swath->dimcvarlist,
1970 dmap->getDataDimension(), "Longitude");
1971 break;
1972 }
1973 }
1974 }
1975 }
1976 dfield->fieldtype = 2;
1977 tempgeocount++;
1978 }
1979 if (tempgeocount == 2)
1980 break;
1981 }
1982 }// end of creating the <dimname,dimfield> map.
1983
1984 // If lat and lon are not together, throw an error.
1985 if (lat_in_datafields!=lon_in_datafields)
1986 throw1("Latitude and longitude must be both under Geolocation fields or Data fields");
1987
1988 }
1989
1990}
1991
1992// Create the dimension name to coordinate variable name map for coordinate variables that are not lat/lon.
1993// The input parameter is the number of dimension maps in this file.
1994void File:: create_swath_nonll_dim_cvar_map()
1995{
1996 // Handle existing and missing fields
1997 for (const auto &swath:this->swaths) {
1998
1999 // Since we find multiple 1-D fields with the same dimension names for some Swath files(AIRS level 1B),
2000 // we currently always treat the third dimension field as a missing field, this may be corrected later.
2001 // Corrections for the above: MODIS data do include the unique existing Z fields, so we have to search
2002 // the existing Z field. KY 2010-8-11
2003 // Our correction is to search all 1-D fields with the same dimension name within one swath,
2004 // if only one field is found, we use this field as the third dimension.
2005 // 1.1 Add the missing Z-dimension field.
2006 // Some dimension name's corresponding fields are missing,
2007 // so add the missing Z-dimension fields based on the dimension name. When the real
2008 // data is read, nature number 1,2,3,.... will be filled!
2009 // NOTE: The latitude and longitude dim names are not handled yet.
2010
2011 // Build a unique 1-D dimension name list.
2012 // Now the list only includes dimension names of "latitude" and "longitude".
2013
2014 pair<set<string>::iterator,bool> tempdimret;
2015 for (map<string,string>::const_iterator j = swath->dimcvarlist.begin();
2016 j!= swath->dimcvarlist.end();++j){
2017 tempdimret = swath->nonmisscvdimlist.insert((*j).first);
2018 }
2019
2020 // Search the geofield group and see if there are any existing 1-D Z dimension data.
2021 // If 1-D field data with the same dimension name is found under GeoField,
2022 // we still search if that 1-D field is the dimension
2023 // field of a dimension name.
2024 for (const auto &gfield:swath->getGeoFields()) {
2025
2026 if (gfield->getRank()==1) {
2027 if (swath->nonmisscvdimlist.find(((gfield->getDimensions())[0])->getName()) == swath->nonmisscvdimlist.end()){
2028 tempdimret = swath->nonmisscvdimlist.insert(((gfield->getDimensions())[0])->getName());
2029 if (gfield->getName() =="Time")
2030 gfield->fieldtype = 5;// This is for IDV.
2031
2032 HDFCFUtil::insert_map(swath->dimcvarlist, ((gfield->getDimensions())[0])->getName(), gfield->getName());
2033 gfield->fieldtype = 3;
2034
2035 }
2036 }
2037 }
2038
2039 // We will also check the third dimension inside DataFields
2040 // This may cause potential problems for AIRS data
2041 // We will double CHECK KY 2010-6-26
2042 // So far the tests seem okay. KY 2010-8-11
2043 for (const auto &dfield:swath->getDataFields()) {
2044
2045 if (dfield->getRank()==1) {
2046 if (swath->nonmisscvdimlist.find(((dfield->getDimensions())[0])->getName()) == swath->nonmisscvdimlist.end()){
2047 tempdimret = swath->nonmisscvdimlist.insert(((dfield->getDimensions())[0])->getName());
2048 if (dfield->getName() =="Time")
2049 dfield->fieldtype = 5;// This is for IDV.
2050
2051 // This is for temporarily COARD fix.
2052 // For 2-D lat/lon, the third dimension should NOT follow
2053 // COARD conventions. It will cause Panoply and IDV failed.
2054 // KY 2010-7-21
2055 HDFCFUtil::insert_map(swath->dimcvarlist, ((dfield->getDimensions())[0])->getName(), dfield->getName());
2056 dfield->fieldtype = 3;
2057
2058 }
2059 }
2060 }
2061
2062
2063 // S1.2.3 Handle the missing fields
2064 // Loop through all dimensions of this swath to search the missing fields
2065 //
2066 bool missingfield_unlim_flag = false;
2067 Field *missingfield_unlim = nullptr;
2068
2069 for (const auto &sdim:swath->getDimensions()) {
2070
2071 if ((swath->nonmisscvdimlist.find(sdim->getName())) == swath->nonmisscvdimlist.end()){// This dimension needs a field
2072
2073 // Need to create a new data field vector element with the name and dimension as above.
2074 auto missingfield = new Field();
2075
2076 // This is for temporarily COARD fix.
2077 // For 2-D lat/lon, the third dimension should NOT follow
2078 // COARD conventions. It will cause Panoply and IDV failed.
2079 // Since Swath is always 2-D lat/lon, so we are okay here. Add a "_d" for each field name.
2080 // KY 2010-7-21
2081 // netCDF-Java now first follows COARDS, change back
2082 // missingfield->name = sdim->getName()+"_d";
2083 Dimension *dim;
2084 // When we can handle multiple dimension maps and the
2085 // number of swath is >1, we add the swath name as suffix to
2086 // avoid the name clashing.
2087 if (true == multi_dimmap && (this->swaths.size() != 1)) {
2088 missingfield->name = sdim->getName()+"_"+swath->name;
2089 dim = new Dimension(missingfield->name,sdim->getSize());
2090 }
2091 else {
2092 missingfield->name = sdim->getName();
2093 dim = new Dimension(sdim->getName(),sdim->getSize());
2094 }
2095 missingfield->rank = 1;
2096 missingfield->type = DFNT_INT32;//This is an HDF constant.the data type is always integer.
2097
2098 // only 1 dimension
2099 missingfield->dims.push_back(dim);
2100
2101 // Provide information for the missing data, since we need to calculate the data, so
2102 // the information is different than a normal field.
2103 // int missingdimsize[1]; //unused variable. SBL 2/7/20
2104 // missingdimsize[0]= sdim->getSize();
2105
2106 if (0 == sdim->getSize()) {
2107 missingfield_unlim_flag = true;
2108 missingfield_unlim = missingfield;
2109 }
2110
2111 //added Z-dimension coordinate variable with nature number
2112 missingfield->fieldtype = 4;
2113
2114 swath->geofields.push_back(missingfield);
2115 HDFCFUtil::insert_map(swath->dimcvarlist,
2116 (missingfield->getDimensions())[0]->getName(), missingfield->name);
2117 }
2118 }
2119
2120 // Correct the unlimited dimension size.
2121 // The code on the following is ok.
2122 // However, coverity is picky about changing the missingfield_unlim_flag in the middle.
2123 // use a temporary variable for the if block.
2124 // The following code correct the dimension size of unlimited dimension.
2125
2126 bool temp_missingfield_unlim_flag = missingfield_unlim_flag;
2127 if (true == temp_missingfield_unlim_flag) {
2128 for (const auto &dfield:swath->getDataFields()) {
2129
2130 for (const auto &fdim:dfield->getDimensions()) {
2131
2132 if (fdim->getName() == (missingfield_unlim->getDimensions())[0]->getName()) {
2133 if (fdim->getSize()!= 0) {
2134 Dimension *dim = missingfield_unlim->getDimensions()[0];
2135 // Correct the dimension size.
2136 dim->dimsize = fdim->getSize();
2137 missingfield_unlim_flag = false;
2138 break;
2139 }
2140 }
2141 }
2142 if (false == missingfield_unlim_flag)
2143 break;
2144 }
2145 }
2146
2147 swath->nonmisscvdimlist.clear();// clear this set.
2148
2149 }// End of handling non-latlon cv
2150
2151}
2152
2153// Handle swath dimension name to coordinate variable name maps.
2154void File::handle_swath_dim_cvar_maps() {
2155
2156 // Start handling name clashing
2157 vector <string> tempfieldnamelist;
2158 for (const auto &swath:this->swaths) {
2159
2160 // First handle geofield, all dimension fields are under the geofield group.
2161 for (const auto &gfield:swath->getGeoFields()) {
2162 if (gfield->fieldtype == 0 && (this->swaths.size() !=1) &&
2163 (true == handle_swath_dimmap) &&
2164 (backward_handle_swath_dimmap == false)){
2165 string new_field_name = gfield->name+"_"+swath->name;
2166 tempfieldnamelist.push_back(HDFCFUtil::get_CF_string(new_field_name));
2167 }
2168 else
2169 tempfieldnamelist.push_back(HDFCFUtil::get_CF_string(gfield->name));
2170 }
2171
2172 for (const auto &dfield:swath->getDataFields()) {
2173 if (dfield->fieldtype == 0 && (this->swaths.size() !=1) &&
2174 true == multi_dimmap){
2175 // If we can handle multi dim. maps fro multi swaths, we
2176 // create the field name with the swath name as suffix to
2177 // avoid name clashing.
2178 string new_field_name = dfield->name+"_"+swath->name;
2179 tempfieldnamelist.push_back(HDFCFUtil::get_CF_string(new_field_name));
2180 }
2181 else
2182 tempfieldnamelist.push_back(HDFCFUtil::get_CF_string(dfield->name));
2183 }
2184 }
2185
2186 HDFCFUtil::Handle_NameClashing(tempfieldnamelist);
2187
2188 int total_fcounter = 0;
2189
2190 // Create a map for dimension field name <original field name, corrected field name>
2191 // Also assure the uniqueness of all field names,save the new field names.
2192 //the original dimension field name to the corrected dimension field name
2193 map<string,string>tempncvarnamelist;
2194 for (const auto &swath:this->swaths) {
2195
2196 // First handle geofield, all dimension fields are under the geofield group.
2197 for (const auto &gfield:swath->getGeoFields())
2198 {
2199
2200 gfield->newname = tempfieldnamelist[total_fcounter];
2201 total_fcounter++;
2202
2203 // If this field is a dimension field, save the name/new name pair.
2204 if (gfield->fieldtype!=0)
2205 HDFCFUtil::insert_map(swath->ncvarnamelist, gfield->getName(), gfield->newname);
2206
2207 }
2208
2209 for (const auto &dfield:swath->getDataFields())
2210 {
2211 dfield->newname = tempfieldnamelist[total_fcounter];
2212 total_fcounter++;
2213
2214 // If this field is a dimension field, save the name/new name pair.
2215 if (dfield->fieldtype!=0) {
2216 HDFCFUtil::insert_map(swath->ncvarnamelist, dfield->getName(), dfield->newname);
2217 }
2218 }
2219 } // end of creating a map for dimension field name <original field name, corrected field name>
2220
2221 // Create a map for dimension name < original dimension name, corrected dimension name>
2222
2223 vector <string>tempalldimnamelist;
2224
2225 for (const auto &swath:this->swaths) {
2227 swath->dimcvarlist.begin(); j!= swath->dimcvarlist.end();++j)
2228 tempalldimnamelist.push_back(HDFCFUtil::get_CF_string((*j).first));
2229 }
2230
2231 // Handle name clashing will make the corrected dimension name follow CF
2232 HDFCFUtil::Handle_NameClashing(tempalldimnamelist);
2233
2234 int total_dcounter = 0;
2235
2236 for (const auto &swath:this->swaths) {
2238 swath->dimcvarlist.begin(); j!= swath->dimcvarlist.end();++j){
2239 HDFCFUtil::insert_map(swath->ndimnamelist, (*j).first, tempalldimnamelist[total_dcounter]);
2240 total_dcounter++;
2241 }
2242 }
2243
2244 // Create corrected dimension vectors.
2245 vector<Dimension*> correcteddims;
2246 string tempcorrecteddimname;
2247 Dimension *correcteddim;
2248
2249 for (const auto &swath:this->swaths) {
2250
2251 // First the geofield.
2252 for (const auto &gfield:swath->getGeoFields()) {
2253
2254 for (const auto &gdim:gfield->getDimensions()) {
2255
2257
2258 // No dimension map or dimension names were handled. just obtain the new dimension name.
2259 if (handle_swath_dimmap == false || multi_dimmap == true) {
2260
2261 // Find the new name of this field
2262 tempmapit = swath->ndimnamelist.find(gdim->getName());
2263 if (tempmapit != swath->ndimnamelist.end())
2264 tempcorrecteddimname= tempmapit->second;
2265 else
2266 throw4("cannot find the corrected dimension name",
2267 swath->getName(),gfield->getName(),gdim->getName());
2268
2269 correcteddim = new Dimension(tempcorrecteddimname,gdim->getSize());
2270 }
2271 else {
2272 // have dimension map, use the datadim and datadim size to replace the geodim and geodim size.
2273 bool isdimmapname = false;
2274
2275 // We have to loop through the dimension map
2276 for (const auto &sdmap:swath->getDimensionMaps()) {
2277
2278 // This dimension name is the geo dimension name in the dimension map,
2279 // replace the name with data dimension name.
2280 if (gdim->getName() == sdmap->getGeoDimension()) {
2281
2282 isdimmapname = true;
2283 gfield->dmap = true;
2284 string temprepdimname = sdmap->getDataDimension();
2285
2286 // Find the new name of this data dimension name
2287 tempmapit = swath->ndimnamelist.find(temprepdimname);
2288 if (tempmapit != swath->ndimnamelist.end())
2289 tempcorrecteddimname= tempmapit->second;
2290 else
2291 throw4("cannot find the corrected dimension name", swath->getName(),
2292 gfield->getName(),gdim->getName());
2293
2294 // Find the size of this data dimension name
2295 // We have to loop through the Dimensions of this swath
2296 bool ddimsflag = false;
2297 for (const auto &sdim:swath->getDimensions()) {
2298 if (sdim->getName() == temprepdimname) {
2299 // Find the dimension size, create the correcteddim
2300 correcteddim = new Dimension(tempcorrecteddimname,sdim->getSize());
2301 ddimsflag = true;
2302 break;
2303 }
2304 }
2305 if (!ddimsflag)
2306 throw4("cannot find the corrected dimension size", swath->getName(),
2307 gfield->getName(),gdim->getName());
2308 break;
2309 }
2310 }
2311 if (false == isdimmapname) { // Still need to assign the corrected dimensions.
2312 // Find the new name of this field
2313 tempmapit = swath->ndimnamelist.find(gdim->getName());
2314 if (tempmapit != swath->ndimnamelist.end())
2315 tempcorrecteddimname= tempmapit->second;
2316 else
2317 throw4("cannot find the corrected dimension name",
2318 swath->getName(),gfield->getName(),gdim->getName());
2319
2320 correcteddim = new Dimension(tempcorrecteddimname,gdim->getSize());
2321
2322 }
2323 }
2324
2325 correcteddims.push_back(correcteddim);
2326 }
2327 gfield->setCorrectedDimensions(correcteddims);
2328 correcteddims.clear();
2329 }// End of creating the corrected dimension vectors for GeoFields.
2330
2331 // Then the data field.
2332 for (const auto &dfield:swath->getDataFields()) {
2333
2334 for (const auto &fdim:dfield->getDimensions()) {
2335
2336 if ((handle_swath_dimmap == false) || multi_dimmap == true) {
2337
2339 // Find the new name of this field
2340 tempmapit = swath->ndimnamelist.find(fdim->getName());
2341 if (tempmapit != swath->ndimnamelist.end())
2342 tempcorrecteddimname= tempmapit->second;
2343 else
2344 throw4("cannot find the corrected dimension name", swath->getName(),
2345 dfield->getName(),fdim->getName());
2346
2347 correcteddim = new Dimension(tempcorrecteddimname,fdim->getSize());
2348 }
2349 else {
2351 bool isdimmapname = false;
2352
2353 // We have to loop through dimension map
2354 for (const auto &smap:swath->getDimensionMaps()) {
2355 // This dimension name is the geo dimension name in the dimension map,
2356 // replace the name with data dimension name.
2357 if (fdim->getName() == smap->getGeoDimension()) {
2358 isdimmapname = true;
2359 dfield->dmap = true;
2360 string temprepdimname = smap->getDataDimension();
2361
2362 // Find the new name of this data dimension name
2363 tempmapit = swath->ndimnamelist.find(temprepdimname);
2364 if (tempmapit != swath->ndimnamelist.end())
2365 tempcorrecteddimname= tempmapit->second;
2366 else
2367 throw4("cannot find the corrected dimension name",
2368 swath->getName(),dfield->getName(),fdim->getName());
2369
2370 // Find the size of this data dimension name
2371 // We have to loop through the Dimensions of this swath
2372 bool ddimsflag = false;
2373 for (const auto &sdim:swath->getDimensions()) {
2374
2375 // Find the dimension size, create the correcteddim
2376 if (sdim->getName() == temprepdimname) {
2377 correcteddim = new Dimension(tempcorrecteddimname,sdim->getSize());
2378 ddimsflag = true;
2379 break;
2380 }
2381 }
2382 if (!ddimsflag)
2383 throw4("cannot find the corrected dimension size",
2384 swath->getName(),dfield->getName(),fdim->getName());
2385 break;
2386 }
2387 }
2388 // Not a dimension with dimension map; Still need to assign the corrected dimensions.
2389 if (!isdimmapname) {
2390
2391 // Find the new name of this field
2392 tempmapit = swath->ndimnamelist.find(fdim->getName());
2393 if (tempmapit != swath->ndimnamelist.end())
2394 tempcorrecteddimname= tempmapit->second;
2395 else
2396 throw4("cannot find the corrected dimension name",
2397 swath->getName(),dfield->getName(),fdim->getName());
2398
2399 correcteddim = new Dimension(tempcorrecteddimname,fdim->getSize());
2400 }
2401
2402 }
2403 correcteddims.push_back(correcteddim);
2404 }
2405 dfield->setCorrectedDimensions(correcteddims);
2406 correcteddims.clear();
2407 }// End of creating the dimensions for data fields.
2408 }
2409}
2410
2411// Handle CF attributes for swaths.
2412// The CF attributes include "coordinates", "units" for coordinate variables and "_FillValue".
2413void File::handle_swath_cf_attrs() {
2414
2415 // Create "coordinates" ,"units" attributes. The attribute "units" only applies to latitude and longitude.
2416 // This is the last round of looping through everything,
2417 // we will match dimension name list to the corresponding dimension field name
2418 // list for every field.
2419 // Since we find some swath files don't specify fillvalue when -9999.0 is found in the real data,
2420 // we specify fillvalue for those fields. This is entirely
2421 // artifical and we will evaluate this approach. KY 2010-3-3
2422
2423 for (const auto &swath:this->swaths) {
2424
2425 // Handle GeoField first.
2426 for (const auto &gfield:swath->getGeoFields()) {
2427
2428 // Real fields: adding the coordinate attribute
2429 if (gfield->fieldtype == 0) {// currently it is always true.
2430 string tempcoordinates="";
2431 string tempfieldname="";
2432 string tempcorrectedfieldname="";
2433 int tempcount = 0;
2434 bool has_ll_coord = false;
2435 if (swath->get_num_map() == 0)
2436 has_ll_coord = true;
2437 else if (handle_swath_dimmap == true) {
2438 if (backward_handle_swath_dimmap == true || multi_dimmap == true)
2439 has_ll_coord = true;
2440 }
2441 for (const auto &dim:gfield->getDimensions()) {
2442
2443 // handle coordinates attributes
2446
2447 // Find the dimension field name
2448 tempmapit = (swath->dimcvarlist).find(dim->getName());
2449 if (tempmapit != (swath->dimcvarlist).end())
2450 tempfieldname = tempmapit->second;
2451 else
2452 throw4("cannot find the dimension field name",swath->getName(),
2453 gfield->getName(),dim->getName());
2454
2455 // Find the corrected dimension field name
2456 tempmapit2 = (swath->ncvarnamelist).find(tempfieldname);
2457 if (tempmapit2 != (swath->ncvarnamelist).end())
2458 tempcorrectedfieldname = tempmapit2->second;
2459 else
2460 throw4("cannot find the corrected dimension field name",
2461 swath->getName(),gfield->getName(),dim->getName());
2462
2463 if (false == has_ll_coord)
2464 has_ll_coord= check_ll_in_coords(tempcorrectedfieldname);
2465
2466 if (tempcount == 0)
2467 tempcoordinates= tempcorrectedfieldname;
2468 else
2469 tempcoordinates = tempcoordinates +" "+tempcorrectedfieldname;
2470 tempcount++;
2471 }
2472 if (true == has_ll_coord)
2473 gfield->setCoordinates(tempcoordinates);
2474 }
2475
2476 // Add units for latitude and longitude
2477 // latitude,adding the CF units degrees_north.
2478 if (gfield->fieldtype == 1) {
2479 string tempunits = "degrees_north";
2480 gfield->setUnits(tempunits);
2481 }
2482
2483 // longitude, adding the CF units degrees_east
2484 if (gfield->fieldtype == 2) {
2485 string tempunits = "degrees_east";
2486 gfield->setUnits(tempunits);
2487 }
2488
2489 // Add units for Z-dimension, now it is always "level"
2490 // We decide not touch the units if the third-dimension CV exists(fieldtype =3)
2491 // KY 2013-02-15
2492 //if ((gfield->fieldtype == 3)||(gfield->fieldtype == 4))
2493 if (gfield->fieldtype == 4) {
2494 string tempunits ="level";
2495 gfield->setUnits(tempunits);
2496 }
2497
2498 // Add units for "Time",
2499 // Be aware that it is always "days since 1900-01-01 00:00:00"(JIRA HFRHANDLER-167)
2500 if (gfield->fieldtype == 5) {
2501 string tempunits = "days since 1900-01-01 00:00:00";
2502 gfield->setUnits(tempunits);
2503 }
2504 // Set the fill value for floating type data that doesn't have the fill value.
2505 // We found _FillValue attribute is missing from some swath data.
2506 // To cover the most cases, an attribute called _FillValue(the value is -9999.0)
2507 // is added to the data whose type is float32 or float64.
2508 if (((gfield->getFillValue()).empty()) &&
2509 (gfield->getType()==DFNT_FLOAT32 || gfield->getType()==DFNT_FLOAT64)) {
2510 float tempfillvalue = -9999.0;
2511 gfield->addFillValue(tempfillvalue);
2512 gfield->setAddedFillValue(true);
2513 }
2514 }
2515
2516 // Data fields
2517 for (const auto &dfield:swath->getDataFields()) {
2518
2519 // Real fields: adding coordinate attributes
2520 if (dfield->fieldtype == 0) {// currently it is always true.
2521 string tempcoordinates="";
2522 string tempfieldname="";
2523 string tempcorrectedfieldname="";
2524 int tempcount = 0;
2525 bool has_ll_coord = false;
2526 if (swath->get_num_map() == 0)
2527 has_ll_coord = true;
2528 else if (handle_swath_dimmap == true) {
2529 if (backward_handle_swath_dimmap == true || multi_dimmap == true)
2530 has_ll_coord = true;
2531 }
2532 for (const auto &dim:dfield->getDimensions()) {
2533
2534 // handle coordinates attributes
2537
2538 // Find the dimension field name
2539 tempmapit = (swath->dimcvarlist).find(dim->getName());
2540 if (tempmapit != (swath->dimcvarlist).end())
2541 tempfieldname = tempmapit->second;
2542 else
2543 throw4("cannot find the dimension field name",swath->getName(),
2544 dfield->getName(),dim->getName());
2545
2546 // Find the corrected dimension field name
2547 tempmapit2 = (swath->ncvarnamelist).find(tempfieldname);
2548 if (tempmapit2 != (swath->ncvarnamelist).end())
2549 tempcorrectedfieldname = tempmapit2->second;
2550 else
2551 throw4("cannot find the corrected dimension field name",
2552 swath->getName(),dfield->getName(),dim->getName());
2553
2554 if (false == has_ll_coord)
2555 has_ll_coord= check_ll_in_coords(tempcorrectedfieldname);
2556
2557 if (tempcount == 0)
2558 tempcoordinates= tempcorrectedfieldname;
2559 else
2560 tempcoordinates = tempcoordinates +" "+tempcorrectedfieldname;
2561 tempcount++;
2562 }
2563 if (true == has_ll_coord)
2564 dfield->setCoordinates(tempcoordinates);
2565 }
2566 // Add units for Z-dimension, now it is always "level"
2567 if ((dfield->fieldtype == 3)||(dfield->fieldtype == 4)) {
2568 string tempunits ="level";
2569 dfield->setUnits(tempunits);
2570 }
2571
2572 // Add units for "Time", Be aware that it is always "days since 1900-01-01 00:00:00"
2573 // documented at JIRA (HFRHANDLER-167)
2574 if (dfield->fieldtype == 5) {
2575 string tempunits = "days since 1900-01-01 00:00:00";
2576 dfield->setUnits(tempunits);
2577 }
2578
2579 // Set the fill value for floating type data that doesn't have the fill value.
2580 // We found _FillValue attribute is missing from some swath data.
2581 // To cover the most cases, an attribute called _FillValue(the value is -9999.0)
2582 // is added to the data whose type is float32 or float64.
2583 if (((dfield->getFillValue()).empty()) &&
2584 (dfield->getType()==DFNT_FLOAT32 || dfield->getType()==DFNT_FLOAT64)) {
2585 float tempfillvalue = -9999.0;
2586 dfield->addFillValue(tempfillvalue);
2587 dfield->setAddedFillValue(true);
2588 }
2589 }
2590 }
2591}
2592
2593// Find dimension that has the dimension name.
2594bool File::find_dim_in_dims(const std::vector<Dimension*>&dims,const std::string &dim_name) const {
2595
2596 bool ret_value = false;
2597 for (const auto &dim:dims) {
2598 if (dim->name == dim_name) {
2599 ret_value = true;
2600 break;
2601 }
2602 }
2603 return ret_value;
2604}
2605
2606// Check if the original dimension names in Lat/lon that holds the dimension maps are used by data fields.
2607void File::check_dm_geo_dims_in_vars() const {
2608
2609 if (handle_swath_dimmap == false)
2610 return;
2611
2612 for (const auto &swath:this->swaths) {
2613
2614 // Currently we only support swath that has 2-D lat/lon(MODIS).
2615 if (swath->get_num_map() > 0) {
2616
2617 for (const auto &dfield:swath->getDataFields()) {
2618
2619 int match_dims = 0;
2620 // We will only check the variables >=2D since lat/lon are 2D.
2621 if (dfield->rank >=2) {
2622 for (const auto &dim:dfield->getDimensions()) {
2623
2624 // There may be multiple dimension maps that hold the same geo-dimension.
2625 // We should not count this duplicately.
2626 bool not_match_geo_dim = true;
2627 for (const auto &sdmap:swath->getDimensionMaps()) {
2628
2629 if ((dim->getName() == sdmap->getGeoDimension()) && not_match_geo_dim){
2630 match_dims++;
2631 not_match_geo_dim = false;
2632 }
2633 }
2634 }
2635 }
2636 // This variable holds the GeoDimensions,this swath
2637 if (match_dims == 2) {
2638 swath->GeoDim_in_vars = true;
2639 break;
2640 }
2641 }
2642
2643 if (swath->GeoDim_in_vars == false) {
2644
2645 for (const auto &gfield:swath->getGeoFields()) {
2646
2647 int match_dims = 0;
2648 // We will only check the variables >=2D since lat/lon are 2D.
2649 if (gfield->rank >=2 && (gfield->name != "Latitude" && gfield->name != "Longitude")) {
2650 for (const auto &dim:gfield->getDimensions()) {
2651
2652 // There may be multiple dimension maps that hold the same geo-dimension.
2653 // We should not count this duplicately.
2654 bool not_match_geo_dim = true;
2655
2656 for (const auto &sdmap:swath->getDimensionMaps()) {
2657 if ((dim->getName() == sdmap->getGeoDimension()) && not_match_geo_dim){
2658 match_dims++;
2659 not_match_geo_dim = false;
2660 }
2661 }
2662 }
2663 }
2664 // This variable holds the GeoDimensions,this swath
2665 if (match_dims == 2){
2666 swath->GeoDim_in_vars = true;
2667 break;
2668 }
2669 }
2670 }
2671 }
2672 }
2673
2674 return;
2675}
2676
2677// Based on the dimension name and the mapped dimension name,obtain the offset and increment.
2678// return false if there is no match.
2679bool SwathDataset::obtain_dmap_offset_inc(const string& ori_dimname, const string & mapped_dimname,int &offset,int&inc) const{
2680 bool ret_value = false;
2681 for (const auto &sdmap:this->dimmaps) {
2682 if (sdmap->geodim==ori_dimname && sdmap->datadim == mapped_dimname){
2683 offset = sdmap->offset;
2684 inc = sdmap->increment;
2685 ret_value = true;
2686 break;
2687 }
2688 }
2689 return ret_value;
2690}
2691
2692// For the multi-dimension map case, generate all the lat/lon names. The original lat/lon
2693// names should be used.
2694// For one swath, we don't need to provide the swath name. Yes, swath name is missed. However. this is
2695// what users want. For multi swaths, swath names are added.
2696void File::create_geo_varnames_list(vector<string> & geo_varnames,const string & swathname,
2697 const string & fieldname,int extra_ll_pairs,bool oneswath) const{
2698 // We will always keep Latitude and Longitude
2699 if (true == oneswath)
2700 geo_varnames.push_back(fieldname);
2701 else {
2702 string nfieldname = fieldname+"_"+swathname;
2703 geo_varnames.push_back(nfieldname);
2704 }
2705 for (int i = 0; i <extra_ll_pairs;i++) {
2706 string nfieldname;
2707 stringstream si;
2708 si << (i+1);
2709 if ( true == oneswath) // No swath name is needed.
2710 nfieldname = fieldname+"_"+si.str();
2711 else
2712 nfieldname = fieldname+"_"+swathname+"_"+si.str();
2713 geo_varnames.push_back(nfieldname);
2714 }
2715
2716}
2717
2718// Make just one routine for both latitude and longtitude dimmaps.
2719// In longitude part, we just ignore ..
2720void File::create_geo_dim_var_maps(SwathDataset*sd, Field*fd,const vector<string>& lat_names,
2721 const vector<string>& lon_names,vector<Dimension*>& geo_var_dim1,
2722 vector<Dimension*>& geo_var_dim2) const{
2723 string field_lat_dim1_name =(fd->dims)[0]->name;
2724 string field_lat_dim2_name =(fd->dims)[1]->name;
2725
2726 // Keep the original Latitude/Longitude and the dimensions when GeoDim_in_vars is true.
2727 if (sd->GeoDim_in_vars == true) {
2728 if ((this->swaths).size() >1) {
2729 (fd->dims)[0]->name = field_lat_dim1_name+"_"+sd->name;
2730 (fd->dims)[1]->name = field_lat_dim2_name+"_"+sd->name;
2731 }
2732 geo_var_dim1.push_back((fd->dims)[0]);
2733 geo_var_dim2.push_back((fd->dims)[1]);
2734 }
2735
2736 // Create dimension list for the lats and lons.
2737 // Consider the multi-swath case and if the dimension names of orig. lat/lon
2738 // are used.
2739 // We also need to consider one geo-dim can map to multiple data dim.
2740 // One caveat for the current approach is that we don't consider
2741 // two dimension maps are not created in order. HDFEOS2 API implies
2742 // the dimension maps are created in order.
2743 short dim1_map_count = 0;
2744 short dim2_map_count = 0;
2745 for (const auto &sdmap:sd->getDimensionMaps()) {
2746 if (sdmap->getGeoDimension()==field_lat_dim1_name){
2747 string data_dim1_name = sdmap->getDataDimension();
2748 int dim1_size = sd->obtain_dimsize_with_dimname(data_dim1_name);
2749 if ((this->swaths).size() > 1)
2750 data_dim1_name = data_dim1_name+"_"+sd->name;
2751
2752 if (sd->GeoDim_in_vars == false && dim1_map_count == 0) {
2753 (fd->dims)[0]->name = data_dim1_name;
2754 (fd->dims)[0]->dimsize = dim1_size;
2755 geo_var_dim1.push_back((fd->dims)[0]);
2756 }
2757 else {
2758 auto lat_dim = new Dimension(data_dim1_name,dim1_size);
2759 geo_var_dim1.push_back(lat_dim);
2760 }
2761 dim1_map_count++;
2762 }
2763 else if (sdmap->getGeoDimension()==field_lat_dim2_name){
2764 string data_dim2_name = sdmap->getDataDimension();
2765 int dim2_size = sd->obtain_dimsize_with_dimname(data_dim2_name);
2766 if ((this->swaths).size() > 1)
2767 data_dim2_name = data_dim2_name+"_"+sd->name;
2768 if (sd->GeoDim_in_vars == false && dim2_map_count == 0) {
2769 (fd->dims)[1]->name = data_dim2_name;
2770 (fd->dims)[1]->dimsize = dim2_size;
2771 geo_var_dim2.push_back((fd->dims)[1]);
2772 }
2773 else {
2774 auto lon_dim = new Dimension(data_dim2_name,dim2_size);
2775 geo_var_dim2.push_back(lon_dim);
2776 }
2777 dim2_map_count++;
2778 }
2779 }
2780
2781 // Build up dimension names to coordinate var lists.
2782 for(int i = 0; i<lat_names.size();i++) {
2783 HDFCFUtil::insert_map(sd->dimcvarlist, (geo_var_dim1[i])->name,lat_names[i]);
2784 HDFCFUtil::insert_map(sd->dimcvarlist, (geo_var_dim2[i])->name,lon_names[i]);
2785 }
2786
2787 return;
2788}
2789
2790// Generate lat/lon variables for the multi-dimension map case for this swath.
2791// Original lat/lon variable information is provided.
2792void File::create_geo_vars(SwathDataset* sd,Field *orig_lat,Field*orig_lon,
2793 const vector<string>& lat_names,const vector<string>& lon_names,
2794 vector<Dimension*>&geo_var_dim1,vector<Dimension*>&geo_var_dim2){
2795
2796 // Need to have ll dimension names to obtain the dimension maps
2797 string ll_ori_dim0_name = (orig_lon->dims)[0]->name;
2798 string ll_ori_dim1_name = (orig_lon->dims)[1]->name;
2799 int dmap_offset = 0;
2800 int dmap_inc = 0;
2801 if (sd->GeoDim_in_vars == false) {
2802
2803 // The original lat's dim has been updated in create_geo_dim_var_maps
2804 // In theory , it should be reasonable to do it here. Later.
2805 (orig_lon->dims)[0]->name = geo_var_dim1[0]->name;
2806 (orig_lon->dims)[0]->dimsize = geo_var_dim1[0]->dimsize;
2807 (orig_lon->dims)[1]->name = geo_var_dim2[0]->name;
2808 (orig_lon->dims)[1]->dimsize = geo_var_dim2[0]->dimsize;
2809 string ll_datadim0_name = geo_var_dim1[0]->name;
2810 string ll_datadim1_name = geo_var_dim2[0]->name;
2811 if (this->swaths.size() >1) {
2812 string prefix_remove = "_"+sd->name;
2813 ll_datadim0_name = ll_datadim0_name.substr(0,ll_datadim0_name.size()-prefix_remove.size());
2814 ll_datadim1_name = ll_datadim1_name.substr(0,ll_datadim1_name.size()-prefix_remove.size());
2815 }
2816
2817 // dimension map offset and inc should be retrieved.
2818 if (false == sd->obtain_dmap_offset_inc(ll_ori_dim0_name,ll_datadim0_name,dmap_offset,dmap_inc)){
2819 throw5("Cannot retrieve dimension map offset and inc ",sd->name,
2820 orig_lon->name,ll_ori_dim0_name,ll_datadim0_name);
2821 }
2822 orig_lon->ll_dim0_inc = dmap_inc;
2823 orig_lon->ll_dim0_offset = dmap_offset;
2824 orig_lat->ll_dim0_inc = dmap_inc;
2825 orig_lat->ll_dim0_offset = dmap_offset;
2826
2827 if (false == sd->obtain_dmap_offset_inc(ll_ori_dim1_name,ll_datadim1_name,dmap_offset,dmap_inc)){
2828 throw5("Cannot retrieve dimension map offset and inc ",sd->name,
2829 orig_lon->name,ll_ori_dim1_name,ll_datadim1_name);
2830 }
2831 orig_lon->ll_dim1_inc = dmap_inc;
2832 orig_lon->ll_dim1_offset = dmap_offset;
2833 orig_lat->ll_dim1_inc = dmap_inc;
2834 orig_lat->ll_dim1_offset = dmap_offset;
2835#if 0
2836cerr<<"orig_lon "<<orig_lon->name <<endl;
2837cerr<<"orig_lon dim1 inc "<<orig_lon->ll_dim1_inc<<endl;
2838cerr<<"orig_lon dim1 offset "<<orig_lon->ll_dim1_offset<<endl;
2839cerr<<"orig_lon dim0 inc "<<orig_lon->ll_dim0_inc<<endl;
2840cerr<<"orig_lon dim0 offset "<<orig_lon->ll_dim0_offset<<endl;
2841#endif
2842
2843
2844 }
2845 else {
2846 // if GeoDim is used, we still need to update longitude's dimension names
2847 // if multiple swaths. Latitude was done in create_geo_dim_var_maps().
2848 if ((this->swaths).size() >1) {
2849 (orig_lon->dims)[0]->name = (orig_lon->dims)[0]->name + "_" + sd->name;
2850 (orig_lon->dims)[1]->name = (orig_lon->dims)[1]->name + "_" + sd->name;
2851 }
2852 }
2853
2854 // We also need to update the latitude and longitude names when num_swath is not 1.
2855 if ((this->swaths).size()>1) {
2856 orig_lat->name = lat_names[0];
2857 orig_lon->name = lon_names[0];
2858 }
2859
2860 // Mark the original lat/lon as coordinate variables.
2861 orig_lat->fieldtype = 1;
2862 orig_lon->fieldtype = 2;
2863
2864 // The added fields.
2865 for (int i = 1; i <lat_names.size();i++) {
2866 auto newlat = new Field();
2867 newlat->name = lat_names[i];
2868 (newlat->dims).push_back(geo_var_dim1[i]);
2869 (newlat->dims).push_back(geo_var_dim2[i]);
2870 newlat->fieldtype = 1;
2871 newlat->rank = 2;
2872 newlat->type = orig_lat->type;
2873 auto newlon = new Field();
2874 newlon->name = lon_names[i];
2875 // Here we need to create new Dimensions
2876 // for Longitude.
2877 auto lon_dim1=
2878 new Dimension(geo_var_dim1[i]->name,geo_var_dim1[i]->dimsize);
2879 auto lon_dim2=
2880 new Dimension(geo_var_dim2[i]->name,geo_var_dim2[i]->dimsize);
2881 (newlon->dims).push_back(lon_dim1);
2882 (newlon->dims).push_back(lon_dim2);
2883 newlon->fieldtype = 2;
2884 newlon->rank = 2;
2885 newlon->type = orig_lon->type;
2886
2887 string ll_datadim0_name = geo_var_dim1[i]->name;
2888 string ll_datadim1_name = geo_var_dim2[i]->name;
2889 if (this->swaths.size() >1) {
2890 string prefix_remove = "_"+sd->name;
2891 ll_datadim0_name = ll_datadim0_name.substr(0,ll_datadim0_name.size()-prefix_remove.size());
2892 ll_datadim1_name = ll_datadim1_name.substr(0,ll_datadim1_name.size()-prefix_remove.size());
2893 }
2894
2895 // Obtain dimension map offset and inc for the new lat/lon.
2896 if (false == sd->obtain_dmap_offset_inc(ll_ori_dim0_name,ll_datadim0_name,dmap_offset,dmap_inc)){
2897 throw5("Cannot retrieve dimension map offset and inc ",sd->name,
2898 newlon->name,ll_ori_dim0_name,ll_datadim0_name);
2899 }
2900 newlon->ll_dim0_inc = dmap_inc;
2901 newlon->ll_dim0_offset = dmap_offset;
2902 newlat->ll_dim0_inc = dmap_inc;
2903 newlat->ll_dim0_offset = dmap_offset;
2904 if (false == sd->obtain_dmap_offset_inc(ll_ori_dim1_name,ll_datadim1_name,dmap_offset,dmap_inc)){
2905 throw5("Cannot retrieve dimension map offset and inc ",sd->name,
2906 newlon->name,ll_ori_dim0_name,ll_datadim1_name);
2907 }
2908 newlon->ll_dim1_inc = dmap_inc;
2909 newlon->ll_dim1_offset = dmap_offset;
2910 newlat->ll_dim1_inc = dmap_inc;
2911 newlat->ll_dim1_offset = dmap_offset;
2912#if 0
2913cerr<<"newlon "<<newlon->name <<endl;
2914cerr<<"newlon dim1 inc "<<newlon->ll_dim1_inc<<endl;
2915cerr<<"newlon dim1 offset "<<newlon->ll_dim1_offset<<endl;
2916cerr<<"newlon dim0 inc "<<newlon->ll_dim0_inc<<endl;
2917cerr<<"newlon dim0 offset "<<newlon->ll_dim0_offset<<endl;
2918#endif
2919 sd->geofields.push_back(newlat);
2920 sd->geofields.push_back(newlon);
2921 }
2922#if 0
2923//cerr<<"Latlon under swath: "<<sd->name<<endl;
2925 sd->getGeoFields().begin();
2926 j != sd->getGeoFields().end(); ++j) {
2927cerr<<"Field name: "<<(*j)->name <<endl;
2928cerr<<"Dimension name and size: "<<endl;
2930 (*j)->getDimensions().begin();k!=(*j)->getDimensions().end();++k)
2931cerr<<(*k)->getName() <<": "<<(*k)->getSize() <<endl;
2932}
2933#endif
2934
2935}
2936
2937// We need to update the dimensions for all the swath and all the fields when
2938// we can handle the multi-dimension map case. This only applies to >1 swath.
2939// For one swath, dimension names are not needed to be updated.
2940void File::update_swath_dims_for_dimmap(const SwathDataset* sd,const std::vector<Dimension*>&geo_var_dim1,
2941 const std::vector<Dimension*>&geo_var_dim2) const{
2942
2943 // Loop through each field under geofields and data fields. update dimensions.
2944 // Obtain each dimension name + _+swath_name, if match with geo_var_dim1 or geo_var_dim2;
2945 // Update the dimension names with the matched one.
2946 for (const auto &gfield:sd->getGeoFields()) {
2947 // No need to update latitude/longitude
2948 if (gfield->fieldtype == 1 || gfield->fieldtype == 2)
2949 continue;
2950 for (const auto &dim:gfield->getDimensions()) {
2951 string new_dim_name = dim->name +"_"+sd->name;
2952 if (find_dim_in_dims(geo_var_dim1,new_dim_name) ||
2953 find_dim_in_dims(geo_var_dim2,new_dim_name))
2954 dim->name = new_dim_name;
2955 }
2956 }
2957
2958 for (const auto &dfield:sd->getDataFields()){
2959 for (const auto &dim:dfield->getDimensions()) {
2960 string new_dim_name = dim->name +"_"+sd->name;
2961 if (find_dim_in_dims(geo_var_dim1,new_dim_name) ||
2962 find_dim_in_dims(geo_var_dim2,new_dim_name))
2963 dim->name = new_dim_name;
2964 }
2965 }
2966
2967 // We also need to update the dimension name of this swath.
2968 for (const auto &dim:sd->getDimensions()) {
2969 string new_dim_name = dim->name +"_"+sd->name;
2970 if (find_dim_in_dims(geo_var_dim1,new_dim_name) ||
2971 find_dim_in_dims(geo_var_dim2,new_dim_name))
2972 dim->name = new_dim_name;
2973 }
2974
2975 return;
2976
2977}
2978
2979// This is the main function to handle the multi-dimension map case.
2980// It creates the lat/lon lists, handle dimension names and then
2981// provide the dimension name to coordinate variable map.
2982void File::create_swath_latlon_dim_cvar_map_for_dimmap(SwathDataset* sd, Field* ori_lat, Field* ori_lon) {
2983
2984 bool one_swath = ((this->swaths).size() == 1);
2985
2986 int num_extra_lat_lon_pairs = 0;
2987
2988#if 0
2989if (sd->GeoDim_in_vars == true)
2990 cerr<<" swath name is "<<sd->name <<endl;
2991#endif
2992
2993 // Since the original lat/lon will be kept for lat/lon with the first dimension map..
2994 if (sd->GeoDim_in_vars == false)
2995 num_extra_lat_lon_pairs--;
2996
2997 num_extra_lat_lon_pairs += (sd->num_map)/2;
2998
2999 vector<string> lat_names;
3000 create_geo_varnames_list(lat_names,sd->name,ori_lat->name,num_extra_lat_lon_pairs,one_swath);
3001 vector<string>lon_names;
3002 create_geo_varnames_list(lon_names,sd->name,ori_lon->name,num_extra_lat_lon_pairs,one_swath);
3003 vector<Dimension*> geo_var_dim1;
3004 vector<Dimension*> geo_var_dim2;
3005
3006 // Define dimensions or obtain dimensions for new field.
3007 create_geo_dim_var_maps(sd, ori_lat,lat_names,lon_names,geo_var_dim1,geo_var_dim2);
3008 create_geo_vars(sd,ori_lat,ori_lon,lat_names,lon_names,geo_var_dim1,geo_var_dim2);
3009
3010 // Update dims for vars,this is only necessary when there are multiple swaths
3011 // Dimension names need to be updated to include swath names.
3012 if ((this->swaths).size() >1)
3013 update_swath_dims_for_dimmap(sd,geo_var_dim1,geo_var_dim2);
3014
3015}
3016
3020void File::Prepare(const char *eosfile_path)
3021{
3022
3023 // check if this is a special HDF-EOS2 grid(MOD08_M3) that have all dimension scales
3024 // added by the HDF4 library but the relation between the dimension scale and the dimension is not
3025 // specified. If the return value is true, we will specify
3026
3027 // Obtain the number of swaths and the number of grids
3028 auto numgrid = (int)(this->grids.size());
3029 auto numswath = (int)(this->swaths.size());
3030
3031 if (numgrid < 0)
3032 throw2("the number of grid is less than 0", eosfile_path);
3033
3034 // First handle grids
3035 if (numgrid > 0) {
3036
3037 // Obtain "XDim","YDim","Latitude","Longitude" and "location" set.
3038 string DIMXNAME = this->get_geodim_x_name();
3039
3040 string DIMYNAME = this->get_geodim_y_name();
3041
3042 string LATFIELDNAME = this->get_latfield_name();
3043
3044 string LONFIELDNAME = this->get_lonfield_name();
3045
3046 // Now only there is only one geo grid name "location", so don't call it know.
3047 string GEOGRIDNAME = "location";
3048
3049 // Check global lat/lon for multiple grids.
3050 // We want to check if there is a global lat/lon for multiple grids.
3051 // AIRS level 3 data provides lat/lon under the GEOGRIDNAME grid.
3052 check_onelatlon_grids();
3053
3054 // Handle the third-dimension(both existing and missing) coordinate variables
3055 for (const auto &grid:this->grids)
3056 handle_one_grid_zdim(grid);
3057
3058
3059 // Handle lat/lon fields for the case of which all grids have one dedicated lat/lon grid.
3060 if (true == this->onelatlon)
3061 handle_onelatlon_grids();
3062 else {
3063 for (const auto &grid:this->grids) {
3064
3065 // Set the horizontal dimension name "dimxname" and "dimyname"
3066 // This will be used to detect the dimension major order.
3067 grid->setDimxName(DIMXNAME);
3068 grid->setDimyName(DIMYNAME);
3069
3070 // Handle lat/lon(both existing lat/lon and calculated lat/lon from EOS2 APIs)
3071 handle_one_grid_latlon(grid);
3072 }
3073 }
3074
3075 // Handle the dimension name to coordinate variable map for grid.
3076 handle_grid_dim_cvar_maps();
3077
3078 // Follow COARDS for grids.
3079 handle_grid_coards();
3080
3081 // Create the corrected dimension vector for each field when COARDS is not followed.
3082 update_grid_field_corrected_dims();
3083
3084 // Handle CF attributes for grids.
3085 handle_grid_cf_attrs();
3086
3087 // Special handling SOM(Space Oblique Mercator) projection files
3088 handle_grid_SOM_projection();
3089
3090
3091 }// End of handling grid
3092
3093 // Check and set the scale type
3094 for (const auto& grid:this->grids)
3095 grid->SetScaleType(grid->name);
3096
3097 if (numgrid==0) {
3098
3099 // Now we handle swath case.
3100 if (numswath > 0) {
3101
3102 // Check if we need to handle dimension maps in this file.
3103 check_swath_dimmap(numswath);
3104
3105 // Check if GeoDim is used by variables when dimension maps are present.
3106 check_dm_geo_dims_in_vars();
3107
3108 // If we need to handle dimension maps,check if we need to keep the old way.
3109 check_swath_dimmap_bk_compat(numswath);
3110
3111 // Create the dimension name to coordinate variable name map for lat/lon.
3112 create_swath_latlon_dim_cvar_map();
3113
3114 // Create the dimension name to coordinate variable name map for non lat/lon coordinate variables.
3115 create_swath_nonll_dim_cvar_map();
3116
3117 // Handle swath dimension name to coordinate variable name maps.
3118 handle_swath_dim_cvar_maps();
3119
3120 // Handle CF attributes for swaths.
3121 // The CF attributes include "coordinates", "units" for coordinate variables and "_FillValue".
3122 handle_swath_cf_attrs();
3123
3124 // Check and set the scale type
3125 for (const auto &swath:this->swaths)
3126 swath->SetScaleType(swath->name);
3127 }
3128
3129 }// End of handling swath
3130
3131}
3132
3133bool File::check_special_1d_grid() {
3134
3135 auto numgrid = (int)(this->grids.size());
3136 auto numswath = (int)(this->swaths.size());
3137
3138 if (numgrid != 1 || numswath != 0)
3139 return false;
3140
3141 // Obtain "XDim","YDim","Latitude","Longitude" and "location" set.
3142 string DIMXNAME = this->get_geodim_x_name();
3143 string DIMYNAME = this->get_geodim_y_name();
3144
3145 if (DIMXNAME != "XDim" || DIMYNAME != "YDim")
3146 return false;
3147
3148 int var_dimx_size = 0;
3149 int var_dimy_size = 0;
3150
3151 const GridDataset *mygrid = (this->grids)[0];
3152
3153 int field_xydim_flag = 0;
3154 for (const auto &dfield:mygrid->getDataFields()) {
3155 if (1==dfield->rank) {
3156 if (dfield->name == "XDim"){
3157 field_xydim_flag++;
3158 var_dimx_size = (dfield->getDimensions())[0]->getSize();
3159 }
3160 if (dfield->name == "YDim"){
3161 field_xydim_flag++;
3162 var_dimy_size = (dfield->getDimensions())[0]->getSize();
3163 }
3164 }
3165 if (2==field_xydim_flag)
3166 break;
3167 }
3168
3169 if (field_xydim_flag !=2)
3170 return false;
3171
3172 // Obtain XDim and YDim size.
3173 int xdimsize = mygrid->getInfo().getX();
3174 int ydimsize = mygrid->getInfo().getY();
3175
3176 if (var_dimx_size != xdimsize || var_dimy_size != ydimsize)
3177 return false;
3178
3179 return true;
3180
3181}
3182
3183
3184bool File::check_ll_in_coords(const string& vname) {
3185
3186 bool ret_val = false;
3187 for (const auto &swath:this->swaths) {
3188 for (const auto &gfield:swath->getGeoFields()) {
3189 // Real fields: adding the coordinate attribute
3190 if (gfield->fieldtype == 1 || gfield->fieldtype == 2) {// currently it is always true.
3191 if (gfield->getNewName() == vname) {
3192 ret_val = true;
3193 break;
3194 }
3195 }
3196 }
3197 if (true == ret_val)
3198 break;
3199 for (const auto &dfield:swath->getDataFields()) {
3200
3201 // Real fields: adding the coordinate attribute
3202 if (dfield->fieldtype == 1 || dfield->fieldtype == 2) {// currently it is always true.
3203 if (dfield->getNewName() == vname) {
3204 ret_val = true;
3205 break;
3206 }
3207 }
3208 }
3209 if (true == ret_val)
3210 break;
3211
3212 }
3213 return ret_val;
3214}
3215
3216
3217// Set scale and offset type
3218// MODIS data has three scale and offset rules.
3219// They are
3220// MODIS_EQ_SCALE: raw_data = scale*data + offset
3221// MODIS_MUL_SCALE: raw_data = scale*(data -offset)
3222// MODIS_DIV_SCALE: raw_data = (data-offset)/scale
3223
3224void Dataset::SetScaleType(const string & EOS2ObjName) {
3225
3226
3227 // Group features of MODIS products.
3228
3229 vector<string> modis_multi_scale_type;
3230 modis_multi_scale_type.emplace_back("L1B");
3231 modis_multi_scale_type.emplace_back("GEO");
3232 modis_multi_scale_type.emplace_back("BRDF");
3233 modis_multi_scale_type.emplace_back("0.05Deg");
3234 modis_multi_scale_type.emplace_back("Reflectance");
3235 modis_multi_scale_type.emplace_back("MOD17A2");
3236 modis_multi_scale_type.emplace_back("North");
3237 modis_multi_scale_type.emplace_back("South");
3238 modis_multi_scale_type.emplace_back("MOD_Swath_Sea_Ice");
3239 modis_multi_scale_type.emplace_back("MOD_Grid_MOD15A2");
3240 modis_multi_scale_type.emplace_back("MOD_Grid_MOD16A2");
3241 modis_multi_scale_type.emplace_back("MOD_Grid_MOD16A3");
3242 modis_multi_scale_type.emplace_back("MODIS_NACP_LAI");
3243
3244 vector<string> modis_div_scale_type;
3245
3246 // Always start with MOD or mod.
3247 modis_div_scale_type.emplace_back("VI");
3248 modis_div_scale_type.emplace_back("1km_2D");
3249 modis_div_scale_type.emplace_back("L2g_2d");
3250 modis_div_scale_type.emplace_back("CMG");
3251 modis_div_scale_type.emplace_back("MODIS SWATH TYPE L2");
3252
3253 string modis_eq_scale_type = "LST";
3254 string modis_equ_scale_lst_group1="MODIS_Grid_8Day_1km_LST21";
3255 string modis_equ_scale_lst_group2="MODIS_Grid_Daily_1km_LST21";
3256
3257 string modis_divequ_scale_group = "MODIS_Grid";
3258 string modis_div_scale_group = "MOD_Grid";
3259
3260 // The scale-offset rule for the following group may be MULTI but since add_offset is 0. So
3261 // the MULTI rule is equal to the EQU rule. KY 2013-01-25
3262 string modis_equ_scale_group = "MODIS_Grid_1km_2D";
3263
3264 if (EOS2ObjName=="mod05" || EOS2ObjName=="mod06" || EOS2ObjName=="mod07"
3265 || EOS2ObjName=="mod08" || EOS2ObjName=="atml2")
3266 {
3267 scaletype = SOType::MODIS_MUL_SCALE;
3268 return;
3269 }
3270
3271 // Find one MYD09GA2012.version005 file that
3272 // the grid names change to MODIS_Grid_500m_2D.
3273 // So add this one. KY 2012-11-20
3274
3275 // Find the grid name in one MCD43C1 file starts with "MCD_CMG_BRDF",
3276 // however, the offset of the data is 0.
3277 // So we may not handle this file here since it follows the CF conventions.
3278 // May need to double check them later. KY 2013-01-24
3279
3280
3281 if (EOS2ObjName.find("MOD")==0 || EOS2ObjName.find("mod")==0)
3282 {
3283 size_t pos = EOS2ObjName.rfind(modis_eq_scale_type);
3284 if (pos != string::npos &&
3285 (pos== (EOS2ObjName.size()-modis_eq_scale_type.size())))
3286 {
3287 scaletype = SOType::MODIS_EQ_SCALE;
3288 return;
3289 }
3290
3291 for(unsigned int k=0; k<modis_multi_scale_type.size(); k++)
3292 {
3293 pos = EOS2ObjName.rfind(modis_multi_scale_type[k]);
3294 if (pos !=string::npos &&
3295 (pos== (EOS2ObjName.size()-modis_multi_scale_type[k].size())))
3296 {
3297 scaletype = SOType::MODIS_MUL_SCALE;
3298 return;
3299 }
3300 }
3301
3302 for(unsigned int k=0; k<modis_div_scale_type.size(); k++)
3303 {
3304 pos = EOS2ObjName.rfind(modis_div_scale_type[k]);
3305 if (pos != string::npos &&
3306 (pos==(EOS2ObjName.size()-modis_div_scale_type[k].size()))){
3307 scaletype = SOType::MODIS_DIV_SCALE;
3308
3309 // We have a case that group
3310 // MODIS_Grid_1km_2D should apply the equal scale equation.
3311 // This will be handled after this loop.
3312 if (EOS2ObjName != "MODIS_Grid_1km_2D")
3313 return;
3314 }
3315 }
3316
3317 // Special handling for MOD_Grid and MODIS_Grid_500m_2D.
3318 // Check if the group name starts with the modis_divequ and modis_div_scale.
3319 pos = EOS2ObjName.find(modis_divequ_scale_group);
3320
3321 // Find the "MODIS_Grid???" group.
3322 // We have to separate MODIS_Grid_1km_2D(EQ),MODIS_Grid_8Day_1km_LST21
3323 // and MODIS_Grid_Daily_1km_LST21 from other grids(DIV).
3324 if (0 == pos) {
3325 size_t eq_scale_pos = EOS2ObjName.find(modis_equ_scale_group)
3326 *EOS2ObjName.find(modis_equ_scale_lst_group1)
3327 *EOS2ObjName.find(modis_equ_scale_lst_group2);
3328 if (0 == eq_scale_pos)
3329 scaletype = SOType::MODIS_EQ_SCALE;
3330 else
3331 scaletype = SOType::MODIS_DIV_SCALE;
3332 }
3333 else {
3334 size_t div_scale_pos = EOS2ObjName.find(modis_div_scale_group);
3335
3336 // Find the "MOD_Grid???" group.
3337 if ( 0 == div_scale_pos)
3338 scaletype = SOType::MODIS_DIV_SCALE;
3339 }
3340 }
3341
3342 // MEASuRES VIP files have the grid name VIP_CMG_GRID.
3343 // This applies to all VIP version 2 files. KY 2013-01-24
3344 if (EOS2ObjName =="VIP_CMG_GRID")
3345 scaletype = SOType::MODIS_DIV_SCALE;
3346}
3347
3348int Dataset::obtain_dimsize_with_dimname(const string & dimname) const{
3349
3350 int ret_value = -1;
3351
3352 for (const auto & dim:this->getDimensions()) {
3353 if (dim->name == dimname){
3354 ret_value = dim->dimsize;
3355 break;
3356 }
3357 }
3358
3359 return ret_value;
3360}
3361
3362// Release resources
3363Field::~Field()
3364{
3365 for_each(this->dims.begin(), this->dims.end(), delete_elem());
3366 for_each(this->correcteddims.begin(), this->correcteddims.end(), delete_elem());
3367}
3368
3369// Release resources
3370Dataset::~Dataset()
3371{
3372 for_each(this->dims.begin(), this->dims.end(), delete_elem());
3373 for_each(this->datafields.begin(), this->datafields.end(),
3374 delete_elem());
3375 for_each(this->attrs.begin(), this->attrs.end(), delete_elem());
3376}
3377
3378// Retrieve dimensions of grids or swaths
3379void Dataset::ReadDimensions(int32 (*entries)(int32, int32, int32 *),
3380 int32 (*inq)(int32, char *, int32 *),
3381 vector<Dimension *> &d_dims)
3382{
3383 // Initialize number of dimensions
3384 int32 numdims = 0;
3385
3386 // Initialize buf size
3387 int32 bufsize = 0;
3388
3389 // Obtain the number of dimensions and buffer size of holding ","
3390 // separated dimension name list.
3391 if ((numdims = entries(this->datasetid, HDFE_NENTDIM, &bufsize)) == -1)
3392 throw2("dimension entry", this->name);
3393
3394 // Read all dimension information
3395 if (numdims > 0) {
3396 vector<char> namelist;
3397 vector<int32> dimsize;
3398
3399 namelist.resize(bufsize + 1);
3400 dimsize.resize(numdims);
3401
3402 // Inquiry dimension name lists and sizes for all dimensions
3403 if (inq(this->datasetid, namelist.data(), dimsize.data()) == -1)
3404 throw2("inquire dimension", this->name);
3405
3406 vector<string> dimnames;
3407
3408 // Make the "," separated name string to a string list without ",".
3409 // This split is for global dimension of a Swath or a Grid object.
3410 HDFCFUtil::Split(namelist.data(), bufsize, ',', dimnames);
3411 int count = 0;
3412
3413 for (const auto &dimname:dimnames) {
3414 auto dim = new Dimension(dimname, dimsize[count]);
3415 d_dims.push_back(dim);
3416 ++count;
3417 }
3418
3419 }
3420}
3421
3422// Retrieve data field info.
3423void Dataset::ReadFields(int32 (*entries)(int32, int32, int32 *),
3424 int32 (*inq)(int32, char *, int32 *, int32 *),
3425 intn (*fldinfo)
3426 (int32, char *, int32 *, int32 *, int32 *, char *),
3427 intn (*getfill)(int32, char *, VOIDP),
3428 bool geofield,
3429 vector<Field *> &fields)
3430{
3431
3432 // Initalize the number of fields
3433 int32 numfields = 0;
3434
3435 // Initalize the buffer size
3436 int32 bufsize = 0;
3437
3438 // Obtain the number of fields and buffer size for the current Swath or
3439 // Grid.
3440 if ((numfields = entries(this->datasetid, geofield ?
3441 HDFE_NENTGFLD : HDFE_NENTDFLD, &bufsize)) == -1)
3442 throw2("field entry", this->name);
3443
3444 // Obtain the information of fields (either data fields or geo-location
3445 // fields of a Swath object)
3446 if (numfields > 0) {
3447 vector<char> namelist;
3448
3449 namelist.resize(bufsize + 1);
3450
3451 // Inquiry fieldname list of the current object
3452 if (inq(this->datasetid, namelist.data(), nullptr, nullptr) == -1)
3453 throw2("inquire field", this->name);
3454
3455 vector<string> fieldnames;
3456
3457 // Split the field namelist, make the "," separated name string to a
3458 // string list without ",".
3459 HDFCFUtil::Split(namelist.data(), bufsize, ',', fieldnames);
3460 for (const auto& fdname:fieldnames){
3461
3462 auto field = new Field();
3463 if (field == nullptr)
3464 throw1("The field is nullptr");
3465 field->name = fdname;
3466
3467 bool throw_error = false;
3468 string err_msg;
3469
3470 // XXX: We assume the maximum number of dimension for an EOS field
3471 // is 16.
3472 int32 dimsize[16];
3473 char dimlist[512]; // XXX: what an HDF-EOS2 developer recommended
3474
3475 // Obtain most information of a field such as rank, dimension etc.
3476 if ((fldinfo(this->datasetid,
3477 const_cast<char *>(field->name.c_str()),
3478 &field->rank, dimsize, &field->type, dimlist)) == -1){
3479 string fieldname_for_eh = field->name;
3480 throw_error = true;
3481 err_msg ="Obtain field info error for field name "+fieldname_for_eh;
3482 }
3483
3484 if (false == throw_error) {
3485
3486 vector<string> dimnames;
3487
3488 // Split the dimension name list for a field
3489 HDFCFUtil::Split(dimlist, ',', dimnames);
3490 if ((int)dimnames.size() != field->rank) {
3491 throw_error = true;
3492 err_msg = "Dimension names size is not consistent with field rank. ";
3493 err_msg += "Field name is "+field->name;
3494 }
3495 else {
3496 for (int k = 0; k < field->rank; ++k) {
3497 auto dim = new Dimension(dimnames[k], dimsize[k]);
3498 field->dims.push_back(dim);
3499 }
3500
3501 // Get fill value of a field
3502 field->filler.resize(DFKNTsize(field->type));
3503 if (getfill(this->datasetid,
3504 const_cast<char *>(field->name.c_str()),
3505 &field->filler[0]) == -1)
3506 field->filler.clear();
3507
3508 // Append the field into the fields vector.
3509 fields.push_back(field);
3510 }
3511 }
3512
3513 if (true == throw_error) {
3514 delete field;
3515 throw1(err_msg);
3516
3517 }
3518 }
3519 }
3520}
3521
3522// Retrieve attribute info.
3523void Dataset::ReadAttributes(int32 (*inq)(int32, char *, int32 *),
3524 intn (*attrinfo)(int32, char *, int32 *, int32 *),
3525 intn (*readattr)(int32, char *, VOIDP),
3526 vector<Attribute *> &obj_attrs)
3527{
3528 // Initalize the number of attributes to be 0
3529 int32 numattrs = 0;
3530
3531 // Initalize the buffer size to be 0
3532 int32 bufsize = 0;
3533
3534 // Obtain the number of attributes in a Grid or Swath
3535 if ((numattrs = inq(this->datasetid, nullptr, &bufsize)) == -1)
3536 throw2("inquire attribute", this->name);
3537
3538 // Obtain the list of "name, type, value" tuple
3539 if (numattrs > 0) {
3540 vector<char> namelist;
3541
3542 namelist.resize(bufsize + 1);
3543
3544 // inquiry namelist and buffer size
3545 if (inq(this->datasetid, namelist.data(), &bufsize) == -1)
3546 throw2("inquire attribute", this->name);
3547
3548 vector<string> attrnames;
3549
3550 // Split the attribute namelist, make the "," separated name string to
3551 // a string list without ",".
3552 HDFCFUtil::Split(namelist.data(), bufsize, ',', attrnames);
3553 for (const auto &attrname:attrnames) {
3554 auto attr = new Attribute();
3555 attr->name = attrname;
3556 attr->newname = HDFCFUtil::get_CF_string(attr->name);
3557
3558 int32 count = 0;
3559
3560 // Obtain the datatype and byte count of this attribute
3561 if (attrinfo(this->datasetid, const_cast<char *>
3562 (attr->name.c_str()), &attr->type, &count) == -1) {
3563 delete attr;
3564 throw3("attribute info", this->name, attr->name);
3565 }
3566
3567 attr->count = count/DFKNTsize(attr->type);
3568 attr->value.resize(count);
3569
3570
3571 // Obtain the attribute value. Note that this function just
3572 // provides a copy of all attribute values.
3573 //The client should properly interpret to obtain individual
3574 // attribute value.
3575 if (readattr(this->datasetid,
3576 const_cast<char *>(attr->name.c_str()),
3577 &attr->value[0]) == -1) {
3578 delete attr;
3579 throw3("read attribute", this->name, attr->name);
3580 }
3581
3582 // Append this attribute to attrs list.
3583 obj_attrs.push_back(attr);
3584 }
3585 }
3586}
3587
3588// Release grid dataset resources
3589GridDataset::~GridDataset()
3590{
3591 if (this->datasetid != -1){
3592 GDdetach(this->datasetid);
3593 }
3594}
3595
3596// Retrieve all information of the grid dataset
3597GridDataset * GridDataset::Read(int32 fd, const string &gridname)
3598{
3599 string err_msg;
3600 bool GD_fun_err = false;
3601 auto grid = new GridDataset(gridname);
3602
3603 // Open this Grid object
3604 if ((grid->datasetid = GDattach(fd, const_cast<char *>(gridname.c_str())))
3605 == -1) {
3606 err_msg = "attach grid";
3607 GD_fun_err = true;
3608 goto cleanFail;
3609 }
3610
3611 // Obtain the size of XDim and YDim as well as latitude and longitude of
3612 // the upleft corner and the low right corner.
3613 {
3614 Info &info = grid->info;
3615 if (GDgridinfo(grid->datasetid, &info.xdim, &info.ydim, info.upleft,
3616 info.lowright) == -1) {
3617 err_msg = "grid info";
3618 GD_fun_err = true;
3619 goto cleanFail;
3620 }
3621 }
3622
3623 // Obtain projection information.
3624 {
3625 Projection &proj = grid->proj;
3626 if (GDprojinfo(grid->datasetid, &proj.code, &proj.zone, &proj.sphere,
3627 proj.param) == -1) {
3628 err_msg = "projection info";
3629 GD_fun_err = true;
3630 goto cleanFail;
3631 }
3632 if (GDpixreginfo(grid->datasetid, &proj.pix) == -1) {
3633 err_msg = "pixreg info";
3634 GD_fun_err = true;
3635 goto cleanFail;
3636 }
3637 if (GDorigininfo(grid->datasetid, &proj.origin) == -1){
3638 err_msg = "origin info";
3639 GD_fun_err = true;
3640 goto cleanFail;
3641 }
3642 }
3643cleanFail:
3644 if (true == GD_fun_err){
3645 delete grid;
3646 throw2(err_msg,gridname);
3647 }
3648
3649 try {
3650 // Read dimensions
3651 grid->ReadDimensions(GDnentries, GDinqdims, grid->dims);
3652
3653 // Read all fields of this Grid.
3654 grid->ReadFields(GDnentries, GDinqfields, GDfieldinfo,
3655 GDgetfillvalue, false, grid->datafields);
3656
3657 // Read all attributes of this Grid.
3658 grid->ReadAttributes(GDinqattrs, GDattrinfo, GDreadattr, grid->attrs);
3659 }
3660 catch (...) {
3661 delete grid;
3662 throw;
3663 }
3664
3665 return grid;
3666}
3667
3668GridDataset::Calculated & GridDataset::getCalculated() const
3669{
3670 if (this->calculated.grid != this)
3671 this->calculated.grid = this;
3672 return this->calculated;
3673}
3674
3675bool GridDataset::Calculated::isYDimMajor()
3676{
3677 this->DetectMajorDimension();
3678 return this->ydimmajor;
3679}
3680
3681
3682int GridDataset::Calculated::DetectFieldMajorDimension() const
3683{
3684 int ym = -1;
3685
3686 // Traverse all data fields
3687 for (const auto& gfd:this->grid->getDataFields()) {
3688
3689 int xdimindex = -1;
3690 int ydimindex = -1;
3691 int index = 0;
3692
3693 // Traverse all dimensions in each data field
3694 for (const auto& dim:gfd->getDimensions()) {
3695 if (dim->getName() == this->grid->dimxname)
3696 xdimindex = index;
3697 else if (dim->getName() == this->grid->dimyname)
3698 ydimindex = index;
3699 ++index;
3700 }
3701 if (xdimindex == -1 || ydimindex == -1)
3702 continue;
3703
3704 int major = ydimindex < xdimindex ? 1 : 0;
3705
3706 if (ym == -1)
3707 ym = major;
3708 break;
3709
3710 // TO gain performance, just check one field.
3711 // The dimension order for all data fields in a grid should be
3712 // consistent.
3713 // Kent adds this if 0 block 2012-09-19
3714#if 0
3715 else if (ym != major)
3716 throw2("inconsistent XDim, YDim order", this->grid->getName());
3717#endif
3718
3719 }
3720 // At least one data field should refer to XDim or YDim
3721 if (ym == -1)
3722 throw2("lack of data fields", this->grid->getName());
3723
3724 return ym;
3725}
3726
3727void GridDataset::Calculated::DetectMajorDimension()
3728{
3729 int ym = -1;
3730 // ydimmajor := true if (YDim, XDim)
3731 // ydimmajor := false if (XDim, YDim)
3732
3733 // Traverse all data fields
3734
3735 for (const auto& fd:this->grid->getDataFields()) {
3736
3737 int xdimindex = -1;
3738 int ydimindex = -1;
3739 int index = 0;
3740
3741 // Traverse all dimensions in each data field
3742 for (const auto& dim:fd->getDimensions()) {
3743 if (dim->getName() == this->grid->dimxname)
3744 xdimindex = index;
3745 else if (dim->getName() == this->grid->dimyname)
3746 ydimindex = index;
3747 ++index;
3748 }
3749 if (xdimindex == -1 || ydimindex == -1)
3750 continue;
3751
3752 // Change the way of deciding the major dimesion (LD -2012/01/10).
3753 int major;
3754 if (this->grid->getProjection().getCode() == GCTP_LAMAZ)
3755 major = 1;
3756 else
3757 major = ydimindex < xdimindex ? 1 : 0;
3758
3759 if (ym == -1)
3760 ym = major;
3761 break;
3762
3763 // TO gain performance, just check one field.
3764 // The dimension order for all data fields in a grid should be
3765 // consistent.
3766 // Kent adds this if 0 block
3767#if 0
3768 else if (ym != major)
3769 throw2("inconsistent XDim, YDim order", this->grid->getName());
3770#endif
3771 }
3772 // At least one data field should refer to XDim or YDim
3773 if (ym == -1)
3774 throw2("lack of data fields", this->grid->getName());
3775 this->ydimmajor = ym != 0;
3776}
3777
3778// The following internal utilities are not used currently, will see if
3779// they are necessary in the future. KY 2012-09-19
3780// The internal utility method to check if two vectors have overlapped.
3781// If not, return true.
3782// Not used. Temporarily comment out to avoid the compiler warnings.
3783#if 0
3784static bool IsDisjoint(const vector<Field *> &l,
3785 const vector<Field *> &r)
3786{
3787 for (vector<Field *>::const_iterator i = l.begin(); i != l.end(); ++i)
3788 {
3789 if (find(r.begin(), r.end(), *i) != r.end())
3790 return false;
3791 }
3792 return true;
3793}
3794
3795// The internal utility method to check if two vectors have overlapped.
3796// If not, return true.
3797static bool IsDisjoint(vector<pair<Field *, string> > &l, const vector<Field *> &r)
3798{
3799 for (vector<pair<Field *, string> >::const_iterator i =
3800 l.begin(); i != l.end(); ++i) {
3801 if (find(r.begin(), r.end(), i->first) != r.end())
3802 return false;
3803 }
3804 return true;
3805}
3806
3807// The internal utility method to check if vector s is a subset of vector b.
3808static bool IsSubset(const vector<Field *> &s, const vector<Field *> &b)
3809{
3810 for (vector<Field *>::const_iterator i = s.begin(); i != s.end(); ++i)
3811 {
3812 if (find(b.begin(), b.end(), *i) == b.end())
3813 return false;
3814 }
3815 return true;
3816}
3817
3818// The internal utility method to check if vector s is a subset of vector b.
3819static bool IsSubset(vector<pair<Field *, string> > &s, const vector<Field *> &b)
3820{
3821 for (vector<pair<Field *, string> >::const_iterator i
3822 = s.begin(); i != s.end(); ++i) {
3823 if (find(b.begin(), b.end(), i->first) == b.end())
3824 return false;
3825 }
3826 return true;
3827}
3828
3829#endif
3830// Destructor, release resources
3831SwathDataset::~SwathDataset()
3832{
3833 if (this->datasetid != -1) {
3834 SWdetach(this->datasetid);
3835 }
3836
3837 for_each(this->dimmaps.begin(), this->dimmaps.end(), delete_elem());
3838 for_each(this->indexmaps.begin(), this->indexmaps.end(),
3839 delete_elem());
3840
3841 for_each(this->geofields.begin(), this->geofields.end(),
3842 delete_elem());
3843 return;
3844
3845}
3846
3847// Read all information of this swath
3848SwathDataset * SwathDataset::Read(int32 fd, const string &swathname)
3849
3850{
3851 auto swath = new SwathDataset(swathname);
3852 if (swath == nullptr)
3853 throw1("Cannot allocate HDF5 Swath object");
3854
3855 // Open this Swath object
3856 if ((swath->datasetid = SWattach(fd,
3857 const_cast<char *>(swathname.c_str())))
3858 == -1) {
3859 delete swath;
3860 throw2("attach swath", swathname);
3861 }
3862
3863 try {
3864
3865 // Read dimensions of this Swath
3866 swath->ReadDimensions(SWnentries, SWinqdims, swath->dims);
3867
3868 // Read all information related to data fields of this Swath
3869 swath->ReadFields(SWnentries, SWinqdatafields, SWfieldinfo,
3870 SWgetfillvalue, false, swath->datafields);
3871
3872 // Read all information related to geo-location fields of this Swath
3873 swath->ReadFields(SWnentries, SWinqgeofields, SWfieldinfo,
3874 SWgetfillvalue, true, swath->geofields);
3875
3876 // Read all attributes of this Swath
3877 swath->ReadAttributes(SWinqattrs, SWattrinfo, SWreadattr, swath->attrs);
3878
3879 // Read dimension map and save the number of dimension map for dim. subsetting
3880 swath->set_num_map(swath->ReadDimensionMaps(swath->dimmaps));
3881
3882 // Read index maps, we haven't found any files with the Index Maps.
3883 swath->ReadIndexMaps(swath->indexmaps);
3884 }
3885 catch (...) {
3886 delete swath;
3887 throw;
3888 }
3889 //}
3890
3891 return swath;
3892}
3893
3894// Read dimension map info.
3895int SwathDataset::ReadDimensionMaps(vector<DimensionMap *> &swath_dimmaps)
3896
3897{
3898 int32 nummaps;
3899 int32 bufsize;
3900
3901 // Obtain number of dimension maps and the buffer size.
3902 if ((nummaps = SWnentries(this->datasetid, HDFE_NENTMAP, &bufsize)) == -1)
3903 throw2("dimmap entry", this->name);
3904
3905 // Will use Split utility method to generate a list of dimension map.
3906 // An example of original representation of a swath dimension map:(d1/d2,
3907 // d3/d4,...)
3908 // d1:the name of this dimension of the data field , d2: the name of the
3909 // corresponding dimension of the geo-location field
3910 // The list will be decomposed from (d1/d2,d3/d4,...) to {[d1,d2,0(offset),
3911 // 1(increment)],[d3,d4,0(offset),1(increment)],...}
3912
3913 if (nummaps > 0) {
3914 vector<char> namelist;
3915 vector<int32> offset;
3916 vector<int32> increment;
3917
3918 namelist.resize(bufsize + 1);
3919 offset.resize(nummaps);
3920 increment.resize(nummaps);
3921 if (SWinqmaps(this->datasetid, namelist.data(), offset.data(), increment.data())
3922 == -1)
3923 throw2("inquire dimmap", this->name);
3924
3925 vector<string> mapnames;
3926 HDFCFUtil::Split(namelist.data(), bufsize, ',', mapnames);
3927 int count = 0;
3928 for (const auto& mapname:mapnames) {
3929 vector<string> parts;
3930 HDFCFUtil::Split(mapname.c_str(), '/', parts);
3931 if (parts.size() != 2)
3932 throw3("dimmap part", parts.size(),
3933 this->name);
3934
3935 DimensionMap *dimmap = new DimensionMap(parts[0], parts[1],
3936 offset[count],
3937 increment[count]);
3938 swath_dimmaps.push_back(dimmap);
3939 ++count;
3940 }
3941 }
3942 return nummaps;
3943}
3944
3945// The following function is nevered tested and probably will never be used.
3946void SwathDataset::ReadIndexMaps(vector<IndexMap *> &swath_indexmaps)
3947
3948{
3949 int32 numindices;
3950 int32 bufsize;
3951
3952 if ((numindices = SWnentries(this->datasetid, HDFE_NENTIMAP, &bufsize)) ==
3953 -1)
3954 throw2("indexmap entry", this->name);
3955 if (numindices > 0) {
3956 // TODO: I have never seen any EOS2 files that have index map.
3957 vector<char> namelist;
3958
3959 namelist.resize(bufsize + 1);
3960 if (SWinqidxmaps(this->datasetid, namelist.data(), nullptr) == -1)
3961 throw2("inquire indexmap", this->name);
3962
3963 vector<string> mapnames;
3964 HDFCFUtil::Split(namelist.data(), bufsize, ',', mapnames);
3965 for (const auto& mapname: mapnames) {
3966 auto indexmap = new IndexMap();
3967 vector<string> parts;
3968 HDFCFUtil::Split(mapname.c_str(), '/', parts);
3969 if (parts.size() != 2)
3970 throw3("indexmap part", parts.size(),
3971 this->name);
3972 indexmap->geo = parts[0];
3973 indexmap->data = parts[1];
3974
3975 {
3976 int32 dimsize;
3977 if ((dimsize =
3978 SWdiminfo(this->datasetid,
3979 const_cast<char *>(indexmap->geo.c_str())))
3980 == -1)
3981 throw3("dimension info", this->name, indexmap->geo);
3982 indexmap->indices.resize(dimsize);
3983 if (SWidxmapinfo(this->datasetid,
3984 const_cast<char *>(indexmap->geo.c_str()),
3985 const_cast<char *>(indexmap->data.c_str()),
3986 &indexmap->indices[0]) == -1)
3987 throw4("indexmap info", this->name, indexmap->geo,
3988 indexmap->data);
3989 }
3990
3991 swath_indexmaps.push_back(indexmap);
3992 }
3993 }
3994}
3995
3996
3997PointDataset::~PointDataset()
3998{
3999}
4000
4001PointDataset * PointDataset::Read(int32 /*fd*/, const string &pointname)
4002{
4003 return nullptr;
4004 // We support the point data via HDF4 APIs. So the following code
4005 // is not necessary. Comment out to avoid the memory leaking. KY 2024-04-22
4006#if 0
4007 auto point = new PointDataset(pointname);
4008 return point;
4009#endif
4010}
4011
4012
4013// Read name list from the EOS2 APIs and store names into a vector
4014bool Utility::ReadNamelist(const char *path,
4015 int32 (*inq)(char *, char *, int32 *),
4016 vector<string> &names)
4017{
4018 char *fname = const_cast<char *>(path);
4019 int32 bufsize;
4020 int numobjs;
4021
4022 if ((numobjs = inq(fname, nullptr, &bufsize)) == -1) return false;
4023 if (numobjs > 0) {
4024 vector<char> buffer;
4025 buffer.resize(bufsize + 1);
4026 if (inq(fname, buffer.data(), &bufsize) == -1) return false;
4027 HDFCFUtil::Split(buffer.data(), bufsize, ',', names);
4028 }
4029 return true;
4030}
4031#endif
4032
4033
STL iterator class.
STL iterator class.
STL class.
STL class.
STL class.
STL class.
STL class.
STL iterator class.
STL iterator class.
STL class.
static bool insert_map(std::map< std::string, std::string > &m, std::string key, std::string val)
Definition HDFCFUtil.cc:84
static void Handle_NameClashing(std::vector< std::string > &newobjnamelist)
General routines to handle name clashings.
Definition HDFCFUtil.cc:194
static std::string get_CF_string(std::string s)
Change special characters to "_".
Definition HDFCFUtil.cc:100
static void Split(const char *s, int len, char sep, std::vector< std::string > &names)
Definition HDFCFUtil.cc:58