bes  Updated for version 3.20.13
StareFunctions.cc
1 // This file is part of libdap, A C++ implementation of the OPeNDAP Data
2 // Access Protocol.
3 
4 // Copyright (c) 2019 OPeNDAP, Inc.
5 // Authors: Kodi Neumiller <kneumiller@opendap.org>
6 //
7 // This library is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU Lesser General Public
9 // License as published by the Free Software Foundation; either
10 // version 2.1 of the License, or (at your option) any later version.
11 //
12 // This library is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 // Lesser General Public License for more details.
16 //
17 // You should have received a copy of the GNU Lesser General Public
18 // License along with this library; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 //
21 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
22 
23 #include "config.h"
24 
25 #include <sstream>
26 #include <memory>
27 #include <cassert>
28 
29 #include <STARE.h>
30 
31 #include <netcdf.h>
32 
33 #include <libdap/BaseType.h>
34 #include <libdap/Float64.h>
35 #include <libdap/Str.h>
36 #include <libdap/Array.h>
37 #include <libdap/Grid.h>
38 #include <libdap/Structure.h>
39 
40 #include <libdap/DMR.h>
41 #include <libdap/D4RValue.h>
42 
43 #include <libdap/Byte.h>
44 #include <libdap/Int16.h>
45 #include <libdap/Int32.h>
46 #include <libdap/UInt16.h>
47 #include <libdap/UInt32.h>
48 #include <libdap/Float32.h>
49 #include <libdap/UInt64.h>
50 #include <libdap/Int64.h>
51 
52 #include "BESDebug.h"
53 // #include "BESUtil.h"
54 #include "BESInternalError.h"
55 #include "BESSyntaxUserError.h"
56 
57 #include "StareFunctions.h"
58 #include "GeoFile.h"
59 
60 #define STARE_FUNC "stare"
61 
62 using namespace libdap;
63 using namespace std;
64 
65 namespace functions {
66 
67 // These default values can be overridden using BES keys.
68 // See StareFunctions.h jhrg 5/21/20
69 // If stare_storage_path is empty, expect the sidecar file in the same
70 // place as the data file. jhrg 5/26/20
71 string stare_storage_path;
72 
73 // TODO Remove this once change-over is complete. jhrg 6/17/21
74 string stare_sidecar_suffix = "_stare.nc";
75 
87 ostream & operator << (ostream &out, const stare_matches &m)
88 {
89  assert(m.stare_indices.size() == m.row_indices.size()
90  && m.row_indices.size() == m.col_indices.size());
91 
92  auto ti = m.target_indices.begin();
93  auto si = m.stare_indices.begin();
94  auto xi = m.row_indices.begin();
95  auto yi = m.col_indices.begin();
96 
97  while (si != m.stare_indices.end()) {
98  out << "Target: " << *ti++ << ", Dataset Index: " << *si++ << ", coord: row: " << *xi++ << ", col: " << *yi++ << endl;
99  }
100 
101  return out;
102 }
103 
104 static void
105 extract_stare_index_array(Array *var, vector<STARE_ArrayIndexSpatialValue> &values)
106 {
107  if (var->var()->type() != dods_uint64_c)
108  throw BESSyntaxUserError("STARE server function passed an invalid Index array (" + var->name()
109  + " is type: " + var->var()->type_name() + ").", __FILE__, __LINE__);
110 
111  values.resize(var->length());
112  var->value((dods_uint64*)values.data()); // Extract the values of 'var' to 'values'
113 }
114 
136 bool
137 target_in_dataset(const vector<STARE_ArrayIndexSpatialValue> &target_indices,
138  const vector<STARE_ArrayIndexSpatialValue> &data_stare_indices) {
139 #if 1
140  // this took 0.23s and worked.
141  // Changes to the range-for loop, fixed the type (was unsigned long long
142  // which works on OSX but not CentOS7). jhrg 11/5/19
143  for (const STARE_ArrayIndexSpatialValue &i : target_indices) {
144  for (const STARE_ArrayIndexSpatialValue &j :data_stare_indices ) {
145  // Check to see if the index 'i' overlaps the index 'j'. The cmpSpatial()
146  // function returns -1, 0, 1 depending on i in j, no overlap or, j in i.
147  // testing for !0 covers the general overlap case.
148  int result = cmpSpatial(i, j);
149  if (result != 0)
150  return true;
151  }
152  }
153 #else
154  // For one sample MOD05 file, this took 5.6s and failed to work.
155  // Problem: seg fault.
156 
157  // Initialize the skiplists for the search
158  auto r = SpatialRange(data_stare_indices);
159  cerr << "built spatial range" << endl;
160  for (const dods_uint64 &sid : target_indices) {
161  if( r.contains(sid) ) { return true; }
162  }
163 #endif
164  return false;
165 }
166 
183 unsigned int
184 count(const vector<STARE_ArrayIndexSpatialValue> &target_indices,
185  const vector<STARE_ArrayIndexSpatialValue> &dataset_indices,
186  bool all_dataset_matches /*= false*/) {
187  unsigned int counter = 0;
188  for (const auto &i : target_indices) {
189  for (const auto &j : dataset_indices)
190  // Here we are counting the number of target indices that overlap the
191  // dataset indices.
192  if (cmpSpatial(i, j) != 0) {
193  counter++;
194  BESDEBUG(STARE_FUNC, "Matching (dataset, target) indices: " << i << ", " << j << endl);
195  if (!all_dataset_matches)
196  break; // exit the inner loop
197  }
198  }
199 
200  return counter;
201 }
202 
211 unique_ptr<stare_matches>
212 stare_subset_helper(const vector<STARE_ArrayIndexSpatialValue> &target_indices,
213  const vector<STARE_ArrayIndexSpatialValue> &dataset_indices,
214  size_t dataset_rows, size_t dataset_cols)
215 {
216  unique_ptr<stare_matches> subset(new stare_matches());
217 
218  auto sid_iter = dataset_indices.begin();
219  for (size_t i = 0; i < dataset_rows; ++i) {
220  for (size_t j = 0; j < dataset_cols; ++j) {
221  auto sid = *sid_iter++;
222  for (const auto &target : target_indices) {
223  if (cmpSpatial(sid, target) != 0) { // != 0 --> sid is in target OR target is in sid
224  subset->add(i, j, sid, target);
225  }
226  }
227  }
228  }
229 
230  return subset;
231 }
232 
233 void
234 read_stare_indices_from_function_argument(BaseType *raw_stare_indices,
235  vector<STARE_ArrayIndexSpatialValue> &s_indices) {
236 
237  auto stare_indices = dynamic_cast<Array *>(raw_stare_indices);
238  if (stare_indices == nullptr)
239  throw BESSyntaxUserError(
240  "Expected an Array but found a " + raw_stare_indices->type_name(), __FILE__, __LINE__);
241 
242  if (stare_indices->var()->type() != dods_uint64_c)
243  throw BESSyntaxUserError(
244  "Expected an Array of UInt64 values but found an Array of " + stare_indices->var()->type_name(),
245  __FILE__, __LINE__);
246 
247  stare_indices->read();
248 
249  extract_stare_index_array(stare_indices, s_indices);
250 }
251 
262 BaseType *
263 StareIntersectionFunction::stare_intersection_dap4_function(D4RValueList *args, DMR &dmr)
264 {
265  if (args->size() != 2) {
266  ostringstream oss;
267  oss << "stare_intersection(): Expected two arguments, but got " << args->size();
268  throw BESSyntaxUserError(oss.str(), __FILE__, __LINE__);
269  }
270 
271  const BaseType *dependent_var = args->get_rvalue(0)->value(dmr);
272  BaseType *raw_stare_indices = args->get_rvalue(1)->value(dmr);
273 
274  unique_ptr<GeoFile> gf(new GeoFile(dmr.filename()));
275 
276  // Read the stare indices for the dependent var from the sidecar file.
277  vector<STARE_ArrayIndexSpatialValue> dep_var_stare_indices;
278  gf->get_stare_indices(dependent_var->name(), dep_var_stare_indices);
279 
280  // Put the stare indices passed into the function into a vector<>
281  vector<STARE_ArrayIndexSpatialValue> target_s_indices;
282  read_stare_indices_from_function_argument(raw_stare_indices, target_s_indices);
283 
284  // Are any of the target indices covered by this variable
285  bool status = target_in_dataset(target_s_indices, dep_var_stare_indices);
286 
287  unique_ptr<Int32> result(new Int32("result"));
288  if (status) {
289  result->set_value(1);
290  }
291  else {
292  result->set_value(0);
293  }
294 
295  return result.release();
296 }
297 
316 BaseType *
317 StareCountFunction::stare_count_dap4_function(D4RValueList *args, DMR &dmr)
318 {
319  if (args->size() != 2) {
320  ostringstream oss;
321  oss << "stare_intersection(): Expected two arguments, but got " << args->size();
322  throw BESSyntaxUserError(oss.str(), __FILE__, __LINE__);
323  }
324 
325  const BaseType *dependent_var = args->get_rvalue(0)->value(dmr);
326  BaseType *raw_stare_indices = args->get_rvalue(1)->value(dmr);
327 
328  unique_ptr<GeoFile> gf(new GeoFile(dmr.filename()));
329 
330  // Read the stare indices for the dependent var from the sidecar file.
331  vector<STARE_ArrayIndexSpatialValue> dep_var_stare_indices;
332  gf->get_stare_indices(dependent_var->name(), dep_var_stare_indices);
333 
334  // Put the stare indices passed into the function into a vector<>
335  vector<STARE_ArrayIndexSpatialValue> target_s_indices;
336  read_stare_indices_from_function_argument(raw_stare_indices, target_s_indices);
337 
338  unsigned int num = count(target_s_indices, dep_var_stare_indices, false);
339 
340  unique_ptr<Int32> result(new Int32("result"));
341  result->set_value(static_cast<int>(num));
342  return result.release();
343 }
344 
359 BaseType *
360 StareSubsetFunction::stare_subset_dap4_function(D4RValueList *args, DMR &dmr)
361 {
362  if (args->size() != 2) {
363  ostringstream oss;
364  oss << "stare_subset(): Expected two arguments, but got " << args->size();
365  throw BESSyntaxUserError(oss.str(), __FILE__, __LINE__);
366  }
367 
368  const BaseType *dependent_var = args->get_rvalue(0)->value(dmr);
369  BaseType *raw_stare_indices = args->get_rvalue(1)->value(dmr);
370 
371  unique_ptr<GeoFile> gf(new GeoFile(dmr.filename()));
372 
373  // Read the stare indices for the dependent var from the sidecar file.
374  vector<STARE_ArrayIndexSpatialValue> dep_var_stare_indices;
375  gf->get_stare_indices(dependent_var->name(), dep_var_stare_indices);
376 
377  // Put the stare indices passed into the function into a vector<>
378  vector<STARE_ArrayIndexSpatialValue> target_s_indices;
379  read_stare_indices_from_function_argument(raw_stare_indices, target_s_indices);
380 
381  unique_ptr <stare_matches> subset = stare_subset_helper(target_s_indices, dep_var_stare_indices,
382  gf->get_variable_rows(dependent_var->name()),
383  gf->get_variable_cols(dependent_var->name()));
384 
385  // When no subset is found (none of the target indices match those in the dataset)
386  if (subset->stare_indices.empty()) {
387  subset->stare_indices.push_back(0);
388  subset->target_indices.push_back(0);
389  subset->row_indices.push_back(-1);
390  subset->col_indices.push_back(-1);
391  }
392  // Transfer values to a Structure
393  unique_ptr<Structure> result(new Structure("result"));
394 
395  unique_ptr<Array> stare(new Array("stare", new UInt64("stare")));
396  // Various static analysis tools say the cast from STARE_SpatialIntervals to
397  // dods_uint64 is not needed. FAIL. It is needed on various newer versions of
398  // g++. jhrg 6/11/22
399  stare->set_value((dods_uint64*)&(subset->stare_indices[0]), static_cast<int>(subset->stare_indices.size()));
400  stare->append_dim(static_cast<int>(subset->stare_indices.size()));
401  result->add_var_nocopy(stare.release());
402 
403  unique_ptr<Array> target(new Array("target", new UInt64("target")));
404  target->set_value((dods_uint64*) &(subset->target_indices[0]), static_cast<int>(subset->target_indices.size()));
405  target->append_dim(static_cast<int>(subset->target_indices.size()));
406  result->add_var_nocopy(target.release());
407 
408  unique_ptr<Array> x(new Array("row", new Int32("row")));
409  x->set_value(subset->row_indices, static_cast<int>(subset->row_indices.size()));
410  x->append_dim(static_cast<int>(subset->row_indices.size()));
411  result->add_var_nocopy(x.release());
412 
413  unique_ptr<Array> y(new Array("col", new Int32("col")));
414  y->set_value(subset->col_indices, static_cast<int>(subset->col_indices.size()));
415  y->append_dim(static_cast<int>(subset->col_indices.size()));
416  result->add_var_nocopy(y.release());
417 
418  return result.release();
419 }
420 
429 double get_double_value(BaseType *btp)
430 {
431  switch (btp->type()) {
432  case libdap::dods_float64_c:
433  return dynamic_cast<Float64*>(btp)->value();
434  case libdap::dods_int64_c:
435  return static_cast<double>(dynamic_cast<Int64*>(btp)->value());
436  case libdap::dods_uint64_c:
437  return static_cast<double>(dynamic_cast<UInt64*>(btp)->value());
438  default: {
439  throw BESSyntaxUserError(string("Expected a constant value, but got ").append(btp->type_name())
440  .append(" instead."), __FILE__, __LINE__);
441  }
442  }
443 }
444 
445 BaseType *
446 StareSubsetArrayFunction::stare_subset_array_dap4_function(D4RValueList *args, DMR &dmr)
447 {
448  if (args->size() != 3) {
449  ostringstream oss;
450  oss << "stare_subset_array(): Expected three arguments, but got " << args->size();
451  throw BESSyntaxUserError(oss.str(), __FILE__, __LINE__);
452  }
453 
454  auto dependent_var = dynamic_cast<Array*>(args->get_rvalue(0)->value(dmr));
455  if (!dependent_var)
456  throw BESSyntaxUserError("Expected an Array as teh first argument to stare_subset_array()", __FILE__, __LINE__);
457 
458  BaseType *mask_val_var = args->get_rvalue(1)->value(dmr);
459  double mask_value = get_double_value(mask_val_var);
460 
461  BaseType *raw_stare_indices = args->get_rvalue(2)->value(dmr);
462 
463  unique_ptr<GeoFile> gf(new GeoFile(dmr.filename()));
464 
465  // Read the stare indices for the dependent var from the sidecar file.
466  vector<STARE_ArrayIndexSpatialValue> dep_var_stare_indices;
467  gf->get_stare_indices(dependent_var->name(), dep_var_stare_indices);
468 
469  // Put the stare indices passed into the function into a vector<>
470  vector<STARE_ArrayIndexSpatialValue> target_s_indices;
471  read_stare_indices_from_function_argument(raw_stare_indices, target_s_indices);
472 
473  // ptr_duplicate() does not copy data values
474  unique_ptr<Array> result(dynamic_cast<Array*>(dependent_var->ptr_duplicate()));
475 
476  // FIXME Add more types. jhrg 6/17/20
477  switch(dependent_var->var()->type()) {
478  case dods_int16_c: {
479  build_masked_data<dods_int16>(dependent_var, dep_var_stare_indices, target_s_indices,
480  static_cast<short>(mask_value), result);
481  break;
482  }
483  case dods_float32_c: {
484  build_masked_data<dods_float32>(dependent_var, dep_var_stare_indices, target_s_indices,
485  static_cast<float>(mask_value), result);
486  break;
487  }
488 
489  default:
490  throw BESInternalError(string("stare_subset_array() failed: Unsupported array element type (")
491  + dependent_var->var()->type_name() + ").", __FILE__, __LINE__);
492  }
493 
494  return result.release();
495 }
496 
504 STARE_SpatialIntervals
505 stare_box_helper(const vector<point> &points, int resolution) {
506  LatLonDegrees64ValueVector latlonbox;
507  for (auto &p: points) {
508  latlonbox.push_back(LatLonDegrees64(p.lat, p.lon));
509  }
510 
511  STARE index(27, 6);
512  return index.CoverBoundingBoxFromLatLonDegrees(latlonbox, resolution);
513 }
514 
526 STARE_SpatialIntervals
527 stare_box_helper(const point &top_left, const point &bottom_right, int resolution) {
528  LatLonDegrees64ValueVector latlonbox;
529  latlonbox.push_back(LatLonDegrees64(top_left.lat, top_left.lon));
530  latlonbox.push_back(LatLonDegrees64(top_left.lat, bottom_right.lon));
531  latlonbox.push_back(LatLonDegrees64(bottom_right.lat, bottom_right.lon));
532  latlonbox.push_back(LatLonDegrees64(bottom_right.lat, top_left.lon));
533 
534  STARE index(27, 6); // Hack values.
535  return index.CoverBoundingBoxFromLatLonDegrees(latlonbox, resolution);
536 }
537 
538 BaseType *
539 StareBoxFunction::stare_box_dap4_function(libdap::D4RValueList *args, libdap::DMR &dmr)
540 {
541  STARE_SpatialIntervals sivs;
542 
543  if (args->size() == 4) {
544  // build cover from simple box
545  double tl_lat = get_double_value(args->get_rvalue(0)->value(dmr));
546  double tl_lon = get_double_value(args->get_rvalue(1)->value(dmr));
547  double br_lat = get_double_value(args->get_rvalue(2)->value(dmr));
548  double br_lon = get_double_value(args->get_rvalue(3)->value(dmr));
549 
550  sivs = stare_box_helper(point(tl_lat, tl_lon), point(br_lat, br_lon));
551  }
552  else if (args->size() >= 6 && (args->size() % 2) == 0) {
553  // build cover from list of three or more points (lat, lon)
554  bool flip_flop = false;
555  double lat_value = -90;
556  vector<point> points;
557  for (auto &arg: *args) {
558  if (flip_flop) {
559  point pt(lat_value, get_double_value(arg->value(dmr)));
560  points.push_back(pt);
561  flip_flop = false;
562  }
563  else {
564  lat_value = get_double_value(arg->value(dmr));
565  flip_flop = true;
566  }
567  }
568 
569  sivs = stare_box_helper(points);
570  }
571  else {
572  ostringstream oss;
573  oss << "stare_box(): Expected four corner lat/lon values or a list of three or more points, but got "
574  << args->size() << " values.";
575  throw BESSyntaxUserError(oss.str(), __FILE__, __LINE__);
576  }
577 
578  unique_ptr<Array> cover(new Array("cover", new UInt64("cover")));
579  cover->set_value((dods_uint64*)(sivs.data()), static_cast<int>(sivs.size()));
580  cover->append_dim(static_cast<int>(sivs.size()));
581 
582  return cover.release();
583 }
584 
585 
586 
587 
588 } // namespace functions
exception thrown if internal error encountered
error thrown if there is a user syntax error in the request or any other user error
Hold the result from the subset helper function as a collection of vectors.