bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
FoDapCovJsonTransform.cc
1// -*- mode: c++; c-basic-offset:4 -*-
2//
3// FoDapCovJsonTransform.cc
4//
5// This file is part of BES CovJSON File Out Module
6//
7// Copyright (c) 2018 OPeNDAP, Inc.
8// Author: Corey Hemphill <hemphilc@oregonstate.edu>
9// Author: River Hendriksen <hendriri@oregonstate.edu>
10// Author: Riley Rimer <rrimer@oregonstate.edu>
11//
12// Adapted from the File Out JSON module implemented by Nathan Potter
13// Author: Kent Yang <myang6@hdfgroup.org> 2022-10
14// Note from KY: Make the module correctly generate simple grid,point,
15// point series and vertical profile coverage. The DAP2
16// grid also correctly maps to coverage.
17// Also the original testsuite is completely replaced.
18// This library is free software; you can redistribute it and/or
19// modify it under the terms of the GNU Lesser General Public
20// License as published by the Free Software Foundation; either
21// version 2.1 of the License, or (at your option) any later version.
22//
23// This library is distributed in the hope that it will be useful,
24// but WITHOUT ANY WARRANTY; without even the implied warranty of
25// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
26// Lesser General Public License for more details.
27//
28// You should have received a copy of the GNU Lesser General Public
29// License along with this library; if not, write to the Free Software
30// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
31//
32
33#include "config.h"
34
35#include <cassert>
36#include <sstream>
37#include <iostream>
38#include <fstream>
39#include <stddef.h>
40#include <string>
41#include <cstring>
42#include <typeinfo>
43#include <iomanip> // setprecision
44#include <sstream> // stringstream
45#include <vector>
46#include <ctime>
47#include <time.h>
48
49using std::ostringstream;
50using std::istringstream;
51
52#define MODULE "covj"
53#define prolog string("FoDapCovJsonTransform::").append(__func__).append("() - ")
54
55#include <libdap/DDS.h>
56#include <libdap/DMR.h>
57#include <libdap/D4Group.h>
58#include <libdap/D4Attributes.h>
59#include <libdap/Structure.h>
60#include <libdap/Constructor.h>
61#include <libdap/Array.h>
62#include <libdap/Grid.h>
63#include <libdap/Sequence.h>
64#include <libdap/Byte.h>
65#include <libdap/UInt16.h>
66#include <libdap/Int16.h>
67#include <libdap/UInt32.h>
68#include <libdap/Int32.h>
69#include <libdap/Float32.h>
70#include <libdap/Float64.h>
71#include <libdap/Str.h>
72#include <libdap/Url.h>
73
74#include <BESDebug.h>
75#include <BESInternalError.h>
76#include <DapFunctionUtils.h>
77#include <RequestServiceTimer.h>
78#include "FoDapCovJsonTransform.h"
79#include "focovjson_utils.h"
80#include "FoCovJsonRequestHandler.h"
81
82using std::map;
83#define FoDapCovJsonTransform_debug_key "focovjson"
84
85
86bool FoDapCovJsonTransform::canConvert()
87{
88 // If x, y, z, and t all exist
89 // We are assuming the following is true:
90 // - shapeVals[0] = x axis
91 // - shapeVals[1] = y axis
92 // - shapeVals[2] = z axis
93 // - shapeVals[3] = t axis
94#if 0
95cerr<<"Before X and Y and Z and T"<<endl;
96cerr<<"Number of parameters is "<<this->parameters.size() <<endl;
97cerr<<"shapeVals is "<<shapeVals.size() <<endl;
98cerr<<"Number of Axis is "<<this->axes.size() <<endl;
99for (int i = 0; i <this->axes.size(); i++) {
100cerr<<"Axis name is "<<this->axes[i]->name << endl;
101cerr<<"Axis value is "<<this->axes[i]->values << endl;
102
103}
104
105#endif
106
107 bool ret_value = false;
108 if(true == is_simple_cf_geographic || true == is_dap2_grid) {
109 domainType = "Grid";
110 ret_value = true;
111 }
112 else {
113
114 ret_value = true;
115 switch (dsg_type) {
116 case SPOINT: {
117 domainType = "Point";
118 break;
119 }
120 case POINTS: {
121 domainType = "PointSeries";
122 break;
123 }
124 case PROFILE: {
125 domainType = "VerticalProfile";
126 break;
127 }
128 default:
129 ret_value = false;
130 }
131 }
132
133 return ret_value;
134
135 // The following code is commented out for the time being.
136 // We only support the simple CF geographic projection, point, pointSeries, vertical profile for the time being.
137 // We will enhance this module to support the other cases in the new tickets.
138 // KY 2022-06-10
139#if 0
140 if(xExists && yExists && zExists && tExists) {
141
142 if (shapeVals.size() < 4)
143 return false;
144
145 // A domain with Grid domain type MUST have the axes "x" and "y"
146 // and MAY have the axes "z" and "t".
147 if((shapeVals[0] > 1) && (shapeVals[1] > 1) && (shapeVals[2] >= 1) && (shapeVals[3] >= 0)) {
148 domainType = "Grid";
149 return true;
150 }
151
152 // A domain with VerticalProfile domain type MUST have the axes "x",
153 // "y", and "z", where "x" and "y" MUST have a single coordinate only.
154 else if((shapeVals[0] == 1) && (shapeVals[1] == 1) && (shapeVals[2] >= 1) && ((shapeVals[3] <= 1) && (shapeVals[3] >= 0))) {
155 domainType = "Vertical Profile";
156 return true;
157 }
158
159 // A domain with PointSeries domain type MUST have the axes "x", "y",
160 // and "t" where "x" and "y" MUST have a single coordinate only. A
161 // domain with PointSeries domain type MAY have the axis "z" which
162 // MUST have a single coordinate only.
163 else if((shapeVals[0] == 1) && (shapeVals[1] == 1) && (shapeVals[2] == 1) && (shapeVals[3] >= 0)) {
164 domainType = "Point Series";
165 return true;
166 }
167
168 // A domain with Point domain type MUST have the axes "x" and "y" and MAY
169 // have the axes "z" and "t" where all MUST have a single coordinate only.
170 else if((shapeVals[0] == 1) && (shapeVals[1] == 1) && (shapeVals[2] == 1) && (shapeVals[3] == 1)) {
171 domainType = "Point";
172 return true;
173 }
174//cerr<<"Before X and Y and T"<<endl;
175 }
176
177 // If just x, y, and t exist
178 // We are assuming the following is true:
179 // - shapeVals[0] = x axis
180 // - shapeVals[1] = y axis
181 // - shapeVals[2] = t axis
182 else if(xExists && yExists && !zExists && tExists) {
183
184 if (shapeVals.size() < 3)
185 return false;
186
187#if 0
188//cerr <<"shapeVals[0] is "<< shapeVals[0] <<endl;
189//cerr <<"shapeVals[1] is "<< shapeVals[1] <<endl;
190//cerr <<"shapeVals[2] is "<< shapeVals[2] <<endl;
191#endif
192
193 // A domain with Grid domain type MUST have the axes "x" and "y"
194 // and MAY have the axes "z" and "t".
195 // The issue here is that shapeVals[0], shapeVals[1],shapeVals[2] may not be exactly x,y,z/t.
196 if((shapeVals[0] >= 1) && (shapeVals[1] >= 1) && (shapeVals[2] >= 0)) {
197 domainType = "Grid";
198 return true;
199 }
200
201 // A domain with PointSeries domain type MUST have the axes "x", "y",
202 // and "t" where "x" and "y" MUST have a single coordinate only. A
203 // domain with PointSeries domain type MAY have the axis "z" which
204 // MUST have a single coordinate only.
205 else if((shapeVals[0] == 1) && (shapeVals[1] == 1) && (shapeVals[2] >= 0)) {
206 domainType = "Point Series";
207 return true;
208 }
209
210 // A domain with Point domain type MUST have the axes "x" and "y" and MAY
211 // have the axes "z" and "t" where all MUST have a single coordinate only.
212 else if((shapeVals[0] == 1) && (shapeVals[1] == 1) && (shapeVals[2] == 1)) {
213 domainType = "Point";
214 return true;
215 }
216//cerr<<"Before X and Y "<<endl;
217 }
218
219 // If just x and y exist
220 // We are assuming the following is true:
221 // - shapeVals[0] = x axis
222 // - shapeVals[1] = y axis
223 else if(xExists && yExists && !zExists && !tExists) {
224
225 if (shapeVals.size() < 2)
226 return false;
227
228 // A domain with Grid domain type MUST have the axes "x" and "y"
229 // and MAY have the axes "z" and "t".
230 if((shapeVals[0] > 1) && (shapeVals[1] > 1)) {
231 domainType = "Grid";
232 return true;
233 }
234
235 // A domain with Point domain type MUST have the axes "x" and "y" and MAY
236 // have the axes "z" and "t" where all MUST have a single coordinate only.
237 else if((shapeVals[0] == 1) && (shapeVals[1] == 1)) {
238 domainType = "Point";
239 return true;
240 }
241 }
242//cerr<<"Coming to the last step."<<endl;
243
244 return false; // This source DDS is not valid as CovJSON
245
246#endif
247}
248
249template<typename T>
250unsigned int FoDapCovJsonTransform::covjsonSimpleTypeArrayWorker(ostream *strm, T *values, unsigned int indx,
251 vector<unsigned int> *shape, unsigned int currentDim, bool is_axis_t_sgeo,libdap::Type a_type)
252{
253 unsigned int currentDimSize = (*shape)[currentDim];
254
255 // FOR TESTING AND DEBUGGING PURPOSES
256 // *strm << "\"currentDim\": \"" << currentDim << "\"" << endl;
257 // *strm << "\"currentDimSize\": \"" << currentDimSize << "\"" << endl;
258
259 for(unsigned int i = 0; i < currentDimSize; i++) {
260 if(currentDim < shape->size() - 1) {
261 BESDEBUG(FoDapCovJsonTransform_debug_key,
262 "covjsonSimpleTypeArrayWorker() - Recursing! indx: " << indx << " currentDim: " << currentDim << " currentDimSize: " << currentDimSize << endl);
263 indx = covjsonSimpleTypeArrayWorker<T>(strm, values, indx, shape, currentDim + 1,is_axis_t_sgeo,a_type);
264 if(i + 1 != currentDimSize) {
265 *strm << ", ";
266 }
267 }
268 else {
269 if(i) {
270 *strm << ", ";
271 }
272 if(typeid(T) == typeid(string)) {
273 // Strings need to be escaped to be included in a CovJSON object.
274 string val = reinterpret_cast<string*>(values)[indx++];
275 *strm << "\"" << focovjson::escape_for_covjson(val) << "\"";
276 }
277 else {
278 // We need to convert CF time to greg time.
279 if(is_axis_t_sgeo) {
280 string axis_t_value;
281 std::ostringstream tmp_stream;
282 long long tmp_value = 0;
283 switch (a_type) {
284 case libdap::dods_byte_c: {
285 unsigned char tmp_byte_value = reinterpret_cast<unsigned char *>(values)[indx++];
286 tmp_value = (long long) tmp_byte_value;
287 break;
288 }
289
290 case libdap::dods_uint16_c: {
291 unsigned short tmp_uint16_value = reinterpret_cast<unsigned short *>(values)[indx++];
292 tmp_value = (long long) tmp_uint16_value;
293 break;
294 }
295
296 case libdap::dods_int16_c: {
297 short tmp_int16_value = reinterpret_cast<short *>(values)[indx++];
298 tmp_value = (long long) tmp_int16_value;
299 break;
300 }
301
302 case libdap::dods_uint32_c: {
303 unsigned int tmp_uint_value = reinterpret_cast<unsigned int *>(values)[indx++];
304 tmp_value = (long long) tmp_uint_value;
305 break;
306 }
307
308 case libdap::dods_int32_c: {
309 int tmp_int_value = reinterpret_cast<int *>(values)[indx++];
310 tmp_value = (long long) tmp_int_value;
311 break;
312 }
313
314 case libdap::dods_float32_c: {
315 float tmp_float_value = reinterpret_cast<float *>(values)[indx++];
316 // In theory, it may cause overflow. In reality, the time in seconds will never be that large.
317 tmp_value = (long long) tmp_float_value;
318 break;
319 }
320
321 case libdap::dods_float64_c: {
322 double tmp_double_value = reinterpret_cast<double *>(values)[indx++];
323 // In theory, it may cause overflow. In reality, the time in seconds will never be that large.
324 tmp_value = (long long) tmp_double_value;
325 break;
326 }
327
328 default:
329 throw BESInternalError("Attempt to extract CF time information from an invalid source", __FILE__, __LINE__);
330 }
331
332
333 axis_t_value = cf_time_to_greg(tmp_value);
334#if 0
335 cerr<<"time value is " <<axis_t_value <<endl;
336 cerr<<"CF time unit is "<<axis_t_units <<endl;
337#endif
338 *strm << "\"" << focovjson::escape_for_covjson(axis_t_value) << "\"";
339 }
340 else
341 *strm << values[indx++];
342 }
343 }
344 }
345
346 return indx;
347}
348
349template<typename T>
350void FoDapCovJsonTransform::covjsonSimpleTypeArray(ostream *strm, libdap::Array *a, string indent, bool sendData)
351{
352 string childindent = indent + _indent_increment;
353 bool axisRetrieved = false;
354 bool parameterRetrieved = false;
355
356 currDataType = a->var()->type_name();
357
358 // FOR TESTING AND DEBUGGING PURPOSES
359 //*strm << "\"type_name\": \"" << a->var()->type_name() << "\"" << endl;
360 //*strm << "\"name\": \"" << a->var()->name() << "\"" << endl;
361
362 getAttributes(strm, a->get_attr_table(), a->name(), &axisRetrieved, &parameterRetrieved);
363
364 //a->print_val(*strm, "\n", true); // For testing purposes
365
366 // sendData = false; // For testing purposes
367
368 // If we are dealing with an Axis
369 if((axisRetrieved == true) && (parameterRetrieved == false)) {
370 struct Axis *currAxis;
371 currAxis = axes[axisCount - 1];
372
373 int numDim = a->dimensions(true);
374 vector<unsigned int> shape(numDim);
375 long length = focovjson::computeConstrainedShape(a, &shape);
376
377 // FOR TESTING AND DEBUGGING PURPOSES
378 // *strm << "\"numDimensions\": \"" << numDim << "\"" << endl;
379 // *strm << "\"length\": \"" << length << "\"" << endl << endl;
380
381 bool handle_axis_t_here = true;
382 if(is_simple_cf_geographic==false && dsg_type == UNSUPPORTED_DSG && currAxis->name.compare("t") == 0)
383 handle_axis_t_here = false;
384 if (handle_axis_t_here) {
385 if (sendData) {
386 currAxis->values += "\"values\": [";
387 unsigned int indx = 0;
388 vector<T> src(length);
389 a->value(src.data());
390
391 ostringstream astrm;
392 bool is_time_axis_for_sgeo = false;
393 if((is_simple_cf_geographic || dsg_type != UNSUPPORTED_DSG) && currAxis->name.compare("t") == 0)
394 is_time_axis_for_sgeo = true;
395
396
397 indx = covjsonSimpleTypeArrayWorker(&astrm, src.data(), 0, &shape, 0,is_time_axis_for_sgeo,a->var()->type());
398 currAxis->values += astrm.str();
399
400 currAxis->values += "]";
401#if 0
402//cerr<<"currAxis value at covjsonSimpleTypeArray is "<<currAxis->values <<endl;
403#endif
404 if (length != indx) {
405 BESDEBUG(FoDapCovJsonTransform_debug_key,
406 "covjsonSimpleTypeArray(Axis) - indx NOT equal to content length! indx: " << indx << " length: " << length << endl);
407 }
408 assert(length == indx);
409 }
410 else {
411 currAxis->values += "\"values\": []";
412 }
413 }
414 }
415
416 // If we are dealing with a Parameter
417 else if(axisRetrieved == false && parameterRetrieved == true) {
418 struct Parameter *currParameter;
419 currParameter = parameters[parameterCount - 1];
420
421#if 0
422cerr<<"Parameter name is "<< currParameter->name<<endl;
423#endif
424 int numDim = a->dimensions(true);
425 vector<unsigned int> shape(numDim);
426 long length = focovjson::computeConstrainedShape(a, &shape);
427
428 // FOR TESTING AND DEBUGGING PURPOSES
429 // *strm << "\"numDimensions\": \"" << a->dimensions(true) << "\"" << endl;
430 // *strm << "\"length\": \"" << length << "\"" << endl << endl;
431
432 currParameter->shape += "\"shape\": [";
433 for(vector<unsigned int>::size_type i = 0; i < shape.size(); i++) {
434 if(i > 0) {
435 currParameter->shape += ", ";
436 }
437
438 // Process the shape's values, which are strings,
439 // convert them into integers, and store them
440 ostringstream otemp;
441 istringstream itemp;
442 int tempVal = 0;
443 otemp << shape[i];
444 istringstream (otemp.str());
445 istringstream (otemp.str()) >> tempVal;
446 shapeVals.push_back(tempVal);
447
448 // t may only have 1 value: the origin timestamp
449 // DANGER: t may not yet be defined
450 if((i == 0) && tExists && is_simple_cf_geographic == false && dsg_type == UNSUPPORTED_DSG) {
451 currParameter->shape += "1";
452 }
453 else {
454 currParameter->shape += otemp.str();
455 }
456 }
457 currParameter->shape += "],";
458
459 if (sendData) {
460 currParameter->values += "\"values\": [";
461 unsigned int indx = 0;
462 vector<T> src(length);
463 a->value(src.data());
464
465 ostringstream pstrm;
466 indx = covjsonSimpleTypeArrayWorker(&pstrm, src.data(), 0, &shape, 0,false,a->var()->type());
467 currParameter->values += pstrm.str();
468
469 currParameter->values += "]";
470
471 if (length != indx) {
472 BESDEBUG(FoDapCovJsonTransform_debug_key,
473 "covjsonSimpleTypeArray(Parameter) - indx NOT equal to content length! indx: " << indx << " length: " << length << endl);
474 }
475 assert(length == indx);
476 }
477 else {
478 currParameter->values += "\"values\": []";
479 }
480 }
481}
482
483void FoDapCovJsonTransform::covjsonStringArray(ostream *strm, libdap::Array *a, string indent, bool sendData)
484{
485 string childindent = indent + _indent_increment;
486 bool axisRetrieved = false;
487 bool parameterRetrieved = false;
488
489 currDataType = a->var()->type_name();
490
491 // FOR TESTING AND DEBUGGING PURPOSES
492 // *strm << "\"attr_tableName\": \"" << a->name() << "\"" << endl;
493
494 // FOR TESTING AND DEBUGGING PURPOSES
495 // *strm << "\"type_name\": \"" << a->var()->type_name() << "\"" << endl;
496
497 getAttributes(strm, a->get_attr_table(), a->name(), &axisRetrieved, &parameterRetrieved);
498
499 // a->print_val(*strm, "\n", true); // For testing purposes
500
501 // sendData = false; // For testing purposes
502
503 // If we are dealing with an Axis
504 if((axisRetrieved == true) && (parameterRetrieved == false)) {
505 struct Axis *currAxis;
506 currAxis = axes[axisCount - 1];
507
508 int numDim = a->dimensions(true);
509 vector<unsigned int> shape(numDim);
510 long length = focovjson::computeConstrainedShape(a, &shape);
511
512 bool handle_axis_t_here = true;
513 if(is_simple_cf_geographic==false && dsg_type == UNSUPPORTED_DSG && currAxis->name.compare("t") == 0)
514 handle_axis_t_here = false;
515 if (handle_axis_t_here) {
516 if (sendData) {
517 currAxis->values += "\"values\": ";
518 unsigned int indx = 0;
519 // The string type utilizes a specialized version of libdap:Array.value()
520 vector<string> sourceValues;
521 a->value(sourceValues);
522
523 ostringstream astrm;
524 indx = covjsonSimpleTypeArrayWorker(&astrm, (string *) (sourceValues.data()), 0, &shape, 0,false,a->var()->type());
525 currAxis->values += astrm.str();
526
527 if (length != indx) {
528 BESDEBUG(FoDapCovJsonTransform_debug_key,
529 "covjsonStringArray(Axis) - indx NOT equal to content length! indx: " << indx << " length: " << length << endl);
530 }
531 assert(length == indx);
532 }
533 else {
534 currAxis->values += "\"values\": []";
535 }
536 }
537 }
538
539 // If we are dealing with a Parameter
540 else if(axisRetrieved == false && parameterRetrieved == true) {
541 struct Parameter *currParameter;
542 currParameter = parameters[parameterCount - 1];
543
544 int numDim = a->dimensions(true);
545 vector<unsigned int> shape(numDim);
546 long length = focovjson::computeConstrainedShape(a, &shape);
547
548 currParameter->shape += "\"shape\": [";
549 for(vector<unsigned int>::size_type i = 0; i < shape.size(); i++) {
550 if(i > 0) {
551 currParameter->shape += ", ";
552 }
553
554 // Process the shape's values, which are strings,
555 // convert them into integers, and store them
556 ostringstream otemp;
557 istringstream itemp;
558 int tempVal = 0;
559 otemp << shape[i];
560 istringstream (otemp.str());
561 istringstream (otemp.str()) >> tempVal;
562 shapeVals.push_back(tempVal);
563
564 // t may only have 1 value: the origin timestamp
565 // DANGER: t may not yet be defined
566 if((i == 0) && tExists && is_simple_cf_geographic == false && dsg_type == UNSUPPORTED_DSG) {
567 //if((i == 0) && tExists) {
568 currParameter->shape += "1";
569 }
570 else {
571 currParameter->shape += otemp.str();
572 }
573 }
574 currParameter->shape += "],";
575
576 if (sendData) {
577 currParameter->values += "\"values\": ";
578 unsigned int indx = 0;
579 // The string type utilizes a specialized version of libdap:Array.value()
580 vector<string> sourceValues;
581 a->value(sourceValues);
582
583 ostringstream pstrm;
584 indx = covjsonSimpleTypeArrayWorker(&pstrm, (string *) (sourceValues.data()), 0, &shape, 0,false,a->var()->type());
585 currParameter->values += pstrm.str();
586
587 if (length != indx) {
588 BESDEBUG(FoDapCovJsonTransform_debug_key,
589 "covjsonStringArray(Parameter) - indx NOT equal to content length! indx: " << indx << " length: " << length << endl);
590 }
591 assert(length == indx);
592 }
593 else {
594 currParameter->values += "\"values\": []";
595 }
596 }
597}
598
599void FoDapCovJsonTransform::addAxis(string name, string values)
600{
601 struct Axis *newAxis = new Axis;
602
603 newAxis->name = name;
604 newAxis->values = values;
605#if 0
606cerr << "axis name is "<< name <<endl;
607cerr << "axis value is "<< values <<endl;
608#endif
609 this->axes.push_back(newAxis);
610 this->axisCount++;
611}
612
613void FoDapCovJsonTransform::addParameter(string id, string name, string type, string dataType, string unit,
614 string longName, string standardName, string shape, string values)
615{
616 struct Parameter *newParameter = new Parameter;
617
618 newParameter->id = id;
619 newParameter->name = name;
620 newParameter->type = type;
621 newParameter->dataType = dataType;
622 newParameter->unit = unit;
623 newParameter->longName = longName;
624 newParameter->standardName = standardName;
625 newParameter->shape = shape;
626 newParameter->values = values;
627
628 this->parameters.push_back(newParameter);
629 this->parameterCount++;
630}
631
632void FoDapCovJsonTransform::getAttributes(ostream *strm, libdap::AttrTable &attr_table, string name,
633 bool *axisRetrieved, bool *parameterRetrieved)
634{
635 string currAxisName;
636 string currAxisTimeOrigin;
637 string currUnit;
638 string currLongName;
639 string currStandardName;
640
641 isAxis = false;
642 isParam = false;
643
644 *axisRetrieved = false;
645 *parameterRetrieved = false;
646
647 // FOR TESTING AND DEBUGGING PURPOSES
648 //*strm << "\"attr_tableName\": \"" << name << "\"" << endl;
649
650
651 if (is_simple_cf_geographic || dsg_type!=UNSUPPORTED_DSG)
652 getAttributes_simple_cf_geographic_dsg(strm,attr_table,name,axisRetrieved,parameterRetrieved);
653 else {
654 // Using CF-1.6 naming conventions -- Also checks for Coads Climatology conventions
655 // http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html
656 if((name.compare("lon") == 0) || (name.compare("LON") == 0)
657 || (name.compare("longitude") == 0) || (name.compare("LONGITUDE") == 0)
658 || (name.compare("COADSX") == 0)) {
659
660 // FOR TESTING AND DEBUGGING PURPOSES
661 // *strm << "\"Found X-Axis\": \"" << name << "\"" << endl;
662
663 if(!xExists) {
664 xExists = true;
665 isAxis = true;
666 currAxisName = "x";
667 }
668 }
669 else if((name.compare("lat") == 0) || (name.compare("LAT") == 0)
670 || (name.compare("latitude") == 0) || (name.compare("LATITUDE") == 0)
671 || (name.compare("COADSY") == 0)) {
672
673 // FOR TESTING AND DEBUGGING PURPOSES
674 //*strm << "\"Found Y-Axis\": \"" << name << "\"" << endl;
675
676 if(!yExists) {
677 yExists = true;
678 isAxis = true;
679 currAxisName = "y";
680 }
681 }
682 else if((name.compare("lev") == 0) || (name.compare("LEV") == 0)
683 || (name.compare("height") == 0) || (name.compare("HEIGHT") == 0)
684 || (name.compare("depth") == 0) || (name.compare("DEPTH") == 0)
685 || (name.compare("pres") == 0) || (name.compare("PRES") == 0)) {
686
687 // FOR TESTING AND DEBUGGING PURPOSES
688 // *strm << "\"Found Z-Axis\": \"" << name << "\"" << endl;
689
690 if(!zExists) {
691 zExists = true;
692 isAxis = true;
693 currAxisName = "z";
694 }
695 }
696 else if((name.compare("time") == 0) || (name.compare("TIME") == 0)) {
697
698 // FOR TESTING AND DEBUGGING PURPOSES
699 // *strm << "\"Found T-Axis\": \"" << name << "\"" << endl;
700
701 if(!tExists) {
702 tExists = true;
703 isAxis = true;
704 currAxisName = "t";
705 }
706 }
707 else {
708 isParam = true;
709 }
710
711 // Only do more if there are actually attributes in the table
712 if(attr_table.get_size() != 0) {
713 libdap::AttrTable::Attr_iter begin = attr_table.attr_begin();
714 libdap::AttrTable::Attr_iter end = attr_table.attr_end();
715
716 for(libdap::AttrTable::Attr_iter at_iter = begin; at_iter != end; at_iter++) {
717 // FOR TESTING AND DEBUGGING PURPOSES
718 // attr_table.print(*strm);
719
720 switch (attr_table.get_attr_type(at_iter)) {
721 case libdap::Attr_container: {
722 libdap::AttrTable *atbl = attr_table.get_attr_table(at_iter);
723 // Recursive call for child attribute table
724 getAttributes(strm, *atbl, name, axisRetrieved, parameterRetrieved);
725 break;
726 }
727 default: {
728 vector<string> *values = attr_table.get_attr_vector(at_iter);
729
730 for(vector<string>::size_type i = 0; i < values->size(); i++) {
731 string currName = attr_table.get_name(at_iter);
732 string currValue = (*values)[i];
733
734 // FOR TESTING AND DEBUGGING PURPOSES
735 //*strm << "\"currName\": \"" << currName << "\", \"currValue\": \"" << currValue << "\"" << endl;
736
737 // From Climate and Forecast (CF) Conventions:
738 // http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#_description_of_the_data
739
740 // We continue to support the use of the units and long_name attributes as defined in COARDS.
741 // We extend COARDS by adding the optional standard_name attribute which is used to provide unique
742 // identifiers for variables. This is important for data exchange since one cannot necessarily
743 // identify a particular variable based on the name assigned to it by the institution that provided
744 // the data.
745
746 // The standard_name attribute can be used to identify variables that contain coordinate data. But since it is an
747 // optional attribute, applications that implement these standards must continue to be able to identify coordinate
748 // types based on the COARDS conventions.
749
750 // See http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
751 if(currName.compare("units") == 0) {
752 currUnit = currValue;
753 if(isAxis) {
754 if(currAxisName.compare("t") == 0) {
755 currAxisTimeOrigin = currValue;
756 }
757 }
758 }
759
760 // Per Jon Blower:
761 // observedProperty->label comes from:
762 // - The CF long_name, if it exists
763 // - If not, the CF standard_name, perhaps with underscores removed
764 // - If the standard_name doesn’t exist, use the variable ID
765 // See http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#long-name
766 else if(currName.compare("long_name") == 0) {
767 currLongName = currValue;
768 }
769 // See http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#standard-name
770 else if(currName.compare("standard_name") == 0) {
771 currStandardName = currValue;
772 }
773 }
774
775 break;
776 }
777 }
778 }
779 }
780
781 if(isAxis) {
782 // If we're dealing with the time axis, capture the time origin
783 // timestamp value with the appropriate formatting for printing.
784 // @TODO See https://covjson.org/spec/#temporal-reference-systems
785 if(currAxisName.compare("t") == 0) {
786 addAxis(currAxisName, "\"values\": [\"" + sanitizeTimeOriginString(currAxisTimeOrigin) + "\"]");
787 }
788 else {
789 addAxis(currAxisName, "");
790 }
791#if 0
792 //cerr<<"currAxisName is "<<currAxisName <<endl;
793 //cerr<<"currUnit is "<<currUnit <<endl;
794#endif
795
796 // See https://covjson.org/spec/#projected-coordinate-reference-systems
797 // KENT: The below "if block" is wrong. If the units of lat/lon includes east, north, it may be geographic projection.
798 // The ProjectedCRS may imply the 2-D lat/lon. If the variable name is the same as the axis name, and the lat/lon
799 // are 1-D, this is a geographic system.
800 if ((is_geo_dap2_grid == false) &&
801 ((currUnit.find("east") != string::npos) || (currUnit.find("East") != string::npos) ||
802 (currUnit.find("north") != string::npos) || (currUnit.find("North") != string::npos)))
803 coordRefType = "ProjectedCRS";
804
805
806 *axisRetrieved = true;
807 }
808 else if(isParam) {
809 addParameter("", name, "", currDataType, currUnit, currLongName, currStandardName, "", "");
810 *parameterRetrieved = true;
811 }
812 else {
813 // Do nothing
814 }
815 }
816}
817
818void FoDapCovJsonTransform::
819 getAttributes_simple_cf_geographic_dsg(ostream *strm, libdap::AttrTable &attr_table, const string& name,
820 bool *axisRetrieved, bool *parameterRetrieved)
821{
822 string currAxisName;
823 string currUnit;
824 string currLongName;
825 string currStandardName;
826
827 isAxis = false;
828 isParam = false;
829
830 *axisRetrieved = false;
831 *parameterRetrieved = false;
832
833 // FOR TESTING AND DEBUGGING PURPOSES
834 //*strm << "\"attr_tableName\": \"" << name << "\"" << endl;
835
836 if(axisVar_x.name == name) {
837 // FOR TESTING AND DEBUGGING PURPOSES
838 //*strm << "\"Found X-Axis\": \"" << name << "\"" << endl;
839
840 if(!xExists) {
841 xExists = true;
842 isAxis = true;
843 currAxisName = "x";
844 }
845 }
846 else if(axisVar_y.name == name) {
847 // FOR TESTING AND DEBUGGING PURPOSES
848 //*strm << "\"Found Y-Axis\": \"" << name << "\"" << endl;
849
850 if(!yExists) {
851 yExists = true;
852 isAxis = true;
853 currAxisName = "y";
854 }
855 }
856 else if(axisVar_z.name == name) {
857 // FOR TESTING AND DEBUGGING PURPOSES
858 //*strm << "\"Found Z-Axis\": \"" << name << "\"" << endl;
859
860 if(!zExists) {
861 zExists = true;
862 isAxis = true;
863 currAxisName = "z";
864 }
865 }
866 else if(axisVar_t.name == name) {
867 // FOR TESTING AND DEBUGGING PURPOSES
868 //*strm << "\"Found T-Axis\": \"" << name << "\"" << endl;
869
870 if(!tExists) {
871 tExists = true;
872 isAxis = true;
873 currAxisName = "t";
874 }
875 }
876 else {
877 // TODO: We should manage to remove this loop when improving the whole module.
878 for (unsigned int i = 0; i <par_vars.size(); i++) {
879 if(par_vars[i] == name){
880 isParam = true;
881 break;
882 }
883 }
884 }
885
886 // Only do more if there are actually attributes in the table
887 if(attr_table.get_size() != 0) {
888 libdap::AttrTable::Attr_iter begin = attr_table.attr_begin();
889 libdap::AttrTable::Attr_iter end = attr_table.attr_end();
890
891 for(libdap::AttrTable::Attr_iter at_iter = begin; at_iter != end; at_iter++) {
892 // FOR TESTING AND DEBUGGING PURPOSES
893 // attr_table.print(*strm);
894
895 switch (attr_table.get_attr_type(at_iter)) {
896 case libdap::Attr_container: {
897 libdap::AttrTable *atbl = attr_table.get_attr_table(at_iter);
898 // Recursive call for child attribute table
899 getAttributes_simple_cf_geographic_dsg(strm, *atbl, name, axisRetrieved, parameterRetrieved);
900 break;
901 }
902 default: {
903 vector<string> *values = attr_table.get_attr_vector(at_iter);
904
905 for(vector<string>::size_type i = 0; i < values->size(); i++) {
906 string currName = attr_table.get_name(at_iter);
907 string currValue = (*values)[i];
908
909 // FOR TESTING AND DEBUGGING PURPOSES
910 //*strm << "\"currName\": \"" << currName << "\", \"currValue\": \"" << currValue << "\"" << endl;
911
912 // From Climate and Forecast (CF) Conventions:
913 // http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#_description_of_the_data
914
915 // We continue to support the use of the units and long_name attributes as defined in COARDS.
916 // We extend COARDS by adding the optional standard_name attribute which is used to provide unique
917 // identifiers for variables. This is important for data exchange since one cannot necessarily
918 // identify a particular variable based on the name assigned to it by the institution that provided
919 // the data.
920
921 // The standard_name attribute can be used to identify variables that contain coordinate data. But since it is an
922 // optional attribute, applications that implement these standards must continue to be able to identify coordinate
923 // types based on the COARDS conventions.
924
925 // See http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
926 if(currName.compare("units") == 0)
927 currUnit = currValue;
928
929 // Per Jon Blower:
930 // observedProperty->label comes from:
931 // - The CF long_name, if it exists
932 // - If not, the CF standard_name, perhaps with underscores removed
933 // - If the standard_name doesn’t exist, use the variable ID
934 // See http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#long-name
935 else if(currName.compare("long_name") == 0) {
936 currLongName = currValue;
937 }
938 // See http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#standard-name
939 else if(currName.compare("standard_name") == 0) {
940 currStandardName = currValue;
941 }
942
943 }
944
945 if (axisVar_z.name == name) {// We need to check if the attribute "positive" exists.
946
947 for(vector<string>::size_type i = 0; i < values->size(); i++) {
948 string currName = attr_table.get_name(at_iter);
949 if (currName == "positive") {
950 string currValue = (*values)[i];
951 axis_z_direction = currValue;
952 break;
953 }
954 }
955
956 }
957
958 break;
959 }
960 }
961 }
962 }
963
964 if(isAxis) {
965 addAxis(currAxisName, "");
966#if 0
967//cerr<<"currAxisName is "<<currAxisName <<endl;
968//cerr<<"currUnit is "<<currUnit <<endl;
969#endif
970 if (currAxisName.compare("t") == 0)
971 axis_t_units = currUnit;
972 if (currAxisName.compare("z") == 0) {
973 axis_z_standardName=currStandardName;
974 axis_z_units = currUnit;
975 }
976 *axisRetrieved = true;
977 }
978 else if(isParam) {
979 addParameter("", name, "", currDataType, currUnit, currLongName, currStandardName, "", "");
980 *parameterRetrieved = true;
981 }
982 else {
983 // Do nothing
984 }
985
986}
987
988
989string FoDapCovJsonTransform::sanitizeTimeOriginString(string timeOrigin)
990{
991 // If the calendar is based on years, months, days,
992 // then the referenced values SHOULD use one of the
993 // following ISO8601-based lexical representations:
994
995 // YYYY
996 // ±XYYYY (where X stands for extra year digits)
997 // YYYY-MM
998 // YYYY-MM-DD
999 // YYYY-MM-DDTHH:MM:SS[.F]Z where Z is either “Z”
1000 // or a time scale offset + -HH:MM
1001 // ex: "2018-01-01T00:12:20Z"
1002
1003 // If calendar dates with reduced precision are
1004 // used in a lexical representation (e.g. "2016"),
1005 // then a client SHOULD interpret those dates in
1006 // that reduced precision.
1007
1008 // Remove any commonly found words from the origin timestamp
1009 vector<string> subStrs = { "hours", "hour", "minutes", "minute",
1010 "seconds", "second", "since", " " };
1011
1012 string cleanTimeOrigin = timeOrigin;
1013
1014 // If base time, use an arbitrary base time string
1015 if(timeOrigin.find("base_time") != string::npos) {
1016 cleanTimeOrigin = "2020-01-01T12:00:00Z";
1017 }
1018 else {
1019 for(unsigned int i = 0; i < subStrs.size(); i++)
1020 focovjson::removeSubstring(cleanTimeOrigin, subStrs[i]);
1021 }
1022
1023 return cleanTimeOrigin;
1024}
1025
1027{
1028 if (!_dds) throw BESInternalError("File out COVJSON, null DDS passed to constructor", __FILE__, __LINE__);
1029}
1030
1031FoDapCovJsonTransform::FoDapCovJsonTransform(libdap::DMR *dmr) : _dmr(dmr)
1032{
1033 if (!_dmr) throw BESInternalError("File out COVJSON, null DMR passed to constructor", __FILE__, __LINE__);
1034}
1035
1036
1038{
1039 strm << BESIndent::LMarg << "FoDapCovJsonTransform::dump - (" << (void *) this << ")" << endl;
1040 BESIndent::Indent();
1041 if(_dds != 0) {
1042 _dds->print(strm);
1043 }
1044 BESIndent::UnIndent();
1045}
1046
1047void FoDapCovJsonTransform::transform(ostream &ostrm, bool sendData, bool testOverride)
1048{
1049 transform(&ostrm, _dds, "", sendData, testOverride);
1050}
1051
1052void FoDapCovJsonTransform::transform_dap4(ostream &ostrm, bool sendData, bool testOverride)
1053{
1054 transform(&ostrm, _dmr, "", sendData, testOverride);
1055}
1056
1057void FoDapCovJsonTransform::transform(ostream *strm, libdap::Constructor *cnstrctr, string indent, bool sendData)
1058{
1059 vector<libdap::BaseType *> leaves;
1060 vector<libdap::BaseType *> nodes;
1061 // Sort the variables into two sets
1062 libdap::DDS::Vars_iter vi = cnstrctr->var_begin();
1063 libdap::DDS::Vars_iter ve = cnstrctr->var_end();
1064
1065//cerr<<"coming to the loop before " <<endl;
1066
1067 for(; vi != ve; vi++) {
1068 if((*vi)->send_p()) {
1069 libdap::BaseType *v = *vi;
1070 libdap::Type type = v->type();
1071
1072 if(type == libdap::dods_array_c) {
1073 type = v->var()->type();
1074 }
1075 if(v->is_constructor_type() || (v->is_vector_type() && v->var()->is_constructor_type())) {
1076 nodes.push_back(v);
1077 }
1078 else {
1079 leaves.push_back(v);
1080 }
1081 }
1082 }
1083
1084 transformNodeWorker(strm, leaves, nodes, indent, sendData);
1085}
1086
1087void FoDapCovJsonTransform::transformNodeWorker(ostream *strm, vector<libdap::BaseType *> leaves,
1088 vector<libdap::BaseType *> nodes, string indent, bool sendData)
1089{
1090 // Get this node's leaves
1091 for(vector<libdap::BaseType *>::size_type l = 0; l < leaves.size(); l++) {
1092 libdap::BaseType *v = leaves[l];
1093 BESDEBUG(FoDapCovJsonTransform_debug_key, "Processing LEAF: " << v->name() << endl);
1094
1095 // FOR TESTING AND DEBUGGING PURPOSES
1096 //*strm << "Processing LEAF: " << v->name() << endl;
1097
1098 transform(strm, v, indent + _indent_increment, sendData);
1099 }
1100
1101 // Get this node's child nodes
1102 for(vector<libdap::BaseType *>::size_type n = 0; n < nodes.size(); n++) {
1103 libdap::BaseType *v = nodes[n];
1104 BESDEBUG(FoDapCovJsonTransform_debug_key, "Processing NODE: " << v->name() << endl);
1105
1106 // FOR TESTING AND DEBUGGING PURPOSES
1107 //*strm << "Processing NODE: " << v->name() << endl;
1108
1109 transform(strm, v, indent + _indent_increment, sendData);
1110 }
1111}
1112
1113void FoDapCovJsonTransform::printCoverageJSON(ostream *strm, string indent, bool testOverride)
1114{
1115 // Determine if the attribute values we read can be converted to CovJSON.
1116 // Test override forces printing output to stream regardless of whether
1117 // or not the file can be converted into CoverageJSON format.
1118 if(testOverride) {
1119 canConvertToCovJson = true;
1120 }
1121 else {
1122 canConvertToCovJson = canConvert();
1123 }
1124
1125 // Only print if this file can be converted to CovJSON
1126 if(canConvertToCovJson) {
1127 // Prints the entire Coverage to stream
1128//cerr<< "Can convert to CovJSON "<<endl;
1129 printCoverage(strm, indent);
1130 }
1131 else {
1132 // If this file can't be converted, then its failing spatial/temporal requirements
1133 throw BESInternalError("File cannot be converted to CovJSON format due to missing or incompatible spatial dimensions", __FILE__, __LINE__);
1134 }
1135}
1136
1137void FoDapCovJsonTransform::printCoverage(ostream *strm, string indent)
1138{
1139 string child_indent1 = indent + _indent_increment;
1140 string child_indent2 = child_indent1 + _indent_increment;
1141
1142 BESDEBUG(FoDapCovJsonTransform_debug_key, "Printing COVERAGE" << endl);
1143
1144 *strm << indent << "{" << endl;
1145 *strm << child_indent1 << "\"type\": \"Coverage\"," << endl;
1146
1147 printDomain(strm, child_indent1);
1148
1149 printParameters(strm, child_indent1);
1150
1151 printRanges(strm, child_indent1);
1152
1153 *strm << indent << "}" << endl;
1154}
1155
1156void FoDapCovJsonTransform::printDomain(ostream *strm, string indent)
1157{
1158 string child_indent1 = indent + _indent_increment;
1159
1160 BESDEBUG(FoDapCovJsonTransform_debug_key, "Printing DOMAIN" << endl);
1161
1162 *strm << indent << "\"domain\": {" << endl;
1163 *strm << child_indent1 << "\"type\" : \"Domain\"," << endl;
1164 *strm << child_indent1 << "\"domainType\": \"" + domainType + "\"," << endl;
1165
1166 // Prints the axes metadata and range values
1167 printAxes(strm, child_indent1);
1168
1169 // Prints the references for the given Axes
1170 printReference(strm, child_indent1);
1171
1172 *strm << indent << "}," << endl;
1173}
1174
1175void FoDapCovJsonTransform::printAxes(ostream *strm, string indent)
1176{
1177 string child_indent1 = indent + _indent_increment;
1178 string child_indent2 = child_indent1 + _indent_increment;
1179
1180 BESDEBUG(FoDapCovJsonTransform_debug_key, "Printing AXES" << endl);
1181
1182 // Obtain bound values if having them
1183 // First handle the t-axis because of GLADS
1184 std::vector<std::string> t_bnd_val;
1185 if(axisVar_t_bnd_val.size() >0) {
1186 t_bnd_val.resize(axisVar_t_bnd_val.size());
1187 for (unsigned i = 0; i < axisVar_t_bnd_val.size();i++) {
1188 t_bnd_val[i] = cf_time_to_greg((long long)(axisVar_t_bnd_val[i]));
1189#if 0
1190//cerr<<"time bound value is "<<t_bnd_val[i] <<endl;
1191#endif
1192 }
1193 }
1194
1195 // bound for x-axis
1196 std::vector<std::string> x_bnd_val;
1197 if(axisVar_x_bnd_val.empty() == false) {
1198 x_bnd_val.resize(axisVar_x_bnd_val.size());
1199 for (unsigned i = 0; i < axisVar_x_bnd_val.size();i++) {
1200 ostringstream temp_strm;
1201 temp_strm<<axisVar_x_bnd_val[i];
1202 x_bnd_val[i] = temp_strm.str();
1203#if 0
1204//cerr<<"X bound value is "<<x_bnd_val[i] <<endl;
1205#endif
1206 }
1207 }
1208
1209 // bound for y-axis
1210 std::vector<std::string> y_bnd_val;
1211 if(axisVar_y_bnd_val.empty() == false) {
1212 y_bnd_val.resize(axisVar_y_bnd_val.size());
1213 for (unsigned i = 0; i < axisVar_y_bnd_val.size();i++) {
1214 ostringstream temp_strm;
1215 temp_strm<<axisVar_y_bnd_val[i];
1216 y_bnd_val[i] = temp_strm.str();
1217#if 0
1218//cerr<<"Y bound value is "<<y_bnd_val[i] <<endl;
1219#endif
1220 }
1221 }
1222
1223 // bound for z-axis
1224 std::vector<std::string> z_bnd_val;
1225 if(axisVar_z_bnd_val.empty() == false) {
1226 z_bnd_val.resize(axisVar_z_bnd_val.size());
1227 for (unsigned i = 0; i < axisVar_z_bnd_val.size();i++) {
1228 ostringstream temp_strm;
1229 temp_strm<<axisVar_z_bnd_val[i];
1230 z_bnd_val[i] = temp_strm.str();
1231#if 0
1232//cerr<<"Z bound value is "<<z_bnd_val[i] <<endl;
1233#endif
1234 }
1235 }
1236
1237 // FOR TESTING AND DEBUGGING PURPOSES
1238 // *strm << "\"type_name\": \"" << a->var()->type_name() << "\"" << endl;
1239
1240 // Write the axes to strm
1241 *strm << indent << "\"axes\": {" << endl;
1242 for(unsigned int i = 0; i < axisCount; i++) {
1243 for(unsigned int j = 0; j < axisCount; j++) {
1244 // Logic for printing axes in the appropriate order
1245
1246 // If x, y, z, and t all exist (x, y, z, t)
1247 if(xExists && yExists && zExists && tExists) {
1248 if((i == 0) && (axes[j]->name.compare("x") == 0)) {
1249 *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
1250 *strm << child_indent2 << axes[j]->values << endl;
1251 print_bound(strm, x_bnd_val,child_indent2,false);
1252 }
1253 else if((i == 1) && (axes[j]->name.compare("y") == 0)) {
1254 *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
1255 *strm << child_indent2 << axes[j]->values << endl;
1256 print_bound(strm, y_bnd_val,child_indent2,false);
1257 }
1258 else if((i == 2) && (axes[j]->name.compare("z") == 0)) {
1259 *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
1260 *strm << child_indent2 << axes[j]->values << endl;
1261 print_bound(strm, z_bnd_val,child_indent2,false);
1262 }
1263 else if((i == 3) && (axes[j]->name.compare("t") == 0)) {
1264 *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
1265 *strm << child_indent2 << axes[j]->values << endl;
1266 print_bound(strm, t_bnd_val,child_indent2,true);
1267 }
1268 }
1269 // If just x, y, and t exist (x, y, t)
1270 else if(xExists && yExists && !zExists && tExists) {
1271 if((i == 0) && (axes[j]->name.compare("x") == 0)) {
1272 *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
1273 *strm << child_indent2 << axes[j]->values << endl;
1274 print_bound(strm, x_bnd_val,child_indent2,false);
1275 }
1276 else if((i == 1) && (axes[j]->name.compare("y") == 0)) {
1277 *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
1278 *strm << child_indent2 << axes[j]->values << endl;
1279 print_bound(strm, y_bnd_val,child_indent2,false);
1280 }
1281 else if((i == 2) && (axes[j]->name.compare("t") == 0)) {
1282 *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
1283 *strm << child_indent2 << axes[j]->values << endl;
1284 print_bound(strm, t_bnd_val,child_indent2,true);
1285 }
1286 }
1287 // If just x and y exist (x, y)
1288 else if(xExists && yExists && !zExists && !tExists) {
1289 if((i == 0) && (axes[j]->name.compare("x") == 0)) {
1290 *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
1291 *strm << child_indent2 << axes[j]->values << endl;
1292 print_bound(strm, x_bnd_val,child_indent2,false);
1293 }
1294 else if((i == 1) && (axes[j]->name.compare("y") == 0)) {
1295 *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
1296 *strm << child_indent2 << axes[j]->values << endl;
1297 print_bound(strm, y_bnd_val,child_indent2,false);
1298 }
1299 }
1300 }
1301 if(i == axisCount - 1) {
1302 *strm << child_indent1 << "}" << endl;
1303 }
1304 else {
1305 *strm << child_indent1 << "}," << endl;
1306 }
1307 }
1308 *strm << indent << "}," << endl;
1309}
1310
1311void FoDapCovJsonTransform::printReference(ostream *strm, string indent)
1312{
1313 string child_indent1 = indent + _indent_increment;
1314 string child_indent2 = child_indent1 + _indent_increment;
1315 string child_indent3 = child_indent2 + _indent_increment;
1316 string child_indent4 = child_indent3 + _indent_increment;
1317 string child_indent5 = child_indent4 + _indent_increment;
1318 string coordVars;
1319
1320 BESDEBUG(FoDapCovJsonTransform_debug_key, "Printing REFERENCES" << endl);
1321
1322 if(xExists) {
1323 coordVars += "\"x\"";
1324 }
1325
1326 if(yExists) {
1327 if(!coordVars.empty()) {
1328 coordVars += ", ";
1329 }
1330 coordVars += "\"y\"";
1331 }
1332
1333 if(zExists && !is_simple_cf_geographic && (dsg_type == UNSUPPORTED_DSG)) {
1334 if(!coordVars.empty()) {
1335 coordVars += ", ";
1336 }
1337 coordVars += "\"z\"";
1338 }
1339
1340 *strm << indent << "\"referencing\": [{" << endl;
1341
1342 // See https://covjson.org/spec/#temporal-reference-systems
1343 if(tExists) {
1344 *strm << child_indent1 << "\"coordinates\": [\"t\"]," << endl;
1345 *strm << child_indent1 << "\"system\": {" << endl;
1346 *strm << child_indent2 << "\"type\": \"TemporalRS\"," << endl;
1347 *strm << child_indent2 << "\"calendar\": \"Gregorian\"" << endl;
1348 *strm << child_indent1 << "}" << endl;
1349 *strm << indent << "}," << endl;
1350 *strm << indent << "{" << endl;
1351 }
1352
1353 // See https://covjson.org/spec/#geospatial-coordinate-reference-systems
1354 *strm << child_indent1 << "\"coordinates\": [" << coordVars << "]," << endl;
1355 *strm << child_indent1 << "\"system\": {" << endl;
1356 *strm << child_indent2 << "\"type\": \"" + coordRefType + "\"," << endl;
1357
1358 // Most of the datasets I've seen do not contain a link to a coordinate
1359 // reference system, so I've set some defaults here - CRH 1/2020
1360 if(coordRefType.compare("ProjectedCRS") == 0) {
1361 // Projected Coordinate Reference System (north/east): http://www.opengis.net/def/crs/EPSG/0/27700
1362 *strm << child_indent2 << "\"id\": \"http://www.opengis.net/def/crs/EPSG/0/27700\"" << endl;
1363 }
1364 else {
1365 if(xExists && yExists && zExists) {
1366 // 3-Dimensional Geographic Coordinate Reference System (lat/lon/height): http://www.opengis.net/def/crs/EPSG/0/4979
1367 if(!is_simple_cf_geographic &&(dsg_type == UNSUPPORTED_DSG))
1368 *strm << child_indent2 << "\"id\": \"http://www.opengis.net/def/crs/EPSG/0/4979\"" << endl;
1369 }
1370 else {
1371 // 2-Dimensional Geographic Coordinate Reference System (lat/lon): http://www.opengis.net/def/crs/OGC/1.3/CRS84
1372 if(!is_simple_cf_geographic && (dsg_type==UNSUPPORTED_DSG) && (!is_geo_dap2_grid))
1373 *strm << child_indent2 << "\"id\": \"http://www.opengis.net/def/crs/OGC/1.3/CRS84\"" << endl;
1374 }
1375 }
1376
1377 *strm << child_indent1 << "}" << endl;
1378
1379 if (zExists && (is_simple_cf_geographic || dsg_type != UNSUPPORTED_DSG)) {
1380 *strm << indent << "}," << endl;
1381 *strm << indent << "{" << endl;
1382 *strm << child_indent1 << "\"coordinates\": [" << "\"z\"]," << endl;
1383 *strm << child_indent1 << "\"system\": {" << endl;
1384 *strm << child_indent2 << "\"type\": \"" <<"VerticalCRS" << "\"," << endl;
1385 *strm << child_indent2 << "\"cs\": {" << endl;
1386 *strm << child_indent3 << "\"csAxes\": [{" << endl;
1387 *strm << child_indent4 << "\"name\": {" << endl;
1388 if (axis_z_standardName =="")
1389 *strm << child_indent5 << "\"en\": \"" << axisVar_z.name << "\"" << endl;
1390 else
1391 *strm << child_indent5 << "\"en\": \"" << axis_z_standardName << "\"" << endl;
1392 *strm << child_indent4 << "}," << endl;
1393 if (axis_z_direction !="")
1394 *strm << child_indent4 << "\"direction\": \"" << axis_z_direction << "\","<<endl;
1395 if (axis_z_units !="") {
1396 *strm << child_indent4 << "\"units\": {" << endl;
1397 *strm << child_indent5 << "\"symbol\": \"" << axis_z_units << "\"" << endl;
1398 *strm << child_indent4 << "}" << endl;
1399 }
1400 *strm << child_indent3 <<"}]"<<endl;
1401 *strm << child_indent2 <<"}"<<endl;
1402 *strm << child_indent1 <<"}"<<endl;
1403 *strm << indent <<"}]"<<endl;
1404 }
1405 else
1406 *strm << indent << "}]" << endl;
1407
1408}
1409
1410void FoDapCovJsonTransform::printParameters(ostream *strm, string indent)
1411{
1412 string child_indent1 = indent + _indent_increment;
1413 string child_indent2 = child_indent1 + _indent_increment;
1414 string child_indent3 = child_indent2 + _indent_increment;
1415 string child_indent4 = child_indent3 + _indent_increment;
1416
1417 BESDEBUG(FoDapCovJsonTransform_debug_key, "Printing PARAMETERS" << endl);
1418
1419 // Write down the parameter metadata
1420 *strm << indent << "\"parameters\": {" << endl;
1421 for(unsigned int i = 0; i < parameterCount; i++) {
1422 *strm << child_indent1 << "\"" << parameters[i]->name << "\": {" << endl;
1423 *strm << child_indent2 << "\"type\": \"Parameter\"," << endl;
1424 *strm << child_indent2 << "\"description\": {" << endl;
1425
1426 if(parameters[i]->longName.compare("") != 0) {
1427 *strm << child_indent3 << "\"en\": \"" << parameters[i]->longName << "\"" << endl;
1428 }
1429 else if(parameters[i]->standardName.compare("") != 0) {
1430 *strm << child_indent3 << "\"en\": \"" << parameters[i]->standardName << "\"" << endl;
1431 }
1432 else {
1433 *strm << child_indent3 << "\"en\": \"" << parameters[i]->name << "\"" << endl;
1434 }
1435
1436 *strm << child_indent2 << "}," << endl;
1437 *strm << child_indent2 << "\"unit\": {" << endl;
1438 *strm << child_indent3 << "\"label\": {" << endl;
1439 *strm << child_indent4 << "\"en\": \"" << parameters[i]->unit << "\"" << endl;
1440 *strm << child_indent3 << "}," << endl;
1441 *strm << child_indent3 << "\"symbol\": {" << endl;
1442 *strm << child_indent4 << "\"value\": \"" << parameters[i]->unit << "\"," << endl;
1443 *strm << child_indent4 << "\"type\": \"http://www.opengis.net/def/uom/UCUM/\"" << endl;
1444 *strm << child_indent3 << "}" << endl;
1445 *strm << child_indent2 << "}," << endl;
1446 *strm << child_indent2 << "\"observedProperty\": {" << endl;
1447
1448 // Per Jon Blower:
1449 // observedProperty->id comes from the CF standard_name,
1450 // mapped to a URI like this: http://vocab.nerc.ac.uk/standard_name/<standard_name>.
1451 // If the standard_name is not present, omit the id.
1452 if(parameters[i]->standardName.compare("") != 0) {
1453 *strm << child_indent3 << "\"id\": \"http://vocab.nerc.ac.uk/standard_name/" << parameters[i]->standardName << "/\"," << endl;
1454 }
1455
1456 // Per Jon Blower:
1457 // observedProperty->label comes from:
1458 // - The CF long_name, if it exists
1459 // - If not, the CF standard_name, perhaps with underscores removed
1460 // - If the standard_name doesn’t exist, use the variable ID
1461 *strm << child_indent3 << "\"label\": {" << endl;
1462
1463 if(parameters[i]->longName.compare("") != 0) {
1464 *strm << child_indent4 << "\"en\": \"" << parameters[i]->longName << "\"" << endl;
1465 }
1466 else if(parameters[i]->standardName.compare("") != 0) {
1467 *strm << child_indent4 << "\"en\": \"" << parameters[i]->standardName << "\"" << endl;
1468 }
1469 else {
1470 *strm << child_indent4 << "\"en\": \"" << parameters[i]->name << "\"" << endl;
1471 }
1472
1473 *strm << child_indent3 << "}" << endl;
1474 *strm << child_indent2 << "}" << endl;
1475
1476 if(i == parameterCount - 1) {
1477 *strm << child_indent1 << "}" << endl;
1478 }
1479 else {
1480 *strm << child_indent1 << "}," << endl;
1481 }
1482 }
1483
1484 *strm << indent << "}," << endl;
1485}
1486
1487void FoDapCovJsonTransform::printRanges(ostream *strm, string indent)
1488{
1489 string child_indent1 = indent + _indent_increment;
1490 string child_indent2 = child_indent1 + _indent_increment;
1491 string child_indent3 = child_indent2 + _indent_increment;
1492 string axisNames;
1493
1494 BESDEBUG(FoDapCovJsonTransform_debug_key, "Printing RANGES" << endl);
1495
1496 if(tExists) {
1497 axisNames += "\"t\"";
1498 }
1499
1500 if(zExists) {
1501 if(!axisNames.empty()) {
1502 axisNames += ", ";
1503 }
1504 axisNames += "\"z\"";
1505 }
1506
1507 if(yExists) {
1508 if(!axisNames.empty()) {
1509 axisNames += ", ";
1510 }
1511 axisNames += "\"y\"";
1512 }
1513
1514 if(xExists) {
1515 if(!axisNames.empty()) {
1516 axisNames += ", ";
1517 }
1518 axisNames += "\"x\"";
1519 }
1520
1521 // Axis name (x, y, or z)
1522 *strm << indent << "\"ranges\": {" << endl;
1523 for(unsigned int i = 0; i < parameterCount; i++) {
1524 string dataType;
1525 // See spec: https://covjson.org/spec/#ndarray-objects
1526 if(parameters[i]->dataType.find("int") == 0 || parameters[i]->dataType.find("Int") == 0
1527 || parameters[i]->dataType.find("integer") == 0 || parameters[i]->dataType.find("Integer") == 0) {
1528 dataType = "integer";
1529 }
1530 else if(parameters[i]->dataType.find("float") == 0 || parameters[i]->dataType.find("Float") == 0) {
1531 dataType = "float";
1532 }
1533 else if(parameters[i]->dataType.find("string") == 0 || parameters[i]->dataType.find("String") == 0) {
1534 dataType = "string";
1535 }
1536 else {
1537 dataType = "string";
1538 }
1539
1540 // @TODO NEEDS REFACTORING FOR BES ISSUE #244
1541 // https://github.com/OPENDAP/bes/issues/244
1542 *strm << child_indent1 << "\"" << parameters[i]->name << "\": {" << endl;
1543 *strm << child_indent2 << "\"type\": \"NdArray\"," << endl;
1544 *strm << child_indent2 << "\"dataType\": \"" << dataType << "\", " << endl;
1545
1546 // For discrete examples axisNames and shape may not need to be printed.
1547 // For point, no axis and shape,
1548 // For pointseries, axisNames: t
1549 // For vertical Profile, axisNames: z
1550 // For parameters that don't have shape, don't print shape.
1551 if (dsg_type !=SPOINT) {
1552 if (dsg_type == POINTS)
1553 axisNames = "\"t\"";
1554 else if (dsg_type == PROFILE)
1555 axisNames = "\"z\"";
1556 *strm << child_indent2 << "\"axisNames\": [" << axisNames << "]," << endl;
1557 if (parameters[i]->shape!="")
1558 *strm << child_indent2 << parameters[i]->shape << endl;
1559 }
1560 *strm << child_indent2 << parameters[i]->values << endl;
1561
1562 if(i == parameterCount - 1) {
1563 *strm << child_indent1 << "}" << endl;
1564 }
1565 else {
1566 *strm << child_indent1 << "}," << endl;
1567 }
1568 }
1569
1570 *strm << indent << "}" << endl;
1571}
1572
1573void FoDapCovJsonTransform::transform(ostream *strm, libdap::DDS *dds, string indent, bool sendData, bool testOverride)
1574{
1575
1576 // We need to support DAP2 grid. If a DDS contains DAP2 grids, since only DAP2 grids can map to the coverage Grid object and
1577 // other objects are most likely not objects that can be mapped to the coverage,
1578 // the other objects need to be ignored; otherwise, wrong information will be generated.
1579
1580 vector<string> dap2_grid_map_names;
1581 for (const auto &var:dds->variables()) {
1582 if(var->send_p()) {
1583 libdap::Type type = var->type();
1584 if (type == libdap::dods_grid_c) {
1585 is_dap2_grid = true;
1586 auto vgrid = dynamic_cast<libdap::Grid*>(var);
1587 for (libdap::Grid::Map_iter i = vgrid->map_begin(); i != vgrid->map_end(); ++i) {
1588 dap2_grid_map_names.emplace_back((*i)->name());
1589#if 0
1590cout <<"grid map name: "<<(*i)->name() <<endl;
1591#endif
1592 }
1593 break;
1594 }
1595 }
1596 }
1597
1598 if (is_dap2_grid)
1599 is_geo_dap2_grid = check_geo_dap2_grid(dds,dap2_grid_map_names);
1600
1601 // Sort the variables into two sets
1602 vector<libdap::BaseType *> leaves;
1603 vector<libdap::BaseType *> nodes;
1604
1605
1606 libdap::DDS::Vars_iter vi = dds->var_begin();
1607 libdap::DDS::Vars_iter ve = dds->var_end();
1608
1609
1610 // If we find this file contains DAP2 grids, ignore other variables.
1611 if (is_dap2_grid == true) {
1612
1613 for(; vi != ve; vi++) {
1614 if((*vi)->send_p()) {
1615 libdap::BaseType *v = *vi;
1616 libdap::Type type = v->type();
1617 if (type == libdap::dods_grid_c) {
1618 if(v->is_constructor_type() || (v->is_vector_type() && v->var()->is_constructor_type())) {
1619 nodes.push_back(v);
1620 }
1621 else {
1622 leaves.push_back(v);
1623 }
1624 }
1625 }
1626 }
1627 }
1628 else {
1629 for(; vi != ve; vi++) {
1630 if((*vi)->send_p()) {
1631 libdap::BaseType *v = *vi;
1632 libdap::Type type = v->type();
1633 if(type == libdap::dods_array_c) {
1634 type = v->var()->type();
1635 }
1636 if(v->is_constructor_type() || (v->is_vector_type() && v->var()->is_constructor_type())) {
1637 nodes.push_back(v);
1638 }
1639 else {
1640 leaves.push_back(v);
1641 }
1642 }
1643 }
1644
1645 // Check if CF discrete Sample Geometries
1646 bool is_simple_discrete = check_update_simple_dsg(dds);
1647
1648 // Check simple grid
1649 if (is_simple_discrete == false && FoCovJsonRequestHandler::get_simple_geo())
1650 check_update_simple_geo(dds, sendData);
1651
1652 }
1653 // Read through the source DDS leaves and nodes, extract all axes and
1654 // parameter data, and store that data as Axis and Parameters
1655 transformNodeWorker(strm, leaves, nodes, indent + _indent_increment + _indent_increment, sendData);
1656
1657 // Verify the request hasn't exceeded bes_timeout, and disable timeout if allowed
1658 RequestServiceTimer::TheTimer()->throw_if_timeout_expired(prolog + "ERROR: bes-timeout expired before transmit", __FILE__, __LINE__);
1660
1661 // Print the Coverage data to stream as CoverageJSON
1662 printCoverageJSON(strm, indent, testOverride);
1663}
1664
1665void FoDapCovJsonTransform::transform(ostream *strm, libdap::DMR *dmr, const string& indent, bool sendData, bool testOverride)
1666{
1667
1668 // Now we only consider the variables under the groups.
1669 libdap::D4Group *root_grp = dmr->root();
1670
1671 // Sort the variables into two sets
1672 vector<libdap::BaseType *> leaves;
1673 vector<libdap::BaseType *> nodes;
1674
1675 for (auto i = root_grp->var_begin(), e = root_grp->var_end(); i != e; ++i) {
1676
1677 if ((*i)->send_p()) {
1678 libdap::BaseType *v = *i;
1679 if(v->is_constructor_type() || (v->is_vector_type() && v->var()->is_constructor_type()))
1680 nodes.push_back(v);
1681 else
1682 leaves.push_back(v);
1683 }
1684 }
1685
1686#if 0
1687 // Check if CF discrete Sample Geometries
1688 bool is_simple_discrete = check_update_simple_dsg(dds);
1689
1690 // Check simple grid
1691 if (is_simple_discrete == false && FoCovJsonRequestHandler::get_simple_geo())
1692 check_update_simple_geo(dds, sendData);
1693
1694 }
1695#endif
1696
1697 // We currently only consider simple grids.
1698 if (FoCovJsonRequestHandler::get_simple_geo())
1699 check_update_simple_geo_dap4(root_grp);
1700
1701
1702 // Read through the source DDS leaves and nodes, extract all axes and
1703 // parameter data, and store that data as Axis and Parameters
1704 transformNodeWorker(strm, leaves, nodes, indent + _indent_increment + _indent_increment, sendData);
1705
1706 // Verify the request hasn't exceeded bes_timeout, and disable timeout if allowed
1707 RequestServiceTimer::TheTimer()->throw_if_timeout_expired(prolog + "ERROR: bes-timeout expired before transmit", __FILE__, __LINE__);
1709
1710 // Print the Coverage data to stream as CoverageJSON
1711 printCoverageJSON(strm, indent, testOverride);
1712}
1713
1714
1715void FoDapCovJsonTransform::transform(ostream *strm, libdap::BaseType *bt, string indent, bool sendData)
1716{
1717 switch(bt->type()) {
1718 // Handle the atomic types - that's easy!
1719 case libdap::dods_byte_c:
1720 case libdap::dods_int16_c:
1721 case libdap::dods_uint16_c:
1722 case libdap::dods_int32_c:
1723 case libdap::dods_uint32_c:
1724 case libdap::dods_float32_c:
1725 case libdap::dods_float64_c:
1726 case libdap::dods_str_c:
1727 case libdap::dods_url_c:
1728 case libdap::dods_int8_c:
1729 case libdap::dods_uint8_c:
1730 case libdap::dods_int64_c:
1731 case libdap::dods_uint64_c:
1732
1733 transformAtomic(strm, bt, indent, sendData);
1734 break;
1735
1736 case libdap::dods_structure_c:
1737 transform(strm, (libdap::Structure *) bt, indent, sendData);
1738 break;
1739
1740 case libdap::dods_grid_c:
1741 is_dap2_grid = true;
1742 transform(strm, (libdap::Grid *) bt, indent, sendData);
1743 break;
1744
1745 case libdap::dods_sequence_c:
1746 transform(strm, (libdap::Sequence *) bt, indent, sendData);
1747 break;
1748
1749 case libdap::dods_array_c:
1750 transform(strm, (libdap::Array *) bt, indent, sendData);
1751 break;
1752
1753 case libdap::dods_enum_c:
1754 case libdap::dods_group_c: {
1755 string s = (string) "File out COVJSON, DAP4 types not yet supported.";
1756 throw BESInternalError(s, __FILE__, __LINE__);
1757 break;
1758 }
1759
1760 default: {
1761 string s = (string) "File out COVJSON, Unrecognized type.";
1762 throw BESInternalError(s, __FILE__, __LINE__);
1763 break;
1764 }
1765 }
1766}
1767
1768#if 0
1769void FoDapCovJsonTransform::transformAtomic(libdap::BaseType *b, const string& indent, bool sendData)
1770{
1771 string childindent = indent + _indent_increment;
1772 struct Axis *newAxis = new Axis;
1773cerr<<"b name is "<<b->name() <<endl;
1774
1775 // Why assigning the name as "test" and why assigning the string type value? KY 2022-01-18
1776 newAxis->name = "test";
1777 if(sendData) {
1778 newAxis->values += "\"values\": [";
1779 if(b->type() == libdap::dods_str_c || b->type() == libdap::dods_url_c) {
1780 libdap::Str *strVar = (libdap::Str *) b;
1781 string tmpString = strVar->value();
1782 newAxis->values += "\"";
1783 newAxis->values += focovjson::escape_for_covjson(tmpString);
1784 newAxis->values += "\"";
1785 }
1786 else {
1787 ostringstream otemp;
1788 istringstream itemp;
1789 int tempVal = 0;
1790 b->print_val(otemp, "", false);
1791 istringstream (otemp.str());
1792 istringstream (otemp.str()) >> tempVal;
1793 newAxis->values += otemp.str();
1794 }
1795 newAxis->values += "]";
1796 }
1797 else {
1798 newAxis->values += "\"values\": []";
1799 }
1800
1801 axes.push_back(newAxis);
1802 axisCount++;
1803}
1804#endif
1805
1806void FoDapCovJsonTransform::transformAtomic(ostream *strm, libdap::BaseType *b, const string& indent, bool sendData) {
1807
1808 string childindent = indent + _indent_increment;
1809 bool axisRetrieved = false;
1810 bool parameterRetrieved = false;
1811
1812 getAttributes(strm, b->get_attr_table(), b->name(), &axisRetrieved, &parameterRetrieved);
1813
1814 if((axisRetrieved == true) && (parameterRetrieved == false)) {
1815 struct Axis *currAxis;
1816 currAxis = axes[axisCount - 1];
1817
1818 if (sendData) {
1819 currAxis->values += "\"values\": [";
1820
1821 bool is_cf_time_axis = false;
1822 if ((is_simple_cf_geographic || dsg_type != UNSUPPORTED_DSG) && currAxis->name.compare("t") == 0)
1823 is_cf_time_axis = true;
1824
1825 if (b->type() == libdap::dods_str_c || b->type() == libdap::dods_url_c) {
1826 // Strings need to be escaped to be included in a CovJSON object.
1827 const auto s = dynamic_cast<libdap::Str *>(b);
1828 string val = s->value();
1829 currAxis->values +="\"" + focovjson::escape_for_covjson(val) + "\"";
1830
1831 }
1832 else if (is_cf_time_axis) {
1833
1834 // We need to convert CF time to greg time.
1835 string axis_t_value;
1836 std::ostringstream tmp_stream;
1837 long long tmp_value = 0;
1838 switch (b->type()) {
1839 case libdap::dods_byte_c: {
1840 const auto tmp_byte = dynamic_cast<libdap::Byte *>(b);
1841 tmp_value = (long long) (tmp_byte->value());
1842 break;
1843 }
1844
1845 case libdap::dods_uint16_c: {
1846 const auto tmp_uint16 = dynamic_cast<libdap::UInt16 *>(b);
1847 tmp_value = (long long) (tmp_uint16->value());
1848 break;
1849 }
1850
1851 case libdap::dods_int16_c: {
1852 const auto tmp_int16 = dynamic_cast<libdap::Int16 *>(b);
1853 tmp_value = (long long) (tmp_int16->value());
1854 break;
1855 }
1856
1857 case libdap::dods_uint32_c: {
1858 const auto tmp_uint32 = dynamic_cast<libdap::UInt32 *>(b);
1859 tmp_value = (long long) (tmp_uint32->value());
1860 break;
1861 }
1862
1863 case libdap::dods_int32_c: {
1864 const auto tmp_int32 = dynamic_cast<libdap::Int32 *>(b);
1865 tmp_value = (long long) (tmp_int32->value());
1866 break;
1867 }
1868
1869 case libdap::dods_float32_c: {
1870 // In theory, it may cause overflow. In reality, the time in seconds will never be that large.
1871 const auto tmp_float32 = dynamic_cast<libdap::Float32 *>(b);
1872 tmp_value = (long long) (tmp_float32->value());
1873 break;
1874 }
1875
1876 case libdap::dods_float64_c: {
1877 // In theory, it may cause overflow. In reality, the time in seconds will never be that large.
1878 const auto tmp_float64 = dynamic_cast<libdap::Float64 *>(b);
1879 tmp_value = (long long) (tmp_float64->value());
1880 break;
1881 }
1882
1883 default:
1884 throw BESInternalError("Attempt to extract CF time information from an invalid source", __FILE__, __LINE__);
1885 }
1886
1887 axis_t_value = cf_time_to_greg(tmp_value);
1888
1889 currAxis->values += "\"" + focovjson::escape_for_covjson(axis_t_value) + "\"";
1890 }
1891 else {
1892 ostringstream otemp;
1893 b->print_val(otemp, "", false);
1894 currAxis->values += otemp.str();
1895 }
1896 currAxis->values += "]";
1897#if 0
1898//cerr<<"currAxis value at covjsonSimpleTypeArray is "<<currAxis->values <<endl;
1899#endif
1900 }
1901 else {
1902 currAxis->values += "\"values\": []";
1903 }
1904 }
1905
1906 // If we are dealing with a Parameter
1907 else if(axisRetrieved == false && parameterRetrieved == true) {
1908 struct Parameter *currParameter;
1909 currParameter = parameters[parameterCount - 1];
1910 if (sendData) {
1911 currParameter->values += "\"values\": [";
1912 if (b->type() == libdap::dods_str_c || b->type() == libdap::dods_url_c) {
1913 // Strings need to be escaped to be included in a CovJSON object.
1914 const auto s = dynamic_cast<libdap::Str *>(b);
1915 string val = s->value();
1916 currParameter->values +="\"" + focovjson::escape_for_covjson(val) + "\"";
1917
1918 }
1919 else {
1920 ostringstream otemp;
1921 b->print_val(otemp, "", false);
1922 currParameter->values += otemp.str();
1923 }
1924 currParameter->values += "]";
1925
1926 }
1927 else {
1928 currParameter->values += "\"values\": []";
1929 }
1930 }
1931
1932}
1933
1934void FoDapCovJsonTransform::transform(ostream *strm, libdap::Array *a, string indent, bool sendData)
1935{
1936 BESDEBUG(FoDapCovJsonTransform_debug_key,
1937 "FoCovJsonTransform::transform() - Processing Array. " << " a->type(): " << a->type() << " a->var()->type(): " << a->var()->type() << endl);
1938#if 0
1939//cerr<<"transform a name is "<<a->name() <<endl;
1940#endif
1941 switch(a->var()->type()) {
1942 // Handle the atomic types - that's easy!
1943 case libdap::dods_byte_c:
1944 covjsonSimpleTypeArray<libdap::dods_byte>(strm, a, indent, sendData);
1945 break;
1946
1947 case libdap::dods_int16_c:
1948 covjsonSimpleTypeArray<libdap::dods_int16>(strm, a, indent, sendData);
1949 break;
1950
1951 case libdap::dods_uint16_c:
1952 covjsonSimpleTypeArray<libdap::dods_uint16>(strm, a, indent, sendData);
1953 break;
1954
1955 case libdap::dods_int32_c:
1956 covjsonSimpleTypeArray<libdap::dods_int32>(strm, a, indent, sendData);
1957 break;
1958
1959 case libdap::dods_uint32_c:
1960 covjsonSimpleTypeArray<libdap::dods_uint32>(strm, a, indent, sendData);
1961 break;
1962
1963 case libdap::dods_float32_c:
1964 covjsonSimpleTypeArray<libdap::dods_float32>(strm, a, indent, sendData);
1965 break;
1966
1967 case libdap::dods_float64_c:
1968 covjsonSimpleTypeArray<libdap::dods_float64>(strm, a, indent, sendData);
1969 break;
1970
1971 case libdap::dods_str_c: {
1972 covjsonStringArray(strm, a, indent, sendData);
1973 break;
1974 }
1975
1976 case libdap::dods_url_c: {
1977 covjsonStringArray(strm, a, indent, sendData);
1978 break;
1979 }
1980
1981 case libdap::dods_structure_c:
1982 throw BESInternalError("File out COVJSON, Arrays of Structure objects not a supported return type.", __FILE__, __LINE__);
1983
1984 case libdap::dods_grid_c:
1985 throw BESInternalError("File out COVJSON, Arrays of Grid objects not a supported return type.", __FILE__, __LINE__);
1986
1987 case libdap::dods_sequence_c:
1988 throw BESInternalError("File out COVJSON, Arrays of Sequence objects not a supported return type.", __FILE__, __LINE__);
1989
1990 case libdap::dods_array_c:
1991 throw BESInternalError("File out COVJSON, Arrays of Array objects not a supported return type.", __FILE__, __LINE__);
1992
1993 case libdap::dods_int8_c:
1994 case libdap::dods_uint8_c:
1995 case libdap::dods_int64_c:
1996 case libdap::dods_uint64_c:
1997 case libdap::dods_enum_c:
1998 case libdap::dods_group_c:
1999 throw BESInternalError("File out COVJSON, DAP4 types not yet supported.", __FILE__, __LINE__);
2000
2001 default:
2002 throw BESInternalError("File out COVJSON, Unrecognized type.", __FILE__, __LINE__);
2003 }
2004}
2005
2006bool FoDapCovJsonTransform::check_update_simple_dsg(libdap::DDS *dds) {
2007
2008 bool ret_value = false;
2009 DSGType sDC_candidate = UNSUPPORTED_DSG;
2010
2011 libdap::AttrTable &globals = dds->get_attr_table();
2012 auto i = globals.attr_begin();
2013 auto e = globals.attr_end();
2014 for (; i != e; i++) {
2015 if (globals.get_attr_type(i) == libdap::Attr_container) {
2016 string attr_name = globals.get_name(i);
2017 if (BESUtil::endsWith(attr_name, "_GLOBAL")) {
2018 libdap::AttrTable *container = globals.get_attr_table(i);
2019 auto ci = container->attr_begin();
2020 auto ce = container->attr_end();
2021 bool find_featureType = false;
2022 for (; ci != ce; ci++) {
2023 if (container->get_attr_type(ci) == libdap::Attr_string) {
2024 string c_attr_name = container->get_name(ci);
2025 if (c_attr_name == "featureType") {
2026 string attr_val = container->get_attr(ci,0);
2027 if (attr_val == "point")
2028 sDC_candidate = SPOINT;
2029 else if (attr_val == "timeSeries")
2030 sDC_candidate = POINTS;
2031 else if (attr_val == "profile")
2032 sDC_candidate = PROFILE;
2033 find_featureType = true;
2034 break;
2035 }
2036 }
2037 }
2038 if (find_featureType)
2039 break;
2040
2041 }
2042 }
2043 else if (globals.get_attr_type(i) == libdap::Attr_string) {
2044 string attr_name = globals.get_name(i);
2045 // If following CF conventions
2046 if (attr_name == "featureType") {
2047 string attr_val = globals.get_attr(i,0);
2048 if (attr_val == "point")
2049 sDC_candidate = SPOINT;
2050 else if (attr_val == "timeSeries")
2051 sDC_candidate = POINTS;
2052 else if (attr_val == "profile")
2053 sDC_candidate = PROFILE;
2054 break;
2055 }
2056 }
2057 }
2058
2059#if 0
2060if (cf_point)
2061 cerr<<"point "<<endl;
2062else if(cf_timeSeries)
2063 cerr<<"timeSeries "<<endl;
2064else if(cf_profile)
2065 cerr<<"Profile "<<endl;
2066#endif
2067
2068
2069 if (sDC_candidate != UNSUPPORTED_DSG) {
2070
2071 libdap::DDS::Vars_iter vi = dds->var_begin();
2072 libdap::DDS::Vars_iter ve = dds->var_end();
2073 for(; vi != ve; vi++) {
2074 if ((*vi)->send_p()) {
2075 libdap::BaseType *v = *vi;
2076 if (is_supported_vars_by_type(v) == false)
2077 continue;
2078 libdap::AttrTable &attrs = v->get_attr_table();
2079 unsigned int num_attrs = attrs.get_size();
2080#if 0
2081//cerr<<"var name is "<<v->name() <<endl;
2082//cerr<<"num_attrs is "<<num_attrs <<endl;
2083#endif
2084 if (num_attrs) {
2085 libdap::AttrTable::Attr_iter di = attrs.attr_begin();
2086 libdap::AttrTable::Attr_iter de = attrs.attr_end();
2087 for (; di != de; di++) {
2088 string cf_attr_name = "axis";
2089 string attr_name = attrs.get_name(di);
2090 unsigned int num_vals = attrs.get_attr_num(di);
2091 if (num_vals == 1 && cf_attr_name == attr_name) {
2092 string val = attrs.get_attr(di,0);
2093 set_axisVar(v,val);
2094#if 0
2095//cerr<<"val is "<<val << endl;
2096#endif
2097 }
2098 }
2099 }
2100 }
2101 }
2102
2103 if (is_simple_dsg(sDC_candidate)) {
2104#if 0
2105//cerr<<"simple dsg "<<endl;
2106#endif
2107 ret_value = obtain_valid_dsg_par_vars(dds);
2108 }
2109 }
2110
2111#if 0
2112cerr<<"axisVar_x.name is "<<axisVar_x.name <<endl;
2113cerr<<"axisVar_x.dim_name is "<<axisVar_x.dim_name <<endl;
2114cerr<<"axisVar_x.dim_size is "<<axisVar_x.dim_size <<endl;
2115cerr<<"axisVar_x.bound_name is "<<axisVar_x.bound_name <<endl;
2116
2117cerr<<"axisVar_y.name is "<<axisVar_y.name <<endl;
2118cerr<<"axisVar_y.dim_name is "<<axisVar_y.dim_name <<endl;
2119cerr<<"axisVar_y.dim_size is "<<axisVar_y.dim_size <<endl;
2120cerr<<"axisVar_y.bound_name is "<<axisVar_y.bound_name <<endl;
2121
2122cerr<<"axisVar_z.name is "<<axisVar_z.name <<endl;
2123cerr<<"axisVar_z.dim_name is "<<axisVar_z.dim_name <<endl;
2124cerr<<"axisVar_z.dim_size is "<<axisVar_z.dim_size <<endl;
2125cerr<<"axisVar_z.bound_name is "<<axisVar_z.bound_name <<endl;
2126
2127cerr<<"axisVar_t.name is "<<axisVar_t.name <<endl;
2128cerr<<"axisVar_t.dim_name is "<<axisVar_t.dim_name <<endl;
2129cerr<<"axisVar_t.dim_size is "<<axisVar_t.dim_size <<endl;
2130cerr<<"axisVar_t.bound_name is "<<axisVar_t.bound_name <<endl;
2131#endif
2132
2133 return ret_value;
2134}
2135
2136bool FoDapCovJsonTransform::is_simple_dsg(DSGType dsg) {
2137
2138 if (dsg == SPOINT)
2139 dsg_type = is_single_point();
2140 else if (dsg == POINTS)
2141 dsg_type = is_point_series();
2142 else if (dsg == PROFILE)
2143 dsg_type = is_single_profile();
2144 return (dsg_type != UNSUPPORTED_DSG);
2145}
2146
2147DSGType FoDapCovJsonTransform::is_single_point() const {
2148
2149 DSGType ret_dsg = SPOINT;
2150 if ((axisVar_z.name !="" && axisVar_z.dim_size >1) ||
2151 (axisVar_t.name !="" && axisVar_t.dim_size >1))
2152 ret_dsg = UNSUPPORTED_DSG;
2153 return ret_dsg;
2154
2155}
2156
2157DSGType FoDapCovJsonTransform::is_point_series() const {
2158
2159 DSGType ret_dsg = POINTS;
2160 if ((axisVar_z.name !="" && axisVar_z.dim_size >1) ||
2161 (axisVar_t.name =="" ))
2162 ret_dsg = UNSUPPORTED_DSG;
2163 return ret_dsg;
2164
2165}
2166
2167
2168DSGType FoDapCovJsonTransform::is_single_profile() const {
2169
2170 DSGType ret_dsg = PROFILE;
2171 if ((axisVar_t.name !="" && axisVar_t.dim_size >1) ||
2172 (axisVar_z.name =="" ))
2173 ret_dsg = UNSUPPORTED_DSG;
2174 return ret_dsg;
2175
2176}
2177
2178bool FoDapCovJsonTransform::is_simple_dsg_common() const {
2179
2180 bool ret_value = true;
2181 if (axisVar_x.name =="" || axisVar_y.name == "")
2182 ret_value = false;
2183 else if (axisVar_x.dim_size >1 || axisVar_y.dim_size >1)
2184 ret_value = false;
2185
2186 return ret_value;
2187
2188}
2189
2190
2191// Only support array and atomic scalar datatypes.
2192bool FoDapCovJsonTransform::is_supported_vars_by_type(libdap::BaseType*v) const{
2193
2194 bool ret_value = false;
2195 libdap::Type type = v->type();
2196 if (type == libdap::dods_array_c)
2197 type = v->var()->type();
2198 switch (type) {
2199 case libdap::dods_byte_c:
2200 case libdap::dods_int16_c:
2201 case libdap::dods_uint16_c:
2202 case libdap::dods_int32_c:
2203 case libdap::dods_uint32_c:
2204 case libdap::dods_float32_c:
2205 case libdap::dods_float64_c:
2206 case libdap::dods_str_c:
2207 case libdap::dods_url_c:
2208 ret_value = true;
2209 break;
2210 default:
2211 break;
2212 }
2213 return ret_value;
2214}
2215
2216void FoDapCovJsonTransform::handle_axisVars_array(libdap::BaseType*v,axisVar & this_axisVar) {
2217
2218 auto d_a = dynamic_cast<libdap::Array *>(v);
2219 if (d_a !=nullptr) {
2220
2221 int d_ndims = d_a->dimensions();
2222
2223 if (d_ndims == 1) {
2224
2225 libdap::Array::Dim_iter di = d_a->dim_begin();
2226 this_axisVar.dim_size = d_a->dimension_size(di, true);
2227 this_axisVar.name = d_a->name();
2228 this_axisVar.dim_name = d_a->dimension_name(di);
2229
2230 }
2231 }
2232
2233}
2234void FoDapCovJsonTransform::set_axisVar(libdap::BaseType*v,const string &val) {
2235
2236 // Here we assume the libdap datatype is either array of atomic datatypes
2237 // or just atomic datatypes. is_supported_vars_by_type() should be called
2238 // beforehand.
2239 if (val == "X") {
2240 if (v->type() == libdap::dods_array_c)
2241 handle_axisVars_array(v,axisVar_x);
2242 else {
2243 axisVar_x.name = v->name();
2244 axisVar_x.dim_size = 0;
2245 }
2246 }
2247 else if (val == "Y") {
2248 if (v->type() == libdap::dods_array_c)
2249 handle_axisVars_array(v,axisVar_y);
2250 else {
2251 axisVar_y.name = v->name();
2252 axisVar_y.dim_size = 0;
2253 }
2254 }
2255 else if (val == "Z") {
2256 if (v->type() == libdap::dods_array_c)
2257 handle_axisVars_array(v,axisVar_z);
2258 else {
2259 axisVar_z.name = v->name();
2260 axisVar_z.dim_size = 0;
2261 }
2262 }
2263 else if (val == "T") {
2264 if (v->type() == libdap::dods_array_c)
2265 handle_axisVars_array(v,axisVar_t);
2266 else {
2267 axisVar_t.name = v->name();
2268 axisVar_t.dim_size = 0;
2269 }
2270 }
2271}
2272
2273bool FoDapCovJsonTransform::is_cf_grid_mapping_var(libdap::BaseType *v) const {
2274
2275 bool ret_value = false;
2276 libdap::AttrTable attr_table = v->get_attr_table();
2277
2278 if (attr_table.get_size() != 0) {
2279
2280 libdap::AttrTable::Attr_iter begin = attr_table.attr_begin();
2281 libdap::AttrTable::Attr_iter end = attr_table.attr_end();
2282
2283 for (libdap::AttrTable::Attr_iter at_iter = begin; at_iter != end; at_iter++) {
2284
2285 if (attr_table.get_attr_type(at_iter) == libdap::Attr_string &&
2286 attr_table.get_name(at_iter) == "grid_mapping_name") {
2287 ret_value = true;
2288 break;
2289 }
2290 }
2291 }
2292
2293 return ret_value;
2294}
2295
2296bool FoDapCovJsonTransform::is_fake_coor_vars(libdap::Array *d_a) const{
2297
2298 bool ret_value = false;
2299
2300 if (d_a->dimensions() == 1) {
2301 libdap::Array::Dim_iter i = d_a->dim_begin();
2302 string dim_name = d_a->dimension_name(i);
2303 if (dim_name == d_a->name()) {
2304 libdap::AttrTable attr_table = d_a->get_attr_table();
2305 if (attr_table.get_size() == 0)
2306 ret_value = true;
2307 }
2308 }
2309 return ret_value;
2310
2311}
2312
2313bool FoDapCovJsonTransform::is_valid_single_point_par_var(libdap::BaseType *v) const {
2314
2315 bool ret_value = true;
2316 if (v->name() == axisVar_x.name || v->name() == axisVar_y.name ||
2317 v->name() == axisVar_z.name || v->name() == axisVar_t.name ||
2318 is_cf_grid_mapping_var(v))
2319 ret_value = false;
2320
2321 return ret_value;
2322}
2323
2324bool FoDapCovJsonTransform::is_valid_array_dsg_par_var(libdap::Array *d_a) const {
2325
2326 bool ret_value = false;
2327
2328 if (d_a->dimensions() == 1) {
2329
2330 libdap::Array::Dim_iter i = d_a->dim_begin();
2331 int dim_size = d_a->dimension_size(i);
2332 string dim_name = d_a->dimension_name(i);
2333 bool fake_coor = is_fake_coor_vars(d_a);
2334 bool real_coor = false;
2335 if (d_a->name() == axisVar_t.name || d_a->name() == axisVar_z.name || d_a->name() == axisVar_x.name || d_a->name() == axisVar_y.name)
2336 real_coor = true;
2337 if (real_coor == false && fake_coor == false) {
2338 if ((dsg_type == SPOINT)
2339 ||(dsg_type == POINTS && dim_name == axisVar_t.dim_name && dim_size == axisVar_t.dim_size)
2340 ||(dsg_type == PROFILE && dim_name == axisVar_z.dim_name && dim_size == axisVar_z.dim_size))
2341 ret_value = true;
2342 }
2343 }
2344 return ret_value;
2345}
2346
2347bool FoDapCovJsonTransform::is_valid_dsg_par_var(libdap::BaseType *v) {
2348
2349 bool ret_value = true;
2350 if (v->type() != libdap::dods_array_c) { // Scalar
2351 // Parameter variables for timeseries and profile should have dimensions
2352 if (dsg_type != SPOINT)
2353 ret_value = false;
2354 else
2355 ret_value = is_valid_single_point_par_var(v);
2356 }
2357 else {// Array
2358 // Single point parameter vars can be 1 element array.
2359 auto d_a = dynamic_cast<libdap::Array *>(v);
2360 ret_value = is_valid_array_dsg_par_var(d_a);
2361 }
2362
2363 return ret_value;
2364}
2365
2366bool FoDapCovJsonTransform::obtain_valid_dsg_par_vars(libdap::DDS *dds) {
2367
2368 libdap::DDS::Vars_iter vi = dds->var_begin();
2369 libdap::DDS::Vars_iter ve = dds->var_end();
2370 for(; vi != ve; vi++) {
2371 if ((*vi)->send_p()) {
2372 libdap::BaseType *v = *vi;
2373 if (is_supported_vars_by_type(v) == false)
2374 continue;
2375 else if (is_valid_dsg_par_var(v) == true)
2376 par_vars.push_back(v->name());
2377 }
2378 }
2379 return (par_vars.empty()==false);
2380}
2381
2382
2383
2384void FoDapCovJsonTransform::check_update_simple_geo(libdap::DDS *dds,bool sendData) {
2385
2386 libdap::DDS::Vars_iter vi = dds->var_begin();
2387 libdap::DDS::Vars_iter ve = dds->var_end();
2388
2389 // First search CF units from 1-D array.
2390 bool has_axis_var_x = false;
2391 short axis_var_x_count = 0;
2392 bool has_axis_var_y = false;
2393 short axis_var_y_count = 0;
2394 bool has_axis_var_z = false;
2395 short axis_var_z_count = 0;
2396 bool has_axis_var_t = false;
2397 short axis_var_t_count = 0;
2398
2399 string units_name ="units";
2400 for (; vi != ve; vi++) {
2401#if 0
2402//cerr<<"coming to the loop " <<endl;
2403#endif
2404 if((*vi)->send_p()) {
2405 libdap::BaseType *v = *vi;
2406 libdap::Type type = v->type();
2407
2408 // Check if this qualifies a simple geographic grid coverage
2409 if(type == libdap::dods_array_c) {
2410 libdap::Array * d_a = dynamic_cast<libdap::Array *>(v);
2411 int d_ndims = d_a->dimensions();
2412#if 0
2413//cerr<<"d_ndims is "<< d_ndims <<endl;
2414#endif
2415 if(d_ndims == 1) {
2416#if 0
2417//cerr<<"d_a name is "<<d_a->name() <<endl;
2418#endif
2419 libdap::AttrTable &attrs = d_a->get_attr_table();
2420 unsigned int num_attrs = attrs.get_size();
2421 if (num_attrs) {
2422 libdap::AttrTable::Attr_iter i = attrs.attr_begin();
2423 libdap::AttrTable::Attr_iter e = attrs.attr_end();
2424 for (; i != e; i++) {
2425 string attr_name = attrs.get_name(i);
2426#if 0
2427//cerr<<"attr_name is "<<attr_name <<endl;
2428#endif
2429 unsigned int num_vals = attrs.get_attr_num(i);
2430 if (num_vals == 1) {
2431 // Check if the attr_name is units.
2432 bool is_attr_units = false;
2433 if((attr_name.size() == units_name.size())
2434 && (attr_name.compare(units_name) == 0))
2435 is_attr_units = true;
2436 if(is_attr_units == false)
2437 if(attr_name.size() == (units_name.size()+1) &&
2438 attr_name[units_name.size()] == '\0' &&
2439 attr_name.compare(0,units_name.size(),units_name) ==0)
2440 is_attr_units = true;
2441
2442 if (is_attr_units) {
2443 string val = attrs.get_attr(i,0);
2444 vector<string> unit_candidates;
2445
2446 // Here we need to check if there are 2 latitudes or longitudes.
2447 // If we find this issue, we should mark it. The coverage json won't support this case.
2448 // longitude axis x
2449 unit_candidates.push_back("degrees_east");
2450 has_axis_var_x = check_add_axis(d_a,val,unit_candidates,axisVar_x,false);
2451 if (true == has_axis_var_x) {
2452 axis_var_x_count++;
2453 if (axis_var_x_count ==2)
2454 break;
2455 }
2456 unit_candidates.clear();
2457
2458 // latitude axis y
2459 unit_candidates.push_back("degrees_north");
2460 has_axis_var_y = check_add_axis(d_a,val,unit_candidates,axisVar_y,false);
2461 if (true == has_axis_var_y) {
2462 axis_var_y_count++;
2463 if (axis_var_y_count == 2)
2464 break;
2465 }
2466 unit_candidates.clear();
2467
2468 // height/pressure
2469 unit_candidates.push_back("hpa");
2470 unit_candidates.push_back("hPa");
2471 unit_candidates.push_back("meter");
2472 unit_candidates.push_back("m");
2473 unit_candidates.push_back("km");
2474 has_axis_var_z = check_add_axis(d_a,val,unit_candidates,axisVar_z,false);
2475 if (true == has_axis_var_z) {
2476 axis_var_z_count++;
2477 if (axis_var_z_count == 2)
2478 break;
2479 }
2480 unit_candidates.clear();
2481#if 0
2482for(int i = 0; i <unit_candidates.size(); i++)
2483 cerr<<"unit_candidates[i] is "<<unit_candidates[i] <<endl;
2484#endif
2485
2486 // time: CF units only
2487 unit_candidates.push_back("seconds since ");
2488 unit_candidates.push_back("minutes since ");
2489 unit_candidates.push_back("hours since ");
2490 unit_candidates.push_back("days since ");
2491#if 0
2492for(int i = 0; i <unit_candidates.size(); i++)
2493cerr<<"unit_candidates[i] again is "<<unit_candidates[i] <<endl;
2494#endif
2495
2496 has_axis_var_t = check_add_axis(d_a,val,unit_candidates,axisVar_t,true);
2497 if (true == has_axis_var_t) {
2498 axis_var_t_count++;
2499 if (axis_var_t_count == 2)
2500 break;
2501 }
2502 unit_candidates.clear();
2503
2504 }
2505
2506 }
2507 }
2508 }
2509 }
2510 }
2511 }
2512 }
2513#if 0
2514cerr<<"axis_var_x_count is "<< axis_var_x_count <<endl;
2515cerr<<"axis_var_y_count is "<< axis_var_y_count <<endl;
2516cerr<<"axis_var_z_count is "<< axis_var_z_count <<endl;
2517cerr<<"axis_var_t_count is "<< axis_var_t_count <<endl;
2518#endif
2519 bool is_simple_geo_candidate = true;
2520 if(axis_var_x_count !=1 || axis_var_y_count !=1)
2521 is_simple_geo_candidate = false;
2522 // Single coverage for the time being
2523 // make z axis and t axis be empty if multiple z or t.
2524 if(axis_var_z_count > 1) {
2525 axisVar_z.name="";
2526 axisVar_z.dim_name = "";
2527 axisVar_z.bound_name = "";
2528 }
2529 if(axis_var_t_count > 1) {
2530 axisVar_t.name="";
2531 axisVar_t.dim_name = "";
2532 axisVar_t.bound_name = "";
2533 }
2534 if(is_simple_geo_candidate == true) {
2535
2536 // Check bound variables
2537 // Check if any 1-D variable has the "bounds" attribute;
2538 // we need to remember the attribute value and match the variable that
2539 // holds the bound values later. KY 2022-1-21
2540 map<string, string> vname_bname;
2541
2542 check_bounds(dds,vname_bname);
2543
2544 map<string, string>::iterator it;
2545#if 0
2546for(it = vname_bname.begin(); it != vname_bname.end(); it++) {
2547cerr<<it->first <<endl;
2548cerr<<it->second <<endl;
2549}
2550#endif
2551
2552 for(it = vname_bname.begin(); it != vname_bname.end(); it++) {
2553// cerr<<it->first <<endl;
2554// cerr<<it->second <<endl;
2555 if(axisVar_x.name == it->first)
2556 axisVar_x.bound_name = it->second;
2557 else if(axisVar_y.name == it->first)
2558 axisVar_y.bound_name = it->second;
2559 else if(axisVar_z.name == it->first)
2560 axisVar_z.bound_name = it->second;
2561 else if(axisVar_t.name == it->first)
2562 axisVar_t.bound_name = it->second;
2563 }
2564#if 0
2565cerr<<"axisVar_x.name is "<<axisVar_x.name <<endl;
2566cerr<<"axisVar_x.dim_name is "<<axisVar_x.dim_name <<endl;
2567cerr<<"axisVar_x.dim_size is "<<axisVar_x.dim_size <<endl;
2568cerr<<"axisVar_x.bound_name is "<<axisVar_x.bound_name <<endl;
2569
2570cerr<<"axisVar_y.name is "<<axisVar_y.name <<endl;
2571cerr<<"axisVar_y.dim_name is "<<axisVar_y.dim_name <<endl;
2572cerr<<"axisVar_y.dim_size is "<<axisVar_y.dim_size <<endl;
2573cerr<<"axisVar_y.bound_name is "<<axisVar_y.bound_name <<endl;
2574
2575cerr<<"axisVar_z.name is "<<axisVar_z.name <<endl;
2576cerr<<"axisVar_z.dim_name is "<<axisVar_z.dim_name <<endl;
2577cerr<<"axisVar_z.dim_size is "<<axisVar_z.dim_size <<endl;
2578cerr<<"axisVar_z.bound_name is "<<axisVar_z.bound_name <<endl;
2579
2580cerr<<"axisVar_t.name is "<<axisVar_t.name <<endl;
2581cerr<<"axisVar_t.dim_name is "<<axisVar_t.dim_name <<endl;
2582cerr<<"axisVar_t.dim_size is "<<axisVar_t.dim_size <<endl;
2583cerr<<"axisVar_t.bound_name is "<<axisVar_t.bound_name <<endl;
2584#endif
2585
2586
2587 is_simple_cf_geographic = obtain_valid_vars(dds,axis_var_z_count,axis_var_t_count);
2588
2589 if(true == is_simple_cf_geographic) {
2590#if 0
2591//cerr<<"this is a simple CF geographic grid we can handle" <<endl;
2592#endif
2593 // We should handle the bound value
2594 // ignore 1-D bound dimension variable,
2595 // Retrieve the values of the 2-D bound variable,
2596 // Will save as the bound value in the coverage
2597
2598 string x_bnd_dim_name;
2599 string y_bnd_dim_name;
2600 string z_bnd_dim_name;
2601 string t_bnd_dim_name;
2602
2603 obtain_bound_values(dds,axisVar_x,axisVar_x_bnd_val, x_bnd_dim_name,sendData);
2604 obtain_bound_values(dds,axisVar_y,axisVar_y_bnd_val, y_bnd_dim_name,sendData);
2605 obtain_bound_values(dds,axisVar_z,axisVar_z_bnd_val, z_bnd_dim_name,sendData);
2606 obtain_bound_values(dds,axisVar_t,axisVar_t_bnd_val, t_bnd_dim_name,sendData);
2607
2608 if(x_bnd_dim_name!="")
2609 bnd_dim_names.push_back(x_bnd_dim_name);
2610 else if(y_bnd_dim_name!="")
2611 bnd_dim_names.push_back(y_bnd_dim_name);
2612 else if(z_bnd_dim_name!="")
2613 bnd_dim_names.push_back(z_bnd_dim_name);
2614 else if(t_bnd_dim_name!="")
2615 bnd_dim_names.push_back(t_bnd_dim_name);
2616 }
2617
2618 }
2619}
2620
2621void FoDapCovJsonTransform::check_update_simple_geo_dap4(libdap::D4Group *d4g) {
2622
2623
2624 // First search CF units from 1-D array.
2625 bool has_axis_var_x = false;
2626 short axis_var_x_count = 0;
2627 bool has_axis_var_y = false;
2628 short axis_var_y_count = 0;
2629 bool has_axis_var_z = false;
2630 short axis_var_z_count = 0;
2631 bool has_axis_var_t = false;
2632 short axis_var_t_count = 0;
2633
2634 string units_name ="units";
2635 for (auto vi = d4g->var_begin(), ve = d4g->var_end(); vi != ve; ++vi) {
2636#if 0
2637//cerr<<"coming to the loop " <<endl;
2638#endif
2639 if((*vi)->send_p()) {
2640
2641 libdap::BaseType *v = *vi;
2642 libdap::Type type = v->type();
2643
2644 // Check if this qualifies a simple geographic grid coverage
2645 // TODO: here we still use dap2's way to find dimensions. This will be changed later.
2646 if(type == libdap::dods_array_c) {
2647 auto d_a = dynamic_cast<libdap::Array *>(v);
2648 int d_ndims = d_a->dimensions();
2649#if 0
2650//cerr<<"d_ndims is "<< d_ndims <<endl;
2651#endif
2652 if (d_ndims == 1) {
2653#if 0
2654//cerr<<"d_a name is "<<d_a->name() <<endl;
2655#endif
2656 libdap::D4Attributes *d4_attrs = d_a->attributes();
2657 for (libdap::D4Attributes::D4AttributesIter ii = d4_attrs->attribute_begin(), ee = d4_attrs->attribute_end();
2658 ii != ee; ++ii) {
2659
2660 string attr_name = (*ii)->name();
2661 unsigned int num_vals = (*ii)->num_values();
2662
2663 if (num_vals == 1) {
2664
2665 // Check if the attr_name is units.
2666 bool is_attr_units = false;
2667 if ((attr_name.size() == units_name.size())
2668 && (attr_name.compare(units_name) == 0))
2669 is_attr_units = true;
2670 if (is_attr_units == false &&
2671 (attr_name.size() == (units_name.size()+1) &&
2672 attr_name[units_name.size()] == '\0' &&
2673 attr_name.compare(0,units_name.size(),units_name) ==0))
2674 is_attr_units = true;
2675
2676 if (is_attr_units) {
2677 string val = (*ii)->value(0);
2678 vector<string> unit_candidates;
2679
2680 // Here we need to check if there are 2 latitudes or longitudes.
2681 // If we find this issue, we should mark it. The coverage json won't support this case.
2682 // longitude axis x
2683 unit_candidates.emplace_back("degrees_east");
2684 has_axis_var_x = check_add_axis(d_a,val,unit_candidates,axisVar_x,false);
2685 if (true == has_axis_var_x) {
2686 axis_var_x_count++;
2687 if (axis_var_x_count == 2)
2688 break;
2689 }
2690 unit_candidates.clear();
2691
2692 // latitude axis y
2693 unit_candidates.emplace_back("degrees_north");
2694 has_axis_var_y = check_add_axis(d_a,val,unit_candidates,axisVar_y,false);
2695 if (true == has_axis_var_y) {
2696 axis_var_y_count++;
2697 if (axis_var_y_count == 2)
2698 break;
2699 }
2700 unit_candidates.clear();
2701
2702 // height/pressure
2703 unit_candidates.emplace_back("hpa");
2704 unit_candidates.emplace_back("hPa");
2705 unit_candidates.emplace_back("meter");
2706 unit_candidates.emplace_back("m");
2707 unit_candidates.emplace_back("km");
2708 has_axis_var_z = check_add_axis(d_a,val,unit_candidates,axisVar_z,false);
2709 if (true == has_axis_var_z) {
2710 axis_var_z_count++;
2711 if (axis_var_z_count == 2)
2712 break;
2713 }
2714 unit_candidates.clear();
2715#if 0
2716for(int i = 0; i <unit_candidates.size(); i++)
2717 cerr<<"unit_candidates[i] is "<<unit_candidates[i] <<endl;
2718#endif
2719
2720 // time: CF units only
2721 unit_candidates.emplace_back("seconds since ");
2722 unit_candidates.emplace_back("minutes since ");
2723 unit_candidates.emplace_back("hours since ");
2724 unit_candidates.emplace_back("days since ");
2725#if 0
2726for(int i = 0; i <unit_candidates.size(); i++)
2727cerr<<"unit_candidates[i] again is "<<unit_candidates[i] <<endl;
2728#endif
2729
2730 has_axis_var_t = check_add_axis(d_a,val,unit_candidates,axisVar_t,true);
2731 if (true == has_axis_var_t) {
2732 axis_var_t_count++;
2733 if (axis_var_t_count == 2)
2734 break;
2735 }
2736 unit_candidates.clear();
2737 }
2738 }
2739 }
2740 }
2741 }
2742 }
2743 }
2744
2745#if 0
2746cerr<<"axis_var_x_count is "<< axis_var_x_count <<endl;
2747cerr<<"axis_var_y_count is "<< axis_var_y_count <<endl;
2748cerr<<"axis_var_z_count is "<< axis_var_z_count <<endl;
2749cerr<<"axis_var_t_count is "<< axis_var_t_count <<endl;
2750#endif
2751
2752 bool is_simple_geo_candidate = true;
2753 if(axis_var_x_count != 1 || axis_var_y_count != 1)
2754 is_simple_geo_candidate = false;
2755
2756 // Single coverage for the time being
2757 // make z axis and t axis be empty if multiple z or t.
2758 if(axis_var_z_count > 1) {
2759 axisVar_z.name="";
2760 axisVar_z.dim_name = "";
2761 axisVar_z.bound_name = "";
2762 }
2763 if(axis_var_t_count > 1) {
2764 axisVar_t.name="";
2765 axisVar_t.dim_name = "";
2766 axisVar_t.bound_name = "";
2767 }
2768 if (is_simple_geo_candidate == true) {
2769
2770#if 0
2771cerr<<"axisVar_x.name is "<<axisVar_x.name <<endl;
2772cerr<<"axisVar_x.dim_name is "<<axisVar_x.dim_name <<endl;
2773cerr<<"axisVar_x.dim_size is "<<axisVar_x.dim_size <<endl;
2774cerr<<"axisVar_x.bound_name is "<<axisVar_x.bound_name <<endl;
2775
2776cerr<<"axisVar_y.name is "<<axisVar_y.name <<endl;
2777cerr<<"axisVar_y.dim_name is "<<axisVar_y.dim_name <<endl;
2778cerr<<"axisVar_y.dim_size is "<<axisVar_y.dim_size <<endl;
2779cerr<<"axisVar_y.bound_name is "<<axisVar_y.bound_name <<endl;
2780
2781cerr<<"axisVar_z.name is "<<axisVar_z.name <<endl;
2782cerr<<"axisVar_z.dim_name is "<<axisVar_z.dim_name <<endl;
2783cerr<<"axisVar_z.dim_size is "<<axisVar_z.dim_size <<endl;
2784cerr<<"axisVar_z.bound_name is "<<axisVar_z.bound_name <<endl;
2785
2786cerr<<"axisVar_t.name is "<<axisVar_t.name <<endl;
2787cerr<<"axisVar_t.dim_name is "<<axisVar_t.dim_name <<endl;
2788cerr<<"axisVar_t.dim_size is "<<axisVar_t.dim_size <<endl;
2789cerr<<"axisVar_t.bound_name is "<<axisVar_t.bound_name <<endl;
2790#endif
2791
2792 is_simple_cf_geographic = obtain_valid_vars_dap4(d4g,axis_var_z_count,axis_var_t_count);
2793 }
2794}
2795
2796
2797bool FoDapCovJsonTransform::check_add_axis(libdap::Array *d_a,const string & unit_value, const vector<string> & CF_unit_values, axisVar & this_axisVar, bool is_t_axis) {
2798
2799 bool ret_value = false;
2800 for (unsigned i = 0; i < CF_unit_values.size(); i++) {
2801#if 0
2802//cerr<<"CF_unit_values "<<CF_unit_values[i] << endl;
2803#endif
2804 bool is_cf_units = false;
2805 if(is_t_axis == false) {
2806 if((unit_value.size() == CF_unit_values[i].size() || unit_value.size() == (CF_unit_values[i].size() +1)) && unit_value.compare(0,CF_unit_values[i].size(),CF_unit_values[i])==0)
2807 is_cf_units = true;
2808 }
2809 else {
2810 if(unit_value.compare(0,CF_unit_values[i].size(),CF_unit_values[i])==0)
2811 is_cf_units = true;
2812 }
2813
2814 if (is_cf_units) {
2815 libdap::Array::Dim_iter di = d_a->dim_begin();
2816 this_axisVar.dim_size = d_a->dimension_size(di, true);
2817 this_axisVar.name = d_a->name();
2818 this_axisVar.dim_name = d_a->dimension_name(di);
2819 this_axisVar.bound_name="";
2820 ret_value = true;
2821#if 0
2822cerr<<"axis size "<< this_axisVar.dim_size <<endl;
2823cerr<<"axis name "<< this_axisVar.name <<endl;
2824cerr<<"axis dim_name "<< this_axisVar.dim_name <<endl;
2825#endif
2826 break;
2827 }
2828
2829 }
2830 return ret_value;
2831
2832}
2833
2834
2835void FoDapCovJsonTransform::check_bounds(libdap::DDS *dds, map<string,string>& vname_bname) {
2836
2837 string bound_name = "bounds";
2838 libdap::DDS::Vars_iter vi = dds->var_begin();
2839 libdap::DDS::Vars_iter ve = dds->var_end();
2840
2841 for(; vi != ve; vi++) {
2842#if 0
2843//cerr<<"coming to the loop " <<endl;
2844#endif
2845 if((*vi)->send_p()) {
2846 libdap::BaseType *v = *vi;
2847 libdap::Type type = v->type();
2848
2849 // Check if this qualifies a simple geographic grid coverage
2850 if(type == libdap::dods_array_c) {
2851 libdap::Array * d_a = dynamic_cast<libdap::Array *>(v);
2852 int d_ndims = d_a->dimensions();
2853#if 0
2854//cerr<<"d_ndims is "<< d_ndims <<endl;
2855#endif
2856 if(d_ndims == 1) {
2857 libdap::AttrTable &attrs = d_a->get_attr_table();
2858 unsigned int num_attrs = attrs.get_size();
2859 if (num_attrs) {
2860 libdap::AttrTable::Attr_iter i = attrs.attr_begin();
2861 libdap::AttrTable::Attr_iter e = attrs.attr_end();
2862 for (; i != e; i++) {
2863 string attr_name = attrs.get_name(i);
2864#if 0
2865//cerr<<"attr_name is "<<attr_name <<endl;
2866#endif
2867 unsigned int num_vals = attrs.get_attr_num(i);
2868 if (num_vals == 1) {
2869 // Check if the attr_name is units.
2870 bool is_attr_bounds = false;
2871 if((attr_name.size() == bound_name.size())
2872 && (attr_name.compare(bound_name) == 0))
2873 is_attr_bounds = true;
2874 if(is_attr_bounds == false)
2875 if(attr_name.size() == (bound_name.size()+1) &&
2876 attr_name[bound_name.size()] == '\0' &&
2877 attr_name.compare(0,bound_name.size(),bound_name) ==0)
2878 is_attr_bounds = true;
2879
2880 if (is_attr_bounds) {
2881 string val = attrs.get_attr(i,0);
2882 vname_bname[d_a->name()] = val;
2883 }
2884 }
2885 }
2886 }
2887 }
2888 }
2889 }
2890 }
2891}
2892
2893void FoDapCovJsonTransform::obtain_bound_values(libdap::DDS *dds, const axisVar & av, std::vector<float>& av_bnd_val, std::string& bnd_dim_name, bool sendData) {
2894
2895//cerr<<"coming to the obtain_bound_values "<<endl;
2896 libdap::Array* d_a = obtain_bound_values_worker(dds, av.bound_name,bnd_dim_name);
2897 if (d_a) {// float, now we just handle this way
2898#if 0
2899//cerr<<"d_a->name in obtain_bound_values is "<<d_a->name() <<endl;
2900//cerr<<"in obtain_bound_values bnd_dim_name is "<<bnd_dim_name <<endl;
2901#endif
2902 if(d_a->var()->type_name() == "Float64") {
2903 if(sendData) {
2904 int num_lengths = d_a->length();
2905 vector<double>temp_val;
2906 temp_val.resize(num_lengths);
2907 d_a->value(temp_val.data());
2908
2909 av_bnd_val.resize(num_lengths);
2910 for (unsigned i = 0; i <av_bnd_val.size();i++)
2911 av_bnd_val[i] =(float)temp_val[i];
2912
2913#if 0
2914for (int i = 0; i <av_bnd_val.size();i++)
2915cerr<<"av_bnd_val["<<i<<"] = "<<av_bnd_val[i] <<endl;
2916#endif
2917 }
2918 }
2919 else if(d_a->var()->type_name() == "Float32") {
2920 if(sendData) {
2921 int num_lengths = d_a->length();
2922 av_bnd_val.resize(num_lengths);
2923 d_a->value(av_bnd_val.data());
2924#if 0
2925for (int i = 0; i <av_bnd_val.size();i++)
2926cerr<<"av_bnd_val["<<i<<"] = "<<av_bnd_val[i] <<endl;
2927#endif
2928 }
2929 }
2930
2931
2932 }
2933}
2934
2935void FoDapCovJsonTransform::obtain_bound_values(libdap::DDS *dds, const axisVar & av, std::vector<double>& av_bnd_val, std::string& bnd_dim_name, bool sendData) {
2936
2937 libdap::Array* d_a = obtain_bound_values_worker(dds, av.bound_name,bnd_dim_name);
2938 if(d_a) {
2939#if 0
2940cerr<<"d_a->name in obtain_bound_values is "<<d_a->name() <<endl;
2941cerr<<"in obtain_bound_values bnd_dim_name is "<<bnd_dim_name <<endl;
2942#endif
2943 if(d_a->var()->type_name() == "Float64") {
2944 if(sendData) {
2945 int num_lengths = d_a->length();
2946 av_bnd_val.resize(num_lengths);
2947 d_a->value(av_bnd_val.data());
2948#if 0
2949for (int i = 0; i <av_bnd_val.size();i++)
2950cerr<<"av_bnd_val["<<i<<"] = "<<av_bnd_val[i] <<endl;
2951#endif
2952 }
2953 }
2954 else if(d_a->var()->type_name() == "Float32") {
2955 if(sendData) {
2956 int num_lengths = d_a->length();
2957 vector<float>temp_val;
2958 temp_val.resize(num_lengths);
2959 d_a->value(temp_val.data());
2960 av_bnd_val.resize(num_lengths);
2961 for (unsigned i = 0; i <av_bnd_val.size();i++)
2962 av_bnd_val[i] =(double)temp_val[i];
2963#if 0
2964for (int i = 0; i <av_bnd_val.size();i++)
2965cerr<<"av_bnd_val["<<i<<"] = "<<av_bnd_val[i] <<endl;
2966#endif
2967 }
2968 }
2969 }
2970}
2971
2972libdap::Array* FoDapCovJsonTransform::obtain_bound_values_worker(libdap::DDS *dds, const string& bnd_name, string & bnd_dim_name) {
2973
2974 libdap::Array* d_a = nullptr;
2975 if(bnd_name!="") {
2976
2977 libdap::DDS::Vars_iter vi = dds->var_begin();
2978 libdap::DDS::Vars_iter ve = dds->var_end();
2979
2980 for(; vi != ve; vi++) {
2981#if 0
2982// cerr<<"In worker: coming to the loop " <<endl;
2983#endif
2984 if((*vi)->send_p()) {
2985 libdap::BaseType *v = *vi;
2986 libdap::Type type = v->type();
2987
2988 // Check if this qualifies a simple geographic grid coverage
2989 if(type == libdap::dods_array_c) {
2990 libdap::Array * td_a = dynamic_cast<libdap::Array *>(v);
2991 int d_ndims = td_a->dimensions();
2992#if 0
2993// cerr<<"In worker: d_ndims is "<< d_ndims <<endl;
2994#endif
2995 if(d_ndims == 2) {
2996 string tmp_bnd_dim_name;
2997 int bound_dim_size = 0;
2998 int dim_count = 0;
2999 libdap::Array::Dim_iter di = td_a->dim_begin();
3000 libdap::Array::Dim_iter de = td_a->dim_end();
3001 for (; di != de; di++) {
3002 if(dim_count == 1) {
3003 bound_dim_size = td_a->dimension_size(di, true);
3004 tmp_bnd_dim_name = td_a->dimension_name(di);
3005#if 0
3006//cerr<<"tmp_bnd_dim_name is "<<tmp_bnd_dim_name <<endl;
3007#endif
3008 }
3009 dim_count++;
3010 }
3011
3012 // For 1-D coordinate bound, the bound size should always be 2.
3013 if((bound_dim_size == 2) && (td_a->name() == bnd_name)) {
3014 d_a = td_a;
3015 bnd_dim_name = tmp_bnd_dim_name;
3016#if 0
3017//cerr<<"bnd_dim_name is "<<bnd_dim_name <<endl;
3018#endif
3019 break;
3020 }
3021 }
3022 }
3023 }
3024 }
3025
3026 }
3027 return d_a;
3028
3029}
3030
3031bool FoDapCovJsonTransform::obtain_valid_vars(libdap::DDS *dds, short axis_var_z_count, short axis_var_t_count ) {
3032
3033#if 0
3034//cerr<<"coming to obtain_valid_vars "<<endl;
3035#endif
3036 bool ret_value = true;
3037 std::vector<std::string> temp_x_y_vars;
3038 std::vector<std::string> temp_x_y_z_vars;
3039 std::vector<std::string> temp_x_y_t_vars;
3040 std::vector<std::string> temp_x_y_z_t_vars;
3041
3042 libdap::DDS::Vars_iter vi = dds->var_begin();
3043 libdap::DDS::Vars_iter ve = dds->var_end();
3044 for(; vi != ve; vi++) {
3045 if((*vi)->send_p()) {
3046 libdap::BaseType *v = *vi;
3047 libdap::Type type = v->type();
3048 if(type == libdap::dods_array_c) {
3049 libdap::Array * d_a = dynamic_cast<libdap::Array *>(v);
3050 int d_ndims = d_a->dimensions();
3051
3052 if(d_ndims >=2) {
3053
3054 short axis_x_count = 0;
3055 short axis_y_count = 0;
3056 short axis_z_count = 0;
3057 short axis_t_count = 0;
3058 bool non_xyzt_dim = false;
3059 bool supported_var = true;
3060
3061 libdap::Array::Dim_iter di = d_a->dim_begin();
3062 libdap::Array::Dim_iter de = d_a->dim_end();
3063
3064 for (; di != de; di++) {
3065 // check x,y,z,t dimensions
3066 if((d_a->dimension_size(di,true) == axisVar_x.dim_size) &&
3067 (d_a->dimension_name(di) == axisVar_x.dim_name))
3068 axis_x_count++;
3069 else if((d_a->dimension_size(di,true) == axisVar_y.dim_size) &&
3070 (d_a->dimension_name(di) == axisVar_y.dim_name))
3071 axis_y_count++;
3072 else if((d_a->dimension_size(di,true) == axisVar_z.dim_size) &&
3073 (d_a->dimension_name(di) == axisVar_z.dim_name))
3074 axis_z_count++;
3075 else if((d_a->dimension_size(di,true) == axisVar_t.dim_size) &&
3076 (d_a->dimension_name(di) == axisVar_t.dim_name))
3077 axis_t_count++;
3078 else
3079 non_xyzt_dim = true;
3080
3081 // Non-x,y,z,t dimension or duplicate x,y,z,t dimensions are not supported.
3082 // Here for the "strict" case, I need to return false for the conversion to grid when
3083 // a non-conform > 1D var appears except the "bound" variables.
3084 if(non_xyzt_dim || axis_x_count >1 || axis_y_count >1 || axis_z_count >1 || axis_t_count >1) {
3085 supported_var = false;
3086#if 0
3087cerr<<"Obtain: d_a->name() is "<<d_a->name() <<endl;
3088#endif
3089 if (FoCovJsonRequestHandler::get_may_ignore_z_axis() == false) {
3090 if(d_a->name()!=axisVar_x.bound_name && d_a->name()!=axisVar_y.bound_name &&
3091 d_a->name()!=axisVar_z.bound_name && d_a->name()!=axisVar_t.bound_name)
3092 ret_value = false;
3093 }
3094 break;
3095 }
3096 }
3097
3098 if(supported_var) {
3099 // save the var names to the vars that hold (x,y),(x,y,z),(x,y,t),(x,y,z,t)
3100 if(axis_x_count == 1 && axis_y_count == 1 && axis_z_count == 0 && axis_t_count == 0)
3101 temp_x_y_vars.emplace_back(d_a->name());
3102 else if(axis_x_count == 1 && axis_y_count == 1 && axis_z_count == 1 && axis_t_count == 0)
3103 temp_x_y_z_vars.emplace_back(d_a->name());
3104 else if(axis_x_count == 1 && axis_y_count == 1 && axis_z_count == 0 && axis_t_count == 1)
3105 temp_x_y_t_vars.emplace_back(d_a->name());
3106 else if(axis_x_count == 1 && axis_y_count == 1 && axis_z_count == 1 && axis_t_count == 1)
3107 temp_x_y_z_t_vars.emplace_back(d_a->name());
3108 }
3109 else if(ret_value == false)
3110 break;
3111 }
3112 }
3113 }
3114 }
3115#if 0
3116//cerr<<"obtain: after loop "<<endl;
3117#endif
3118
3119 if (ret_value == true) {
3120 if(FoCovJsonRequestHandler::get_may_ignore_z_axis()== true) {
3121
3122#if 0
3123cerr<<"coming to ignore mode "<<endl;
3124cerr<<"axis_var_z_count: "<<axis_var_z_count <<endl;
3125cerr<<"axis_var_t_count: "<<axis_var_t_count <<endl;
3126#endif
3127
3128 // Select the common factor of (x,y),(x,y,z),(x,y,t),(x,y,z,t) among variables
3129 // If having vars that only holds x,y; these vars are only vars that will appear at the final coverage.
3130 if(axis_var_z_count <=1 && axis_var_t_count <=1) {
3131
3132 for (unsigned i = 0; i <temp_x_y_vars.size(); i++)
3133 par_vars.emplace_back(temp_x_y_vars[i]);
3134 for (unsigned i = 0; i <temp_x_y_t_vars.size(); i++)
3135 par_vars.emplace_back(temp_x_y_t_vars[i]);
3136
3137 if (temp_x_y_vars.empty()) {
3138 for (unsigned i = 0; i <temp_x_y_z_vars.size(); i++)
3139 par_vars.emplace_back(temp_x_y_z_vars[i]);
3140 for (unsigned i = 0; i <temp_x_y_z_t_vars.size(); i++)
3141 par_vars.emplace_back(temp_x_y_z_t_vars[i]);
3142
3143 }
3144 else {
3145 // Ignore the (x,y,z) and (x,y,z,t) when (x,y) exists.
3146 // We also need to ignore the z-axis TODO,we may need to support multiple verical coordinates. !
3147 if (axis_var_z_count == 1) {
3148 axisVar_z.name="";
3149 axisVar_z.dim_name = "";
3150 axisVar_z.bound_name = "";
3151 }
3152 }
3153 }
3154 else if (axis_var_z_count >1 && axis_var_t_count <=1) {
3155 //Cover all variables that have (x,y) or (x,y,t)
3156 for (unsigned i = 0; i <temp_x_y_vars.size(); i++)
3157 par_vars.emplace_back(temp_x_y_vars[i]);
3158 for (unsigned i = 0; i <temp_x_y_t_vars.size(); i++)
3159 par_vars.emplace_back(temp_x_y_t_vars[i]);
3160 }
3161 else if (axis_var_z_count <=1 && axis_var_t_count >1) {
3162 //Cover all variables that have (x,y) or (x,y,z)
3163 for (unsigned i = 0; i <temp_x_y_vars.size(); i++)
3164 par_vars.emplace_back(temp_x_y_vars[i]);
3165 for (unsigned i = 0; i <temp_x_y_z_vars.size(); i++)
3166 par_vars.emplace_back(temp_x_y_z_vars[i]);
3167 }
3168 else {
3169 // Select the common factor of (x,y),(x,y,z),(x,y,t),(x,y,z,t) among variables
3170 // If having vars that only holds x,y; these vars are only vars that will appear at the final coverage.
3171 for (unsigned i = 0; i <temp_x_y_vars.size(); i++)
3172 par_vars.emplace_back(temp_x_y_vars[i]);
3173 }
3174 }
3175 else {
3176#if 0
3177cerr<<"coming to strict mode "<<endl;
3178#endif
3179 if(axis_var_z_count >1 || axis_var_t_count >1)
3180 ret_value = false;
3181 else {
3182 //Cover all variables that have (x,y) or (x,y,z) or (x,y,t) or (x,y,z,t)
3183 for (unsigned i = 0; i <temp_x_y_vars.size(); i++)
3184 par_vars.emplace_back(temp_x_y_vars[i]);
3185 for (unsigned i = 0; i <temp_x_y_z_vars.size(); i++)
3186 par_vars.emplace_back(temp_x_y_z_vars[i]);
3187 for (unsigned i = 0; i <temp_x_y_t_vars.size(); i++)
3188 par_vars.emplace_back(temp_x_y_t_vars[i]);
3189 for (unsigned i = 0; i <temp_x_y_z_t_vars.size(); i++)
3190 par_vars.emplace_back(temp_x_y_z_t_vars[i]);
3191 }
3192 }
3193
3194#if 0
3195cerr<<"Parameter Names: "<<endl;
3196for(unsigned i = 0; i <par_vars.size(); i++)
3197 cerr<<par_vars[i]<<endl;
3198#endif
3199
3200
3201 if(par_vars.size() == 0)
3202 ret_value = false;
3203
3204 }
3205 return ret_value;
3206
3207}
3208
3209bool FoDapCovJsonTransform::obtain_valid_vars_dap4(libdap::D4Group *d4g, short axis_var_z_count, short axis_var_t_count ) {
3210
3211#if 0
3212//cerr<<"coming to obtain_valid_vars "<<endl;
3213#endif
3214 bool ret_value = true;
3215 std::vector<std::string> temp_x_y_vars;
3216 std::vector<std::string> temp_x_y_z_vars;
3217 std::vector<std::string> temp_x_y_t_vars;
3218 std::vector<std::string> temp_x_y_z_t_vars;
3219
3220 for (auto vi = d4g->var_begin(), ve = d4g->var_end(); vi != ve; ++vi) {
3221
3222 if ((*vi)->send_p()) {
3223
3224 libdap::BaseType *v = *vi;
3225 libdap::Type type = v->type();
3226
3227 if (type == libdap::dods_array_c) {
3228
3229 auto d_a = dynamic_cast<libdap::Array *>(v);
3230 int d_ndims = d_a->dimensions();
3231
3232 if(d_ndims >=2) {
3233
3234 short axis_x_count = 0;
3235 short axis_y_count = 0;
3236 short axis_z_count = 0;
3237 short axis_t_count = 0;
3238 bool non_xyzt_dim = false;
3239 bool supported_var = true;
3240
3241 libdap::Array::Dim_iter di = d_a->dim_begin();
3242 libdap::Array::Dim_iter de = d_a->dim_end();
3243
3244 for (; di != de; di++) {
3245 // check x,y,z,t dimensions
3246 if((d_a->dimension_size(di,true) == axisVar_x.dim_size) &&
3247 (d_a->dimension_name(di) == axisVar_x.dim_name))
3248 axis_x_count++;
3249 else if((d_a->dimension_size(di,true) == axisVar_y.dim_size) &&
3250 (d_a->dimension_name(di) == axisVar_y.dim_name))
3251 axis_y_count++;
3252 else if((d_a->dimension_size(di,true) == axisVar_z.dim_size) &&
3253 (d_a->dimension_name(di) == axisVar_z.dim_name))
3254 axis_z_count++;
3255 else if((d_a->dimension_size(di,true) == axisVar_t.dim_size) &&
3256 (d_a->dimension_name(di) == axisVar_t.dim_name))
3257 axis_t_count++;
3258 else
3259 non_xyzt_dim = true;
3260
3261 // Non-x,y,z,t dimension or duplicate x,y,z,t dimensions are not supported.
3262 // Here for the "strict" case, I need to return false for the conversion to grid when
3263 // a non-conform > 1D var appears except the "bound" variables.
3264 if(non_xyzt_dim || axis_x_count >1 || axis_y_count >1 || axis_z_count >1 || axis_t_count >1) {
3265 supported_var = false;
3266#if 0
3267cerr<<"Obtain: d_a->name() is "<<d_a->name() <<endl;
3268#endif
3269 if (FoCovJsonRequestHandler::get_may_ignore_z_axis() == false) {
3270 if(d_a->name()!=axisVar_x.bound_name && d_a->name()!=axisVar_y.bound_name &&
3271 d_a->name()!=axisVar_z.bound_name && d_a->name()!=axisVar_t.bound_name)
3272 ret_value = false;
3273 }
3274 break;
3275 }
3276 }
3277
3278 if(supported_var) {
3279 // save the var names to the vars that hold (x,y),(x,y,z),(x,y,t),(x,y,z,t)
3280 if(axis_x_count == 1 && axis_y_count == 1 && axis_z_count == 0 && axis_t_count == 0)
3281 temp_x_y_vars.emplace_back(d_a->name());
3282 else if(axis_x_count == 1 && axis_y_count == 1 && axis_z_count == 1 && axis_t_count == 0)
3283 temp_x_y_z_vars.emplace_back(d_a->name());
3284 else if(axis_x_count == 1 && axis_y_count == 1 && axis_z_count == 0 && axis_t_count == 1)
3285 temp_x_y_t_vars.emplace_back(d_a->name());
3286 else if(axis_x_count == 1 && axis_y_count == 1 && axis_z_count == 1 && axis_t_count == 1)
3287 temp_x_y_z_t_vars.emplace_back(d_a->name());
3288 }
3289 else if(ret_value == false)
3290 break;
3291 }
3292 }
3293 }
3294 }
3295#if 0
3296//cerr<<"obtain: after loop "<<endl;
3297#endif
3298
3299 if (ret_value == true) {
3300 if(FoCovJsonRequestHandler::get_may_ignore_z_axis()== true) {
3301
3302#if 0
3303cerr<<"coming to ignore mode "<<endl;
3304cerr<<"axis_var_z_count: "<<axis_var_z_count <<endl;
3305cerr<<"axis_var_t_count: "<<axis_var_t_count <<endl;
3306#endif
3307
3308 // Select the common factor of (x,y),(x,y,z),(x,y,t),(x,y,z,t) among variables
3309 // If having vars that only holds x,y; these vars are only vars that will be in the final output.
3310 if(axis_var_z_count <=1 && axis_var_t_count <=1) {
3311
3312 for (const auto &txy_var:temp_x_y_vars)
3313 par_vars.emplace_back(txy_var);
3314 for (const auto &txyt_var:temp_x_y_t_vars)
3315 par_vars.emplace_back(txyt_var);
3316
3317 if (temp_x_y_vars.empty()) {
3318 for (const auto &txyz_var:temp_x_y_z_vars)
3319 par_vars.emplace_back(txyz_var);
3320 for (const auto &txyzt_var:temp_x_y_z_t_vars)
3321 par_vars.emplace_back(txyzt_var);
3322
3323 }
3324 else {
3325 // Ignore the (x,y,z) and (x,y,z,t) when (x,y) exists.
3326 // We also need to ignore the z-axis TODO,we may need to support multiple verical coordinates. !
3327 if (axis_var_z_count == 1) {
3328 axisVar_z.name="";
3329 axisVar_z.dim_name = "";
3330 axisVar_z.bound_name = "";
3331 }
3332 }
3333 }
3334 else if (axis_var_z_count >1 && axis_var_t_count <=1) {
3335 //Cover all variables that have (x,y) or (x,y,t)
3336 for (const auto &txy_var:temp_x_y_vars)
3337 par_vars.emplace_back(txy_var);
3338 for (const auto &txyt_var:temp_x_y_t_vars)
3339 par_vars.emplace_back(txyt_var);
3340 }
3341 else if (axis_var_z_count <=1 && axis_var_t_count >1) {
3342 //Cover all variables that have (x,y) or (x,y,z)
3343 for (const auto &txy_var:temp_x_y_vars)
3344 par_vars.emplace_back(txy_var);
3345 for (const auto &txyz_var:temp_x_y_z_vars)
3346 par_vars.emplace_back(txyz_var);
3347 }
3348 else {
3349 // Select the common factor of (x,y),(x,y,z),(x,y,t),(x,y,z,t) among variables
3350 // If having vars that only holds x,y; these vars are only vars that will appear at the final coverage.
3351 for (const auto &txy_var:temp_x_y_vars)
3352 par_vars.emplace_back(txy_var);
3353 }
3354 }
3355 else {
3356#if 0
3357cerr<<"coming to strict mode "<<endl;
3358#endif
3359 if(axis_var_z_count >1 || axis_var_t_count >1)
3360 ret_value = false;
3361 else {
3362 //Cover all variables that have (x,y) or (x,y,z) or (x,y,t) or (x,y,z,t)
3363 for (const auto &txy_var:temp_x_y_vars)
3364 par_vars.emplace_back(txy_var);
3365 for (const auto &txyz_var:temp_x_y_z_vars)
3366 par_vars.emplace_back(txyz_var);
3367 for (const auto &txyt_var:temp_x_y_t_vars)
3368 par_vars.emplace_back(txyt_var);
3369 for (const auto &txyzt_var:temp_x_y_z_t_vars)
3370 par_vars.emplace_back(txyzt_var);
3371 }
3372 }
3373
3374#if 0
3375cerr<<"Parameter Names: "<<endl;
3376for(unsigned i = 0; i <par_vars.size(); i++)
3377 cerr<<par_vars[i]<<endl;
3378#endif
3379
3380
3381 if (par_vars.empty() == true)
3382 ret_value = false;
3383
3384 }
3385 return ret_value;
3386
3387}
3388
3389
3390std::string FoDapCovJsonTransform::cf_time_to_greg(long long time_val) {
3391
3392 tm ycf_1;
3393
3394 // Here obtain the cf_time from the axis_t_units.
3395 string cf_time= axis_t_units ;
3396
3397 // Check the time unit,day,hour, minute or second.
3398 short time_unit_length = -1;
3399 if(cf_time.compare(0,3,"day") == 0)
3400 time_unit_length = 0;
3401 else if(cf_time.compare(0,4,"hour") == 0)
3402 time_unit_length = 1;
3403 else if(cf_time.compare(0,6,"minute") == 0)
3404 time_unit_length = 2;
3405 else if(cf_time.compare(0,6,"second") == 0)
3406 time_unit_length = 3;
3407
3408#if 0
3409//cerr<<"time_unit_length is "<<time_unit_length <<endl;
3410#endif
3411
3412 // Remove any commonly found words from the origin timestamp
3413 vector<string> subStrs = { "days", "day", "hours", "hour", "minutes", "minute",
3414 "seconds", "second", "since", " " };
3415
3416 for(unsigned int i = 0; i < subStrs.size(); i++)
3417 focovjson::removeSubstring(cf_time, subStrs[i]);
3418
3419#if 0
3420//cerr<<"cf_time stripped is "<<cf_time <<endl;
3421#endif
3422
3423 // Separate the date from the hms.
3424 size_t cf_time_space_pos = cf_time.find(' ');
3425 string cf_date,cf_hms;
3426
3427 if(cf_time_space_pos!=string::npos) {
3428 cf_date= cf_time.substr(0,cf_time_space_pos);
3429 cf_hms = cf_time.substr(cf_time_space_pos+1);
3430 }
3431 // If without hours/minutes/seconds, we need to set them to 0.
3432 if(cf_hms==" " || cf_hms=="")
3433 cf_hms ="00:00:00";
3434
3435#if 0
3436cerr<<"cf_date is "<<cf_date <<endl;
3437cerr<<"cf_hms is "<<cf_hms <<endl;
3438#endif
3439
3440 // We need to obtain year,month,date,hour,minute and second
3441 // of the time.
3442
3443 string cf_y,cf_mo,cf_d;
3444 size_t cf_date_dash_pos = cf_date.find('-');
3445 if(cf_date_dash_pos !=string::npos) {
3446 string cf_md;
3447 cf_y = cf_date.substr(0,cf_date_dash_pos);
3448 cf_md = cf_date.substr(cf_date_dash_pos+1);
3449 size_t cf_md_dash_pos = cf_md.find("-");
3450 if(cf_md_dash_pos !=string::npos) {
3451 cf_mo = cf_md.substr(0,cf_md_dash_pos);
3452 cf_d = cf_md.substr(cf_md_dash_pos+1);
3453 }
3454 }
3455
3456 string cf_h,cf_ms,cf_m,cf_s;
3457 size_t cf_hms_colon_pos = cf_hms.find(':');
3458 if(cf_hms_colon_pos !=string::npos) {
3459 cf_h = cf_hms.substr(0,cf_hms_colon_pos);
3460 cf_ms = cf_hms.substr(cf_hms_colon_pos+1);
3461 size_t cf_ms_colon_pos = cf_ms.find(":");
3462 if(cf_ms_colon_pos !=string::npos) {
3463 cf_m = cf_ms.substr(0,cf_ms_colon_pos);
3464 cf_s = cf_ms.substr(cf_ms_colon_pos+1);
3465 }
3466 }
3467
3468
3469#if 0
3470cerr<<"cf_y is "<<cf_y <<endl;
3471cerr<<"cf_mo is "<<cf_mo <<endl;
3472cerr<<"cf_d is "<<cf_d <<endl;
3473
3474cerr<<"cf_h is "<<cf_h <<endl;
3475cerr<<"cf_m is "<<cf_m <<endl;
3476cerr<<"cf_s is "<<cf_s <<endl;
3477#endif
3478
3479 // We need to convert the time from string to integer.
3480 int cf_y_i,cf_mo_i,cf_d_i,cf_h_i,cf_m_i,cf_s_i;
3481 cf_y_i = stoi(cf_y);
3482 cf_mo_i = stoi(cf_mo);
3483 cf_d_i = stoi(cf_d);
3484 cf_h_i = stoi(cf_h);
3485 cf_m_i = stoi(cf_m);
3486 cf_s_i = stoi(cf_s);
3487
3488#if 0
3489cerr<<"cf_y_i " <<cf_y_i <<endl;
3490cerr<<"cf_mo_i " <<cf_mo_i <<endl;
3491cerr<<"cf_d_i " <<cf_d_i <<endl;
3492cerr<<"cf_h_i " <<cf_h_i <<endl;
3493cerr<<"cf_m_i " <<cf_m_i <<endl;
3494cerr<<"cf_s_i " <<cf_s_i <<endl;
3495#endif
3496
3497 // Now we want to assign these time info to struct tm
3498 // Note: the mktime() and localtime() may only work for the date after 1970.
3499 // This should be sufficient for the data we serve now.
3500 ycf_1.tm_hour = cf_h_i; ycf_1.tm_min = cf_m_i; ycf_1.tm_sec = cf_s_i;
3501 ycf_1.tm_year = cf_y_i-1900; ycf_1.tm_mon = cf_mo_i; ycf_1.tm_mday = cf_d_i;
3502#if 0
3503 //time_t t_ycf_1 = mktime(&ycf_1);
3504#endif
3505 time_t t_ycf_1 = timegm(&ycf_1);
3506
3507#if 0
3508cerr<<"t_ycf_1 is "<<t_ycf_1 <<endl;
3509cerr<<"time_val is "<<time_val <<endl;
3510#endif
3511
3512 //time_val = 11046060;
3513 // Here is the value to calculate the new time. We need to convert them to seconds.
3514 //double val = 1.000000000001;
3515 time_t t_ycf_2 ;
3516 // Here we need to convert days, hours, minutes to seconds
3517 if(time_unit_length == 0)
3518 t_ycf_2 = t_ycf_1 + 86400*time_val;
3519 else if (time_unit_length == 1)
3520 t_ycf_2 = t_ycf_1 + 3600*time_val;
3521 else if (time_unit_length == 2)
3522 t_ycf_2 = t_ycf_1 + 60*time_val;
3523 else if (time_unit_length == 3)
3524 t_ycf_2 = t_ycf_1 + time_val;
3525
3526#if 0
3527 //time_t t_ycf_2 = t_ycf_1 + 86340;
3528//cerr<<"t_ycf_2 is "<<t_ycf_2 <<endl;
3529#endif
3530
3531 // jhrg 2/2/24 struct tm *t_new_ycf;
3532 struct tm temp_new_ycf{};
3533 // The use of localtime() is to calculate the time based on the CF time unit.
3534 // So the value actually represents the GMT time.
3535 // Note: we didn't consider the use of local time in the CF.
3536 // Our currently supported product uses GMT. Will consider the other cases later.
3537#if 0
3538 //t_new_ycf = localtime(&t_ycf_2);
3539 //t_new_ycf = gmtime(&t_ycf_2);
3540#endif
3541 auto t_new_ycf = gmtime_r(&t_ycf_2, &temp_new_ycf);
3542
3543#if 0
3544cerr<< "t_new_ycf.tm_year is " <<t_new_ycf->tm_year <<endl;
3545cerr<< "t_new_ycf.tm_mon is " <<t_new_ycf->tm_mon <<endl;
3546cerr<< "t_new_ycf.tm_day is " <<t_new_ycf->tm_mday <<endl;
3547cerr<< "t_new_ycf.tm_hour is " <<t_new_ycf->tm_hour <<endl;
3548cerr<< "t_new_ycf.tm_min is " <<t_new_ycf->tm_min <<endl;
3549cerr<< "t_new_ycf.tm_sec is " <<t_new_ycf->tm_sec <<endl;
3550#endif
3551 if(t_new_ycf->tm_mon == 0) {
3552 t_new_ycf->tm_year--;
3553 t_new_ycf->tm_mon = 12;
3554 }
3555 // Now, we need to change the time from int to string.
3556 string covjson_mon = (t_new_ycf->tm_mon<10)?
3557 ("0"+to_string(t_new_ycf->tm_mon)):
3558 to_string(t_new_ycf->tm_mon);
3559 string covjson_mday = (t_new_ycf->tm_mday<10)?
3560 ("0"+to_string(t_new_ycf->tm_mday)):
3561 to_string(t_new_ycf->tm_mday);
3562
3563 string covjson_hour = (t_new_ycf->tm_hour<10)?
3564 ("0"+to_string(t_new_ycf->tm_hour)):
3565 to_string(t_new_ycf->tm_hour);
3566
3567 string covjson_min = (t_new_ycf->tm_min<10)?
3568 ("0"+to_string(t_new_ycf->tm_min)):
3569 to_string(t_new_ycf->tm_min);
3570
3571 string covjson_sec = (t_new_ycf->tm_sec<10)?
3572 ("0"+to_string(t_new_ycf->tm_sec)):
3573 to_string(t_new_ycf->tm_sec);
3574
3575
3576 // This is the final time.
3577 string covjson_time = to_string(1900+t_new_ycf->tm_year)+"-"+
3578 covjson_mon+"-"+covjson_mday+"T"+
3579 covjson_hour+":"+covjson_min+":"+
3580 covjson_sec+"Z";
3581
3582 return covjson_time;
3583}
3584
3585void FoDapCovJsonTransform::print_bound(ostream *strm, const std::vector<std::string> & t_bnd_val, const std::string & indent, bool is_t_axis) const {
3586
3587 if(axisVar_t.bound_name !="") {
3588 std::string print_values;
3589 if(t_bnd_val.size() >0) {
3590 print_values = "\"bounds\": [";
3591 for(unsigned i = 0; i <t_bnd_val.size(); i++) {
3592 string tmpString = t_bnd_val[i];
3593
3594 if (is_t_axis) {
3595 print_values +="\"";
3596 print_values +=focovjson::escape_for_covjson(tmpString);
3597 print_values +="\"";
3598 }
3599 else
3600 print_values +=tmpString;
3601
3602 if(i !=(t_bnd_val.size()-1))
3603 print_values +=", ";
3604
3605
3606 }
3607 print_values += "]";
3608 }
3609 else
3610 print_values= "\"bounds\": []";
3611 *strm << indent << print_values <<endl;
3612 }
3613
3614}
3615
3616bool FoDapCovJsonTransform::check_geo_dap2_grid(libdap::DDS *dds, const vector<string> &dap2_grid_map_names) const {
3617
3618 libdap::DDS::Vars_iter vi = dds->var_begin();
3619 libdap::DDS::Vars_iter ve = dds->var_end();
3620
3621 bool has_lat = false;
3622 bool has_lon = false;
3623 bool ret_value = false;
3624
3625 for (; vi != ve; vi++) {
3626
3627 if ((*vi)->send_p()) {
3628
3629 libdap::BaseType *v = *vi;
3630 libdap::Type type = v->type();
3631
3632 if (type == libdap::dods_array_c) {
3633
3634 for (const auto &map_name:dap2_grid_map_names) {
3635 if (v->name() == map_name) {
3636 auto d_a = dynamic_cast<libdap::Array *>(v);
3637 short lat_or_lon = check_cf_unit_attr(d_a);
3638 if (lat_or_lon == 1)
3639 has_lat = true;
3640 else if (lat_or_lon == 2)
3641 has_lon = true;
3642 break;
3643 }
3644 }
3645 }
3646
3647 if (has_lat && has_lon) {
3648 ret_value = true;
3649 break;
3650 }
3651 }
3652 }
3653
3654 return ret_value;
3655
3656}
3657
3658short FoDapCovJsonTransform::check_cf_unit_attr(libdap::Array *d_a) const {
3659
3660 short ret_value = 0;
3661
3662 // The map must be 1-D array.
3663 if (d_a->dimensions() == 1) {
3664
3665 libdap::AttrTable &attrs = d_a->get_attr_table();
3666 unsigned int num_attrs = attrs.get_size();
3667
3668 if (num_attrs) {
3669
3670 string lat_unit = "degrees_north";
3671 string lon_unit = "degrees_east";
3672
3673 libdap::AttrTable::Attr_iter i = attrs.attr_begin();
3674 libdap::AttrTable::Attr_iter e = attrs.attr_end();
3675
3676 for (; i != e; i++) {
3677
3678 string attr_name = attrs.get_name(i);
3679#if 0
3680//cerr<<"attr_name is "<<attr_name <<endl;
3681#endif
3682 unsigned int num_vals = attrs.get_attr_num(i);
3683
3684 if (num_vals == 1) {
3685
3686 string units_name ="units";
3687 // Check if the attr_name is units.
3688 bool is_attr_units = false;
3689 if ((attr_name.size() == units_name.size())
3690 && (attr_name.compare(units_name) == 0))
3691 is_attr_units = true;
3692 if ((is_attr_units == false) &&
3693 (attr_name.size() == (units_name.size()+1) &&
3694 attr_name[units_name.size()] == '\0' &&
3695 attr_name.compare(0,units_name.size(),units_name) ==0))
3696 is_attr_units = true;
3697
3698 if (is_attr_units) {
3699
3700 string val = attrs.get_attr(i,0);
3701 if (val.compare(0,lat_unit.size(),lat_unit) == 0)
3702 ret_value = 1;
3703 else if (val.compare(0,lon_unit.size(),lon_unit) == 0)
3704 ret_value = 2;
3705 if (ret_value !=0)
3706 break;
3707
3708 }
3709 }
3710 }
3711 }
3712 }
3713 return ret_value;
3714
3715}
exception thrown if internal error encountered
static bool endsWith(std::string const &fullString, std::string const &ending)
Definition BESUtil.cc:837
static void conditional_timeout_cancel()
Checks if the timeout alarm should be canceled based on the value of the BES key BES....
Definition BESUtil.cc:898
FoDapCovJsonTransform(libdap::DDS *dds)
Get the CovJSON encoding for a DDS.
virtual void dump(std::ostream &strm) const
Dumps information about this transformation object for debugging purposes.
static RequestServiceTimer * TheTimer()
Return a pointer to a singleton timer instance. If an instance does not exist it will create and init...
void throw_if_timeout_expired(const std::string &message, const std::string &file, const int line)
Checks the RequestServiceTimer to determine if the time spent servicing the request at this point has...
STL class.
STL class.