libdap Updated for version 3.21.1
libdap4 is an implementation of OPeNDAP's DAP protocol.
GeoConstraint.cc
Go to the documentation of this file.
1
2// -*- mode: c++; c-basic-offset:4 -*-
3
4// This file is part of libdap, A C++ implementation of the OPeNDAP Data
5// Access Protocol.
6
7// Copyright (c) 2006 OPeNDAP, Inc.
8// Author: James Gallagher <jgallagher@opendap.org>
9//
10// This library is free software; you can redistribute it and/or
11// modify it under the terms of the GNU Lesser General Public
12// License as published by the Free Software Foundation; either
13// version 2.1 of the License, or (at your option) any later version.
14//
15// This library is distributed in the hope that it will be useful,
16// but WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18// Lesser General Public License for more details.
19//
20// You should have received a copy of the GNU Lesser General Public
21// License along with this library; if not, write to the Free Software
22// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23//
24// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
25
26// The Grid Selection Expression Clause class.
27
28#include "config.h"
29
30#include <algorithm> // for find_if
31#include <cmath>
32#include <cstring>
33#include <iostream>
34#include <sstream>
35
36// #define DODS_DEBUG
37// #define DODS_DEBUG2
38
39#include <Array.h>
40#include <Error.h>
41#include <Float64.h>
42#include <InternalErr.h>
43#include <debug.h>
44#include <dods-datatypes.h>
45#include <util.h>
46
47#include "GeoConstraint.h"
48
49using namespace std;
50using namespace libdap;
51
52namespace functions {
53
58class is_prefix {
59private:
60 string s;
61
62public:
63 is_prefix(const string &in) : s(in) {}
64
65 bool operator()(const string &prefix) { return s.find(prefix) == 0; }
66};
67
78bool unit_or_name_match(set<string> units, set<string> names, const string &var_units, const string &var_name) {
79 return (units.find(var_units) != units.end() ||
80 find_if(names.begin(), names.end(), is_prefix(var_name)) != names.end());
81}
82
97GeoConstraint::Notation GeoConstraint::categorize_notation(const double left, const double right) const {
98 return (left < 0 || right < 0) ? neg_pos : pos;
99}
100
101/* A private method to translate the longitude constraint from -180/179
102 notation to 0/359 notation.
103
104 About the two notations:
105 0 180 360
106 |---------------------------|-------------------------|
107 0 180,-180 0
108
109 so in the neg-pos notation (using the name I give it in this class) both
110 180 and -180 are the same longitude. And in the pos notation 0 and 360 are
111 the same.
112
113 @param left Value-result parameter; the left side of the bounding box
114 @parm right Value-result parameter; the right side of the bounding box */
115void GeoConstraint::transform_constraint_to_pos_notation(double &left, double &right) const {
116 if (left < 0)
117 left += 360;
118
119 if (right < 0)
120 right += 360;
121}
122
132 // Assume earlier logic is correct (since the test is expensive)
133 // for each value, add 180
134 // Longitude could be represented using any of the numeric types
135 for (int i = 0; i < d_lon_length; ++i)
136 if (d_lon[i] < 0)
137 d_lon[i] += 360;
138}
139
150 for (int i = 0; i < d_lon_length; ++i)
151 if (d_lon[i] > 180)
152 d_lon[i] -= 360;
153}
154
155bool GeoConstraint::is_bounding_box_valid(const double left, const double top, const double right,
156 const double bottom) const {
157 if ((left < d_lon[0] && right < d_lon[0]) || (left > d_lon[d_lon_length - 1] && right > d_lon[d_lon_length - 1]))
158 return false;
159
160 if (d_latitude_sense == normal) {
161 // When sense is normal, the largest values are at the origin.
162 if ((top > d_lat[0] && bottom > d_lat[0]) ||
163 (top < d_lat[d_lat_length - 1] && bottom < d_lat[d_lat_length - 1]))
164 return false;
165 } else {
166 if ((top < d_lat[0] && bottom < d_lat[0]) ||
167 (top > d_lat[d_lat_length - 1] && bottom > d_lat[d_lat_length - 1]))
168 return false;
169 }
170
171 return true;
172}
173
184void GeoConstraint::find_longitude_indeces(double left, double right, int &longitude_index_left,
185 int &longitude_index_right) const {
186 // Use all values 'modulo 360' to take into account the cases where the
187 // constraint and/or the longitude vector are given using values greater
188 // than 360 (i.e., when 380 is used to mean 20).
189 double t_left = fmod(left, 360.0);
190 double t_right = fmod(right, 360.0);
191
192 // Find the place where 'longitude starts.' That is, what value of the
193 // index 'i' corresponds to the smallest value of d_lon. Why we do this:
194 // Some data sources use offset longitude axes so that the 'seam' is
195 // shifted to a place other than the date line.
196 int i = 0;
197 int lon_origin_index = 0;
198 double smallest_lon = fmod(d_lon[0], 360.0);
199 while (i < d_lon_length) {
200 double curent_lon_value = fmod(d_lon[i], 360.0);
201 if (smallest_lon > curent_lon_value) {
202 smallest_lon = curent_lon_value;
203 lon_origin_index = i;
204 }
205 ++i;
206 }
207
208 DBG2(cerr << "lon_origin_index: " << lon_origin_index << endl);
209
210 // Scan from the index of the smallest value looking for the place where
211 // the value is greater than or equal to the left most point of the bounding
212 // box.
213 i = lon_origin_index;
214 while (fmod(d_lon[i], 360.0) < t_left) {
215 ++i;
216 i = i % d_lon_length;
217
218 // If we cycle completely through all the values/indices, throw
219 if (i == lon_origin_index)
220 throw Error("geogrid: Could not find an index for the longitude value '" + double_to_string(left) + "'");
221 }
222
223 if (fmod(d_lon[i], 360.0) == t_left)
224 longitude_index_left = i;
225 else
226 longitude_index_left = (i - 1) > 0 ? i - 1 : 0;
227
228 DBG2(cerr << "longitude_index_left: " << longitude_index_left << endl);
229
230 // Assume the vector is circular --> the largest value is next to the
231 // smallest.
232 int largest_lon_index = (lon_origin_index - 1 + d_lon_length) % d_lon_length;
233 i = largest_lon_index;
234 while (fmod(d_lon[i], 360.0) > t_right) {
235 // This is like modulus but for 'counting down'
236 i = (i == 0) ? d_lon_length - 1 : i - 1;
237 if (i == largest_lon_index)
238 throw Error("geogrid: Could not find an index for the longitude value '" + double_to_string(right) + "'");
239 }
240
241 if (fmod(d_lon[i], 360.0) == t_right)
242 longitude_index_right = i;
243 else
244 longitude_index_right = (i + 1) < d_lon_length - 1 ? i + 1 : d_lon_length - 1;
245
246 DBG2(cerr << "longitude_index_right: " << longitude_index_right << endl);
247}
248
261void GeoConstraint::find_latitude_indeces(double top, double bottom, LatitudeSense sense, int &latitude_index_top,
262 int &latitude_index_bottom) const {
263 int i, j;
264
265 if (sense == normal) {
266 i = 0;
267 while (i < d_lat_length - 1 && top < d_lat[i])
268 ++i;
269
270 j = d_lat_length - 1;
271 while (j > 0 && bottom > d_lat[j])
272 --j;
273
274 if (d_lat[i] == top)
275 latitude_index_top = i;
276 else
277 latitude_index_top = (i - 1) > 0 ? i - 1 : 0;
278
279 if (d_lat[j] == bottom)
280 latitude_index_bottom = j;
281 else
282 latitude_index_bottom = (j + 1) < d_lat_length - 1 ? j + 1 : d_lat_length - 1;
283 } else {
284 i = d_lat_length - 1;
285 while (i > 0 && d_lat[i] > top)
286 --i;
287
288 j = 0;
289 while (j < d_lat_length - 1 && d_lat[j] < bottom)
290 ++j;
291
292 if (d_lat[i] == top)
293 latitude_index_top = i;
294 else
295 latitude_index_top = (i + 1) < d_lat_length - 1 ? i + 1 : d_lat_length - 1;
296
297 if (d_lat[j] == bottom)
298 latitude_index_bottom = j;
299 else
300 latitude_index_bottom = (j - 1) > 0 ? j - 1 : 0;
301 }
302}
303
308 return d_lat[0] >= d_lat[d_lat_length - 1] ? normal : inverted;
309}
310
311// Use 'index' as the pivot point. Move the points behind index to the front of
312// the vector and those points in front of and at index to the rear.
313static void swap_vector_ends(char *dest, char *src, int len, int index, int elem_sz) {
314 memcpy(dest, src + index * elem_sz, (len - index) * elem_sz);
315
316 memcpy(dest + (len - index) * elem_sz, src, index * elem_sz);
317}
318
319template <class T>
320static void transpose(std::vector<std::vector<T>> a, std::vector<std::vector<T>> b, int width, int height) {
321 for (int i = 0; i < width; i++) {
322 for (int j = 0; j < height; j++) {
323 b[j][i] = a[i][j];
324 }
325 }
326}
327
335void GeoConstraint::transpose_vector(double *src, const int length) {
336 double *tmp = new double[length];
337
338 int i = 0, j = length - 1;
339 while (i < length)
340 tmp[j--] = src[i++];
341
342 memcpy(src, tmp, length * sizeof(double));
343
344 delete[] tmp;
345}
346
347static int count_size_except_latitude_and_longitude(Array &a) {
348 if (a.dim_end() - a.dim_begin() <= 2) // < 2 is really an error...
349 return 1;
350
351 int size = 1;
352 for (Array::Dim_iter i = a.dim_begin(); (i + 2) != a.dim_end(); ++i)
353 size *= a.dimension_size(i, true);
354
355 return size;
356}
357
358void GeoConstraint::flip_latitude_within_array(Array &a, int lat_length, int lon_length) {
359 if (!d_array_data) {
360 a.read();
361 d_array_data = static_cast<char *>(a.value());
362 d_array_data_size = a.width(true); // Bytes not elements
363 }
364
365 int size = count_size_except_latitude_and_longitude(a);
366 // char *tmp_data = new char[d_array_data_size];
367 vector<char> tmp_data(d_array_data_size);
368 int array_elem_size = a.var()->width(true);
369 int lat_lon_size = (d_array_data_size / size);
370
371 DBG(cerr << "lat, lon_length: " << lat_length << ", " << lon_length << endl);
372 DBG(cerr << "size: " << size << endl);
373 DBG(cerr << "d_array_data_size: " << d_array_data_size << endl);
374 DBG(cerr << "array_elem_size: " << array_elem_size << endl);
375 DBG(cerr << "lat_lon_size: " << lat_lon_size << endl);
376
377 for (int i = 0; i < size; ++i) {
378 int lat = 0;
379 int s_lat = lat_length - 1;
380 // lon_length is the element size; memcpy() needs the number of bytes
381 int lon_size = array_elem_size * lon_length;
382 int offset = i * lat_lon_size;
383 while (s_lat > -1)
384 memcpy(tmp_data.data() + offset + (lat++ * lon_size), d_array_data + offset + (s_lat-- * lon_size),
385 lon_size);
386 }
387
388 memcpy(d_array_data, tmp_data.data(), d_array_data_size);
389}
390
399void GeoConstraint::reorder_longitude_map(int longitude_index_left) {
400 double *tmp_lon = new double[d_lon_length];
401
402 swap_vector_ends((char *)tmp_lon, (char *)d_lon, d_lon_length, longitude_index_left, sizeof(double));
403
404 memcpy(d_lon, tmp_lon, d_lon_length * sizeof(double));
405
406 delete[] tmp_lon;
407}
408
409static int count_dimensions_except_longitude(Array &a) {
410 int size = 1;
411 for (Array::Dim_iter i = a.dim_begin(); (i + 1) != a.dim_end(); ++i)
412 size *= a.dimension_size(i, true);
413
414 return size;
415}
416
435
437 throw Error("This grid does not have Longitude as its rightmost dimension, the geogrid()\ndoes not support "
438 "constraints that wrap around the edges of this type of grid.");
439
440 DBG(cerr << "Constraint for the left half: " << get_longitude_index_left() << ", " << get_lon_length() - 1 << endl);
441
442 // Build a constraint for the left part and get those values
444 a.set_read_p(false);
445 a.read();
446 DBG2(a.print_val(stderr));
447
448 // Save the left-hand data to local storage
449 int left_size = a.width(true); // width() == length() * element size
450 char *left_data = (char *)a.value(); // value() allocates and copies
451
452 // Build a constraint for the 'right' part, which goes from the left edge
453 // of the array to the right index and read those data.
454 // (Don't call a.clear_constraint() since that will clear the constraint on
455 // all the dimensions while add_constraint() will replace a constraint on
456 // the given dimension).
457
458 DBG(cerr << "Constraint for the right half: " << 0 << ", " << get_longitude_index_right() << endl);
459
460 a.add_constraint(lon_dim, 0, 1, get_longitude_index_right());
461 a.set_read_p(false);
462 a.read();
463 DBG2(a.print_val(stderr));
464
465 char *right_data = (char *)a.value();
466 int right_size = a.width(true);
467
468 // Make one big lump O'data
469 d_array_data_size = left_size + right_size;
470 d_array_data = new char[d_array_data_size];
471
472 // Assume COARDS conventions are being followed: lon varies fastest.
473 // These *_elements variables are actually elements * bytes/element since
474 // memcpy() uses bytes.
475 int elem_size = a.var()->width(true);
476 int left_row_size = (get_lon_length() - get_longitude_index_left()) * elem_size;
477 int right_row_size = (get_longitude_index_right() + 1) * elem_size;
478 int total_bytes_per_row = left_row_size + right_row_size;
479
480 DBG2(cerr << "elem_size: " << elem_size << "; left & right size: " << left_row_size << ", " << right_row_size
481 << endl);
482
483 // This will work for any number of dimension so long as longitude is the
484 // right-most array dimension.
485 int rows_to_copy = count_dimensions_except_longitude(a);
486 for (int i = 0; i < rows_to_copy; ++i) {
487 DBG(cerr << "Copying " << i << "th row" << endl);
488 DBG(cerr << "left memcpy: " << *(float *)(left_data + (left_row_size * i)) << endl);
489
490 memcpy(d_array_data + (total_bytes_per_row * i), left_data + (left_row_size * i), left_row_size);
491
492 DBG(cerr << "right memcpy: " << *(float *)(right_data + (right_row_size * i)) << endl);
493
494 memcpy(d_array_data + (total_bytes_per_row * i) + left_row_size, right_data + (right_row_size * i),
495 right_row_size);
496 }
497
498 delete[] left_data;
499 delete[] right_data;
500}
501
505 : d_array_data(0), d_array_data_size(0), d_lat(0), d_lon(0), d_lat_length(0), d_lon_length(0),
506 d_latitude_index_top(0), d_latitude_index_bottom(0), d_longitude_index_left(0), d_longitude_index_right(0),
507 d_bounding_box_set(false), d_longitude_rightmost(false), d_longitude_notation(unknown_notation),
508 d_latitude_sense(unknown_sense) {
509 // Build sets of attribute values for easy searching. Maybe overkill???
510 d_coards_lat_units.insert("degrees_north");
511 d_coards_lat_units.insert("degree_north");
512 d_coards_lat_units.insert("degree_N");
513 d_coards_lat_units.insert("degrees_N");
514
515 d_coards_lon_units.insert("degrees_east");
516 d_coards_lon_units.insert("degree_east");
517 d_coards_lon_units.insert("degrees_E");
518 d_coards_lon_units.insert("degree_E");
519
520 d_lat_names.insert("COADSY");
521 d_lat_names.insert("lat");
522 d_lat_names.insert("Lat");
523 d_lat_names.insert("LAT");
524
525 d_lon_names.insert("COADSX");
526 d_lon_names.insert("lon");
527 d_lon_names.insert("Lon");
528 d_lon_names.insert("LON");
529}
530
541void GeoConstraint::set_bounding_box(double top, double left, double bottom, double right) {
542 // Ensure this method is called only once. What about pthreads?
543 // The method Array::reset_constraint() might make this so it could be
544 // called more than once. jhrg 8/30/06
545 if (d_bounding_box_set)
546 throw Error("It is not possible to register more than one geographical constraint on a variable.");
547
548 // Record the 'sense' of the latitude for use here and later on.
549 d_latitude_sense = categorize_latitude();
550#if 0
551 if (d_latitude_sense == inverted)
552 throw Error("geogrid() does not currently work with inverted data (data where the north pole is at a negative latitude value).");
553#endif
554
555 // Categorize the notation used by the bounding box (0/359 or -180/179).
556 d_longitude_notation = categorize_notation(left, right);
557
558 // If the notation uses -180/179, transform the request to 0/359 notation.
559 if (d_longitude_notation == neg_pos)
561
562 // If the grid uses -180/179, transform it to 0/359 as well. This will make
563 // subsequent logic easier and adds only a few extra operations, even with
564 // large maps.
565 Notation longitude_notation = categorize_notation(d_lon[0], d_lon[d_lon_length - 1]);
566
567 if (longitude_notation == neg_pos)
569
570 if (!is_bounding_box_valid(left, top, right, bottom))
571 throw Error("The bounding box does not intersect any data within this Grid or Array. The\ngeographical extent "
572 "of these data are from latitude " +
573 double_to_string(d_lat[0]) + " to " + double_to_string(d_lat[d_lat_length - 1]) +
574 "\nand longitude " + double_to_string(d_lon[0]) + " to " +
575 double_to_string(d_lon[d_lon_length - 1]) + " while the bounding box provided was latitude " +
576 double_to_string(top) + " to " + double_to_string(bottom) + "\nand longitude " +
577 double_to_string(left) + " to " + double_to_string(right));
578
579 // This is simpler than the longitude case because there's no need to
580 // test for several notations, no need to accommodate them in the return,
581 // no modulo arithmetic for the axis and no need to account for a
582 // constraint with two disconnected parts to be joined.
583 find_latitude_indeces(top, bottom, d_latitude_sense, d_latitude_index_top, d_latitude_index_bottom);
584
585 // Find the longitude map indexes that correspond to the bounding box.
586 find_longitude_indeces(left, right, d_longitude_index_left, d_longitude_index_right);
587
588 DBG(cerr << "Bounding box (tlbr): " << d_latitude_index_top << ", " << d_longitude_index_left << ", "
589 << d_latitude_index_bottom << ", " << d_longitude_index_right << endl);
590
591 d_bounding_box_set = true;
592}
593
594} // namespace functions
GeoConstraint()
Initialize GeoConstraint.
void transform_constraint_to_pos_notation(double &left, double &right) const
virtual void transform_longitude_to_pos_notation()
virtual void reorder_longitude_map(int longitude_index_left)
virtual void transform_longitude_to_neg_pos_notation()
virtual void reorder_data_longitude_axis(libdap::Array &a, libdap::Array::Dim_iter lon_dim)
bool is_longitude_rightmost() const
void find_latitude_indeces(double top, double bottom, LatitudeSense sense, int &latitude_index_top, int &latitude_index_bottom) const
int get_longitude_index_left() const
Notation categorize_notation(const double left, const double right) const
virtual void flip_latitude_within_array(libdap::Array &a, int lat_length, int lon_length)
void find_longitude_indeces(double left, double right, int &longitude_index_left, int &longitude_index_right) const
int get_longitude_index_right() const
virtual void transpose_vector(double *src, const int length)
virtual bool is_bounding_box_valid(const double left, const double top, const double right, const double bottom) const
virtual LatitudeSense categorize_latitude() const
void set_bounding_box(double top, double left, double bottom, double right)
A multidimensional array of identical data types.
Definition Array.h:121
Dim_iter dim_end()
Definition Array.cc:692
virtual void add_constraint(Dim_iter i, int start, int stride, int stop)
Adds a constraint to an Array dimension.
Definition Array.cc:598
void print_val(ostream &out, string space="", bool print_decl_p=true) override
Prints the value of the variable.
Definition Array.cc:1187
std::vector< dimension >::iterator Dim_iter
Definition Array.h:225
virtual int dimension_size(Dim_iter i, bool constrained=false)
Returns the size of the dimension.
Definition Array.cc:723
Dim_iter dim_begin()
Definition Array.cc:689
virtual bool read()
Read data into a local buffer.
Definition BaseType.cc:775
virtual unsigned int width(bool constrained=false) const
How many bytes does this variable use Return the number of bytes of storage this variable uses....
Definition BaseType.cc:1138
A class for error processing.
Definition Error.h:92
unsigned int width(bool constrained=false) const override
Returns the width of the data, in bytes.
Definition Vector.h:191
virtual void value(dods_byte *b) const
Definition Vector.cc:2295
void set_read_p(bool state) override
Indicates that the data is ready to send.
Definition Vector.cc:389
BaseType * var(const string &name="", bool exact_match=true, btp_stack *s=nullptr) override
Definition Vector.cc:469
#define DBG(x)
Definition debug.h:58
#define DBG2(x)
Definition debug.h:74
bool unit_or_name_match(set< string > units, set< string > names, const string &var_units, const string &var_name)
top level DAP object to house generic methods
Definition AISConnect.cc:30
string double_to_string(const double &num)
Definition util.cc:962