OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
FONgGrid.cc
Go to the documentation of this file.
1 // FONgGrid.cc
2 
3 // This file is part of BES GDAL File Out Module
4 
5 // Copyright (c) 2012 OPeNDAP, Inc.
6 // Author: James Gallagher <jgallagher@opendap.org>
7 //
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Lesser General Public
10 // License as published by the Free Software Foundation; either
11 // version 2.1 of the License, or (at your option) any later version.
12 //
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Lesser General Public License for more details.
17 //
18 // You should have received a copy of the GNU Lesser General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 //
22 // You can contact University Corporation for Atmospheric Research at
23 // 3080 Center Green Drive, Boulder, CO 80301
24 
25 #include <algorithm>
26 
27 #include <gdal.h>
28 #include <gdal_priv.h>
29 #include <ogr_spatialref.h>
30 
31 #include <DDS.h>
32 #include <Grid.h>
33 // #include <ce_functions.h>
34 #include <util.h>
35 
36 #include <BESInternalError.h>
37 #include <BESDebug.h>
38 
39 #include "GeoTiffTransmitter.h"
40 #include "FONgTransform.h"
41 #include "FONgBaseType.h"
42 #include "FONgGrid.h"
43 
44 using namespace libdap;
45 
50 FONgGrid::FONgGrid(Grid *g) : FONgBaseType(), d_grid(g), d_lat(0), d_lon(0)
51 {
52  d_type = dods_grid_c;
53 
54  // Build sets of attribute values for easy searching.
55  // Copied from GeoConstriant in libdap. That class is
56  // abstract and didn't want to modify libdap's ABI for this hack.
57 
58  d_coards_lat_units.insert("degrees_north");
59  d_coards_lat_units.insert("degree_north");
60  d_coards_lat_units.insert("degree_N");
61  d_coards_lat_units.insert("degrees_N");
62 
63  d_coards_lon_units.insert("degrees_east");
64  d_coards_lon_units.insert("degree_east");
65  d_coards_lon_units.insert("degrees_E");
66  d_coards_lon_units.insert("degree_E");
67 
68  d_lat_names.insert("COADSY");
69  d_lat_names.insert("lat");
70  d_lat_names.insert("Lat");
71  d_lat_names.insert("LAT");
72 
73  d_lon_names.insert("COADSX");
74  d_lon_names.insert("lon");
75  d_lon_names.insert("Lon");
76  d_lon_names.insert("LON");
77 }
78 
85 {
86 }
87 
92 class is_prefix
93 {
94 private:
95  string s;
96 public:
97  is_prefix(const string & in): s(in)
98  {}
99 
100  bool operator()(const string & prefix)
101  {
102  return s.find(prefix) == 0;
103  }
104 };
105 
116 bool
117 FONgGrid::m_lat_unit_or_name_match(const string &var_units, const string &var_name,
118  const string &long_name)
119 {
120  return (long_name == "latitude"
121  || d_coards_lat_units.find(var_units) != d_coards_lat_units.end()
122  || find_if(d_lat_names.begin(), d_lat_names.end(), is_prefix(var_name)) != d_lat_names.end());
123 }
124 
125 bool
126 FONgGrid::m_lon_unit_or_name_match(const string &var_units, const string &var_name,
127  const string &long_name)
128 {
129  return (long_name == "longitude"
130  || d_coards_lon_units.find(var_units) != d_coards_lon_units.end()
131  || find_if(d_lon_names.begin(), d_lon_names.end(), is_prefix(var_name)) != d_lon_names.end());
132 }
133 
150 {
151  Grid::Map_iter m = d_grid->map_begin();
152 
153  // Assume that a Grid is correct and thus has exactly as many maps as its
154  // array part has dimensions. Thus, don't bother to test the Grid's array
155  // dimension iterator for '!= dim_end()'.
156  Array::Dim_iter d = d_grid->get_array()->dim_begin();
157 
158  // The fields d_latitude and d_longitude may be initialized to null or they
159  // may already contain pointers to the maps to use. In the latter case,
160  // skip the heuristics used in this code. However, given that all this
161  // method does is find the lat/lon maps, if they are given in the ctor,
162  // This method will likely not be called at all.
163 
164  while (m != d_grid->map_end() && (!d_lat || !d_lon)) {
165  string units_value = (*m)->get_attr_table().get_attr("units");
166  units_value = remove_quotes(units_value);
167  string long_name = (*m)->get_attr_table().get_attr("long_name");
168  long_name = remove_quotes(long_name);
169  string map_name = (*m)->name();
170 
171  // The 'units' attribute must match exactly; the name only needs to
172  // match a prefix.
173  if (!d_lat && m_lat_unit_or_name_match(units_value, map_name, long_name)) {
174  // Set both d_latitude (a pointer to the real map vector) and
175  // d_lat, a vector of the values represented as doubles. It's easier
176  // to work with d_lat, but it's d_latitude that needs to be set
177  // when constraining the grid. Also, record the grid variable's
178  // dimension iterator so that it's easier to set the Grid's Array
179  // (which also has to be constrained).
180  d_lat = dynamic_cast < Array * >(*m);
181  if (!d_lat)
182  throw InternalErr(__FILE__, __LINE__, "Expected an array.");
183 
184  if (!d_lat->read_p())
185  d_lat->read();
186  }
187 
188  if (!d_lon && m_lon_unit_or_name_match(units_value, map_name, long_name)) {
189  d_lon = dynamic_cast < Array * >(*m);
190  if (!d_lon)
191  throw InternalErr(__FILE__, __LINE__, "Expected an array.");
192 
193  if (!d_lon->read_p())
194  d_lon->read();
195  }
196 
197  ++m;
198  ++d;
199  }
200 
201  return d_lat && d_lon;
202 }
203 
213 {
214  BESDEBUG("fong3", "Entering FONgGrid::extract_coordinates" << endl);
215 
216  double *lat = 0, *lon = 0;
217  try {
218  // Find the lat and lon maps for this Grid; throws Error
219  // if the are not found.
221 
222  // The array size
223  t.set_height(d_lat->length());
224  t.set_width(d_lon->length());
225 
226  lat = extract_double_array(d_lat);
227  lon = extract_double_array(d_lon);
228 
229  t.set_top(lat[0]);
230  t.set_left(lon[0]);
231  t.set_bottom(lat[t.height() - 1]);
232  t.set_right(lon[t.width() -1]);
233 
234  // Read this from the 'missing_value' or '_FillValue' attributes
235  string missing_value = d_grid->get_attr_table().get_attr("missing_value");
236  if (missing_value.empty())
237  missing_value = d_grid->get_array()->get_attr_table().get_attr("missing_value");
238  if (missing_value.empty())
239  missing_value = d_grid->get_attr_table().get_attr("_FillValue");
240  if (missing_value.empty())
241  missing_value = d_grid->get_array()->get_attr_table().get_attr("_FillValue");
242 
243  BESDEBUG("fong3", "missing_value attribute: " << missing_value << endl);
244 
245  // NB: no_data_type() is 'none' by default
246  if (!missing_value.empty()) {
247  t.set_no_data(missing_value);
248  if (t.no_data() < 0)
250  else
252  }
253 
254  t.geo_transform_set(true);
255 
256  t.set_num_bands(t.num_bands() + 1);
257  t.push_var(this);
258 
259  delete[] lat;
260  delete[] lon;
261  }
262  catch (Error &e) {
263  delete[] lat;
264  delete[] lon;
265 
266  throw;
267  }
268 }
269 
270 
272 static bool is_spherical(BaseType *btp)
273 {
274  /* crs:grid_mapping_name = "latitude_longitude"
275  crs:semi_major_axis = 6371000.0 ;
276  crs:inverse_flattening = 0 ; */
277 
278  bool gmn = btp->get_attr_table().get_attr("grid_mapping_name") == "latitude_longitude";
279  bool sma = btp->get_attr_table().get_attr("semi_major_axis") == "6371000.0";
280  bool iflat = btp->get_attr_table().get_attr("inverse_flattening") == "0";
281 
282  return gmn && sma && iflat;
283 }
284 
286 static bool is_wgs84(BaseType *btp)
287 {
288  /*crs:grid_mapping_name = "latitude_longitude";
289  crs:longitude_of_prime_meridian = 0.0 ;
290  crs:semi_major_axis = 6378137.0 ;
291  crs:inverse_flattening = 298.257223563 ; */
292 
293  bool gmn = btp->get_attr_table().get_attr("grid_mapping_name") == "latitude_longitude";
294  bool lpm = btp->get_attr_table().get_attr("longitude_of_prime_meridian") == "0.0";
295  bool sma = btp->get_attr_table().get_attr("semi_major_axis") == "6378137.0";
296  bool iflat = btp->get_attr_table().get_attr("inverse_flattening") == "298.257223563";
297 
298  return gmn && lpm && sma && iflat;
299 }
300 
307 string FONgGrid::get_projection(DDS *dds)
308 {
309  // Here's the information about the CF and projections
310  // http://cf-pcmdi.llnl.gov/documents/cf-conventions/1.4/cf-conventions.html#grid-mappings-and-projections
311  // How this code looks for mapping information: Look for an
312  // attribute named 'grid_mapping' and get it's value. This attribute
313  // with be the name of a variable in the dataset, so get that
314  // variable. Now, look at the attributes of that variable.
315  string mapping_info = d_grid->get_attr_table().get_attr("grid_mapping");
316  if (mapping_info.empty())
317  mapping_info = d_grid->get_array()->get_attr_table().get_attr("grid_mapping");
318 
319  string WK_GCS = GeoTiffTransmitter::default_gcs;
320 
321  if (!mapping_info.empty()) {
322  // "WGS84": same as "EPSG:4326" but has no dependence on EPSG data files.
323  // "WGS72": same as "EPSG:4322" but has no dependence on EPSG data files.
324  // "NAD27": same as "EPSG:4267" but has no dependence on EPSG data files.
325  // "NAD83": same as "EPSG:4269" but has no dependence on EPSG data files.
326  // "EPSG:n": same as doing an ImportFromEPSG(n).
327 
328  // The mapping info is actually stored as attributes of an Int32 variable.
329  BaseType *btp = dds->var(mapping_info);
330  if (btp->name() == "crs") {
331  if (is_wgs84(btp))
332  WK_GCS = "WGS84";
333  else if (is_spherical(btp))
334  WK_GCS = "EPSG:4047";
335  }
336  }
337 
338  OGRSpatialReference srs;
339  srs.SetWellKnownGeogCS(WK_GCS.c_str());
340  char *srs_wkt = NULL;
341  srs.exportToWkt(&srs_wkt);
342 
343  string wkt = srs_wkt; // save the result
344 
345  CPLFree(srs_wkt); // free memory alloc'd by GDAL
346 
347  return wkt;
348 }
349 
351 {
352  if (!d_grid->get_array()->read_p())
353  d_grid->get_array()->read();
354 
355  // This code assumes read() has been called.
356  return extract_double_array(d_grid->get_array());
357 }
358 
virtual void set_top(int top)
virtual int height()
virtual ~FONgGrid()
Destructor that cleans up the grid.
Definition: FONgGrid.cc:84
virtual void set_left(int left)
libdap::Type d_type
Definition: FONgBaseType.h:41
static string default_gcs
FONgGrid(libdap::Grid *g)
Constructor for FONgGrid that takes a DAP Grid.
Definition: FONgGrid.cc:50
void set_num_bands(int n)
Definition: FONgTransform.h:96
virtual void set_bottom(int top)
string get_projection(libdap::DDS *dds)
Set the projection information For Grids, look for CF information.
Definition: FONgGrid.cc:307
virtual void set_height(int height)
void push_var(FONgBaseType *v)
Definition: FONgTransform.h:98
virtual void set_width(int width)
bool find_lat_lon_maps()
A private method called by the constructor that searches for latitude and longitude map vectors...
Definition: FONgGrid.cc:149
static class NCMLUtil overview
virtual int width()
virtual void set_right(int left)
#define NULL
Definition: wcsUtil.h:65
void geo_transform_set(bool state)
Definition: FONgTransform.h:87
virtual double * get_data()
Get the data values for the band(s). Call must delete.
Definition: FONgGrid.cc:350
void set_no_data_type(no_data_type_t t)
Definition: FONgTransform.h:92
A DAP BaseType with file out gdal information included.
Definition: FONgBaseType.h:38
virtual void extract_coordinates(FONgTransform &t)
Extract the size (pixels), element data type and top-left and bottom-right lat/lon corner points for ...
Definition: FONgGrid.cc:212
double no_data()
Definition: FONgTransform.h:89
Transformation object that converts an OPeNDAP DataDDS to a GeoTiff file.
Definition: FONgTransform.h:41
#define BESDEBUG(x, y)
macro used to send debug information to the debug stream
Definition: BESDebug.h:64
void set_no_data(const string &nd)
Definition: FONgTransform.h:90