bes Updated for version 3.20.13
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//
14// This library is free software; you can redistribute it and/or
15// modify it under the terms of the GNU Lesser General Public
16// License as published by the Free Software Foundation; either
17// version 2.1 of the License, or (at your option) any later version.
18//
19// This library is distributed in the hope that it will be useful,
20// but WITHOUT ANY WARRANTY; without even the implied warranty of
21// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22// Lesser General Public License for more details.
23//
24// You should have received a copy of the GNU Lesser General Public
25// License along with this library; if not, write to the Free Software
26// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
27//
28
29#include "config.h"
30
31#include <cassert>
32#include <sstream>
33#include <iostream>
34#include <fstream>
35#include <stddef.h>
36#include <string>
37#include <cstring>
38#include <typeinfo>
39#include <iomanip> // setprecision
40#include <sstream> // stringstream
41#include <vector>
42#include <ctime>
43#include <time.h>
44
45using std::ostringstream;
46using std::istringstream;
47
48#define MODULE "covj"
49#define prolog string("FoDapCovJsonTransform::").append(__func__).append("() - ")
50
51#include <libdap/DDS.h>
52#include <libdap/Structure.h>
53#include <libdap/Constructor.h>
54#include <libdap/Array.h>
55#include <libdap/Grid.h>
56#include <libdap/Sequence.h>
57#include <libdap/Float64.h>
58#include <libdap/Str.h>
59#include <libdap/Url.h>
60
61#include <BESDebug.h>
62#include <BESInternalError.h>
63#include <DapFunctionUtils.h>
64#include <RequestServiceTimer.h>
65#include "FoDapCovJsonTransform.h"
66#include "focovjson_utils.h"
67#include "FoCovJsonRequestHandler.h"
68
69using std::map;
70#define FoDapCovJsonTransform_debug_key "focovjson"
71
72
73bool FoDapCovJsonTransform::canConvert()
74{
75 // If x, y, z, and t all exist
76 // We are assuming the following is true:
77 // - shapeVals[0] = x axis
78 // - shapeVals[1] = y axis
79 // - shapeVals[2] = z axis
80 // - shapeVals[3] = t axis
81#if 0
82cerr<<"Before X and Y and Z and T"<<endl;
83cerr<<"Number of parameters is "<<this->parameters.size() <<endl;
84cerr<<"shapeVals is "<<shapeVals.size() <<endl;
85cerr<<"Number of Axis is "<<this->axes.size() <<endl;
86for (int i = 0; i <this->axes.size(); i++) {
87cerr<<"Axis name is "<<this->axes[i]->name << endl;
88cerr<<"Axis value is "<<this->axes[i]->values << endl;
89
90}
91
92#endif
93
94 if(true == is_simple_cf_geographic) {
95 domainType = "Grid";
96 return true;
97 }
98 else
99 return false;
100
101 // The following code is commented out for the time being.
102 // We currently only support the simple CF geographic projection for the time being.
103 // We will enhance this module to support the other cases in the new tickets.
104 // KY 2022-06-10
105#if 0
106 if(xExists && yExists && zExists && tExists) {
107
108 if (shapeVals.size() < 4)
109 return false;
110
111 // A domain with Grid domain type MUST have the axes "x" and "y"
112 // and MAY have the axes "z" and "t".
113 if((shapeVals[0] > 1) && (shapeVals[1] > 1) && (shapeVals[2] >= 1) && (shapeVals[3] >= 0)) {
114 domainType = "Grid";
115 return true;
116 }
117
118 // A domain with VerticalProfile domain type MUST have the axes "x",
119 // "y", and "z", where "x" and "y" MUST have a single coordinate only.
120 else if((shapeVals[0] == 1) && (shapeVals[1] == 1) && (shapeVals[2] >= 1) && ((shapeVals[3] <= 1) && (shapeVals[3] >= 0))) {
121 domainType = "Vertical Profile";
122 return true;
123 }
124
125 // A domain with PointSeries domain type MUST have the axes "x", "y",
126 // and "t" where "x" and "y" MUST have a single coordinate only. A
127 // domain with PointSeries domain type MAY have the axis "z" which
128 // MUST have a single coordinate only.
129 else if((shapeVals[0] == 1) && (shapeVals[1] == 1) && (shapeVals[2] == 1) && (shapeVals[3] >= 0)) {
130 domainType = "Point Series";
131 return true;
132 }
133
134 // A domain with Point domain type MUST have the axes "x" and "y" and MAY
135 // have the axes "z" and "t" where all MUST have a single coordinate only.
136 else if((shapeVals[0] == 1) && (shapeVals[1] == 1) && (shapeVals[2] == 1) && (shapeVals[3] == 1)) {
137 domainType = "Point";
138 return true;
139 }
140//cerr<<"Before X and Y and T"<<endl;
141 }
142
143 // If just x, y, and t exist
144 // We are assuming the following is true:
145 // - shapeVals[0] = x axis
146 // - shapeVals[1] = y axis
147 // - shapeVals[2] = t axis
148 else if(xExists && yExists && !zExists && tExists) {
149
150 if (shapeVals.size() < 3)
151 return false;
152
153#if 0
154//cerr <<"shapeVals[0] is "<< shapeVals[0] <<endl;
155//cerr <<"shapeVals[1] is "<< shapeVals[1] <<endl;
156//cerr <<"shapeVals[2] is "<< shapeVals[2] <<endl;
157#endif
158
159 // A domain with Grid domain type MUST have the axes "x" and "y"
160 // and MAY have the axes "z" and "t".
161 // The issue here is that shapeVals[0], shapeVals[1],shapeVals[2] may not be exactly x,y,z/t.
162 if((shapeVals[0] >= 1) && (shapeVals[1] >= 1) && (shapeVals[2] >= 0)) {
163 domainType = "Grid";
164 return true;
165 }
166
167 // A domain with PointSeries domain type MUST have the axes "x", "y",
168 // and "t" where "x" and "y" MUST have a single coordinate only. A
169 // domain with PointSeries domain type MAY have the axis "z" which
170 // MUST have a single coordinate only.
171 else if((shapeVals[0] == 1) && (shapeVals[1] == 1) && (shapeVals[2] >= 0)) {
172 domainType = "Point Series";
173 return true;
174 }
175
176 // A domain with Point domain type MUST have the axes "x" and "y" and MAY
177 // have the axes "z" and "t" where all MUST have a single coordinate only.
178 else if((shapeVals[0] == 1) && (shapeVals[1] == 1) && (shapeVals[2] == 1)) {
179 domainType = "Point";
180 return true;
181 }
182//cerr<<"Before X and Y "<<endl;
183 }
184
185 // If just x and y exist
186 // We are assuming the following is true:
187 // - shapeVals[0] = x axis
188 // - shapeVals[1] = y axis
189 else if(xExists && yExists && !zExists && !tExists) {
190
191 if (shapeVals.size() < 2)
192 return false;
193
194 // A domain with Grid domain type MUST have the axes "x" and "y"
195 // and MAY have the axes "z" and "t".
196 if((shapeVals[0] > 1) && (shapeVals[1] > 1)) {
197 domainType = "Grid";
198 return true;
199 }
200
201 // A domain with Point domain type MUST have the axes "x" and "y" and MAY
202 // have the axes "z" and "t" where all MUST have a single coordinate only.
203 else if((shapeVals[0] == 1) && (shapeVals[1] == 1)) {
204 domainType = "Point";
205 return true;
206 }
207 }
208//cerr<<"Coming to the last step."<<endl;
209
210 return false; // This source DDS is not valid as CovJSON
211
212#endif
213}
214
215template<typename T>
216unsigned int FoDapCovJsonTransform::covjsonSimpleTypeArrayWorker(ostream *strm, T *values, unsigned int indx,
217 vector<unsigned int> *shape, unsigned int currentDim, bool is_axis_t_sgeo,libdap::Type a_type)
218{
219 unsigned int currentDimSize = (*shape)[currentDim];
220
221 // FOR TESTING AND DEBUGGING PURPOSES
222 // *strm << "\"currentDim\": \"" << currentDim << "\"" << endl;
223 // *strm << "\"currentDimSize\": \"" << currentDimSize << "\"" << endl;
224
225 for(unsigned int i = 0; i < currentDimSize; i++) {
226 if(currentDim < shape->size() - 1) {
227 BESDEBUG(FoDapCovJsonTransform_debug_key,
228 "covjsonSimpleTypeArrayWorker() - Recursing! indx: " << indx << " currentDim: " << currentDim << " currentDimSize: " << currentDimSize << endl);
229 indx = covjsonSimpleTypeArrayWorker<T>(strm, values, indx, shape, currentDim + 1,is_axis_t_sgeo,a_type);
230 if(i + 1 != currentDimSize) {
231 *strm << ", ";
232 }
233 }
234 else {
235 if(i) {
236 *strm << ", ";
237 }
238 if(typeid(T) == typeid(string)) {
239 // Strings need to be escaped to be included in a CovJSON object.
240 string val = reinterpret_cast<string*>(values)[indx++];
241 *strm << "\"" << focovjson::escape_for_covjson(val) << "\"";
242 }
243 else {
244 // We need to convert CF time to greg time.
245 if(is_axis_t_sgeo) {
246 string axis_t_value;
247 std::ostringstream tmp_stream;
248 long long tmp_value = 0;
249 switch (a_type) {
250 case libdap::dods_byte_c: {
251 unsigned char tmp_byte_value = reinterpret_cast<unsigned char *>(values)[indx++];
252 tmp_value = (long long) tmp_byte_value;
253 break;
254 }
255
256 case libdap::dods_uint16_c: {
257 unsigned short tmp_uint16_value = reinterpret_cast<unsigned short *>(values)[indx++];
258 tmp_value = (long long) tmp_uint16_value;
259 break;
260 }
261
262 case libdap::dods_int16_c: {
263 short tmp_int16_value = reinterpret_cast<short *>(values)[indx++];
264 tmp_value = (long long) tmp_int16_value;
265 break;
266 }
267
268 case libdap::dods_uint32_c: {
269 unsigned int tmp_uint_value = reinterpret_cast<unsigned int *>(values)[indx++];
270 tmp_value = (long long) tmp_uint_value;
271 break;
272 }
273
274 case libdap::dods_int32_c: {
275 int tmp_int_value = reinterpret_cast<int *>(values)[indx++];
276 tmp_value = (long long) tmp_int_value;
277 break;
278 }
279
280 case libdap::dods_float32_c: {
281 float tmp_float_value = reinterpret_cast<float *>(values)[indx++];
282 // In theory, it may cause overflow. In reality, the time in seconds will never be that large.
283 tmp_value = (long long) tmp_float_value;
284 break;
285 }
286
287 case libdap::dods_float64_c: {
288 double tmp_double_value = reinterpret_cast<double *>(values)[indx++];
289 // In theory, it may cause overflow. In reality, the time in seconds will never be that large.
290 tmp_value = (long long) tmp_double_value;
291 break;
292 }
293
294 default:
295 throw BESInternalError("Attempt to extract CF time information from an invalid source", __FILE__, __LINE__);
296 }
297
298
299 axis_t_value = cf_time_to_greg(tmp_value);
300#if 0
301 cerr<<"time value is " <<axis_t_value <<endl;
302 cerr<<"CF time unit is "<<axis_t_units <<endl;
303#endif
304 *strm << "\"" << focovjson::escape_for_covjson(axis_t_value) << "\"";
305 }
306 else
307 *strm << values[indx++];
308 }
309 }
310 }
311
312 return indx;
313}
314
315template<typename T>
316void FoDapCovJsonTransform::covjsonSimpleTypeArray(ostream *strm, libdap::Array *a, string indent, bool sendData)
317{
318 string childindent = indent + _indent_increment;
319 bool axisRetrieved = false;
320 bool parameterRetrieved = false;
321
322 currDataType = a->var()->type_name();
323
324 // FOR TESTING AND DEBUGGING PURPOSES
325 //*strm << "\"type_name\": \"" << a->var()->type_name() << "\"" << endl;
326 //*strm << "\"name\": \"" << a->var()->name() << "\"" << endl;
327
328 getAttributes(strm, a->get_attr_table(), a->name(), &axisRetrieved, &parameterRetrieved);
329
330 //a->print_val(*strm, "\n", true); // For testing purposes
331
332 // sendData = false; // For testing purposes
333
334 // If we are dealing with an Axis
335 if((axisRetrieved == true) && (parameterRetrieved == false)) {
336 struct Axis *currAxis;
337 currAxis = axes[axisCount - 1];
338
339 int numDim = a->dimensions(true);
340 vector<unsigned int> shape(numDim);
341 long length = focovjson::computeConstrainedShape(a, &shape);
342
343 // FOR TESTING AND DEBUGGING PURPOSES
344 // *strm << "\"numDimensions\": \"" << numDim << "\"" << endl;
345 // *strm << "\"length\": \"" << length << "\"" << endl << endl;
346
347 bool handle_axis_t_here = true;
348 if(!is_simple_cf_geographic && currAxis->name.compare("t") == 0)
349 handle_axis_t_here = false;
350 if (handle_axis_t_here) {
351 //if (currAxis->name.compare("t") != 0) {
352 if (sendData) {
353 currAxis->values += "\"values\": [";
354 unsigned int indx = 0;
355 vector<T> src(length);
356 a->value(src.data());
357
358 ostringstream astrm;
359 bool is_time_axis_for_sgeo = false;
360 if(is_simple_cf_geographic && currAxis->name.compare("t") == 0)
361 is_time_axis_for_sgeo = true;
362
363
364 indx = covjsonSimpleTypeArrayWorker(&astrm, src.data(), 0, &shape, 0,is_time_axis_for_sgeo,a->var()->type());
365 currAxis->values += astrm.str();
366
367 currAxis->values += "]";
368
369//cerr<<"currAxis value at covjsonSimpleTypeArray is "<<currAxis->values <<endl;
370 if (length != indx) {
371 BESDEBUG(FoDapCovJsonTransform_debug_key,
372 "covjsonSimpleTypeArray(Axis) - indx NOT equal to content length! indx: " << indx << " length: " << length << endl);
373 }
374 assert(length == indx);
375 }
376 else {
377 currAxis->values += "\"values\": []";
378 }
379 }
380 }
381
382 // If we are dealing with a Parameter
383 else if(axisRetrieved == false && parameterRetrieved == true) {
384 struct Parameter *currParameter;
385 currParameter = parameters[parameterCount - 1];
386
387//cerr<<"Parameter name is "<< currParameter->name<<endl;
388 int numDim = a->dimensions(true);
389 vector<unsigned int> shape(numDim);
390 long length = focovjson::computeConstrainedShape(a, &shape);
391
392 // FOR TESTING AND DEBUGGING PURPOSES
393 // *strm << "\"numDimensions\": \"" << a->dimensions(true) << "\"" << endl;
394 // *strm << "\"length\": \"" << length << "\"" << endl << endl;
395
396 currParameter->shape += "\"shape\": [";
397 for(vector<unsigned int>::size_type i = 0; i < shape.size(); i++) {
398 if(i > 0) {
399 currParameter->shape += ", ";
400 }
401
402 // Process the shape's values, which are strings,
403 // convert them into integers, and store them
404 ostringstream otemp;
405 istringstream itemp;
406 int tempVal = 0;
407 otemp << shape[i];
408 istringstream (otemp.str());
409 istringstream (otemp.str()) >> tempVal;
410//cerr<<"tempVal is "<<tempVal <<endl;
411 shapeVals.push_back(tempVal);
412
413 // t may only have 1 value: the origin timestamp
414 // DANGER: t may not yet be defined
415 if((i == 0) && tExists && is_simple_cf_geographic == false) {
416 //if((i == 0) && tExists) {
417 currParameter->shape += "1";
418 }
419 else {
420 currParameter->shape += otemp.str();
421 }
422 }
423 currParameter->shape += "],";
424
425 if (sendData) {
426 currParameter->values += "\"values\": [";
427 unsigned int indx = 0;
428 vector<T> src(length);
429 a->value(src.data());
430
431 ostringstream pstrm;
432 indx = covjsonSimpleTypeArrayWorker(&pstrm, src.data(), 0, &shape, 0,false,a->var()->type());
433 currParameter->values += pstrm.str();
434
435 currParameter->values += "]";
436
437 if (length != indx) {
438 BESDEBUG(FoDapCovJsonTransform_debug_key,
439 "covjsonSimpleTypeArray(Parameter) - indx NOT equal to content length! indx: " << indx << " length: " << length << endl);
440 }
441 assert(length == indx);
442 }
443 else {
444 currParameter->values += "\"values\": []";
445 }
446 }
447}
448
449void FoDapCovJsonTransform::covjsonStringArray(ostream *strm, libdap::Array *a, string indent, bool sendData)
450{
451 string childindent = indent + _indent_increment;
452 bool axisRetrieved = false;
453 bool parameterRetrieved = false;
454
455 currDataType = a->var()->type_name();
456
457 // FOR TESTING AND DEBUGGING PURPOSES
458 // *strm << "\"attr_tableName\": \"" << a->name() << "\"" << endl;
459
460 // FOR TESTING AND DEBUGGING PURPOSES
461 // *strm << "\"type_name\": \"" << a->var()->type_name() << "\"" << endl;
462
463 getAttributes(strm, a->get_attr_table(), a->name(), &axisRetrieved, &parameterRetrieved);
464
465 // a->print_val(*strm, "\n", true); // For testing purposes
466
467 // sendData = false; // For testing purposes
468
469 // If we are dealing with an Axis
470 if((axisRetrieved == true) && (parameterRetrieved == false)) {
471 struct Axis *currAxis;
472 currAxis = axes[axisCount - 1];
473
474 int numDim = a->dimensions(true);
475 vector<unsigned int> shape(numDim);
476 long length = focovjson::computeConstrainedShape(a, &shape);
477
478 bool handle_axis_t_here = true;
479 if(!is_simple_cf_geographic && currAxis->name.compare("t") == 0)
480 handle_axis_t_here = false;
481 if (handle_axis_t_here) {
482 //if (currAxis->name.compare("t") != 0) {
483 if (sendData) {
484 currAxis->values += "\"values\": ";
485 unsigned int indx = 0;
486 // The string type utilizes a specialized version of libdap:Array.value()
487 vector<string> sourceValues;
488 a->value(sourceValues);
489
490 ostringstream astrm;
491 indx = covjsonSimpleTypeArrayWorker(&astrm, (string *) (sourceValues.data()), 0, &shape, 0,false,a->var()->type());
492 currAxis->values += astrm.str();
493
494 if (length != indx) {
495 BESDEBUG(FoDapCovJsonTransform_debug_key,
496 "covjsonStringArray(Axis) - indx NOT equal to content length! indx: " << indx << " length: " << length << endl);
497 }
498 assert(length == indx);
499 }
500 else {
501 currAxis->values += "\"values\": []";
502 }
503 }
504 }
505
506 // If we are dealing with a Parameter
507 else if(axisRetrieved == false && parameterRetrieved == true) {
508 struct Parameter *currParameter;
509 currParameter = parameters[parameterCount - 1];
510
511 int numDim = a->dimensions(true);
512 vector<unsigned int> shape(numDim);
513 long length = focovjson::computeConstrainedShape(a, &shape);
514
515 currParameter->shape += "\"shape\": [";
516 for(vector<unsigned int>::size_type i = 0; i < shape.size(); i++) {
517 if(i > 0) {
518 currParameter->shape += ", ";
519 }
520
521 // Process the shape's values, which are strings,
522 // convert them into integers, and store them
523 ostringstream otemp;
524 istringstream itemp;
525 int tempVal = 0;
526 otemp << shape[i];
527 istringstream (otemp.str());
528 istringstream (otemp.str()) >> tempVal;
529 shapeVals.push_back(tempVal);
530
531 // t may only have 1 value: the origin timestamp
532 // DANGER: t may not yet be defined
533 if((i == 0) && tExists && is_simple_cf_geographic == false) {
534 //if((i == 0) && tExists) {
535 currParameter->shape += "1";
536 }
537 else {
538 currParameter->shape += otemp.str();
539 }
540 }
541 currParameter->shape += "],";
542
543 if (sendData) {
544 currParameter->values += "\"values\": ";
545 unsigned int indx = 0;
546 // The string type utilizes a specialized version of libdap:Array.value()
547 vector<string> sourceValues;
548 a->value(sourceValues);
549
550 ostringstream pstrm;
551 indx = covjsonSimpleTypeArrayWorker(&pstrm, (string *) (sourceValues.data()), 0, &shape, 0,false,a->var()->type());
552 currParameter->values += pstrm.str();
553
554 if (length != indx) {
555 BESDEBUG(FoDapCovJsonTransform_debug_key,
556 "covjsonStringArray(Parameter) - indx NOT equal to content length! indx: " << indx << " length: " << length << endl);
557 }
558 assert(length == indx);
559 }
560 else {
561 currParameter->values += "\"values\": []";
562 }
563 }
564}
565
566void FoDapCovJsonTransform::addAxis(string name, string values)
567{
568 struct Axis *newAxis = new Axis;
569
570 newAxis->name = name;
571 newAxis->values = values;
572#if 0
573cerr << "axis name is "<< name <<endl;
574cerr << "axis value is "<< values <<endl;
575#endif
576 this->axes.push_back(newAxis);
577 this->axisCount++;
578}
579
580void FoDapCovJsonTransform::addParameter(string id, string name, string type, string dataType, string unit,
581 string longName, string standardName, string shape, string values)
582{
583 struct Parameter *newParameter = new Parameter;
584
585 newParameter->id = id;
586 newParameter->name = name;
587 newParameter->type = type;
588 newParameter->dataType = dataType;
589 newParameter->unit = unit;
590 newParameter->longName = longName;
591 newParameter->standardName = standardName;
592 newParameter->shape = shape;
593 newParameter->values = values;
594
595 this->parameters.push_back(newParameter);
596 this->parameterCount++;
597}
598
599void FoDapCovJsonTransform::getAttributes(ostream *strm, libdap::AttrTable &attr_table, string name,
600 bool *axisRetrieved, bool *parameterRetrieved)
601{
602 string currAxisName;
603 string currAxisTimeOrigin;
604 string currUnit;
605 string currLongName;
606 string currStandardName;
607
608 isAxis = false;
609 isParam = false;
610
611 *axisRetrieved = false;
612 *parameterRetrieved = false;
613
614 // FOR TESTING AND DEBUGGING PURPOSES
615 //*strm << "\"attr_tableName\": \"" << name << "\"" << endl;
616
617
618 if(is_simple_cf_geographic) {
619 getAttributes_simple_cf_geographic(strm,attr_table,name,axisRetrieved,parameterRetrieved);
620
621 }
622 else {
623 // Using CF-1.6 naming conventions -- Also checks for Coads Climatology conventions
624 // http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html
625 if((name.compare("lon") == 0) || (name.compare("LON") == 0)
626 || (name.compare("longitude") == 0) || (name.compare("LONGITUDE") == 0)
627 || (name.compare("COADSX") == 0)) {
628
629 // FOR TESTING AND DEBUGGING PURPOSES
630 // *strm << "\"Found X-Axis\": \"" << name << "\"" << endl;
631
632 if(!xExists) {
633 xExists = true;
634 isAxis = true;
635 currAxisName = "x";
636 }
637 }
638 else if((name.compare("lat") == 0) || (name.compare("LAT") == 0)
639 || (name.compare("latitude") == 0) || (name.compare("LATITUDE") == 0)
640 || (name.compare("COADSY") == 0)) {
641
642 // FOR TESTING AND DEBUGGING PURPOSES
643 // *strm << "\"Found Y-Axis\": \"" << name << "\"" << endl;
644
645 if(!yExists) {
646 yExists = true;
647 isAxis = true;
648 currAxisName = "y";
649 }
650 }
651 else if((name.compare("lev") == 0) || (name.compare("LEV") == 0)
652 || (name.compare("height") == 0) || (name.compare("HEIGHT") == 0)
653 || (name.compare("depth") == 0) || (name.compare("DEPTH") == 0)
654 || (name.compare("pres") == 0) || (name.compare("PRES") == 0)) {
655
656 // FOR TESTING AND DEBUGGING PURPOSES
657 // *strm << "\"Found Z-Axis\": \"" << name << "\"" << endl;
658
659 if(!zExists) {
660 zExists = true;
661 isAxis = true;
662 currAxisName = "z";
663 }
664 }
665 else if((name.compare("time") == 0) || (name.compare("TIME") == 0)) {
666
667 // FOR TESTING AND DEBUGGING PURPOSES
668 // *strm << "\"Found T-Axis\": \"" << name << "\"" << endl;
669
670 if(!tExists) {
671 tExists = true;
672 isAxis = true;
673 currAxisName = "t";
674 }
675 }
676 else {
677 isParam = true;
678 }
679
680 // Only do more if there are actually attributes in the table
681 if(attr_table.get_size() != 0) {
682 libdap::AttrTable::Attr_iter begin = attr_table.attr_begin();
683 libdap::AttrTable::Attr_iter end = attr_table.attr_end();
684
685 for(libdap::AttrTable::Attr_iter at_iter = begin; at_iter != end; at_iter++) {
686 // FOR TESTING AND DEBUGGING PURPOSES
687 // attr_table.print(*strm);
688
689 switch (attr_table.get_attr_type(at_iter)) {
690 case libdap::Attr_container: {
691 libdap::AttrTable *atbl = attr_table.get_attr_table(at_iter);
692 // Recursive call for child attribute table
693 getAttributes(strm, *atbl, name, axisRetrieved, parameterRetrieved);
694 break;
695 }
696 default: {
697 vector<string> *values = attr_table.get_attr_vector(at_iter);
698
699 for(vector<string>::size_type i = 0; i < values->size(); i++) {
700 string currName = attr_table.get_name(at_iter);
701 string currValue = (*values)[i];
702
703 // FOR TESTING AND DEBUGGING PURPOSES
704 //*strm << "\"currName\": \"" << currName << "\", \"currValue\": \"" << currValue << "\"" << endl;
705
706 // From Climate and Forecast (CF) Conventions:
707 // http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#_description_of_the_data
708
709 // We continue to support the use of the units and long_name attributes as defined in COARDS.
710 // We extend COARDS by adding the optional standard_name attribute which is used to provide unique
711 // identifiers for variables. This is important for data exchange since one cannot necessarily
712 // identify a particular variable based on the name assigned to it by the institution that provided
713 // the data.
714
715 // The standard_name attribute can be used to identify variables that contain coordinate data. But since it is an
716 // optional attribute, applications that implement these standards must continue to be able to identify coordinate
717 // types based on the COARDS conventions.
718
719 // See http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
720 if(currName.compare("units") == 0) {
721 currUnit = currValue;
722
723 if(isAxis) {
724 if(currAxisName.compare("t") == 0) {
725 currAxisTimeOrigin = currValue;
726 }
727 }
728 }
729
730 // Per Jon Blower:
731 // observedProperty->label comes from:
732 // - The CF long_name, if it exists
733 // - If not, the CF standard_name, perhaps with underscores removed
734 // - If the standard_name doesn’t exist, use the variable ID
735 // See http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#long-name
736 else if(currName.compare("long_name") == 0) {
737 currLongName = currValue;
738 }
739 // See http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#standard-name
740 else if(currName.compare("standard_name") == 0) {
741 currStandardName = currValue;
742 }
743 }
744
745 break;
746 }
747 }
748 }
749 }
750
751 if(isAxis) {
752 // If we're dealing with the time axis, capture the time origin
753 // timestamp value with the appropriate formatting for printing.
754 // @TODO See https://covjson.org/spec/#temporal-reference-systems
755 if(currAxisName.compare("t") == 0) {
756 addAxis(currAxisName, "\"values\": [\"" + sanitizeTimeOriginString(currAxisTimeOrigin) + "\"]");
757 }
758 else {
759//cerr<<"addAxis 1 "<<endl;
760 addAxis(currAxisName, "");
761 }
762//cerr<<"currAxisName is "<<currAxisName <<endl;
763//cerr<<"currUnit is "<<currUnit <<endl;
764
765 // See https://covjson.org/spec/#projected-coordinate-reference-systems
766 // KENT: The below "if block" is wrong. If the units of lat/lon includes east, north, it may be geographic projection.
767 // The ProjectedCRS may imply the 2-D lat/lon. If the variable name is the same as the axis name, and the lat/lon
768 // are 1-D, this is a geographic system.
769 if((currUnit.find("east") != string::npos) || (currUnit.find("East") != string::npos) ||
770 (currUnit.find("north") != string::npos) || (currUnit.find("North") != string::npos)) {
771 coordRefType = "ProjectedCRS";
772 }
773
774 *axisRetrieved = true;
775 }
776 else if(isParam) {
777 addParameter("", name, "", currDataType, currUnit, currLongName, currStandardName, "", "");
778 *parameterRetrieved = true;
779 }
780 else {
781 // Do nothing
782 }
783 }
784}
785
786void FoDapCovJsonTransform::
787 getAttributes_simple_cf_geographic(ostream *strm, libdap::AttrTable &attr_table, string name,
788 bool *axisRetrieved, bool *parameterRetrieved)
789{
790 string currAxisName;
791 string currUnit;
792 string currLongName;
793 string currStandardName;
794
795 isAxis = false;
796 isParam = false;
797
798 *axisRetrieved = false;
799 *parameterRetrieved = false;
800
801 // FOR TESTING AND DEBUGGING PURPOSES
802 //*strm << "\"attr_tableName\": \"" << name << "\"" << endl;
803
804 if(axisVar_x.name == name) {
805 // FOR TESTING AND DEBUGGING PURPOSES
806 // *strm << "\"Found X-Axis\": \"" << name << "\"" << endl;
807
808 if(!xExists) {
809 xExists = true;
810 isAxis = true;
811 currAxisName = "x";
812 }
813 }
814 else if(axisVar_y.name == name) {
815 // FOR TESTING AND DEBUGGING PURPOSES
816 // *strm << "\"Found Y-Axis\": \"" << name << "\"" << endl;
817
818 if(!yExists) {
819 yExists = true;
820 isAxis = true;
821 currAxisName = "y";
822 }
823 }
824 else if(axisVar_z.name == name) {
825 // FOR TESTING AND DEBUGGING PURPOSES
826 // *strm << "\"Found Z-Axis\": \"" << name << "\"" << endl;
827
828 if(!zExists) {
829 zExists = true;
830 isAxis = true;
831 currAxisName = "z";
832 }
833 }
834 else if(axisVar_t.name == name) {
835 // FOR TESTING AND DEBUGGING PURPOSES
836 // *strm << "\"Found T-Axis\": \"" << name << "\"" << endl;
837
838 if(!tExists) {
839 tExists = true;
840 isAxis = true;
841 currAxisName = "t";
842 }
843 }
844 else {
845 // TODO: We should manage to remove this loop when improving the whole module.
846 for (unsigned int i = 0; i <par_vars.size(); i++) {
847 if(par_vars[i] == name){
848 isParam = true;
849 break;
850 }
851 }
852 }
853
854 // Only do more if there are actually attributes in the table
855 if(attr_table.get_size() != 0) {
856 libdap::AttrTable::Attr_iter begin = attr_table.attr_begin();
857 libdap::AttrTable::Attr_iter end = attr_table.attr_end();
858
859 for(libdap::AttrTable::Attr_iter at_iter = begin; at_iter != end; at_iter++) {
860 // FOR TESTING AND DEBUGGING PURPOSES
861 // attr_table.print(*strm);
862
863 switch (attr_table.get_attr_type(at_iter)) {
864 case libdap::Attr_container: {
865 libdap::AttrTable *atbl = attr_table.get_attr_table(at_iter);
866 // Recursive call for child attribute table
867 getAttributes_simple_cf_geographic(strm, *atbl, name, axisRetrieved, parameterRetrieved);
868 break;
869 }
870 default: {
871 vector<string> *values = attr_table.get_attr_vector(at_iter);
872
873 for(vector<string>::size_type i = 0; i < values->size(); i++) {
874 string currName = attr_table.get_name(at_iter);
875 string currValue = (*values)[i];
876
877 // FOR TESTING AND DEBUGGING PURPOSES
878 //*strm << "\"currName\": \"" << currName << "\", \"currValue\": \"" << currValue << "\"" << endl;
879
880 // From Climate and Forecast (CF) Conventions:
881 // http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#_description_of_the_data
882
883 // We continue to support the use of the units and long_name attributes as defined in COARDS.
884 // We extend COARDS by adding the optional standard_name attribute which is used to provide unique
885 // identifiers for variables. This is important for data exchange since one cannot necessarily
886 // identify a particular variable based on the name assigned to it by the institution that provided
887 // the data.
888
889 // The standard_name attribute can be used to identify variables that contain coordinate data. But since it is an
890 // optional attribute, applications that implement these standards must continue to be able to identify coordinate
891 // types based on the COARDS conventions.
892
893 // See http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
894 if(currName.compare("units") == 0)
895 currUnit = currValue;
896
897 // Per Jon Blower:
898 // observedProperty->label comes from:
899 // - The CF long_name, if it exists
900 // - If not, the CF standard_name, perhaps with underscores removed
901 // - If the standard_name doesn’t exist, use the variable ID
902 // See http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#long-name
903 else if(currName.compare("long_name") == 0) {
904 currLongName = currValue;
905 }
906 // See http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#standard-name
907 else if(currName.compare("standard_name") == 0) {
908 currStandardName = currValue;
909 }
910 }
911
912 break;
913 }
914 }
915 }
916 }
917
918 if(isAxis) {
919 addAxis(currAxisName, "");
920//cerr<<"currAxisName is "<<currAxisName <<endl;
921//cerr<<"currUnit is "<<currUnit <<endl;
922 if(currAxisName.compare("t") == 0)
923 axis_t_units = currUnit;
924 *axisRetrieved = true;
925 }
926 else if(isParam) {
927 addParameter("", name, "", currDataType, currUnit, currLongName, currStandardName, "", "");
928 *parameterRetrieved = true;
929 }
930 else {
931 // Do nothing
932 }
933
934}
935
936
937
938
939string FoDapCovJsonTransform::sanitizeTimeOriginString(string timeOrigin)
940{
941 // If the calendar is based on years, months, days,
942 // then the referenced values SHOULD use one of the
943 // following ISO8601-based lexical representations:
944
945 // YYYY
946 // ±XYYYY (where X stands for extra year digits)
947 // YYYY-MM
948 // YYYY-MM-DD
949 // YYYY-MM-DDTHH:MM:SS[.F]Z where Z is either “Z”
950 // or a time scale offset + -HH:MM
951 // ex: "2018-01-01T00:12:20Z"
952
953 // If calendar dates with reduced precision are
954 // used in a lexical representation (e.g. "2016"),
955 // then a client SHOULD interpret those dates in
956 // that reduced precision.
957
958 // Remove any commonly found words from the origin timestamp
959 vector<string> subStrs = { "hours", "hour", "minutes", "minute",
960 "seconds", "second", "since", " " };
961
962 string cleanTimeOrigin = timeOrigin;
963
964 // If base time, use an arbitrary base time string
965 if(timeOrigin.find("base_time") != string::npos) {
966 cleanTimeOrigin = "2020-01-01T12:00:00Z";
967 }
968 else {
969 for(unsigned int i = 0; i < subStrs.size(); i++)
970 focovjson::removeSubstring(cleanTimeOrigin, subStrs[i]);
971 }
972
973 return cleanTimeOrigin;
974}
975
977 _dds(dds), _returnAs(""), _indent_increment(" "), atomicVals(""), currDataType(""), domainType("Unknown"),
978 coordRefType("GeographicCRS"), xExists(false), yExists(false), zExists(false), tExists(false), isParam(false),
979 isAxis(false), canConvertToCovJson(false), axisCount(0), parameterCount(0),is_simple_cf_geographic(false)
980{
981 if (!_dds) throw BESInternalError("File out COVJSON, null DDS passed to constructor", __FILE__, __LINE__);
982}
983
984void FoDapCovJsonTransform::dump(ostream &strm) const
985{
986 strm << BESIndent::LMarg << "FoDapCovJsonTransform::dump - (" << (void *) this << ")" << endl;
987 BESIndent::Indent();
988 if(_dds != 0) {
989 _dds->print(strm);
990 }
991 BESIndent::UnIndent();
992}
993
994void FoDapCovJsonTransform::transform(ostream &ostrm, bool sendData, bool testOverride)
995{
996 transform(&ostrm, _dds, "", sendData, testOverride);
997}
998
999void FoDapCovJsonTransform::transform(ostream *strm, libdap::Constructor *cnstrctr, string indent, bool sendData)
1000{
1001 vector<libdap::BaseType *> leaves;
1002 vector<libdap::BaseType *> nodes;
1003 // Sort the variables into two sets
1004 libdap::DDS::Vars_iter vi = cnstrctr->var_begin();
1005 libdap::DDS::Vars_iter ve = cnstrctr->var_end();
1006
1007//cerr<<"coming to the loop before " <<endl;
1008
1009 for(; vi != ve; vi++) {
1010 if((*vi)->send_p()) {
1011 libdap::BaseType *v = *vi;
1012 libdap::Type type = v->type();
1013
1014 if(type == libdap::dods_array_c) {
1015 type = v->var()->type();
1016 }
1017 if(v->is_constructor_type() || (v->is_vector_type() && v->var()->is_constructor_type())) {
1018 nodes.push_back(v);
1019 }
1020 else {
1021 leaves.push_back(v);
1022 }
1023 }
1024 }
1025
1026 transformNodeWorker(strm, leaves, nodes, indent, sendData);
1027}
1028
1029void FoDapCovJsonTransform::transformNodeWorker(ostream *strm, vector<libdap::BaseType *> leaves,
1030 vector<libdap::BaseType *> nodes, string indent, bool sendData)
1031{
1032 // Get this node's leaves
1033 for(vector<libdap::BaseType *>::size_type l = 0; l < leaves.size(); l++) {
1034 libdap::BaseType *v = leaves[l];
1035 BESDEBUG(FoDapCovJsonTransform_debug_key, "Processing LEAF: " << v->name() << endl);
1036
1037 // FOR TESTING AND DEBUGGING PURPOSES
1038 // *strm << "Processing LEAF: " << v->name() << endl;
1039
1040 transform(strm, v, indent + _indent_increment, sendData);
1041 }
1042
1043 // Get this node's child nodes
1044 for(vector<libdap::BaseType *>::size_type n = 0; n < nodes.size(); n++) {
1045 libdap::BaseType *v = nodes[n];
1046 BESDEBUG(FoDapCovJsonTransform_debug_key, "Processing NODE: " << v->name() << endl);
1047
1048 // FOR TESTING AND DEBUGGING PURPOSES
1049 // *strm << "Processing NODE: " << v->name() << endl;
1050
1051 transform(strm, v, indent + _indent_increment, sendData);
1052 }
1053}
1054
1055void FoDapCovJsonTransform::printCoverageJSON(ostream *strm, string indent, bool testOverride)
1056{
1057 // Determine if the attribute values we read can be converted to CovJSON.
1058 // Test override forces printing output to stream regardless of whether
1059 // or not the file can be converted into CoverageJSON format.
1060 if(testOverride) {
1061 canConvertToCovJson = true;
1062 }
1063 else {
1064 canConvertToCovJson = canConvert();
1065 }
1066
1067 // Only print if this file can be converted to CovJSON
1068 if(canConvertToCovJson) {
1069 // Prints the entire Coverage to stream
1070//cerr<< "Can convert to CovJSON "<<endl;
1071 printCoverage(strm, indent);
1072 }
1073 else {
1074 // If this file can't be converted, then its failing spatial/temporal requirements
1075 throw BESInternalError("File cannot be converted to CovJSON format due to missing or incompatible spatial dimensions", __FILE__, __LINE__);
1076 }
1077}
1078
1079void FoDapCovJsonTransform::printCoverage(ostream *strm, string indent)
1080{
1081 string child_indent1 = indent + _indent_increment;
1082 string child_indent2 = child_indent1 + _indent_increment;
1083
1084 BESDEBUG(FoDapCovJsonTransform_debug_key, "Printing COVERAGE" << endl);
1085
1086 *strm << indent << "{" << endl;
1087 *strm << child_indent1 << "\"type\": \"Coverage\"," << endl;
1088
1089 printDomain(strm, child_indent1);
1090
1091 printParameters(strm, child_indent1);
1092
1093 printRanges(strm, child_indent1);
1094
1095 *strm << indent << "}" << endl;
1096}
1097
1098void FoDapCovJsonTransform::printDomain(ostream *strm, string indent)
1099{
1100 string child_indent1 = indent + _indent_increment;
1101
1102 BESDEBUG(FoDapCovJsonTransform_debug_key, "Printing DOMAIN" << endl);
1103
1104 *strm << indent << "\"domain\": {" << endl;
1105 *strm << child_indent1 << "\"type\" : \"Domain\"," << endl;
1106 *strm << child_indent1 << "\"domainType\": \"" + domainType + "\"," << endl;
1107
1108 // Prints the axes metadata and range values
1109 printAxes(strm, child_indent1);
1110
1111 // Prints the references for the given Axes
1112 printReference(strm, child_indent1);
1113
1114 *strm << indent << "}," << endl;
1115}
1116
1117void FoDapCovJsonTransform::printAxes(ostream *strm, string indent)
1118{
1119 string child_indent1 = indent + _indent_increment;
1120 string child_indent2 = child_indent1 + _indent_increment;
1121
1122 BESDEBUG(FoDapCovJsonTransform_debug_key, "Printing AXES" << endl);
1123
1124 // Obtain bound values if having them
1125 // First handle the t-axis because of GLADS
1126 std::vector<std::string> t_bnd_val;
1127 if(axisVar_t_bnd_val.size() >0) {
1128 t_bnd_val.resize(axisVar_t_bnd_val.size());
1129 for (unsigned i = 0; i < axisVar_t_bnd_val.size();i++) {
1130 t_bnd_val[i] = cf_time_to_greg((long long)(axisVar_t_bnd_val[i]));
1131#if 0
1132//cerr<<"time bound value is "<<t_bnd_val[i] <<endl;
1133#endif
1134 }
1135 }
1136
1137 // bound for x-axis
1138 std::vector<std::string> x_bnd_val;
1139 if(axisVar_x_bnd_val.empty() == false) {
1140 x_bnd_val.resize(axisVar_x_bnd_val.size());
1141 for (unsigned i = 0; i < axisVar_x_bnd_val.size();i++) {
1142 ostringstream temp_strm;
1143 temp_strm<<axisVar_x_bnd_val[i];
1144 x_bnd_val[i] = temp_strm.str();
1145#if 0
1146//cerr<<"X bound value is "<<x_bnd_val[i] <<endl;
1147#endif
1148 }
1149 }
1150
1151 // bound for y-axis
1152 std::vector<std::string> y_bnd_val;
1153 if(axisVar_y_bnd_val.empty() == false) {
1154 y_bnd_val.resize(axisVar_y_bnd_val.size());
1155 for (unsigned i = 0; i < axisVar_y_bnd_val.size();i++) {
1156 ostringstream temp_strm;
1157 temp_strm<<axisVar_y_bnd_val[i];
1158 y_bnd_val[i] = temp_strm.str();
1159#if 0
1160//cerr<<"Y bound value is "<<y_bnd_val[i] <<endl;
1161#endif
1162 }
1163 }
1164
1165 // bound for z-axis
1166 std::vector<std::string> z_bnd_val;
1167 if(axisVar_z_bnd_val.empty() == false) {
1168 z_bnd_val.resize(axisVar_z_bnd_val.size());
1169 for (unsigned i = 0; i < axisVar_z_bnd_val.size();i++) {
1170 ostringstream temp_strm;
1171 temp_strm<<axisVar_z_bnd_val[i];
1172 z_bnd_val[i] = temp_strm.str();
1173#if 0
1174//cerr<<"Z bound value is "<<z_bnd_val[i] <<endl;
1175#endif
1176 }
1177 }
1178
1179 // FOR TESTING AND DEBUGGING PURPOSES
1180 // *strm << "\"type_name\": \"" << a->var()->type_name() << "\"" << endl;
1181
1182 // Write the axes to strm
1183 *strm << indent << "\"axes\": {" << endl;
1184 for(unsigned int i = 0; i < axisCount; i++) {
1185 for(unsigned int j = 0; j < axisCount; j++) {
1186 // Logic for printing axes in the appropriate order
1187
1188 // If x, y, z, and t all exist (x, y, z, t)
1189 if(xExists && yExists && zExists && tExists) {
1190 if((i == 0) && (axes[j]->name.compare("x") == 0)) {
1191 *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
1192 *strm << child_indent2 << axes[j]->values << endl;
1193 print_bound(strm, x_bnd_val,child_indent2,false);
1194 }
1195 else if((i == 1) && (axes[j]->name.compare("y") == 0)) {
1196 *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
1197 *strm << child_indent2 << axes[j]->values << endl;
1198 print_bound(strm, y_bnd_val,child_indent2,false);
1199 }
1200 else if((i == 2) && (axes[j]->name.compare("z") == 0)) {
1201 *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
1202 *strm << child_indent2 << axes[j]->values << endl;
1203 print_bound(strm, z_bnd_val,child_indent2,false);
1204 }
1205 else if((i == 3) && (axes[j]->name.compare("t") == 0)) {
1206 *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
1207 *strm << child_indent2 << axes[j]->values << endl;
1208 print_bound(strm, t_bnd_val,child_indent2,true);
1209 }
1210 }
1211 // If just x, y, and t exist (x, y, t)
1212 else if(xExists && yExists && !zExists && tExists) {
1213 if((i == 0) && (axes[j]->name.compare("x") == 0)) {
1214 *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
1215 *strm << child_indent2 << axes[j]->values << endl;
1216 print_bound(strm, x_bnd_val,child_indent2,false);
1217 }
1218 else if((i == 1) && (axes[j]->name.compare("y") == 0)) {
1219 *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
1220 *strm << child_indent2 << axes[j]->values << endl;
1221 print_bound(strm, y_bnd_val,child_indent2,false);
1222 }
1223 else if((i == 2) && (axes[j]->name.compare("t") == 0)) {
1224 *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
1225 *strm << child_indent2 << axes[j]->values << endl;
1226 print_bound(strm, t_bnd_val,child_indent2,true);
1227 }
1228 }
1229 // If just x and y exist (x, y)
1230 else if(xExists && yExists && !zExists && !tExists) {
1231 if((i == 0) && (axes[j]->name.compare("x") == 0)) {
1232 *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
1233 *strm << child_indent2 << axes[j]->values << endl;
1234 print_bound(strm, x_bnd_val,child_indent2,false);
1235 }
1236 else if((i == 1) && (axes[j]->name.compare("y") == 0)) {
1237 *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
1238 *strm << child_indent2 << axes[j]->values << endl;
1239 print_bound(strm, y_bnd_val,child_indent2,false);
1240 }
1241 }
1242 }
1243 if(i == axisCount - 1) {
1244 *strm << child_indent1 << "}" << endl;
1245 }
1246 else {
1247 *strm << child_indent1 << "}," << endl;
1248 }
1249 }
1250 *strm << indent << "}," << endl;
1251}
1252
1253void FoDapCovJsonTransform::printReference(ostream *strm, string indent)
1254{
1255 string child_indent1 = indent + _indent_increment;
1256 string child_indent2 = child_indent1 + _indent_increment;
1257 string coordVars;
1258
1259 BESDEBUG(FoDapCovJsonTransform_debug_key, "Printing REFERENCES" << endl);
1260
1261 if(xExists) {
1262 coordVars += "\"x\"";
1263 }
1264
1265 if(yExists) {
1266 if(!coordVars.empty()) {
1267 coordVars += ", ";
1268 }
1269 coordVars += "\"y\"";
1270 }
1271
1272 if(zExists) {
1273 if(!coordVars.empty()) {
1274 coordVars += ", ";
1275 }
1276 coordVars += "\"z\"";
1277 }
1278
1279 *strm << indent << "\"referencing\": [{" << endl;
1280
1281 // See https://covjson.org/spec/#temporal-reference-systems
1282 if(tExists) {
1283 *strm << child_indent1 << "\"coordinates\": [\"t\"]," << endl;
1284 *strm << child_indent1 << "\"system\": {" << endl;
1285 *strm << child_indent2 << "\"type\": \"TemporalRS\"," << endl;
1286 *strm << child_indent2 << "\"calendar\": \"Gregorian\"" << endl;
1287 *strm << child_indent1 << "}" << endl;
1288 *strm << indent << "}," << endl;
1289 *strm << indent << "{" << endl;
1290 }
1291
1292 // See https://covjson.org/spec/#geospatial-coordinate-reference-systems
1293 *strm << child_indent1 << "\"coordinates\": [" << coordVars << "]," << endl;
1294 *strm << child_indent1 << "\"system\": {" << endl;
1295 *strm << child_indent2 << "\"type\": \"" + coordRefType + "\"," << endl;
1296
1297 // Most of the datasets I've seen do not contain a link to a coordinate
1298 // reference system, so I've set some defaults here - CRH 1/2020
1299 if(coordRefType.compare("ProjectedCRS") == 0) {
1300 // Projected Coordinate Reference System (north/east): http://www.opengis.net/def/crs/EPSG/0/27700
1301 *strm << child_indent2 << "\"id\": \"http://www.opengis.net/def/crs/EPSG/0/27700\"" << endl;
1302 }
1303 else {
1304 if(xExists && yExists && zExists) {
1305 // 3-Dimensional Geographic Coordinate Reference System (lat/lon/height): http://www.opengis.net/def/crs/EPSG/0/4979
1306 if(!is_simple_cf_geographic)
1307 *strm << child_indent2 << "\"id\": \"http://www.opengis.net/def/crs/EPSG/0/4979\"" << endl;
1308 }
1309 else {
1310 // 2-Dimensional Geographic Coordinate Reference System (lat/lon): http://www.opengis.net/def/crs/OGC/1.3/CRS84
1311 if(!is_simple_cf_geographic)
1312 *strm << child_indent2 << "\"id\": \"http://www.opengis.net/def/crs/OGC/1.3/CRS84\"" << endl;
1313 }
1314 }
1315
1316 *strm << child_indent1 << "}" << endl;
1317 *strm << indent << "}]" << endl;
1318}
1319
1320void FoDapCovJsonTransform::printParameters(ostream *strm, string indent)
1321{
1322 string child_indent1 = indent + _indent_increment;
1323 string child_indent2 = child_indent1 + _indent_increment;
1324 string child_indent3 = child_indent2 + _indent_increment;
1325 string child_indent4 = child_indent3 + _indent_increment;
1326
1327 BESDEBUG(FoDapCovJsonTransform_debug_key, "Printing PARAMETERS" << endl);
1328
1329 // Write down the parameter metadata
1330 *strm << indent << "\"parameters\": {" << endl;
1331 for(unsigned int i = 0; i < parameterCount; i++) {
1332 *strm << child_indent1 << "\"" << parameters[i]->name << "\": {" << endl;
1333 *strm << child_indent2 << "\"type\": \"Parameter\"," << endl;
1334 *strm << child_indent2 << "\"description\": {" << endl;
1335
1336 if(parameters[i]->longName.compare("") != 0) {
1337 *strm << child_indent3 << "\"en\": \"" << parameters[i]->longName << "\"" << endl;
1338 }
1339 else if(parameters[i]->standardName.compare("") != 0) {
1340 *strm << child_indent3 << "\"en\": \"" << parameters[i]->standardName << "\"" << endl;
1341 }
1342 else {
1343 *strm << child_indent3 << "\"en\": \"" << parameters[i]->name << "\"" << endl;
1344 }
1345
1346 *strm << child_indent2 << "}," << endl;
1347 *strm << child_indent2 << "\"unit\": {" << endl;
1348 *strm << child_indent3 << "\"label\": {" << endl;
1349 *strm << child_indent4 << "\"en\": \"" << parameters[i]->unit << "\"" << endl;
1350 *strm << child_indent3 << "}," << endl;
1351 *strm << child_indent3 << "\"symbol\": {" << endl;
1352 *strm << child_indent4 << "\"value\": \"" << parameters[i]->unit << "\"," << endl;
1353 *strm << child_indent4 << "\"type\": \"http://www.opengis.net/def/uom/UCUM/\"" << endl;
1354 *strm << child_indent3 << "}" << endl;
1355 *strm << child_indent2 << "}," << endl;
1356 *strm << child_indent2 << "\"observedProperty\": {" << endl;
1357
1358 // Per Jon Blower:
1359 // observedProperty->id comes from the CF standard_name,
1360 // mapped to a URI like this: http://vocab.nerc.ac.uk/standard_name/<standard_name>.
1361 // If the standard_name is not present, omit the id.
1362 if(parameters[i]->standardName.compare("") != 0) {
1363 *strm << child_indent3 << "\"id\": \"http://vocab.nerc.ac.uk/standard_name/" << parameters[i]->standardName << "/\"," << endl;
1364 }
1365
1366 // Per Jon Blower:
1367 // observedProperty->label comes from:
1368 // - The CF long_name, if it exists
1369 // - If not, the CF standard_name, perhaps with underscores removed
1370 // - If the standard_name doesn’t exist, use the variable ID
1371 *strm << child_indent3 << "\"label\": {" << endl;
1372
1373 if(parameters[i]->longName.compare("") != 0) {
1374 *strm << child_indent4 << "\"en\": \"" << parameters[i]->longName << "\"" << endl;
1375 }
1376 else if(parameters[i]->standardName.compare("") != 0) {
1377 *strm << child_indent4 << "\"en\": \"" << parameters[i]->standardName << "\"" << endl;
1378 }
1379 else {
1380 *strm << child_indent4 << "\"en\": \"" << parameters[i]->name << "\"" << endl;
1381 }
1382
1383 *strm << child_indent3 << "}" << endl;
1384 *strm << child_indent2 << "}" << endl;
1385
1386 if(i == parameterCount - 1) {
1387 *strm << child_indent1 << "}" << endl;
1388 }
1389 else {
1390 *strm << child_indent1 << "}," << endl;
1391 }
1392 }
1393
1394 *strm << indent << "}," << endl;
1395}
1396
1397void FoDapCovJsonTransform::printRanges(ostream *strm, string indent)
1398{
1399 string child_indent1 = indent + _indent_increment;
1400 string child_indent2 = child_indent1 + _indent_increment;
1401 string child_indent3 = child_indent2 + _indent_increment;
1402 string axisNames;
1403
1404 BESDEBUG(FoDapCovJsonTransform_debug_key, "Printing RANGES" << endl);
1405
1406 if(tExists) {
1407 axisNames += "\"t\"";
1408 }
1409
1410 if(zExists) {
1411 if(!axisNames.empty()) {
1412 axisNames += ", ";
1413 }
1414 axisNames += "\"z\"";
1415 }
1416
1417 if(yExists) {
1418 if(!axisNames.empty()) {
1419 axisNames += ", ";
1420 }
1421 axisNames += "\"y\"";
1422 }
1423
1424 if(xExists) {
1425 if(!axisNames.empty()) {
1426 axisNames += ", ";
1427 }
1428 axisNames += "\"x\"";
1429 }
1430
1431 // Axis name (x, y, or z)
1432 *strm << indent << "\"ranges\": {" << endl;
1433 for(unsigned int i = 0; i < parameterCount; i++) {
1434 string dataType;
1435 // See spec: https://covjson.org/spec/#ndarray-objects
1436 if(parameters[i]->dataType.find("int") == 0 || parameters[i]->dataType.find("Int") == 0
1437 || parameters[i]->dataType.find("integer") == 0 || parameters[i]->dataType.find("Integer") == 0) {
1438 dataType = "integer";
1439 }
1440 else if(parameters[i]->dataType.find("float") == 0 || parameters[i]->dataType.find("Float") == 0) {
1441 dataType = "float";
1442 }
1443 else if(parameters[i]->dataType.find("string") == 0 || parameters[i]->dataType.find("String") == 0) {
1444 dataType = "string";
1445 }
1446 else {
1447 dataType = "string";
1448 }
1449
1450 // @TODO NEEDS REFACTORING FOR BES ISSUE #244
1451 // https://github.com/OPENDAP/bes/issues/244
1452 *strm << child_indent1 << "\"" << parameters[i]->name << "\": {" << endl;
1453 *strm << child_indent2 << "\"type\": \"NdArray\"," << endl;
1454 *strm << child_indent2 << "\"dataType\": \"" << dataType << "\", " << endl;
1455 *strm << child_indent2 << "\"axisNames\": [" << axisNames << "]," << endl;
1456 *strm << child_indent2 << parameters[i]->shape << endl;
1457 *strm << child_indent2 << parameters[i]->values << endl;
1458
1459 if(i == parameterCount - 1) {
1460 *strm << child_indent1 << "}" << endl;
1461 }
1462 else {
1463 *strm << child_indent1 << "}," << endl;
1464 }
1465 }
1466
1467 *strm << indent << "}" << endl;
1468}
1469
1470void FoDapCovJsonTransform::transform(ostream *strm, libdap::DDS *dds, string indent, bool sendData, bool testOverride)
1471{
1472 // Sort the variables into two sets
1473 vector<libdap::BaseType *> leaves;
1474 vector<libdap::BaseType *> nodes;
1475
1476 libdap::DDS::Vars_iter vi = dds->var_begin();
1477 libdap::DDS::Vars_iter ve = dds->var_end();
1478 for(; vi != ve; vi++) {
1479 if((*vi)->send_p()) {
1480 libdap::BaseType *v = *vi;
1481 libdap::Type type = v->type();
1482 if(type == libdap::dods_array_c) {
1483 type = v->var()->type();
1484 }
1485 if(v->is_constructor_type() || (v->is_vector_type() && v->var()->is_constructor_type())) {
1486 nodes.push_back(v);
1487 }
1488 else {
1489 leaves.push_back(v);
1490 }
1491 }
1492 }
1493
1494 if(FoCovJsonRequestHandler::get_simple_geo()) {
1495 // Sort the variables into two sets
1496 vi = dds->var_begin();
1497 ve = dds->var_end();
1498
1499 // Kent: We need to do pre-processing to handle more grid GES DISC stuff.
1500 // First search CF units from 1-D array.
1501
1502 bool has_axis_var_x = false;
1503 short axis_var_x_count = 0;
1504 bool has_axis_var_y = false;
1505 short axis_var_y_count = 0;
1506 bool has_axis_var_z = false;
1507 short axis_var_z_count = 0;
1508 bool has_axis_var_t = false;
1509 short axis_var_t_count = 0;
1510
1511 string units_name ="units";
1512 for(; vi != ve; vi++) {
1513//cerr<<"coming to the loop " <<endl;
1514 if((*vi)->send_p()) {
1515 libdap::BaseType *v = *vi;
1516 libdap::Type type = v->type();
1517
1518 // Check if this qualifies a simple geographic grid coverage
1519 if(type == libdap::dods_array_c) {
1520 libdap::Array * d_a = dynamic_cast<libdap::Array *>(v);
1521 int d_ndims = d_a->dimensions();
1522//cerr<<"d_ndims is "<< d_ndims <<endl;
1523 if(d_ndims == 1) {
1524//cerr<<"d_a name is "<<d_a->name() <<endl;
1525 libdap::AttrTable &attrs = d_a->get_attr_table();
1526 unsigned int num_attrs = attrs.get_size();
1527 if (num_attrs) {
1528 libdap::AttrTable::Attr_iter i = attrs.attr_begin();
1529 libdap::AttrTable::Attr_iter e = attrs.attr_end();
1530 for (; i != e; i++) {
1531 string attr_name = attrs.get_name(i);
1532//cerr<<"attr_name is "<<attr_name <<endl;
1533 unsigned int num_vals = attrs.get_attr_num(i);
1534 if (num_vals == 1) {
1535 // Check if the attr_name is units.
1536 bool is_attr_units = false;
1537 if((attr_name.size() == units_name.size())
1538 && (attr_name.compare(units_name) == 0))
1539 is_attr_units = true;
1540 if(is_attr_units == false)
1541 if(attr_name.size() == (units_name.size()+1) &&
1542 attr_name[units_name.size()] == '\0' &&
1543 attr_name.compare(0,units_name.size(),units_name) ==0)
1544 is_attr_units = true;
1545
1546 if (is_attr_units) {
1547 string val = attrs.get_attr(i,0);
1548 vector<string> unit_candidates;
1549
1550 // Here we need to check if there are 2 latitudes or longitudes.
1551 // If we find this issue, we should mark it. The coverage json won't support this case.
1552 // longitude axis x
1553 unit_candidates.push_back("degrees_east");
1554 has_axis_var_x = check_add_axis(d_a,val,unit_candidates,axisVar_x,false);
1555 if (true == has_axis_var_x) {
1556 axis_var_x_count++;
1557 if (axis_var_x_count ==2)
1558 break;
1559 }
1560 unit_candidates.clear();
1561
1562 // latitude axis y
1563 unit_candidates.push_back("degrees_north");
1564 has_axis_var_y = check_add_axis(d_a,val,unit_candidates,axisVar_y,false);
1565 if (true == has_axis_var_y) {
1566 axis_var_y_count++;
1567 if (axis_var_y_count == 2)
1568 break;
1569 }
1570 unit_candidates.clear();
1571
1572 // height/pressure
1573 unit_candidates.push_back("hpa");
1574 unit_candidates.push_back("hPa");
1575 unit_candidates.push_back("meter");
1576 unit_candidates.push_back("m");
1577 unit_candidates.push_back("km");
1578 has_axis_var_z = check_add_axis(d_a,val,unit_candidates,axisVar_z,false);
1579 if (true == has_axis_var_z) {
1580 axis_var_z_count++;
1581 if (axis_var_z_count == 2)
1582 break;
1583 }
1584 unit_candidates.clear();
1585#if 0
1586for(int i = 0; i <unit_candidates.size(); i++)
1587 cerr<<"unit_candidates[i] is "<<unit_candidates[i] <<endl;
1588#endif
1589
1590 // time: CF units only
1591 unit_candidates.push_back("seconds since ");
1592 unit_candidates.push_back("minutes since ");
1593 unit_candidates.push_back("hours since ");
1594 unit_candidates.push_back("days since ");
1595#if 0
1596for(int i = 0; i <unit_candidates.size(); i++)
1597cerr<<"unit_candidates[i] again is "<<unit_candidates[i] <<endl;
1598#endif
1599
1600 has_axis_var_t = check_add_axis(d_a,val,unit_candidates,axisVar_t,true);
1601 // STOP
1602 if (true == has_axis_var_t) {
1603 axis_var_t_count++;
1604 if (axis_var_t_count == 2)
1605 break;
1606 }
1607 unit_candidates.clear();
1608
1609 }
1610
1611 }
1612 }
1613 }
1614 }
1615 }
1616 }
1617 }
1618#if 0
1619cerr<<"axis_var_x_count is "<< axis_var_x_count <<endl;
1620cerr<<"axis_var_y_count is "<< axis_var_y_count <<endl;
1621cerr<<"axis_var_z_count is "<< axis_var_z_count <<endl;
1622cerr<<"axis_var_t_count is "<< axis_var_t_count <<endl;
1623#endif
1624 bool is_simple_geo_candidate = true;
1625 if(axis_var_x_count !=1 || axis_var_y_count !=1)
1626 is_simple_geo_candidate = false;
1627 // Single coverage for the time being
1628 // make z axis and t axis be empty if multiple z or t.
1629 if(axis_var_z_count > 1) {
1630 axisVar_z.name="";
1631 axisVar_z.dim_name = "";
1632 axisVar_z.bound_name = "";
1633 }
1634 if(axis_var_t_count > 1) {
1635 axisVar_t.name="";
1636 axisVar_t.dim_name = "";
1637 axisVar_t.bound_name = "";
1638 }
1639 if(is_simple_geo_candidate == true) {
1640
1641 // Check bound variables
1642 // Check if any 1-D variable has the "bounds" attribute;
1643 // we need to remember the attribute value and match the variable that
1644 // holds the bound values later. KY 2022-1-21
1645 map<string, string> vname_bname;
1646
1647 check_bounds(dds,vname_bname);
1648
1649 map<string, string>::iterator it;
1650#if 0
1651for(it = vname_bname.begin(); it != vname_bname.end(); it++) {
1652cerr<<it->first <<endl;
1653cerr<<it->second <<endl;
1654}
1655#endif
1656
1657 for(it = vname_bname.begin(); it != vname_bname.end(); it++) {
1658// cerr<<it->first <<endl;
1659// cerr<<it->second <<endl;
1660 if(axisVar_x.name == it->first)
1661 axisVar_x.bound_name = it->second;
1662 else if(axisVar_y.name == it->first)
1663 axisVar_y.bound_name = it->second;
1664 else if(axisVar_z.name == it->first)
1665 axisVar_z.bound_name = it->second;
1666 else if(axisVar_t.name == it->first)
1667 axisVar_t.bound_name = it->second;
1668 }
1669#if 0
1670cerr<<"axisVar_x.name is "<<axisVar_x.name <<endl;
1671cerr<<"axisVar_x.dim_name is "<<axisVar_x.dim_name <<endl;
1672cerr<<"axisVar_x.dim_size is "<<axisVar_x.dim_size <<endl;
1673cerr<<"axisVar_x.bound_name is "<<axisVar_x.bound_name <<endl;
1674
1675cerr<<"axisVar_y.name is "<<axisVar_y.name <<endl;
1676cerr<<"axisVar_y.dim_name is "<<axisVar_y.dim_name <<endl;
1677cerr<<"axisVar_y.dim_size is "<<axisVar_y.dim_size <<endl;
1678cerr<<"axisVar_y.bound_name is "<<axisVar_y.bound_name <<endl;
1679
1680cerr<<"axisVar_z.name is "<<axisVar_z.name <<endl;
1681cerr<<"axisVar_z.dim_name is "<<axisVar_z.dim_name <<endl;
1682cerr<<"axisVar_z.dim_size is "<<axisVar_z.dim_size <<endl;
1683cerr<<"axisVar_z.bound_name is "<<axisVar_z.bound_name <<endl;
1684
1685cerr<<"axisVar_t.name is "<<axisVar_t.name <<endl;
1686cerr<<"axisVar_t.dim_name is "<<axisVar_t.dim_name <<endl;
1687cerr<<"axisVar_t.dim_size is "<<axisVar_t.dim_size <<endl;
1688cerr<<"axisVar_t.bound_name is "<<axisVar_t.bound_name <<endl;
1689#endif
1690
1691
1692 is_simple_cf_geographic = obtain_valid_vars(dds,axis_var_z_count,axis_var_t_count);
1693
1694 if(true == is_simple_cf_geographic) {
1695
1696//cerr<<"this is a simple CF geographic grid we can handle" <<endl;
1697 // We should handle the bound value
1698 // ignore 1-D bound dimension variable,
1699 // Retrieve the values of the 2-D bound variable,
1700 // Will save as the bound value in the coverage
1701
1702 string x_bnd_dim_name;
1703 string y_bnd_dim_name;
1704 string z_bnd_dim_name;
1705 string t_bnd_dim_name;
1706
1707 obtain_bound_values(dds,axisVar_x,axisVar_x_bnd_val, x_bnd_dim_name,sendData);
1708 obtain_bound_values(dds,axisVar_y,axisVar_y_bnd_val, y_bnd_dim_name,sendData);
1709 obtain_bound_values(dds,axisVar_z,axisVar_z_bnd_val, z_bnd_dim_name,sendData);
1710 obtain_bound_values(dds,axisVar_t,axisVar_t_bnd_val, t_bnd_dim_name,sendData);
1711
1712 if(x_bnd_dim_name!="")
1713 bnd_dim_names.push_back(x_bnd_dim_name);
1714 else if(y_bnd_dim_name!="")
1715 bnd_dim_names.push_back(y_bnd_dim_name);
1716 else if(z_bnd_dim_name!="")
1717 bnd_dim_names.push_back(z_bnd_dim_name);
1718 else if(t_bnd_dim_name!="")
1719 bnd_dim_names.push_back(t_bnd_dim_name);
1720 }
1721
1722 }
1723 }
1724
1725 // Read through the source DDS leaves and nodes, extract all axes and
1726 // parameter data, and store that data as Axis and Parameters
1727 transformNodeWorker(strm, leaves, nodes, indent + _indent_increment + _indent_increment, sendData);
1728
1729 // Verify the request hasn't exceeded bes_timeout, and disable timeout if allowed
1730 RequestServiceTimer::TheTimer()->throw_if_timeout_expired(prolog + "ERROR: bes-timeout expired before transmit", __FILE__, __LINE__);
1732
1733 // Print the Coverage data to stream as CoverageJSON
1734 printCoverageJSON(strm, indent, testOverride);
1735}
1736
1737void FoDapCovJsonTransform::transform(ostream *strm, libdap::BaseType *bt, string indent, bool sendData)
1738{
1739 switch(bt->type()) {
1740 // Handle the atomic types - that's easy!
1741 case libdap::dods_byte_c:
1742 case libdap::dods_int16_c:
1743 case libdap::dods_uint16_c:
1744 case libdap::dods_int32_c:
1745 case libdap::dods_uint32_c:
1746 case libdap::dods_float32_c:
1747 case libdap::dods_float64_c:
1748 case libdap::dods_str_c:
1749 case libdap::dods_url_c:
1750 transformAtomic(bt, indent, sendData);
1751 break;
1752
1753 case libdap::dods_structure_c:
1754 transform(strm, (libdap::Structure *) bt, indent, sendData);
1755 break;
1756
1757 case libdap::dods_grid_c:
1758 transform(strm, (libdap::Grid *) bt, indent, sendData);
1759 break;
1760
1761 case libdap::dods_sequence_c:
1762 transform(strm, (libdap::Sequence *) bt, indent, sendData);
1763 break;
1764
1765 case libdap::dods_array_c:
1766 transform(strm, (libdap::Array *) bt, indent, sendData);
1767 break;
1768
1769 case libdap::dods_int8_c:
1770 case libdap::dods_uint8_c:
1771 case libdap::dods_int64_c:
1772 case libdap::dods_uint64_c:
1773 case libdap::dods_enum_c:
1774 case libdap::dods_group_c: {
1775 string s = (string) "File out COVJSON, DAP4 types not yet supported.";
1776 throw BESInternalError(s, __FILE__, __LINE__);
1777 break;
1778 }
1779
1780 default: {
1781 string s = (string) "File out COVJSON, Unrecognized type.";
1782 throw BESInternalError(s, __FILE__, __LINE__);
1783 break;
1784 }
1785 }
1786}
1787
1788void FoDapCovJsonTransform::transformAtomic(libdap::BaseType *b, string indent, bool sendData)
1789{
1790 string childindent = indent + _indent_increment;
1791 struct Axis *newAxis = new Axis;
1792
1793 // Why assigning the name as "test" and why assigning the string type value? KY 2022-01-18
1794 newAxis->name = "test";
1795 if(sendData) {
1796 newAxis->values += "\"values\": [";
1797 if(b->type() == libdap::dods_str_c || b->type() == libdap::dods_url_c) {
1798 libdap::Str *strVar = (libdap::Str *) b;
1799 string tmpString = strVar->value();
1800 newAxis->values += "\"";
1801 newAxis->values += focovjson::escape_for_covjson(tmpString);
1802 newAxis->values += "\"";
1803 }
1804 else {
1805 ostringstream otemp;
1806 istringstream itemp;
1807 int tempVal = 0;
1808 b->print_val(otemp, "", false);
1809 istringstream (otemp.str());
1810 istringstream (otemp.str()) >> tempVal;
1811 newAxis->values += otemp.str();
1812 }
1813 newAxis->values += "]";
1814 }
1815 else {
1816 newAxis->values += "\"values\": []";
1817 }
1818
1819 axes.push_back(newAxis);
1820 axisCount++;
1821}
1822
1823void FoDapCovJsonTransform::transform(ostream *strm, libdap::Array *a, string indent, bool sendData)
1824{
1825 BESDEBUG(FoDapCovJsonTransform_debug_key,
1826 "FoCovJsonTransform::transform() - Processing Array. " << " a->type(): " << a->type() << " a->var()->type(): " << a->var()->type() << endl);
1827
1828 switch(a->var()->type()) {
1829 // Handle the atomic types - that's easy!
1830 case libdap::dods_byte_c:
1831 covjsonSimpleTypeArray<libdap::dods_byte>(strm, a, indent, sendData);
1832 break;
1833
1834 case libdap::dods_int16_c:
1835 covjsonSimpleTypeArray<libdap::dods_int16>(strm, a, indent, sendData);
1836 break;
1837
1838 case libdap::dods_uint16_c:
1839 covjsonSimpleTypeArray<libdap::dods_uint16>(strm, a, indent, sendData);
1840 break;
1841
1842 case libdap::dods_int32_c:
1843 covjsonSimpleTypeArray<libdap::dods_int32>(strm, a, indent, sendData);
1844 break;
1845
1846 case libdap::dods_uint32_c:
1847 covjsonSimpleTypeArray<libdap::dods_uint32>(strm, a, indent, sendData);
1848 break;
1849
1850 case libdap::dods_float32_c:
1851 covjsonSimpleTypeArray<libdap::dods_float32>(strm, a, indent, sendData);
1852 break;
1853
1854 case libdap::dods_float64_c:
1855 covjsonSimpleTypeArray<libdap::dods_float64>(strm, a, indent, sendData);
1856 break;
1857
1858 case libdap::dods_str_c: {
1859 covjsonStringArray(strm, a, indent, sendData);
1860 break;
1861 }
1862
1863 case libdap::dods_url_c: {
1864 covjsonStringArray(strm, a, indent, sendData);
1865 break;
1866 }
1867
1868 case libdap::dods_structure_c:
1869 throw BESInternalError("File out COVJSON, Arrays of Structure objects not a supported return type.", __FILE__, __LINE__);
1870
1871 case libdap::dods_grid_c:
1872 throw BESInternalError("File out COVJSON, Arrays of Grid objects not a supported return type.", __FILE__, __LINE__);
1873
1874 case libdap::dods_sequence_c:
1875 throw BESInternalError("File out COVJSON, Arrays of Sequence objects not a supported return type.", __FILE__, __LINE__);
1876
1877 case libdap::dods_array_c:
1878 throw BESInternalError("File out COVJSON, Arrays of Array objects not a supported return type.", __FILE__, __LINE__);
1879
1880 case libdap::dods_int8_c:
1881 case libdap::dods_uint8_c:
1882 case libdap::dods_int64_c:
1883 case libdap::dods_uint64_c:
1884 case libdap::dods_enum_c:
1885 case libdap::dods_group_c:
1886 throw BESInternalError("File out COVJSON, DAP4 types not yet supported.", __FILE__, __LINE__);
1887
1888 default:
1889 throw BESInternalError("File out COVJSON, Unrecognized type.", __FILE__, __LINE__);
1890 }
1891}
1892
1893bool 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) {
1894
1895 bool ret_value = false;
1896 for (unsigned i = 0; i < CF_unit_values.size(); i++) {
1897//cerr<<"CF_unit_values "<<CF_unit_values[i] << endl;
1898 bool is_cf_units = false;
1899 if(is_t_axis == false) {
1900 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)
1901 is_cf_units = true;
1902 }
1903 else {
1904 if(unit_value.compare(0,CF_unit_values[i].size(),CF_unit_values[i])==0)
1905 is_cf_units = true;
1906 }
1907
1908 if (is_cf_units) {
1909 libdap::Array::Dim_iter di = d_a->dim_begin();
1910 this_axisVar.dim_size = d_a->dimension_size(di, true);
1911 this_axisVar.name = d_a->name();
1912 this_axisVar.dim_name = d_a->dimension_name(di);
1913 this_axisVar.bound_name="";
1914 ret_value = true;
1915#if 0
1916cerr<<"axis size "<< this_axisVar.dim_size <<endl;
1917cerr<<"axis name "<< this_axisVar.name <<endl;
1918cerr<<"axis dim_name "<< this_axisVar.dim_name <<endl;
1919#endif
1920 break;
1921 }
1922
1923 }
1924 return ret_value;
1925
1926}
1927
1928
1929void FoDapCovJsonTransform::check_bounds(libdap::DDS *dds, map<string,string>& vname_bname) {
1930
1931 string bound_name = "bounds";
1932 libdap::DDS::Vars_iter vi = dds->var_begin();
1933 libdap::DDS::Vars_iter ve = dds->var_end();
1934
1935 for(; vi != ve; vi++) {
1936//cerr<<"coming to the loop " <<endl;
1937 if((*vi)->send_p()) {
1938 libdap::BaseType *v = *vi;
1939 libdap::Type type = v->type();
1940
1941 // Check if this qualifies a simple geographic grid coverage
1942 if(type == libdap::dods_array_c) {
1943 libdap::Array * d_a = dynamic_cast<libdap::Array *>(v);
1944 int d_ndims = d_a->dimensions();
1945//cerr<<"d_ndims is "<< d_ndims <<endl;
1946 if(d_ndims == 1) {
1947 libdap::AttrTable &attrs = d_a->get_attr_table();
1948 unsigned int num_attrs = attrs.get_size();
1949 if (num_attrs) {
1950 libdap::AttrTable::Attr_iter i = attrs.attr_begin();
1951 libdap::AttrTable::Attr_iter e = attrs.attr_end();
1952 for (; i != e; i++) {
1953 string attr_name = attrs.get_name(i);
1954//cerr<<"attr_name is "<<attr_name <<endl;
1955 unsigned int num_vals = attrs.get_attr_num(i);
1956 if (num_vals == 1) {
1957 // Check if the attr_name is units.
1958 bool is_attr_bounds = false;
1959 if((attr_name.size() == bound_name.size())
1960 && (attr_name.compare(bound_name) == 0))
1961 is_attr_bounds = true;
1962 if(is_attr_bounds == false)
1963 if(attr_name.size() == (bound_name.size()+1) &&
1964 attr_name[bound_name.size()] == '\0' &&
1965 attr_name.compare(0,bound_name.size(),bound_name) ==0)
1966 is_attr_bounds = true;
1967
1968 if (is_attr_bounds) {
1969 string val = attrs.get_attr(i,0);
1970 vname_bname[d_a->name()] = val;
1971 }
1972 }
1973 }
1974 }
1975 }
1976 }
1977 }
1978 }
1979}
1980
1981void FoDapCovJsonTransform::obtain_bound_values(libdap::DDS *dds, const axisVar & av, std::vector<float>& av_bnd_val, std::string& bnd_dim_name, bool sendData) {
1982
1983//cerr<<"coming to the obtain_bound_values "<<endl;
1984 libdap::Array* d_a = obtain_bound_values_worker(dds, av.bound_name,bnd_dim_name);
1985 if (d_a) {// float, now we just handle this way
1986#if 0
1987//cerr<<"d_a->name in obtain_bound_values is "<<d_a->name() <<endl;
1988//cerr<<"in obtain_bound_values bnd_dim_name is "<<bnd_dim_name <<endl;
1989#endif
1990 if(d_a->var()->type_name() == "Float64") {
1991 if(sendData) {
1992 int num_lengths = d_a->length();
1993 vector<double>temp_val;
1994 temp_val.resize(num_lengths);
1995 d_a->value(temp_val.data());
1996
1997 av_bnd_val.resize(num_lengths);
1998 for (unsigned i = 0; i <av_bnd_val.size();i++)
1999 av_bnd_val[i] =(float)temp_val[i];
2000
2001#if 0
2002for (int i = 0; i <av_bnd_val.size();i++)
2003cerr<<"av_bnd_val["<<i<<"] = "<<av_bnd_val[i] <<endl;
2004#endif
2005 }
2006 }
2007 else if(d_a->var()->type_name() == "Float32") {
2008 if(sendData) {
2009 int num_lengths = d_a->length();
2010 av_bnd_val.resize(num_lengths);
2011 d_a->value(av_bnd_val.data());
2012#if 0
2013for (int i = 0; i <av_bnd_val.size();i++)
2014cerr<<"av_bnd_val["<<i<<"] = "<<av_bnd_val[i] <<endl;
2015#endif
2016 }
2017 }
2018
2019
2020 }
2021}
2022
2023void FoDapCovJsonTransform::obtain_bound_values(libdap::DDS *dds, const axisVar & av, std::vector<double>& av_bnd_val, std::string& bnd_dim_name, bool sendData) {
2024
2025 libdap::Array* d_a = obtain_bound_values_worker(dds, av.bound_name,bnd_dim_name);
2026 if(d_a) {
2027#if 0
2028cerr<<"d_a->name in obtain_bound_values is "<<d_a->name() <<endl;
2029cerr<<"in obtain_bound_values bnd_dim_name is "<<bnd_dim_name <<endl;
2030#endif
2031 if(d_a->var()->type_name() == "Float64") {
2032 if(sendData) {
2033 int num_lengths = d_a->length();
2034 av_bnd_val.resize(num_lengths);
2035 d_a->value(av_bnd_val.data());
2036#if 0
2037for (int i = 0; i <av_bnd_val.size();i++)
2038cerr<<"av_bnd_val["<<i<<"] = "<<av_bnd_val[i] <<endl;
2039#endif
2040 }
2041 }
2042 else if(d_a->var()->type_name() == "Float32") {
2043 if(sendData) {
2044 int num_lengths = d_a->length();
2045 vector<float>temp_val;
2046 temp_val.resize(num_lengths);
2047 d_a->value(temp_val.data());
2048 av_bnd_val.resize(num_lengths);
2049 for (unsigned i = 0; i <av_bnd_val.size();i++)
2050 av_bnd_val[i] =(double)temp_val[i];
2051#if 0
2052for (int i = 0; i <av_bnd_val.size();i++)
2053cerr<<"av_bnd_val["<<i<<"] = "<<av_bnd_val[i] <<endl;
2054#endif
2055 }
2056 }
2057 }
2058}
2059
2060libdap::Array* FoDapCovJsonTransform::obtain_bound_values_worker(libdap::DDS *dds, const string& bnd_name, string & bnd_dim_name) {
2061
2062 libdap::Array* d_a = nullptr;
2063 if(bnd_name!="") {
2064
2065 libdap::DDS::Vars_iter vi = dds->var_begin();
2066 libdap::DDS::Vars_iter ve = dds->var_end();
2067
2068 for(; vi != ve; vi++) {
2069// cerr<<"In worker: coming to the loop " <<endl;
2070 if((*vi)->send_p()) {
2071 libdap::BaseType *v = *vi;
2072 libdap::Type type = v->type();
2073
2074 // Check if this qualifies a simple geographic grid coverage
2075 if(type == libdap::dods_array_c) {
2076 libdap::Array * td_a = dynamic_cast<libdap::Array *>(v);
2077 int d_ndims = td_a->dimensions();
2078// cerr<<"In worker: d_ndims is "<< d_ndims <<endl;
2079 if(d_ndims == 2) {
2080 string tmp_bnd_dim_name;
2081 int bound_dim_size = 0;
2082 int dim_count = 0;
2083 libdap::Array::Dim_iter di = td_a->dim_begin();
2084 libdap::Array::Dim_iter de = td_a->dim_end();
2085 for (; di != de; di++) {
2086 if(dim_count == 1) {
2087 bound_dim_size = td_a->dimension_size(di, true);
2088 tmp_bnd_dim_name = td_a->dimension_name(di);
2089//cerr<<"tmp_bnd_dim_name is "<<tmp_bnd_dim_name <<endl;
2090 }
2091 dim_count++;
2092 }
2093
2094 // For 1-D coordinate bound, the bound size should always be 2.
2095 if((bound_dim_size == 2) && (td_a->name() == bnd_name)) {
2096 d_a = td_a;
2097 bnd_dim_name = tmp_bnd_dim_name;
2098//cerr<<"bnd_dim_name is "<<bnd_dim_name <<endl;
2099 break;
2100 }
2101 }
2102 }
2103 }
2104 }
2105
2106 }
2107 return d_a;
2108
2109}
2110
2111bool FoDapCovJsonTransform::obtain_valid_vars(libdap::DDS *dds, short axis_var_z_count, short axis_var_t_count ) {
2112
2113//cerr<<"coming to obtain_valid_vars "<<endl;
2114 bool ret_value = true;
2115 std::vector<std::string> temp_x_y_vars;
2116 std::vector<std::string> temp_x_y_z_vars;
2117 std::vector<std::string> temp_x_y_t_vars;
2118 std::vector<std::string> temp_x_y_z_t_vars;
2119
2120 libdap::DDS::Vars_iter vi = dds->var_begin();
2121 libdap::DDS::Vars_iter ve = dds->var_end();
2122 for(; vi != ve; vi++) {
2123 if((*vi)->send_p()) {
2124 libdap::BaseType *v = *vi;
2125 libdap::Type type = v->type();
2126 if(type == libdap::dods_array_c) {
2127 libdap::Array * d_a = dynamic_cast<libdap::Array *>(v);
2128 int d_ndims = d_a->dimensions();
2129
2130 if(d_ndims >=2) {
2131
2132 short axis_x_count = 0;
2133 short axis_y_count = 0;
2134 short axis_z_count = 0;
2135 short axis_t_count = 0;
2136 bool non_xyzt_dim = false;
2137 bool supported_var = true;
2138
2139 libdap::Array::Dim_iter di = d_a->dim_begin();
2140 libdap::Array::Dim_iter de = d_a->dim_end();
2141
2142 for (; di != de; di++) {
2143 // check x,y,z,t dimensions
2144 if((d_a->dimension_size(di,true) == axisVar_x.dim_size) &&
2145 (d_a->dimension_name(di) == axisVar_x.dim_name))
2146 axis_x_count++;
2147 else if((d_a->dimension_size(di,true) == axisVar_y.dim_size) &&
2148 (d_a->dimension_name(di) == axisVar_y.dim_name))
2149 axis_y_count++;
2150 else if((d_a->dimension_size(di,true) == axisVar_z.dim_size) &&
2151 (d_a->dimension_name(di) == axisVar_z.dim_name))
2152 axis_z_count++;
2153 else if((d_a->dimension_size(di,true) == axisVar_t.dim_size) &&
2154 (d_a->dimension_name(di) == axisVar_t.dim_name))
2155 axis_t_count++;
2156 else
2157 non_xyzt_dim = true;
2158
2159 // Non-x,y,z,t dimension or duplicate x,y,z,t dimensions are not supported.
2160 // Here for the "strict" case, I need to return false for the conversion to grid when
2161 // a non-conform > 1D var appears except the "bound" variables.
2162 if(non_xyzt_dim || axis_x_count >1 || axis_y_count >1 || axis_z_count >1 || axis_t_count >1) {
2163 supported_var = false;
2164//cerr<<"Obtain: d_a->name() is "<<d_a->name() <<endl;
2165 if (FoCovJsonRequestHandler::get_may_ignore_z_axis() == false) {
2166 if(d_a->name()!=axisVar_x.bound_name && d_a->name()!=axisVar_y.bound_name &&
2167 d_a->name()!=axisVar_z.bound_name && d_a->name()!=axisVar_t.bound_name)
2168 ret_value = false;
2169 }
2170 break;
2171 }
2172 }
2173
2174 if(supported_var) {
2175 // save the var names to the vars that hold (x,y),(x,y,z),(x,y,t),(x,y,z,t)
2176 if(axis_x_count == 1 & axis_y_count == 1 && axis_z_count == 0 && axis_t_count == 0)
2177 temp_x_y_vars.push_back(d_a->name());
2178 else if(axis_x_count == 1 & axis_y_count == 1 && axis_z_count == 1 && axis_t_count == 0)
2179 temp_x_y_z_vars.push_back(d_a->name());
2180 else if(axis_x_count == 1 & axis_y_count == 1 && axis_z_count == 0 && axis_t_count == 1)
2181 temp_x_y_t_vars.push_back(d_a->name());
2182 else if(axis_x_count == 1 & axis_y_count == 1 && axis_z_count == 1 && axis_t_count == 1)
2183 temp_x_y_z_t_vars.push_back(d_a->name());
2184 }
2185 else if(ret_value == false)
2186 break;
2187 }
2188 }
2189 }
2190 }
2191//cerr<<"obtain: after loop "<<endl;
2192
2193 if (ret_value == true) {
2194 if(FoCovJsonRequestHandler::get_may_ignore_z_axis()== true) {
2195
2196#if 0
2197cerr<<"coming to ignore mode "<<endl;
2198cerr<<"axis_var_z_count: "<<axis_var_z_count <<endl;
2199cerr<<"axis_var_t_count: "<<axis_var_t_count <<endl;
2200#endif
2201
2202 // Select the common factor of (x,y),(x,y,z),(x,y,t),(x,y,z,t) among variables
2203 // If having vars that only holds x,y; these vars are only vars that will appear at the final coverage.
2204 if(axis_var_z_count <=1 && axis_var_t_count <=1) {
2205
2206 for (unsigned i = 0; i <temp_x_y_vars.size(); i++)
2207 par_vars.push_back(temp_x_y_vars[i]);
2208 for (unsigned i = 0; i <temp_x_y_t_vars.size(); i++)
2209 par_vars.push_back(temp_x_y_t_vars[i]);
2210
2211 if (temp_x_y_vars.empty()) {
2212 for (unsigned i = 0; i <temp_x_y_z_vars.size(); i++)
2213 par_vars.push_back(temp_x_y_z_vars[i]);
2214 for (unsigned i = 0; i <temp_x_y_z_t_vars.size(); i++)
2215 par_vars.push_back(temp_x_y_z_t_vars[i]);
2216
2217 }
2218 else {
2219 // Ignore the (x,y,z) and (x,y,z,t) when (x,y) exists.
2220 // We also need to ignore the z-axis TODO,we may need to support multiple verical coordinates. !
2221 if (axis_var_z_count == 1) {
2222 axisVar_z.name="";
2223 axisVar_z.dim_name = "";
2224 axisVar_z.bound_name = "";
2225 }
2226 }
2227 }
2228 else if (axis_var_z_count >1 && axis_var_t_count <=1) {
2229 //Cover all variables that have (x,y) or (x,y,t)
2230 for (unsigned i = 0; i <temp_x_y_vars.size(); i++)
2231 par_vars.push_back(temp_x_y_vars[i]);
2232 for (unsigned i = 0; i <temp_x_y_t_vars.size(); i++)
2233 par_vars.push_back(temp_x_y_t_vars[i]);
2234 }
2235 else if (axis_var_z_count <=1 && axis_var_t_count >1) {
2236 //Cover all variables that have (x,y) or (x,y,z)
2237 for (unsigned i = 0; i <temp_x_y_vars.size(); i++)
2238 par_vars.push_back(temp_x_y_vars[i]);
2239 for (unsigned i = 0; i <temp_x_y_z_vars.size(); i++)
2240 par_vars.push_back(temp_x_y_z_vars[i]);
2241 }
2242 else {
2243 // Select the common factor of (x,y),(x,y,z),(x,y,t),(x,y,z,t) among variables
2244 // If having vars that only holds x,y; these vars are only vars that will appear at the final coverage.
2245 for (unsigned i = 0; i <temp_x_y_vars.size(); i++)
2246 par_vars.push_back(temp_x_y_vars[i]);
2247 }
2248 }
2249 else {
2250//cerr<<"coming to strict mode "<<endl;
2251 if(axis_var_z_count >1 || axis_var_t_count >1)
2252 ret_value = false;
2253 else {
2254 //Cover all variables that have (x,y) or (x,y,z) or (x,y,t) or (x,y,z,t)
2255 for (unsigned i = 0; i <temp_x_y_vars.size(); i++)
2256 par_vars.push_back(temp_x_y_vars[i]);
2257 for (unsigned i = 0; i <temp_x_y_z_vars.size(); i++)
2258 par_vars.push_back(temp_x_y_z_vars[i]);
2259 for (unsigned i = 0; i <temp_x_y_t_vars.size(); i++)
2260 par_vars.push_back(temp_x_y_t_vars[i]);
2261 for (unsigned i = 0; i <temp_x_y_z_t_vars.size(); i++)
2262 par_vars.push_back(temp_x_y_z_t_vars[i]);
2263 }
2264 }
2265
2266#if 0
2267cerr<<"Parameter Names: "<<endl;
2268for(unsigned i = 0; i <par_vars.size(); i++)
2269 cerr<<par_vars[i]<<endl;
2270#endif
2271
2272
2273 if(par_vars.size() == 0)
2274 ret_value = false;
2275
2276 }
2277 return ret_value;
2278
2279}
2280
2281std::string FoDapCovJsonTransform::cf_time_to_greg(long long time_val) {
2282
2283 tm ycf_1;
2284
2285 // Here obtain the cf_time from the axis_t_units.
2286 string cf_time= axis_t_units ;
2287
2288 // Check the time unit,day,hour, minute or second.
2289 short time_unit_length = -1;
2290 if(cf_time.compare(0,3,"day") == 0)
2291 time_unit_length = 0;
2292 else if(cf_time.compare(0,4,"hour") == 0)
2293 time_unit_length = 1;
2294 else if(cf_time.compare(0,6,"minute") == 0)
2295 time_unit_length = 2;
2296 else if(cf_time.compare(0,6,"second") == 0)
2297 time_unit_length = 3;
2298
2299//cerr<<"time_unit_length is "<<time_unit_length <<endl;
2300
2301 // Remove any commonly found words from the origin timestamp
2302 vector<string> subStrs = { "days", "day", "hours", "hour", "minutes", "minute",
2303 "seconds", "second", "since", " " };
2304
2305 for(unsigned int i = 0; i < subStrs.size(); i++)
2306 focovjson::removeSubstring(cf_time, subStrs[i]);
2307
2308//cerr<<"cf_time stripped is "<<cf_time <<endl;
2309
2310 // Separate the date from the hms.
2311 size_t cf_time_space_pos = cf_time.find(' ');
2312 string cf_date,cf_hms;
2313
2314 if(cf_time_space_pos!=string::npos) {
2315 cf_date= cf_time.substr(0,cf_time_space_pos);
2316 cf_hms = cf_time.substr(cf_time_space_pos+1);
2317 }
2318 // If without hours/minutes/seconds, we need to set them to 0.
2319 if(cf_hms==" " || cf_hms=="")
2320 cf_hms ="00:00:00";
2321
2322#if 0
2323cerr<<"cf_date is "<<cf_date <<endl;
2324cerr<<"cf_hms is "<<cf_hms <<endl;
2325#endif
2326
2327 // We need to obtain year,month,date,hour,minute and second
2328 // of the time.
2329
2330 string cf_y,cf_mo,cf_d;
2331 size_t cf_date_dash_pos = cf_date.find('-');
2332 if(cf_date_dash_pos !=string::npos) {
2333 string cf_md;
2334 cf_y = cf_date.substr(0,cf_date_dash_pos);
2335 cf_md = cf_date.substr(cf_date_dash_pos+1);
2336 size_t cf_md_dash_pos = cf_md.find("-");
2337 if(cf_md_dash_pos !=string::npos) {
2338 cf_mo = cf_md.substr(0,cf_md_dash_pos);
2339 cf_d = cf_md.substr(cf_md_dash_pos+1);
2340 }
2341 }
2342
2343 string cf_h,cf_ms,cf_m,cf_s;
2344 size_t cf_hms_colon_pos = cf_hms.find(':');
2345 if(cf_hms_colon_pos !=string::npos) {
2346 cf_h = cf_hms.substr(0,cf_hms_colon_pos);
2347 cf_ms = cf_hms.substr(cf_hms_colon_pos+1);
2348 size_t cf_ms_colon_pos = cf_ms.find(":");
2349 if(cf_ms_colon_pos !=string::npos) {
2350 cf_m = cf_ms.substr(0,cf_ms_colon_pos);
2351 cf_s = cf_ms.substr(cf_ms_colon_pos+1);
2352 }
2353 }
2354
2355
2356#if 0
2357cerr<<"cf_y is "<<cf_y <<endl;
2358cerr<<"cf_mo is "<<cf_mo <<endl;
2359cerr<<"cf_d is "<<cf_d <<endl;
2360
2361cerr<<"cf_h is "<<cf_h <<endl;
2362cerr<<"cf_m is "<<cf_m <<endl;
2363cerr<<"cf_s is "<<cf_s <<endl;
2364#endif
2365
2366 // We need to convert the time from string to integer.
2367 int cf_y_i,cf_mo_i,cf_d_i,cf_h_i,cf_m_i,cf_s_i;
2368 cf_y_i = stoi(cf_y);
2369 cf_mo_i = stoi(cf_mo);
2370 cf_d_i = stoi(cf_d);
2371 cf_h_i = stoi(cf_h);
2372 cf_m_i = stoi(cf_m);
2373 cf_s_i = stoi(cf_s);
2374
2375#if 0
2376cerr<<"cf_y_i " <<cf_y_i <<endl;
2377cerr<<"cf_mo_i " <<cf_mo_i <<endl;
2378cerr<<"cf_d_i " <<cf_d_i <<endl;
2379cerr<<"cf_h_i " <<cf_h_i <<endl;
2380cerr<<"cf_m_i " <<cf_m_i <<endl;
2381cerr<<"cf_s_i " <<cf_s_i <<endl;
2382#endif
2383
2384 // Now we want to assign these time info to struct tm
2385 // Note: the mktime() and localtime() may only work for the date after 1970.
2386 // This should be sufficient for the data we serve now.
2387 ycf_1.tm_hour = cf_h_i; ycf_1.tm_min = cf_m_i; ycf_1.tm_sec = cf_s_i;
2388 ycf_1.tm_year = cf_y_i-1900; ycf_1.tm_mon = cf_mo_i; ycf_1.tm_mday = cf_d_i;
2389#if 0
2390 //time_t t_ycf_1 = mktime(&ycf_1);
2391#endif
2392 time_t t_ycf_1 = timegm(&ycf_1);
2393
2394#if 0
2395cerr<<"t_ycf_1 is "<<t_ycf_1 <<endl;
2396cerr<<"time_val is "<<time_val <<endl;
2397#endif
2398
2399 //time_val = 11046060;
2400 // Here is the value to calculate the new time. We need to convert them to seconds.
2401 //double val = 1.000000000001;
2402 time_t t_ycf_2 ;
2403 // Here we need to convert days, hours, minutes to seconds
2404 if(time_unit_length == 0)
2405 t_ycf_2 = t_ycf_1 + 86400*time_val;
2406 else if (time_unit_length == 1)
2407 t_ycf_2 = t_ycf_1 + 3600*time_val;
2408 else if (time_unit_length == 2)
2409 t_ycf_2 = t_ycf_1 + 60*time_val;
2410 else if (time_unit_length == 3)
2411 t_ycf_2 = t_ycf_1 + time_val;
2412
2413
2414 //time_t t_ycf_2 = t_ycf_1 + 86340;
2415//cerr<<"t_ycf_2 is "<<t_ycf_2 <<endl;
2416 struct tm *t_new_ycf;
2417 struct tm temp_new_ycf;
2418 // The use of localtime() is to calcuate the time based on the CF time unit.
2419 // So the value actually represents the GMT time.
2420 // Note: we didn't consider the use of local time in the CF.
2421 // Our currently supported product uses GMT. Will consider the other cases later.
2422#if 0
2423 //t_new_ycf = localtime(&t_ycf_2);
2424 //t_new_ycf = gmtime(&t_ycf_2);
2425#endif
2426 t_new_ycf = gmtime_r(&t_ycf_2,&temp_new_ycf);
2427
2428#if 0
2429cerr<< "t_new_ycf.tm_year is " <<t_new_ycf->tm_year <<endl;
2430cerr<< "t_new_ycf.tm_mon is " <<t_new_ycf->tm_mon <<endl;
2431cerr<< "t_new_ycf.tm_day is " <<t_new_ycf->tm_mday <<endl;
2432cerr<< "t_new_ycf.tm_hour is " <<t_new_ycf->tm_hour <<endl;
2433cerr<< "t_new_ycf.tm_min is " <<t_new_ycf->tm_min <<endl;
2434cerr<< "t_new_ycf.tm_sec is " <<t_new_ycf->tm_sec <<endl;
2435#endif
2436 if(t_new_ycf->tm_mon == 0) {
2437 t_new_ycf->tm_year--;
2438 t_new_ycf->tm_mon = 12;
2439 }
2440 // Now, we need to change the time from int to string.
2441 string covjson_mon = (t_new_ycf->tm_mon<10)?
2442 ("0"+to_string(t_new_ycf->tm_mon)):
2443 to_string(t_new_ycf->tm_mon);
2444 string covjson_mday = (t_new_ycf->tm_mday<10)?
2445 ("0"+to_string(t_new_ycf->tm_mday)):
2446 to_string(t_new_ycf->tm_mday);
2447
2448 string covjson_hour = (t_new_ycf->tm_hour<10)?
2449 ("0"+to_string(t_new_ycf->tm_hour)):
2450 to_string(t_new_ycf->tm_hour);
2451
2452 string covjson_min = (t_new_ycf->tm_min<10)?
2453 ("0"+to_string(t_new_ycf->tm_min)):
2454 to_string(t_new_ycf->tm_min);
2455
2456 string covjson_sec = (t_new_ycf->tm_sec<10)?
2457 ("0"+to_string(t_new_ycf->tm_sec)):
2458 to_string(t_new_ycf->tm_sec);
2459
2460
2461 // This is the final time.
2462 string covjson_time = to_string(1900+t_new_ycf->tm_year)+"-"+
2463 covjson_mon+"-"+covjson_mday+"T"+
2464 covjson_hour+":"+covjson_min+":"+
2465 covjson_sec+"Z";
2466
2467 return covjson_time;
2468}
2469
2470void FoDapCovJsonTransform::print_bound(ostream *strm, const std::vector<std::string> & t_bnd_val, const std::string & indent, bool is_t_axis) const {
2471
2472 if(axisVar_t.bound_name !="") {
2473 std::string print_values;
2474 if(t_bnd_val.size() >0) {
2475 print_values = "\"bounds\": [";
2476 for(unsigned i = 0; i <t_bnd_val.size(); i++) {
2477 string tmpString = t_bnd_val[i];
2478
2479 if (is_t_axis) {
2480 print_values +="\"";
2481 print_values +=focovjson::escape_for_covjson(tmpString);
2482 print_values +="\"";
2483 }
2484 else
2485 print_values +=tmpString;
2486
2487 if(i !=(t_bnd_val.size()-1))
2488 print_values +=", ";
2489
2490
2491 }
2492 print_values += "]";
2493 }
2494 else
2495 print_values= "\"bounds\": []";
2496 *strm << indent << print_values <<endl;
2497 }
2498
2499}
exception thrown if internal error encountered
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:895
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...