bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
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
62using namespace libdap;
63using namespace std;
64
65namespace 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
71string stare_storage_path;
72
73// TODO Remove this once change-over is complete. jhrg 6/17/21
74string stare_sidecar_suffix = "_stare.nc";
75
87ostream & 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
104static void
105extract_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
128
136bool
137target_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
183unsigned int
184count(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
211unique_ptr<stare_matches>
212stare_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
233void
234read_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
262BaseType *
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
316BaseType *
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
359BaseType *
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
429double 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
445BaseType *
446StareSubsetArrayFunction::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
504STARE_SpatialIntervals
505stare_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
525
526STARE_SpatialIntervals
527stare_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
538BaseType *
539StareBoxFunction::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
error thrown if there is a user syntax error in the request or any other user error
static libdap::BaseType * stare_count_dap4_function(libdap::D4RValueList *args, libdap::DMR &dmr)
Count the number of STARE indices in the arg that overlap the indices of this dataset.
static libdap::BaseType * stare_intersection_dap4_function(libdap::D4RValueList *args, libdap::DMR &dmr)
Return true/false indicating that the given stare indices intersect the variables.
static libdap::BaseType * stare_subset_dap4_function(libdap::D4RValueList *args, libdap::DMR &dmr)
For the given target STARE indices, return the overlapping dataset X, Y, and STARE indices.
Hold the result from the subset helper function as a collection of vectors.