bes  Updated for version 3.20.10
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 
44 using std::ostringstream;
45 using std::istringstream;
46 
47 #include <libdap/DDS.h>
48 #include <libdap/Structure.h>
49 #include <libdap/Constructor.h>
50 #include <libdap/Array.h>
51 #include <libdap/Grid.h>
52 #include <libdap/Sequence.h>
53 #include <libdap/Float64.h>
54 #include <libdap/Str.h>
55 #include <libdap/Url.h>
56 
57 #include <BESDebug.h>
58 #include <BESInternalError.h>
59 #include <DapFunctionUtils.h>
60 #include "FoDapCovJsonTransform.h"
61 #include "focovjson_utils.h"
62 
63 #define FoDapCovJsonTransform_debug_key "focovjson"
64 
65 
66 bool FoDapCovJsonTransform::canConvert()
67 {
68  // If x, y, z, and t all exist
69  // We are assuming the following is true:
70  // - shapeVals[0] = x axis
71  // - shapeVals[1] = y axis
72  // - shapeVals[2] = z axis
73  // - shapeVals[3] = t axis
74  if(xExists && yExists && zExists && tExists) {
75 
76  if (shapeVals.size() < 4)
77  return false;
78 
79  // A domain with Grid domain type MUST have the axes "x" and "y"
80  // and MAY have the axes "z" and "t".
81  if((shapeVals[0] > 1) && (shapeVals[1] > 1) && (shapeVals[2] >= 1) && (shapeVals[3] >= 0)) {
82  domainType = "Grid";
83  return true;
84  }
85 
86  // A domain with VerticalProfile domain type MUST have the axes "x",
87  // "y", and "z", where "x" and "y" MUST have a single coordinate only.
88  else if((shapeVals[0] == 1) && (shapeVals[1] == 1) && (shapeVals[2] >= 1) && ((shapeVals[3] <= 1) && (shapeVals[3] >= 0))) {
89  domainType = "Vertical Profile";
90  return true;
91  }
92 
93  // A domain with PointSeries domain type MUST have the axes "x", "y",
94  // and "t" where "x" and "y" MUST have a single coordinate only. A
95  // domain with PointSeries domain type MAY have the axis "z" which
96  // MUST have a single coordinate only.
97  else if((shapeVals[0] == 1) && (shapeVals[1] == 1) && (shapeVals[2] == 1) && (shapeVals[3] >= 0)) {
98  domainType = "Point Series";
99  return true;
100  }
101 
102  // A domain with Point domain type MUST have the axes "x" and "y" and MAY
103  // have the axes "z" and "t" where all MUST have a single coordinate only.
104  else if((shapeVals[0] == 1) && (shapeVals[1] == 1) && (shapeVals[2] == 1) && (shapeVals[3] == 1)) {
105  domainType = "Point";
106  return true;
107  }
108  }
109 
110  // If just x, y, and t exist
111  // We are assuming the following is true:
112  // - shapeVals[0] = x axis
113  // - shapeVals[1] = y axis
114  // - shapeVals[2] = t axis
115  else if(xExists && yExists && !zExists && tExists) {
116 
117  if (shapeVals.size() < 3)
118  return false;
119 
120  // A domain with Grid domain type MUST have the axes "x" and "y"
121  // and MAY have the axes "z" and "t".
122  if((shapeVals[0] > 1) && (shapeVals[1] > 1) && (shapeVals[2] >= 0)) {
123  domainType = "Grid";
124  return true;
125  }
126 
127  // A domain with PointSeries domain type MUST have the axes "x", "y",
128  // and "t" where "x" and "y" MUST have a single coordinate only. A
129  // domain with PointSeries domain type MAY have the axis "z" which
130  // MUST have a single coordinate only.
131  else if((shapeVals[0] == 1) && (shapeVals[1] == 1) && (shapeVals[2] >= 0)) {
132  domainType = "Point Series";
133  return true;
134  }
135 
136  // A domain with Point domain type MUST have the axes "x" and "y" and MAY
137  // have the axes "z" and "t" where all MUST have a single coordinate only.
138  else if((shapeVals[0] == 1) && (shapeVals[1] == 1) && (shapeVals[2] == 1)) {
139  domainType = "Point";
140  return true;
141  }
142  }
143 
144  // If just x and y exist
145  // We are assuming the following is true:
146  // - shapeVals[0] = x axis
147  // - shapeVals[1] = y axis
148  else if(xExists && yExists && !zExists && !tExists) {
149 
150  if (shapeVals.size() < 2)
151  return false;
152 
153  // A domain with Grid domain type MUST have the axes "x" and "y"
154  // and MAY have the axes "z" and "t".
155  if((shapeVals[0] > 1) && (shapeVals[1] > 1)) {
156  domainType = "Grid";
157  return true;
158  }
159 
160  // A domain with Point domain type MUST have the axes "x" and "y" and MAY
161  // have the axes "z" and "t" where all MUST have a single coordinate only.
162  else if((shapeVals[0] == 1) && (shapeVals[1] == 1)) {
163  domainType = "Point";
164  return true;
165  }
166  }
167 
168  return false; // This source DDS is not valid as CovJSON
169 }
170 
171 template<typename T>
172 unsigned int FoDapCovJsonTransform::covjsonSimpleTypeArrayWorker(ostream *strm, T *values, unsigned int indx,
173  vector<unsigned int> *shape, unsigned int currentDim)
174 {
175  unsigned int currentDimSize = (*shape)[currentDim];
176 
177  // FOR TESTING AND DEBUGGING PURPOSES
178  // *strm << "\"currentDim\": \"" << currentDim << "\"" << endl;
179  // *strm << "\"currentDimSize\": \"" << currentDimSize << "\"" << endl;
180 
181  for(unsigned int i = 0; i < currentDimSize; i++) {
182  if(currentDim < shape->size() - 1) {
183  BESDEBUG(FoDapCovJsonTransform_debug_key,
184  "covjsonSimpleTypeArrayWorker() - Recursing! indx: " << indx << " currentDim: " << currentDim << " currentDimSize: " << currentDimSize << endl);
185  indx = covjsonSimpleTypeArrayWorker<T>(strm, values, indx, shape, currentDim + 1);
186  if(i + 1 != currentDimSize) {
187  *strm << ", ";
188  }
189  }
190  else {
191  if(i) {
192  *strm << ", ";
193  }
194  if(typeid(T) == typeid(string)) {
195  // Strings need to be escaped to be included in a CovJSON object.
196  string val = reinterpret_cast<string*>(values)[indx++];
197  *strm << "\"" << focovjson::escape_for_covjson(val) << "\"";
198  }
199  else {
200  *strm << values[indx++];
201  }
202  }
203  }
204 
205  return indx;
206 }
207 
208 template<typename T>
209 void FoDapCovJsonTransform::covjsonSimpleTypeArray(ostream *strm, libdap::Array *a, string indent, bool sendData)
210 {
211  string childindent = indent + _indent_increment;
212  bool axisRetrieved = false;
213  bool parameterRetrieved = false;
214 
215  currDataType = a->var()->type_name();
216 
217  // FOR TESTING AND DEBUGGING PURPOSES
218  // *strm << "\"type_name\": \"" << a->var()->type_name() << "\"" << endl;
219 
220  getAttributes(strm, a->get_attr_table(), a->name(), &axisRetrieved, &parameterRetrieved);
221 
222  // a->print_val(*strm, "\n", true); // For testing purposes
223 
224  // sendData = false; // For testing purposes
225 
226  // If we are dealing with an Axis
227  if((axisRetrieved == true) && (parameterRetrieved == false)) {
228  struct Axis *currAxis;
229  currAxis = axes[axisCount - 1];
230 
231  int numDim = a->dimensions(true);
232  vector<unsigned int> shape(numDim);
233  long length = focovjson::computeConstrainedShape(a, &shape);
234 
235  // FOR TESTING AND DEBUGGING PURPOSES
236  // *strm << "\"numDimensions\": \"" << numDim << "\"" << endl;
237  // *strm << "\"length\": \"" << length << "\"" << endl << endl;
238 
239  if (currAxis->name.compare("t") != 0) {
240  if (sendData) {
241  currAxis->values += "\"values\": [";
242  unsigned int indx = 0;
243  vector<T> src(length);
244  a->value(&src[0]);
245 
246  ostringstream astrm;
247  indx = covjsonSimpleTypeArrayWorker(&astrm, &src[0], 0, &shape, 0);
248  currAxis->values += astrm.str();
249 
250  currAxis->values += "]";
251 
252  if (length != indx) {
253  BESDEBUG(FoDapCovJsonTransform_debug_key,
254  "covjsonSimpleTypeArray(Axis) - indx NOT equal to content length! indx: " << indx << " length: " << length << endl);
255  }
256  assert(length == indx);
257  }
258  else {
259  currAxis->values += "\"values\": []";
260  }
261  }
262  }
263 
264  // If we are dealing with a Parameter
265  else if(axisRetrieved == false && parameterRetrieved == true) {
266  struct Parameter *currParameter;
267  currParameter = parameters[parameterCount - 1];
268 
269  int numDim = a->dimensions(true);
270  vector<unsigned int> shape(numDim);
271  long length = focovjson::computeConstrainedShape(a, &shape);
272 
273  // FOR TESTING AND DEBUGGING PURPOSES
274  // *strm << "\"numDimensions\": \"" << a->dimensions(true) << "\"" << endl;
275  // *strm << "\"length\": \"" << length << "\"" << endl << endl;
276 
277  currParameter->shape += "\"shape\": [";
278  for(vector<unsigned int>::size_type i = 0; i < shape.size(); i++) {
279  if(i > 0) {
280  currParameter->shape += ", ";
281  }
282 
283  // Process the shape's values, which are strings,
284  // convert them into integers, and store them
285  ostringstream otemp;
286  istringstream itemp;
287  int tempVal = 0;
288  otemp << shape[i];
289  istringstream (otemp.str());
290  istringstream (otemp.str()) >> tempVal;
291  shapeVals.push_back(tempVal);
292 
293  // t may only have 1 value: the origin timestamp
294  // DANGER: t may not yet be defined
295  if((i == 0) && tExists) {
296  currParameter->shape += "1";
297  }
298  else {
299  currParameter->shape += otemp.str();
300  }
301  }
302  currParameter->shape += "],";
303 
304  if (sendData) {
305  currParameter->values += "\"values\": [";
306  unsigned int indx = 0;
307  vector<T> src(length);
308  a->value(&src[0]);
309 
310  ostringstream pstrm;
311  indx = covjsonSimpleTypeArrayWorker(&pstrm, &src[0], 0, &shape, 0);
312  currParameter->values += pstrm.str();
313 
314  currParameter->values += "]";
315 
316  if (length != indx) {
317  BESDEBUG(FoDapCovJsonTransform_debug_key,
318  "covjsonSimpleTypeArray(Parameter) - indx NOT equal to content length! indx: " << indx << " length: " << length << endl);
319  }
320  assert(length == indx);
321  }
322  else {
323  currParameter->values += "\"values\": []";
324  }
325  }
326 }
327 
328 void FoDapCovJsonTransform::covjsonStringArray(ostream *strm, libdap::Array *a, string indent, bool sendData)
329 {
330  string childindent = indent + _indent_increment;
331  bool axisRetrieved = false;
332  bool parameterRetrieved = false;
333 
334  currDataType = a->var()->type_name();
335 
336  // FOR TESTING AND DEBUGGING PURPOSES
337  // *strm << "\"attr_tableName\": \"" << a->name() << "\"" << endl;
338 
339  // FOR TESTING AND DEBUGGING PURPOSES
340  // *strm << "\"type_name\": \"" << a->var()->type_name() << "\"" << endl;
341 
342  getAttributes(strm, a->get_attr_table(), a->name(), &axisRetrieved, &parameterRetrieved);
343 
344  // a->print_val(*strm, "\n", true); // For testing purposes
345 
346  // sendData = false; // For testing purposes
347 
348  // If we are dealing with an Axis
349  if((axisRetrieved == true) && (parameterRetrieved == false)) {
350  struct Axis *currAxis;
351  currAxis = axes[axisCount - 1];
352 
353  int numDim = a->dimensions(true);
354  vector<unsigned int> shape(numDim);
355  long length = focovjson::computeConstrainedShape(a, &shape);
356 
357  if (currAxis->name.compare("t") != 0) {
358  if (sendData) {
359  currAxis->values += "\"values\": ";
360  unsigned int indx = 0;
361  // The string type utilizes a specialized version of libdap:Array.value()
362  vector<string> sourceValues;
363  a->value(sourceValues);
364 
365  ostringstream astrm;
366  indx = covjsonSimpleTypeArrayWorker(&astrm, (string *) (&sourceValues[0]), 0, &shape, 0);
367  currAxis->values += astrm.str();
368 
369  if (length != indx) {
370  BESDEBUG(FoDapCovJsonTransform_debug_key,
371  "covjsonStringArray(Axis) - indx NOT equal to content length! indx: " << indx << " length: " << length << endl);
372  }
373  assert(length == indx);
374  }
375  else {
376  currAxis->values += "\"values\": []";
377  }
378  }
379  }
380 
381  // If we are dealing with a Parameter
382  else if(axisRetrieved == false && parameterRetrieved == true) {
383  struct Parameter *currParameter;
384  currParameter = parameters[parameterCount - 1];
385 
386  int numDim = a->dimensions(true);
387  vector<unsigned int> shape(numDim);
388  long length = focovjson::computeConstrainedShape(a, &shape);
389 
390  currParameter->shape += "\"shape\": [";
391  for(vector<unsigned int>::size_type i = 0; i < shape.size(); i++) {
392  if(i > 0) {
393  currParameter->shape += ", ";
394  }
395 
396  // Process the shape's values, which are strings,
397  // convert them into integers, and store them
398  ostringstream otemp;
399  istringstream itemp;
400  int tempVal = 0;
401  otemp << shape[i];
402  istringstream (otemp.str());
403  istringstream (otemp.str()) >> tempVal;
404  shapeVals.push_back(tempVal);
405 
406  // t may only have 1 value: the origin timestamp
407  // DANGER: t may not yet be defined
408  if((i == 0) && tExists) {
409  currParameter->shape += "1";
410  }
411  else {
412  currParameter->shape += otemp.str();
413  }
414  }
415  currParameter->shape += "],";
416 
417  if (sendData) {
418  currParameter->values += "\"values\": ";
419  unsigned int indx = 0;
420  // The string type utilizes a specialized version of libdap:Array.value()
421  vector<string> sourceValues;
422  a->value(sourceValues);
423 
424  ostringstream pstrm;
425  indx = covjsonSimpleTypeArrayWorker(&pstrm, (string *) (&sourceValues[0]), 0, &shape, 0);
426  currParameter->values += pstrm.str();
427 
428  if (length != indx) {
429  BESDEBUG(FoDapCovJsonTransform_debug_key,
430  "covjsonStringArray(Parameter) - indx NOT equal to content length! indx: " << indx << " length: " << length << endl);
431  }
432  assert(length == indx);
433  }
434  else {
435  currParameter->values += "\"values\": []";
436  }
437  }
438 }
439 
440 void FoDapCovJsonTransform::addAxis(string name, string values)
441 {
442  struct Axis *newAxis = new Axis;
443 
444  newAxis->name = name;
445  newAxis->values = values;
446 
447  this->axes.push_back(newAxis);
448  this->axisCount++;
449 }
450 
451 void FoDapCovJsonTransform::addParameter(string id, string name, string type, string dataType, string unit,
452  string longName, string standardName, string shape, string values)
453 {
454  struct Parameter *newParameter = new Parameter;
455 
456  newParameter->id = id;
457  newParameter->name = name;
458  newParameter->type = type;
459  newParameter->dataType = dataType;
460  newParameter->unit = unit;
461  newParameter->longName = longName;
462  newParameter->standardName = standardName;
463  newParameter->shape = shape;
464  newParameter->values = values;
465 
466  this->parameters.push_back(newParameter);
467  this->parameterCount++;
468 }
469 
470 void FoDapCovJsonTransform::getAttributes(ostream *strm, libdap::AttrTable &attr_table, string name,
471  bool *axisRetrieved, bool *parameterRetrieved)
472 {
473  string currAxisName;
474  string currAxisTimeOrigin;
475  string currUnit;
476  string currLongName;
477  string currStandardName;
478 
479  isAxis = false;
480  isParam = false;
481 
482  *axisRetrieved = false;
483  *parameterRetrieved = false;
484 
485  // FOR TESTING AND DEBUGGING PURPOSES
486  //*strm << "\"attr_tableName\": \"" << name << "\"" << endl;
487 
488  // Using CF-1.6 naming conventions -- Also checks for Coads Climatology conventions
489  // http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html
490  if((name.compare("lon") == 0) || (name.compare("LON") == 0)
491  || (name.compare("longitude") == 0) || (name.compare("LONGITUDE") == 0)
492  || (name.compare("COADSX") == 0)) {
493 
494  // FOR TESTING AND DEBUGGING PURPOSES
495  // *strm << "\"Found X-Axis\": \"" << name << "\"" << endl;
496 
497  if(!xExists) {
498  xExists = true;
499  isAxis = true;
500  currAxisName = "x";
501  }
502  }
503  else if((name.compare("lat") == 0) || (name.compare("LAT") == 0)
504  || (name.compare("latitude") == 0) || (name.compare("LATITUDE") == 0)
505  || (name.compare("COADSY") == 0)) {
506 
507  // FOR TESTING AND DEBUGGING PURPOSES
508  // *strm << "\"Found Y-Axis\": \"" << name << "\"" << endl;
509 
510  if(!yExists) {
511  yExists = true;
512  isAxis = true;
513  currAxisName = "y";
514  }
515  }
516  else if((name.compare("lev") == 0) || (name.compare("LEV") == 0)
517  || (name.compare("height") == 0) || (name.compare("HEIGHT") == 0)
518  || (name.compare("depth") == 0) || (name.compare("DEPTH") == 0)
519  || (name.compare("pres") == 0) || (name.compare("PRES") == 0)) {
520 
521  // FOR TESTING AND DEBUGGING PURPOSES
522  // *strm << "\"Found Z-Axis\": \"" << name << "\"" << endl;
523 
524  if(!zExists) {
525  zExists = true;
526  isAxis = true;
527  currAxisName = "z";
528  }
529  }
530  else if((name.compare("time") == 0) || (name.compare("TIME") == 0)) {
531 
532  // FOR TESTING AND DEBUGGING PURPOSES
533  // *strm << "\"Found T-Axis\": \"" << name << "\"" << endl;
534 
535  if(!tExists) {
536  tExists = true;
537  isAxis = true;
538  currAxisName = "t";
539  }
540  }
541  else {
542  isParam = true;
543  }
544 
545  // Only do more if there are actually attributes in the table
546  if(attr_table.get_size() != 0) {
547  libdap::AttrTable::Attr_iter begin = attr_table.attr_begin();
548  libdap::AttrTable::Attr_iter end = attr_table.attr_end();
549 
550  for(libdap::AttrTable::Attr_iter at_iter = begin; at_iter != end; at_iter++) {
551  // FOR TESTING AND DEBUGGING PURPOSES
552  // attr_table.print(*strm);
553 
554  switch (attr_table.get_attr_type(at_iter)) {
555  case libdap::Attr_container: {
556  libdap::AttrTable *atbl = attr_table.get_attr_table(at_iter);
557  // Recursive call for child attribute table
558  getAttributes(strm, *atbl, name, axisRetrieved, parameterRetrieved);
559  break;
560  }
561  default: {
562  vector<string> *values = attr_table.get_attr_vector(at_iter);
563 
564  for(vector<string>::size_type i = 0; i < values->size(); i++) {
565  string currName = attr_table.get_name(at_iter);
566  string currValue = (*values)[i];
567 
568  // FOR TESTING AND DEBUGGING PURPOSES
569  //*strm << "\"currName\": \"" << currName << "\", \"currValue\": \"" << currValue << "\"" << endl;
570 
571  // From Climate and Forecast (CF) Conventions:
572  // http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#_description_of_the_data
573 
574  // We continue to support the use of the units and long_name attributes as defined in COARDS.
575  // We extend COARDS by adding the optional standard_name attribute which is used to provide unique
576  // identifiers for variables. This is important for data exchange since one cannot necessarily
577  // identify a particular variable based on the name assigned to it by the institution that provided
578  // the data.
579 
580  // The standard_name attribute can be used to identify variables that contain coordinate data. But since it is an
581  // optional attribute, applications that implement these standards must continue to be able to identify coordinate
582  // types based on the COARDS conventions.
583 
584  // See http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
585  if(currName.compare("units") == 0) {
586  currUnit = currValue;
587 
588  if(isAxis) {
589  if(currAxisName.compare("t") == 0) {
590  currAxisTimeOrigin = currValue;
591  }
592  }
593  }
594 
595  // Per Jon Blower:
596  // observedProperty->label comes from:
597  // - The CF long_name, if it exists
598  // - If not, the CF standard_name, perhaps with underscores removed
599  // - If the standard_name doesn’t exist, use the variable ID
600  // See http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#long-name
601  else if(currName.compare("long_name") == 0) {
602  currLongName = currValue;
603  }
604  // See http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#standard-name
605  else if(currName.compare("standard_name") == 0) {
606  currStandardName = currValue;
607  }
608  }
609 
610  break;
611  }
612  }
613  }
614  }
615 
616  if(isAxis) {
617  // If we're dealing with the time axis, capture the time origin
618  // timestamp value with the appropriate formatting for printing.
619  // @TODO See https://covjson.org/spec/#temporal-reference-systems
620  if(currAxisName.compare("t") == 0) {
621  addAxis(currAxisName, "\"values\": [\"" + sanitizeTimeOriginString(currAxisTimeOrigin) + "\"]");
622  }
623  else {
624  addAxis(currAxisName, "");
625  }
626 
627  // See https://covjson.org/spec/#projected-coordinate-reference-systems
628  if((currUnit.find("east") != string::npos) || (currUnit.find("East") != string::npos) ||
629  (currUnit.find("north") != string::npos) || (currUnit.find("North") != string::npos)) {
630  coordRefType = "ProjectedCRS";
631  }
632 
633  *axisRetrieved = true;
634  }
635  else if(isParam) {
636  addParameter("", name, "", currDataType, currUnit, currLongName, currStandardName, "", "");
637  *parameterRetrieved = true;
638  }
639  else {
640  // Do nothing
641  }
642 }
643 
644 string FoDapCovJsonTransform::sanitizeTimeOriginString(string timeOrigin)
645 {
646  // If the calendar is based on years, months, days,
647  // then the referenced values SHOULD use one of the
648  // following ISO8601-based lexical representations:
649 
650  // YYYY
651  // ±XYYYY (where X stands for extra year digits)
652  // YYYY-MM
653  // YYYY-MM-DD
654  // YYYY-MM-DDTHH:MM:SS[.F]Z where Z is either “Z”
655  // or a time scale offset + -HH:MM
656  // ex: "2018-01-01T00:12:20Z"
657 
658  // If calendar dates with reduced precision are
659  // used in a lexical representation (e.g. "2016"),
660  // then a client SHOULD interpret those dates in
661  // that reduced precision.
662 
663  // Remove any commonly found words from the origin timestamp
664  vector<string> subStrs = { "hours", "hour", "minutes", "minute",
665  "seconds", "second", "since", " " };
666 
667  string cleanTimeOrigin = timeOrigin;
668 
669  // If base time, use an arbitrary base time string
670  if(timeOrigin.find("base_time") != string::npos) {
671  cleanTimeOrigin = "2020-01-01T12:00:00Z";
672  }
673  else {
674  for(unsigned int i = 0; i < subStrs.size(); i++)
675  focovjson::removeSubstring(cleanTimeOrigin, subStrs[i]);
676  }
677 
678  return cleanTimeOrigin;
679 }
680 
682  _dds(dds), _returnAs(""), _indent_increment(" "), atomicVals(""), currDataType(""), domainType("Unknown"),
683  coordRefType("GeographicCRS"), xExists(false), yExists(false), zExists(false), tExists(false), isParam(false),
684  isAxis(false), canConvertToCovJson(false), axisCount(0), parameterCount(0)
685 {
686  if (!_dds) throw BESInternalError("File out COVJSON, null DDS passed to constructor", __FILE__, __LINE__);
687 }
688 
689 void FoDapCovJsonTransform::dump(ostream &strm) const
690 {
691  strm << BESIndent::LMarg << "FoDapCovJsonTransform::dump - (" << (void *) this << ")" << endl;
692  BESIndent::Indent();
693  if(_dds != 0) {
694  _dds->print(strm);
695  }
696  BESIndent::UnIndent();
697 }
698 
699 void FoDapCovJsonTransform::transform(ostream &ostrm, bool sendData, bool testOverride)
700 {
701  transform(&ostrm, _dds, "", sendData, testOverride);
702 }
703 
704 void FoDapCovJsonTransform::transform(ostream *strm, libdap::Constructor *cnstrctr, string indent, bool sendData)
705 {
706  vector<libdap::BaseType *> leaves;
707  vector<libdap::BaseType *> nodes;
708  // Sort the variables into two sets
709  libdap::DDS::Vars_iter vi = cnstrctr->var_begin();
710  libdap::DDS::Vars_iter ve = cnstrctr->var_end();
711 
712  for(; vi != ve; vi++) {
713  if((*vi)->send_p()) {
714  libdap::BaseType *v = *vi;
715  libdap::Type type = v->type();
716 
717  if(type == libdap::dods_array_c) {
718  type = v->var()->type();
719  }
720  if(v->is_constructor_type() || (v->is_vector_type() && v->var()->is_constructor_type())) {
721  nodes.push_back(v);
722  }
723  else {
724  leaves.push_back(v);
725  }
726  }
727  }
728 
729  transformNodeWorker(strm, leaves, nodes, indent, sendData);
730 }
731 
732 void FoDapCovJsonTransform::transformNodeWorker(ostream *strm, vector<libdap::BaseType *> leaves,
733  vector<libdap::BaseType *> nodes, string indent, bool sendData)
734 {
735  // Get this node's leaves
736  for(vector<libdap::BaseType *>::size_type l = 0; l < leaves.size(); l++) {
737  libdap::BaseType *v = leaves[l];
738  BESDEBUG(FoDapCovJsonTransform_debug_key, "Processing LEAF: " << v->name() << endl);
739 
740  // FOR TESTING AND DEBUGGING PURPOSES
741  // *strm << "Processing LEAF: " << v->name() << endl;
742 
743  transform(strm, v, indent + _indent_increment, sendData);
744  }
745 
746  // Get this node's child nodes
747  for(vector<libdap::BaseType *>::size_type n = 0; n < nodes.size(); n++) {
748  libdap::BaseType *v = nodes[n];
749  BESDEBUG(FoDapCovJsonTransform_debug_key, "Processing NODE: " << v->name() << endl);
750 
751  // FOR TESTING AND DEBUGGING PURPOSES
752  // *strm << "Processing NODE: " << v->name() << endl;
753 
754  transform(strm, v, indent + _indent_increment, sendData);
755  }
756 }
757 
758 void FoDapCovJsonTransform::printCoverageJSON(ostream *strm, string indent, bool testOverride)
759 {
760  // Determine if the attribute values we read can be converted to CovJSON.
761  // Test override forces printing output to stream regardless of whether
762  // or not the file can be converted into CoverageJSON format.
763  if(testOverride) {
764  canConvertToCovJson = true;
765  }
766  else {
767  canConvertToCovJson = canConvert();
768  }
769 
770  // Only print if this file can be converted to CovJSON
771  if(canConvertToCovJson) {
772  // Prints the entire Coverage to stream
773  printCoverage(strm, indent);
774  }
775  else {
776  // If this file can't be converted, then its failing spatial/temporal requirements
777  throw BESInternalError("File cannot be converted to CovJSON format due to missing or incompatible spatial dimensions", __FILE__, __LINE__);
778  }
779 }
780 
781 void FoDapCovJsonTransform::printCoverage(ostream *strm, string indent)
782 {
783  string child_indent1 = indent + _indent_increment;
784  string child_indent2 = child_indent1 + _indent_increment;
785 
786  BESDEBUG(FoDapCovJsonTransform_debug_key, "Printing COVERAGE" << endl);
787 
788  *strm << indent << "{" << endl;
789  *strm << child_indent1 << "\"type\": \"Coverage\"," << endl;
790 
791  printDomain(strm, child_indent1);
792 
793  printParameters(strm, child_indent1);
794 
795  printRanges(strm, child_indent1);
796 
797  *strm << indent << "}" << endl;
798 }
799 
800 void FoDapCovJsonTransform::printDomain(ostream *strm, string indent)
801 {
802  string child_indent1 = indent + _indent_increment;
803 
804  BESDEBUG(FoDapCovJsonTransform_debug_key, "Printing DOMAIN" << endl);
805 
806  *strm << indent << "\"domain\": {" << endl;
807  *strm << child_indent1 << "\"type\" : \"Domain\"," << endl;
808  *strm << child_indent1 << "\"domainType\": \"" + domainType + "\"," << endl;
809 
810  // Prints the axes metadata and range values
811  printAxes(strm, child_indent1);
812 
813  // Prints the references for the given Axes
814  printReference(strm, child_indent1);
815 
816  *strm << indent << "}," << endl;
817 }
818 
819 void FoDapCovJsonTransform::printAxes(ostream *strm, string indent)
820 {
821  string child_indent1 = indent + _indent_increment;
822  string child_indent2 = child_indent1 + _indent_increment;
823 
824  BESDEBUG(FoDapCovJsonTransform_debug_key, "Printing AXES" << endl);
825 
826  // FOR TESTING AND DEBUGGING PURPOSES
827  // *strm << "\"type_name\": \"" << a->var()->type_name() << "\"" << endl;
828 
829  // Write the axes to strm
830  *strm << indent << "\"axes\": {" << endl;
831  for(unsigned int i = 0; i < axisCount; i++) {
832  for(unsigned int j = 0; j < axisCount; j++) {
833  // Logic for printing axes in the appropriate order
834 
835  // If x, y, z, and t all exist (x, y, z, t)
836  if(xExists && yExists && zExists && tExists) {
837  if((i == 0) && (axes[j]->name.compare("x") == 0)) {
838  *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
839  *strm << child_indent2 << axes[j]->values << endl;
840  }
841  else if((i == 1) && (axes[j]->name.compare("y") == 0)) {
842  *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
843  *strm << child_indent2 << axes[j]->values << endl;
844  }
845  else if((i == 2) && (axes[j]->name.compare("z") == 0)) {
846  *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
847  *strm << child_indent2 << axes[j]->values << endl;
848  }
849  else if((i == 3) && (axes[j]->name.compare("t") == 0)) {
850  *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
851  *strm << child_indent2 << axes[j]->values << endl;
852  }
853  }
854  // If just x, y, and t exist (x, y, t)
855  else if(xExists && yExists && !zExists && tExists) {
856  if((i == 0) && (axes[j]->name.compare("x") == 0)) {
857  *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
858  *strm << child_indent2 << axes[j]->values << endl;
859  }
860  else if((i == 1) && (axes[j]->name.compare("y") == 0)) {
861  *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
862  *strm << child_indent2 << axes[j]->values << endl;
863  }
864  else if((i == 2) && (axes[j]->name.compare("t") == 0)) {
865  *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
866  *strm << child_indent2 << axes[j]->values << endl;
867  }
868  }
869  // If just x and y exist (x, y)
870  else if(xExists && yExists && !zExists && !tExists) {
871  if((i == 0) && (axes[j]->name.compare("x") == 0)) {
872  *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
873  *strm << child_indent2 << axes[j]->values << endl;
874  }
875  else if((i == 1) && (axes[j]->name.compare("y") == 0)) {
876  *strm << child_indent1 << "\"" << axes[j]->name << "\": {" << endl;
877  *strm << child_indent2 << axes[j]->values << endl;
878  }
879  }
880  }
881  if(i == axisCount - 1) {
882  *strm << child_indent1 << "}" << endl;
883  }
884  else {
885  *strm << child_indent1 << "}," << endl;
886  }
887  }
888  *strm << indent << "}," << endl;
889 }
890 
891 void FoDapCovJsonTransform::printReference(ostream *strm, string indent)
892 {
893  string child_indent1 = indent + _indent_increment;
894  string child_indent2 = child_indent1 + _indent_increment;
895  string coordVars;
896 
897  BESDEBUG(FoDapCovJsonTransform_debug_key, "Printing REFERENCES" << endl);
898 
899  if(xExists) {
900  coordVars += "\"x\"";
901  }
902 
903  if(yExists) {
904  if(coordVars.length() > 0) {
905  coordVars += ", ";
906  }
907  coordVars += "\"y\"";
908  }
909 
910  if(zExists) {
911  if(coordVars.length() > 0) {
912  coordVars += ", ";
913  }
914  coordVars += "\"z\"";
915  }
916 
917  *strm << indent << "\"referencing\": [{" << endl;
918 
919  // See https://covjson.org/spec/#temporal-reference-systems
920  if(tExists) {
921  *strm << child_indent1 << "\"coordinates\": [\"t\"]," << endl;
922  *strm << child_indent1 << "\"system\": {" << endl;
923  *strm << child_indent2 << "\"type\": \"TemporalRS\"," << endl;
924  *strm << child_indent2 << "\"calendar\": \"Gregorian\"" << endl;
925  *strm << child_indent1 << "}" << endl;
926  *strm << indent << "}," << endl;
927  *strm << indent << "{" << endl;
928  }
929 
930  // See https://covjson.org/spec/#geospatial-coordinate-reference-systems
931  *strm << child_indent1 << "\"coordinates\": [" << coordVars << "]," << endl;
932  *strm << child_indent1 << "\"system\": {" << endl;
933  *strm << child_indent2 << "\"type\": \"" + coordRefType + "\"," << endl;
934 
935  // Most of the datasets I've seen do not contain a link to a coordinate
936  // reference system, so I've set some defaults here - CRH 1/2020
937  if(coordRefType.compare("ProjectedCRS") == 0) {
938  // Projected Coordinate Reference System (north/east): http://www.opengis.net/def/crs/EPSG/0/27700
939  *strm << child_indent2 << "\"id\": \"http://www.opengis.net/def/crs/EPSG/0/27700\"" << endl;
940  }
941  else {
942  if(xExists && yExists && zExists) {
943  // 3-Dimensional Geographic Coordinate Reference System (lat/lon/height): http://www.opengis.net/def/crs/EPSG/0/4979
944  *strm << child_indent2 << "\"id\": \"http://www.opengis.net/def/crs/EPSG/0/4979\"" << endl;
945  }
946  else {
947  // 2-Dimensional Geographic Coordinate Reference System (lat/lon): http://www.opengis.net/def/crs/OGC/1.3/CRS84
948  *strm << child_indent2 << "\"id\": \"http://www.opengis.net/def/crs/OGC/1.3/CRS84\"" << endl;
949  }
950  }
951 
952  *strm << child_indent1 << "}" << endl;
953  *strm << indent << "}]" << endl;
954 }
955 
956 void FoDapCovJsonTransform::printParameters(ostream *strm, string indent)
957 {
958  string child_indent1 = indent + _indent_increment;
959  string child_indent2 = child_indent1 + _indent_increment;
960  string child_indent3 = child_indent2 + _indent_increment;
961  string child_indent4 = child_indent3 + _indent_increment;
962 
963  BESDEBUG(FoDapCovJsonTransform_debug_key, "Printing PARAMETERS" << endl);
964 
965  // Write down the parameter metadata
966  *strm << indent << "\"parameters\": {" << endl;
967  for(unsigned int i = 0; i < parameterCount; i++) {
968  *strm << child_indent1 << "\"" << parameters[i]->name << "\": {" << endl;
969  *strm << child_indent2 << "\"type\": \"Parameter\"," << endl;
970  *strm << child_indent2 << "\"description\": {" << endl;
971 
972  if(parameters[i]->longName.compare("") != 0) {
973  *strm << child_indent3 << "\"en\": \"" << parameters[i]->longName << "\"" << endl;
974  }
975  else if(parameters[i]->standardName.compare("") != 0) {
976  *strm << child_indent3 << "\"en\": \"" << parameters[i]->standardName << "\"" << endl;
977  }
978  else {
979  *strm << child_indent3 << "\"en\": \"" << parameters[i]->name << "\"" << endl;
980  }
981 
982  *strm << child_indent2 << "}," << endl;
983  *strm << child_indent2 << "\"unit\": {" << endl;
984  *strm << child_indent3 << "\"label\": {" << endl;
985  *strm << child_indent4 << "\"en\": \"" << parameters[i]->unit << "\"" << endl;
986  *strm << child_indent3 << "}," << endl;
987  *strm << child_indent3 << "\"symbol\": {" << endl;
988  *strm << child_indent4 << "\"value\": \"" << parameters[i]->unit << "\"," << endl;
989  *strm << child_indent4 << "\"type\": \"http://www.opengis.net/def/uom/UCUM/\"" << endl;
990  *strm << child_indent3 << "}" << endl;
991  *strm << child_indent2 << "}," << endl;
992  *strm << child_indent2 << "\"observedProperty\": {" << endl;
993 
994  // Per Jon Blower:
995  // observedProperty->id comes from the CF standard_name,
996  // mapped to a URI like this: http://vocab.nerc.ac.uk/standard_name/<standard_name>.
997  // If the standard_name is not present, omit the id.
998  if(parameters[i]->standardName.compare("") != 0) {
999  *strm << child_indent3 << "\"id\": \"http://vocab.nerc.ac.uk/standard_name/" << parameters[i]->standardName << "/\"," << endl;
1000  }
1001 
1002  // Per Jon Blower:
1003  // observedProperty->label comes from:
1004  // - The CF long_name, if it exists
1005  // - If not, the CF standard_name, perhaps with underscores removed
1006  // - If the standard_name doesn’t exist, use the variable ID
1007  *strm << child_indent3 << "\"label\": {" << endl;
1008 
1009  if(parameters[i]->longName.compare("") != 0) {
1010  *strm << child_indent4 << "\"en\": \"" << parameters[i]->longName << "\"" << endl;
1011  }
1012  else if(parameters[i]->standardName.compare("") != 0) {
1013  *strm << child_indent4 << "\"en\": \"" << parameters[i]->standardName << "\"" << endl;
1014  }
1015  else {
1016  *strm << child_indent4 << "\"en\": \"" << parameters[i]->name << "\"" << endl;
1017  }
1018 
1019  *strm << child_indent3 << "}" << endl;
1020  *strm << child_indent2 << "}" << endl;
1021 
1022  if(i == parameterCount - 1) {
1023  *strm << child_indent1 << "}" << endl;
1024  }
1025  else {
1026  *strm << child_indent1 << "}," << endl;
1027  }
1028  }
1029 
1030  *strm << indent << "}," << endl;
1031 }
1032 
1033 void FoDapCovJsonTransform::printRanges(ostream *strm, string indent)
1034 {
1035  string child_indent1 = indent + _indent_increment;
1036  string child_indent2 = child_indent1 + _indent_increment;
1037  string child_indent3 = child_indent2 + _indent_increment;
1038  string axisNames;
1039 
1040  BESDEBUG(FoDapCovJsonTransform_debug_key, "Printing RANGES" << endl);
1041 
1042  if(tExists) {
1043  axisNames += "\"t\"";
1044  }
1045 
1046  if(zExists) {
1047  if(axisNames.length() > 0) {
1048  axisNames += ", ";
1049  }
1050  axisNames += "\"z\"";
1051  }
1052 
1053  if(yExists) {
1054  if(axisNames.length() > 0) {
1055  axisNames += ", ";
1056  }
1057  axisNames += "\"y\"";
1058  }
1059 
1060  if(xExists) {
1061  if(axisNames.length() > 0) {
1062  axisNames += ", ";
1063  }
1064  axisNames += "\"x\"";
1065  }
1066 
1067  // Axis name (x, y, or z)
1068  *strm << indent << "\"ranges\": {" << endl;
1069  for(unsigned int i = 0; i < parameterCount; i++) {
1070  string dataType;
1071  // See spec: https://covjson.org/spec/#ndarray-objects
1072  if(parameters[i]->dataType.find("int") == 0 || parameters[i]->dataType.find("Int") == 0
1073  || parameters[i]->dataType.find("integer") == 0 || parameters[i]->dataType.find("Integer") == 0) {
1074  dataType = "integer";
1075  }
1076  else if(parameters[i]->dataType.find("float") == 0 || parameters[i]->dataType.find("Float") == 0) {
1077  dataType = "float";
1078  }
1079  else if(parameters[i]->dataType.find("string") == 0 || parameters[i]->dataType.find("String") == 0) {
1080  dataType = "string";
1081  }
1082  else {
1083  dataType = "string";
1084  }
1085 
1086  // @TODO NEEDS REFACTORING FOR BES ISSUE #244
1087  // https://github.com/OPENDAP/bes/issues/244
1088  *strm << child_indent1 << "\"" << parameters[i]->name << "\": {" << endl;
1089  *strm << child_indent2 << "\"type\": \"NdArray\"," << endl;
1090  *strm << child_indent2 << "\"dataType\": \"" << dataType << "\", " << endl;
1091  *strm << child_indent2 << "\"axisNames\": [" << axisNames << "]," << endl;
1092  *strm << child_indent2 << parameters[i]->shape << endl;
1093  *strm << child_indent2 << parameters[i]->values << endl;
1094 
1095  if(i == parameterCount - 1) {
1096  *strm << child_indent1 << "}" << endl;
1097  }
1098  else {
1099  *strm << child_indent1 << "}," << endl;
1100  }
1101  }
1102 
1103  *strm << indent << "}" << endl;
1104 }
1105 
1106 void FoDapCovJsonTransform::transform(ostream *strm, libdap::DDS *dds, string indent, bool sendData, bool testOverride)
1107 {
1108  // Sort the variables into two sets
1109  vector<libdap::BaseType *> leaves;
1110  vector<libdap::BaseType *> nodes;
1111 
1112  libdap::DDS::Vars_iter vi = dds->var_begin();
1113  libdap::DDS::Vars_iter ve = dds->var_end();
1114  for(; vi != ve; vi++) {
1115  if((*vi)->send_p()) {
1116  libdap::BaseType *v = *vi;
1117  libdap::Type type = v->type();
1118  if(type == libdap::dods_array_c) {
1119  type = v->var()->type();
1120  }
1121  if(v->is_constructor_type() || (v->is_vector_type() && v->var()->is_constructor_type())) {
1122  nodes.push_back(v);
1123  }
1124  else {
1125  leaves.push_back(v);
1126  }
1127  }
1128  }
1129 
1130  // Read through the source DDS leaves and nodes, extract all axes and
1131  // parameter data, and store that data as Axis and Parameters
1132  transformNodeWorker(strm, leaves, nodes, indent + _indent_increment + _indent_increment, sendData);
1133 
1134  // Print the Coverage data to stream as CoverageJSON
1135  printCoverageJSON(strm, indent, testOverride);
1136 }
1137 
1138 void FoDapCovJsonTransform::transform(ostream *strm, libdap::BaseType *bt, string indent, bool sendData)
1139 {
1140  switch(bt->type()) {
1141  // Handle the atomic types - that's easy!
1142  case libdap::dods_byte_c:
1143  case libdap::dods_int16_c:
1144  case libdap::dods_uint16_c:
1145  case libdap::dods_int32_c:
1146  case libdap::dods_uint32_c:
1147  case libdap::dods_float32_c:
1148  case libdap::dods_float64_c:
1149  case libdap::dods_str_c:
1150  case libdap::dods_url_c:
1151  transformAtomic(bt, indent, sendData);
1152  break;
1153 
1154  case libdap::dods_structure_c:
1155  transform(strm, (libdap::Structure *) bt, indent, sendData);
1156  break;
1157 
1158  case libdap::dods_grid_c:
1159  transform(strm, (libdap::Grid *) bt, indent, sendData);
1160  break;
1161 
1162  case libdap::dods_sequence_c:
1163  transform(strm, (libdap::Sequence *) bt, indent, sendData);
1164  break;
1165 
1166  case libdap::dods_array_c:
1167  transform(strm, (libdap::Array *) bt, indent, sendData);
1168  break;
1169 
1170  case libdap::dods_int8_c:
1171  case libdap::dods_uint8_c:
1172  case libdap::dods_int64_c:
1173  case libdap::dods_uint64_c:
1174  case libdap::dods_enum_c:
1175  case libdap::dods_group_c: {
1176  string s = (string) "File out COVJSON, DAP4 types not yet supported.";
1177  throw BESInternalError(s, __FILE__, __LINE__);
1178  break;
1179  }
1180 
1181  default: {
1182  string s = (string) "File out COVJSON, Unrecognized type.";
1183  throw BESInternalError(s, __FILE__, __LINE__);
1184  break;
1185  }
1186  }
1187 }
1188 
1189 void FoDapCovJsonTransform::transformAtomic(libdap::BaseType *b, string indent, bool sendData)
1190 {
1191  string childindent = indent + _indent_increment;
1192  struct Axis *newAxis = new Axis;
1193 
1194  newAxis->name = "test";
1195  if(sendData) {
1196  newAxis->values += "\"values\": [";
1197  if(b->type() == libdap::dods_str_c || b->type() == libdap::dods_url_c) {
1198  libdap::Str *strVar = (libdap::Str *) b;
1199  string tmpString = strVar->value();
1200  newAxis->values += "\"";
1201  newAxis->values += focovjson::escape_for_covjson(tmpString);
1202  newAxis->values += "\"";
1203  }
1204  else {
1205  ostringstream otemp;
1206  istringstream itemp;
1207  int tempVal = 0;
1208  b->print_val(otemp, "", false);
1209  istringstream (otemp.str());
1210  istringstream (otemp.str()) >> tempVal;
1211  newAxis->values += otemp.str();
1212  }
1213  newAxis->values += "]";
1214  }
1215  else {
1216  newAxis->values += "\"values\": []";
1217  }
1218 
1219  axes.push_back(newAxis);
1220  axisCount++;
1221 }
1222 
1223 void FoDapCovJsonTransform::transform(ostream *strm, libdap::Array *a, string indent, bool sendData)
1224 {
1225  BESDEBUG(FoDapCovJsonTransform_debug_key,
1226  "FoCovJsonTransform::transform() - Processing Array. " << " a->type(): " << a->type() << " a->var()->type(): " << a->var()->type() << endl);
1227 
1228  switch(a->var()->type()) {
1229  // Handle the atomic types - that's easy!
1230  case libdap::dods_byte_c:
1231  covjsonSimpleTypeArray<libdap::dods_byte>(strm, a, indent, sendData);
1232  break;
1233 
1234  case libdap::dods_int16_c:
1235  covjsonSimpleTypeArray<libdap::dods_int16>(strm, a, indent, sendData);
1236  break;
1237 
1238  case libdap::dods_uint16_c:
1239  covjsonSimpleTypeArray<libdap::dods_uint16>(strm, a, indent, sendData);
1240  break;
1241 
1242  case libdap::dods_int32_c:
1243  covjsonSimpleTypeArray<libdap::dods_int32>(strm, a, indent, sendData);
1244  break;
1245 
1246  case libdap::dods_uint32_c:
1247  covjsonSimpleTypeArray<libdap::dods_uint32>(strm, a, indent, sendData);
1248  break;
1249 
1250  case libdap::dods_float32_c:
1251  covjsonSimpleTypeArray<libdap::dods_float32>(strm, a, indent, sendData);
1252  break;
1253 
1254  case libdap::dods_float64_c:
1255  covjsonSimpleTypeArray<libdap::dods_float64>(strm, a, indent, sendData);
1256  break;
1257 
1258  case libdap::dods_str_c: {
1259  covjsonStringArray(strm, a, indent, sendData);
1260  break;
1261  }
1262 
1263  case libdap::dods_url_c: {
1264  covjsonStringArray(strm, a, indent, sendData);
1265  break;
1266  }
1267 
1268  case libdap::dods_structure_c:
1269  throw BESInternalError("File out COVJSON, Arrays of Structure objects not a supported return type.", __FILE__, __LINE__);
1270 
1271  case libdap::dods_grid_c:
1272  throw BESInternalError("File out COVJSON, Arrays of Grid objects not a supported return type.", __FILE__, __LINE__);
1273 
1274  case libdap::dods_sequence_c:
1275  throw BESInternalError("File out COVJSON, Arrays of Sequence objects not a supported return type.", __FILE__, __LINE__);
1276 
1277  case libdap::dods_array_c:
1278  throw BESInternalError("File out COVJSON, Arrays of Array objects not a supported return type.", __FILE__, __LINE__);
1279 
1280  case libdap::dods_int8_c:
1281  case libdap::dods_uint8_c:
1282  case libdap::dods_int64_c:
1283  case libdap::dods_uint64_c:
1284  case libdap::dods_enum_c:
1285  case libdap::dods_group_c:
1286  throw BESInternalError("File out COVJSON, DAP4 types not yet supported.", __FILE__, __LINE__);
1287 
1288  default:
1289  throw BESInternalError("File out COVJSON, Unrecognized type.", __FILE__, __LINE__);
1290  }
1291 }
exception thrown if internal error encountered
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.
Type
Type of JSON value.
Definition: rapidjson.h:664