bes  Updated for version 3.20.13
HDFEOS2ArrayGridGeoField.h
1 // This file is part of the hdf4 data handler for the OPeNDAP data server.
3 // It retrieves the latitude and longitude of the HDF-EOS2 Grid
4 // There are two typical cases:
5 // read the lat/lon from the file and calculate lat/lon using the EOS2 library.
6 // Several variations are also handled:
7 // 1. For geographic and Cylinderic Equal Area projections, condense 2-D to 1-D.
8 // 2. For some files, the longitude is within 0-360 range instead of -180 - 180 range.
9 // We need to convert 0-360 to -180-180.
10 // 3. Some files have fillvalues in the lat. and lon. for the geographic projection.
11 // 4. Several MODIS files don't have the correct parameters inside StructMetadata.
12 // We can obtain the starting point, the step and replace the fill value.
13 // Authors: MuQun Yang <myang6@hdfgroup.org> Choonghwan Lee
14 // Copyright (c) 2009-2012 The HDF Group
16 #ifdef USE_HDFEOS2_LIB
17 #ifndef HDFEOS2ARRAY_GRIDGEOFIELD_H
18 #define HDFEOS2ARRAY_GRIDGEOFIELD_H
19 
20 #include <cmath>
21 
22 #include <libdap/Array.h>
23 
24 #include "mfhdf.h"
25 #include "hdf.h"
26 #include "HdfEosDef.h"
27 
28 
29 class HDFEOS2ArrayGridGeoField:public libdap::Array
30 {
31  public:
32  HDFEOS2ArrayGridGeoField (int rank, int fieldtype, bool llflag, bool ydimmajor, bool condenseddim, bool speciallon, int specialformat, /*short field_cache,*/const std::string &filename, const int gridfd, const std::string & gridname, const std::string & fieldname,const string & n = "", libdap::BaseType * v = nullptr):
33  libdap::Array (n, v),
34  rank (rank),
35  fieldtype (fieldtype),
36  llflag (llflag),
37  ydimmajor (ydimmajor),
38  condenseddim (condenseddim),
39  speciallon (speciallon),
40  specialformat (specialformat),
41  /*field_cache(field_cache),*/
42  filename(filename),
43  gridfd(gridfd),
44  gridname (gridname),
45  fieldname (fieldname)
46  {
47  }
48  ~ HDFEOS2ArrayGridGeoField () override = default;
49 
50  int format_constraint (int *cor, int *step, int *edg);
51 
52  libdap::BaseType *ptr_duplicate () override
53  {
54  return new HDFEOS2ArrayGridGeoField (*this);
55  }
56 
57  virtual bool read () override;
58 
59  private:
60 
61  // Field array rank
62  int rank;
63 
64  // Distinguish coordinate variables from general variables.
65  // For fieldtype values:
66  // 0 the field is a general field
67  // 1 the field is latitude.
68  // 2 the field is longtitude.
69  // 3 the field is a coordinate variable defined as level.
70  // 4 the field is an inserted natural number.
71  // 5 the field is time.
72  int fieldtype;
73 
74  // The flag to indicate if lat/lon is an existing field in the file or needs to be calculated.
75  bool llflag;
76 
77  // Flag to check if this lat/lon field is YDim major(YDim,XDim). This is necessary to use GDij2ll
78  bool ydimmajor;
79 
80  // Flag to check if this 2-D lat/lon can be condensed to 1-D lat/lon
81  bool condenseddim;
82 
83  // Flag to check if this file's longitude needs to be handled specially.
84  // Note: longitude values range from 0 to 360 for some fields. We need to map the values to -180 to 180.
85  bool speciallon;
86 
87  // Latitude and longitude values of some HDF-EOS2 grids need to be handled in special ways.
88  // There are four cases that we need to calculate lat/lon differently.
89  // This number is used to distinguish them.
90  // 1) specialformat = 1
91  // Projection: Geographic
92  // upleft and lowright coordinates don't follow EOS's DDDMMMSSS conventions.
93  // Instead, they simply represent lat/lon values as -180.0 or -90.0.
94  // Products: mostly MODIS MCD Grid
95 
96  // 2) specialformat = 2
97  // Projection: Geographic
98  // upleft and lowright coordinates don't follow EOS's DDDMMMSSS conventions.
99  // Instead, their upleft or lowright are simply represented as default(-1).
100  // Products: mostly TRMM CERES Grid
101 
102  // 3) specialformat = 3
103  // One MOD13C2 doesn't provide project code
104  // The upperleft and lowerright coordinates are all -1
105  // We have to calculate lat/lon by ourselves.
106  // Since it doesn't provide the project code, we double check their information
107  // and find that it covers the whole globe with 0.05 degree resolution.
108  // Lat. is from 90 to -90 and Lon is from -180 to 180.
109 
110  // 4) specialformat = 4
111  // Projection: Space Oblique Mercator(SOM)
112  // The lat/lon needs to be handled differently for the SOM projection
113  // Products: MISR
114  int specialformat;
115 
116  // Temp here: HDF-EOS2 file name
117  std::string filename;
118 
119  int gridfd;
120 
121  // HDF-EOS2 grid name
122  std::string gridname;
123 
124  // HDF-EOS2 field name
125  std::string fieldname;
126  // Calculate Lat and Lon based on HDF-EOS2 library.
127  void CalculateLatLon (int32 gridid, int fieldtype, int specialformat, float64 * outlatlon, float64* latlon_all, int32 * offset, int32 * count, int32 * step, int nelms,bool write_latlon_cache);
128 
129  // Calculate Special Latitude and Longitude.
130  //One MOD13C2 file doesn't provide projection code
131  // The upperleft and lowerright coordinates are all -1
132  // We have to calculate lat/lon by ourselves.
133  // Since it doesn't provide the project code, we double check their information
134  // and find that it covers the whole globe with 0.05 degree resolution.
135  // Lat. is from 90 to -90 and Lon is from -180 to 180.
136  void CalculateSpeLatLon (int32 gridid, int fieldtype, float64 * outlatlon, const int32 * offset, const int32 * count, const int32 * step) const;
137 
138  // Calculate Latitude and Longtiude for the Geo-projection for very large number of elements per dimension.
139  void CalculateLargeGeoLatLon(int32 gridid, int fieldtype, float64* latlon, float64* latlon_all, const int *start, const int *count, const int *step, int nelms,bool write_latlon_cache) const;
140  // test for undefined values returned by longitude-latitude calculation
141  bool isundef_lat(double value) const
142  {
143  if (std::isinf(value))
144  return true;
145  if (std::isnan(value))
146  return true;
147 
148  // GCTP_AMAZ returns "1e+51" for values at the opposite poles
149  if(value < -90.0 || value > 90.0)
150  return true;
151  // This is ok.
152  return false;
153  } // end bool isundef_lat(double value)
154 
155  bool isundef_lon(double value) const
156  {
157  if (std::isinf(value))
158  return true;
159  if (std::isnan(value))
160  return true;
161  // GCTP_LAMAZ returns "1e+51" for values at the opposite poles
162  if (value < -180.0 || value > 180.0)
163  return true;
164  return false;
165  } // end bool isundef_lat(double value)
166 
167  // Given rol, col address in double array of dimension YDim x XDim
168  // return value of nearest neighbor to (row,col) which is not undefined
169  double nearestNeighborLatVal(double *array, int row, int col, int YDim, int XDim)
170  {
171  // test valid row, col address range
172  if(row < 0 || row > YDim || col < 0 || col > XDim)
173  {
174  cerr << "nearestNeighborLatVal("<<row<<", "<<col<<", "<<YDim<<", "<<XDim;
175  cerr <<"): index out of range"<<endl;
176  return 0.0;
177  }
178  // address (0,0)
179  if(row < YDim/2 && col < XDim/2)
180  { /* search by incrementing both row and col */
181  if(!isundef_lat(array[(row+1)*XDim+col])) return(array[(row+1)*XDim+col]);
182  if(!isundef_lat(array[row*XDim+col+1])) return(array[row*XDim+col+1]);
183  if(!isundef_lat(array[(row+1)*XDim+col+1])) return(array[(row+1)*XDim+col+1]);
184  /* recurse on the diagonal */
185  return(nearestNeighborLatVal(array, row+1, col+1, YDim, XDim));
186  }
187  if(row < YDim/2 && col > XDim/2)
188  { /* search by incrementing row and decrementing col */
189  if(!isundef_lat(array[(row+1)*XDim+col])) return(array[(row+1)*XDim+col]);
190  if(!isundef_lat(array[row*XDim+col-1])) return(array[row*XDim+col-1]);
191  if(!isundef_lat(array[(row+1)*XDim+col-1])) return(array[(row+1)*XDim+col-1]);
192  /* recurse on the diagonal */
193  return(nearestNeighborLatVal(array, row+1, col-1, YDim, XDim));
194  }
195  if(row > YDim/2 && col < XDim/2)
196  { /* search by incrementing col and decrementing row */
197  if(!isundef_lat(array[(row-1)*XDim+col])) return(array[(row-1)*XDim+col]);
198  if(!isundef_lat(array[row*XDim+col+1])) return(array[row*XDim+col+1]);
199  if(!isundef_lat(array[(row-1)*XDim+col+1])) return(array[(row-1)*XDim+col+1]);
200  /* recurse on the diagonal */
201  return(nearestNeighborLatVal(array, row-1, col+1, YDim, XDim));
202  }
203  if(row > YDim/2 && col > XDim/2)
204  { /* search by decrementing both row and col */
205  if(!isundef_lat(array[(row-1)*XDim+col])) return(array[(row-1)*XDim+col]);
206  if(!isundef_lat(array[row*XDim+col-1])) return(array[row*XDim+col-1]);
207  if(!isundef_lat(array[(row-1)*XDim+col-1])) return(array[(row-1)*XDim+col-1]);
208  /* recurse on the diagonal */
209  return(nearestNeighborLatVal(array, row-1, col-1, YDim, XDim));
210  }
211  // dummy return, turn off the compiling warning
212  return 0.0;
213  } // end
214 
215  double nearestNeighborLonVal(double *array, int row, int col, int YDim, int XDim)
216  {
217  // test valid row, col address range
218  if(row < 0 || row > YDim || col < 0 || col > XDim)
219  {
220  cerr << "nearestNeighborLonVal("<<row<<", "<<col<<", "<<YDim<<", "<<XDim;
221  cerr <<"): index out of range"<<endl;
222  return 0.0;
223  }
224  // address (0,0)
225  if(row < YDim/2 && col < XDim/2)
226  { /* search by incrementing both row and col */
227  if(!isundef_lon(array[(row+1)*XDim+col])) return(array[(row+1)*XDim+col]);
228  if(!isundef_lon(array[row*XDim+col+1])) return(array[row*XDim+col+1]);
229  if(!isundef_lon(array[(row+1)*XDim+col+1])) return(array[(row+1)*XDim+col+1]);
230  /* recurse on the diagonal */
231  return(nearestNeighborLonVal(array, row+1, col+1, YDim, XDim));
232  }
233  if(row < YDim/2 && col > XDim/2)
234  { /* search by incrementing row and decrementing col */
235  if(!isundef_lon(array[(row+1)*XDim+col])) return(array[(row+1)*XDim+col]);
236  if(!isundef_lon(array[row*XDim+col-1])) return(array[row*XDim+col-1]);
237  if(!isundef_lon(array[(row+1)*XDim+col-1])) return(array[(row+1)*XDim+col-1]);
238  /* recurse on the diagonal */
239  return(nearestNeighborLonVal(array, row+1, col-1, YDim, XDim));
240  }
241  if(row > YDim/2 && col < XDim/2)
242  { /* search by incrementing col and decrementing row */
243  if(!isundef_lon(array[(row-1)*XDim+col])) return(array[(row-1)*XDim+col]);
244  if(!isundef_lon(array[row*XDim+col+1])) return(array[row*XDim+col+1]);
245  if(!isundef_lon(array[(row-1)*XDim+col+1])) return(array[(row-1)*XDim+col+1]);
246  /* recurse on the diagonal */
247  return(nearestNeighborLonVal(array, row-1, col+1, YDim, XDim));
248  }
249  if(row > YDim/2 && col > XDim/2)
250  { /* search by decrementing both row and col */
251  if(!isundef_lon(array[(row-1)*XDim+col])) return(array[(row-1)*XDim+col]);
252  if(!isundef_lon(array[row*XDim+col-1])) return(array[row*XDim+col-1]);
253  if(!isundef_lon(array[(row-1)*XDim+col-1])) return(array[(row-1)*XDim+col-1]);
254  /* recurse on the diagonal */
255  return(nearestNeighborLonVal(array, row-1, col-1, YDim, XDim));
256  }
257 
258  // dummy return, turn off the compiling warning
259  return 0.0;
260  } // end
261 
262  // Calculate Latitude and Longitude for SOM Projection.
263  // since the latitude and longitude of the SOM projection are 3-D, so we need to handle this projection in a special way.
264  // Based on our current understanding, the third dimension size is always 180.
265  // This is according to the MISR Lat/lon calculation document
266  // at http://eosweb.larc.nasa.gov/PRODOCS/misr/DPS/DPS_v50_RevS.pdf
267  void CalculateSOMLatLon(int32, const int*, const int*, const int*, int,const string &, bool);
268 
269  // Calculate Latitude and Longitude for LAMAZ Projection.
270  void CalculateLAMAZLatLon(int32, int, float64*, float64*,const int*, const int*, const int*, bool);
271 
272  // Subsetting the latitude and longitude.
273  template <class T> void LatLon2DSubset (T* outlatlon, int ydim, int xdim, T* latlon, const int32 * offset, const int32 * count, const int32 * step) const;
274 
275  // Handle latitude and longitude when having fill value for geographic projection
276  //template <class T> void HandleFillLatLon(T* total_latlon, T* latlon,bool ydimmajor,
277  template <class T> void HandleFillLatLon(vector<T> total_latlon, T* latlon,bool ydimmajor, int fieldtype, int32 xdim , int32 ydim, const int32* offset, const int32* count, const int32* step, int fv);
278 
279  // Corrected Latitude and longitude when the lat/lon has fill value case.
280  template < class T > bool CorLatLon (T * latlon, int fieldtype, int elms, int fv);
281 
282  // Converted longitude from 0-360 to -180-180.
283  template < class T > void CorSpeLon (T * lon, int xdim) const;
284 
285  // Lat and Lon for GEO and CEA projections need to be condensed from 2-D to 1-D.
286  // This function does this.
287  void getCorrectSubset (const int *offset, const int *count, const int *step, int32 * offset32, int32 * count32, int32 * step32, bool condenseddim, bool ydimmajor, int fieldtype, int rank) const;
288 
289  // Helper function to handle the case that lat. and lon. contain fill value.
290  template < class T > int findfirstfv (T * array, int start, int end, int fillvalue);
291 
292 };
293 #endif
294 #endif