aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@spatialys.com>2019-08-10 15:15:44 +0200
committerEven Rouault <even.rouault@spatialys.com>2019-08-10 15:27:59 +0200
commit63981af418d84749cd4d70752f83fd551100389f (patch)
tree10ddaceb884aa02ea1f3a75334853766cce958d9
parent17dc5eb1b6d09d436c80470497dd2abfb902cf6d (diff)
downloadPROJ-63981af418d84749cd4d70752f83fd551100389f.tar.gz
PROJ-63981af418d84749cd4d70752f83fd551100389f.zip
PROJJSON: add support for DatumEnsemble and Dynamic[Geodetic|Vertical]ReferenceFrame
-rw-r--r--data/crsjson.schema.json275
-rw-r--r--include/proj/datum.hpp6
-rw-r--r--src/iso19111/crs.cpp19
-rw-r--r--src/iso19111/datum.cpp94
-rw-r--r--src/iso19111/io.cpp156
-rw-r--r--test/unit/test_io.cpp266
6 files changed, 746 insertions, 70 deletions
diff --git a/data/crsjson.schema.json b/data/crsjson.schema.json
index 5e2d41d0..e38f3038 100644
--- a/data/crsjson.schema.json
+++ b/data/crsjson.schema.json
@@ -6,6 +6,7 @@
"oneOf": [
{ "$ref": "#/definitions/crs" },
{ "$ref": "#/definitions/datum" },
+ { "$ref": "#/definitions/datum_ensemble" },
{ "$ref": "#/definitions/ellipsoid" },
{ "$ref": "#/definitions/prime_meridian" },
{ "$ref": "#/definitions/single_operation" },
@@ -147,14 +148,20 @@
"crs": {
"oneOf": [
- { "$ref": "#/definitions/geodetic_crs" },
- { "$ref": "#/definitions/derived_crs" },
- { "$ref": "#/definitions/vertical_crs" },
- { "$ref": "#/definitions/compound_crs" },
{ "$ref": "#/definitions/bound_crs" },
+ { "$ref": "#/definitions/compound_crs" },
+ { "$ref": "#/definitions/derived_engineering_crs" },
+ { "$ref": "#/definitions/derived_geodetic_crs" },
+ { "$ref": "#/definitions/derived_parametric_crs" },
+ { "$ref": "#/definitions/derived_projected_crs" },
+ { "$ref": "#/definitions/derived_temporal_crs" },
+ { "$ref": "#/definitions/derived_vertical_crs" },
{ "$ref": "#/definitions/engineering_crs" },
+ { "$ref": "#/definitions/geodetic_crs" },
{ "$ref": "#/definitions/parametric_crs" },
- { "$ref": "#/definitions/temporal_crs" }
+ { "$ref": "#/definitions/projected_crs" },
+ { "$ref": "#/definitions/temporal_crs" },
+ { "$ref": "#/definitions/vertical_crs" }
]
},
@@ -162,27 +169,132 @@
"oneOf": [
{ "$ref": "#/definitions/geodetic_reference_frame" },
{ "$ref": "#/definitions/vertical_reference_frame" },
+ { "$ref": "#/definitions/dynamic_geodetic_reference_frame" },
+ { "$ref": "#/definitions/dynamic_vertical_reference_frame" },
{ "$ref": "#/definitions/temporal_datum" },
{ "$ref": "#/definitions/parametric_datum" },
{ "$ref": "#/definitions/engineering_datum" }
]
},
- "derived_crs": {
+ "datum_ensemble": {
+ "type": "object",
+ "properties": {
+ "type": { "type": "string", "enum": ["DatumEnsemble"] },
+ "name": { "type": "string" },
+ "members": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "name": { "type": "string" },
+ "id": { "$ref": "#/definitions/id" }
+ },
+ "required" : [ "name" ],
+ "additionalProperties": false
+ }
+ },
+ "ellipsoid": { "$ref": "#/definitions/ellipsoid" },
+ "accuracy": { "type": "string" },
+ "id": { "$ref": "#/definitions/id" }
+ },
+ "required" : [ "name", "members", "accuracy" ],
+ "additionalProperties": false
+ },
+
+ "derived_engineering_crs": {
+ "type": "object",
+ "allOf": [{ "$ref": "#/definitions/object_usage" }],
+ "properties": {
+ "type": { "type": "string",
+ "enum": ["DerivedEngineeringCRS"] },
+ "name": { "type": "string" },
+ "base_crs": { "$ref": "#/definitions/engineering_crs" },
+ "conversion": { "$ref": "#/definitions/conversion" },
+ "coordinate_system": { "$ref": "#/definitions/coordinate_system" },
+ "scope": {},
+ "area": {},
+ "bbox": {},
+ "usages": {},
+ "remarks": {},
+ "id": {}
+ },
+ "required" : [ "name", "base_crs", "conversion", "coordinate_system" ],
+ "additionalProperties": false
+ },
+
+ "derived_geodetic_crs": {
+ "type": "object",
+ "allOf": [{ "$ref": "#/definitions/object_usage" }],
+ "properties": {
+ "type": { "type": "string",
+ "enum": ["DerivedGeodeticCRS",
+ "DerivedGeographicCRS"] },
+ "name": { "type": "string" },
+ "base_crs": { "$ref": "#/definitions/geodetic_crs" },
+ "conversion": { "$ref": "#/definitions/conversion" },
+ "coordinate_system": { "$ref": "#/definitions/coordinate_system" },
+ "scope": {},
+ "area": {},
+ "bbox": {},
+ "usages": {},
+ "remarks": {},
+ "id": {}
+ },
+ "required" : [ "name", "base_crs", "conversion", "coordinate_system" ],
+ "additionalProperties": false
+ },
+
+ "derived_parametric_crs": {
+ "type": "object",
+ "allOf": [{ "$ref": "#/definitions/object_usage" }],
+ "properties": {
+ "type": { "type": "string",
+ "enum": ["DerivedParametricCRS"] },
+ "name": { "type": "string" },
+ "base_crs": { "$ref": "#/definitions/parametric_crs" },
+ "conversion": { "$ref": "#/definitions/conversion" },
+ "coordinate_system": { "$ref": "#/definitions/coordinate_system" },
+ "scope": {},
+ "area": {},
+ "bbox": {},
+ "usages": {},
+ "remarks": {},
+ "id": {}
+ },
+ "required" : [ "name", "base_crs", "conversion", "coordinate_system" ],
+ "additionalProperties": false
+ },
+
+ "derived_projected_crs": {
+ "type": "object",
+ "allOf": [{ "$ref": "#/definitions/object_usage" }],
+ "properties": {
+ "type": { "type": "string",
+ "enum": ["DerivedProjectedCRS"] },
+ "name": { "type": "string" },
+ "base_crs": { "$ref": "#/definitions/projected_crs" },
+ "conversion": { "$ref": "#/definitions/conversion" },
+ "coordinate_system": { "$ref": "#/definitions/coordinate_system" },
+ "scope": {},
+ "area": {},
+ "bbox": {},
+ "usages": {},
+ "remarks": {},
+ "id": {}
+ },
+ "required" : [ "name", "base_crs", "conversion", "coordinate_system" ],
+ "additionalProperties": false
+ },
+
+ "derived_temporal_crs": {
"type": "object",
"allOf": [{ "$ref": "#/definitions/object_usage" }],
"properties": {
"type": { "type": "string",
- "enum": ["ProjectedCRS",
- "DerivedGeodeticCRS",
- "DerivedGeographicCRS",
- "DerivedProjectedCRS",
- "DerivedVerticalCRS",
- "DerivedTemporalCRS",
- "DerivedParametricCRS",
- "DerivedEngineeringCRS"] },
+ "enum": ["DerivedTemporalCRS"] },
"name": { "type": "string" },
- "base_crs": { "$ref": "#/definitions/crs" },
+ "base_crs": { "$ref": "#/definitions/temporal_crs" },
"conversion": { "$ref": "#/definitions/conversion" },
"coordinate_system": { "$ref": "#/definitions/coordinate_system" },
"scope": {},
@@ -196,6 +308,69 @@
"additionalProperties": false
},
+ "derived_vertical_crs": {
+ "type": "object",
+ "allOf": [{ "$ref": "#/definitions/object_usage" }],
+ "properties": {
+ "type": { "type": "string",
+ "enum": ["DerivedVerticalCRS"] },
+ "name": { "type": "string" },
+ "base_crs": { "$ref": "#/definitions/vertical_crs" },
+ "conversion": { "$ref": "#/definitions/conversion" },
+ "coordinate_system": { "$ref": "#/definitions/coordinate_system" },
+ "scope": {},
+ "area": {},
+ "bbox": {},
+ "usages": {},
+ "remarks": {},
+ "id": {}
+ },
+ "required" : [ "name", "base_crs", "conversion", "coordinate_system" ],
+ "additionalProperties": false
+ },
+
+ "dynamic_geodetic_reference_frame": {
+ "type": "object",
+ "allOf": [{ "$ref": "#/definitions/geodetic_reference_frame" }],
+ "properties": {
+ "type": { "type": "string", "enum": ["DynamicGeodeticReferenceFrame"] },
+ "name": {},
+ "anchor": {},
+ "ellipsoid": {},
+ "prime_meridian": {},
+ "frame_reference_epoch": { "type": "number" },
+ "deformation_model": { "type": "string" },
+ "scope": {},
+ "area": {},
+ "bbox": {},
+ "usages": {},
+ "remarks": {},
+ "id": {}
+ },
+ "required" : [ "name", "ellipsoid", "frame_reference_epoch" ],
+ "additionalProperties": false
+ },
+
+ "dynamic_vertical_reference_frame": {
+ "type": "object",
+ "allOf": [{ "$ref": "#/definitions/vertical_reference_frame" }],
+ "properties": {
+ "type": { "type": "string", "enum": ["DynamicVerticalReferenceFrame"] },
+ "name": {},
+ "anchor": {},
+ "frame_reference_epoch": { "type": "number" },
+ "deformation_model": { "type": "string" },
+ "scope": {},
+ "area": {},
+ "bbox": {},
+ "usages": {},
+ "remarks": {},
+ "id": {}
+ },
+ "required" : [ "name", "frame_reference_epoch" ],
+ "additionalProperties": false
+ },
+
"ellipsoid": {
"type": "object",
"oneOf":[
@@ -273,11 +448,16 @@
"geodetic_crs": {
"type": "object",
- "allOf": [{ "$ref": "#/definitions/object_usage" }],
"properties": {
"type": { "type": "string", "enum": ["GeodeticCRS", "GeographicCRS"] },
"name": { "type": "string" },
- "datum": { "$ref": "#/definitions/geodetic_reference_frame" },
+ "datum": {
+ "oneOf": [
+ { "$ref": "#/definitions/geodetic_reference_frame" },
+ { "$ref": "#/definitions/dynamic_geodetic_reference_frame" }
+ ]
+ },
+ "datum_ensemble": { "$ref": "#/definitions/datum_ensemble" },
"coordinate_system": { "$ref": "#/definitions/coordinate_system" },
"scope": {},
"area": {},
@@ -286,7 +466,12 @@
"remarks": {},
"id": {}
},
- "required" : [ "name", "datum" ],
+ "required" : [ "name" ],
+ "description": "One and only one of datum and datum_ensemble must be provided",
+ "allOf": [
+ { "$ref": "#/definitions/object_usage" },
+ { "$ref": "#/definitions/one_and_only_one_of_datum_or_datum_ensemble" }
+ ],
"additionalProperties": false
},
@@ -333,6 +518,23 @@
"additionalProperties": false
},
+ "one_and_only_one_of_datum_or_datum_ensemble": {
+ "allOf": [
+ {
+ "not": {
+ "type": "object",
+ "required": [ "datum", "datum_ensemble" ]
+ }
+ },
+ {
+ "oneOf": [
+ { "type": "object", "required": ["datum"] },
+ { "type": "object", "required": ["datum_ensemble"] }
+ ]
+ }
+ ]
+ },
+
"object_usage": {
"anyOf": [
{
@@ -430,6 +632,27 @@
]
},
+ "projected_crs": {
+ "type": "object",
+ "allOf": [{ "$ref": "#/definitions/object_usage" }],
+ "properties": {
+ "type": { "type": "string",
+ "enum": ["ProjectedCRS"] },
+ "name": { "type": "string" },
+ "base_crs": { "$ref": "#/definitions/geodetic_crs" },
+ "conversion": { "$ref": "#/definitions/conversion" },
+ "coordinate_system": { "$ref": "#/definitions/coordinate_system" },
+ "scope": {},
+ "area": {},
+ "bbox": {},
+ "usages": {},
+ "remarks": {},
+ "id": {}
+ },
+ "required" : [ "name", "base_crs", "conversion", "coordinate_system" ],
+ "additionalProperties": false
+ },
+
"temporal_crs": {
"type": "object",
"allOf": [{ "$ref": "#/definitions/object_usage" }],
@@ -548,11 +771,16 @@
"vertical_crs": {
"type": "object",
- "allOf": [{ "$ref": "#/definitions/object_usage" }],
"properties": {
"type": { "type": "string", "enum": ["VerticalCRS"] },
"name": { "type": "string" },
- "datum": { "$ref": "#/definitions/vertical_reference_frame" },
+ "datum": {
+ "oneOf": [
+ { "$ref": "#/definitions/vertical_reference_frame" },
+ { "$ref": "#/definitions/dynamic_vertical_reference_frame" }
+ ]
+ },
+ "datum_ensemble": { "$ref": "#/definitions/datum_ensemble" },
"coordinate_system": { "$ref": "#/definitions/coordinate_system" },
"scope": {},
"area": {},
@@ -561,7 +789,12 @@
"remarks": {},
"id": {}
},
- "required" : [ "name", "datum" ],
+ "required" : [ "name"],
+ "description": "One and only one of datum and datum_ensemble must be provided",
+ "allOf": [
+ { "$ref": "#/definitions/object_usage" },
+ { "$ref": "#/definitions/one_and_only_one_of_datum_or_datum_ensemble" }
+ ],
"additionalProperties": false
},
diff --git a/include/proj/datum.hpp b/include/proj/datum.hpp
index 3724bfb1..a26c94b9 100644
--- a/include/proj/datum.hpp
+++ b/include/proj/datum.hpp
@@ -118,7 +118,8 @@ using DatumEnsembleNNPtr = util::nn<DatumEnsemblePtr>;
*
* \remark Implements DatumEnsemble from \ref ISO_19111_2019
*/
-class PROJ_GCC_DLL DatumEnsemble final : public common::IdentifiedObject {
+class PROJ_GCC_DLL DatumEnsemble final : public common::IdentifiedObject,
+ public io::IJSONExportable {
public:
//! @cond Doxygen_Suppress
PROJ_DLL ~DatumEnsemble() override;
@@ -136,6 +137,9 @@ class PROJ_GCC_DLL DatumEnsemble final : public common::IdentifiedObject {
//! @cond Doxygen_Suppress
PROJ_INTERNAL void _exportToWKT(io::WKTFormatter *formatter)
const override; // throw(io::FormattingException)
+
+ PROJ_INTERNAL void _exportToJSON(io::JSONFormatter *formatter)
+ const override; // throw(io::FormattingException)
//! @endcond
protected:
diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp
index 4f2ab7c6..b03ece23 100644
--- a/src/iso19111/crs.cpp
+++ b/src/iso19111/crs.cpp
@@ -1362,7 +1362,9 @@ void GeodeticCRS::_exportToJSON(
writer.AddObjKey("datum");
l_datum->_exportToJSON(formatter);
} else {
- // TODO DatumEnsemble
+ writer.AddObjKey("datum_ensemble");
+ formatter->setOmitTypeInImmediateChild();
+ datumEnsemble()->_exportToJSON(formatter);
}
writer.AddObjKey("coordinate_system");
@@ -2113,7 +2115,9 @@ void GeographicCRS::_exportToJSON(
writer.AddObjKey("datum");
l_datum->_exportToJSON(formatter);
} else {
- // TODO DatumEnsemble
+ writer.AddObjKey("datum_ensemble");
+ formatter->setOmitTypeInImmediateChild();
+ datumEnsemble()->_exportToJSON(formatter);
}
writer.AddObjKey("coordinate_system");
@@ -2305,8 +2309,15 @@ void VerticalCRS::_exportToJSON(
writer.Add(l_name);
}
- writer.AddObjKey("datum");
- datum()->_exportToJSON(formatter);
+ const auto &l_datum(datum());
+ if (l_datum) {
+ writer.AddObjKey("datum");
+ l_datum->_exportToJSON(formatter);
+ } else {
+ writer.AddObjKey("datum_ensemble");
+ formatter->setOmitTypeInImmediateChild();
+ datumEnsemble()->_exportToJSON(formatter);
+ }
writer.AddObjKey("coordinate_system");
formatter->setOmitTypeInImmediateChild();
diff --git a/src/iso19111/datum.cpp b/src/iso19111/datum.cpp
index d6e6bc21..65905ca9 100644
--- a/src/iso19111/datum.cpp
+++ b/src/iso19111/datum.cpp
@@ -1264,8 +1264,11 @@ void GeodeticReferenceFrame::_exportToWKT(
void GeodeticReferenceFrame::_exportToJSON(
io::JSONFormatter *formatter) const // throw(FormattingException)
{
- auto objectContext(formatter->MakeObjectContext("GeodeticReferenceFrame",
- !identifiers().empty()));
+ auto dynamicGRF = dynamic_cast<const DynamicGeodeticReferenceFrame *>(this);
+
+ auto objectContext(formatter->MakeObjectContext(
+ dynamicGRF ? "DynamicGeodeticReferenceFrame" : "GeodeticReferenceFrame",
+ !identifiers().empty()));
auto &writer = formatter->writer();
writer.AddObjKey("name");
@@ -1278,6 +1281,17 @@ void GeodeticReferenceFrame::_exportToJSON(
Datum::getPrivate()->exportAnchorDefinition(formatter);
+ if (dynamicGRF) {
+ writer.AddObjKey("frame_reference_epoch");
+ writer.Add(dynamicGRF->frameReferenceEpoch().value());
+
+ const auto &deformationModel = dynamicGRF->deformationModelName();
+ if (deformationModel.has_value()) {
+ writer.AddObjKey("deformation_model");
+ writer.Add(*deformationModel);
+ }
+ }
+
writer.AddObjKey("ellipsoid");
formatter->setOmitTypeInImmediateChild();
ellipsoid()->_exportToJSON(formatter);
@@ -1422,7 +1436,7 @@ void DynamicGeodeticReferenceFrame::_exportToWKT(
// ---------------------------------------------------------------------------
-/** \brief Instantiate a DyanmicGeodeticReferenceFrame
+/** \brief Instantiate a DynamicGeodeticReferenceFrame
*
* @param properties See \ref general_properties.
* At minimum the name should be defined.
@@ -1431,7 +1445,7 @@ void DynamicGeodeticReferenceFrame::_exportToWKT(
* @param primeMeridian the PrimeMeridian.
* @param frameReferenceEpochIn the frame reference epoch.
* @param deformationModelNameIn deformation model name, or empty
- * @return new DyanmicGeodeticReferenceFrame.
+ * @return new DynamicGeodeticReferenceFrame.
*/
DynamicGeodeticReferenceFrameNNPtr DynamicGeodeticReferenceFrame::create(
const util::PropertyMap &properties, const EllipsoidNNPtr &ellipsoid,
@@ -1562,6 +1576,56 @@ void DatumEnsemble::_exportToWKT(
// ---------------------------------------------------------------------------
+//! @cond Doxygen_Suppress
+void DatumEnsemble::_exportToJSON(
+ io::JSONFormatter *formatter) const // throw(FormattingException)
+{
+ auto objectContext(
+ formatter->MakeObjectContext("DatumEnsemble", !identifiers().empty()));
+ auto &writer = formatter->writer();
+
+ writer.AddObjKey("name");
+ auto l_name = nameStr();
+ if (l_name.empty()) {
+ writer.Add("unnamed");
+ } else {
+ writer.Add(l_name);
+ }
+
+ auto l_datums = datums();
+ writer.AddObjKey("members");
+ {
+ auto membersContext(writer.MakeArrayContext(false));
+ for (const auto &datum : l_datums) {
+ auto memberContext(writer.MakeObjectContext());
+ writer.AddObjKey("name");
+ const auto &l_datum_name = datum->nameStr();
+ if (!l_datum_name.empty()) {
+ writer.Add(l_datum_name);
+ } else {
+ writer.Add("unnamed");
+ }
+ datum->formatID(formatter);
+ }
+ }
+
+ auto grfFirst = std::dynamic_pointer_cast<GeodeticReferenceFrame>(
+ l_datums[0].as_nullable());
+ if (grfFirst) {
+ writer.AddObjKey("ellipsoid");
+ formatter->setOmitTypeInImmediateChild();
+ grfFirst->ellipsoid()->_exportToJSON(formatter);
+ }
+
+ writer.AddObjKey("accuracy");
+ writer.Add(positionalAccuracy()->value());
+
+ formatID(formatter);
+}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
/** \brief Instantiate a DatumEnsemble.
*
* @param properties See \ref general_properties.
@@ -1729,8 +1793,11 @@ void VerticalReferenceFrame::_exportToWKT(
void VerticalReferenceFrame::_exportToJSON(
io::JSONFormatter *formatter) const // throw(FormattingException)
{
- auto objectContext(formatter->MakeObjectContext("VerticalReferenceFrame",
- !identifiers().empty()));
+ auto dynamicGRF = dynamic_cast<const DynamicVerticalReferenceFrame *>(this);
+
+ auto objectContext(formatter->MakeObjectContext(
+ dynamicGRF ? "DynamicVerticalReferenceFrame" : "VerticalReferenceFrame",
+ !identifiers().empty()));
auto &writer = formatter->writer();
writer.AddObjKey("name");
@@ -1743,6 +1810,17 @@ void VerticalReferenceFrame::_exportToJSON(
Datum::getPrivate()->exportAnchorDefinition(formatter);
+ if (dynamicGRF) {
+ writer.AddObjKey("frame_reference_epoch");
+ writer.Add(dynamicGRF->frameReferenceEpoch().value());
+
+ const auto &deformationModel = dynamicGRF->deformationModelName();
+ if (deformationModel.has_value()) {
+ writer.AddObjKey("deformation_model");
+ writer.Add(*deformationModel);
+ }
+ }
+
ObjectUsage::baseExportToJSON(formatter);
}
//! @endcond
@@ -1882,7 +1960,7 @@ void DynamicVerticalReferenceFrame::_exportToWKT(
// ---------------------------------------------------------------------------
-/** \brief Instantiate a DyanmicVerticalReferenceFrame
+/** \brief Instantiate a DynamicVerticalReferenceFrame
*
* @param properties See \ref general_properties.
* At minimum the name should be defined.
@@ -1890,7 +1968,7 @@ void DynamicVerticalReferenceFrame::_exportToWKT(
* @param realizationMethodIn the realization method, or empty.
* @param frameReferenceEpochIn the frame reference epoch.
* @param deformationModelNameIn deformation model name, or empty
- * @return new DyanmicVerticalReferenceFrame.
+ * @return new DynamicVerticalReferenceFrame.
*/
DynamicVerticalReferenceFrameNNPtr DynamicVerticalReferenceFrame::create(
const util::PropertyMap &properties,
diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp
index eadc54cc..8467a9e1 100644
--- a/src/iso19111/io.cpp
+++ b/src/iso19111/io.cpp
@@ -4396,8 +4396,13 @@ class JSONParser {
GeodeticCRSNNPtr buildGeodeticCRS(const json &j);
ProjectedCRSNNPtr buildProjectedCRS(const json &j);
ConversionNNPtr buildConversion(const json &j);
+ DatumEnsembleNNPtr buildDatumEnsemble(const json &j);
GeodeticReferenceFrameNNPtr buildGeodeticReferenceFrame(const json &j);
VerticalReferenceFrameNNPtr buildVerticalReferenceFrame(const json &j);
+ DynamicGeodeticReferenceFrameNNPtr
+ buildDynamicGeodeticReferenceFrame(const json &j);
+ DynamicVerticalReferenceFrameNNPtr
+ buildDynamicVerticalReferenceFrame(const json &j);
EllipsoidNNPtr buildEllipsoid(const json &j);
PrimeMeridianNNPtr buildPrimeMeridian(const json &j);
CoordinateSystemNNPtr buildCS(const json &j);
@@ -4690,12 +4695,21 @@ BaseObjectNNPtr JSONParser::create(const json &j)
if (type == "BoundCRS") {
return buildBoundCRS(j);
}
+ if (type == "DatumEnsemble") {
+ return buildDatumEnsemble(j);
+ }
if (type == "GeodeticReferenceFrame") {
return buildGeodeticReferenceFrame(j);
}
if (type == "VerticalReferenceFrame") {
return buildVerticalReferenceFrame(j);
}
+ if (type == "DynamicGeodeticReferenceFrame") {
+ return buildDynamicGeodeticReferenceFrame(j);
+ }
+ if (type == "DynamicVerticalReferenceFrame") {
+ return buildDynamicVerticalReferenceFrame(j);
+ }
if (type == "Ellipsoid") {
return buildEllipsoid(j);
}
@@ -4720,12 +4734,20 @@ BaseObjectNNPtr JSONParser::create(const json &j)
// ---------------------------------------------------------------------------
GeographicCRSNNPtr JSONParser::buildGeographicCRS(const json &j) {
- auto datumJ = getObject(j, "datum");
- if (getType(datumJ) != "GeodeticReferenceFrame") {
- throw ParsingException("Unsupported type for datum.");
- }
- auto datum = buildGeodeticReferenceFrame(datumJ);
+ GeodeticReferenceFramePtr datum;
DatumEnsemblePtr datumEnsemble;
+ if (j.contains("datum")) {
+ auto datumJ = getObject(j, "datum");
+ datum = util::nn_dynamic_pointer_cast<GeodeticReferenceFrame>(
+ create(datumJ));
+ if (!datum) {
+ throw ParsingException("datum of wrong type");
+ }
+
+ } else {
+ datumEnsemble =
+ buildDatumEnsemble(getObject(j, "datum_ensemble")).as_nullable();
+ }
auto csJ = getObject(j, "coordinate_system");
auto ellipsoidalCS =
util::nn_dynamic_pointer_cast<EllipsoidalCS>(buildCS(csJ));
@@ -4793,17 +4815,25 @@ ProjectedCRSNNPtr JSONParser::buildProjectedCRS(const json &j) {
// ---------------------------------------------------------------------------
VerticalCRSNNPtr JSONParser::buildVerticalCRS(const json &j) {
- auto datumJ = getObject(j, "datum");
- if (getType(datumJ) != "VerticalReferenceFrame") {
- throw ParsingException("Unsupported type for datum.");
+ VerticalReferenceFramePtr datum;
+ DatumEnsemblePtr datumEnsemble;
+ if (j.contains("datum")) {
+ auto datumJ = getObject(j, "datum");
+ datum = util::nn_dynamic_pointer_cast<VerticalReferenceFrame>(
+ create(datumJ));
+ if (!datum) {
+ throw ParsingException("datum of wrong type");
+ }
+ } else {
+ datumEnsemble =
+ buildDatumEnsemble(getObject(j, "datum_ensemble")).as_nullable();
}
- auto datum = buildVerticalReferenceFrame(datumJ);
auto csJ = getObject(j, "coordinate_system");
auto verticalCS = util::nn_dynamic_pointer_cast<VerticalCS>(buildCS(csJ));
if (!verticalCS) {
throw ParsingException("expected a vertical CS");
}
- return VerticalCRS::create(buildProperties(j), datum,
+ return VerticalCRS::create(buildProperties(j), datum, datumEnsemble,
NN_NO_CHECK(verticalCS));
}
@@ -5098,6 +5128,70 @@ CoordinateSystemNNPtr JSONParser::buildCS(const json &j) {
// ---------------------------------------------------------------------------
+DatumEnsembleNNPtr JSONParser::buildDatumEnsemble(const json &j) {
+ auto membersJ = getArray(j, "members");
+ std::vector<DatumNNPtr> datums;
+ const bool hasEllipsoid(j.contains("ellipsoid"));
+ for (const auto &memberJ : membersJ) {
+ if (!memberJ.is_object()) {
+ throw ParsingException(
+ "Unexpected type for value of a \"members\" member");
+ }
+ auto datumName(getName(memberJ));
+ if (dbContext_ && memberJ.contains("id")) {
+ auto id = getObject(memberJ, "id");
+ auto authority = getString(id, "authority");
+ auto authFactory =
+ AuthorityFactory::create(NN_NO_CHECK(dbContext_), authority);
+ auto code = id["code"];
+ std::string codeStr;
+ if (code.is_string()) {
+ codeStr = code.get<std::string>();
+ } else if (code.is_number_integer()) {
+ codeStr = internal::toString(code.get<int>());
+ } else {
+ throw ParsingException("Unexpected type for value of \"code\"");
+ }
+ try {
+ datums.push_back(authFactory->createDatum(codeStr));
+ } catch (const std::exception &) {
+ throw ParsingException("No Datum of code " + codeStr);
+ }
+ continue;
+ } else if (dbContext_) {
+ auto authFactory = AuthorityFactory::create(NN_NO_CHECK(dbContext_),
+ std::string());
+ auto list = authFactory->createObjectsFromName(
+ datumName, {AuthorityFactory::ObjectType::DATUM},
+ false /* approximate=false*/);
+ if (!list.empty()) {
+ auto datum = util::nn_dynamic_pointer_cast<Datum>(list.front());
+ if (!datum)
+ throw ParsingException(
+ "DatumEnsemble member is not a datum");
+ datums.push_back(NN_NO_CHECK(datum));
+ continue;
+ }
+ }
+
+ // Fallback if no db match
+ if (hasEllipsoid) {
+ datums.emplace_back(GeodeticReferenceFrame::create(
+ buildProperties(memberJ),
+ buildEllipsoid(getObject(j, "ellipsoid")),
+ optional<std::string>(), PrimeMeridian::GREENWICH));
+ } else {
+ datums.emplace_back(
+ VerticalReferenceFrame::create(buildProperties(memberJ)));
+ }
+ }
+ return DatumEnsemble::create(
+ buildProperties(j), datums,
+ PositionalAccuracy::create(getString(j, "accuracy")));
+}
+
+// ---------------------------------------------------------------------------
+
GeodeticReferenceFrameNNPtr
JSONParser::buildGeodeticReferenceFrame(const json &j) {
auto ellipsoidJ = getObject(j, "ellipsoid");
@@ -5114,6 +5208,29 @@ JSONParser::buildGeodeticReferenceFrame(const json &j) {
// ---------------------------------------------------------------------------
+DynamicGeodeticReferenceFrameNNPtr
+JSONParser::buildDynamicGeodeticReferenceFrame(const json &j) {
+ auto ellipsoidJ = getObject(j, "ellipsoid");
+ auto pm = j.contains("prime_meridian")
+ ? buildPrimeMeridian(getObject(j, "prime_meridian"))
+ : PrimeMeridian::GREENWICH;
+ optional<std::string> anchor;
+ if (j.contains("anchor")) {
+ anchor = getString(j, "anchor");
+ }
+ Measure frameReferenceEpoch(getNumber(j, "frame_reference_epoch"),
+ UnitOfMeasure::YEAR);
+ optional<std::string> deformationModel;
+ if (j.contains("deformation_model")) {
+ deformationModel = getString(j, "deformation_model");
+ }
+ return DynamicGeodeticReferenceFrame::create(
+ buildProperties(j), buildEllipsoid(ellipsoidJ), anchor, pm,
+ frameReferenceEpoch, deformationModel);
+}
+
+// ---------------------------------------------------------------------------
+
VerticalReferenceFrameNNPtr
JSONParser::buildVerticalReferenceFrame(const json &j) {
optional<std::string> anchor;
@@ -5125,6 +5242,25 @@ JSONParser::buildVerticalReferenceFrame(const json &j) {
// ---------------------------------------------------------------------------
+DynamicVerticalReferenceFrameNNPtr
+JSONParser::buildDynamicVerticalReferenceFrame(const json &j) {
+ optional<std::string> anchor;
+ if (j.contains("anchor")) {
+ anchor = getString(j, "anchor");
+ }
+ Measure frameReferenceEpoch(getNumber(j, "frame_reference_epoch"),
+ UnitOfMeasure::YEAR);
+ optional<std::string> deformationModel;
+ if (j.contains("deformation_model")) {
+ deformationModel = getString(j, "deformation_model");
+ }
+ return DynamicVerticalReferenceFrame::create(
+ buildProperties(j), anchor, util::optional<RealizationMethod>(),
+ frameReferenceEpoch, deformationModel);
+}
+
+// ---------------------------------------------------------------------------
+
PrimeMeridianNNPtr JSONParser::buildPrimeMeridian(const json &j) {
if (!j.contains("longitude")) {
throw ParsingException("Missing \"longitude\" key");
diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp
index d3c4c8ec..67fdcb77 100644
--- a/test/unit/test_io.cpp
+++ b/test/unit/test_io.cpp
@@ -9135,7 +9135,7 @@ TEST(wkt_export, invalid_angular_unit) {
// ---------------------------------------------------------------------------
-TEST(wkt_export, json_import_ellipsoid_flattened_sphere) {
+TEST(json_import, ellipsoid_flattened_sphere) {
auto json = "{\n"
" \"type\": \"Ellipsoid\",\n"
" \"name\": \"WGS 84\",\n"
@@ -9154,7 +9154,7 @@ TEST(wkt_export, json_import_ellipsoid_flattened_sphere) {
// ---------------------------------------------------------------------------
-TEST(wkt_export, json_import_ellipsoid_major_minor_custom_unit) {
+TEST(json_import, ellipsoid_major_minor_custom_unit) {
auto json = "{\n"
" \"type\": \"Ellipsoid\",\n"
" \"name\": \"foo\",\n"
@@ -9176,7 +9176,7 @@ TEST(wkt_export, json_import_ellipsoid_major_minor_custom_unit) {
// ---------------------------------------------------------------------------
-TEST(wkt_export, json_import_ellipsoid_sphere) {
+TEST(json_import, ellipsoid_sphere) {
auto json = "{\n"
" \"type\": \"Ellipsoid\",\n"
" \"name\": \"Sphere\",\n"
@@ -9194,7 +9194,7 @@ TEST(wkt_export, json_import_ellipsoid_sphere) {
// ---------------------------------------------------------------------------
-TEST(wkt_export, json_import_ellipsoid_errors) {
+TEST(json_import, ellipsoid_errors) {
EXPECT_THROW(createFromUserInput("{", nullptr), ParsingException);
EXPECT_THROW(createFromUserInput("{}", nullptr), ParsingException);
EXPECT_THROW(createFromUserInput("{ \"type\": \"Ellipsoid\" }", nullptr),
@@ -9215,7 +9215,7 @@ TEST(wkt_export, json_import_ellipsoid_errors) {
// ---------------------------------------------------------------------------
-TEST(wkt_export, json_import_prime_meridian) {
+TEST(json_import, prime_meridian) {
auto json = "{\n"
" \"type\": \"PrimeMeridian\",\n"
" \"name\": \"Paris\",\n"
@@ -9236,7 +9236,7 @@ TEST(wkt_export, json_import_prime_meridian) {
// ---------------------------------------------------------------------------
-TEST(wkt_export, json_import_prime_meridian_errors) {
+TEST(json_import, prime_meridian_errors) {
EXPECT_THROW(createFromUserInput("{ \"type\": \"PrimeMeridian\", \"name\": "
"\"foo\" }",
nullptr),
@@ -9249,8 +9249,7 @@ TEST(wkt_export, json_import_prime_meridian_errors) {
// ---------------------------------------------------------------------------
-TEST(wkt_export,
- json_import_geodetic_reference_frame_with_implicit_prime_meridian) {
+TEST(json_import, geodetic_reference_frame_with_implicit_prime_meridian) {
auto json = "{\n"
" \"type\": \"GeodeticReferenceFrame\",\n"
" \"name\": \"World Geodetic System 1984\",\n"
@@ -9261,15 +9260,14 @@ TEST(wkt_export,
" }\n"
"}";
auto obj = createFromUserInput(json, nullptr);
- auto gdr = nn_dynamic_pointer_cast<GeodeticReferenceFrame>(obj);
- ASSERT_TRUE(gdr != nullptr);
- EXPECT_EQ(gdr->exportToJSON((JSONFormatter::create().get())), json);
+ auto grf = nn_dynamic_pointer_cast<GeodeticReferenceFrame>(obj);
+ ASSERT_TRUE(grf != nullptr);
+ EXPECT_EQ(grf->exportToJSON((JSONFormatter::create().get())), json);
}
// ---------------------------------------------------------------------------
-TEST(wkt_export,
- json_import_geodetic_reference_frame_with_explicit_prime_meridian) {
+TEST(json_import, geodetic_reference_frame_with_explicit_prime_meridian) {
auto json = "{\n"
" \"type\": \"GeodeticReferenceFrame\",\n"
" \"name\": \"Nouvelle Triangulation Francaise (Paris)\",\n"
@@ -9291,14 +9289,35 @@ TEST(wkt_export,
" }\n"
"}";
auto obj = createFromUserInput(json, nullptr);
- auto gdr = nn_dynamic_pointer_cast<GeodeticReferenceFrame>(obj);
- ASSERT_TRUE(gdr != nullptr);
- EXPECT_EQ(gdr->exportToJSON((JSONFormatter::create().get())), json);
+ auto grf = nn_dynamic_pointer_cast<GeodeticReferenceFrame>(obj);
+ ASSERT_TRUE(grf != nullptr);
+ EXPECT_EQ(grf->exportToJSON((JSONFormatter::create().get())), json);
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(json_import,
+ dynamic_geodetic_reference_frame_with_implicit_prime_meridian) {
+ auto json = "{\n"
+ " \"type\": \"DynamicGeodeticReferenceFrame\",\n"
+ " \"name\": \"World Geodetic System 1984\",\n"
+ " \"frame_reference_epoch\": 1,\n"
+ " \"deformation_model\": \"foo\",\n"
+ " \"ellipsoid\": {\n"
+ " \"name\": \"WGS 84\",\n"
+ " \"semi_major_axis\": 6378137,\n"
+ " \"inverse_flattening\": 298.257223563\n"
+ " }\n"
+ "}";
+ auto obj = createFromUserInput(json, nullptr);
+ auto dgrf = nn_dynamic_pointer_cast<DynamicGeodeticReferenceFrame>(obj);
+ ASSERT_TRUE(dgrf != nullptr);
+ EXPECT_EQ(dgrf->exportToJSON((JSONFormatter::create().get())), json);
}
// ---------------------------------------------------------------------------
-TEST(wkt_export, json_import_geodetic_reference_frame_errors) {
+TEST(json_import, geodetic_reference_frame_errors) {
EXPECT_THROW(
createFromUserInput(
"{ \"type\": \"GeodeticReferenceFrame\", \"name\": \"foo\" }",
@@ -9308,7 +9327,22 @@ TEST(wkt_export, json_import_geodetic_reference_frame_errors) {
// ---------------------------------------------------------------------------
-TEST(wkt_export, json_import_several_usages) {
+TEST(json_import, dynamic_vertical_reference_frame) {
+ auto json = "{\n"
+ " \"type\": \"DynamicVerticalReferenceFrame\",\n"
+ " \"name\": \"bar\",\n"
+ " \"frame_reference_epoch\": 1,\n"
+ " \"deformation_model\": \"foo\"\n"
+ "}";
+ auto obj = createFromUserInput(json, nullptr);
+ auto dvrf = nn_dynamic_pointer_cast<DynamicVerticalReferenceFrame>(obj);
+ ASSERT_TRUE(dvrf != nullptr);
+ EXPECT_EQ(dvrf->exportToJSON((JSONFormatter::create().get())), json);
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(json_import, several_usages) {
auto json = "{\n"
" \"type\": \"GeodeticReferenceFrame\",\n"
" \"name\": \"World Geodetic System 1984\",\n"
@@ -9341,7 +9375,7 @@ TEST(wkt_export, json_import_several_usages) {
// ---------------------------------------------------------------------------
-TEST(wkt_export, json_import_geographic_crs) {
+TEST(json_import, geographic_crs) {
auto json = "{\n"
" \"type\": \"GeographicCRS\",\n"
" \"name\": \"WGS 84\",\n"
@@ -9392,7 +9426,7 @@ TEST(wkt_export, json_import_geographic_crs) {
// ---------------------------------------------------------------------------
-TEST(wkt_export, json_import_geographic_crs_errors) {
+TEST(json_import, geographic_crs_errors) {
EXPECT_THROW(
createFromUserInput(
"{ \"type\": \"GeographicCRS\", \"name\": \"foo\" }", nullptr),
@@ -9450,7 +9484,7 @@ TEST(wkt_export, json_import_geographic_crs_errors) {
// ---------------------------------------------------------------------------
-TEST(wkt_export, json_import_geocentric_crs) {
+TEST(json_import, geocentric_crs) {
auto json = "{\n"
" \"type\": \"GeodeticCRS\",\n"
" \"name\": \"WGS 84\",\n"
@@ -9495,7 +9529,7 @@ TEST(wkt_export, json_import_geocentric_crs) {
// ---------------------------------------------------------------------------
-TEST(wkt_export, json_import_projected_crs) {
+TEST(json_import, projected_crs) {
auto json = "{\n"
" \"type\": \"ProjectedCRS\",\n"
" \"name\": \"WGS 84 / UTM zone 31N\",\n"
@@ -9615,7 +9649,7 @@ TEST(wkt_export, json_import_projected_crs) {
// ---------------------------------------------------------------------------
-TEST(wkt_export, json_import_compound_crs) {
+TEST(json_import, compound_crs) {
auto json = "{\n"
" \"type\": \"CompoundCRS\",\n"
" \"name\": \"WGS 84 + EGM2008 height\",\n"
@@ -9687,7 +9721,7 @@ TEST(wkt_export, json_import_compound_crs) {
// ---------------------------------------------------------------------------
-TEST(wkt_export, json_import_bound_crs) {
+TEST(json_import, bound_crs) {
auto json =
"{\n"
" \"type\": \"BoundCRS\",\n"
@@ -9788,7 +9822,7 @@ TEST(wkt_export, json_import_bound_crs) {
// ---------------------------------------------------------------------------
-TEST(wkt_export, json_import_transformation) {
+TEST(json_import, transformation) {
auto json = "{\n"
" \"type\": \"Transformation\",\n"
" \"name\": \"GDA94 to GDA2020 (1)\",\n"
@@ -9975,7 +10009,7 @@ TEST(wkt_export, json_import_transformation) {
// ---------------------------------------------------------------------------
-TEST(wkt_export, json_import_concatenated_operation) {
+TEST(json_import, concatenated_operation) {
auto json =
"{\n"
" \"type\": \"ConcatenatedOperation\",\n"
@@ -10402,3 +10436,183 @@ TEST(wkt_export, json_import_concatenated_operation) {
ASSERT_TRUE(concat != nullptr);
EXPECT_EQ(concat->exportToJSON((JSONFormatter::create().get())), json);
}
+
+// ---------------------------------------------------------------------------
+
+TEST(json_import, geographic_crs_with_datum_ensemble) {
+ auto json = "{\n"
+ " \"type\": \"GeographicCRS\",\n"
+ " \"name\": \"WGS 84\",\n"
+ " \"datum_ensemble\": {\n"
+ " \"name\": \"WGS 84 ensemble\",\n"
+ " \"members\": [\n"
+ " {\n"
+ " \"name\": \"World Geodetic System 1984 (Transit)\"\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"World Geodetic System 1984 (G730)\"\n"
+ " }\n"
+ " ],\n"
+ " \"ellipsoid\": {\n"
+ " \"name\": \"WGS 84\",\n"
+ " \"semi_major_axis\": 6378137,\n"
+ " \"inverse_flattening\": 298.257223563\n"
+ " },\n"
+ " \"accuracy\": \"2\"\n"
+ " },\n"
+ " \"coordinate_system\": {\n"
+ " \"subtype\": \"ellipsoidal\",\n"
+ " \"axis\": [\n"
+ " {\n"
+ " \"name\": \"Latitude\",\n"
+ " \"abbreviation\": \"lat\",\n"
+ " \"direction\": \"north\",\n"
+ " \"unit\": \"degree\"\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Longitude\",\n"
+ " \"abbreviation\": \"lon\",\n"
+ " \"direction\": \"east\",\n"
+ " \"unit\": \"degree\"\n"
+ " }\n"
+ " ]\n"
+ " }\n"
+ "}";
+
+ auto expected_json =
+ "{\n"
+ " \"type\": \"GeographicCRS\",\n"
+ " \"name\": \"WGS 84\",\n"
+ " \"datum_ensemble\": {\n"
+ " \"name\": \"WGS 84 ensemble\",\n"
+ " \"members\": [\n"
+ " {\n"
+ " \"name\": \"World Geodetic System 1984 (Transit)\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 1166\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"World Geodetic System 1984 (G730)\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 1152\n"
+ " }\n"
+ " }\n"
+ " ],\n"
+ " \"ellipsoid\": {\n"
+ " \"name\": \"WGS 84\",\n"
+ " \"semi_major_axis\": 6378137,\n"
+ " \"inverse_flattening\": 298.257223563,\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 7030\n"
+ " }\n"
+ " },\n"
+ " \"accuracy\": \"2\"\n"
+ " },\n"
+ " \"coordinate_system\": {\n"
+ " \"subtype\": \"ellipsoidal\",\n"
+ " \"axis\": [\n"
+ " {\n"
+ " \"name\": \"Latitude\",\n"
+ " \"abbreviation\": \"lat\",\n"
+ " \"direction\": \"north\",\n"
+ " \"unit\": \"degree\"\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Longitude\",\n"
+ " \"abbreviation\": \"lon\",\n"
+ " \"direction\": \"east\",\n"
+ " \"unit\": \"degree\"\n"
+ " }\n"
+ " ]\n"
+ " }\n"
+ "}";
+
+ {
+ // No database
+ auto obj = createFromUserInput(json, nullptr);
+ auto gcrs = nn_dynamic_pointer_cast<GeographicCRS>(obj);
+ ASSERT_TRUE(gcrs != nullptr);
+ EXPECT_EQ(gcrs->exportToJSON((JSONFormatter::create().get())), json);
+ }
+ {
+ auto obj = createFromUserInput(json, DatabaseContext::create());
+ auto gcrs = nn_dynamic_pointer_cast<GeographicCRS>(obj);
+ ASSERT_TRUE(gcrs != nullptr);
+ EXPECT_EQ(gcrs->exportToJSON((JSONFormatter::create().get())),
+ expected_json);
+ }
+ {
+ auto obj =
+ createFromUserInput(expected_json, DatabaseContext::create());
+ auto gcrs = nn_dynamic_pointer_cast<GeographicCRS>(obj);
+ ASSERT_TRUE(gcrs != nullptr);
+ EXPECT_EQ(gcrs->exportToJSON((JSONFormatter::create().get())),
+ expected_json);
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(json_import, datum_ensemble_without_ellipsoid) {
+ auto json = "{\n"
+ " \"type\": \"DatumEnsemble\",\n"
+ " \"name\": \"ensemble\",\n"
+ " \"members\": [\n"
+ " {\n"
+ " \"name\": \"member1\"\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"member2\"\n"
+ " }\n"
+ " ],\n"
+ " \"accuracy\": \"2\"\n"
+ "}";
+
+ // No database
+ auto obj = createFromUserInput(json, nullptr);
+ auto ensemble = nn_dynamic_pointer_cast<DatumEnsemble>(obj);
+ ASSERT_TRUE(ensemble != nullptr);
+ EXPECT_EQ(ensemble->exportToJSON((JSONFormatter::create().get())), json);
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(json_import, vertical_crs_with_datum_ensemble) {
+ auto json = "{\n"
+ " \"type\": \"VerticalCRS\",\n"
+ " \"name\": \"foo\",\n"
+ " \"datum_ensemble\": {\n"
+ " \"name\": \"ensemble\",\n"
+ " \"members\": [\n"
+ " {\n"
+ " \"name\": \"member1\"\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"member2\"\n"
+ " }\n"
+ " ],\n"
+ " \"accuracy\": \"2\"\n"
+ " },\n"
+ " \"coordinate_system\": {\n"
+ " \"subtype\": \"vertical\",\n"
+ " \"axis\": [\n"
+ " {\n"
+ " \"name\": \"Gravity-related height\",\n"
+ " \"abbreviation\": \"H\",\n"
+ " \"direction\": \"up\",\n"
+ " \"unit\": \"metre\"\n"
+ " }\n"
+ " ]\n"
+ " }\n"
+ "}";
+
+ // No database
+ auto obj = createFromUserInput(json, nullptr);
+ auto vcrs = nn_dynamic_pointer_cast<VerticalCRS>(obj);
+ ASSERT_TRUE(vcrs != nullptr);
+ EXPECT_EQ(vcrs->exportToJSON((JSONFormatter::create().get())), json);
+}