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 
45 using std::ostringstream;
46 using 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 
69 using std::map;
70 #define FoDapCovJsonTransform_debug_key "focovjson"
71 
72 
73 bool 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
82 cerr<<"Before X and Y and Z and T"<<endl;
83 cerr<<"Number of parameters is "<<this->parameters.size() <<endl;
84 cerr<<"shapeVals is "<<shapeVals.size() <<endl;
85 cerr<<"Number of Axis is "<<this->axes.size() <<endl;
86 for (int i = 0; i <this->axes.size(); i++) {
87 cerr<<"Axis name is "<<this->axes[i]->name << endl;
88 cerr<<"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 
215 template<typename T>
216 unsigned 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 
315 template<typename T>
316 void 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 
449 void 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 
566 void 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
573 cerr << "axis name is "<< name <<endl;
574 cerr << "axis value is "<< values <<endl;
575 #endif
576  this->axes.push_back(newAxis);
577  this->axisCount++;
578 }
579 
580 void 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 
599 void 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 
786 void 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 
939 string 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 
984 void 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 
994 void FoDapCovJsonTransform::transform(ostream &ostrm, bool sendData, bool testOverride)
995 {
996  transform(&ostrm, _dds, "", sendData, testOverride);
997 }
998 
999 void 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 
1029 void 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 
1055 void 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 
1079 void 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 
1098 void 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 
1117 void 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 
1253 void 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 
1320 void 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 
1397 void 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 
1470 void 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
1586 for(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
1596 for(int i = 0; i <unit_candidates.size(); i++)
1597 cerr<<"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
1619 cerr<<"axis_var_x_count is "<< axis_var_x_count <<endl;
1620 cerr<<"axis_var_y_count is "<< axis_var_y_count <<endl;
1621 cerr<<"axis_var_z_count is "<< axis_var_z_count <<endl;
1622 cerr<<"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
1651 for(it = vname_bname.begin(); it != vname_bname.end(); it++) {
1652 cerr<<it->first <<endl;
1653 cerr<<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
1670 cerr<<"axisVar_x.name is "<<axisVar_x.name <<endl;
1671 cerr<<"axisVar_x.dim_name is "<<axisVar_x.dim_name <<endl;
1672 cerr<<"axisVar_x.dim_size is "<<axisVar_x.dim_size <<endl;
1673 cerr<<"axisVar_x.bound_name is "<<axisVar_x.bound_name <<endl;
1674 
1675 cerr<<"axisVar_y.name is "<<axisVar_y.name <<endl;
1676 cerr<<"axisVar_y.dim_name is "<<axisVar_y.dim_name <<endl;
1677 cerr<<"axisVar_y.dim_size is "<<axisVar_y.dim_size <<endl;
1678 cerr<<"axisVar_y.bound_name is "<<axisVar_y.bound_name <<endl;
1679 
1680 cerr<<"axisVar_z.name is "<<axisVar_z.name <<endl;
1681 cerr<<"axisVar_z.dim_name is "<<axisVar_z.dim_name <<endl;
1682 cerr<<"axisVar_z.dim_size is "<<axisVar_z.dim_size <<endl;
1683 cerr<<"axisVar_z.bound_name is "<<axisVar_z.bound_name <<endl;
1684 
1685 cerr<<"axisVar_t.name is "<<axisVar_t.name <<endl;
1686 cerr<<"axisVar_t.dim_name is "<<axisVar_t.dim_name <<endl;
1687 cerr<<"axisVar_t.dim_size is "<<axisVar_t.dim_size <<endl;
1688 cerr<<"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 
1737 void 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 
1788 void 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 
1823 void 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 
1893 bool 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
1916 cerr<<"axis size "<< this_axisVar.dim_size <<endl;
1917 cerr<<"axis name "<< this_axisVar.name <<endl;
1918 cerr<<"axis dim_name "<< this_axisVar.dim_name <<endl;
1919 #endif
1920  break;
1921  }
1922 
1923  }
1924  return ret_value;
1925 
1926 }
1927 
1928 
1929 void 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 
1981 void 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
2002 for (int i = 0; i <av_bnd_val.size();i++)
2003 cerr<<"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
2013 for (int i = 0; i <av_bnd_val.size();i++)
2014 cerr<<"av_bnd_val["<<i<<"] = "<<av_bnd_val[i] <<endl;
2015 #endif
2016  }
2017  }
2018 
2019 
2020  }
2021 }
2022 
2023 void 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
2028 cerr<<"d_a->name in obtain_bound_values is "<<d_a->name() <<endl;
2029 cerr<<"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
2037 for (int i = 0; i <av_bnd_val.size();i++)
2038 cerr<<"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
2052 for (int i = 0; i <av_bnd_val.size();i++)
2053 cerr<<"av_bnd_val["<<i<<"] = "<<av_bnd_val[i] <<endl;
2054 #endif
2055  }
2056  }
2057  }
2058 }
2059 
2060 libdap::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 
2111 bool 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
2197 cerr<<"coming to ignore mode "<<endl;
2198 cerr<<"axis_var_z_count: "<<axis_var_z_count <<endl;
2199 cerr<<"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
2267 cerr<<"Parameter Names: "<<endl;
2268 for(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 
2281 std::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
2323 cerr<<"cf_date is "<<cf_date <<endl;
2324 cerr<<"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
2357 cerr<<"cf_y is "<<cf_y <<endl;
2358 cerr<<"cf_mo is "<<cf_mo <<endl;
2359 cerr<<"cf_d is "<<cf_d <<endl;
2360 
2361 cerr<<"cf_h is "<<cf_h <<endl;
2362 cerr<<"cf_m is "<<cf_m <<endl;
2363 cerr<<"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
2376 cerr<<"cf_y_i " <<cf_y_i <<endl;
2377 cerr<<"cf_mo_i " <<cf_mo_i <<endl;
2378 cerr<<"cf_d_i " <<cf_d_i <<endl;
2379 cerr<<"cf_h_i " <<cf_h_i <<endl;
2380 cerr<<"cf_m_i " <<cf_m_i <<endl;
2381 cerr<<"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
2395 cerr<<"t_ycf_1 is "<<t_ycf_1 <<endl;
2396 cerr<<"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
2429 cerr<< "t_new_ycf.tm_year is " <<t_new_ycf->tm_year <<endl;
2430 cerr<< "t_new_ycf.tm_mon is " <<t_new_ycf->tm_mon <<endl;
2431 cerr<< "t_new_ycf.tm_day is " <<t_new_ycf->tm_mday <<endl;
2432 cerr<< "t_new_ycf.tm_hour is " <<t_new_ycf->tm_hour <<endl;
2433 cerr<< "t_new_ycf.tm_min is " <<t_new_ycf->tm_min <<endl;
2434 cerr<< "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 
2470 void 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...
Type
Type of JSON value.
Definition: rapidjson.h:664