bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
NCArray.cc
1// -*- mode: c++; c-basic-offset:4 -*-
2
3// This file is part of nc_handler, a data handler for the OPeNDAP data
4// server.
5
6// Copyright (c) 2002,2003 OPeNDAP, Inc.
7// Author: James Gallagher <jgallagher@opendap.org>
8//
9// This is free software; you can redistribute it and/or modify it under the
10// terms of the GNU Lesser General Public License as published by the Free
11// Software Foundation; either version 2.1 of the License, or (at your
12// option) any later version.
13//
14// This software is distributed in the hope that it will be useful, but
15// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
17// License for more details.
18//
19// You should have received a copy of the GNU Lesser General Public
20// License along with this library; if not, write to the Free Software
21// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22//
23// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
24
25// (c) COPYRIGHT URI/MIT 1994-1999
26// Please read the full copyright statement in the file COPYRIGHT.
27//
28// Authors:
29// reza Reza Nekovei (rnekovei@ieee.org)
30
31// netCDF sub-class implementation for NCByte,...NCGrid.
32// The files are patterned after the subcalssing examples
33// Test<type>.c,h files.
34//
35// ReZa 1/12/95
36
37#include "config_nc.h"
38
39#include <cstring>
40#include <iostream>
41#include <sstream>
42#include <algorithm>
43
44#include <netcdf.h>
45
46// #define DODS_DEBUG 1
47
48#include <libdap/BaseType.h>
49#include <libdap/Error.h>
50#include <libdap/InternalErr.h>
51#include <libdap/util.h>
52#include <libdap/debug.h>
53
54#include <BESDebug.h>
55
56#include "NCRequestHandler.h"
57#include "NCArray.h"
58#include "NCStructure.h"
59// #include "nc_util.h"
60
61#define MODULE "nc"
62#define prolog std::string("NCArray::").append(__func__).append("() - ")
63
64BaseType *
65NCArray::ptr_duplicate()
66{
67 return new NCArray(*this);
68}
69
77NCArray::NCArray(const string &n, const string &d, BaseType *v)
78 : Array(n, d, v)
79{}
80
81NCArray::NCArray(const NCArray &rhs) : Array(rhs)
82{}
83
84NCArray::~NCArray()
85{
86}
87
88NCArray &
89NCArray::operator=(const NCArray &rhs)
90{
91 if (this == &rhs)
92 return *this;
93
94 dynamic_cast<Array &>(*this) = rhs;
95
96 return *this;
97}
98
99
100// Should this be a private method? jhrg 11/3/04
114long
115NCArray::format_constraint(size_t *cor, ptrdiff_t *step, size_t *edg,
116 bool *has_stride)
117{
118 int start, stride, stop;
119 int id = 0;
120 long nels = 1;
121
122 *has_stride = false;
123
124 for (Dim_iter p = dim_begin(); p != dim_end(); ++p) {
125 start = dimension_start(p, true);
126 stride = dimension_stride(p, true);
127 stop = dimension_stop(p, true);
128#if 0
129 // Check for an empty constraint and use the whole dimension if so.
130 if (start + stop + stride == 0) {
131 start = dimension_start(p, false);
132 stride = dimension_stride(p, false);
133 stop = dimension_stop(p, false);
134 }
135#endif
136 cor[id] = start;
137 step[id] = stride;
138 edg[id] = ((stop - start) / stride) + 1; // count of elements
139 nels *= edg[id++]; // total number of values for variable
140
141 if (stride != 1)
142 *has_stride = true;
143 }
144
145 return nels;
146}
147
148void NCArray::do_cardinal_array_read(int ncid, int varid, nc_type datatype,
149 vector<char> &values, bool has_values, int values_offset,
150 int nels, size_t cor[], size_t edg[], ptrdiff_t step[], bool has_stride)
151{
152 size_t size;
153 int errstat;
154#if NETCDF_VERSION >= 4
155 errstat = nc_inq_type(ncid, datatype, 0, &size);
156 if (errstat != NC_NOERR)
157 throw Error(errstat, "Could not get the size for the type.");
158#else
159 size = nctypelen(datatype);
160#endif
161
162 BESDEBUG( MODULE, prolog << "size = " << size << endl);
163 switch (datatype) {
164 case NC_FLOAT:
165 case NC_DOUBLE:
166 case NC_SHORT:
167 case NC_INT:
168#if NETCDF_VERSION >= 4
169 case NC_USHORT:
170 case NC_UINT:
171 case NC_UBYTE:
172#endif
173 {
174 if (!has_values) {
175 values.resize(nels * size);
176 if (has_stride)
177 errstat = nc_get_vars(ncid, varid, cor, edg, step, values.data());
178 else
179 errstat = nc_get_vara(ncid, varid, cor, edg, values.data());
180 if (errstat != NC_NOERR){
181 ostringstream oss;
182 oss << prolog << "Could not get the value for Array variable '" << name() << "'.";
183 oss << " dimensions: " << dimensions(true);
184 oss << " nc_get_vara() errstat: " << errstat;
185 throw Error(errstat, oss.str());
186 }
187 // Do not set has_values to true here because the 'true' state
188 // indicates that the values for an entire compound have been
189 // read.
190 }
191
192 val2buf(values.data() + values_offset);
193 set_read_p(true);
194 break;
195 }
196
197 case NC_BYTE:{
198 if (!has_values) {
199 values.resize(nels * size);
200 if (has_stride)
201 errstat = nc_get_vars(ncid, varid, cor, edg, step, values.data());
202 else
203 errstat = nc_get_vara(ncid, varid, cor, edg, values.data());
204 if (errstat != NC_NOERR)
205 throw Error(errstat, prolog + "Could not get the value for variable '" + name() + string("' (NCArray::do_cardinal_array_read)"));
206 }
207 if (NCRequestHandler::get_promote_byte_to_short()) {
208 // the data set's signed byte data are going to be stored in a short
209 // not an unsigned byte array. But double check that the template
210 // data type is Int16.
211 if (var()->type() != libdap::dods_int16_c) {
212 throw Error(string("NC.PromoteByteToShort is set but the underlying array type is still a Byte: ") + name() + string("."));
213 }
214 // temporary vector for short (int16) data
215 vector<short int> tmp(nels);
216
217 // Pointer into the byte data. These values might be part of a compound and
218 // thus might have been read by a previous call (has_values is true in that
219 // case).
220 const char *raw_byte_data = values.data() + values_offset;
221 for (int i = 0; i < nels; ++i)
222 tmp[i] = *raw_byte_data++;
223 val2buf(tmp.data());
224 set_read_p(true);
225 }
226 else {
227 val2buf(values.data() + values_offset);
228 set_read_p(true);
229 }
230 break;
231 }
232
233 case NC_CHAR: {
234 // Use the dimension info from netcdf since that's the place where
235 // this variable has N-dims. In the DAP representation it's a N-1
236 // dimensional variable.
237 int num_dim; // number of dim. in variable
238 int vdimids[MAX_VAR_DIMS]; // variable dimension ids
239 errstat = nc_inq_var(ncid, varid, (char *)0, (nc_type*)0, &num_dim, vdimids, (int *)0);
240 if (errstat != NC_NOERR)
241 throw Error(errstat, string("Could not read information about the variable `") + name() + string("'."));
242 if (num_dim < 2) // one-dim --> DAP String and we should not be here
243 throw Error(string("A one-dimensional NC_CHAR array should now map to a DAP string: '") + name() + string("'."));
244
245 size_t vdims[MAX_VAR_DIMS]; // variable dimension sizes
246 for (int i = 0; i < num_dim; ++i)
247 if ((errstat = nc_inq_dimlen(ncid, vdimids[i], &vdims[i])) != NC_NOERR)
248 throw Error(errstat, string("Could not read dimension information about the variable `") + name() + string("'."));
249
250 int nth_dim_size = vdims[num_dim - 1];
251 cor[num_dim - 1] = 0;
252 edg[num_dim - 1] = nth_dim_size;
253 if (has_stride)
254 step[num_dim - 1] = 1;
255
256 if (!has_values) {
257 values.resize(nels * nth_dim_size * size);
258 if (has_stride)
259 errstat = nc_get_vars_text(ncid, varid, cor, edg, step, values.data());
260 else
261 errstat = nc_get_vara_text(ncid, varid, cor, edg, values.data());
262 if (errstat != NC_NOERR)
263 throw Error(errstat, string("Could not read the variable '") + name() + string("'."));
264 }
265
266 // How large is the Nth dimension? Allocate space for the N-1 dims.
267 vector<string> strg(nels);
268 vector<char> buf(nth_dim_size + 1);
269 // put the char values in the string array
270 for (int i = 0; i < nels; i++) {
271 strncpy(buf.data(), values.data() + values_offset + (i * nth_dim_size), nth_dim_size);
272 buf[nth_dim_size] = '\0';
273 strg[i] = buf.data();
274 }
275
276 set_read_p(true);
277 val2buf(strg.data());
278 break;
279 }
280#if NETCDF_VERSION >= 4
281 case NC_STRING: {
282 if (!has_values) {
283 values.resize(nels * size);
284 if (has_stride)
285 errstat = nc_get_vars_string(ncid, varid, cor, edg, step, (char**)(values.data() + values_offset));
286 else
287 errstat = nc_get_vara_string(ncid, varid, cor, edg, (char**)(values.data() + values_offset));
288 if (errstat != NC_NOERR)
289 throw Error(errstat, string("Could not read the variable `") + name() + string("'."));
290 }
291
292 // put the char values in the string array
293 vector < string > strg(nels);
294 for (int i = 0; i < nels; i++) {
295 // values_offset is in bytes; then cast to char** to find the
296 // ith element; then dereference to get the C-style string.
297 strg[i] = *((char**)(values.data() + values_offset) + i);
298 }
299
300 nc_free_string(nels, (char**)values.data());
301 set_read_p(true);
302 val2buf(strg.data());
303 break;
304 }
305#endif
306 default:
307 throw InternalErr(__FILE__, __LINE__, string("Unknown data type for the variable '") + name() + string("'."));
308 }
309}
310
311void NCArray::do_array_read(int ncid, int varid, nc_type datatype,
312 vector<char> &values, bool has_values, int values_offset,
313 int nels, size_t cor[], size_t edg[], ptrdiff_t step[], bool has_stride)
314{
315 int errstat;
316
317#if NETCDF_VERSION >= 4
318 if (datatype >= NC_FIRSTUSERTYPEID /*is_user_defined_type(ncid, datatype)*/) {
319 // datatype >= NC_FIRSTUSERTYPEID) {
320 char type_name[NC_MAX_NAME+1];
321 size_t size;
322 nc_type base_type;
323 size_t nfields;
324 int class_type;
325 errstat = nc_inq_user_type(ncid, datatype, type_name, &size, &base_type, &nfields, &class_type);
326 //cerr << "User-defined attribute type size: " << size << ", nfields: " << nfields << endl;
327 if (errstat != NC_NOERR)
328 throw(InternalErr(__FILE__, __LINE__, "Could not get information about a user-defined type (" + long_to_string(errstat) + ")."));
329
330 switch (class_type) {
331 case NC_COMPOUND: {
332 if (!has_values) {
333 values.resize(size * nels);
334 if (has_stride)
335 errstat = nc_get_vars(ncid, varid, cor, edg, step, values.data());
336 else
337 errstat = nc_get_vara(ncid, varid, cor, edg, values.data());
338 if (errstat != NC_NOERR)
339 throw Error(errstat, string("Could not get the value for variable '") + name() + string("'"));
340 has_values = true;
341 }
342
343 for (int element = 0; element < nels; ++element) {
344 NCStructure *ncs = dynamic_cast<NCStructure*> (var()->ptr_duplicate());
345 for (int i = 0; i < nfields; ++i) {
346 char field_name[NC_MAX_NAME+1];
347 nc_type field_typeid;
348 size_t field_offset;
349 // These are unused... should they be used?
350 // int field_ndims;
351 // int field_sizes[MAX_NC_DIMS];
352 nc_inq_compound_field(ncid, datatype, i, field_name, &field_offset, &field_typeid, nullptr, nullptr); //&field_ndims, field_sizes.data());
353 BaseType *field = ncs->var(field_name);
354 if (field_typeid >= NC_FIRSTUSERTYPEID /*is_user_defined_type(ncid, field_typeid)*/) {
355 // Interior user defined types have names, but not field_names
356 // so use the type name as the field name (matches the
357 // behavior of the ncdds.cc code).
358 nc_inq_compound_name(ncid, field_typeid, field_name);
359 field = ncs->var(field_name);
360 NCStructure &child_ncs = dynamic_cast<NCStructure&> (*field);
361 child_ncs.do_structure_read(ncid, varid, field_typeid,
362 values, has_values, field_offset + values_offset + size * element);
363 }
364 else if (field->is_vector_type()) {
365 // Because the netcdf api reads data 'atomically' from
366 // compounds, this call works for both cardinal and
367 // array variables.
368 NCArray &child_array = dynamic_cast<NCArray&>(*field);
369 child_array.do_array_read(ncid, varid, field_typeid,
370 values, has_values, field_offset + values_offset + size * element,
371 nels, cor, edg, step, has_stride);
372 }
373 else if (field->is_simple_type()) {
374 field->val2buf(values.data() + (element * size) + field_offset);
375 }
376 else {
377 throw InternalErr(__FILE__, __LINE__, "Expecting a netcdf user defined type or an array or a scalar.");
378 }
379
380 field->set_read_p(true);
381 }
382 ncs->set_read_p(true);
383 set_vec(element, ncs);
384 }
385
386 set_read_p(true);
387 break;
388 }
389
390 case NC_VLEN:
391 if (NCRequestHandler::get_ignore_unknown_types())
392 cerr << "in build_user_defined; found a vlen." << endl;
393 else
394 throw Error("The netCDF handler does not currently support NC_VLEN attributes.");
395 break;
396
397 case NC_OPAQUE: {
398 // Use the dimension info from netcdf since that's the place where
399 // this variable has N-dims. In the DAP representation it's a N-1
400 // dimensional variable.
401 int num_dim; // number of dim. in variable
402 int vdimids[MAX_VAR_DIMS]; // variable dimension ids
403 errstat = nc_inq_var(ncid, varid, (char *)0, (nc_type*)0, &num_dim, vdimids, (int *)0);
404 if (errstat != NC_NOERR)
405 throw Error(errstat, string("Could not read information about the variable `") + name() + string("'."));
406 if (num_dim < 1) // one-dim --> DAP String and we should not be here
407 throw Error(string("A one-dimensional NC_OPAQUE array should now map to a DAP Byte: '") + name() + string("'."));
408
409 size_t vdims[MAX_VAR_DIMS]; // variable dimension sizes
410 for (int i = 0; i < num_dim; ++i)
411 if ((errstat = nc_inq_dimlen(ncid, vdimids[i], &vdims[i])) != NC_NOERR)
412 throw Error(errstat, string("Could not read dimension information about the variable `") + name() + string("'."));
413
414 int nth_dim_size = vdims[num_dim - 1];
415 cor[num_dim - 1] = 0;
416 edg[num_dim - 1] = nth_dim_size;
417 if (has_stride)
418 step[num_dim - 1] = 1;
419
420 if (!has_values) {
421 values.resize(size * nels);
422 if (has_stride)
423 errstat = nc_get_vars(ncid, varid, cor, edg, step, values.data());
424 else
425 errstat = nc_get_vara(ncid, varid, cor, edg, values.data());
426 if (errstat != NC_NOERR)
427 throw Error(errstat, string("Could not get the value for variable '") + name() + string("' (NC_OPAQUE)"));
428 has_values = true; // This value may never be used. jhrg 1/9/12
429 }
430
431 val2buf(values.data() + values_offset);
432
433 set_read_p(true);
434 break;
435 }
436
437 case NC_ENUM: {
438 nc_type base_nc_type;
439 errstat = nc_inq_enum(ncid, datatype, nullptr /*name.data()*/, &base_nc_type, nullptr/*&base_size*/, nullptr/*&num_members*/);
440 if (errstat != NC_NOERR)
441 throw(InternalErr(__FILE__, __LINE__, "Could not get information about an enum(" + long_to_string(errstat) + ")."));
442
443 do_cardinal_array_read(ncid, varid, base_nc_type,
444 values, has_values, values_offset,
445 nels, cor, edg, step, has_stride);
446
447 set_read_p(true);
448 break;
449 }
450
451 default:
452 throw InternalErr(__FILE__, __LINE__, "Expected one of NC_COMPOUND, NC_VLEN, NC_OPAQUE or NC_ENUM");
453 }
454
455 }
456 else {
457 do_cardinal_array_read(ncid, varid, datatype, values, has_values, values_offset,
458 nels, cor, edg, step, has_stride);
459 }
460#else
461 do_cardinal_array_read(ncid, varid, datatype, values, has_values, values_offset,
462 nels, cor, edg, step, has_stride);
463#endif
464}
465
466bool NCArray::read()
467{
468 if (read_p()) // Nothing to do
469 return true;
470
471 int ncid;
472 int errstat = nc_open(dataset().c_str(), NC_NOWRITE, &ncid); /* netCDF id */
473 if (errstat != NC_NOERR)
474 throw Error(errstat, string("Could not open the dataset's file (") + dataset().c_str() + string(")"));
475
476 int varid; /* variable Id */
477 errstat = nc_inq_varid(ncid, name().c_str(), &varid);
478 if (errstat != NC_NOERR)
479 throw InternalErr(__FILE__, __LINE__, "Could not get variable ID for: " + name() + ". (error: " + long_to_string(errstat) + ").");
480
481 nc_type datatype; // variable data type
482 errstat = nc_inq_vartype(ncid, varid, &datatype);
483 if (errstat != NC_NOERR)
484 throw Error(errstat, string("Could not read information about the variable `") + name() + string("'."));
485
486 size_t cor[MAX_NC_DIMS]; /* corner coordinates */
487 size_t edg[MAX_NC_DIMS]; /* edges of hyper-cube */
488 ptrdiff_t step[MAX_NC_DIMS]; /* stride of hyper-cube */
489 bool has_stride;
490 for(unsigned int i=0; i<MAX_NC_DIMS; i++){
491 cor[i] = edg[i] = step[i] = 0;
492 }
493 long nels = format_constraint(cor, step, edg, &has_stride);
494// ostringstream oss;
495// for(unsigned int i=0; i<MAX_NC_DIMS; i++){
496// oss << cor[i] << ", " << edg[i] << ", " << step[i] << endl;
497// }
498// BESDEBUG( MODULE, prolog << "corners, edges, stride" << endl << oss.str() << endl);
499
500 vector<char> values;
501 do_array_read(ncid, varid, datatype, values, false /*has_values*/, 0 /*values_offset*/,
502 nels, cor, edg, step, has_stride);
503 set_read_p(true);
504
505 if (nc_close(ncid) != NC_NOERR)
506 throw InternalErr(__FILE__, __LINE__, "Could not close the dataset!");
507
508 return true;
509}
NCArray(const string &n, const string &d, BaseType *v)
Definition NCArray.cc:77
STL class.