aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/proj/coordinatesystem.hpp5
-rw-r--r--include/proj/crs.hpp2
-rw-r--r--scripts/reference_exported_symbols.txt2
-rw-r--r--src/iso19111/coordinatesystem.cpp21
-rw-r--r--src/iso19111/crs.cpp27
-rw-r--r--src/iso19111/io.cpp33
-rw-r--r--test/unit/test_io.cpp84
7 files changed, 161 insertions, 13 deletions
diff --git a/include/proj/coordinatesystem.hpp b/include/proj/coordinatesystem.hpp
index e1650168..b40b038d 100644
--- a/include/proj/coordinatesystem.hpp
+++ b/include/proj/coordinatesystem.hpp
@@ -314,6 +314,11 @@ class PROJ_GCC_DLL SphericalCS final : public CoordinateSystem {
const CoordinateSystemAxisNNPtr &axis2,
const CoordinateSystemAxisNNPtr &axis3);
+ PROJ_DLL static SphericalCSNNPtr
+ create(const util::PropertyMap &properties,
+ const CoordinateSystemAxisNNPtr &axis1,
+ const CoordinateSystemAxisNNPtr &axis2);
+
protected:
PROJ_INTERNAL explicit SphericalCS(
const std::vector<CoordinateSystemAxisNNPtr> &axisIn);
diff --git a/include/proj/crs.hpp b/include/proj/crs.hpp
index dcab094a..7fde88c8 100644
--- a/include/proj/crs.hpp
+++ b/include/proj/crs.hpp
@@ -270,6 +270,8 @@ class PROJ_GCC_DLL GeodeticCRS : virtual public SingleCRS,
PROJ_DLL bool isGeocentric() PROJ_PURE_DECL;
+ PROJ_DLL bool isSphericalPlanetocentric() PROJ_PURE_DECL;
+
PROJ_DLL static GeodeticCRSNNPtr
create(const util::PropertyMap &properties,
const datum::GeodeticReferenceFrameNNPtr &datum,
diff --git a/scripts/reference_exported_symbols.txt b/scripts/reference_exported_symbols.txt
index d49a59b2..6cb734e2 100644
--- a/scripts/reference_exported_symbols.txt
+++ b/scripts/reference_exported_symbols.txt
@@ -152,6 +152,7 @@ osgeo::proj::crs::GeodeticCRS::ellipsoid() const
osgeo::proj::crs::GeodeticCRS::~GeodeticCRS()
osgeo::proj::crs::GeodeticCRS::identify(std::shared_ptr<osgeo::proj::io::AuthorityFactory> const&) const
osgeo::proj::crs::GeodeticCRS::isGeocentric() const
+osgeo::proj::crs::GeodeticCRS::isSphericalPlanetocentric() const
osgeo::proj::crs::GeodeticCRS::primeMeridian() const
osgeo::proj::crs::GeodeticCRS::velocityModel() const
osgeo::proj::crs::GeographicCRS::coordinateSystem() const
@@ -225,6 +226,7 @@ osgeo::proj::cs::OrdinalCS::create(osgeo::proj::util::PropertyMap const&, std::v
osgeo::proj::cs::OrdinalCS::~OrdinalCS()
osgeo::proj::cs::ParametricCS::create(osgeo::proj::util::PropertyMap const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::cs::CoordinateSystemAxis> > const&)
osgeo::proj::cs::ParametricCS::~ParametricCS()
+osgeo::proj::cs::SphericalCS::create(osgeo::proj::util::PropertyMap const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::cs::CoordinateSystemAxis> > const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::cs::CoordinateSystemAxis> > const&)
osgeo::proj::cs::SphericalCS::create(osgeo::proj::util::PropertyMap const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::cs::CoordinateSystemAxis> > const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::cs::CoordinateSystemAxis> > const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::cs::CoordinateSystemAxis> > const&)
osgeo::proj::cs::SphericalCS::~SphericalCS()
osgeo::proj::cs::TemporalCountCS::create(osgeo::proj::util::PropertyMap const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::cs::CoordinateSystemAxis> > const&)
diff --git a/src/iso19111/coordinatesystem.cpp b/src/iso19111/coordinatesystem.cpp
index 498e3035..f9db5406 100644
--- a/src/iso19111/coordinatesystem.cpp
+++ b/src/iso19111/coordinatesystem.cpp
@@ -667,6 +667,27 @@ SphericalCSNNPtr SphericalCS::create(const util::PropertyMap &properties,
// ---------------------------------------------------------------------------
+/** \brief Instantiate a SphericalCS with 2 axis.
+ *
+ * This is an extension to ISO19111 to support (planet)-ocentric CS with
+ * geocentric latitude.
+ *
+ * @param properties See \ref general_properties.
+ * @param axis1 The first axis.
+ * @param axis2 The second axis.
+ * @return a new SphericalCS.
+ */
+SphericalCSNNPtr SphericalCS::create(const util::PropertyMap &properties,
+ const CoordinateSystemAxisNNPtr &axis1,
+ const CoordinateSystemAxisNNPtr &axis2) {
+ std::vector<CoordinateSystemAxisNNPtr> axis{axis1, axis2};
+ auto cs(SphericalCS::nn_make_shared<SphericalCS>(axis));
+ cs->setProperties(properties);
+ return cs;
+}
+
+// ---------------------------------------------------------------------------
+
//! @cond Doxygen_Suppress
EllipsoidalCS::~EllipsoidalCS() = default;
//! @endcond
diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp
index 7c8fcd81..6f511043 100644
--- a/src/iso19111/crs.cpp
+++ b/src/iso19111/crs.cpp
@@ -1608,7 +1608,7 @@ GeodeticCRS::velocityModel() PROJ_PURE_DEFN {
// ---------------------------------------------------------------------------
-/** \brief Return whether the CRS is a geocentric one.
+/** \brief Return whether the CRS is a Cartesian geocentric one.
*
* A geocentric CRS is a geodetic CRS that has a Cartesian coordinate system
* with three axis, whose direction is respectively
@@ -1629,6 +1629,31 @@ bool GeodeticCRS::isGeocentric() PROJ_PURE_DEFN {
// ---------------------------------------------------------------------------
+/** \brief Return whether the CRS is a Spherical planetocentric one.
+ *
+ * A Spherical planetocentric CRS is a geodetic CRS that has a spherical
+ * (angular) coordinate system with 2 axis, which represent geocentric latitude/
+ * longitude or longitude/geocentric latitude.
+ *
+ * Such CRS are typically used in use case that apply to non-Earth bodies.
+ *
+ * @return true if the CRS is a Spherical planetocentric CRS.
+ *
+ * @since 8.2
+ */
+bool GeodeticCRS::isSphericalPlanetocentric() PROJ_PURE_DEFN {
+ const auto &cs = coordinateSystem();
+ const auto &axisList = cs->axisList();
+ return axisList.size() == 2 &&
+ dynamic_cast<cs::SphericalCS *>(cs.get()) != nullptr &&
+ ((ci_equal(axisList[0]->nameStr(), "planetocentric latitude") &&
+ ci_equal(axisList[1]->nameStr(), "planetocentric longitude")) ||
+ (ci_equal(axisList[0]->nameStr(), "planetocentric longitude") &&
+ ci_equal(axisList[1]->nameStr(), "planetocentric latitude")));
+}
+
+// ---------------------------------------------------------------------------
+
/** \brief Instantiate a GeodeticCRS from a datum::GeodeticReferenceFrame and a
* cs::SphericalCS.
*
diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp
index a1b06ae7..1373c583 100644
--- a/src/iso19111/io.cpp
+++ b/src/iso19111/io.cpp
@@ -2798,7 +2798,11 @@ WKTParser::Private::buildCS(const WKTNodeNNPtr &node, /* maybe null */
return VerticalCS::create(csMap, axisList[0]);
}
} else if (ci_equal(csType, "spherical")) {
- if (axisCount == 3) {
+ if (axisCount == 2) {
+ // Extension to ISO19111 to support (planet)-ocentric CS with
+ // geocentric latitude
+ return SphericalCS::create(csMap, axisList[0], axisList[1]);
+ } else if (axisCount == 3) {
return SphericalCS::create(csMap, axisList[0], axisList[1],
axisList[2]);
}
@@ -5945,62 +5949,67 @@ CoordinateSystemNNPtr JSONParser::buildCS(const json &j) {
axisList.emplace_back(buildAxis(axis));
}
const PropertyMap &csMap = emptyPropertyMap;
+ const auto axisCount = axisList.size();
if (subtype == "ellipsoidal") {
- if (axisList.size() == 2) {
+ if (axisCount == 2) {
return EllipsoidalCS::create(csMap, axisList[0], axisList[1]);
}
- if (axisList.size() == 3) {
+ if (axisCount == 3) {
return EllipsoidalCS::create(csMap, axisList[0], axisList[1],
axisList[2]);
}
throw ParsingException("Expected 2 or 3 axis");
}
if (subtype == "Cartesian") {
- if (axisList.size() == 2) {
+ if (axisCount == 2) {
return CartesianCS::create(csMap, axisList[0], axisList[1]);
}
- if (axisList.size() == 3) {
+ if (axisCount == 3) {
return CartesianCS::create(csMap, axisList[0], axisList[1],
axisList[2]);
}
throw ParsingException("Expected 2 or 3 axis");
}
if (subtype == "vertical") {
- if (axisList.size() == 1) {
+ if (axisCount == 1) {
return VerticalCS::create(csMap, axisList[0]);
}
throw ParsingException("Expected 1 axis");
}
if (subtype == "spherical") {
- if (axisList.size() == 3) {
+ if (axisCount == 2) {
+ // Extension to ISO19111 to support (planet)-ocentric CS with
+ // geocentric latitude
+ return SphericalCS::create(csMap, axisList[0], axisList[1]);
+ } else if (axisCount == 3) {
return SphericalCS::create(csMap, axisList[0], axisList[1],
axisList[2]);
}
- throw ParsingException("Expected 3 axis");
+ throw ParsingException("Expected 2 or 3 axis");
}
if (subtype == "ordinal") {
return OrdinalCS::create(csMap, axisList);
}
if (subtype == "parametric") {
- if (axisList.size() == 1) {
+ if (axisCount == 1) {
return ParametricCS::create(csMap, axisList[0]);
}
throw ParsingException("Expected 1 axis");
}
if (subtype == "TemporalDateTime") {
- if (axisList.size() == 1) {
+ if (axisCount == 1) {
return DateTimeTemporalCS::create(csMap, axisList[0]);
}
throw ParsingException("Expected 1 axis");
}
if (subtype == "TemporalCount") {
- if (axisList.size() == 1) {
+ if (axisCount == 1) {
return TemporalCountCS::create(csMap, axisList[0]);
}
throw ParsingException("Expected 1 axis");
}
if (subtype == "TemporalMeasure") {
- if (axisList.size() == 1) {
+ if (axisCount == 1) {
return TemporalMeasureCS::create(csMap, axisList[0]);
}
throw ParsingException("Expected 1 axis");
diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp
index 72ac1868..96e3a849 100644
--- a/test/unit/test_io.cpp
+++ b/test/unit/test_io.cpp
@@ -894,6 +894,37 @@ TEST(wkt_parse, wkt2_EPSG_4979) {
// ---------------------------------------------------------------------------
+TEST(wkt_parse, wkt2_spherical_planetocentric) {
+ const auto wkt =
+ "GEODCRS[\"Mercury (2015) / Ocentric\",\n"
+ " DATUM[\"Mercury (2015)\",\n"
+ " ELLIPSOID[\"Mercury (2015)\",2440530,1075.12334801762,\n"
+ " LENGTHUNIT[\"metre\",1]],\n"
+ " ANCHOR[\"Hun Kal: 20.0\"]],\n"
+ " PRIMEM[\"Reference Meridian\",0,\n"
+ " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
+ " CS[spherical,2],\n"
+ " AXIS[\"planetocentric latitude (U)\",north,\n"
+ " ORDER[1],\n"
+ " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
+ " AXIS[\"planetocentric longitude (V)\",east,\n"
+ " ORDER[2],\n"
+ " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
+ " ID[\"IAU\",19902,2015],\n"
+ " REMARK[\"Source of IAU Coordinate systems: "
+ "doi://10.1007/s10569-017-9805-5\"]]";
+ auto obj = WKTParser().createFromWKT(wkt);
+ auto crs = nn_dynamic_pointer_cast<GeodeticCRS>(obj);
+ ASSERT_TRUE(crs != nullptr);
+ EXPECT_TRUE(crs->isSphericalPlanetocentric());
+ EXPECT_EQ(
+ crs->exportToWKT(
+ WKTFormatter::create(WKTFormatter::Convention::WKT2_2019).get()),
+ wkt);
+}
+
+// ---------------------------------------------------------------------------
+
static void checkGeocentric(GeodeticCRSPtr crs) {
// Explicitly check this is NOT a GeographicCRS
EXPECT_TRUE(!dynamic_cast<GeographicCRS *>(crs.get()));
@@ -11476,6 +11507,59 @@ TEST(json_import, geographic_crs) {
// ---------------------------------------------------------------------------
+TEST(json_import, spherical_planetocentric) {
+ const auto json = "{\n"
+ " \"$schema\": \"foo\",\n"
+ " \"type\": \"GeodeticCRS\",\n"
+ " \"name\": \"Mercury (2015) / Ocentric\",\n"
+ " \"datum\": {\n"
+ " \"type\": \"GeodeticReferenceFrame\",\n"
+ " \"name\": \"Mercury (2015)\",\n"
+ " \"anchor\": \"Hun Kal: 20.0\",\n"
+ " \"ellipsoid\": {\n"
+ " \"name\": \"Mercury (2015)\",\n"
+ " \"semi_major_axis\": 2440530,\n"
+ " \"inverse_flattening\": 1075.12334801762\n"
+ " },\n"
+ " \"prime_meridian\": {\n"
+ " \"name\": \"Reference Meridian\",\n"
+ " \"longitude\": 0\n"
+ " }\n"
+ " },\n"
+ " \"coordinate_system\": {\n"
+ " \"subtype\": \"spherical\",\n"
+ " \"axis\": [\n"
+ " {\n"
+ " \"name\": \"Planetocentric latitude\",\n"
+ " \"abbreviation\": \"U\",\n"
+ " \"direction\": \"north\",\n"
+ " \"unit\": \"degree\"\n"
+ " },\n"
+ " {\n"
+ " \"name\": \"Planetocentric longitude\",\n"
+ " \"abbreviation\": \"V\",\n"
+ " \"direction\": \"east\",\n"
+ " \"unit\": \"degree\"\n"
+ " }\n"
+ " ]\n"
+ " },\n"
+ " \"id\": {\n"
+ " \"authority\": \"IAU\",\n"
+ " \"code\": 19902\n"
+ " },\n"
+ " \"remarks\": \"Source of IAU Coordinate systems: "
+ "doi://10.1007/s10569-017-9805-5\"\n"
+ "}";
+ auto obj = createFromUserInput(json, nullptr);
+ auto gcrs = nn_dynamic_pointer_cast<GeodeticCRS>(obj);
+ ASSERT_TRUE(gcrs != nullptr);
+ EXPECT_TRUE(gcrs->isSphericalPlanetocentric());
+ EXPECT_EQ(gcrs->exportToJSON(&(JSONFormatter::create()->setSchema("foo"))),
+ json);
+}
+
+// ---------------------------------------------------------------------------
+
TEST(json_import, geographic_crs_errors) {
EXPECT_THROW(
createFromUserInput(