aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@spatialys.com>2019-08-10 17:44:53 +0200
committerEven Rouault <even.rouault@spatialys.com>2019-08-10 17:44:53 +0200
commit0a1261781de96d2bb8c76fbd905ebf8b0121d3a6 (patch)
treebc08254e1603339aa5450a5ee1b680a92de66315
parent63981af418d84749cd4d70752f83fd551100389f (diff)
downloadPROJ-0a1261781de96d2bb8c76fbd905ebf8b0121d3a6.tar.gz
PROJ-0a1261781de96d2bb8c76fbd905ebf8b0121d3a6.zip
PROJJSON: a few fixes, and add import of DerivedCRS, EngineeringCRS, ParametricCRS and TemporalCRS
-rw-r--r--data/crsjson.schema.json55
-rw-r--r--src/iso19111/common.cpp2
-rw-r--r--src/iso19111/coordinatesystem.cpp5
-rw-r--r--src/iso19111/crs.cpp4
-rw-r--r--src/iso19111/io.cpp146
-rw-r--r--test/unit/test_io.cpp707
6 files changed, 891 insertions, 28 deletions
diff --git a/data/crsjson.schema.json b/data/crsjson.schema.json
index e38f3038..10b17caa 100644
--- a/data/crsjson.schema.json
+++ b/data/crsjson.schema.json
@@ -37,11 +37,51 @@
"type": { "type": "string", "enum": ["Axis"] },
"name": { "type": "string" },
"abbreviation": { "type": "string" },
- "direction": { "type": "string" },
+ "direction": { "type": "string",
+ "enum": [ "north",
+ "northNorthEast",
+ "northEast",
+ "eastNorthEast",
+ "east",
+ "eastSouthEast",
+ "southEast",
+ "southSouthEast",
+ "south",
+ "southSouthWest",
+ "southWest",
+ "westSouthWest",
+ "west",
+ "westNorthWest",
+ "northWest",
+ "northNorthWest",
+ "up",
+ "down",
+ "geocentricX",
+ "geocentricY",
+ "geocentricZ",
+ "columnPositive",
+ "columnNegative",
+ "rowPositive",
+ "rowNegative",
+ "displayRight",
+ "displayLeft",
+ "displayUp",
+ "displayDown",
+ "forward",
+ "aft",
+ "port",
+ "starboard",
+ "clockwise",
+ "counterClockwise",
+ "towards",
+ "awayFrom",
+ "future",
+ "past",
+ "unspecified" ] },
"unit": { "$ref": "#/definitions/unit" },
"id": { "$ref": "#/definitions/id" }
},
- "required" : [ "name" ],
+ "required" : [ "name", "abbreviation", "direction" ],
"additionalProperties": false
},
@@ -135,7 +175,16 @@
"properties": {
"type": { "type": "string", "enum": ["CoordinateSystem"] },
"name": { "type": "string" },
- "subtype": { "type": "string" },
+ "subtype": { "type": "string",
+ "enum": ["Cartesian",
+ "spherical",
+ "ellipsoidal",
+ "vertical",
+ "ordinal",
+ "parametric",
+ "TemporalDateTime",
+ "TemporalCount",
+ "TemporalMeasure"] },
"axis": {
"type": "array",
"items": { "$ref": "#/definitions/axis" }
diff --git a/src/iso19111/common.cpp b/src/iso19111/common.cpp
index fb7a63c0..f375ea0a 100644
--- a/src/iso19111/common.cpp
+++ b/src/iso19111/common.cpp
@@ -257,7 +257,7 @@ void UnitOfMeasure::_exportToJSON(
} else if (l_type == Type::TIME) {
writer.Add("TimeUnit");
} else if (l_type == Type::PARAMETRIC) {
- writer.Add("ParametericUnit");
+ writer.Add("ParametricUnit");
} else {
writer.Add("Unit");
}
diff --git a/src/iso19111/coordinatesystem.cpp b/src/iso19111/coordinatesystem.cpp
index ef53dd57..5a852b0d 100644
--- a/src/iso19111/coordinatesystem.cpp
+++ b/src/iso19111/coordinatesystem.cpp
@@ -409,12 +409,13 @@ void CoordinateSystemAxis::_exportToJSON(
writer.AddObjKey("direction");
writer.Add(direction().toString());
- writer.AddObjKey("unit");
const auto &l_unit(unit());
if (l_unit == common::UnitOfMeasure::METRE ||
l_unit == common::UnitOfMeasure::DEGREE) {
+ writer.AddObjKey("unit");
writer.Add(l_unit.name());
- } else {
+ } else if (l_unit.type() != common::UnitOfMeasure::Type::NONE) {
+ writer.AddObjKey("unit");
l_unit._exportToJSON(formatter);
}
diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp
index b03ece23..b9694ba2 100644
--- a/src/iso19111/crs.cpp
+++ b/src/iso19111/crs.cpp
@@ -2711,6 +2711,7 @@ void DerivedCRS::_exportToJSON(
baseCRS()->_exportToJSON(formatter);
writer.AddObjKey("conversion");
+ formatter->setOmitTypeInImmediateChild();
derivingConversionRef()->_exportToJSON(formatter);
writer.AddObjKey("coordinate_system");
@@ -4837,6 +4838,7 @@ void TemporalCRS::_exportToJSON(
}
writer.AddObjKey("datum");
+ formatter->setOmitTypeInImmediateChild();
datum()->_exportToJSON(formatter);
writer.AddObjKey("coordinate_system");
@@ -4974,6 +4976,7 @@ void EngineeringCRS::_exportToJSON(
}
writer.AddObjKey("datum");
+ formatter->setOmitTypeInImmediateChild();
datum()->_exportToJSON(formatter);
writer.AddObjKey("coordinate_system");
@@ -5105,6 +5108,7 @@ void ParametricCRS::_exportToJSON(
}
writer.AddObjKey("datum");
+ formatter->setOmitTypeInImmediateChild();
datum()->_exportToJSON(formatter);
writer.AddObjKey("coordinate_system");
diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp
index 8467a9e1..cd54f4ea 100644
--- a/src/iso19111/io.cpp
+++ b/src/iso19111/io.cpp
@@ -4414,6 +4414,61 @@ class JSONParser {
TransformationNNPtr buildTransformation(const json &j);
ConcatenatedOperationNNPtr buildConcatenatedOperation(const json &j);
+ static util::optional<std::string> getAnchor(const json &j) {
+ util::optional<std::string> anchor;
+ if (j.contains("anchor")) {
+ anchor = getString(j, "anchor");
+ }
+ return anchor;
+ }
+
+ EngineeringDatumNNPtr buildEngineeringDatum(const json &j) {
+ return EngineeringDatum::create(buildProperties(j), getAnchor(j));
+ }
+
+ ParametricDatumNNPtr buildParametricDatum(const json &j) {
+ return ParametricDatum::create(buildProperties(j), getAnchor(j));
+ }
+
+ TemporalDatumNNPtr buildTemporalDatum(const json &j) {
+ auto calendar = getString(j, "calendar");
+ auto origin = DateTime::create(j.contains("time_origin")
+ ? getString(j, "time_origin")
+ : std::string());
+ return TemporalDatum::create(buildProperties(j), origin, calendar);
+ }
+
+ template <class TargetCRS, class DatumBuilderType,
+ class CS = CoordinateSystem>
+ util::nn<std::shared_ptr<TargetCRS>> buildCRS(const json &j,
+ DatumBuilderType f) {
+ auto datum = (this->*f)(getObject(j, "datum"));
+ auto cs = buildCS(getObject(j, "coordinate_system"));
+ auto csCast = util::nn_dynamic_pointer_cast<CS>(cs);
+ if (!csCast) {
+ throw ParsingException("coordinate_system not of expected type");
+ }
+ return TargetCRS::create(buildProperties(j), datum,
+ NN_NO_CHECK(csCast));
+ }
+
+ template <class TargetCRS, class BaseCRS, class CS = CoordinateSystem>
+ util::nn<std::shared_ptr<TargetCRS>> buildDerivedCRS(const json &j) {
+ auto baseCRSObj = create(getObject(j, "base_crs"));
+ auto baseCRS = util::nn_dynamic_pointer_cast<BaseCRS>(baseCRSObj);
+ if (!baseCRS) {
+ throw ParsingException("base_crs not of expected type");
+ }
+ auto cs = buildCS(getObject(j, "coordinate_system"));
+ auto csCast = util::nn_dynamic_pointer_cast<CS>(cs);
+ if (!csCast) {
+ throw ParsingException("coordinate_system not of expected type");
+ }
+ auto conv = buildConversion(getObject(j, "conversion"));
+ return TargetCRS::create(buildProperties(j), NN_NO_CHECK(baseCRS), conv,
+ NN_NO_CHECK(csCast));
+ }
+
public:
JSONParser() = default;
@@ -4511,7 +4566,7 @@ UnitOfMeasure JSONParser::getUnit(const json &j, const char *key) {
type = UnitOfMeasure::Type::SCALE;
} else if (typeStr == "TimeUnit") {
type = UnitOfMeasure::Type::TIME;
- } else if (typeStr == "ParametericUnit") {
+ } else if (typeStr == "ParametricUnit") {
type = UnitOfMeasure::Type::PARAMETRIC;
} else if (typeStr == "Unit") {
type = UnitOfMeasure::Type::UNKNOWN;
@@ -4695,6 +4750,58 @@ BaseObjectNNPtr JSONParser::create(const json &j)
if (type == "BoundCRS") {
return buildBoundCRS(j);
}
+ if (type == "EngineeringCRS") {
+ return buildCRS<EngineeringCRS>(j, &JSONParser::buildEngineeringDatum);
+ }
+ if (type == "ParametricCRS") {
+ return buildCRS<ParametricCRS,
+ decltype(&JSONParser::buildParametricDatum),
+ ParametricCS>(j, &JSONParser::buildParametricDatum);
+ }
+ if (type == "TemporalCRS") {
+ return buildCRS<TemporalCRS, decltype(&JSONParser::buildTemporalDatum),
+ TemporalCS>(j, &JSONParser::buildTemporalDatum);
+ }
+ if (type == "DerivedGeodeticCRS") {
+ auto baseCRSObj = create(getObject(j, "base_crs"));
+ auto baseCRS = util::nn_dynamic_pointer_cast<GeodeticCRS>(baseCRSObj);
+ if (!baseCRS) {
+ throw ParsingException("base_crs not of expected type");
+ }
+ auto cs = buildCS(getObject(j, "coordinate_system"));
+ auto conv = buildConversion(getObject(j, "conversion"));
+ auto csCartesian = util::nn_dynamic_pointer_cast<CartesianCS>(cs);
+ if (csCartesian)
+ return DerivedGeodeticCRS::create(buildProperties(j),
+ NN_NO_CHECK(baseCRS), conv,
+ NN_NO_CHECK(csCartesian));
+ auto csSpherical = util::nn_dynamic_pointer_cast<SphericalCS>(cs);
+ if (csSpherical)
+ return DerivedGeodeticCRS::create(buildProperties(j),
+ NN_NO_CHECK(baseCRS), conv,
+ NN_NO_CHECK(csSpherical));
+ throw ParsingException("coordinate_system not of expected type");
+ }
+ if (type == "DerivedGeographicCRS") {
+ return buildDerivedCRS<DerivedGeographicCRS, GeodeticCRS,
+ EllipsoidalCS>(j);
+ }
+ if (type == "DerivedProjectedCRS") {
+ return buildDerivedCRS<DerivedProjectedCRS, ProjectedCRS>(j);
+ }
+ if (type == "DerivedVerticalCRS") {
+ return buildDerivedCRS<DerivedVerticalCRS, VerticalCRS, VerticalCS>(j);
+ }
+ if (type == "DerivedEngineeringCRS") {
+ return buildDerivedCRS<DerivedEngineeringCRS, EngineeringCRS>(j);
+ }
+ if (type == "DerivedParametricCRS") {
+ return buildDerivedCRS<DerivedParametricCRS, ParametricCRS,
+ ParametricCS>(j);
+ }
+ if (type == "DerivedTemporalCRS") {
+ return buildDerivedCRS<DerivedTemporalCRS, TemporalCRS, TemporalCS>(j);
+ }
if (type == "DatumEnsemble") {
return buildDatumEnsemble(j);
}
@@ -4710,6 +4817,15 @@ BaseObjectNNPtr JSONParser::create(const json &j)
if (type == "DynamicVerticalReferenceFrame") {
return buildDynamicVerticalReferenceFrame(j);
}
+ if (type == "EngineeringDatum") {
+ return buildEngineeringDatum(j);
+ }
+ if (type == "ParametricDatum") {
+ return buildParametricDatum(j);
+ }
+ if (type == "TemporalDatum") {
+ return buildTemporalDatum(j);
+ }
if (type == "Ellipsoid") {
return buildEllipsoid(j);
}
@@ -5033,7 +5149,9 @@ JSONParser::buildConcatenatedOperation(const json &j) {
CoordinateSystemAxisNNPtr JSONParser::buildAxis(const json &j) {
auto dirString = getString(j, "direction");
auto abbreviation = getString(j, "abbreviation");
- auto unit = getUnit(j, "unit");
+ auto unit = j.contains("unit") ? getUnit(j, "unit")
+ : UnitOfMeasure(std::string(), 1.0,
+ UnitOfMeasure::Type::NONE);
auto direction = AxisDirection::valueOf(dirString);
if (!direction) {
throw ParsingException(concat("unhandled axis direction: ", dirString));
@@ -5198,12 +5316,8 @@ 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), anchor, pm);
+ buildProperties(j), buildEllipsoid(ellipsoidJ), getAnchor(j), pm);
}
// ---------------------------------------------------------------------------
@@ -5214,10 +5328,6 @@ JSONParser::buildDynamicGeodeticReferenceFrame(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");
- }
Measure frameReferenceEpoch(getNumber(j, "frame_reference_epoch"),
UnitOfMeasure::YEAR);
optional<std::string> deformationModel;
@@ -5225,7 +5335,7 @@ JSONParser::buildDynamicGeodeticReferenceFrame(const json &j) {
deformationModel = getString(j, "deformation_model");
}
return DynamicGeodeticReferenceFrame::create(
- buildProperties(j), buildEllipsoid(ellipsoidJ), anchor, pm,
+ buildProperties(j), buildEllipsoid(ellipsoidJ), getAnchor(j), pm,
frameReferenceEpoch, deformationModel);
}
@@ -5233,21 +5343,13 @@ JSONParser::buildDynamicGeodeticReferenceFrame(const json &j) {
VerticalReferenceFrameNNPtr
JSONParser::buildVerticalReferenceFrame(const json &j) {
- optional<std::string> anchor;
- if (j.contains("anchor")) {
- anchor = getString(j, "anchor");
- }
- return VerticalReferenceFrame::create(buildProperties(j), anchor);
+ return VerticalReferenceFrame::create(buildProperties(j), getAnchor(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;
@@ -5255,7 +5357,7 @@ JSONParser::buildDynamicVerticalReferenceFrame(const json &j) {
deformationModel = getString(j, "deformation_model");
}
return DynamicVerticalReferenceFrame::create(
- buildProperties(j), anchor, util::optional<RealizationMethod>(),
+ buildProperties(j), getAnchor(j), util::optional<RealizationMethod>(),
frameReferenceEpoch, deformationModel);
}
diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp
index 67fdcb77..74ef82f3 100644
--- a/test/unit/test_io.cpp
+++ b/test/unit/test_io.cpp
@@ -10581,6 +10581,41 @@ TEST(json_import, datum_ensemble_without_ellipsoid) {
// ---------------------------------------------------------------------------
+TEST(json_import, vertical_crs) {
+ auto json = "{\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"
+ "}";
+
+ auto obj = createFromUserInput(json, nullptr);
+ auto crs = nn_dynamic_pointer_cast<VerticalCRS>(obj);
+ ASSERT_TRUE(crs != nullptr);
+ EXPECT_EQ(crs->exportToJSON((JSONFormatter::create().get())), json);
+
+ auto datum = crs->datum();
+ auto datum_json = datum->exportToJSON((JSONFormatter::create().get()));
+ auto datum_obj = createFromUserInput(datum_json, nullptr);
+ auto datum_got = nn_dynamic_pointer_cast<VerticalReferenceFrame>(datum_obj);
+ ASSERT_TRUE(datum_got != nullptr);
+}
+
+// ---------------------------------------------------------------------------
+
TEST(json_import, vertical_crs_with_datum_ensemble) {
auto json = "{\n"
" \"type\": \"VerticalCRS\",\n"
@@ -10616,3 +10651,675 @@ TEST(json_import, vertical_crs_with_datum_ensemble) {
ASSERT_TRUE(vcrs != nullptr);
EXPECT_EQ(vcrs->exportToJSON((JSONFormatter::create().get())), json);
}
+
+// ---------------------------------------------------------------------------
+
+TEST(json_import, parametric_crs) {
+ auto json = "{\n"
+ " \"type\": \"ParametricCRS\",\n"
+ " \"name\": \"WMO standard atmosphere layer 0\",\n"
+ " \"datum\": {\n"
+ " \"name\": \"Mean Sea Level\",\n"
+ " \"anchor\": \"1013.25 hPa at 15°C\"\n"
+ " },\n"
+ " \"coordinate_system\": {\n"
+ " \"subtype\": \"parametric\",\n"
+ " \"axis\": [\n"
+ " {\n"
+ " \"name\": \"Pressure\",\n"
+ " \"abbreviation\": \"hPa\",\n"
+ " \"direction\": \"up\",\n"
+ " \"unit\": {\n"
+ " \"type\": \"ParametricUnit\",\n"
+ " \"name\": \"HectoPascal\",\n"
+ " \"conversion_factor\": 100\n"
+ " }\n"
+ " }\n"
+ " ]\n"
+ " }\n"
+ "}";
+
+ auto obj = createFromUserInput(json, nullptr);
+ auto crs = nn_dynamic_pointer_cast<ParametricCRS>(obj);
+ ASSERT_TRUE(crs != nullptr);
+ EXPECT_EQ(crs->exportToJSON((JSONFormatter::create().get())), json);
+
+ auto datum = crs->datum();
+ auto datum_json = datum->exportToJSON((JSONFormatter::create().get()));
+ auto datum_obj = createFromUserInput(datum_json, nullptr);
+ auto datum_got = nn_dynamic_pointer_cast<ParametricDatum>(datum_obj);
+ ASSERT_TRUE(datum_got != nullptr);
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(json_import, engineering_crs) {
+ auto json = "{\n"
+ " \"type\": \"EngineeringCRS\",\n"
+ " \"name\": \"Engineering CRS\",\n"
+ " \"datum\": {\n"
+ " \"name\": \"Engineering datum\"\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"
+ "}";
+
+ auto obj = createFromUserInput(json, nullptr);
+ auto crs = nn_dynamic_pointer_cast<EngineeringCRS>(obj);
+ ASSERT_TRUE(crs != nullptr);
+ EXPECT_EQ(crs->exportToJSON((JSONFormatter::create().get())), json);
+
+ auto datum = crs->datum();
+ auto datum_json = datum->exportToJSON((JSONFormatter::create().get()));
+ auto datum_obj = createFromUserInput(datum_json, nullptr);
+ auto datum_got = nn_dynamic_pointer_cast<EngineeringDatum>(datum_obj);
+ ASSERT_TRUE(datum_got != nullptr);
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(json_import, temporal_crs) {
+ auto json = "{\n"
+ " \"type\": \"TemporalCRS\",\n"
+ " \"name\": \"Temporal CRS\",\n"
+ " \"datum\": {\n"
+ " \"name\": \"Gregorian calendar\",\n"
+ " \"calendar\": \"proleptic Gregorian\",\n"
+ " \"time_origin\": \"0000-01-01\"\n"
+ " },\n"
+ " \"coordinate_system\": {\n"
+ " \"subtype\": \"TemporalDateTime\",\n"
+ " \"axis\": [\n"
+ " {\n"
+ " \"name\": \"Time\",\n"
+ " \"abbreviation\": \"T\",\n"
+ " \"direction\": \"future\"\n"
+ " }\n"
+ " ]\n"
+ " }\n"
+ "}";
+
+ auto obj = createFromUserInput(json, nullptr);
+ auto crs = nn_dynamic_pointer_cast<TemporalCRS>(obj);
+ ASSERT_TRUE(crs != nullptr);
+ EXPECT_EQ(crs->exportToJSON((JSONFormatter::create().get())), json);
+
+ auto datum = crs->datum();
+ auto datum_json = datum->exportToJSON((JSONFormatter::create().get()));
+ auto datum_obj = createFromUserInput(datum_json, nullptr);
+ auto datum_got = nn_dynamic_pointer_cast<TemporalDatum>(datum_obj);
+ ASSERT_TRUE(datum_got != nullptr);
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(json_import, derived_geodetic_crs) {
+ auto json = "{\n"
+ " \"type\": \"DerivedGeodeticCRS\",\n"
+ " \"name\": \"Derived geodetic CRS\",\n"
+ " \"base_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"
+ " },\n"
+ " \"conversion\": {\n"
+ " \"name\": \"Some conversion\",\n"
+ " \"method\": {\n"
+ " \"name\": \"Some method\"\n"
+ " },\n"
+ " \"parameters\": [\n"
+ " {\n"
+ " \"name\": \"foo\",\n"
+ " \"value\": 1,\n"
+ " \"unit\": \"metre\"\n"
+ " }\n"
+ " ]\n"
+ " },\n"
+ " \"coordinate_system\": {\n"
+ " \"subtype\": \"Cartesian\",\n"
+ " \"axis\": [\n"
+ " {\n"
+ " \"name\": \"Geocentric X\",\n"
+ " \"abbreviation\": \"X\",\n"
+ " \"direction\": \"geocentricX\",\n"
+ " \"unit\": \"metre\"\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Geocentric Y\",\n"
+ " \"abbreviation\": \"Y\",\n"
+ " \"direction\": \"geocentricY\",\n"
+ " \"unit\": \"metre\"\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Geocentric Z\",\n"
+ " \"abbreviation\": \"Z\",\n"
+ " \"direction\": \"geocentricZ\",\n"
+ " \"unit\": \"metre\"\n"
+ " }\n"
+ " ]\n"
+ " }\n"
+ "}";
+ auto obj = createFromUserInput(json, nullptr);
+ auto crs = nn_dynamic_pointer_cast<DerivedGeodeticCRS>(obj);
+ ASSERT_TRUE(crs != nullptr);
+ EXPECT_EQ(crs->exportToJSON((JSONFormatter::create().get())), json);
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(json_import, derived_geographic_crs) {
+ auto json = "{\n"
+ " \"type\": \"DerivedGeographicCRS\",\n"
+ " \"name\": \"WMO Atlantic Pole\",\n"
+ " \"base_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"
+ " },\n"
+ " \"conversion\": {\n"
+ " \"name\": \"Atlantic pole\",\n"
+ " \"method\": {\n"
+ " \"name\": \"Pole rotation\"\n"
+ " },\n"
+ " \"parameters\": [\n"
+ " {\n"
+ " \"name\": \"Latitude of rotated pole\",\n"
+ " \"value\": 52,\n"
+ " \"unit\": \"degree\"\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Longitude of rotated pole\",\n"
+ " \"value\": -30,\n"
+ " \"unit\": \"degree\"\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Axis rotation\",\n"
+ " \"value\": -25,\n"
+ " \"unit\": \"degree\"\n"
+ " }\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"
+ "}";
+ auto obj = createFromUserInput(json, nullptr);
+ auto crs = nn_dynamic_pointer_cast<DerivedGeographicCRS>(obj);
+ ASSERT_TRUE(crs != nullptr);
+ EXPECT_EQ(crs->exportToJSON((JSONFormatter::create().get())), json);
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(json_import, derived_projected_crs) {
+ auto json = "{\n"
+ " \"type\": \"DerivedProjectedCRS\",\n"
+ " \"name\": \"derived projectedCRS\",\n"
+ " \"base_crs\": {\n"
+ " \"type\": \"ProjectedCRS\",\n"
+ " \"name\": \"WGS 84 / UTM zone 31N\",\n"
+ " \"base_crs\": {\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"
+ " },\n"
+ " \"conversion\": {\n"
+ " \"name\": \"UTM zone 31N\",\n"
+ " \"method\": {\n"
+ " \"name\": \"Transverse Mercator\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 9807\n"
+ " }\n"
+ " },\n"
+ " \"parameters\": [\n"
+ " {\n"
+ " \"name\": \"Latitude of natural origin\",\n"
+ " \"value\": 0,\n"
+ " \"unit\": \"degree\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8801\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Longitude of natural origin\",\n"
+ " \"value\": 3,\n"
+ " \"unit\": \"degree\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8802\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Scale factor at natural origin\",\n"
+ " \"value\": 0.9996,\n"
+ " \"unit\": \"unity\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8805\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"False easting\",\n"
+ " \"value\": 500000,\n"
+ " \"unit\": \"metre\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8806\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"False northing\",\n"
+ " \"value\": 0,\n"
+ " \"unit\": \"metre\",\n"
+ " \"id\": {\n"
+ " \"authority\": \"EPSG\",\n"
+ " \"code\": 8807\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"
+ " },\n"
+ " \"conversion\": {\n"
+ " \"name\": \"unnamed\",\n"
+ " \"method\": {\n"
+ " \"name\": \"PROJ unimplemented\"\n"
+ " },\n"
+ " \"parameters\": [\n"
+ " {\n"
+ " \"name\": \"foo\",\n"
+ " \"value\": 1,\n"
+ " \"unit\": \"metre\"\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"
+ "}";
+ auto obj = createFromUserInput(json, nullptr);
+ auto crs = nn_dynamic_pointer_cast<DerivedProjectedCRS>(obj);
+ ASSERT_TRUE(crs != nullptr);
+ EXPECT_EQ(crs->exportToJSON((JSONFormatter::create().get())), json);
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(json_import, derived_vertical_crs) {
+ auto json = "{\n"
+ " \"type\": \"DerivedVerticalCRS\",\n"
+ " \"name\": \"Derived vertCRS\",\n"
+ " \"base_crs\": {\n"
+ " \"type\": \"VerticalCRS\",\n"
+ " \"name\": \"ODN height\",\n"
+ " \"datum\": {\n"
+ " \"type\": \"VerticalReferenceFrame\",\n"
+ " \"name\": \"Ordnance Datum Newlyn\"\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"
+ " },\n"
+ " \"conversion\": {\n"
+ " \"name\": \"unnamed\",\n"
+ " \"method\": {\n"
+ " \"name\": \"PROJ unimplemented\"\n"
+ " },\n"
+ " \"parameters\": [\n"
+ " {\n"
+ " \"name\": \"foo\",\n"
+ " \"value\": 1,\n"
+ " \"unit\": \"metre\"\n"
+ " }\n"
+ " ]\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"
+ "}";
+ auto obj = createFromUserInput(json, nullptr);
+ auto crs = nn_dynamic_pointer_cast<DerivedVerticalCRS>(obj);
+ ASSERT_TRUE(crs != nullptr);
+ EXPECT_EQ(crs->exportToJSON((JSONFormatter::create().get())), json);
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(json_import, derived_engineering_crs) {
+ auto json = "{\n"
+ " \"type\": \"DerivedEngineeringCRS\",\n"
+ " \"name\": \"Derived EngineeringCRS\",\n"
+ " \"base_crs\": {\n"
+ " \"type\": \"EngineeringCRS\",\n"
+ " \"name\": \"Engineering CRS\",\n"
+ " \"datum\": {\n"
+ " \"name\": \"Engineering datum\"\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"
+ " },\n"
+ " \"conversion\": {\n"
+ " \"name\": \"unnamed\",\n"
+ " \"method\": {\n"
+ " \"name\": \"PROJ unimplemented\"\n"
+ " },\n"
+ " \"parameters\": [\n"
+ " {\n"
+ " \"name\": \"foo\",\n"
+ " \"value\": 1,\n"
+ " \"unit\": \"metre\"\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"
+ "}";
+ auto obj = createFromUserInput(json, nullptr);
+ auto crs = nn_dynamic_pointer_cast<DerivedEngineeringCRS>(obj);
+ ASSERT_TRUE(crs != nullptr);
+ EXPECT_EQ(crs->exportToJSON((JSONFormatter::create().get())), json);
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(json_import, derived_parametric_crs) {
+ auto json = "{\n"
+ " \"type\": \"DerivedParametricCRS\",\n"
+ " \"name\": \"Derived ParametricCRS\",\n"
+ " \"base_crs\": {\n"
+ " \"type\": \"ParametricCRS\",\n"
+ " \"name\": \"Parametric CRS\",\n"
+ " \"datum\": {\n"
+ " \"name\": \"Parametric datum\"\n"
+ " },\n"
+ " \"coordinate_system\": {\n"
+ " \"subtype\": \"parametric\",\n"
+ " \"axis\": [\n"
+ " {\n"
+ " \"name\": \"unknown parametric\",\n"
+ " \"abbreviation\": \"\",\n"
+ " \"direction\": \"unspecified\",\n"
+ " \"unit\": {\n"
+ " \"type\": \"ParametricUnit\",\n"
+ " \"name\": \"unknown\",\n"
+ " \"conversion_factor\": 1\n"
+ " }\n"
+ " }\n"
+ " ]\n"
+ " }\n"
+ " },\n"
+ " \"conversion\": {\n"
+ " \"name\": \"unnamed\",\n"
+ " \"method\": {\n"
+ " \"name\": \"PROJ unimplemented\"\n"
+ " },\n"
+ " \"parameters\": [\n"
+ " {\n"
+ " \"name\": \"foo\",\n"
+ " \"value\": 1,\n"
+ " \"unit\": \"metre\"\n"
+ " }\n"
+ " ]\n"
+ " },\n"
+ " \"coordinate_system\": {\n"
+ " \"subtype\": \"parametric\",\n"
+ " \"axis\": [\n"
+ " {\n"
+ " \"name\": \"Pressure\",\n"
+ " \"abbreviation\": \"hPa\",\n"
+ " \"direction\": \"up\",\n"
+ " \"unit\": {\n"
+ " \"type\": \"ParametricUnit\",\n"
+ " \"name\": \"HectoPascal\",\n"
+ " \"conversion_factor\": 100\n"
+ " }\n"
+ " }\n"
+ " ]\n"
+ " }\n"
+ "}";
+ auto obj = createFromUserInput(json, nullptr);
+ auto crs = nn_dynamic_pointer_cast<DerivedParametricCRS>(obj);
+ ASSERT_TRUE(crs != nullptr);
+ EXPECT_EQ(crs->exportToJSON((JSONFormatter::create().get())), json);
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(json_import, derived_temporal_crs) {
+ auto json = "{\n"
+ " \"type\": \"DerivedTemporalCRS\",\n"
+ " \"name\": \"Derived TemporalCRS\",\n"
+ " \"base_crs\": {\n"
+ " \"type\": \"TemporalCRS\",\n"
+ " \"name\": \"Temporal CRS\",\n"
+ " \"datum\": {\n"
+ " \"name\": \"Gregorian calendar\",\n"
+ " \"calendar\": \"proleptic Gregorian\",\n"
+ " \"time_origin\": \"0000-01-01\"\n"
+ " },\n"
+ " \"coordinate_system\": {\n"
+ " \"subtype\": \"TemporalDateTime\",\n"
+ " \"axis\": [\n"
+ " {\n"
+ " \"name\": \"unknown temporal\",\n"
+ " \"abbreviation\": \"\",\n"
+ " \"direction\": \"future\",\n"
+ " \"unit\": {\n"
+ " \"type\": \"TimeUnit\",\n"
+ " \"name\": \"unknown\",\n"
+ " \"conversion_factor\": 1\n"
+ " }\n"
+ " }\n"
+ " ]\n"
+ " }\n"
+ " },\n"
+ " \"conversion\": {\n"
+ " \"name\": \"unnamed\",\n"
+ " \"method\": {\n"
+ " \"name\": \"PROJ unimplemented\"\n"
+ " },\n"
+ " \"parameters\": [\n"
+ " {\n"
+ " \"name\": \"foo\",\n"
+ " \"value\": 1,\n"
+ " \"unit\": \"metre\"\n"
+ " }\n"
+ " ]\n"
+ " },\n"
+ " \"coordinate_system\": {\n"
+ " \"subtype\": \"TemporalDateTime\",\n"
+ " \"axis\": [\n"
+ " {\n"
+ " \"name\": \"Time\",\n"
+ " \"abbreviation\": \"T\",\n"
+ " \"direction\": \"future\"\n"
+ " }\n"
+ " ]\n"
+ " }\n"
+ "}";
+ auto obj = createFromUserInput(json, nullptr);
+ auto crs = nn_dynamic_pointer_cast<DerivedTemporalCRS>(obj);
+ ASSERT_TRUE(crs != nullptr);
+ EXPECT_EQ(crs->exportToJSON((JSONFormatter::create().get())), json);
+}