diff --git a/data/projjson.schema.json b/data/projjson.schema.json index 7197d97975..6dceb2b2ac 100644 --- a/data/projjson.schema.json +++ b/data/projjson.schema.json @@ -237,6 +237,7 @@ "vertical", "ordinal", "parametric", + "affine", "TemporalDateTime", "TemporalCount", "TemporalMeasure"] }, diff --git a/docs/source/specifications/projjson.rst b/docs/source/specifications/projjson.rst index db22c3b68d..73943c8e7f 100644 --- a/docs/source/specifications/projjson.rst +++ b/docs/source/specifications/projjson.rst @@ -57,6 +57,7 @@ History of the schema - Added CoordinateMetadata - Added "datum_epoch" property to GeodeticReferenceFrame and VerticalReferenceFrame - Added "minimum_value", "maximum_value" and "range_meaning" properties to Axis + - Added "affine" in the CoordinateSystem.subtype enumeration. * v0.5: - Implemented in PROJ 9.1: + add "meridian" member in Axis object type. diff --git a/include/proj/coordinatesystem.hpp b/include/proj/coordinatesystem.hpp index 306a87edd6..0dc0702e23 100644 --- a/include/proj/coordinatesystem.hpp +++ b/include/proj/coordinatesystem.hpp @@ -582,6 +582,56 @@ class PROJ_GCC_DLL CartesianCS final : public CoordinateSystem { // --------------------------------------------------------------------------- +class AffineCS; +/** Shared pointer of AffineCS. */ +using AffineCSPtr = std::shared_ptr; +/** Non-null shared pointer of AffineCS. */ +using AffineCSNNPtr = util::nn; + +/** \brief A two- or three-dimensional coordinate system in Euclidean space + * with straight axes that are not necessarily orthogonal. + * + * \remark Implements AffineCS from \ref ISO_19111_2019 + * \since 9.2 + */ +class PROJ_GCC_DLL AffineCS final : public CoordinateSystem { + public: + //! @cond Doxygen_Suppress + PROJ_DLL ~AffineCS() override; + //! @endcond + + PROJ_DLL static AffineCSNNPtr + create(const util::PropertyMap &properties, + const CoordinateSystemAxisNNPtr &axis1, + const CoordinateSystemAxisNNPtr &axis2); + PROJ_DLL static AffineCSNNPtr + create(const util::PropertyMap &properties, + const CoordinateSystemAxisNNPtr &axis1, + const CoordinateSystemAxisNNPtr &axis2, + const CoordinateSystemAxisNNPtr &axis3); + + PROJ_PRIVATE : + //! @cond Doxygen_Suppress + PROJ_INTERNAL AffineCSNNPtr + alterUnit(const common::UnitOfMeasure &unit) const; + + //! @endcond + + protected: + PROJ_INTERNAL explicit AffineCS( + const std::vector &axisIn); + INLINED_MAKE_SHARED + + PROJ_INTERNAL std::string getWKT2Type(bool) const override { + return "affine"; + } + + private: + AffineCS(const AffineCS &other) = delete; +}; + +// --------------------------------------------------------------------------- + class OrdinalCS; /** Shared pointer of OrdinalCS. */ using OrdinalCSPtr = std::shared_ptr; diff --git a/schemas/v0.6/projjson.schema.json b/schemas/v0.6/projjson.schema.json index 7197d97975..6dceb2b2ac 100644 --- a/schemas/v0.6/projjson.schema.json +++ b/schemas/v0.6/projjson.schema.json @@ -237,6 +237,7 @@ "vertical", "ordinal", "parametric", + "affine", "TemporalDateTime", "TemporalCount", "TemporalMeasure"] }, diff --git a/scripts/reference_exported_symbols.txt b/scripts/reference_exported_symbols.txt index c56c972119..5440b1628f 100644 --- a/scripts/reference_exported_symbols.txt +++ b/scripts/reference_exported_symbols.txt @@ -198,6 +198,9 @@ osgeo::proj::crs::VerticalCRS::geoidModel() const osgeo::proj::crs::VerticalCRS::identify(std::shared_ptr const&) const osgeo::proj::crs::VerticalCRS::velocityModel() const osgeo::proj::crs::VerticalCRS::~VerticalCRS() +osgeo::proj::cs::AffineCS::~AffineCS() +osgeo::proj::cs::AffineCS::create(osgeo::proj::util::PropertyMap const&, dropbox::oxygen::nn > const&, dropbox::oxygen::nn > const&) +osgeo::proj::cs::AffineCS::create(osgeo::proj::util::PropertyMap const&, dropbox::oxygen::nn > const&, dropbox::oxygen::nn > const&, dropbox::oxygen::nn > const&) osgeo::proj::cs::AxisDirection::valueOf(std::string const&) osgeo::proj::cs::CartesianCS::~CartesianCS() osgeo::proj::cs::CartesianCS::createEastingNorthing(osgeo::proj::common::UnitOfMeasure const&) diff --git a/src/iso19111/coordinatesystem.cpp b/src/iso19111/coordinatesystem.cpp index 1d144df191..d7cdcb643d 100644 --- a/src/iso19111/coordinatesystem.cpp +++ b/src/iso19111/coordinatesystem.cpp @@ -1288,6 +1288,73 @@ CartesianCS::alterUnit(const common::UnitOfMeasure &unit) const { // --------------------------------------------------------------------------- +//! @cond Doxygen_Suppress +AffineCS::~AffineCS() = default; +//! @endcond + +// --------------------------------------------------------------------------- + +AffineCS::AffineCS(const std::vector &axisIn) + : CoordinateSystem(axisIn) {} + +// --------------------------------------------------------------------------- + +/** \brief Instantiate a AffineCS. + * + * @param properties See \ref general_properties. + * @param axis1 The first axis. + * @param axis2 The second axis. + * @return a new AffineCS. + */ +AffineCSNNPtr AffineCS::create(const util::PropertyMap &properties, + const CoordinateSystemAxisNNPtr &axis1, + const CoordinateSystemAxisNNPtr &axis2) { + std::vector axis{axis1, axis2}; + auto cs(AffineCS::nn_make_shared(axis)); + cs->setProperties(properties); + return cs; +} + +// --------------------------------------------------------------------------- + +/** \brief Instantiate a AffineCS. + * + * @param properties See \ref general_properties. + * @param axis1 The first axis. + * @param axis2 The second axis. + * @param axis3 The third axis. + * @return a new AffineCS. + */ +AffineCSNNPtr AffineCS::create(const util::PropertyMap &properties, + const CoordinateSystemAxisNNPtr &axis1, + const CoordinateSystemAxisNNPtr &axis2, + const CoordinateSystemAxisNNPtr &axis3) { + std::vector axis{axis1, axis2, axis3}; + auto cs(AffineCS::nn_make_shared(axis)); + cs->setProperties(properties); + return cs; +} + +// --------------------------------------------------------------------------- + +//! @cond Doxygen_Suppress +AffineCSNNPtr AffineCS::alterUnit(const common::UnitOfMeasure &unit) const { + const auto &l_axisList = CoordinateSystem::getPrivate()->axisList; + if (l_axisList.size() == 2) { + return AffineCS::create(util::PropertyMap(), + l_axisList[0]->alterUnit(unit), + l_axisList[1]->alterUnit(unit)); + } else { + assert(l_axisList.size() == 3); + return AffineCS::create( + util::PropertyMap(), l_axisList[0]->alterUnit(unit), + l_axisList[1]->alterUnit(unit), l_axisList[2]->alterUnit(unit)); + } +} +//! @endcond + +// --------------------------------------------------------------------------- + //! @cond Doxygen_Suppress OrdinalCS::~OrdinalCS() = default; //! @endcond diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp index 0c9a81a4eb..5207df600c 100644 --- a/src/iso19111/io.cpp +++ b/src/iso19111/io.cpp @@ -2940,7 +2940,8 @@ WKTParser::Private::buildCS(const WKTNodeNNPtr &node, /* maybe null */ : ci_equal(csType, "parametric") ? UnitOfMeasure::Type::PARAMETRIC : ci_equal(csType, "Cartesian") || - ci_equal(csType, "vertical") + ci_equal(csType, "vertical") || + ci_equal(csType, "affine") ? UnitOfMeasure::Type::LINEAR : (ci_equal(csType, "temporal") || ci_equal(csType, "TemporalDateTime") || @@ -2972,6 +2973,13 @@ WKTParser::Private::buildCS(const WKTNodeNNPtr &node, /* maybe null */ return CartesianCS::create(csMap, axisList[0], axisList[1], axisList[2]); } + } else if (ci_equal(csType, "affine")) { + if (axisCount == 2) { + return AffineCS::create(csMap, axisList[0], axisList[1]); + } else if (axisCount == 3) { + return AffineCS::create(csMap, axisList[0], axisList[1], + axisList[2]); + } } else if (ci_equal(csType, "vertical")) { if (axisCount == 1) { return VerticalCS::create(csMap, axisList[0]); @@ -6635,6 +6643,16 @@ CoordinateSystemNNPtr JSONParser::buildCS(const json &j) { } throw ParsingException("Expected 2 or 3 axis"); } + if (subtype == "affine") { + if (axisCount == 2) { + return AffineCS::create(csMap, axisList[0], axisList[1]); + } + if (axisCount == 3) { + return AffineCS::create(csMap, axisList[0], axisList[1], + axisList[2]); + } + throw ParsingException("Expected 2 or 3 axis"); + } if (subtype == "vertical") { if (axisCount == 1) { return VerticalCS::create(csMap, axisList[0]); diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp index cf3675c9b6..5fbd1e1cf2 100644 --- a/test/unit/test_io.cpp +++ b/test/unit/test_io.cpp @@ -2979,6 +2979,30 @@ TEST(wkt_parse, vdatum_with_ANCHOREPOCH) { // --------------------------------------------------------------------------- +TEST(wkt_parse, engineeringCRS_WKT2_affine_CS) { + + auto wkt = "ENGCRS[\"Engineering CRS\",\n" + " EDATUM[\"Engineering datum\"],\n" + " CS[affine,2],\n" + " AXIS[\"(E)\",east,\n" + " ORDER[1],\n" + " LENGTHUNIT[\"metre\",1,\n" + " ID[\"EPSG\",9001]]],\n" + " AXIS[\"(N)\",north,\n" + " ORDER[2],\n" + " LENGTHUNIT[\"metre\",1,\n" + " ID[\"EPSG\",9001]]]]"; + + auto obj = WKTParser().createFromWKT(wkt); + auto crs = nn_dynamic_pointer_cast(obj); + ASSERT_TRUE(crs != nullptr); + EXPECT_EQ(crs->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT2).get()), + wkt); +} + +// --------------------------------------------------------------------------- + TEST(wkt_parse, COMPOUNDCRS) { auto obj = WKTParser().createFromWKT( "COMPOUNDCRS[\"horizontal + vertical\",\n" @@ -15599,6 +15623,43 @@ TEST(json_import, engineering_crs) { // --------------------------------------------------------------------------- +TEST(json_import, engineering_crs_affine_CS) { + + auto json = "{\n" + " \"$schema\": \"foo\",\n" + " \"type\": \"EngineeringCRS\",\n" + " \"name\": \"myEngCRS\",\n" + " \"datum\": {\n" + " \"name\": \"myEngDatum\"\n" + " },\n" + " \"coordinate_system\": {\n" + " \"subtype\": \"affine\",\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(obj); + ASSERT_TRUE(crs != nullptr); + EXPECT_EQ(crs->exportToJSON(&(JSONFormatter::create()->setSchema("foo"))), + json); +} + +// --------------------------------------------------------------------------- + TEST(json_import, temporal_crs) { auto json = "{\n" " \"$schema\": \"foo\",\n"