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
44using std::ostringstream;
45using 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
66bool 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
171template<typename T>
172unsigned 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
208template<typename T>
209void 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
328void 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
440void 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
451void 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
470void 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
644string 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
689void 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
699void FoDapCovJsonTransform::transform(ostream &ostrm, bool sendData, bool testOverride)
700{
701 transform(&ostrm, _dds, "", sendData, testOverride);
702}
703
704void 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
732void 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
758void 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
781void 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
800void 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
819void 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
891void 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
956void 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
1033void 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
1106void 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
1138void 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
1189void 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
1223void 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