NASA Logo
Ocean Color Science Software

ocssw V2022
get_geospatial.cpp
Go to the documentation of this file.
1 #include "get_geospatial.hpp"
2 #include "find_variable.hpp"
3 #include "boost/algorithm/string.hpp"
4 #include <boost/geometry.hpp>
5 #include <boost/geometry/geometries/point_xy.hpp>
6 #include <boost/geometry/geometries/polygon.hpp>
7 #include <algorithm>
8 #include <stack>
9 namespace bg = boost::geometry;
10 typedef bg::model::point<double, 2, bg::cs::spherical_equatorial<bg::degree>> Point_t;
11 typedef bg::model::linestring<Point_t> Linestring_t;
12 typedef bg::model::polygon<Point_t> Polygon_t;
13 typedef bg::model::multi_point<Point_t> MultiPoint_t;
14 
16  scan_line_vars["elat"] = std::vector<float>();
17  scan_line_vars["slat"] = std::vector<float>();
18  scan_line_vars["clat"] = std::vector<float>();
19  scan_line_vars["elon"] = std::vector<float>();
20  scan_line_vars["slon"] = std::vector<float>();
21 }
22 
24  netCDF::NcVar solzen_nc;
25  for (const auto &name: possible_sozlen_names) {
26  solzen_nc = find_nc_variable_cpp(name, nc_file);
27  if (!solzen_nc.isNull()) break;
28  }
29  if (solzen_nc.isNull()) {
30  std::cerr << "--Error--: Could not find solar zenith angle to compute Day Night Flag" << std::endl;
31  exit(EXIT_FAILURE);
32  }
33  std::vector<float> solzen;
34  allocate_var(solzen, solzen_nc);
35  const float night_angle = 90.0f;
36  std::stack<bool> state;
37  for (const auto &angle: solzen) {
38  if (angle > 180.0 || angle < -180 || std::isnan(angle) || !std::isfinite(angle))
39  continue;
40  bool flag = angle < night_angle;
41  if (state.empty()) {
42  state.push(flag);
43  } else if (state.top() != flag) {
44  return "Mixed";
45  }
46  }
47  if (state.empty()) {
48  return "Undefined";
49  } else {
50  if (state.top())
51  return "Day";
52  else
53  return "Night";
54  }
55 }
56 
57 void Geospatialbounds::get_lat_lon(const netCDF::NcFile &nc_input) {
58  netCDF::NcVar latnc = find_nc_variable_cpp("latitude", nc_input);
59  netCDF::NcVar lonnc = find_nc_variable_cpp("longitude", nc_input);
60  allocate_var(lat, latnc);
61  allocate_var(lon, lonnc);
62  std::vector<netCDF::NcDim> dims = latnc.getDims();
63  if (dims.size() != 2) {
64  std::cerr << "-Error- : Latitude array must be two-dimensional." << std::endl;
65  } else {
66  number_of_lines = dims.at(0).getSize();
67  pixels_per_line = dims.at(1).getSize();
68  }
69 }
70 
71 void Geospatialbounds::allocate_var(std::vector<float> &data, const netCDF::NcVar &var) {
72  if (!var.isNull()) {
73  std::vector<netCDF::NcDim> dims = var.getDims();
74  size_t size = 1;
75  for (const auto &dim: dims) {
76  size *= dim.getSize();
77  }
78  data.resize(size);
79  var.getVar(data.data());
80  }
81 }
82 
83 void Geospatialbounds::allocate_attr(std::vector<float> &data, const netCDF::NcAtt &att) {
84  if (!att.isNull()) {
85  size_t size = att.getAttLength();
86  if (size == 0)
87  return;
88  if (att.getType() == NC_STRING || att.getType() == NC_CHAR)
89  return;
90  data.resize(size);
91  att.getValues(data.data());
92  }
93 }
94 
96  : Geospatialbounds(netCDF::NcFile(path, netCDF::NcFile::read)) {
97 }
98 
99 Geospatialbounds::Geospatialbounds(const netCDF::NcFile &nc_input) : Geospatialbounds() {
100  netCDF::NcGroupAtt attr = nc_input.getAtt("instrument");
101  if (attr.isNull()) {
102  std::cerr << "Warning: Instrument not found. Returning an empty string" << std::endl;
103  } else {
104  attr.getValues(instrument);
105  }
106  attr = nc_input.getAtt("platform");
107  if (attr.isNull()) {
108  std::cerr << "Warning: Platform not found. Returning an empty string" << std::endl;
109  } else {
110  attr.getValues(platform);
111  }
112  // search for gring lat/lon or polygon in global attributes
113 
114  const auto attrs = nc_input.getAtts();
115  for (const auto &_attr: attrs) {
116  std::string attr_name = _attr.first;
117  boost::algorithm::to_lower(attr_name);
118  if (attr_name == "gringpointlatitude") {
119  allocate_attr(gringpointlatitude, _attr.second);
120  }
121  if (attr_name == "gringpointlongitude") {
122  allocate_attr(gringpointlongitude, _attr.second);
123  }
124  if (attr_name == "geospatial_bounds") {
125  _attr.second.getValues(geospatial_bounds_wkt);
126  }
127  }
128  // search for group attributes
129  if (gringpointlatitude.empty()) {
130  auto gringpointlatitude_attr = find_nc_grp_attribute_cpp("gringpointlatitude", nc_input);
131  allocate_attr(gringpointlatitude, gringpointlatitude_attr);
132  }
133  if (gringpointlongitude.empty()) {
134  auto gringpointlongitude_attr = find_nc_grp_attribute_cpp("gringpointlongitude", nc_input);
135  allocate_attr(gringpointlongitude, gringpointlongitude_attr);
136  }
137  if (geospatial_bounds_wkt.empty()) {
138  auto geospatial_bounds_wkt_attr = find_nc_grp_attribute_cpp("geospatial_bounds", nc_input);
139  if (!geospatial_bounds_wkt_attr.isNull())
140  geospatial_bounds_wkt_attr.getValues(geospatial_bounds_wkt);
141  }
142  auto crs = find_nc_grp_attribute_cpp("geospatial_bounds_crs", nc_input);
143  if (!crs.isNull()) {
144  Polygon_t poly;
145  boost::geometry::read_wkt(geospatial_bounds_wkt, poly);
146  geospatial_bounds_wkt = "POLYGON((";
147  double first_ini = true;
148  double x_first, y_first;
149  // getting the vertices back
150  for (auto it = boost::begin(boost::geometry::exterior_ring(poly));
151  it != boost::end(boost::geometry::exterior_ring(poly)); ++it) {
152  double x = bg::get<0>(*it);
153  double y = bg::get<1>(*it);
154  if (first_ini) {
155  x_first = x;
156  y_first = y;
157  first_ini = false;
158  }
159  geospatial_bounds_wkt += std::to_string(y);
160  geospatial_bounds_wkt += " ";
161  geospatial_bounds_wkt += std::to_string(x);
162  geospatial_bounds_wkt += ", ";
163  // use the coordinates...
164  }
165  // Polygon must be closed though it does not affect dateline crossing
166  geospatial_bounds_wkt += std::to_string(y_first);
167  geospatial_bounds_wkt += " ";
168  geospatial_bounds_wkt += std::to_string(x_first);
169  geospatial_bounds_wkt += "))";
170  }
171  if (std::isnan(max_lon)) {
172  auto geospatial_lon_max_attr = find_nc_grp_attribute_cpp("geospatial_lon_max", nc_input);
173  if (!geospatial_lon_max_attr.isNull())
174  geospatial_lon_max_attr.getValues(&max_lon);
175  }
176  if (std::isnan(min_lon)) {
177  auto geospatial_lon_min_attr = find_nc_grp_attribute_cpp("geospatial_lon_min", nc_input);
178  if (!geospatial_lon_min_attr.isNull())
179  geospatial_lon_min_attr.getValues(&min_lon);
180  }
181  if (time_coverage_start.empty()) {
182  auto attr_time_coverage = find_nc_grp_attribute_cpp("time_coverage_start", nc_input);
183  if (!attr_time_coverage.isNull())
184  attr_time_coverage.getValues(time_coverage_start);
185  }
186  if (day_night_flag.empty()) {
187  auto attr_day_night_flag = find_nc_grp_attribute_cpp("day_night_flag", nc_input);
188  if (!attr_day_night_flag.isNull())
189  attr_day_night_flag.getValues(day_night_flag);
190  else
191  day_night_flag = get_day_night_flag(nc_input);
192  }
193  for (auto &sc_ln_attr: scan_line_vars) {
194  std::string var_name = sc_ln_attr.first;
195  auto &var_val = sc_ln_attr.second;
196  netCDF::NcVar var = find_nc_variable_cpp(var_name, nc_input);
197  allocate_var(var_val, var);
198  }
199  for (const auto &sc_ln_attr: scan_line_vars) {
200  auto &var_val = sc_ln_attr.second;
201  if (var_val.empty()) {
202  get_lat_lon(nc_input);
203  break;
204  }
205  number_of_lines = var_val.size();
206  }
207 }
208 
209 void Geospatialbounds::calc_scan_line(const std::vector<float> &lat, const std::vector<float> &lon,
210  size_t numScans, size_t numPixels,
211  std::unordered_map<std::string, std::vector<float>> &scan_line_vars) {
212  assert(lat.size() == numPixels * numScans);
213  assert(lon.size() == numPixels * numScans);
214  scan_line_vars.at("elat") = std::vector<float>(numScans, NAN);
215  scan_line_vars.at("elon") = std::vector<float>(numScans, NAN);
216  scan_line_vars.at("slon") = std::vector<float>(numScans, NAN);
217  scan_line_vars.at("slat") = std::vector<float>(numScans, NAN);
218  scan_line_vars.at("clat") = std::vector<float>(numScans, NAN);
219  std::vector<int8_t> mask(lat.size(), 1);
220  auto f_lat = [](const float &v) {
221  if (v > 90.0 || v < -90 || std::isnan(v) || !std::isfinite(v))
222  return 0;
223  else
224  return 1;
225  };
226  auto f_lon = [](const float &v, const int8_t &mask) {
227  if (v > 180.0 || v < -180 || std::isnan(v) || !std::isfinite(v) || !mask)
228  return 0;
229  else
230  return 1;
231  };
232  std::transform(lat.begin(), lat.end(), mask.begin(), f_lat);
233  std::transform(lat.begin(), lat.end(), mask.begin(), mask.begin(), f_lon);
234  auto get_index = [&](size_t is, size_t ip) { return numPixels * is + ip; };
235  float last_lat = NAN;
236  for (size_t is = 0; is < numScans; is++) {
237  size_t spix = 0;
238  size_t cpix = numPixels / 2;
239  size_t epix = numPixels - 1;
240  // find good geolocation for spix and epix
241  size_t good_spix = spix;
242  size_t good_epix = epix;
243 
244  for (size_t ip = spix; ip < epix; ip++) {
245  size_t i = get_index(is, ip);
246  if (mask.at(i)) {
247  good_spix = ip;
248  break;
249  }
250  }
251  for (size_t ip = epix; ip >= spix; ip--) {
252  size_t i = get_index(is, ip);
253  if (mask.at(i)) {
254  good_epix = ip;
255  break;
256  }
257  }
258  size_t i = get_index(is, good_epix);
259  scan_line_vars.at("elat")[is] = lat.at(i);
260  scan_line_vars.at("elon")[is] = lon.at(i);
261  i = get_index(is, good_spix);
262  scan_line_vars.at("slat")[is] = lat.at(i);
263  scan_line_vars.at("slon")[is] = lon.at(i);
264  if (std::isnan(last_lat))
265  last_lat = (scan_line_vars.at("elat")[is] + scan_line_vars.at("slat")[is]) / 2;
266  i = get_index(is, cpix);
267  if (f_lat(lat.at(i))) {
268  last_lat = lat.at(i);
269  }
270  scan_line_vars.at("clat")[is] = last_lat;
271  }
272 };
273 
274 std::pair<std::vector<float>, std::vector<float>> Geospatialbounds::calc_gring(
275  const std::vector<float> &slat, const std::vector<float> &elat, const std::vector<float> &clat,
276  const std::vector<float> &slon, const std::vector<float> &elon) {
277  const float geobox_bounds{20.0f};
278  std::vector<std::vector<float>> geobox{{},
279  {},
280  {},
281  {}};
282  // find last valid scan
283  size_t nscan = clat.size();
284  // find first valid scan
285  size_t first_scan = nscan;
286  size_t last_scan = nscan;
287  for (size_t is = 0; is < nscan; is++) {
288  if (!std::isnan(elat.at(is))) {
289  if (first_scan == nscan)
290  first_scan = is;
291  last_scan = is;
292  }
293  }
294  if (first_scan == nscan) {
295  std::cerr << "-Error- : not valid elat is supplied. Exiting";
296  exit(EXIT_FAILURE);
297  }
298  geobox.at(0).push_back(slon.at(first_scan));
299  geobox.at(1).push_back(slat.at(first_scan));
300  geobox.at(2).push_back(elon.at(first_scan));
301  geobox.at(3).push_back(elat.at(first_scan));
302  //
303  float last_lat = clat.at(first_scan);
304  for (size_t is = first_scan + 1; is < last_scan; is++) {
305  if (std::isnan(clat.at(is)))
306  continue;
307  if (std::isnan(elat.at(is)))
308  continue;
309  if (std::abs(last_lat - clat.at(is)) > geobox_bounds) {
310  geobox.at(0).push_back(slon.at(is));
311  geobox.at(1).push_back(slat.at(is));
312  geobox.at(2).push_back(elon.at(is));
313  geobox.at(3).push_back(elat.at(is));
314  last_lat = clat.at(is);
315  }
316  }
317  geobox.at(0).push_back(slon.at(last_scan));
318  geobox.at(1).push_back(slat.at(last_scan));
319  geobox.at(2).push_back(elon.at(last_scan));
320  geobox.at(3).push_back(elat.at(last_scan));
321  std::vector<float> lat_gring;
322  std::vector<float> lon_gring;
323  lat_gring.push_back(geobox.at(1).at(0));
324  for (const auto &val: geobox.at(3)) {
325  lat_gring.push_back(val);
326  }
327  while (geobox.at(1).size() > 1) {
328  lat_gring.push_back(geobox.at(1).back());
329  geobox.at(1).pop_back();
330  }
331 
332  lon_gring.push_back(geobox.at(0).at(0));
333  for (const auto &val: geobox.at(2)) {
334  lon_gring.push_back(val);
335  }
336  while (geobox.at(0).size() > 1) {
337  lon_gring.push_back(geobox.at(0).back());
338  geobox.at(0).pop_back();
339  }
340  return {lat_gring, lon_gring};
341 }
342 
343 std::string Geospatialbounds::calc_gring(const std::pair<std::vector<float>, std::vector<float>> &gring) {
344  const std::vector<float> &lat_gring = gring.first;
345  const std::vector<float> &lon_gring = gring.second;
346  std::string wkt = "POLYGON((";
347  size_t len = lat_gring.size();
348  for (size_t i = 0; i < len; i++) {
349  wkt += std::to_string(lon_gring.at(i));
350  wkt += " ";
351  wkt += std::to_string(lat_gring.at(i));
352  wkt += ", ";
353  }
354  // Polygon must be closed though it does not affect dateline crossing
355  wkt += std::to_string(lon_gring.at(0));
356  wkt += " ";
357  wkt += std::to_string(lat_gring.at(0));
358  wkt += "))";
359  return wkt;
360 };
361 
362 const std::unordered_map<std::string, std::vector<float>> &Geospatialbounds::get_bounds() {
363  if (scan_line_vars.at("elat").empty()) {
364  calc_scan_line(lat, lon, number_of_lines, pixels_per_line, scan_line_vars);
365  }
366  return scan_line_vars;
367 }
368 
369 std::pair<std::vector<float>, std::vector<float>> Geospatialbounds::get_gring() {
370  if (gringpointlatitude.empty()) {
371  scan_line_vars = get_bounds();
372  const auto &slat = scan_line_vars.at("slat");
373  const auto &elat = scan_line_vars.at("elat");
374  const auto &clat = scan_line_vars.at("clat");
375  const auto &slon = scan_line_vars.at("slon");
376  const auto &elon = scan_line_vars.at("elon");
377  auto res = calc_gring(slat, elat, clat, slon, elon);
378  gringpointlatitude = res.first;
379  gringpointlongitude = res.second;
380  }
382 }
383 
385  if (geospatial_bounds_wkt.empty()) {
386  geospatial_bounds_wkt = calc_gring(get_gring());
387  }
388  return geospatial_bounds_wkt;
389 }
390 
392  if (std::isnan(max_lon)) {
393  auto res = get_gring();
394  const auto &longring = res.second;
395  max_lon = *std::max_element(longring.begin(), longring.end());
396  min_lon = *std::min_element(longring.begin(), longring.end());
397  }
398  return max_lon;
399 }
400 
402  get_max_lon();
403  return min_lon;
404 }
405 
407  if (scan_line_vars.at("elat").empty())
408  scan_line_vars = get_bounds();
409  return scan_line_vars.at("elat").data();
410 }
411 
413  if (scan_line_vars.at("slat").empty())
414  scan_line_vars = get_bounds();
415  return scan_line_vars.at("slat").data();
416 }
static void calc_scan_line(const std::vector< float > &, const std::vector< float > &, size_t, size_t, std::unordered_map< std::string, std::vector< float >> &)
bg::model::multi_point< Point_t > MultiPoint_t
netCDF::NcGroupAtt find_nc_grp_attribute_cpp(const std::string &att_name, T &nc_file)
static void allocate_var(std::vector< float > &, const netCDF::NcVar &var)
std::string get_bounds_wkt()
bg::model::polygon< Point_t > Polygon_t
this program makes no use of any feature of the SDP Toolkit that could generate such a then geolocation is calculated at that and then aggregated up to Resolved feature request Bug by adding three new int8 SDSs for each high resolution offsets between the high resolution geolocation and a bi linear interpolation extrapolation of the positions This can be used to reconstruct the high resolution geolocation Resolved Bug by delaying cumulation of gflags until after validation of derived products Resolved Bug by setting Latitude and Longitude to the correct fill resolving to support Near Real Time because they may be unnecessary if use of entrained ephemeris and attitude data is turned resolving bug report Corrected to filter out Aqua attitude records with missing status helping resolve bug MOD_PR03 will still correctly write scan and pixel data that does not depend upon the start thereby resolving MODur00108 Changed header guard macro names to avoid reserved name resolving MODur00104 Maneuver list file for Terra satellite was updated to include data from Jecue DuChateu Maneuver list files for both Terra and Aqua were updated to include two maneuvers from recent IOT weekly reports The limits for Z component of angular momentum was and to set the fourth GEO scan quality flag for a scan depending upon whether or not it occurred during one of them Added _FillValue attributes to many and changed the fill value for the sector start times from resolving MODur00072 Writes boundingcoordinate metadata to L1A archived metadata For PERCENT *ECS change to treat rather resolving GSFcd01518 Added a LogReport Changed to create the Average Temperatures vdata even if the number of scans is
Definition: HISTORY.txt:301
int32 nscan
Definition: l1_czcs_hdf.c:19
@ string
netCDF::NcVar find_nc_variable_cpp(const std::string &var_name, T &nc_file)
bg::model::point< double, 2, bg::cs::spherical_equatorial< bg::degree > > Point_t
gringpointlatitude
Definition: sort_gring.py:36
const float * get_elat()
int state(double tjdTDB, JPLIntUtilType *util, double posvel[13][6], double *pnut)
gringpointlongitude
Definition: sort_gring.py:35
string path
Definition: color_dtdb.py:221
bg::model::polygon< Point_t > Polygon_t
Definition: get_dataday.cpp:25
a context in which it is NOT documented to do so subscript which cannot be easily calculated when extracting TONS attitude data from the Terra L0 files Corrected several defects in extraction of entrained ephemeris and and as HDF file for both the L1A and Geolocation enabling retrieval of South Polar DEM data Resolved Bug by changing to opent the geolocation file only after a successful read of the L1A and also by checking for fatal errors from not restoring C5 and to report how many of those high resolution values were water in the new WaterPresent SDS Added valid_range attribute to Land SeaMask Changed to bilinearly interpolate the geoid_height to remove artifacts at one degree lines Made corrections to const qualification of pointers allowed by new version of M API library Removed casts that are no longer for same not the geoid Corrected off by one error in calculation of high resolution offsets Corrected parsing of maneuver list configuration parameter Corrected to set Height SDS to fill values when geolocation when for elevation and land water mask
Definition: HISTORY.txt:114
const std::string & get_day_night_flag() const
no change in intended resolving MODur00064 Corrected handling of bad ephemeris attitude data
Definition: HISTORY.txt:356
bg::model::linestring< Point_t > Linestring_t
subroutine geometry
const std::unordered_map< std::string, std::vector< float > > & get_bounds()
int16 get_index(char *Vdata_name)
Definition: get_index.c:6
const float * get_slat()
int32 spix
Definition: l1_czcs_hdf.c:21
double max_lon
Definition: GEO_DEM.c:64
static void allocate_attr(std::vector< float > &, const netCDF::NcAtt &att)
int32 epix
Definition: l1_czcs_hdf.c:23
std::pair< std::vector< float >, std::vector< float > > get_gring()
#define abs(a)
Definition: misc.h:90
int i
Definition: decode_rs.h:71
static std::pair< std::vector< float >, std::vector< float > > calc_gring(const std::vector< float > &, const std::vector< float > &, const std::vector< float > &, const std::vector< float > &, const std::vector< float > &)