aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/crsjson.schema.json292
-rw-r--r--include/proj/coordinateoperation.hpp12
-rw-r--r--include/proj/crs.hpp103
-rw-r--r--include/proj/datum.hpp21
-rw-r--r--include/proj/internal/coordinateoperation_internal.hpp11
-rw-r--r--include/proj/io.hpp3
-rw-r--r--src/iso19111/coordinateoperation.cpp174
-rw-r--r--src/iso19111/crs.cpp275
-rw-r--r--src/iso19111/datum.cpp88
-rw-r--r--src/iso19111/io.cpp263
-rw-r--r--test/unit/test_io.cpp790
-rwxr-xr-xtravis/install.sh19
12 files changed, 1939 insertions, 112 deletions
diff --git a/data/crsjson.schema.json b/data/crsjson.schema.json
index 86f1e13e..5e2d41d0 100644
--- a/data/crsjson.schema.json
+++ b/data/crsjson.schema.json
@@ -8,11 +8,28 @@
{ "$ref": "#/definitions/datum" },
{ "$ref": "#/definitions/ellipsoid" },
{ "$ref": "#/definitions/prime_meridian" },
- { "$ref": "#/definitions/conversion" }
+ { "$ref": "#/definitions/single_operation" },
+ { "$ref": "#/definitions/concatenated_operation" }
],
"definitions": {
+ "abridged_transformation": {
+ "type": "object",
+ "properties": {
+ "type": { "type": "string", "enum": ["AbridgedTransformation"] },
+ "name": { "type": "string" },
+ "method": { "$ref": "#/definitions/method" },
+ "parameters": {
+ "type": "array",
+ "items": { "$ref": "#/definitions/parameter_value" }
+ },
+ "id": { "$ref": "#/definitions/id" }
+ },
+ "required" : [ "name", "method", "parameters" ],
+ "additionalProperties": false
+ },
+
"axis": {
"type": "object",
"properties": {
@@ -40,6 +57,62 @@
"additionalProperties": false
},
+ "bound_crs": {
+ "type": "object",
+ "properties": {
+ "type": { "type": "string", "enum": ["BoundCRS"] },
+ "source_crs": { "$ref": "#/definitions/crs" },
+ "target_crs": { "$ref": "#/definitions/crs" },
+ "transformation": { "$ref": "#/definitions/abridged_transformation" }
+ },
+ "required" : [ "source_crs", "target_crs", "transformation" ],
+ "additionalProperties": false
+ },
+
+ "compound_crs": {
+ "type": "object",
+ "allOf": [{ "$ref": "#/definitions/object_usage" }],
+ "properties": {
+ "type": { "type": "string", "enum": ["CompoundCRS"] },
+ "name": { "type": "string" },
+ "components": {
+ "type": "array",
+ "items": { "$ref": "#/definitions/crs" }
+ },
+ "scope": {},
+ "area": {},
+ "bbox": {},
+ "usages": {},
+ "remarks": {},
+ "id": {}
+ },
+ "required" : [ "name", "components" ],
+ "additionalProperties": false
+ },
+
+ "concatenated_operation": {
+ "type": "object",
+ "allOf": [{ "$ref": "#/definitions/object_usage" }],
+ "properties": {
+ "type": { "type": "string", "enum": ["ConcatenatedOperation"] },
+ "name": { "type": "string" },
+ "source_crs": { "$ref": "#/definitions/crs" },
+ "target_crs": { "$ref": "#/definitions/crs" },
+ "steps": {
+ "type": "array",
+ "items": { "$ref": "#/definitions/single_operation" }
+ },
+ "scope": {},
+ "area": {},
+ "bbox": {},
+ "usages": {},
+ "remarks": {},
+ "id": {}
+ },
+ "required" : [ "name", "source_crs", "target_crs", "steps" ],
+ "additionalProperties": false
+ },
+
"conversion": {
"type": "object",
"properties": {
@@ -52,7 +125,7 @@
},
"id": { "$ref": "#/definitions/id" }
},
- "required" : [ "name", "method", "parameters" ],
+ "required" : [ "name", "method" ],
"additionalProperties": false
},
@@ -75,12 +148,24 @@
"crs": {
"oneOf": [
{ "$ref": "#/definitions/geodetic_crs" },
- { "$ref": "#/definitions/derived_crs" }
+ { "$ref": "#/definitions/derived_crs" },
+ { "$ref": "#/definitions/vertical_crs" },
+ { "$ref": "#/definitions/compound_crs" },
+ { "$ref": "#/definitions/bound_crs" },
+ { "$ref": "#/definitions/engineering_crs" },
+ { "$ref": "#/definitions/parametric_crs" },
+ { "$ref": "#/definitions/temporal_crs" }
]
},
"datum": {
- "oneOf": [ { "$ref": "#/definitions/geodetic_reference_frame" } ]
+ "oneOf": [
+ { "$ref": "#/definitions/geodetic_reference_frame" },
+ { "$ref": "#/definitions/vertical_reference_frame" },
+ { "$ref": "#/definitions/temporal_datum" },
+ { "$ref": "#/definitions/parametric_datum" },
+ { "$ref": "#/definitions/engineering_datum" }
+ ]
},
"derived_crs": {
@@ -88,12 +173,19 @@
"allOf": [{ "$ref": "#/definitions/object_usage" }],
"properties": {
"type": { "type": "string",
- "enum": ["ProjectedCRS", "DerivedGeodeticCRS",
- "DerivedGeographicCRS"] },
+ "enum": ["ProjectedCRS",
+ "DerivedGeodeticCRS",
+ "DerivedGeographicCRS",
+ "DerivedProjectedCRS",
+ "DerivedVerticalCRS",
+ "DerivedTemporalCRS",
+ "DerivedParametricCRS",
+ "DerivedEngineeringCRS"] },
"name": { "type": "string" },
"base_crs": { "$ref": "#/definitions/crs" },
"conversion": { "$ref": "#/definitions/conversion" },
"coordinate_system": { "$ref": "#/definitions/coordinate_system" },
+ "scope": {},
"area": {},
"bbox": {},
"usages": {},
@@ -142,14 +234,52 @@
]
},
+ "engineering_crs": {
+ "type": "object",
+ "allOf": [{ "$ref": "#/definitions/object_usage" }],
+ "properties": {
+ "type": { "type": "string", "enum": ["EngineeringCRS"] },
+ "name": { "type": "string" },
+ "datum": { "$ref": "#/definitions/engineering_datum" },
+ "coordinate_system": { "$ref": "#/definitions/coordinate_system" },
+ "scope": {},
+ "area": {},
+ "bbox": {},
+ "usages": {},
+ "remarks": {},
+ "id": {}
+ },
+ "required" : [ "name", "datum" ],
+ "additionalProperties": false
+ },
+
+ "engineering_datum": {
+ "type": "object",
+ "allOf": [{ "$ref": "#/definitions/object_usage" }],
+ "properties": {
+ "type": { "type": "string", "enum": ["EngineeringDatum"] },
+ "name": { "type": "string" },
+ "anchor": { "type": "string" },
+ "scope": {},
+ "area": {},
+ "bbox": {},
+ "usages": {},
+ "remarks": {},
+ "id": {}
+ },
+ "required" : [ "name" ],
+ "additionalProperties": false
+ },
+
"geodetic_crs": {
"type": "object",
"allOf": [{ "$ref": "#/definitions/object_usage" }],
"properties": {
"type": { "type": "string", "enum": ["GeodeticCRS", "GeographicCRS"] },
"name": { "type": "string" },
- "datum": { "$ref": "#/definitions/datum" },
+ "datum": { "$ref": "#/definitions/geodetic_reference_frame" },
"coordinate_system": { "$ref": "#/definitions/coordinate_system" },
+ "scope": {},
"area": {},
"bbox": {},
"usages": {},
@@ -169,6 +299,7 @@
"anchor": { "type": "string" },
"ellipsoid": { "$ref": "#/definitions/ellipsoid" },
"prime_meridian": { "$ref": "#/definitions/prime_meridian" },
+ "scope": {},
"area": {},
"bbox": {},
"usages": {},
@@ -207,6 +338,7 @@
{
"type": "object",
"properties": {
+ "scope": { "type": "string" },
"area": { "type": "string" },
"bbox": { "$ref": "#/definitions/bbox" },
"remarks": { "type": "string" },
@@ -242,6 +374,43 @@
"additionalProperties": false
},
+ "parametric_crs": {
+ "type": "object",
+ "allOf": [{ "$ref": "#/definitions/object_usage" }],
+ "properties": {
+ "type": { "type": "string", "enum": ["ParametricCRS"] },
+ "name": { "type": "string" },
+ "datum": { "$ref": "#/definitions/parametric_datum" },
+ "coordinate_system": { "$ref": "#/definitions/coordinate_system" },
+ "scope": {},
+ "area": {},
+ "bbox": {},
+ "usages": {},
+ "remarks": {},
+ "id": {}
+ },
+ "required" : [ "name", "datum" ],
+ "additionalProperties": false
+ },
+
+ "parametric_datum": {
+ "type": "object",
+ "allOf": [{ "$ref": "#/definitions/object_usage" }],
+ "properties": {
+ "type": { "type": "string", "enum": ["ParametricDatum"] },
+ "name": { "type": "string" },
+ "anchor": { "type": "string" },
+ "scope": {},
+ "area": {},
+ "bbox": {},
+ "usages": {},
+ "remarks": {},
+ "id": {}
+ },
+ "required" : [ "name" ],
+ "additionalProperties": false
+ },
+
"prime_meridian": {
"type": "object",
"properties": {
@@ -254,6 +423,77 @@
"additionalProperties": false
},
+ "single_operation": {
+ "oneOf": [
+ { "$ref": "#/definitions/conversion" },
+ { "$ref": "#/definitions/transformation" }
+ ]
+ },
+
+ "temporal_crs": {
+ "type": "object",
+ "allOf": [{ "$ref": "#/definitions/object_usage" }],
+ "properties": {
+ "type": { "type": "string", "enum": ["TemporalCRS"] },
+ "name": { "type": "string" },
+ "datum": { "$ref": "#/definitions/temporal_datum" },
+ "coordinate_system": { "$ref": "#/definitions/coordinate_system" },
+ "scope": {},
+ "area": {},
+ "bbox": {},
+ "usages": {},
+ "remarks": {},
+ "id": {}
+ },
+ "required" : [ "name", "datum" ],
+ "additionalProperties": false
+ },
+
+ "temporal_datum": {
+ "type": "object",
+ "allOf": [{ "$ref": "#/definitions/object_usage" }],
+ "properties": {
+ "type": { "type": "string", "enum": ["TemporalDatum"] },
+ "name": { "type": "string" },
+ "calendar": { "type": "string" },
+ "time_origin": { "type": "string" },
+ "scope": {},
+ "area": {},
+ "bbox": {},
+ "usages": {},
+ "remarks": {},
+ "id": {}
+ },
+ "required" : [ "name", "calendar" ],
+ "additionalProperties": false
+ },
+
+ "transformation": {
+ "type": "object",
+ "allOf": [{ "$ref": "#/definitions/object_usage" }],
+ "properties": {
+ "type": { "type": "string", "enum": ["Transformation"] },
+ "name": { "type": "string" },
+ "source_crs": { "$ref": "#/definitions/crs" },
+ "target_crs": { "$ref": "#/definitions/crs" },
+ "interpolation_crs": { "$ref": "#/definitions/crs" },
+ "method": { "$ref": "#/definitions/method" },
+ "parameters": {
+ "type": "array",
+ "items": { "$ref": "#/definitions/parameter_value" }
+ },
+ "accuracy": { "type": "string" },
+ "scope": {},
+ "area": {},
+ "bbox": {},
+ "usages": {},
+ "remarks": {},
+ "id": {}
+ },
+ "required" : [ "name", "source_crs", "target_crs", "method", "parameters" ],
+ "additionalProperties": false
+ },
+
"unit": {
"oneOf": [
{
@@ -281,6 +521,7 @@
"items": {
"type": "object",
"properties": {
+ "scope": { "type": "string" },
"area": { "type": "string" },
"bbox": { "$ref": "#/definitions/bbox" }
},
@@ -303,6 +544,43 @@
{ "type": "number" },
{ "$ref": "#/definitions/value_and_unit" }
]
+ },
+
+ "vertical_crs": {
+ "type": "object",
+ "allOf": [{ "$ref": "#/definitions/object_usage" }],
+ "properties": {
+ "type": { "type": "string", "enum": ["VerticalCRS"] },
+ "name": { "type": "string" },
+ "datum": { "$ref": "#/definitions/vertical_reference_frame" },
+ "coordinate_system": { "$ref": "#/definitions/coordinate_system" },
+ "scope": {},
+ "area": {},
+ "bbox": {},
+ "usages": {},
+ "remarks": {},
+ "id": {}
+ },
+ "required" : [ "name", "datum" ],
+ "additionalProperties": false
+ },
+
+ "vertical_reference_frame": {
+ "type": "object",
+ "allOf": [{ "$ref": "#/definitions/object_usage" }],
+ "properties": {
+ "type": { "type": "string", "enum": ["VerticalReferenceFrame"] },
+ "name": { "type": "string" },
+ "anchor": { "type": "string" },
+ "scope": {},
+ "area": {},
+ "bbox": {},
+ "usages": {},
+ "remarks": {},
+ "id": {}
+ },
+ "required" : [ "name" ],
+ "additionalProperties": false
}
}
diff --git a/include/proj/coordinateoperation.hpp b/include/proj/coordinateoperation.hpp
index 9b2dfe64..2f6095f7 100644
--- a/include/proj/coordinateoperation.hpp
+++ b/include/proj/coordinateoperation.hpp
@@ -116,7 +116,8 @@ using CoordinateOperationNNPtr = util::nn<CoordinateOperationPtr>;
* \remark Implements CoordinateOperation from \ref ISO_19111_2019
*/
class PROJ_GCC_DLL CoordinateOperation : public common::ObjectUsage,
- public io::IPROJStringExportable {
+ public io::IPROJStringExportable,
+ public io::IJSONExportable {
public:
//! @cond Doxygen_Suppress
PROJ_DLL ~CoordinateOperation() override;
@@ -849,8 +850,7 @@ EPSG:8833
*/
-class PROJ_GCC_DLL Conversion : public SingleOperation,
- public io::IJSONExportable {
+class PROJ_GCC_DLL Conversion : public SingleOperation {
public:
//! @cond Doxygen_Suppress
PROJ_DLL ~Conversion() override;
@@ -1550,6 +1550,9 @@ class PROJ_GCC_DLL Transformation : public SingleOperation {
PROJ_INTERNAL void _exportToWKT(io::WKTFormatter *formatter)
const override; // throw(io::FormattingException)
+ PROJ_INTERNAL void _exportToJSON(io::JSONFormatter *formatter)
+ const override; // throw(FormattingException)
+
PROJ_INTERNAL TransformationNNPtr shallowClone() const;
//! @endcond
@@ -1655,6 +1658,9 @@ class PROJ_GCC_DLL ConcatenatedOperation final : public CoordinateOperation {
util::IComparable::Criterion criterion =
util::IComparable::Criterion::STRICT) const override;
+ PROJ_INTERNAL void _exportToJSON(io::JSONFormatter *formatter)
+ const override; // throw(FormattingException)
+
PROJ_INTERNAL static void
fixStepsDirection(const crs::CRSNNPtr &concatOpSourceCRS,
const crs::CRSNNPtr &concatOpTargetCRS,
diff --git a/include/proj/crs.hpp b/include/proj/crs.hpp
index 702b2d36..b83e0a6b 100644
--- a/include/proj/crs.hpp
+++ b/include/proj/crs.hpp
@@ -81,7 +81,8 @@ using CRSNNPtr = util::nn<CRSPtr>;
*
* \remark Implements CRS from \ref ISO_19111_2019
*/
-class PROJ_GCC_DLL CRS : public common::ObjectUsage {
+class PROJ_GCC_DLL CRS : public common::ObjectUsage,
+ public io::IJSONExportable {
public:
//! @cond Doxygen_Suppress
PROJ_DLL ~CRS() override;
@@ -211,8 +212,7 @@ using GeodeticCRSNNPtr = util::nn<GeodeticCRSPtr>;
* \remark Implements GeodeticCRS from \ref ISO_19111_2019
*/
class PROJ_GCC_DLL GeodeticCRS : virtual public SingleCRS,
- public io::IPROJStringExportable,
- public io::IJSONExportable {
+ public io::IPROJStringExportable {
public:
//! @cond Doxygen_Suppress
PROJ_DLL ~GeodeticCRS() override;
@@ -447,6 +447,12 @@ class PROJ_GCC_DLL VerticalCRS : virtual public SingleCRS,
PROJ_INTERNAL void _exportToWKT(io::WKTFormatter *formatter)
const override; // throw(io::FormattingException)
+ PROJ_INTERNAL void _exportToPROJString(io::PROJStringFormatter *formatter)
+ const override; // throw(FormattingException)
+
+ PROJ_INTERNAL void _exportToJSON(io::JSONFormatter *formatter)
+ const override; // throw(FormattingException)
+
PROJ_INTERNAL bool
_isEquivalentTo(const util::IComparable *other,
util::IComparable::Criterion criterion =
@@ -460,9 +466,6 @@ class PROJ_GCC_DLL VerticalCRS : virtual public SingleCRS,
const cs::VerticalCSNNPtr &csIn);
PROJ_INTERNAL VerticalCRS(const VerticalCRS &other);
- PROJ_INTERNAL void _exportToPROJString(io::PROJStringFormatter *formatter)
- const override; // throw(FormattingException)
-
PROJ_INTERNAL std::list<std::pair<CRSNNPtr, int>>
_identify(const io::AuthorityFactoryPtr &authorityFactory) const override;
@@ -502,6 +505,10 @@ class PROJ_GCC_DLL DerivedCRS : virtual public SingleCRS {
//! @cond Doxygen_Suppress
PROJ_INTERNAL const operation::ConversionNNPtr &
derivingConversionRef() PROJ_PURE_DECL;
+
+ PROJ_INTERNAL void _exportToJSON(io::JSONFormatter *formatter)
+ const override; // throw(FormattingException)
+
//! @endcond
protected:
@@ -522,6 +529,8 @@ class PROJ_GCC_DLL DerivedCRS : virtual public SingleCRS {
util::IComparable::Criterion criterion =
util::IComparable::Criterion::STRICT) const override;
+ PROJ_INTERNAL virtual const char *className() const = 0;
+
private:
PROJ_OPAQUE_PRIVATE_DATA
DerivedCRS &operator=(const DerivedCRS &other) = delete;
@@ -553,8 +562,7 @@ using ProjectedCRSNNPtr = util::nn<ProjectedCRSPtr>;
* \remark Implements ProjectedCRS from \ref ISO_19111_2019
*/
class PROJ_GCC_DLL ProjectedCRS final : public DerivedCRS,
- public io::IPROJStringExportable,
- public io::IJSONExportable {
+ public io::IPROJStringExportable {
public:
//! @cond Doxygen_Suppress
PROJ_DLL ~ProjectedCRS() override;
@@ -607,6 +615,10 @@ class PROJ_GCC_DLL ProjectedCRS final : public DerivedCRS,
PROJ_INTERNAL std::list<std::pair<CRSNNPtr, int>>
_identify(const io::AuthorityFactoryPtr &authorityFactory) const override;
+ PROJ_INTERNAL const char *className() const override {
+ return "ProjectedCRS";
+ }
+
INLINED_MAKE_SHARED
PROJ_INTERNAL CRSNNPtr _shallowClone() const override;
@@ -647,7 +659,11 @@ class PROJ_GCC_DLL TemporalCRS : virtual public SingleCRS {
//! @cond Doxygen_Suppress
PROJ_INTERNAL void _exportToWKT(io::WKTFormatter *formatter)
const override; // throw(io::FormattingException)
- //! @endcond
+
+ PROJ_INTERNAL void _exportToJSON(io::JSONFormatter *formatter)
+ const override; // throw(FormattingException)
+
+ //! @endcond
protected:
PROJ_INTERNAL TemporalCRS(const datum::TemporalDatumNNPtr &datumIn,
@@ -704,7 +720,11 @@ class PROJ_GCC_DLL EngineeringCRS : virtual public SingleCRS {
//! @cond Doxygen_Suppress
PROJ_INTERNAL void _exportToWKT(io::WKTFormatter *formatter)
const override; // throw(io::FormattingException)
- //! @endcond
+
+ PROJ_INTERNAL void _exportToJSON(io::JSONFormatter *formatter)
+ const override; // throw(FormattingException)
+
+ //! @endcond
protected:
PROJ_INTERNAL EngineeringCRS(const datum::EngineeringDatumNNPtr &datumIn,
@@ -760,7 +780,11 @@ class PROJ_GCC_DLL ParametricCRS : virtual public SingleCRS {
//! @cond Doxygen_Suppress
PROJ_INTERNAL void _exportToWKT(io::WKTFormatter *formatter)
const override; // throw(io::FormattingException)
- //! @endcond
+
+ PROJ_INTERNAL void _exportToJSON(io::JSONFormatter *formatter)
+ const override; // throw(FormattingException)
+
+ //! @endcond
protected:
PROJ_INTERNAL ParametricCRS(const datum::ParametricDatumNNPtr &datumIn,
@@ -832,6 +856,9 @@ class PROJ_GCC_DLL CompoundCRS final : public CRS,
PROJ_INTERNAL void _exportToPROJString(io::PROJStringFormatter *formatter)
const override; // throw(FormattingException)
+ PROJ_INTERNAL void _exportToJSON(io::JSONFormatter *formatter)
+ const override; // throw(FormattingException)
+
PROJ_INTERNAL CRSNNPtr _shallowClone() const override;
PROJ_INTERNAL bool
@@ -917,6 +944,9 @@ class PROJ_GCC_DLL BoundCRS final : public CRS,
PROJ_INTERNAL void _exportToPROJString(io::PROJStringFormatter *formatter)
const override; // throw(FormattingException)
+ PROJ_INTERNAL void _exportToJSON(io::JSONFormatter *formatter)
+ const override; // throw(FormattingException)
+
PROJ_INTERNAL bool
_isEquivalentTo(const util::IComparable *other,
util::IComparable::Criterion criterion =
@@ -977,9 +1007,12 @@ class PROJ_GCC_DLL DerivedGeodeticCRS final : public GeodeticCRS,
void _exportToWKT(io::WKTFormatter *formatter)
const override; // throw(io::FormattingException)
- PROJ_INTERNAL void _exportToJSON(io::JSONFormatter *formatter)
- const override; // throw(FormattingException)
- //! @endcond
+ PROJ_INTERNAL void
+ _exportToJSON(io::JSONFormatter *formatter) const override {
+ return DerivedCRS::_exportToJSON(formatter);
+ }
+
+ //! @endcond
protected:
PROJ_INTERNAL
@@ -1006,6 +1039,10 @@ class PROJ_GCC_DLL DerivedGeodeticCRS final : public GeodeticCRS,
PROJ_INTERNAL void _exportToPROJString(io::PROJStringFormatter *formatter)
const override; // throw(FormattingException)
+ PROJ_INTERNAL const char *className() const override {
+ return "DerivedGeodeticCRS";
+ }
+
INLINED_MAKE_SHARED
private:
@@ -1049,9 +1086,12 @@ class PROJ_GCC_DLL DerivedGeographicCRS final : public GeographicCRS,
PROJ_INTERNAL void _exportToWKT(io::WKTFormatter *formatter)
const override; // throw(io::FormattingException)
- PROJ_INTERNAL void _exportToJSON(io::JSONFormatter *formatter)
- const override; // throw(FormattingException)
- //! @endcond
+ PROJ_INTERNAL void
+ _exportToJSON(io::JSONFormatter *formatter) const override {
+ return DerivedCRS::_exportToJSON(formatter);
+ }
+
+ //! @endcond
protected:
PROJ_INTERNAL
@@ -1070,6 +1110,10 @@ class PROJ_GCC_DLL DerivedGeographicCRS final : public GeographicCRS,
PROJ_INTERNAL std::list<std::pair<CRSNNPtr, int>>
_identify(const io::AuthorityFactoryPtr &authorityFactory) const override;
+ PROJ_INTERNAL const char *className() const override {
+ return "DerivedGeographicCRS";
+ }
+
// cppcheck-suppress functionStatic
PROJ_INTERNAL void _exportToPROJString(io::PROJStringFormatter *formatter)
const override; // throw(FormattingException)
@@ -1131,6 +1175,10 @@ class PROJ_GCC_DLL DerivedProjectedCRS final : public DerivedCRS {
util::IComparable::Criterion criterion =
util::IComparable::Criterion::STRICT) const override;
+ PROJ_INTERNAL const char *className() const override {
+ return "DerivedProjectedCRS";
+ }
+
INLINED_MAKE_SHARED
private:
@@ -1170,7 +1218,13 @@ class PROJ_GCC_DLL DerivedVerticalCRS final : public VerticalCRS,
//! @cond Doxygen_Suppress
PROJ_INTERNAL void _exportToWKT(io::WKTFormatter *formatter)
const override; // throw(io::FormattingException)
- //! @endcond
+
+ PROJ_INTERNAL void
+ _exportToJSON(io::JSONFormatter *formatter) const override {
+ return DerivedCRS::_exportToJSON(formatter);
+ }
+
+ //! @endcond
protected:
PROJ_INTERNAL
@@ -1189,6 +1243,10 @@ class PROJ_GCC_DLL DerivedVerticalCRS final : public VerticalCRS,
PROJ_INTERNAL std::list<std::pair<CRSNNPtr, int>>
_identify(const io::AuthorityFactoryPtr &authorityFactory) const override;
+ PROJ_INTERNAL const char *className() const override {
+ return "DerivedVerticalCRS";
+ }
+
// cppcheck-suppress functionStatic
PROJ_INTERNAL void _exportToPROJString(io::PROJStringFormatter *formatter)
const override; // throw(FormattingException)
@@ -1251,7 +1309,12 @@ class PROJ_GCC_DLL DerivedCRSTemplate final : public DerivedCRSTraits::BaseType,
//! @cond Doxygen_Suppress
PROJ_INTERNAL void _exportToWKT(io::WKTFormatter *formatter)
const override; // throw(io::FormattingException)
- //! @endcond
+
+ PROJ_INTERNAL void
+ _exportToJSON(io::JSONFormatter *formatter) const override {
+ return DerivedCRS::_exportToJSON(formatter);
+ }
+ //! @endcond
protected:
PROJ_INTERNAL
@@ -1267,6 +1330,8 @@ class PROJ_GCC_DLL DerivedCRSTemplate final : public DerivedCRSTraits::BaseType,
util::IComparable::Criterion criterion =
util::IComparable::Criterion::STRICT) const override;
+ PROJ_INTERNAL const char *className() const override;
+
INLINED_MAKE_SHARED
private:
diff --git a/include/proj/datum.hpp b/include/proj/datum.hpp
index 00e56a1c..3724bfb1 100644
--- a/include/proj/datum.hpp
+++ b/include/proj/datum.hpp
@@ -61,7 +61,8 @@ namespace datum {
*
* \remark Implements Datum from \ref ISO_19111_2019
*/
-class PROJ_GCC_DLL Datum : public common::ObjectUsage {
+class PROJ_GCC_DLL Datum : public common::ObjectUsage,
+ public io::IJSONExportable {
public:
//! @cond Doxygen_Suppress
PROJ_DLL ~Datum() override;
@@ -379,8 +380,7 @@ using GeodeticReferenceFrameNNPtr = util::nn<GeodeticReferenceFramePtr>;
*
* \remark Implements GeodeticReferenceFrame from \ref ISO_19111_2019
*/
-class PROJ_GCC_DLL GeodeticReferenceFrame : public Datum,
- public io::IJSONExportable {
+class PROJ_GCC_DLL GeodeticReferenceFrame : public Datum {
public:
//! @cond Doxygen_Suppress
PROJ_DLL ~GeodeticReferenceFrame() override;
@@ -566,7 +566,11 @@ class PROJ_GCC_DLL VerticalReferenceFrame : public Datum {
PROJ_INTERNAL void _exportToWKT(io::WKTFormatter *formatter)
const override; // throw(io::FormattingException)
- //! @endcond
+
+ PROJ_INTERNAL void _exportToJSON(io::JSONFormatter *formatter)
+ const override; // throw(FormattingException)
+
+ //! @endcond
protected:
#ifdef DOXYGEN_ENABLED
@@ -680,6 +684,9 @@ class PROJ_GCC_DLL TemporalDatum final : public Datum {
PROJ_INTERNAL void _exportToWKT(io::WKTFormatter *formatter)
const override; // throw(io::FormattingException)
+ PROJ_INTERNAL void _exportToJSON(io::JSONFormatter *formatter)
+ const override; // throw(FormattingException)
+
PROJ_INTERNAL bool
_isEquivalentTo(const util::IComparable *other,
util::IComparable::Criterion criterion =
@@ -728,6 +735,9 @@ class PROJ_GCC_DLL EngineeringDatum final : public Datum {
PROJ_INTERNAL void _exportToWKT(io::WKTFormatter *formatter)
const override; // throw(io::FormattingException)
+ PROJ_INTERNAL void _exportToJSON(io::JSONFormatter *formatter)
+ const override; // throw(FormattingException)
+
PROJ_INTERNAL bool
_isEquivalentTo(const util::IComparable *other,
util::IComparable::Criterion criterion =
@@ -772,6 +782,9 @@ class PROJ_GCC_DLL ParametricDatum final : public Datum {
PROJ_INTERNAL void _exportToWKT(io::WKTFormatter *formatter)
const override; // throw(io::FormattingException)
+ PROJ_INTERNAL void _exportToJSON(io::JSONFormatter *formatter)
+ const override; // throw(FormattingException)
+
PROJ_INTERNAL bool
_isEquivalentTo(const util::IComparable *other,
util::IComparable::Criterion criterion =
diff --git a/include/proj/internal/coordinateoperation_internal.hpp b/include/proj/internal/coordinateoperation_internal.hpp
index 207c20b4..7ae2cd78 100644
--- a/include/proj/internal/coordinateoperation_internal.hpp
+++ b/include/proj/internal/coordinateoperation_internal.hpp
@@ -149,6 +149,10 @@ class InverseConversion : public Conversion, public InverseCoordinateOperation {
Conversion::_exportToWKT(formatter);
}
+ void _exportToJSON(io::JSONFormatter *formatter) const override {
+ Conversion::_exportToJSON(formatter);
+ }
+
void
_exportToPROJString(io::PROJStringFormatter *formatter) const override {
InverseCoordinateOperation::_exportToPROJString(formatter);
@@ -200,6 +204,10 @@ class InverseTransformation : public Transformation,
return InverseCoordinateOperation::_exportToPROJString(formatter);
}
+ void _exportToJSON(io::JSONFormatter *formatter) const override {
+ Transformation::_exportToJSON(formatter);
+ }
+
bool
_isEquivalentTo(const util::IComparable *other,
util::IComparable::Criterion criterion =
@@ -270,6 +278,9 @@ class PROJBasedOperation : public SingleOperation {
void _exportToPROJString(io::PROJStringFormatter *formatter)
const override; // throw(FormattingException)
+ void _exportToJSON(io::JSONFormatter *formatter)
+ const override; // throw(FormattingException)
+
CoordinateOperationNNPtr _shallowClone() const override;
INLINED_MAKE_SHARED
diff --git a/include/proj/io.hpp b/include/proj/io.hpp
index 043b4c4b..66c1a4cd 100644
--- a/include/proj/io.hpp
+++ b/include/proj/io.hpp
@@ -505,6 +505,9 @@ class PROJ_GCC_DLL JSONFormatter {
PROJ_INTERNAL void setOmitTypeInImmediateChild();
+ PROJ_INTERNAL void setAbridgedTransformation(bool abriged);
+ PROJ_INTERNAL bool abridgedTransformation() const;
+
// cppcheck-suppress functionStatic
PROJ_INTERNAL bool outputId() const;
diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp
index f4e90fe8..27a22b51 100644
--- a/src/iso19111/coordinateoperation.cpp
+++ b/src/iso19111/coordinateoperation.cpp
@@ -5484,13 +5484,16 @@ void Conversion::_exportToJSON(
formatter->setAllowIDInImmediateChild();
method()->_exportToJSON(formatter);
- writer.AddObjKey("parameters");
- {
- auto parametersContext(writer.MakeArrayContext(false));
- for (const auto &genOpParamvalue : parameterValues()) {
- formatter->setAllowIDInImmediateChild();
- formatter->setOmitTypeInImmediateChild();
- genOpParamvalue->_exportToJSON(formatter);
+ const auto &l_parameterValues = parameterValues();
+ if (!l_parameterValues.empty()) {
+ writer.AddObjKey("parameters");
+ {
+ auto parametersContext(writer.MakeArrayContext(false));
+ for (const auto &genOpParamvalue : l_parameterValues) {
+ formatter->setAllowIDInImmediateChild();
+ formatter->setOmitTypeInImmediateChild();
+ genOpParamvalue->_exportToJSON(formatter);
+ }
}
}
@@ -7862,6 +7865,76 @@ void Transformation::_exportToWKT(io::WKTFormatter *formatter) const {
// ---------------------------------------------------------------------------
+//! @cond Doxygen_Suppress
+void Transformation::_exportToJSON(
+ io::JSONFormatter *formatter) const // throw(FormattingException)
+{
+ auto &writer = formatter->writer();
+ auto objectContext(formatter->MakeObjectContext(
+ formatter->abridgedTransformation() ? "AbridgedTransformation"
+ : "Transformation",
+ !identifiers().empty()));
+
+ writer.AddObjKey("name");
+ auto l_name = nameStr();
+ if (l_name.empty()) {
+ writer.Add("unnamed");
+ } else {
+ writer.Add(l_name);
+ }
+
+ if (!formatter->abridgedTransformation()) {
+ writer.AddObjKey("source_crs");
+ formatter->setAllowIDInImmediateChild();
+ sourceCRS()->_exportToJSON(formatter);
+
+ writer.AddObjKey("target_crs");
+ formatter->setAllowIDInImmediateChild();
+ targetCRS()->_exportToJSON(formatter);
+
+ const auto &l_interpolationCRS = interpolationCRS();
+ if (l_interpolationCRS) {
+ writer.AddObjKey("interpolation_crs");
+ formatter->setAllowIDInImmediateChild();
+ l_interpolationCRS->_exportToJSON(formatter);
+ }
+ }
+
+ writer.AddObjKey("method");
+ formatter->setOmitTypeInImmediateChild();
+ formatter->setAllowIDInImmediateChild();
+ method()->_exportToJSON(formatter);
+
+ writer.AddObjKey("parameters");
+ {
+ auto parametersContext(writer.MakeArrayContext(false));
+ for (const auto &genOpParamvalue : parameterValues()) {
+ formatter->setAllowIDInImmediateChild();
+ formatter->setOmitTypeInImmediateChild();
+ genOpParamvalue->_exportToJSON(formatter);
+ }
+ }
+
+ if (!formatter->abridgedTransformation()) {
+ if (!coordinateOperationAccuracies().empty()) {
+ writer.AddObjKey("accuracy");
+ writer.Add(coordinateOperationAccuracies()[0]->value());
+ }
+ }
+
+ if (formatter->abridgedTransformation()) {
+ if (formatter->outputId()) {
+ formatID(formatter);
+ }
+ } else {
+ ObjectUsage::baseExportToJSON(formatter);
+ }
+}
+
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
static void exportSourceCRSAndTargetCRSToWKT(const CoordinateOperation *co,
io::WKTFormatter *formatter) {
auto l_sourceCRS = co->sourceCRS();
@@ -9678,6 +9751,46 @@ void ConcatenatedOperation::_exportToWKT(io::WKTFormatter *formatter) const {
// ---------------------------------------------------------------------------
//! @cond Doxygen_Suppress
+void ConcatenatedOperation::_exportToJSON(
+ io::JSONFormatter *formatter) const // throw(FormattingException)
+{
+ auto &writer = formatter->writer();
+ auto objectContext(formatter->MakeObjectContext("ConcatenatedOperation",
+ !identifiers().empty()));
+
+ writer.AddObjKey("name");
+ auto l_name = nameStr();
+ if (l_name.empty()) {
+ writer.Add("unnamed");
+ } else {
+ writer.Add(l_name);
+ }
+
+ writer.AddObjKey("source_crs");
+ formatter->setAllowIDInImmediateChild();
+ sourceCRS()->_exportToJSON(formatter);
+
+ writer.AddObjKey("target_crs");
+ formatter->setAllowIDInImmediateChild();
+ targetCRS()->_exportToJSON(formatter);
+
+ writer.AddObjKey("steps");
+ {
+ auto parametersContext(writer.MakeArrayContext(false));
+ for (const auto &operation : operations()) {
+ formatter->setAllowIDInImmediateChild();
+ operation->_exportToJSON(formatter);
+ }
+ }
+
+ ObjectUsage::baseExportToJSON(formatter);
+}
+
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
CoordinateOperationNNPtr ConcatenatedOperation::_shallowClone() const {
auto op =
ConcatenatedOperation::nn_make_shared<ConcatenatedOperation>(*this);
@@ -13148,6 +13261,53 @@ void PROJBasedOperation::_exportToWKT(io::WKTFormatter *formatter) const {
// ---------------------------------------------------------------------------
+void PROJBasedOperation::_exportToJSON(
+ io::JSONFormatter *formatter) const // throw(FormattingException)
+{
+ auto &writer = formatter->writer();
+ auto objectContext(formatter->MakeObjectContext(
+ (sourceCRS() && targetCRS()) ? "Transformation" : "Conversion",
+ !identifiers().empty()));
+
+ writer.AddObjKey("name");
+ auto l_name = nameStr();
+ if (l_name.empty()) {
+ writer.Add("unnamed");
+ } else {
+ writer.Add(l_name);
+ }
+
+ if (sourceCRS() && targetCRS()) {
+ writer.AddObjKey("source_crs");
+ formatter->setAllowIDInImmediateChild();
+ sourceCRS()->_exportToJSON(formatter);
+
+ writer.AddObjKey("target_crs");
+ formatter->setAllowIDInImmediateChild();
+ targetCRS()->_exportToJSON(formatter);
+ }
+
+ writer.AddObjKey("method");
+ formatter->setOmitTypeInImmediateChild();
+ formatter->setAllowIDInImmediateChild();
+ method()->_exportToJSON(formatter);
+
+ const auto &l_parameterValues = parameterValues();
+ if (!l_parameterValues.empty()) {
+ writer.AddObjKey("parameters");
+ {
+ auto parametersContext(writer.MakeArrayContext(false));
+ for (const auto &genOpParamvalue : l_parameterValues) {
+ formatter->setAllowIDInImmediateChild();
+ formatter->setOmitTypeInImmediateChild();
+ genOpParamvalue->_exportToJSON(formatter);
+ }
+ }
+ }
+}
+
+// ---------------------------------------------------------------------------
+
void PROJBasedOperation::_exportToPROJString(
io::PROJStringFormatter *formatter) const {
if (projStringExportable_) {
diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp
index 670d0c1a..4f2ab7c6 100644
--- a/src/iso19111/crs.cpp
+++ b/src/iso19111/crs.cpp
@@ -2265,6 +2265,7 @@ void VerticalCRS::_exportToWKT(io::WKTFormatter *formatter) const {
// ---------------------------------------------------------------------------
+//! @cond Doxygen_Suppress
void VerticalCRS::_exportToPROJString(
io::PROJStringFormatter *formatter) const // throw(io::FormattingException)
{
@@ -2284,6 +2285,36 @@ void VerticalCRS::_exportToPROJString(
}
}
}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
+void VerticalCRS::_exportToJSON(
+ io::JSONFormatter *formatter) const // throw(io::FormattingException)
+{
+ auto &writer = formatter->writer();
+ auto objectContext(
+ formatter->MakeObjectContext("VerticalCRS", !identifiers().empty()));
+
+ writer.AddObjKey("name");
+ auto l_name = nameStr();
+ if (l_name.empty()) {
+ writer.Add("unnamed");
+ } else {
+ writer.Add(l_name);
+ }
+
+ writer.AddObjKey("datum");
+ datum()->_exportToJSON(formatter);
+
+ writer.AddObjKey("coordinate_system");
+ formatter->setOmitTypeInImmediateChild();
+ coordinateSystem()->_exportToJSON(formatter);
+
+ ObjectUsage::baseExportToJSON(formatter);
+}
+//! @endcond
// ---------------------------------------------------------------------------
@@ -2650,6 +2681,38 @@ void DerivedCRS::baseExportToWKT(io::WKTFormatter *formatter,
// ---------------------------------------------------------------------------
//! @cond Doxygen_Suppress
+void DerivedCRS::_exportToJSON(
+ io::JSONFormatter *formatter) const // throw(io::FormattingException)
+{
+ auto &writer = formatter->writer();
+ auto objectContext(
+ formatter->MakeObjectContext(className(), !identifiers().empty()));
+
+ writer.AddObjKey("name");
+ auto l_name = nameStr();
+ if (l_name.empty()) {
+ writer.Add("unnamed");
+ } else {
+ writer.Add(l_name);
+ }
+
+ writer.AddObjKey("base_crs");
+ baseCRS()->_exportToJSON(formatter);
+
+ writer.AddObjKey("conversion");
+ derivingConversionRef()->_exportToJSON(formatter);
+
+ writer.AddObjKey("coordinate_system");
+ formatter->setOmitTypeInImmediateChild();
+ coordinateSystem()->_exportToJSON(formatter);
+
+ ObjectUsage::baseExportToJSON(formatter);
+}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
struct ProjectedCRS::Private {
GeodeticCRSNNPtr baseCRS_;
cs::CartesianCSNNPtr cs_;
@@ -3514,6 +3577,36 @@ void CompoundCRS::_exportToWKT(io::WKTFormatter *formatter) const {
// ---------------------------------------------------------------------------
+//! @cond Doxygen_Suppress
+void CompoundCRS::_exportToJSON(
+ io::JSONFormatter *formatter) const // throw(io::FormattingException)
+{
+ auto &writer = formatter->writer();
+ auto objectContext(
+ formatter->MakeObjectContext("CompoundCRS", !identifiers().empty()));
+
+ writer.AddObjKey("name");
+ auto l_name = nameStr();
+ if (l_name.empty()) {
+ writer.Add("unnamed");
+ } else {
+ writer.Add(l_name);
+ }
+
+ writer.AddObjKey("components");
+ {
+ auto componentsContext(writer.MakeArrayContext(false));
+ for (const auto &crs : componentReferenceSystems()) {
+ crs->_exportToJSON(formatter);
+ }
+ }
+
+ ObjectUsage::baseExportToJSON(formatter);
+}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
void CompoundCRS::_exportToPROJString(
io::PROJStringFormatter *formatter) const // throw(io::FormattingException)
{
@@ -4004,6 +4097,30 @@ void BoundCRS::_exportToWKT(io::WKTFormatter *formatter) const {
// ---------------------------------------------------------------------------
+//! @cond Doxygen_Suppress
+void BoundCRS::_exportToJSON(
+ io::JSONFormatter *formatter) const // throw(io::FormattingException)
+{
+ auto &writer = formatter->writer();
+ auto objectContext(
+ formatter->MakeObjectContext("BoundCRS", !identifiers().empty()));
+
+ writer.AddObjKey("source_crs");
+ d->baseCRS()->_exportToJSON(formatter);
+
+ writer.AddObjKey("target_crs");
+ d->hubCRS()->_exportToJSON(formatter);
+
+ writer.AddObjKey("transformation");
+ formatter->setOmitTypeInImmediateChild();
+ formatter->setAbridgedTransformation(true);
+ d->transformation()->_exportToJSON(formatter);
+ formatter->setAbridgedTransformation(false);
+}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
void BoundCRS::_exportToPROJString(
io::PROJStringFormatter *formatter) const // throw(io::FormattingException)
{
@@ -4298,38 +4415,6 @@ void DerivedGeodeticCRS::_exportToWKT(io::WKTFormatter *formatter) const {
// ---------------------------------------------------------------------------
-//! @cond Doxygen_Suppress
-void DerivedGeodeticCRS::_exportToJSON(
- io::JSONFormatter *formatter) const // throw(io::FormattingException)
-{
- auto &writer = formatter->writer();
- auto objectContext(formatter->MakeObjectContext("DerivedGeodeticCRS",
- !identifiers().empty()));
-
- writer.AddObjKey("name");
- auto l_name = nameStr();
- if (l_name.empty()) {
- writer.Add("unnamed");
- } else {
- writer.Add(l_name);
- }
-
- writer.AddObjKey("base_crs");
- baseCRS()->_exportToJSON(formatter);
-
- writer.AddObjKey("conversion");
- derivingConversionRef()->_exportToJSON(formatter);
-
- writer.AddObjKey("coordinate_system");
- formatter->setOmitTypeInImmediateChild();
- coordinateSystem()->_exportToJSON(formatter);
-
- ObjectUsage::baseExportToJSON(formatter);
-}
-//! @endcond
-
-// ---------------------------------------------------------------------------
-
void DerivedGeodeticCRS::_exportToPROJString(
io::PROJStringFormatter *) const // throw(io::FormattingException)
{
@@ -4468,38 +4553,6 @@ void DerivedGeographicCRS::_exportToWKT(io::WKTFormatter *formatter) const {
// ---------------------------------------------------------------------------
-//! @cond Doxygen_Suppress
-void DerivedGeographicCRS::_exportToJSON(
- io::JSONFormatter *formatter) const // throw(io::FormattingException)
-{
- auto &writer = formatter->writer();
- auto objectContext(formatter->MakeObjectContext("DerivedGeographicCRS",
- !identifiers().empty()));
-
- writer.AddObjKey("name");
- auto l_name = nameStr();
- if (l_name.empty()) {
- writer.Add("unnamed");
- } else {
- writer.Add(l_name);
- }
-
- writer.AddObjKey("base_crs");
- baseCRS()->_exportToJSON(formatter);
-
- writer.AddObjKey("conversion");
- derivingConversionRef()->_exportToJSON(formatter);
-
- writer.AddObjKey("coordinate_system");
- formatter->setOmitTypeInImmediateChild();
- coordinateSystem()->_exportToJSON(formatter);
-
- ObjectUsage::baseExportToJSON(formatter);
-}
-//! @endcond
-
-// ---------------------------------------------------------------------------
-
void DerivedGeographicCRS::_exportToPROJString(
io::PROJStringFormatter *) const // throw(io::FormattingException)
{
@@ -4756,6 +4809,35 @@ void TemporalCRS::_exportToWKT(io::WKTFormatter *formatter) const {
// ---------------------------------------------------------------------------
+//! @cond Doxygen_Suppress
+void TemporalCRS::_exportToJSON(
+ io::JSONFormatter *formatter) const // throw(io::FormattingException)
+{
+ auto &writer = formatter->writer();
+ auto objectContext(
+ formatter->MakeObjectContext("TemporalCRS", !identifiers().empty()));
+
+ writer.AddObjKey("name");
+ auto l_name = nameStr();
+ if (l_name.empty()) {
+ writer.Add("unnamed");
+ } else {
+ writer.Add(l_name);
+ }
+
+ writer.AddObjKey("datum");
+ datum()->_exportToJSON(formatter);
+
+ writer.AddObjKey("coordinate_system");
+ formatter->setOmitTypeInImmediateChild();
+ coordinateSystem()->_exportToJSON(formatter);
+
+ ObjectUsage::baseExportToJSON(formatter);
+}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
bool TemporalCRS::_isEquivalentTo(
const util::IComparable *other,
util::IComparable::Criterion criterion) const {
@@ -4864,6 +4946,35 @@ void EngineeringCRS::_exportToWKT(io::WKTFormatter *formatter) const {
// ---------------------------------------------------------------------------
+//! @cond Doxygen_Suppress
+void EngineeringCRS::_exportToJSON(
+ io::JSONFormatter *formatter) const // throw(io::FormattingException)
+{
+ auto &writer = formatter->writer();
+ auto objectContext(
+ formatter->MakeObjectContext("EngineeringCRS", !identifiers().empty()));
+
+ writer.AddObjKey("name");
+ auto l_name = nameStr();
+ if (l_name.empty()) {
+ writer.Add("unnamed");
+ } else {
+ writer.Add(l_name);
+ }
+
+ writer.AddObjKey("datum");
+ datum()->_exportToJSON(formatter);
+
+ writer.AddObjKey("coordinate_system");
+ formatter->setOmitTypeInImmediateChild();
+ coordinateSystem()->_exportToJSON(formatter);
+
+ ObjectUsage::baseExportToJSON(formatter);
+}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
bool EngineeringCRS::_isEquivalentTo(
const util::IComparable *other,
util::IComparable::Criterion criterion) const {
@@ -4966,6 +5077,35 @@ void ParametricCRS::_exportToWKT(io::WKTFormatter *formatter) const {
// ---------------------------------------------------------------------------
+//! @cond Doxygen_Suppress
+void ParametricCRS::_exportToJSON(
+ io::JSONFormatter *formatter) const // throw(io::FormattingException)
+{
+ auto &writer = formatter->writer();
+ auto objectContext(
+ formatter->MakeObjectContext("ParametricCRS", !identifiers().empty()));
+
+ writer.AddObjKey("name");
+ auto l_name = nameStr();
+ if (l_name.empty()) {
+ writer.Add("unnamed");
+ } else {
+ writer.Add(l_name);
+ }
+
+ writer.AddObjKey("datum");
+ datum()->_exportToJSON(formatter);
+
+ writer.AddObjKey("coordinate_system");
+ formatter->setOmitTypeInImmediateChild();
+ coordinateSystem()->_exportToJSON(formatter);
+
+ ObjectUsage::baseExportToJSON(formatter);
+}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
bool ParametricCRS::_isEquivalentTo(
const util::IComparable *other,
util::IComparable::Criterion criterion) const {
@@ -5160,6 +5300,13 @@ DerivedCRSTemplate<DerivedCRSTraits>::create(
// ---------------------------------------------------------------------------
+template <class DerivedCRSTraits>
+const char *DerivedCRSTemplate<DerivedCRSTraits>::className() const {
+ return DerivedCRSTraits::CRSName().c_str();
+}
+
+// ---------------------------------------------------------------------------
+
static void DerivedCRSTemplateCheckExportToWKT(io::WKTFormatter *formatter,
const std::string &crsName,
bool wkt2_2018_only) {
diff --git a/src/iso19111/datum.cpp b/src/iso19111/datum.cpp
index 2944da1e..d6e6bc21 100644
--- a/src/iso19111/datum.cpp
+++ b/src/iso19111/datum.cpp
@@ -1726,6 +1726,30 @@ void VerticalReferenceFrame::_exportToWKT(
// ---------------------------------------------------------------------------
//! @cond Doxygen_Suppress
+void VerticalReferenceFrame::_exportToJSON(
+ io::JSONFormatter *formatter) const // throw(FormattingException)
+{
+ auto objectContext(formatter->MakeObjectContext("VerticalReferenceFrame",
+ !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);
+ }
+
+ Datum::getPrivate()->exportAnchorDefinition(formatter);
+
+ ObjectUsage::baseExportToJSON(formatter);
+}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
bool VerticalReferenceFrame::_isEquivalentTo(
const util::IComparable *other,
util::IComparable::Criterion criterion) const {
@@ -1989,6 +2013,32 @@ void TemporalDatum::_exportToWKT(
// ---------------------------------------------------------------------------
//! @cond Doxygen_Suppress
+void TemporalDatum::_exportToJSON(
+ io::JSONFormatter *formatter) const // throw(FormattingException)
+{
+ auto objectContext(
+ formatter->MakeObjectContext("TemporalDatum", !identifiers().empty()));
+ auto &writer = formatter->writer();
+
+ writer.AddObjKey("name");
+ writer.Add(nameStr());
+
+ writer.AddObjKey("calendar");
+ writer.Add(calendar());
+
+ const auto &timeOriginStr = temporalOrigin().toString();
+ if (!timeOriginStr.empty()) {
+ writer.AddObjKey("time_origin");
+ writer.Add(timeOriginStr);
+ }
+
+ ObjectUsage::baseExportToJSON(formatter);
+}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
bool TemporalDatum::_isEquivalentTo(
const util::IComparable *other,
util::IComparable::Criterion criterion) const {
@@ -2062,6 +2112,25 @@ void EngineeringDatum::_exportToWKT(
// ---------------------------------------------------------------------------
//! @cond Doxygen_Suppress
+void EngineeringDatum::_exportToJSON(
+ io::JSONFormatter *formatter) const // throw(FormattingException)
+{
+ auto objectContext(formatter->MakeObjectContext("EngineeringDatum",
+ !identifiers().empty()));
+ auto &writer = formatter->writer();
+
+ writer.AddObjKey("name");
+ writer.Add(nameStr());
+
+ Datum::getPrivate()->exportAnchorDefinition(formatter);
+
+ ObjectUsage::baseExportToJSON(formatter);
+}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
bool EngineeringDatum::_isEquivalentTo(
const util::IComparable *other,
util::IComparable::Criterion criterion) const {
@@ -2128,6 +2197,25 @@ void ParametricDatum::_exportToWKT(
// ---------------------------------------------------------------------------
//! @cond Doxygen_Suppress
+void ParametricDatum::_exportToJSON(
+ io::JSONFormatter *formatter) const // throw(FormattingException)
+{
+ auto objectContext(formatter->MakeObjectContext("ParametricDatum",
+ !identifiers().empty()));
+ auto &writer = formatter->writer();
+
+ writer.AddObjKey("name");
+ writer.Add(nameStr());
+
+ Datum::getPrivate()->exportAnchorDefinition(formatter);
+
+ ObjectUsage::baseExportToJSON(formatter);
+}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
bool ParametricDatum::_isEquivalentTo(
const util::IComparable *other,
util::IComparable::Criterion criterion) const {
diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp
index bcd14429..eadc54cc 100644
--- a/src/iso19111/io.cpp
+++ b/src/iso19111/io.cpp
@@ -4390,17 +4390,24 @@ class JSONParser {
static Measure getMeasure(const json &j);
ObjectDomainPtr buildObjectDomain(const json &j);
- PropertyMap buildProperties(const json &j);
+ PropertyMap buildProperties(const json &j, bool removeInverseOf = false);
GeographicCRSNNPtr buildGeographicCRS(const json &j);
GeodeticCRSNNPtr buildGeodeticCRS(const json &j);
ProjectedCRSNNPtr buildProjectedCRS(const json &j);
ConversionNNPtr buildConversion(const json &j);
GeodeticReferenceFrameNNPtr buildGeodeticReferenceFrame(const json &j);
+ VerticalReferenceFrameNNPtr buildVerticalReferenceFrame(const json &j);
EllipsoidNNPtr buildEllipsoid(const json &j);
PrimeMeridianNNPtr buildPrimeMeridian(const json &j);
CoordinateSystemNNPtr buildCS(const json &j);
CoordinateSystemAxisNNPtr buildAxis(const json &j);
+ VerticalCRSNNPtr buildVerticalCRS(const json &j);
+ CRSNNPtr buildCRS(const json &j);
+ CompoundCRSNNPtr buildCompoundCRS(const json &j);
+ BoundCRSNNPtr buildBoundCRS(const json &j);
+ TransformationNNPtr buildTransformation(const json &j);
+ ConcatenatedOperationNNPtr buildConcatenatedOperation(const json &j);
public:
JSONParser() = default;
@@ -4592,14 +4599,23 @@ ObjectDomainPtr JSONParser::buildObjectDomain(const json &j) {
// ---------------------------------------------------------------------------
-PropertyMap JSONParser::buildProperties(const json &j) {
+PropertyMap JSONParser::buildProperties(const json &j, bool removeInverseOf) {
PropertyMap map;
- map.set(IdentifiedObject::NAME_KEY, getName(j));
+ std::string name(getName(j));
+ if (removeInverseOf && starts_with(name, "Inverse of ")) {
+ name = name.substr(strlen("Inverse of "));
+ }
+ map.set(IdentifiedObject::NAME_KEY, name);
if (j.contains("id")) {
auto id = getObject(j, "id");
- map.set(metadata::Identifier::CODESPACE_KEY,
- getString(id, "authority"));
+ auto codeSpace(getString(id, "authority"));
+ if (removeInverseOf && starts_with(codeSpace, "INVERSE(") &&
+ codeSpace.back() == ')') {
+ codeSpace = codeSpace.substr(strlen("INVERSE("));
+ codeSpace.resize(codeSpace.size() - 1);
+ }
+ map.set(metadata::Identifier::CODESPACE_KEY, codeSpace);
if (!id.contains("code")) {
throw ParsingException("Missing \"code\" key");
}
@@ -4652,7 +4668,7 @@ PropertyMap JSONParser::buildProperties(const json &j) {
BaseObjectNNPtr JSONParser::create(const json &j)
{
- if (j.type() != json::value_t::object) {
+ if (!j.is_object()) {
throw ParsingException("JSON object expected");
}
auto type = getString(j, "type");
@@ -4665,9 +4681,21 @@ BaseObjectNNPtr JSONParser::create(const json &j)
if (type == "ProjectedCRS") {
return buildProjectedCRS(j);
}
+ if (type == "VerticalCRS") {
+ return buildVerticalCRS(j);
+ }
+ if (type == "CompoundCRS") {
+ return buildCompoundCRS(j);
+ }
+ if (type == "BoundCRS") {
+ return buildBoundCRS(j);
+ }
if (type == "GeodeticReferenceFrame") {
return buildGeodeticReferenceFrame(j);
}
+ if (type == "VerticalReferenceFrame") {
+ return buildVerticalReferenceFrame(j);
+ }
if (type == "Ellipsoid") {
return buildEllipsoid(j);
}
@@ -4677,6 +4705,15 @@ BaseObjectNNPtr JSONParser::create(const json &j)
if (type == "CoordinateSystem") {
return buildCS(j);
}
+ if (type == "Conversion") {
+ return buildConversion(j);
+ }
+ if (type == "Transformation") {
+ return buildTransformation(j);
+ }
+ if (type == "ConcatenatedOperation") {
+ return buildConcatenatedOperation(j);
+ }
throw ParsingException("Unsupported value of \"type\"");
}
@@ -4755,6 +4792,48 @@ 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.");
+ }
+ 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,
+ NN_NO_CHECK(verticalCS));
+}
+
+// ---------------------------------------------------------------------------
+
+CRSNNPtr JSONParser::buildCRS(const json &j) {
+ auto crs = util::nn_dynamic_pointer_cast<CRS>(create(j));
+ if (crs) {
+ return NN_NO_CHECK(crs);
+ }
+ throw ParsingException("Object is not a CRS");
+}
+
+// ---------------------------------------------------------------------------
+
+CompoundCRSNNPtr JSONParser::buildCompoundCRS(const json &j) {
+ auto componentsJ = getArray(j, "components");
+ std::vector<CRSNNPtr> components;
+ for (const auto &componentJ : componentsJ) {
+ if (!componentJ.is_object()) {
+ throw ParsingException(
+ "Unexpected type for a \"components\" child");
+ }
+ components.push_back(buildCRS(componentJ));
+ }
+ return CompoundCRS::create(buildProperties(j), components);
+}
+
+// ---------------------------------------------------------------------------
+
ConversionNNPtr JSONParser::buildConversion(const json &j) {
auto methodJ = getObject(j, "method");
auto convProps = buildProperties(j);
@@ -4775,11 +4854,152 @@ ConversionNNPtr JSONParser::buildConversion(const json &j) {
OperationParameter::create(buildProperties(param)));
values.emplace_back(ParameterValue::create(getMeasure(param)));
}
+
+ std::string convName;
+ std::string methodName;
+ if (convProps.getStringValue(IdentifiedObject::NAME_KEY, convName) &&
+ methodProps.getStringValue(IdentifiedObject::NAME_KEY, methodName) &&
+ starts_with(convName, "Inverse of ") &&
+ starts_with(methodName, "Inverse of ")) {
+
+ auto invConvProps = buildProperties(j, true);
+ auto invMethodProps = buildProperties(methodJ, true);
+ return NN_NO_CHECK(util::nn_dynamic_pointer_cast<Conversion>(
+ Conversion::create(invConvProps, invMethodProps, parameters, values)
+ ->inverse()));
+ }
return Conversion::create(convProps, methodProps, parameters, values);
}
// ---------------------------------------------------------------------------
+BoundCRSNNPtr JSONParser::buildBoundCRS(const json &j) {
+
+ auto sourceCRS = buildCRS(getObject(j, "source_crs"));
+ auto targetCRS = buildCRS(getObject(j, "target_crs"));
+ auto transformationJ = getObject(j, "transformation");
+ auto methodJ = getObject(transformationJ, "method");
+ auto parametersJ = getArray(transformationJ, "parameters");
+ std::vector<OperationParameterNNPtr> parameters;
+ std::vector<ParameterValueNNPtr> values;
+ for (const auto &param : parametersJ) {
+ if (!param.is_object()) {
+ throw ParsingException(
+ "Unexpected type for a \"parameters\" child");
+ }
+ parameters.emplace_back(
+ OperationParameter::create(buildProperties(param)));
+ if (param.contains("value")) {
+ auto v = param["value"];
+ if (v.is_string()) {
+ values.emplace_back(
+ ParameterValue::createFilename(v.get<std::string>()));
+ continue;
+ }
+ }
+ values.emplace_back(ParameterValue::create(getMeasure(param)));
+ }
+
+ CRSPtr sourceTransformationCRS;
+ if (dynamic_cast<GeographicCRS *>(targetCRS.get())) {
+ sourceTransformationCRS = sourceCRS->extractGeographicCRS();
+ if (!sourceTransformationCRS) {
+ sourceTransformationCRS =
+ std::dynamic_pointer_cast<VerticalCRS>(sourceCRS.as_nullable());
+ if (!sourceTransformationCRS) {
+ throw ParsingException(
+ "Cannot find GeographicCRS or VerticalCRS in sourceCRS");
+ }
+ }
+ } else {
+ sourceTransformationCRS = sourceCRS;
+ }
+
+ auto transformation = Transformation::create(
+ buildProperties(transformationJ), NN_NO_CHECK(sourceTransformationCRS),
+ targetCRS, nullptr, buildProperties(methodJ), parameters, values,
+ std::vector<PositionalAccuracyNNPtr>());
+
+ return BoundCRS::create(sourceCRS, targetCRS, transformation);
+}
+
+// ---------------------------------------------------------------------------
+
+TransformationNNPtr JSONParser::buildTransformation(const json &j) {
+
+ auto sourceCRS = buildCRS(getObject(j, "source_crs"));
+ auto targetCRS = buildCRS(getObject(j, "target_crs"));
+ auto methodJ = getObject(j, "method");
+ auto parametersJ = getArray(j, "parameters");
+ std::vector<OperationParameterNNPtr> parameters;
+ std::vector<ParameterValueNNPtr> values;
+ for (const auto &param : parametersJ) {
+ if (!param.is_object()) {
+ throw ParsingException(
+ "Unexpected type for a \"parameters\" child");
+ }
+ parameters.emplace_back(
+ OperationParameter::create(buildProperties(param)));
+ if (param.contains("value")) {
+ auto v = param["value"];
+ if (v.is_string()) {
+ values.emplace_back(
+ ParameterValue::createFilename(v.get<std::string>()));
+ continue;
+ }
+ }
+ values.emplace_back(ParameterValue::create(getMeasure(param)));
+ }
+ CRSPtr interpolationCRS;
+ if (j.contains("interpolation_crs")) {
+ interpolationCRS =
+ buildCRS(getObject(j, "interpolation_crs")).as_nullable();
+ }
+ std::vector<PositionalAccuracyNNPtr> accuracies;
+ if (j.contains("accuracy")) {
+ accuracies.push_back(
+ PositionalAccuracy::create(getString(j, "accuracy")));
+ }
+
+ return Transformation::create(buildProperties(j), sourceCRS, targetCRS,
+ interpolationCRS, buildProperties(methodJ),
+ parameters, values, accuracies);
+}
+
+// ---------------------------------------------------------------------------
+
+ConcatenatedOperationNNPtr
+JSONParser::buildConcatenatedOperation(const json &j) {
+
+ auto sourceCRS = buildCRS(getObject(j, "source_crs"));
+ auto targetCRS = buildCRS(getObject(j, "target_crs"));
+ auto stepsJ = getArray(j, "steps");
+ std::vector<CoordinateOperationNNPtr> operations;
+ for (const auto &stepJ : stepsJ) {
+ if (!stepJ.is_object()) {
+ throw ParsingException("Unexpected type for a \"steps\" child");
+ }
+ auto op = nn_dynamic_pointer_cast<CoordinateOperation>(create(stepJ));
+ if (!op) {
+ throw ParsingException("Invalid content in a \"steps\" child");
+ }
+ operations.emplace_back(NN_NO_CHECK(op));
+ }
+
+ ConcatenatedOperation::fixStepsDirection(sourceCRS, targetCRS, operations);
+
+ try {
+ return ConcatenatedOperation::create(
+ buildProperties(j), operations,
+ std::vector<PositionalAccuracyNNPtr>());
+ } catch (const InvalidOperation &e) {
+ throw ParsingException(
+ std::string("Cannot build concatenated operation: ") + e.what());
+ }
+}
+
+// ---------------------------------------------------------------------------
+
CoordinateSystemAxisNNPtr JSONParser::buildAxis(const json &j) {
auto dirString = getString(j, "direction");
auto abbreviation = getString(j, "abbreviation");
@@ -4884,9 +5104,23 @@ JSONParser::buildGeodeticReferenceFrame(const json &j) {
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");
+ }
return GeodeticReferenceFrame::create(
- buildProperties(j), buildEllipsoid(ellipsoidJ),
- optional<std::string>() /* anchor */, pm);
+ buildProperties(j), buildEllipsoid(ellipsoidJ), anchor, pm);
+}
+
+// ---------------------------------------------------------------------------
+
+VerticalReferenceFrameNNPtr
+JSONParser::buildVerticalReferenceFrame(const json &j) {
+ optional<std::string> anchor;
+ if (j.contains("anchor")) {
+ anchor = getString(j, "anchor");
+ }
+ return VerticalReferenceFrame::create(buildProperties(j), anchor);
}
// ---------------------------------------------------------------------------
@@ -8605,6 +8839,7 @@ struct JSONFormatter::Private {
std::vector<bool> outputIdStack_{true};
bool allowIDInImmediateChild_ = false;
bool omitTypeInImmediateChild_ = false;
+ bool abridgedTransformation_ = false;
std::string result_{};
@@ -8717,6 +8952,18 @@ JSONFormatter::ObjectContext::~ObjectContext() {
m_formatter.d->popOutputId();
}
+// ---------------------------------------------------------------------------
+
+void JSONFormatter::setAbridgedTransformation(bool outputIn) {
+ d->abridgedTransformation_ = outputIn;
+}
+
+// ---------------------------------------------------------------------------
+
+bool JSONFormatter::abridgedTransformation() const {
+ return d->abridgedTransformation_;
+}
+
//! @endcond
// ---------------------------------------------------------------------------
diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp
index c993c5e4..d3c4c8ec 100644
--- a/test/unit/test_io.cpp
+++ b/test/unit/test_io.cpp
@@ -9612,3 +9612,793 @@ TEST(wkt_export, json_import_projected_crs) {
ASSERT_TRUE(pcrs != nullptr);
EXPECT_EQ(pcrs->exportToJSON((JSONFormatter::create().get())), json);
}
+
+// ---------------------------------------------------------------------------
+
+TEST(wkt_export, json_import_compound_crs) {
+ auto json = "{\n"
+ " \"type\": \"CompoundCRS\",\n"
+ " \"name\": \"WGS 84 + EGM2008 height\",\n"
+ " \"components\": [\n"
+ " {\n"
+ " \"type\": \"GeographicCRS\",\n"
+ " \"name\": \"WGS 84\",\n"
+ " \"datum\": {\n"
+ " \"type\": \"GeodeticReferenceFrame\",\n"
+ " \"name\": \"World Geodetic System 1984\",\n"
+ " \"ellipsoid\": {\n"
+ " \"name\": \"WGS 84\",\n"
+ " \"semi_major_axis\": 6378137,\n"
+ " \"inverse_flattening\": 298.257223563\n"
+ " }\n"
+ " },\n"
+ " \"coordinate_system\": {\n"
+ " \"subtype\": \"ellipsoidal\",\n"
+ " \"axis\": [\n"
+ " {\n"
+ " \"name\": \"Geodetic latitude\",\n"
+ " \"abbreviation\": \"Lat\",\n"
+ " \"direction\": \"north\",\n"
+ " \"unit\": \"degree\"\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Geodetic longitude\",\n"
+ " \"abbreviation\": \"Lon\",\n"
+ " \"direction\": \"east\",\n"
+ " \"unit\": \"degree\"\n"
+ " }\n"
+ " ]\n"
+ " },\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 4326\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"type\": \"VerticalCRS\",\n"
+ " \"name\": \"EGM2008 height\",\n"
+ " \"datum\": {\n"
+ " \"type\": \"VerticalReferenceFrame\",\n"
+ " \"name\": \"EGM2008 geoid\"\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"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 3855\n"
+ " }\n"
+ " }\n"
+ " ]\n"
+ "}";
+ auto obj = createFromUserInput(json, nullptr);
+ auto compoundCRS = nn_dynamic_pointer_cast<CompoundCRS>(obj);
+ ASSERT_TRUE(compoundCRS != nullptr);
+ EXPECT_EQ(compoundCRS->exportToJSON((JSONFormatter::create().get())), json);
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(wkt_export, json_import_bound_crs) {
+ auto json =
+ "{\n"
+ " \"type\": \"BoundCRS\",\n"
+ " \"source_crs\": {\n"
+ " \"type\": \"GeographicCRS\",\n"
+ " \"name\": \"unknown\",\n"
+ " \"datum\": {\n"
+ " \"type\": \"GeodeticReferenceFrame\",\n"
+ " \"name\": \"Unknown based on GRS80 ellipsoid\",\n"
+ " \"ellipsoid\": {\n"
+ " \"name\": \"GRS 1980\",\n"
+ " \"semi_major_axis\": 6378137,\n"
+ " \"inverse_flattening\": 298.257222101,\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 7019\n"
+ " }\n"
+ " }\n"
+ " },\n"
+ " \"coordinate_system\": {\n"
+ " \"subtype\": \"ellipsoidal\",\n"
+ " \"axis\": [\n"
+ " {\n"
+ " \"name\": \"Longitude\",\n"
+ " \"abbreviation\": \"lon\",\n"
+ " \"direction\": \"east\",\n"
+ " \"unit\": \"degree\"\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Latitude\",\n"
+ " \"abbreviation\": \"lat\",\n"
+ " \"direction\": \"north\",\n"
+ " \"unit\": \"degree\"\n"
+ " }\n"
+ " ]\n"
+ " }\n"
+ " },\n"
+ " \"target_crs\": {\n"
+ " \"type\": \"GeographicCRS\",\n"
+ " \"name\": \"WGS 84\",\n"
+ " \"datum\": {\n"
+ " \"type\": \"GeodeticReferenceFrame\",\n"
+ " \"name\": \"World Geodetic System 1984\",\n"
+ " \"ellipsoid\": {\n"
+ " \"name\": \"WGS 84\",\n"
+ " \"semi_major_axis\": 6378137,\n"
+ " \"inverse_flattening\": 298.257223563\n"
+ " }\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"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 4326\n"
+ " }\n"
+ " },\n"
+ " \"transformation\": {\n"
+ " \"name\": \"unknown to WGS84\",\n"
+ " \"method\": {\n"
+ " \"name\": \"NTv2\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 9615\n"
+ " }\n"
+ " },\n"
+ " \"parameters\": [\n"
+ " {\n"
+ " \"name\": \"Latitude and longitude difference file\",\n"
+ " \"value\": \"@foo\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8656\n"
+ " }\n"
+ " }\n"
+ " ]\n"
+ " }\n"
+ "}";
+ auto obj = createFromUserInput(json, nullptr);
+ auto boundCRS = nn_dynamic_pointer_cast<BoundCRS>(obj);
+ ASSERT_TRUE(boundCRS != nullptr);
+ EXPECT_EQ(boundCRS->exportToJSON((JSONFormatter::create().get())), json);
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(wkt_export, json_import_transformation) {
+ auto json = "{\n"
+ " \"type\": \"Transformation\",\n"
+ " \"name\": \"GDA94 to GDA2020 (1)\",\n"
+ " \"source_crs\": {\n"
+ " \"type\": \"GeographicCRS\",\n"
+ " \"name\": \"GDA94\",\n"
+ " \"datum\": {\n"
+ " \"type\": \"GeodeticReferenceFrame\",\n"
+ " \"name\": \"Geocentric Datum of Australia 1994\",\n"
+ " \"ellipsoid\": {\n"
+ " \"name\": \"GRS 1980\",\n"
+ " \"semi_major_axis\": 6378137,\n"
+ " \"inverse_flattening\": 298.257222101\n"
+ " }\n"
+ " },\n"
+ " \"coordinate_system\": {\n"
+ " \"subtype\": \"ellipsoidal\",\n"
+ " \"axis\": [\n"
+ " {\n"
+ " \"name\": \"Geodetic latitude\",\n"
+ " \"abbreviation\": \"Lat\",\n"
+ " \"direction\": \"north\",\n"
+ " \"unit\": \"degree\"\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Geodetic longitude\",\n"
+ " \"abbreviation\": \"Lon\",\n"
+ " \"direction\": \"east\",\n"
+ " \"unit\": \"degree\"\n"
+ " }\n"
+ " ]\n"
+ " }\n"
+ " },\n"
+ " \"target_crs\": {\n"
+ " \"type\": \"GeographicCRS\",\n"
+ " \"name\": \"GDA2020\",\n"
+ " \"datum\": {\n"
+ " \"type\": \"GeodeticReferenceFrame\",\n"
+ " \"name\": \"Geocentric Datum of Australia 2020\",\n"
+ " \"ellipsoid\": {\n"
+ " \"name\": \"GRS 1980\",\n"
+ " \"semi_major_axis\": 6378137,\n"
+ " \"inverse_flattening\": 298.257222101\n"
+ " }\n"
+ " },\n"
+ " \"coordinate_system\": {\n"
+ " \"subtype\": \"ellipsoidal\",\n"
+ " \"axis\": [\n"
+ " {\n"
+ " \"name\": \"Geodetic latitude\",\n"
+ " \"abbreviation\": \"Lat\",\n"
+ " \"direction\": \"north\",\n"
+ " \"unit\": \"degree\"\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Geodetic longitude\",\n"
+ " \"abbreviation\": \"Lon\",\n"
+ " \"direction\": \"east\",\n"
+ " \"unit\": \"degree\"\n"
+ " }\n"
+ " ]\n"
+ " }\n"
+ " },\n"
+ " \"method\": {\n"
+ " \"name\": \"Coordinate Frame rotation (geog2D domain)\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 9607\n"
+ " }\n"
+ " },\n"
+ " \"parameters\": [\n"
+ " {\n"
+ " \"name\": \"X-axis translation\",\n"
+ " \"value\": 61.55,\n"
+ " \"unit\": {\n"
+ " \"type\": \"LinearUnit\",\n"
+ " \"name\": \"millimetre\",\n"
+ " \"conversion_factor\": 0.001\n"
+ " },\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8605\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Y-axis translation\",\n"
+ " \"value\": -10.87,\n"
+ " \"unit\": {\n"
+ " \"type\": \"LinearUnit\",\n"
+ " \"name\": \"millimetre\",\n"
+ " \"conversion_factor\": 0.001\n"
+ " },\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8606\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Z-axis translation\",\n"
+ " \"value\": -40.19,\n"
+ " \"unit\": {\n"
+ " \"type\": \"LinearUnit\",\n"
+ " \"name\": \"millimetre\",\n"
+ " \"conversion_factor\": 0.001\n"
+ " },\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8607\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"X-axis rotation\",\n"
+ " \"value\": -39.4924,\n"
+ " \"unit\": {\n"
+ " \"type\": \"AngularUnit\",\n"
+ " \"name\": \"milliarc-second\",\n"
+ " \"conversion_factor\": 4.84813681109536e-09\n"
+ " },\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8608\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Y-axis rotation\",\n"
+ " \"value\": -32.7221,\n"
+ " \"unit\": {\n"
+ " \"type\": \"AngularUnit\",\n"
+ " \"name\": \"milliarc-second\",\n"
+ " \"conversion_factor\": 4.84813681109536e-09\n"
+ " },\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8609\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Z-axis rotation\",\n"
+ " \"value\": -32.8979,\n"
+ " \"unit\": {\n"
+ " \"type\": \"AngularUnit\",\n"
+ " \"name\": \"milliarc-second\",\n"
+ " \"conversion_factor\": 4.84813681109536e-09\n"
+ " },\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8610\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Scale difference\",\n"
+ " \"value\": -9.994,\n"
+ " \"unit\": {\n"
+ " \"type\": \"ScaleUnit\",\n"
+ " \"name\": \"parts per billion\",\n"
+ " \"conversion_factor\": 1e-09\n"
+ " },\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8611\n"
+ " }\n"
+ " }\n"
+ " ],\n"
+ " \"accuracy\": \"0.01\",\n"
+ " \"scope\": \"scope\",\n"
+ " \"area\": \"Australia - GDA\",\n"
+ " \"bbox\": {\n"
+ " \"south_latitude\": -60.56,\n"
+ " \"west_longitude\": 93.41,\n"
+ " \"north_latitude\": -8.47,\n"
+ " \"east_longitude\": 173.35\n"
+ " },\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8048\n"
+ " },\n"
+ " \"remarks\": \"foo\"\n"
+ "}";
+ auto obj = createFromUserInput(json, nullptr);
+ auto transf = nn_dynamic_pointer_cast<Transformation>(obj);
+ ASSERT_TRUE(transf != nullptr);
+ EXPECT_EQ(transf->exportToJSON((JSONFormatter::create().get())), json);
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(wkt_export, json_import_concatenated_operation) {
+ auto json =
+ "{\n"
+ " \"type\": \"ConcatenatedOperation\",\n"
+ " \"name\": \"Inverse of Vicgrid + GDA94 to GDA2020 (1)\",\n"
+ " \"source_crs\": {\n"
+ " \"type\": \"ProjectedCRS\",\n"
+ " \"name\": \"GDA94 / Vicgrid\",\n"
+ " \"base_crs\": {\n"
+ " \"name\": \"GDA94\",\n"
+ " \"datum\": {\n"
+ " \"type\": \"GeodeticReferenceFrame\",\n"
+ " \"name\": \"Geocentric Datum of Australia 1994\",\n"
+ " \"ellipsoid\": {\n"
+ " \"name\": \"GRS 1980\",\n"
+ " \"semi_major_axis\": 6378137,\n"
+ " \"inverse_flattening\": 298.257222101\n"
+ " }\n"
+ " },\n"
+ " \"coordinate_system\": {\n"
+ " \"subtype\": \"ellipsoidal\",\n"
+ " \"axis\": [\n"
+ " {\n"
+ " \"name\": \"Geodetic latitude\",\n"
+ " \"abbreviation\": \"Lat\",\n"
+ " \"direction\": \"north\",\n"
+ " \"unit\": \"degree\"\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Geodetic longitude\",\n"
+ " \"abbreviation\": \"Lon\",\n"
+ " \"direction\": \"east\",\n"
+ " \"unit\": \"degree\"\n"
+ " }\n"
+ " ]\n"
+ " },\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 4283\n"
+ " }\n"
+ " },\n"
+ " \"conversion\": {\n"
+ " \"name\": \"Vicgrid\",\n"
+ " \"method\": {\n"
+ " \"name\": \"Lambert Conic Conformal (2SP)\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 9802\n"
+ " }\n"
+ " },\n"
+ " \"parameters\": [\n"
+ " {\n"
+ " \"name\": \"Latitude of false origin\",\n"
+ " \"value\": -37,\n"
+ " \"unit\": \"degree\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8821\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Longitude of false origin\",\n"
+ " \"value\": 145,\n"
+ " \"unit\": \"degree\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8822\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Latitude of 1st standard parallel\",\n"
+ " \"value\": -36,\n"
+ " \"unit\": \"degree\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8823\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Latitude of 2nd standard parallel\",\n"
+ " \"value\": -38,\n"
+ " \"unit\": \"degree\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8824\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Easting at false origin\",\n"
+ " \"value\": 2500000,\n"
+ " \"unit\": \"metre\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8826\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Northing at false origin\",\n"
+ " \"value\": 2500000,\n"
+ " \"unit\": \"metre\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8827\n"
+ " }\n"
+ " }\n"
+ " ]\n"
+ " },\n"
+ " \"coordinate_system\": {\n"
+ " \"subtype\": \"Cartesian\",\n"
+ " \"axis\": [\n"
+ " {\n"
+ " \"name\": \"Easting\",\n"
+ " \"abbreviation\": \"E\",\n"
+ " \"direction\": \"east\",\n"
+ " \"unit\": \"metre\"\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Northing\",\n"
+ " \"abbreviation\": \"N\",\n"
+ " \"direction\": \"north\",\n"
+ " \"unit\": \"metre\"\n"
+ " }\n"
+ " ]\n"
+ " },\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 3111\n"
+ " }\n"
+ " },\n"
+ " \"target_crs\": {\n"
+ " \"type\": \"GeographicCRS\",\n"
+ " \"name\": \"GDA2020\",\n"
+ " \"datum\": {\n"
+ " \"type\": \"GeodeticReferenceFrame\",\n"
+ " \"name\": \"Geocentric Datum of Australia 2020\",\n"
+ " \"ellipsoid\": {\n"
+ " \"name\": \"GRS 1980\",\n"
+ " \"semi_major_axis\": 6378137,\n"
+ " \"inverse_flattening\": 298.257222101\n"
+ " }\n"
+ " },\n"
+ " \"coordinate_system\": {\n"
+ " \"subtype\": \"ellipsoidal\",\n"
+ " \"axis\": [\n"
+ " {\n"
+ " \"name\": \"Geodetic latitude\",\n"
+ " \"abbreviation\": \"Lat\",\n"
+ " \"direction\": \"north\",\n"
+ " \"unit\": \"degree\"\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Geodetic longitude\",\n"
+ " \"abbreviation\": \"Lon\",\n"
+ " \"direction\": \"east\",\n"
+ " \"unit\": \"degree\"\n"
+ " }\n"
+ " ]\n"
+ " },\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 7844\n"
+ " }\n"
+ " },\n"
+ " \"steps\": [\n"
+ " {\n"
+ " \"type\": \"Conversion\",\n"
+ " \"name\": \"Inverse of Vicgrid\",\n"
+ " \"method\": {\n"
+ " \"name\": \"Inverse of Lambert Conic Conformal (2SP)\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"INVERSE(EPSG)\",\n"
+ " \"code\": 9802\n"
+ " }\n"
+ " },\n"
+ " \"parameters\": [\n"
+ " {\n"
+ " \"name\": \"Latitude of false origin\",\n"
+ " \"value\": -37,\n"
+ " \"unit\": \"degree\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8821\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Longitude of false origin\",\n"
+ " \"value\": 145,\n"
+ " \"unit\": \"degree\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8822\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Latitude of 1st standard parallel\",\n"
+ " \"value\": -36,\n"
+ " \"unit\": \"degree\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8823\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Latitude of 2nd standard parallel\",\n"
+ " \"value\": -38,\n"
+ " \"unit\": \"degree\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8824\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Easting at false origin\",\n"
+ " \"value\": 2500000,\n"
+ " \"unit\": \"metre\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8826\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Northing at false origin\",\n"
+ " \"value\": 2500000,\n"
+ " \"unit\": \"metre\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8827\n"
+ " }\n"
+ " }\n"
+ " ],\n"
+ " \"id\": {\n"
+ " \"authority\": \"INVERSE(EPSG)\",\n"
+ " \"code\": 17361\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"type\": \"Transformation\",\n"
+ " \"name\": \"GDA94 to GDA2020 (1)\",\n"
+ " \"source_crs\": {\n"
+ " \"type\": \"GeographicCRS\",\n"
+ " \"name\": \"GDA94\",\n"
+ " \"datum\": {\n"
+ " \"type\": \"GeodeticReferenceFrame\",\n"
+ " \"name\": \"Geocentric Datum of Australia 1994\",\n"
+ " \"ellipsoid\": {\n"
+ " \"name\": \"GRS 1980\",\n"
+ " \"semi_major_axis\": 6378137,\n"
+ " \"inverse_flattening\": 298.257222101\n"
+ " }\n"
+ " },\n"
+ " \"coordinate_system\": {\n"
+ " \"subtype\": \"ellipsoidal\",\n"
+ " \"axis\": [\n"
+ " {\n"
+ " \"name\": \"Geodetic latitude\",\n"
+ " \"abbreviation\": \"Lat\",\n"
+ " \"direction\": \"north\",\n"
+ " \"unit\": \"degree\"\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Geodetic longitude\",\n"
+ " \"abbreviation\": \"Lon\",\n"
+ " \"direction\": \"east\",\n"
+ " \"unit\": \"degree\"\n"
+ " }\n"
+ " ]\n"
+ " },\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 4283\n"
+ " }\n"
+ " },\n"
+ " \"target_crs\": {\n"
+ " \"type\": \"GeographicCRS\",\n"
+ " \"name\": \"GDA2020\",\n"
+ " \"datum\": {\n"
+ " \"type\": \"GeodeticReferenceFrame\",\n"
+ " \"name\": \"Geocentric Datum of Australia 2020\",\n"
+ " \"ellipsoid\": {\n"
+ " \"name\": \"GRS 1980\",\n"
+ " \"semi_major_axis\": 6378137,\n"
+ " \"inverse_flattening\": 298.257222101\n"
+ " }\n"
+ " },\n"
+ " \"coordinate_system\": {\n"
+ " \"subtype\": \"ellipsoidal\",\n"
+ " \"axis\": [\n"
+ " {\n"
+ " \"name\": \"Geodetic latitude\",\n"
+ " \"abbreviation\": \"Lat\",\n"
+ " \"direction\": \"north\",\n"
+ " \"unit\": \"degree\"\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Geodetic longitude\",\n"
+ " \"abbreviation\": \"Lon\",\n"
+ " \"direction\": \"east\",\n"
+ " \"unit\": \"degree\"\n"
+ " }\n"
+ " ]\n"
+ " },\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 7844\n"
+ " }\n"
+ " },\n"
+ " \"method\": {\n"
+ " \"name\": \"Coordinate Frame rotation (geog2D domain)\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 9607\n"
+ " }\n"
+ " },\n"
+ " \"parameters\": [\n"
+ " {\n"
+ " \"name\": \"X-axis translation\",\n"
+ " \"value\": 61.55,\n"
+ " \"unit\": {\n"
+ " \"type\": \"LinearUnit\",\n"
+ " \"name\": \"millimetre\",\n"
+ " \"conversion_factor\": 0.001\n"
+ " },\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8605\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Y-axis translation\",\n"
+ " \"value\": -10.87,\n"
+ " \"unit\": {\n"
+ " \"type\": \"LinearUnit\",\n"
+ " \"name\": \"millimetre\",\n"
+ " \"conversion_factor\": 0.001\n"
+ " },\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8606\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Z-axis translation\",\n"
+ " \"value\": -40.19,\n"
+ " \"unit\": {\n"
+ " \"type\": \"LinearUnit\",\n"
+ " \"name\": \"millimetre\",\n"
+ " \"conversion_factor\": 0.001\n"
+ " },\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8607\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"X-axis rotation\",\n"
+ " \"value\": -39.4924,\n"
+ " \"unit\": {\n"
+ " \"type\": \"AngularUnit\",\n"
+ " \"name\": \"milliarc-second\",\n"
+ " \"conversion_factor\": 4.84813681109536e-09\n"
+ " },\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8608\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Y-axis rotation\",\n"
+ " \"value\": -32.7221,\n"
+ " \"unit\": {\n"
+ " \"type\": \"AngularUnit\",\n"
+ " \"name\": \"milliarc-second\",\n"
+ " \"conversion_factor\": 4.84813681109536e-09\n"
+ " },\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8609\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Z-axis rotation\",\n"
+ " \"value\": -32.8979,\n"
+ " \"unit\": {\n"
+ " \"type\": \"AngularUnit\",\n"
+ " \"name\": \"milliarc-second\",\n"
+ " \"conversion_factor\": 4.84813681109536e-09\n"
+ " },\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8610\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Scale difference\",\n"
+ " \"value\": -9.994,\n"
+ " \"unit\": {\n"
+ " \"type\": \"ScaleUnit\",\n"
+ " \"name\": \"parts per billion\",\n"
+ " \"conversion_factor\": 1e-09\n"
+ " },\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8611\n"
+ " }\n"
+ " }\n"
+ " ],\n"
+ " \"accuracy\": \"0.01\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8048\n"
+ " },\n"
+ " \"remarks\": \"remarks\"\n"
+ " }\n"
+ " ],\n"
+ " \"area\": \"Australia - GDA\",\n"
+ " \"bbox\": {\n"
+ " \"south_latitude\": -60.56,\n"
+ " \"west_longitude\": 93.41,\n"
+ " \"north_latitude\": -8.47,\n"
+ " \"east_longitude\": 173.35\n"
+ " }\n"
+ "}";
+ auto obj = createFromUserInput(json, nullptr);
+ auto concat = nn_dynamic_pointer_cast<ConcatenatedOperation>(obj);
+ ASSERT_TRUE(concat != nullptr);
+ EXPECT_EQ(concat->exportToJSON((JSONFormatter::create().get())), json);
+}
diff --git a/travis/install.sh b/travis/install.sh
index 7a31e5c2..249e0f2c 100755
--- a/travis/install.sh
+++ b/travis/install.sh
@@ -57,6 +57,25 @@ cat out.json
echo "Validating JSON"
jsonschema -i out.json /tmp/proj_autoconf_install_from_dist_all/share/proj/crsjson.schema.json && echo "Valid !"
+/tmp/proj_autoconf_install_from_dist_all/bin/projinfo EPSG:4326+3855 -o PROJJSON -q > out.json
+cat out.json
+echo "Validating JSON"
+jsonschema -i out.json /tmp/proj_autoconf_install_from_dist_all/share/proj/crsjson.schema.json && echo "Valid !"
+
+/tmp/proj_autoconf_install_from_dist_all/bin/projinfo "+proj=longlat +ellps=GRS80 +nadgrids=@foo +type=crs" -o PROJJSON -q > out.json
+cat out.json
+echo "Validating JSON"
+jsonschema -i out.json /tmp/proj_autoconf_install_from_dist_all/share/proj/crsjson.schema.json && echo "Valid !"
+/tmp/proj_autoconf_install_from_dist_all/bin/projinfo @out.json -o PROJJSON -q > out2.json
+diff -u out.json out2.json
+
+/tmp/proj_autoconf_install_from_dist_all/bin/projinfo -s EPSG:3111 -t GDA2020 -o PROJJSON -o PROJJSON -q > out.json
+cat out.json
+echo "Validating JSON"
+jsonschema -i out.json /tmp/proj_autoconf_install_from_dist_all/share/proj/crsjson.schema.json && echo "Valid !"
+/tmp/proj_autoconf_install_from_dist_all/bin/projinfo @out.json -o PROJJSON -q > out2.json
+diff -u out.json out2.json
+
cd ..
# cmake build from generated tarball