diff options
Diffstat (limited to 'test/unit/test_operation.cpp')
| -rw-r--r-- | test/unit/test_operation.cpp | 6271 |
1 files changed, 6271 insertions, 0 deletions
diff --git a/test/unit/test_operation.cpp b/test/unit/test_operation.cpp new file mode 100644 index 00000000..2d2688a8 --- /dev/null +++ b/test/unit/test_operation.cpp @@ -0,0 +1,6271 @@ +/****************************************************************************** + * + * Project: PROJ + * Purpose: Test ISO19111:2018 implementation + * Author: Even Rouault <even dot rouault at spatialys dot com> + * + ****************************************************************************** + * Copyright (c) 2018, Even Rouault <even dot rouault at spatialys dot com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gtest_include.h" + +// to be able to use internal::replaceAll +#define FROM_PROJ_CPP + +#include "proj/common.hpp" +#include "proj/coordinateoperation.hpp" +#include "proj/coordinatesystem.hpp" +#include "proj/crs.hpp" +#include "proj/datum.hpp" +#include "proj/io.hpp" +#include "proj/metadata.hpp" +#include "proj/util.hpp" + +#include "proj/internal/internal.hpp" + +#include "proj_constants.h" + +#include <string> +#include <vector> + +using namespace osgeo::proj::common; +using namespace osgeo::proj::crs; +using namespace osgeo::proj::cs; +using namespace osgeo::proj::datum; +using namespace osgeo::proj::io; +using namespace osgeo::proj::internal; +using namespace osgeo::proj::metadata; +using namespace osgeo::proj::operation; +using namespace osgeo::proj::util; + +namespace { +struct UnrelatedObject : public IComparable { + UnrelatedObject() = default; + + bool _isEquivalentTo(const IComparable *, Criterion) const override { + assert(false); + return false; + } +}; + +static nn<std::shared_ptr<UnrelatedObject>> createUnrelatedObject() { + return nn_make_shared<UnrelatedObject>(); +} +} // namespace + +// --------------------------------------------------------------------------- + +TEST(operation, method) { + + auto method = OperationMethod::create( + PropertyMap(), std::vector<OperationParameterNNPtr>{}); + EXPECT_TRUE(method->isEquivalentTo(method.get())); + EXPECT_FALSE(method->isEquivalentTo(createUnrelatedObject().get())); + auto otherMethod = OperationMethod::create( + PropertyMap(), + std::vector<OperationParameterNNPtr>{OperationParameter::create( + PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName"))}); + EXPECT_TRUE(otherMethod->isEquivalentTo(otherMethod.get())); + EXPECT_FALSE(method->isEquivalentTo(otherMethod.get())); + auto otherMethod2 = OperationMethod::create( + PropertyMap(), + std::vector<OperationParameterNNPtr>{OperationParameter::create( + PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName2"))}); + EXPECT_FALSE(otherMethod->isEquivalentTo(otherMethod2.get())); + EXPECT_FALSE(otherMethod->isEquivalentTo( + otherMethod2.get(), IComparable::Criterion::EQUIVALENT)); +} + +// --------------------------------------------------------------------------- + +TEST(operation, method_parameter_different_order) { + + auto method1 = OperationMethod::create( + PropertyMap(), std::vector<OperationParameterNNPtr>{ + OperationParameter::create(PropertyMap().set( + IdentifiedObject::NAME_KEY, "paramName")), + OperationParameter::create(PropertyMap().set( + IdentifiedObject::NAME_KEY, "paramName2"))}); + + auto method2 = OperationMethod::create( + PropertyMap(), std::vector<OperationParameterNNPtr>{ + OperationParameter::create(PropertyMap().set( + IdentifiedObject::NAME_KEY, "paramName2")), + OperationParameter::create(PropertyMap().set( + IdentifiedObject::NAME_KEY, "paramName"))}); + + auto method3 = OperationMethod::create( + PropertyMap(), std::vector<OperationParameterNNPtr>{ + OperationParameter::create(PropertyMap().set( + IdentifiedObject::NAME_KEY, "paramName3")), + OperationParameter::create(PropertyMap().set( + IdentifiedObject::NAME_KEY, "paramName"))}); + + EXPECT_FALSE(method1->isEquivalentTo(method2.get())); + EXPECT_TRUE(method1->isEquivalentTo(method2.get(), + IComparable::Criterion::EQUIVALENT)); + EXPECT_FALSE(method1->isEquivalentTo(method3.get(), + IComparable::Criterion::EQUIVALENT)); +} + +// --------------------------------------------------------------------------- + +TEST(operation, ParameterValue) { + + auto valStr1 = ParameterValue::create("str1"); + auto valStr2 = ParameterValue::create("str2"); + EXPECT_TRUE(valStr1->isEquivalentTo(valStr1.get())); + EXPECT_FALSE(valStr1->isEquivalentTo(createUnrelatedObject().get())); + EXPECT_FALSE(valStr1->isEquivalentTo(valStr2.get())); + + auto valMeasure1 = ParameterValue::create(Angle(-90.0)); + auto valMeasure1Eps = ParameterValue::create(Angle(-90.0 - 1e-11)); + auto valMeasure2 = ParameterValue::create(Angle(-89.0)); + EXPECT_TRUE(valMeasure1->isEquivalentTo(valMeasure1.get())); + EXPECT_TRUE(valMeasure1->isEquivalentTo( + valMeasure1.get(), IComparable::Criterion::EQUIVALENT)); + EXPECT_FALSE(valMeasure1->isEquivalentTo(valMeasure1Eps.get())); + EXPECT_TRUE(valMeasure1->isEquivalentTo( + valMeasure1Eps.get(), IComparable::Criterion::EQUIVALENT)); + + EXPECT_FALSE(valMeasure1->isEquivalentTo(valStr1.get())); + EXPECT_FALSE(valMeasure1->isEquivalentTo(valMeasure2.get())); + EXPECT_FALSE(valMeasure1->isEquivalentTo( + valMeasure2.get(), IComparable::Criterion::EQUIVALENT)); + + auto valInt1 = ParameterValue::create(1); + auto valInt2 = ParameterValue::create(2); + EXPECT_TRUE(valInt1->isEquivalentTo(valInt1.get())); + EXPECT_FALSE(valInt1->isEquivalentTo(valInt2.get())); + + auto valTrue = ParameterValue::create(true); + auto valFalse = ParameterValue::create(false); + EXPECT_TRUE(valTrue->isEquivalentTo(valTrue.get())); + EXPECT_FALSE(valTrue->isEquivalentTo(valFalse.get())); +} + +// --------------------------------------------------------------------------- + +TEST(operation, OperationParameter) { + + auto op1 = OperationParameter::create( + PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName")); + auto op2 = OperationParameter::create( + PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName2")); + EXPECT_TRUE(op1->isEquivalentTo(op1.get())); + EXPECT_FALSE(op1->isEquivalentTo(createUnrelatedObject().get())); + EXPECT_FALSE(op1->isEquivalentTo(op2.get())); +} + +// --------------------------------------------------------------------------- + +TEST(operation, OperationParameterValue) { + + auto op1 = OperationParameter::create( + PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName")); + auto op2 = OperationParameter::create( + PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName2")); + auto valStr1 = ParameterValue::create("str1"); + auto valStr2 = ParameterValue::create("str2"); + auto opv11 = OperationParameterValue::create(op1, valStr1); + EXPECT_TRUE(opv11->isEquivalentTo(opv11.get())); + EXPECT_FALSE(opv11->isEquivalentTo(createUnrelatedObject().get())); + auto opv12 = OperationParameterValue::create(op1, valStr2); + EXPECT_FALSE(opv11->isEquivalentTo(opv12.get())); + auto opv21 = OperationParameterValue::create(op2, valStr1); + EXPECT_FALSE(opv11->isEquivalentTo(opv12.get())); +} + +// --------------------------------------------------------------------------- + +TEST(operation, SingleOperation) { + + auto sop1 = Transformation::create( + PropertyMap(), nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4326), + nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4807), + static_cast<CRSPtr>(GeographicCRS::EPSG_4979.as_nullable()), + PropertyMap(), + std::vector<OperationParameterNNPtr>{OperationParameter::create( + PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName"))}, + std::vector<ParameterValueNNPtr>{ + ParameterValue::createFilename("foo.bin")}, + std::vector<PositionalAccuracyNNPtr>{ + PositionalAccuracy::create("0.1")}); + + EXPECT_TRUE(sop1->isEquivalentTo(sop1.get())); + EXPECT_FALSE(sop1->isEquivalentTo(createUnrelatedObject().get())); + + auto sop2 = Transformation::create( + PropertyMap(), nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4326), + nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4807), + static_cast<CRSPtr>(GeographicCRS::EPSG_4979.as_nullable()), + PropertyMap(), + std::vector<OperationParameterNNPtr>{OperationParameter::create( + PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName2"))}, + std::vector<ParameterValueNNPtr>{ + ParameterValue::createFilename("foo.bin")}, + std::vector<PositionalAccuracyNNPtr>{ + PositionalAccuracy::create("0.1")}); + EXPECT_FALSE(sop1->isEquivalentTo(sop2.get())); + + auto sop3 = Transformation::create( + PropertyMap(), nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4326), + nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4807), + static_cast<CRSPtr>(GeographicCRS::EPSG_4979.as_nullable()), + PropertyMap(), + std::vector<OperationParameterNNPtr>{ + OperationParameter::create( + PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName")), + OperationParameter::create( + PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName2"))}, + std::vector<ParameterValueNNPtr>{ + ParameterValue::createFilename("foo.bin"), + ParameterValue::createFilename("foo2.bin")}, + std::vector<PositionalAccuracyNNPtr>{ + PositionalAccuracy::create("0.1")}); + EXPECT_FALSE(sop1->isEquivalentTo(sop3.get())); + + auto sop4 = Transformation::create( + PropertyMap(), nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4326), + nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4807), + static_cast<CRSPtr>(GeographicCRS::EPSG_4979.as_nullable()), + PropertyMap(), + std::vector<OperationParameterNNPtr>{OperationParameter::create( + PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName"))}, + std::vector<ParameterValueNNPtr>{ + ParameterValue::createFilename("foo2.bin")}, + std::vector<PositionalAccuracyNNPtr>{ + PositionalAccuracy::create("0.1")}); + EXPECT_FALSE(sop1->isEquivalentTo(sop4.get())); +} + +// --------------------------------------------------------------------------- + +TEST(operation, SingleOperation_different_order) { + + auto sop1 = Transformation::create( + PropertyMap().set(IdentifiedObject::NAME_KEY, "ignored1"), + GeographicCRS::EPSG_4326, GeographicCRS::EPSG_4807, nullptr, + PropertyMap(), + std::vector<OperationParameterNNPtr>{ + OperationParameter::create( + PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName")), + OperationParameter::create( + PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName2"))}, + std::vector<ParameterValueNNPtr>{ + ParameterValue::createFilename("foo.bin"), + ParameterValue::createFilename("foo2.bin")}, + {}); + + auto sop2 = Transformation::create( + PropertyMap().set(IdentifiedObject::NAME_KEY, "ignored2"), + GeographicCRS::EPSG_4326, GeographicCRS::EPSG_4807, nullptr, + PropertyMap(), + std::vector<OperationParameterNNPtr>{ + OperationParameter::create( + PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName2")), + OperationParameter::create( + PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName"))}, + std::vector<ParameterValueNNPtr>{ + ParameterValue::createFilename("foo2.bin"), + ParameterValue::createFilename("foo.bin")}, + {}); + + auto sop3 = Transformation::create( + PropertyMap().set(IdentifiedObject::NAME_KEY, "ignored3"), + GeographicCRS::EPSG_4326, GeographicCRS::EPSG_4807, nullptr, + PropertyMap(), + std::vector<OperationParameterNNPtr>{ + OperationParameter::create( + PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName")), + OperationParameter::create( + PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName2"))}, + std::vector<ParameterValueNNPtr>{ + ParameterValue::createFilename("foo2.bin"), + ParameterValue::createFilename("foo.bin")}, + {}); + + EXPECT_FALSE(sop1->isEquivalentTo(sop2.get())); + EXPECT_TRUE( + sop1->isEquivalentTo(sop2.get(), IComparable::Criterion::EQUIVALENT)); + EXPECT_FALSE( + sop1->isEquivalentTo(sop3.get(), IComparable::Criterion::EQUIVALENT)); +} + +// --------------------------------------------------------------------------- + +TEST(operation, transformation_to_wkt) { + PropertyMap propertiesTransformation; + propertiesTransformation + .set(Identifier::CODESPACE_KEY, "codeSpaceTransformation") + .set(Identifier::CODE_KEY, "codeTransformation") + .set(IdentifiedObject::NAME_KEY, "transformationName") + .set(IdentifiedObject::REMARKS_KEY, "my remarks"); + + auto transf = Transformation::create( + propertiesTransformation, + nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4326), + nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4807), + static_cast<CRSPtr>(GeographicCRS::EPSG_4979.as_nullable()), + PropertyMap() + .set(Identifier::CODESPACE_KEY, "codeSpaceOperationMethod") + .set(Identifier::CODE_KEY, "codeOperationMethod") + .set(IdentifiedObject::NAME_KEY, "operationMethodName"), + std::vector<OperationParameterNNPtr>{OperationParameter::create( + PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName"))}, + std::vector<ParameterValueNNPtr>{ + ParameterValue::createFilename("foo.bin")}, + std::vector<PositionalAccuracyNNPtr>{ + PositionalAccuracy::create("0.1")}); + + std::string src_wkt; + { + auto formatter = WKTFormatter::create(); + formatter->setOutputId(false); + src_wkt = GeographicCRS::EPSG_4326->exportToWKT(formatter.get()); + } + + std::string dst_wkt; + { + auto formatter = WKTFormatter::create(); + formatter->setOutputId(false); + dst_wkt = GeographicCRS::EPSG_4807->exportToWKT(formatter.get()); + } + + std::string interpolation_wkt; + { + auto formatter = WKTFormatter::create(); + formatter->setOutputId(false); + interpolation_wkt = + GeographicCRS::EPSG_4979->exportToWKT(formatter.get()); + } + + auto expected = + "COORDINATEOPERATION[\"transformationName\",\n" + " SOURCECRS[" + + src_wkt + "],\n" + " TARGETCRS[" + + dst_wkt + + "],\n" + " METHOD[\"operationMethodName\",\n" + " ID[\"codeSpaceOperationMethod\",\"codeOperationMethod\"]],\n" + " PARAMETERFILE[\"paramName\",\"foo.bin\"],\n" + " INTERPOLATIONCRS[" + + interpolation_wkt + + "],\n" + " OPERATIONACCURACY[0.1],\n" + " ID[\"codeSpaceTransformation\",\"codeTransformation\"],\n" + " REMARK[\"my remarks\"]]"; + + EXPECT_EQ( + replaceAll(replaceAll(transf->exportToWKT(WKTFormatter::create().get()), + " ", ""), + "\n", ""), + replaceAll(replaceAll(expected, " ", ""), "\n", "")); + + EXPECT_THROW( + transf->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + FormattingException); + + EXPECT_TRUE(transf->isEquivalentTo(transf.get())); + EXPECT_FALSE(transf->isEquivalentTo(createUnrelatedObject().get())); +} + +// --------------------------------------------------------------------------- + +TEST(operation, concatenated_operation) { + + PropertyMap propertiesTransformation; + propertiesTransformation + .set(Identifier::CODESPACE_KEY, "codeSpaceTransformation") + .set(Identifier::CODE_KEY, "codeTransformation") + .set(IdentifiedObject::NAME_KEY, "transformationName") + .set(IdentifiedObject::REMARKS_KEY, "my remarks"); + + auto transf_1 = Transformation::create( + propertiesTransformation, + nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4326), + nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4807), nullptr, + PropertyMap().set(IdentifiedObject::NAME_KEY, "operationMethodName"), + std::vector<OperationParameterNNPtr>{OperationParameter::create( + PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName"))}, + std::vector<ParameterValueNNPtr>{ + ParameterValue::createFilename("foo.bin")}, + std::vector<PositionalAccuracyNNPtr>()); + + auto transf_2 = Transformation::create( + propertiesTransformation, + nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4807), + nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4979), nullptr, + PropertyMap().set(IdentifiedObject::NAME_KEY, "operationMethodName"), + std::vector<OperationParameterNNPtr>{OperationParameter::create( + PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName"))}, + std::vector<ParameterValueNNPtr>{ + ParameterValue::createFilename("foo.bin")}, + std::vector<PositionalAccuracyNNPtr>()); + + auto concat = ConcatenatedOperation::create( + PropertyMap() + .set(Identifier::CODESPACE_KEY, "codeSpace") + .set(Identifier::CODE_KEY, "code") + .set(IdentifiedObject::NAME_KEY, "name") + .set(IdentifiedObject::REMARKS_KEY, "my remarks"), + std::vector<CoordinateOperationNNPtr>{transf_1, transf_2}, + std::vector<PositionalAccuracyNNPtr>{ + PositionalAccuracy::create("0.1")}); + + std::string src_wkt; + { + auto formatter = + WKTFormatter::create(WKTFormatter::Convention::WKT2_2018); + formatter->setOutputId(false); + src_wkt = GeographicCRS::EPSG_4326->exportToWKT(formatter.get()); + } + + std::string dst_wkt; + { + auto formatter = + WKTFormatter::create(WKTFormatter::Convention::WKT2_2018); + formatter->setOutputId(false); + dst_wkt = GeographicCRS::EPSG_4979->exportToWKT(formatter.get()); + } + + std::string step1_wkt; + { + auto formatter = + WKTFormatter::create(WKTFormatter::Convention::WKT2_2018); + formatter->setOutputId(false); + step1_wkt = transf_1->exportToWKT(formatter.get()); + } + + std::string step2_wkt; + { + auto formatter = + WKTFormatter::create(WKTFormatter::Convention::WKT2_2018); + formatter->setOutputId(false); + step2_wkt = transf_2->exportToWKT(formatter.get()); + } + + auto expected = "CONCATENATEDOPERATION[\"name\",\n" + " SOURCECRS[" + + src_wkt + "],\n" + " TARGETCRS[" + + dst_wkt + "],\n" + " STEP[" + + step1_wkt + "],\n" + " STEP[" + + step2_wkt + "],\n" + " ID[\"codeSpace\",\"code\"],\n" + " REMARK[\"my remarks\"]]"; + + EXPECT_EQ(replaceAll(replaceAll(concat->exportToWKT( + WKTFormatter::create( + WKTFormatter::Convention::WKT2_2018) + .get()), + " ", ""), + "\n", ""), + replaceAll(replaceAll(expected, " ", ""), "\n", "")); + + EXPECT_THROW(concat->exportToWKT(WKTFormatter::create().get()), + FormattingException); + + EXPECT_THROW(ConcatenatedOperation::create( + PropertyMap().set(IdentifiedObject::NAME_KEY, "name"), + std::vector<CoordinateOperationNNPtr>{transf_1, transf_1}, + std::vector<PositionalAccuracyNNPtr>()), + InvalidOperation); + + auto inv = concat->inverse(); + EXPECT_EQ(inv->nameStr(), "Inverse of name"); + EXPECT_EQ(inv->sourceCRS()->nameStr(), concat->targetCRS()->nameStr()); + EXPECT_EQ(inv->targetCRS()->nameStr(), concat->sourceCRS()->nameStr()); + auto inv_as_concat = nn_dynamic_pointer_cast<ConcatenatedOperation>(inv); + ASSERT_TRUE(inv_as_concat != nullptr); + + ASSERT_EQ(inv_as_concat->operations().size(), 2); + EXPECT_EQ(inv_as_concat->operations()[0]->nameStr(), + "Inverse of transformationName"); + EXPECT_EQ(inv_as_concat->operations()[1]->nameStr(), + "Inverse of transformationName"); + + EXPECT_TRUE(concat->isEquivalentTo(concat.get())); + EXPECT_FALSE(concat->isEquivalentTo(createUnrelatedObject().get())); + EXPECT_FALSE( + ConcatenatedOperation::create(PropertyMap(), + std::vector<CoordinateOperationNNPtr>{ + transf_1, transf_1->inverse()}, + std::vector<PositionalAccuracyNNPtr>()) + ->isEquivalentTo(ConcatenatedOperation::create( + PropertyMap(), + std::vector<CoordinateOperationNNPtr>{ + transf_1->inverse(), transf_1}, + std::vector<PositionalAccuracyNNPtr>()) + .get())); + EXPECT_FALSE( + ConcatenatedOperation::create(PropertyMap(), + std::vector<CoordinateOperationNNPtr>{ + transf_1, transf_1->inverse()}, + std::vector<PositionalAccuracyNNPtr>()) + ->isEquivalentTo(ConcatenatedOperation::create( + PropertyMap(), + std::vector<CoordinateOperationNNPtr>{ + transf_1, transf_1->inverse(), transf_1}, + std::vector<PositionalAccuracyNNPtr>()) + .get())); +} + +// --------------------------------------------------------------------------- + +TEST(operation, transformation_createGeocentricTranslations) { + + auto transf = Transformation::createGeocentricTranslations( + PropertyMap(), GeographicCRS::EPSG_4269, GeographicCRS::EPSG_4326, 1.0, + 2.0, 3.0, std::vector<PositionalAccuracyNNPtr>()); + + auto params = transf->getTOWGS84Parameters(); + auto expected = std::vector<double>{1.0, 2.0, 3.0, 0.0, 0.0, 0.0, 0.0}; + EXPECT_EQ(params, expected); + + auto inv_transf = transf->inverse(); + auto inv_transf_as_transf = + nn_dynamic_pointer_cast<Transformation>(inv_transf); + ASSERT_TRUE(inv_transf_as_transf != nullptr); + + EXPECT_EQ(transf->sourceCRS()->nameStr(), + inv_transf_as_transf->targetCRS()->nameStr()); + EXPECT_EQ(transf->targetCRS()->nameStr(), + inv_transf_as_transf->sourceCRS()->nameStr()); + auto expected_inv = + std::vector<double>{-1.0, -2.0, -3.0, 0.0, 0.0, 0.0, 0.0}; + EXPECT_EQ(inv_transf_as_transf->getTOWGS84Parameters(), expected_inv); + + EXPECT_EQ(transf->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=cart " + "+ellps=GRS80 +step +proj=helmert +x=1 +y=2 +z=3 +step +inv " + "+proj=cart +ellps=WGS84 +step +proj=unitconvert +xy_in=rad " + "+xy_out=deg +step +proj=axisswap +order=2,1"); +} + +// --------------------------------------------------------------------------- + +static GeodeticCRSNNPtr createGeocentricDatumWGS84() { + PropertyMap propertiesCRS; + propertiesCRS.set(Identifier::CODESPACE_KEY, "EPSG") + .set(Identifier::CODE_KEY, 4328) + .set(IdentifiedObject::NAME_KEY, "WGS 84"); + return GeodeticCRS::create( + propertiesCRS, GeodeticReferenceFrame::EPSG_6326, + CartesianCS::createGeocentric(UnitOfMeasure::METRE)); +} + +// --------------------------------------------------------------------------- + +static GeodeticCRSNNPtr createGeocentricKM() { + PropertyMap propertiesCRS; + propertiesCRS.set(Identifier::CODESPACE_KEY, "EPSG") + .set(Identifier::CODE_KEY, 4328) + .set(IdentifiedObject::NAME_KEY, "WGS 84"); + return GeodeticCRS::create( + propertiesCRS, GeodeticReferenceFrame::EPSG_6326, + CartesianCS::createGeocentric( + UnitOfMeasure("kilometre", 1000.0, UnitOfMeasure::Type::LINEAR))); +} + +// --------------------------------------------------------------------------- + +TEST(operation, + transformation_createGeocentricTranslations_between_geocentricCRS) { + + auto transf1 = Transformation::createGeocentricTranslations( + PropertyMap(), createGeocentricDatumWGS84(), createGeocentricKM(), 1.0, + 2.0, 3.0, std::vector<PositionalAccuracyNNPtr>()); + + EXPECT_EQ(transf1->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=helmert +x=1 +y=2 +z=3 +step " + "+proj=unitconvert +xy_in=m +z_in=m +xy_out=km +z_out=km"); + + auto transf2 = Transformation::createGeocentricTranslations( + PropertyMap(), createGeocentricKM(), createGeocentricDatumWGS84(), 1.0, + 2.0, 3.0, std::vector<PositionalAccuracyNNPtr>()); + + EXPECT_EQ(transf2->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=unitconvert +xy_in=km +z_in=km " + "+xy_out=m +z_out=m +step +proj=helmert +x=1 +y=2 +z=3"); + + auto transf3 = Transformation::createGeocentricTranslations( + PropertyMap(), createGeocentricKM(), createGeocentricKM(), 1.0, 2.0, + 3.0, std::vector<PositionalAccuracyNNPtr>()); + + EXPECT_EQ(transf3->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=unitconvert +xy_in=km +z_in=km " + "+xy_out=m +z_out=m +step +proj=helmert +x=1 +y=2 +z=3 +step " + "+proj=unitconvert +xy_in=m +z_in=m +xy_out=km +z_out=km"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, transformation_createGeocentricTranslations_null) { + + auto transf = Transformation::createGeocentricTranslations( + PropertyMap(), createGeocentricDatumWGS84(), + createGeocentricDatumWGS84(), 0.0, 0.0, 0.0, + std::vector<PositionalAccuracyNNPtr>()); + + EXPECT_EQ(transf->inverse()->exportToPROJString( + PROJStringFormatter::create().get()), + ""); +} + +// --------------------------------------------------------------------------- + +TEST(operation, transformation_createGeocentricTranslations_neg_zero) { + + auto transf = Transformation::createGeocentricTranslations( + PropertyMap(), createGeocentricDatumWGS84(), + createGeocentricDatumWGS84(), 1.0, -0.0, 0.0, + std::vector<PositionalAccuracyNNPtr>()); + + EXPECT_EQ(transf->inverse()->exportToPROJString( + PROJStringFormatter::create().get()), + "+proj=helmert +x=-1 +y=0 +z=0"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, transformation_createPositionVector) { + + auto transf = Transformation::createPositionVector( + PropertyMap(), GeographicCRS::EPSG_4269, GeographicCRS::EPSG_4326, 1.0, + 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, std::vector<PositionalAccuracyNNPtr>{ + PositionalAccuracy::create("100")}); + ASSERT_EQ(transf->coordinateOperationAccuracies().size(), 1); + + auto expected = std::vector<double>{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0}; + EXPECT_EQ(transf->getTOWGS84Parameters(), expected); + + EXPECT_EQ(transf->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=cart " + "+ellps=GRS80 +step +proj=helmert +x=1 +y=2 +z=3 +rx=4 +ry=5 " + "+rz=6 +s=7 +convention=position_vector +step +inv +proj=cart " + "+ellps=WGS84 +step " + "+proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap " + "+order=2,1"); + + auto inv_transf = transf->inverse(); + ASSERT_EQ(inv_transf->coordinateOperationAccuracies().size(), 1); + + EXPECT_EQ(transf->sourceCRS()->nameStr(), + inv_transf->targetCRS()->nameStr()); + EXPECT_EQ(transf->targetCRS()->nameStr(), + inv_transf->sourceCRS()->nameStr()); + +#ifdef USE_APPROXIMATE_HELMERT_INVERSE + auto inv_transf_as_transf = + nn_dynamic_pointer_cast<Transformation>(inv_transf); + ASSERT_TRUE(inv_transf_as_transf != nullptr); +#else + EXPECT_EQ( + inv_transf->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=cart " + "+ellps=WGS84 +step +inv +proj=helmert +x=1 +y=2 +z=3 +rx=4 " + "+ry=5 +rz=6 +s=7 +convention=position_vector +step +inv " + "+proj=cart +ellps=GRS80 +step +proj=unitconvert +xy_in=rad " + "+xy_out=deg +step +proj=axisswap +order=2,1"); + + // In WKT, use approximate formula + auto wkt = inv_transf->exportToWKT(WKTFormatter::create().get()); + EXPECT_TRUE( + wkt.find("Transformation from WGS 84 to NAD83 (approx. inversion)") != + std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("Position Vector transformation (geog2D domain)") != + std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("ID[\"EPSG\",9606]]") != std::string::npos) << wkt; + EXPECT_TRUE(wkt.find("\"X-axis translation\",-1") != std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("\"Y-axis translation\",-2") != std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("\"Z-axis translation\",-3") != std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("\"X-axis rotation\",-4") != std::string::npos) << wkt; + EXPECT_TRUE(wkt.find("\"Y-axis rotation\",-5") != std::string::npos) << wkt; + EXPECT_TRUE(wkt.find("\"Z-axis rotation\",-6") != std::string::npos) << wkt; + EXPECT_TRUE(wkt.find("\"Scale difference\",-7") != std::string::npos) + << wkt; +#endif +} + +// --------------------------------------------------------------------------- + +TEST(operation, transformation_createCoordinateFrameRotation) { + + auto transf = Transformation::createCoordinateFrameRotation( + PropertyMap(), GeographicCRS::EPSG_4269, GeographicCRS::EPSG_4326, 1.0, + 2.0, 3.0, -4.0, -5.0, -6.0, 7.0, + std::vector<PositionalAccuracyNNPtr>()); + + auto params = transf->getTOWGS84Parameters(); + auto expected = std::vector<double>{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0}; + EXPECT_EQ(params, expected); + + EXPECT_EQ(transf->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=cart " + "+ellps=GRS80 +step +proj=helmert +x=1 +y=2 +z=3 +rx=-4 +ry=-5 " + "+rz=-6 +s=7 +convention=coordinate_frame +step +inv +proj=cart " + "+ellps=WGS84 +step " + "+proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap " + "+order=2,1"); + + auto inv_transf = transf->inverse(); + ASSERT_EQ(inv_transf->coordinateOperationAccuracies().size(), 0); + + EXPECT_EQ(transf->sourceCRS()->nameStr(), + inv_transf->targetCRS()->nameStr()); + EXPECT_EQ(transf->targetCRS()->nameStr(), + inv_transf->sourceCRS()->nameStr()); + +#ifdef USE_APPROXIMATE_HELMERT_INVERSE + auto inv_transf_as_transf = + nn_dynamic_pointer_cast<Transformation>(inv_transf); + ASSERT_TRUE(inv_transf_as_transf != nullptr); +#else + EXPECT_EQ( + inv_transf->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=cart " + "+ellps=WGS84 +step +inv +proj=helmert +x=1 +y=2 +z=3 +rx=-4 " + "+ry=-5 +rz=-6 +s=7 +convention=coordinate_frame +step +inv " + "+proj=cart +ellps=GRS80 +step +proj=unitconvert +xy_in=rad " + "+xy_out=deg +step +proj=axisswap +order=2,1"); + + // In WKT, use approximate formula + auto wkt = inv_transf->exportToWKT(WKTFormatter::create().get()); + EXPECT_TRUE( + wkt.find("Transformation from WGS 84 to NAD83 (approx. inversion)") != + std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("Coordinate Frame rotation (geog2D domain)") != + std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("ID[\"EPSG\",9607]]") != std::string::npos) << wkt; + EXPECT_TRUE(wkt.find("\"X-axis translation\",-1") != std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("\"Y-axis translation\",-2") != std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("\"Z-axis translation\",-3") != std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("\"X-axis rotation\",4") != std::string::npos) << wkt; + EXPECT_TRUE(wkt.find("\"Y-axis rotation\",5") != std::string::npos) << wkt; + EXPECT_TRUE(wkt.find("\"Z-axis rotation\",6") != std::string::npos) << wkt; + EXPECT_TRUE(wkt.find("\"Scale difference\",-7") != std::string::npos) + << wkt; +#endif +} + +// --------------------------------------------------------------------------- + +TEST(operation, transformation_createTimeDependentPositionVector) { + + auto transf = Transformation::createTimeDependentPositionVector( + PropertyMap(), GeographicCRS::EPSG_4269, GeographicCRS::EPSG_4326, 1.0, + 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 2018.5, + std::vector<PositionalAccuracyNNPtr>()); + + auto inv_transf = transf->inverse(); + + EXPECT_EQ(transf->sourceCRS()->nameStr(), + inv_transf->targetCRS()->nameStr()); + EXPECT_EQ(transf->targetCRS()->nameStr(), + inv_transf->sourceCRS()->nameStr()); + + auto projString = + inv_transf->exportToPROJString(PROJStringFormatter::create().get()); + EXPECT_TRUE(projString.find("+proj=helmert +x=1 +y=2 +z=3 +rx=4 +ry=5 " + "+rz=6 +s=7 +dx=0.1 +dy=0.2 +dz=0.3 +drx=0.4 " + "+dry=0.5 +drz=0.6 +ds=0.7 +t_epoch=2018.5 " + "+convention=position_vector") != + std::string::npos) + << projString; + + // In WKT, use approximate formula + auto wkt = inv_transf->exportToWKT(WKTFormatter::create().get()); + EXPECT_TRUE( + wkt.find("Transformation from WGS 84 to NAD83 (approx. inversion)") != + std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("Time-dependent Position Vector tfm (geog2D)") != + std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("ID[\"EPSG\",1054]]") != std::string::npos) << wkt; + EXPECT_TRUE(wkt.find("\"X-axis translation\",-1") != std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("\"Y-axis translation\",-2") != std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("\"Z-axis translation\",-3") != std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("\"X-axis rotation\",-4") != std::string::npos) << wkt; + EXPECT_TRUE(wkt.find("\"Y-axis rotation\",-5") != std::string::npos) << wkt; + EXPECT_TRUE(wkt.find("\"Z-axis rotation\",-6") != std::string::npos) << wkt; + EXPECT_TRUE(wkt.find("\"Scale difference\",-7") != std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("\"Rate of change of X-axis translation\",-0.1") != + std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("\"Rate of change of Y-axis translation\",-0.2") != + std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("\"Rate of change of Z-axis translation\",-0.3") != + std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("\"Rate of change of X-axis rotation\",-0.4") != + std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("\"Rate of change of Y-axis rotation\",-0.5") != + std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("\"Rate of change of Z-axis rotation\",-0.6") != + std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("\"Rate of change of Scale difference\",-0.7") != + std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("\"Parameter reference epoch\",2018.5") != + std::string::npos) + << wkt; +} + +// --------------------------------------------------------------------------- + +TEST(operation, transformation_createTimeDependentCoordinateFrameRotation) { + + auto transf = Transformation::createTimeDependentCoordinateFrameRotation( + PropertyMap(), GeographicCRS::EPSG_4269, GeographicCRS::EPSG_4326, 1.0, + 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 2018.5, + std::vector<PositionalAccuracyNNPtr>()); + + auto inv_transf = transf->inverse(); + + EXPECT_EQ(transf->sourceCRS()->nameStr(), + inv_transf->targetCRS()->nameStr()); + EXPECT_EQ(transf->targetCRS()->nameStr(), + inv_transf->sourceCRS()->nameStr()); + + auto projString = + inv_transf->exportToPROJString(PROJStringFormatter::create().get()); + EXPECT_TRUE(projString.find("+proj=helmert +x=1 +y=2 +z=3 +rx=4 +ry=5 " + "+rz=6 +s=7 +dx=0.1 +dy=0.2 +dz=0.3 +drx=0.4 " + "+dry=0.5 +drz=0.6 +ds=0.7 +t_epoch=2018.5 " + "+convention=coordinate_frame") != + std::string::npos) + << projString; + + // In WKT, use approximate formula + auto wkt = inv_transf->exportToWKT(WKTFormatter::create().get()); + EXPECT_TRUE( + wkt.find("Transformation from WGS 84 to NAD83 (approx. inversion)") != + std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("Time-dependent Coordinate Frame rotation (geog2D)") != + std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("ID[\"EPSG\",1057]]") != std::string::npos) << wkt; + EXPECT_TRUE(wkt.find("\"X-axis translation\",-1") != std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("\"Y-axis translation\",-2") != std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("\"Z-axis translation\",-3") != std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("\"X-axis rotation\",-4") != std::string::npos) << wkt; + EXPECT_TRUE(wkt.find("\"Y-axis rotation\",-5") != std::string::npos) << wkt; + EXPECT_TRUE(wkt.find("\"Z-axis rotation\",-6") != std::string::npos) << wkt; + EXPECT_TRUE(wkt.find("\"Scale difference\",-7") != std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("\"Rate of change of X-axis translation\",-0.1") != + std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("\"Rate of change of Y-axis translation\",-0.2") != + std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("\"Rate of change of Z-axis translation\",-0.3") != + std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("\"Rate of change of X-axis rotation\",-0.4") != + std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("\"Rate of change of Y-axis rotation\",-0.5") != + std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("\"Rate of change of Z-axis rotation\",-0.6") != + std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("\"Rate of change of Scale difference\",-0.7") != + std::string::npos) + << wkt; + EXPECT_TRUE(wkt.find("\"Parameter reference epoch\",2018.5") != + std::string::npos) + << wkt; +} + +// --------------------------------------------------------------------------- + +TEST(operation, transformation_successive_helmert_noop) { + + auto transf_1 = Transformation::createPositionVector( + PropertyMap(), GeographicCRS::EPSG_4326, GeographicCRS::EPSG_4269, 1.0, + 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, std::vector<PositionalAccuracyNNPtr>()); + auto transf_2 = Transformation::createPositionVector( + PropertyMap(), GeographicCRS::EPSG_4269, GeographicCRS::EPSG_4326, -1.0, + -2.0, -3.0, -4.0, -5.0, -6.0, -7.0, + std::vector<PositionalAccuracyNNPtr>()); + + auto concat = ConcatenatedOperation::create( + PropertyMap(), + std::vector<CoordinateOperationNNPtr>{transf_1, transf_2}, + std::vector<PositionalAccuracyNNPtr>{}); + + EXPECT_EQ(concat->exportToPROJString(PROJStringFormatter::create().get()), + ""); +} + +// --------------------------------------------------------------------------- + +TEST(operation, transformation_successive_helmert_non_trivial_1) { + + auto transf_1 = Transformation::createPositionVector( + PropertyMap(), GeographicCRS::EPSG_4326, GeographicCRS::EPSG_4269, 1.0, + 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, std::vector<PositionalAccuracyNNPtr>()); + auto transf_2 = Transformation::createPositionVector( + PropertyMap(), GeographicCRS::EPSG_4269, GeographicCRS::EPSG_4326, -1.0, + -2.0, -3.0, -4.0, -5.0, -6.0, 7.0, + std::vector<PositionalAccuracyNNPtr>()); + + auto concat = ConcatenatedOperation::create( + PropertyMap(), + std::vector<CoordinateOperationNNPtr>{transf_1, transf_2}, + std::vector<PositionalAccuracyNNPtr>{}); + + EXPECT_NE(concat->exportToPROJString(PROJStringFormatter::create().get()), + ""); +} + +// --------------------------------------------------------------------------- + +TEST(operation, transformation_successive_helmert_non_trivial_2) { + + auto transf_1 = Transformation::createPositionVector( + PropertyMap(), GeographicCRS::EPSG_4326, GeographicCRS::EPSG_4269, 1.0, + 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, std::vector<PositionalAccuracyNNPtr>()); + auto transf_2 = Transformation::createCoordinateFrameRotation( + PropertyMap(), GeographicCRS::EPSG_4269, GeographicCRS::EPSG_4326, -1.0, + -2.0, -3.0, -4.0, -5.0, -6.0, -7.0, + std::vector<PositionalAccuracyNNPtr>()); + + auto concat = ConcatenatedOperation::create( + PropertyMap(), + std::vector<CoordinateOperationNNPtr>{transf_1, transf_2}, + std::vector<PositionalAccuracyNNPtr>{}); + + EXPECT_NE(concat->exportToPROJString(PROJStringFormatter::create().get()), + ""); +} + +// --------------------------------------------------------------------------- + +TEST(operation, transformation_createMolodensky) { + + auto transf = Transformation::createMolodensky( + PropertyMap(), GeographicCRS::EPSG_4326, GeographicCRS::EPSG_4269, 1.0, + 2.0, 3.0, 4.0, 5.0, std::vector<PositionalAccuracyNNPtr>()); + + auto wkt = transf->exportToWKT(WKTFormatter::create().get()); + EXPECT_TRUE(replaceAll(replaceAll(wkt, " ", ""), "\n", "") + .find("METHOD[\"Molodensky\",ID[\"EPSG\",9604]]") != + std::string::npos) + << wkt; + + auto inv_transf = transf->inverse(); + auto inv_transf_as_transf = + nn_dynamic_pointer_cast<Transformation>(inv_transf); + ASSERT_TRUE(inv_transf_as_transf != nullptr); + + EXPECT_EQ(transf->sourceCRS()->nameStr(), + inv_transf_as_transf->targetCRS()->nameStr()); + EXPECT_EQ(transf->targetCRS()->nameStr(), + inv_transf_as_transf->sourceCRS()->nameStr()); + + auto projString = inv_transf_as_transf->exportToPROJString( + PROJStringFormatter::create().get()); + EXPECT_EQ(projString, "+proj=pipeline +step +proj=axisswap +order=2,1 " + "+step +proj=unitconvert +xy_in=deg +xy_out=rad " + "+step +proj=molodensky +ellps=GRS80 +dx=-1 +dy=-2 " + "+dz=-3 +da=-4 +df=-5 +step +proj=unitconvert " + "+xy_in=rad +xy_out=deg +step +proj=axisswap " + "+order=2,1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, transformation_createAbridgedMolodensky) { + + auto transf = Transformation::createAbridgedMolodensky( + PropertyMap(), GeographicCRS::EPSG_4326, GeographicCRS::EPSG_4269, 1.0, + 2.0, 3.0, 4.0, 5.0, std::vector<PositionalAccuracyNNPtr>()); + + auto wkt = transf->exportToWKT(WKTFormatter::create().get()); + EXPECT_TRUE(replaceAll(replaceAll(wkt, " ", ""), "\n", "") + .find(replaceAll( + "METHOD[\"Abridged Molodensky\",ID[\"EPSG\",9605]]", + " ", "")) != std::string::npos) + << wkt; + + auto inv_transf = transf->inverse(); + auto inv_transf_as_transf = + nn_dynamic_pointer_cast<Transformation>(inv_transf); + ASSERT_TRUE(inv_transf_as_transf != nullptr); + + EXPECT_EQ(transf->sourceCRS()->nameStr(), + inv_transf_as_transf->targetCRS()->nameStr()); + EXPECT_EQ(transf->targetCRS()->nameStr(), + inv_transf_as_transf->sourceCRS()->nameStr()); + + auto projString = inv_transf_as_transf->exportToPROJString( + PROJStringFormatter::create().get()); + EXPECT_EQ(projString, "+proj=pipeline +step +proj=axisswap +order=2,1 " + "+step +proj=unitconvert +xy_in=deg +xy_out=rad " + "+step +proj=molodensky +ellps=GRS80 +dx=-1 +dy=-2 " + "+dz=-3 +da=-4 +df=-5 +abridged +step " + "+proj=unitconvert +xy_in=rad +xy_out=deg +step " + "+proj=axisswap +order=2,1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, transformation_inverse) { + + auto transf = Transformation::create( + PropertyMap() + .set(IdentifiedObject::NAME_KEY, "my transformation") + .set(Identifier::CODESPACE_KEY, "my codeSpace") + .set(Identifier::CODE_KEY, "my code"), + GeographicCRS::EPSG_4326, GeographicCRS::EPSG_4269, nullptr, + PropertyMap() + .set(IdentifiedObject::NAME_KEY, "my operation") + .set(Identifier::CODESPACE_KEY, "my codeSpace") + .set(Identifier::CODE_KEY, "my code"), + std::vector<OperationParameterNNPtr>{OperationParameter::create( + PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName"))}, + std::vector<ParameterValueNNPtr>{ + ParameterValue::createFilename("foo.bin")}, + std::vector<PositionalAccuracyNNPtr>{ + PositionalAccuracy::create("0.1")}); + auto inv = transf->inverse(); + EXPECT_EQ(inv->inverse(), transf); + EXPECT_EQ( + inv->exportToWKT(WKTFormatter::create().get()), + "COORDINATEOPERATION[\"Inverse of my transformation\",\n" + " SOURCECRS[\n" + " GEODCRS[\"NAD83\",\n" + " DATUM[\"North American Datum 1983\",\n" + " ELLIPSOID[\"GRS 1980\",6378137,298.257222101,\n" + " LENGTHUNIT[\"metre\",1]]],\n" + " PRIMEM[\"Greenwich\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " CS[ellipsoidal,2],\n" + " AXIS[\"latitude\",north,\n" + " ORDER[1],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " AXIS[\"longitude\",east,\n" + " ORDER[2],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]]]],\n" + " TARGETCRS[\n" + " GEODCRS[\"WGS 84\",\n" + " DATUM[\"World Geodetic System 1984\",\n" + " ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n" + " LENGTHUNIT[\"metre\",1]]],\n" + " PRIMEM[\"Greenwich\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " CS[ellipsoidal,2],\n" + " AXIS[\"latitude\",north,\n" + " ORDER[1],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " AXIS[\"longitude\",east,\n" + " ORDER[2],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]]]],\n" + " METHOD[\"Inverse of my operation\",\n" + " ID[\"INVERSE(my codeSpace)\",\"my code\"]],\n" + " PARAMETERFILE[\"paramName\",\"foo.bin\"],\n" + " OPERATIONACCURACY[0.1],\n" + " ID[\"INVERSE(my codeSpace)\",\"my code\"]]"); + + EXPECT_THROW(inv->exportToPROJString(PROJStringFormatter::create().get()), + FormattingException); +} + +// --------------------------------------------------------------------------- + +TEST(operation, transformation_createTOWGS84) { + + EXPECT_THROW(Transformation::createTOWGS84(GeographicCRS::EPSG_4326, + std::vector<double>()), + InvalidOperation); + + auto crsIn = CompoundCRS::create(PropertyMap(), std::vector<CRSNNPtr>{}); + EXPECT_THROW( + Transformation::createTOWGS84(crsIn, std::vector<double>(7, 0)), + InvalidOperation); +} + +// --------------------------------------------------------------------------- + +TEST(operation, utm_export) { + auto conv = Conversion::createUTM(PropertyMap(), 1, false); + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=utm +zone=1 +south"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"UTM zone 1S\",\n" + " METHOD[\"Transverse Mercator\",\n" + " ID[\"EPSG\",9807]],\n" + " PARAMETER[\"Latitude of natural origin\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8801]],\n" + " PARAMETER[\"Longitude of natural origin\",-177,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"Scale factor at natural origin\",0.9996,\n" + " SCALEUNIT[\"unity\",1],\n" + " ID[\"EPSG\",8805]],\n" + " PARAMETER[\"False easting\",500000,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",10000000,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]],\n" + " ID[\"EPSG\",17001]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Transverse_Mercator\"],\n" + "PARAMETER[\"latitude_of_origin\",0],\n" + "PARAMETER[\"central_meridian\",-177],\n" + "PARAMETER[\"scale_factor\",0.9996],\n" + "PARAMETER[\"false_easting\",500000],\n" + "PARAMETER[\"false_northing\",10000000]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, tmerc_export) { + auto conv = Conversion::createTransverseMercator( + PropertyMap(), Angle(1), Angle(2), Scale(3), Length(4), Length(5)); + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=tmerc +lat_0=1 +lon_0=2 +k_0=3 +x_0=4 +y_0=5"); + + { + auto formatter = PROJStringFormatter::create(); + formatter->setUseETMercForTMerc(true); + EXPECT_EQ(conv->exportToPROJString(formatter.get()), + "+proj=etmerc +lat_0=1 +lon_0=2 +k_0=3 +x_0=4 +y_0=5"); + } + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Transverse Mercator\",\n" + " METHOD[\"Transverse Mercator\",\n" + " ID[\"EPSG\",9807]],\n" + " PARAMETER[\"Latitude of natural origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8801]],\n" + " PARAMETER[\"Longitude of natural origin\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"Scale factor at natural origin\",3,\n" + " SCALEUNIT[\"unity\",1],\n" + " ID[\"EPSG\",8805]],\n" + " PARAMETER[\"False easting\",4,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",5,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Transverse_Mercator\"],\n" + "PARAMETER[\"latitude_of_origin\",1],\n" + "PARAMETER[\"central_meridian\",2],\n" + "PARAMETER[\"scale_factor\",3],\n" + "PARAMETER[\"false_easting\",4],\n" + "PARAMETER[\"false_northing\",5]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, gstmerc_export) { + auto conv = Conversion::createGaussSchreiberTransverseMercator( + PropertyMap(), Angle(1), Angle(2), Scale(3), Length(4), Length(5)); + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=gstmerc +lat_0=1 +lon_0=2 +k_0=3 +x_0=4 +y_0=5"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Gauss Schreiber Transverse Mercator\",\n" + " METHOD[\"Gauss Schreiber Transverse Mercator\"],\n" + " PARAMETER[\"Latitude of natural origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8801]],\n" + " PARAMETER[\"Longitude of natural origin\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"Scale factor at natural origin\",3,\n" + " SCALEUNIT[\"unity\",1],\n" + " ID[\"EPSG\",8805]],\n" + " PARAMETER[\"False easting\",4,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",5,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Gauss_Schreiber_Transverse_Mercator\"],\n" + "PARAMETER[\"latitude_of_origin\",1],\n" + "PARAMETER[\"central_meridian\",2],\n" + "PARAMETER[\"scale_factor\",3],\n" + "PARAMETER[\"false_easting\",4],\n" + "PARAMETER[\"false_northing\",5]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, tmerc_south_oriented_export) { + auto conv = Conversion::createTransverseMercatorSouthOriented( + PropertyMap(), Angle(1), Angle(2), Scale(3), Length(4), Length(5)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=tmerc +axis=wsu +lat_0=1 +lon_0=2 +k_0=3 +x_0=4 +y_0=5"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Transverse Mercator (South Orientated)\",\n" + " METHOD[\"Transverse Mercator (South Orientated)\",\n" + " ID[\"EPSG\",9808]],\n" + " PARAMETER[\"Latitude of natural origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8801]],\n" + " PARAMETER[\"Longitude of natural origin\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"Scale factor at natural origin\",3,\n" + " SCALEUNIT[\"unity\",1],\n" + " ID[\"EPSG\",8805]],\n" + " PARAMETER[\"False easting\",4,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",5,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Transverse_Mercator_South_Orientated\"],\n" + "PARAMETER[\"latitude_of_origin\",1],\n" + "PARAMETER[\"central_meridian\",2],\n" + "PARAMETER[\"scale_factor\",3],\n" + "PARAMETER[\"false_easting\",4],\n" + "PARAMETER[\"false_northing\",5]"); + + auto wkt = "PROJCRS[\"Hartebeesthoek94 / Lo29\"," + " BASEGEODCRS[\"Hartebeesthoek94\"," + " DATUM[\"Hartebeesthoek94\"," + " ELLIPSOID[\"WGS " + "84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1.0]]]]," + " CONVERSION[\"South African Survey Grid zone 29\"," + " METHOD[\"Transverse Mercator (South " + "Orientated)\",ID[\"EPSG\",9808]]," + " PARAMETER[\"Latitude of natural " + "origin\",0,ANGLEUNIT[\"degree\",0.01745329252]]," + " PARAMETER[\"Longitude of natural " + "origin\",29,ANGLEUNIT[\"degree\",0.01745329252]]," + " PARAMETER[\"Scale factor at natural " + "origin\",1,SCALEUNIT[\"unity\",1.0]]," + " PARAMETER[\"False easting\",0,LENGTHUNIT[\"metre\",1.0]]," + " PARAMETER[\"False northing\",0,LENGTHUNIT[\"metre\",1.0]]]," + " CS[cartesian,2]," + " AXIS[\"westing (Y)\",west,ORDER[1]]," + " AXIS[\"southing (X)\",south,ORDER[2]]," + " LENGTHUNIT[\"metre\",1.0]," + " ID[\"EPSG\",2053]]"; + auto obj = WKTParser().createFromWKT(wkt); + auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + ASSERT_TRUE(crs != nullptr); + EXPECT_EQ(crs->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=tmerc " + "+axis=wsu +lat_0=0 +lon_0=29 +k_0=1 +x_0=0 +y_0=0 +ellps=WGS84"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, tped_export) { + auto conv = Conversion::createTwoPointEquidistant( + PropertyMap(), Angle(1), Angle(2), Angle(3), Angle(4), Length(5), + Length(6)); + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=tpeqd +lat_1=1 +lon_1=2 +lat_2=3 +lon_2=4 +x_0=5 +y_0=6"); + + auto formatter = WKTFormatter::create(); + formatter->simulCurNodeHasId(); + EXPECT_EQ(conv->exportToWKT(formatter.get()), + "CONVERSION[\"Two Point Equidistant\",\n" + " METHOD[\"Two Point Equidistant\"],\n" + " PARAMETER[\"Latitude of 1st point\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " PARAMETER[\"Longitude of 1st point\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " PARAMETER[\"Latitude of 2nd point\",3,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " PARAMETER[\"Longitude of 2nd point\",4,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " PARAMETER[\"False easting\",5,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",6,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Two_Point_Equidistant\"],\n" + "PARAMETER[\"Latitude_Of_1st_Point\",1],\n" + "PARAMETER[\"Longitude_Of_1st_Point\",2],\n" + "PARAMETER[\"Latitude_Of_2nd_Point\",3],\n" + "PARAMETER[\"Longitude_Of_2nd_Point\",4],\n" + "PARAMETER[\"false_easting\",5],\n" + "PARAMETER[\"false_northing\",6]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, tmg_export) { + auto conv = Conversion::createTunisiaMappingGrid( + PropertyMap(), Angle(1), Angle(2), Length(3), Length(4)); + EXPECT_THROW(conv->exportToPROJString(PROJStringFormatter::create().get()), + FormattingException); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Tunisia Mapping Grid\",\n" + " METHOD[\"Tunisia Mapping Grid\",\n" + " ID[\"EPSG\",9816]],\n" + " PARAMETER[\"Latitude of false origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8821]],\n" + " PARAMETER[\"Longitude of false origin\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8822]],\n" + " PARAMETER[\"Easting at false origin\",3,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8826]],\n" + " PARAMETER[\"Northing at false origin\",4,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8827]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Tunisia_Mapping_Grid\"],\n" + "PARAMETER[\"latitude_of_origin\",1],\n" + "PARAMETER[\"central_meridian\",2],\n" + "PARAMETER[\"false_easting\",3],\n" + "PARAMETER[\"false_northing\",4]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, aea_export) { + auto conv = Conversion::createAlbersEqualArea(PropertyMap(), Angle(1), + Angle(2), Angle(3), Angle(4), + Length(5), Length(6)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=aea +lat_0=1 +lon_0=2 +lat_1=3 +lat_2=4 +x_0=5 +y_0=6"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Albers Equal Area\",\n" + " METHOD[\"Albers Equal Area\",\n" + " ID[\"EPSG\",9822]],\n" + " PARAMETER[\"Latitude of false origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8821]],\n" + " PARAMETER[\"Longitude of false origin\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8822]],\n" + " PARAMETER[\"Latitude of 1st standard parallel\",3,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8823]],\n" + " PARAMETER[\"Latitude of 2nd standard parallel\",4,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8824]],\n" + " PARAMETER[\"Easting at false origin\",5,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8826]],\n" + " PARAMETER[\"Northing at false origin\",6,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8827]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Albers_Conic_Equal_Area\"],\n" + "PARAMETER[\"latitude_of_center\",1],\n" + "PARAMETER[\"longitude_of_center\",2],\n" + "PARAMETER[\"standard_parallel_1\",3],\n" + "PARAMETER[\"standard_parallel_2\",4],\n" + "PARAMETER[\"false_easting\",5],\n" + "PARAMETER[\"false_northing\",6]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, azimuthal_equidistant_export) { + auto conv = Conversion::createAzimuthalEquidistant( + PropertyMap(), Angle(1), Angle(2), Length(3), Length(4)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=aeqd +lat_0=1 +lon_0=2 +x_0=3 +y_0=4"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Modified Azimuthal Equidistant\",\n" + " METHOD[\"Modified Azimuthal Equidistant\",\n" + " ID[\"EPSG\",9832]],\n" + " PARAMETER[\"Latitude of natural origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8801]],\n" + " PARAMETER[\"Longitude of natural origin\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"False easting\",3,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",4,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Azimuthal_Equidistant\"],\n" + "PARAMETER[\"latitude_of_center\",1],\n" + "PARAMETER[\"longitude_of_center\",2],\n" + "PARAMETER[\"false_easting\",3],\n" + "PARAMETER[\"false_northing\",4]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, guam_projection_export) { + auto conv = Conversion::createGuamProjection( + PropertyMap(), Angle(1), Angle(2), Length(3), Length(4)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=aeqd +guam +lat_0=1 +lon_0=2 +x_0=3 +y_0=4"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Guam Projection\",\n" + " METHOD[\"Guam Projection\",\n" + " ID[\"EPSG\",9831]],\n" + " PARAMETER[\"Latitude of natural origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8801]],\n" + " PARAMETER[\"Longitude of natural origin\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"False easting\",3,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",4,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_THROW( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + FormattingException); +} + +// --------------------------------------------------------------------------- + +TEST(operation, bonne_export) { + auto conv = Conversion::createBonne(PropertyMap(), Angle(1), Angle(2), + Length(3), Length(4)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=bonne +lat_1=1 +lon_0=2 +x_0=3 +y_0=4"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Bonne\",\n" + " METHOD[\"Bonne\",\n" + " ID[\"EPSG\",9827]],\n" + " PARAMETER[\"Latitude of natural origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8801]],\n" + " PARAMETER[\"Longitude of natural origin\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"False easting\",3,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",4,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Bonne\"],\n" + "PARAMETER[\"standard_parallel_1\",1],\n" + "PARAMETER[\"central_meridian\",2],\n" + "PARAMETER[\"false_easting\",3],\n" + "PARAMETER[\"false_northing\",4]"); + + auto obj = WKTParser().createFromWKT( + "PROJCS[\"unnamed\"," + "GEOGCS[\"unnamed ellipse\"," + " DATUM[\"unknown\"," + " SPHEROID[\"unnamed\",6378137,298.257223563]]," + " PRIMEM[\"Greenwich\",0]," + " UNIT[\"degree\",0.0174532925199433]]," + "PROJECTION[\"Bonne\"]," + "PARAMETER[\"standard_parallel_1\",1]," + "PARAMETER[\"central_meridian\",2]," + "PARAMETER[\"false_easting\",3]," + "PARAMETER[\"false_northing\",4]," + "UNIT[\"metre\",1]]"); + auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + ASSERT_TRUE(crs != nullptr); + EXPECT_EQ(crs->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad " + "+step +proj=bonne +lat_1=1 +lon_0=2 +x_0=3 +y_0=4 +ellps=WGS84"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, lambert_cylindrical_equal_area_spherical_export) { + auto conv = Conversion::createLambertCylindricalEqualAreaSpherical( + PropertyMap(), Angle(1), Angle(2), Length(3), Length(4)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=cea +lat_ts=1 +lon_0=2 +x_0=3 +y_0=4"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Lambert Cylindrical Equal Area (Spherical)\",\n" + " METHOD[\"Lambert Cylindrical Equal Area (Spherical)\",\n" + " ID[\"EPSG\",9834]],\n" + " PARAMETER[\"Latitude of 1st standard parallel\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8823]],\n" + " PARAMETER[\"Longitude of natural origin\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"False easting\",3,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",4,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Cylindrical_Equal_Area\"],\n" + "PARAMETER[\"standard_parallel_1\",1],\n" + "PARAMETER[\"central_meridian\",2],\n" + "PARAMETER[\"false_easting\",3],\n" + "PARAMETER[\"false_northing\",4]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, lambert_cylindrical_equal_area_export) { + auto conv = Conversion::createLambertCylindricalEqualArea( + PropertyMap(), Angle(1), Angle(2), Length(3), Length(4)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=cea +lat_ts=1 +lon_0=2 +x_0=3 +y_0=4"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Lambert Cylindrical Equal Area\",\n" + " METHOD[\"Lambert Cylindrical Equal Area\",\n" + " ID[\"EPSG\",9835]],\n" + " PARAMETER[\"Latitude of 1st standard parallel\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8823]],\n" + " PARAMETER[\"Longitude of natural origin\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"False easting\",3,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",4,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Cylindrical_Equal_Area\"],\n" + "PARAMETER[\"standard_parallel_1\",1],\n" + "PARAMETER[\"central_meridian\",2],\n" + "PARAMETER[\"false_easting\",3],\n" + "PARAMETER[\"false_northing\",4]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, lcc1sp_export) { + auto conv = Conversion::createLambertConicConformal_1SP( + PropertyMap(), Angle(1), Angle(2), Scale(3), Length(4), Length(5)); + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=lcc +lat_1=1 +lat_0=1 +lon_0=2 +k_0=3 +x_0=4 +y_0=5"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Lambert Conic Conformal (1SP)\",\n" + " METHOD[\"Lambert Conic Conformal (1SP)\",\n" + " ID[\"EPSG\",9801]],\n" + " PARAMETER[\"Latitude of natural origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8801]],\n" + " PARAMETER[\"Longitude of natural origin\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"Scale factor at natural origin\",3,\n" + " SCALEUNIT[\"unity\",1],\n" + " ID[\"EPSG\",8805]],\n" + " PARAMETER[\"False easting\",4,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",5,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Lambert_Conformal_Conic_1SP\"],\n" + "PARAMETER[\"latitude_of_origin\",1],\n" + "PARAMETER[\"central_meridian\",2],\n" + "PARAMETER[\"scale_factor\",3],\n" + "PARAMETER[\"false_easting\",4],\n" + "PARAMETER[\"false_northing\",5]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, lcc2sp_export) { + auto conv = Conversion::createLambertConicConformal_2SP( + PropertyMap(), Angle(1), Angle(2), Angle(3), Angle(4), Length(5), + Length(6)); + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=lcc +lat_0=1 +lon_0=2 +lat_1=3 +lat_2=4 +x_0=5 +y_0=6"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Lambert Conic Conformal (2SP)\",\n" + " METHOD[\"Lambert Conic Conformal (2SP)\",\n" + " ID[\"EPSG\",9802]],\n" + " PARAMETER[\"Latitude of false origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8821]],\n" + " PARAMETER[\"Longitude of false origin\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8822]],\n" + " PARAMETER[\"Latitude of 1st standard parallel\",3,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8823]],\n" + " PARAMETER[\"Latitude of 2nd standard parallel\",4,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8824]],\n" + " PARAMETER[\"Easting at false origin\",5,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8826]],\n" + " PARAMETER[\"Northing at false origin\",6,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8827]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Lambert_Conformal_Conic_2SP\"],\n" + "PARAMETER[\"latitude_of_origin\",1],\n" + "PARAMETER[\"central_meridian\",2],\n" + "PARAMETER[\"standard_parallel_1\",3],\n" + "PARAMETER[\"standard_parallel_2\",4],\n" + "PARAMETER[\"false_easting\",5],\n" + "PARAMETER[\"false_northing\",6]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, lcc2sp_isEquivalentTo_parallels_switched) { + auto conv1 = Conversion::createLambertConicConformal_2SP( + PropertyMap(), Angle(1), Angle(2), Angle(3), Angle(4), Length(5), + Length(6)); + auto conv2 = Conversion::createLambertConicConformal_2SP( + PropertyMap(), Angle(1), Angle(2), Angle(4), Angle(3), Length(5), + Length(6)); + + EXPECT_TRUE( + conv1->isEquivalentTo(conv2.get(), IComparable::Criterion::EQUIVALENT)); + + auto conv3 = Conversion::createLambertConicConformal_2SP( + PropertyMap(), Angle(1), Angle(2), Angle(3), Angle(3), Length(5), + Length(6)); + + EXPECT_FALSE( + conv1->isEquivalentTo(conv3.get(), IComparable::Criterion::EQUIVALENT)); +} + +// --------------------------------------------------------------------------- + +TEST(operation, lcc2sp_michigan_export) { + auto conv = Conversion::createLambertConicConformal_2SP_Michigan( + PropertyMap(), Angle(1), Angle(2), Angle(3), Angle(4), Length(5), + Length(6), Scale(7)); + EXPECT_EQ( + conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=lcc +lat_0=1 +lon_0=2 +lat_1=3 +lat_2=4 +x_0=5 +y_0=6 +k_0=7"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Lambert Conic Conformal (2SP Michigan)\",\n" + " METHOD[\"Lambert Conic Conformal (2SP Michigan)\",\n" + " ID[\"EPSG\",1051]],\n" + " PARAMETER[\"Latitude of false origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8821]],\n" + " PARAMETER[\"Longitude of false origin\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8822]],\n" + " PARAMETER[\"Latitude of 1st standard parallel\",3,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8823]],\n" + " PARAMETER[\"Latitude of 2nd standard parallel\",4,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8824]],\n" + " PARAMETER[\"Easting at false origin\",5,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8826]],\n" + " PARAMETER[\"Northing at false origin\",6,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8827]],\n" + " PARAMETER[\"Ellipsoid scaling factor\",7,\n" + " SCALEUNIT[\"unity\",1],\n" + " ID[\"EPSG\",1038]]]"); + + EXPECT_THROW( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + FormattingException); +} + +// --------------------------------------------------------------------------- + +TEST(operation, lcc2sp_belgium_export) { + auto conv = Conversion::createLambertConicConformal_2SP_Belgium( + PropertyMap(), Angle(1), Angle(2), Angle(3), Angle(4), Length(5), + Length(6)); + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=lcc +lat_0=1 +lon_0=2 +lat_1=3 +lat_2=4 +x_0=5 +y_0=6"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Lambert Conic Conformal (2SP Belgium)\",\n" + " METHOD[\"Lambert Conic Conformal (2SP Belgium)\",\n" + " ID[\"EPSG\",9803]],\n" + " PARAMETER[\"Latitude of false origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8821]],\n" + " PARAMETER[\"Longitude of false origin\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8822]],\n" + " PARAMETER[\"Latitude of 1st standard parallel\",3,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8823]],\n" + " PARAMETER[\"Latitude of 2nd standard parallel\",4,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8824]],\n" + " PARAMETER[\"Easting at false origin\",5,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8826]],\n" + " PARAMETER[\"Northing at false origin\",6,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8827]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Lambert_Conformal_Conic_2SP_Belgium\"],\n" + "PARAMETER[\"latitude_of_origin\",1],\n" + "PARAMETER[\"central_meridian\",2],\n" + "PARAMETER[\"standard_parallel_1\",3],\n" + "PARAMETER[\"standard_parallel_2\",4],\n" + "PARAMETER[\"false_easting\",5],\n" + "PARAMETER[\"false_northing\",6]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, cassini_soldner_export) { + auto conv = Conversion::createCassiniSoldner( + PropertyMap(), Angle(1), Angle(2), Length(4), Length(5)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=cass +lat_0=1 +lon_0=2 +x_0=4 +y_0=5"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Cassini-Soldner\",\n" + " METHOD[\"Cassini-Soldner\",\n" + " ID[\"EPSG\",9806]],\n" + " PARAMETER[\"Latitude of natural origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8801]],\n" + " PARAMETER[\"Longitude of natural origin\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"False easting\",4,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",5,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Cassini_Soldner\"],\n" + "PARAMETER[\"latitude_of_origin\",1],\n" + "PARAMETER[\"central_meridian\",2],\n" + "PARAMETER[\"false_easting\",4],\n" + "PARAMETER[\"false_northing\",5]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, equidistant_conic_export) { + auto conv = Conversion::createEquidistantConic(PropertyMap(), Angle(1), + Angle(2), Angle(3), Angle(4), + Length(5), Length(6)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=eqdc +lat_0=1 +lon_0=2 +lat_1=3 +lat_2=4 +x_0=5 +y_0=6"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Equidistant Conic\",\n" + " METHOD[\"Equidistant Conic\"],\n" + " PARAMETER[\"Latitude of natural origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8801]],\n" + " PARAMETER[\"Longitude of natural origin\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"Latitude of 1st standard parallel\",3,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8823]],\n" + " PARAMETER[\"Latitude of 2nd standard parallel\",4,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8824]],\n" + " PARAMETER[\"False easting\",5,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",6,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Equidistant_Conic\"],\n" + "PARAMETER[\"latitude_of_center\",1],\n" + "PARAMETER[\"longitude_of_center\",2],\n" + "PARAMETER[\"standard_parallel_1\",3],\n" + "PARAMETER[\"standard_parallel_2\",4],\n" + "PARAMETER[\"false_easting\",5],\n" + "PARAMETER[\"false_northing\",6]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, eckert_export) { + + std::vector<std::string> numbers{"", "1", "2", "3", "4", "5", "6"}; + std::vector<std::string> latinNumbers{"", "I", "II", "III", + "IV", "V", "VI"}; + + for (int i = 1; i <= 6; i++) { + auto conv = + (i == 1) + ? Conversion::createEckertI(PropertyMap(), Angle(1), Length(2), + Length(3)) + : (i == 2) + ? Conversion::createEckertII(PropertyMap(), Angle(1), + Length(2), Length(3)) + : (i == 3) + ? Conversion::createEckertIII( + PropertyMap(), Angle(1), Length(2), Length(3)) + : (i == 4) ? Conversion::createEckertIV( + PropertyMap(), Angle(1), Length(2), + Length(3)) + : (i == 5) ? Conversion::createEckertV( + PropertyMap(), Angle(1), + Length(2), Length(3)) + : + + Conversion::createEckertVI( + PropertyMap(), Angle(1), + Length(2), Length(3)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=eck" + numbers[i] + " +lon_0=1 +x_0=2 +y_0=3"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Eckert " + latinNumbers[i] + + "\",\n" + " METHOD[\"Eckert " + + latinNumbers[i] + + "\"],\n" + " PARAMETER[\"Longitude of natural origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"False easting\",2,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",3,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ(conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL) + .get()), + "PROJECTION[\"Eckert_" + latinNumbers[i] + + "\"],\n" + "PARAMETER[\"central_meridian\",1],\n" + "PARAMETER[\"false_easting\",2],\n" + "PARAMETER[\"false_northing\",3]"); + } +} + +// --------------------------------------------------------------------------- + +TEST(operation, createEquidistantCylindrical) { + auto conv = Conversion::createEquidistantCylindrical( + PropertyMap(), Angle(1), Angle(2), Length(3), Length(4)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=eqc +lat_ts=1 +lon_0=2 +x_0=3 +y_0=4"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Equidistant Cylindrical\",\n" + " METHOD[\"Equidistant Cylindrical\",\n" + " ID[\"EPSG\",1028]],\n" + " PARAMETER[\"Latitude of 1st standard parallel\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8823]],\n" + " PARAMETER[\"Longitude of natural origin\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"False easting\",3,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",4,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Equirectangular\"],\n" + "PARAMETER[\"standard_parallel_1\",1],\n" + "PARAMETER[\"central_meridian\",2],\n" + "PARAMETER[\"false_easting\",3],\n" + "PARAMETER[\"false_northing\",4]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, createEquidistantCylindricalSpherical) { + auto conv = Conversion::createEquidistantCylindricalSpherical( + PropertyMap(), Angle(1), Angle(2), Length(3), Length(4)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=eqc +lat_ts=1 +lon_0=2 +x_0=3 +y_0=4"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Equidistant Cylindrical (Spherical)\",\n" + " METHOD[\"Equidistant Cylindrical (Spherical)\",\n" + " ID[\"EPSG\",1029]],\n" + " PARAMETER[\"Latitude of 1st standard parallel\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8823]],\n" + " PARAMETER[\"Longitude of natural origin\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"False easting\",3,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",4,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Equirectangular\"],\n" + "PARAMETER[\"standard_parallel_1\",1],\n" + "PARAMETER[\"central_meridian\",2],\n" + "PARAMETER[\"false_easting\",3],\n" + "PARAMETER[\"false_northing\",4]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, gall_export) { + + auto conv = + Conversion::createGall(PropertyMap(), Angle(1), Length(2), Length(3)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=gall +lon_0=1 +x_0=2 +y_0=3"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Gall Stereographic\",\n" + " METHOD[\"Gall Stereographic\"],\n" + " PARAMETER[\"Longitude of natural origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"False easting\",2,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",3,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Gall_Stereographic\"],\n" + "PARAMETER[\"central_meridian\",1],\n" + "PARAMETER[\"false_easting\",2],\n" + "PARAMETER[\"false_northing\",3]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, goode_homolosine_export) { + + auto conv = Conversion::createGoodeHomolosine(PropertyMap(), Angle(1), + Length(2), Length(3)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=goode +lon_0=1 +x_0=2 +y_0=3"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Goode Homolosine\",\n" + " METHOD[\"Goode Homolosine\"],\n" + " PARAMETER[\"Longitude of natural origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"False easting\",2,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",3,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Goode_Homolosine\"],\n" + "PARAMETER[\"central_meridian\",1],\n" + "PARAMETER[\"false_easting\",2],\n" + "PARAMETER[\"false_northing\",3]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, interrupted_goode_homolosine_export) { + + auto conv = Conversion::createInterruptedGoodeHomolosine( + PropertyMap(), Angle(1), Length(2), Length(3)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=igh +lon_0=1 +x_0=2 +y_0=3"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Interrupted Goode Homolosine\",\n" + " METHOD[\"Interrupted Goode Homolosine\"],\n" + " PARAMETER[\"Longitude of natural origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"False easting\",2,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",3,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Interrupted_Goode_Homolosine\"],\n" + "PARAMETER[\"central_meridian\",1],\n" + "PARAMETER[\"false_easting\",2],\n" + "PARAMETER[\"false_northing\",3]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, geostationary_satellite_sweep_x_export) { + + auto conv = Conversion::createGeostationarySatelliteSweepX( + PropertyMap(), Angle(1), Length(2), Length(3), Length(4)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=geos +sweep=x +lon_0=1 +h=2 +x_0=3 +y_0=4"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Geostationary Satellite (Sweep X)\",\n" + " METHOD[\"Geostationary Satellite (Sweep X)\"],\n" + " PARAMETER[\"Longitude of natural origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"Satellite Height\",2,\n" + " LENGTHUNIT[\"metre\",1,\n" + " ID[\"EPSG\",9001]]],\n" + " PARAMETER[\"False easting\",3,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",4,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_THROW( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + FormattingException); +} + +// --------------------------------------------------------------------------- + +TEST(operation, geostationary_satellite_sweep_y_export) { + + auto conv = Conversion::createGeostationarySatelliteSweepY( + PropertyMap(), Angle(1), Length(2), Length(3), Length(4)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=geos +lon_0=1 +h=2 +x_0=3 +y_0=4"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Geostationary Satellite (Sweep Y)\",\n" + " METHOD[\"Geostationary Satellite (Sweep Y)\"],\n" + " PARAMETER[\"Longitude of natural origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"Satellite Height\",2,\n" + " LENGTHUNIT[\"metre\",1,\n" + " ID[\"EPSG\",9001]]],\n" + " PARAMETER[\"False easting\",3,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",4,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Geostationary_Satellite\"],\n" + "PARAMETER[\"central_meridian\",1],\n" + "PARAMETER[\"satellite_height\",2],\n" + "PARAMETER[\"false_easting\",3],\n" + "PARAMETER[\"false_northing\",4]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, gnomonic_export) { + auto conv = Conversion::createGnomonic(PropertyMap(), Angle(1), Angle(2), + Length(4), Length(5)); + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=gnom +lat_0=1 +lon_0=2 +x_0=4 +y_0=5"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Gnomonic\",\n" + " METHOD[\"Gnomonic\"],\n" + " PARAMETER[\"Latitude of natural origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8801]],\n" + " PARAMETER[\"Longitude of natural origin\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"False easting\",4,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",5,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Gnomonic\"],\n" + "PARAMETER[\"latitude_of_origin\",1],\n" + "PARAMETER[\"central_meridian\",2],\n" + "PARAMETER[\"false_easting\",4],\n" + "PARAMETER[\"false_northing\",5]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, hotine_oblique_mercator_variant_A_export) { + auto conv = Conversion::createHotineObliqueMercatorVariantA( + PropertyMap(), Angle(1), Angle(2), Angle(3), Angle(4), Scale(5), + Length(6), Length(7)); + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=omerc +no_uoff +lat_0=1 +lonc=2 +alpha=3 +gamma=4 +k=5 " + "+x_0=6 +y_0=7"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Hotine Oblique Mercator (variant A)\",\n" + " METHOD[\"Hotine Oblique Mercator (variant A)\",\n" + " ID[\"EPSG\",9812]],\n" + " PARAMETER[\"Latitude of projection centre\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8811]],\n" + " PARAMETER[\"Longitude of projection centre\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8812]],\n" + " PARAMETER[\"Azimuth of initial line\",3,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8813]],\n" + " PARAMETER[\"Angle from Rectified to Skew Grid\",4,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8814]],\n" + " PARAMETER[\"Scale factor on initial line\",5,\n" + " SCALEUNIT[\"unity\",1],\n" + " ID[\"EPSG\",8815]],\n" + " PARAMETER[\"False easting\",6,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",7,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Hotine_Oblique_Mercator\"],\n" + "PARAMETER[\"latitude_of_center\",1],\n" + "PARAMETER[\"longitude_of_center\",2],\n" + "PARAMETER[\"azimuth\",3],\n" + "PARAMETER[\"rectified_grid_angle\",4],\n" + "PARAMETER[\"scale_factor\",5],\n" + "PARAMETER[\"false_easting\",6],\n" + "PARAMETER[\"false_northing\",7]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, hotine_oblique_mercator_variant_A_export_swiss_mercator) { + auto conv = Conversion::createHotineObliqueMercatorVariantA( + PropertyMap(), Angle(1), Angle(2), Angle(90), Angle(90), Scale(5), + Length(6), Length(7)); + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=somerc +lat_0=1 +lon_0=2 +k_0=5 " + "+x_0=6 +y_0=7"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, hotine_oblique_mercator_variant_B_export) { + auto conv = Conversion::createHotineObliqueMercatorVariantB( + PropertyMap(), Angle(1), Angle(2), Angle(3), Angle(4), Scale(5), + Length(6), Length(7)); + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=omerc +lat_0=1 +lonc=2 +alpha=3 +gamma=4 +k=5 " + "+x_0=6 +y_0=7"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Hotine Oblique Mercator (variant B)\",\n" + " METHOD[\"Hotine Oblique Mercator (variant B)\",\n" + " ID[\"EPSG\",9815]],\n" + " PARAMETER[\"Latitude of projection centre\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8811]],\n" + " PARAMETER[\"Longitude of projection centre\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8812]],\n" + " PARAMETER[\"Azimuth of initial line\",3,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8813]],\n" + " PARAMETER[\"Angle from Rectified to Skew Grid\",4,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8814]],\n" + " PARAMETER[\"Scale factor on initial line\",5,\n" + " SCALEUNIT[\"unity\",1],\n" + " ID[\"EPSG\",8815]],\n" + " PARAMETER[\"Easting at projection centre\",6,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8816]],\n" + " PARAMETER[\"Northing at projection centre\",7,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8817]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Hotine_Oblique_Mercator_Azimuth_Center\"],\n" + "PARAMETER[\"latitude_of_center\",1],\n" + "PARAMETER[\"longitude_of_center\",2],\n" + "PARAMETER[\"azimuth\",3],\n" + "PARAMETER[\"rectified_grid_angle\",4],\n" + "PARAMETER[\"scale_factor\",5],\n" + "PARAMETER[\"false_easting\",6],\n" + "PARAMETER[\"false_northing\",7]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, hotine_oblique_mercator_variant_B_export_swiss_mercator) { + auto conv = Conversion::createHotineObliqueMercatorVariantB( + PropertyMap(), Angle(1), Angle(2), Angle(90), Angle(90), Scale(5), + Length(6), Length(7)); + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=somerc +lat_0=1 +lon_0=2 +k_0=5 " + "+x_0=6 +y_0=7"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, hotine_oblique_mercator_two_point_natural_origin_export) { + auto conv = Conversion::createHotineObliqueMercatorTwoPointNaturalOrigin( + PropertyMap(), Angle(1), Angle(2), Angle(3), Angle(4), Angle(5), + Scale(6), Length(7), Length(8)); + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=omerc +lat_0=1 +lat_1=2 +lon_1=3 +lat_2=4 +lon_2=5 +k=6 " + "+x_0=7 +y_0=8"); + + auto formatter = WKTFormatter::create(); + formatter->simulCurNodeHasId(); + EXPECT_EQ( + conv->exportToWKT(formatter.get()), + "CONVERSION[\"Hotine Oblique Mercator Two Point Natural Origin\",\n" + " METHOD[\"Hotine Oblique Mercator Two Point Natural Origin\"],\n" + " PARAMETER[\"Latitude of projection centre\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8811]],\n" + " PARAMETER[\"Latitude of 1st point\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " PARAMETER[\"Longitude of 1st point\",3,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " PARAMETER[\"Latitude of 2nd point\",4,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " PARAMETER[\"Longitude of 2nd point\",5,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " PARAMETER[\"Scale factor on initial line\",6,\n" + " SCALEUNIT[\"unity\",1],\n" + " ID[\"EPSG\",8815]],\n" + " PARAMETER[\"Easting at projection centre\",7,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8816]],\n" + " PARAMETER[\"Northing at projection centre\",8,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8817]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Hotine_Oblique_Mercator_Two_Point_Natural_Origin\"],\n" + "PARAMETER[\"latitude_of_origin\",1],\n" + "PARAMETER[\"latitude_of_point_1\",2],\n" + "PARAMETER[\"longitude_of_point_1\",3],\n" + "PARAMETER[\"latitude_of_point_2\",4],\n" + "PARAMETER[\"longitude_of_point_2\",5],\n" + "PARAMETER[\"scale_factor\",6],\n" + "PARAMETER[\"false_easting\",7],\n" + "PARAMETER[\"false_northing\",8]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, imw_polyconic_export) { + auto conv = Conversion::createInternationalMapWorldPolyconic( + PropertyMap(), Angle(1), Angle(3), Angle(4), Length(5), Length(6)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=imw_p +lon_0=1 +lat_1=3 +lat_2=4 +x_0=5 +y_0=6"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"International Map of the World Polyconic\",\n" + " METHOD[\"International Map of the World Polyconic\"],\n" + " PARAMETER[\"Longitude of natural origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"Latitude of 1st standard parallel\",3,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8823]],\n" + " PARAMETER[\"Latitude of 2nd standard parallel\",4,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8824]],\n" + " PARAMETER[\"False easting\",5,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",6,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"International_Map_of_the_World_Polyconic\"],\n" + "PARAMETER[\"central_meridian\",1],\n" + "PARAMETER[\"standard_parallel_1\",3],\n" + "PARAMETER[\"standard_parallel_2\",4],\n" + "PARAMETER[\"false_easting\",5],\n" + "PARAMETER[\"false_northing\",6]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, krovak_north_oriented_export) { + auto conv = Conversion::createKrovakNorthOriented( + PropertyMap(), Angle(49.5), Angle(42.5), Angle(30.28813972222222), + Angle(78.5), Scale(0.9999), Length(5), Length(6)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=krovak +lat_0=49.5 +lon_0=42.5 +k=0.9999 +x_0=5 +y_0=6"); + + EXPECT_EQ( + conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Krovak (North Orientated)\",\n" + " METHOD[\"Krovak (North Orientated)\",\n" + " ID[\"EPSG\",1041]],\n" + " PARAMETER[\"Latitude of projection centre\",49.5,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8811]],\n" + " PARAMETER[\"Longitude of origin\",42.5,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8833]],\n" + " PARAMETER[\"Co-latitude of cone axis\",30.2881397222222,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",1036]],\n" + " PARAMETER[\"Latitude of pseudo standard parallel\",78.5,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8818]],\n" + " PARAMETER[\"Scale factor on pseudo standard parallel\",0.9999,\n" + " SCALEUNIT[\"unity\",1],\n" + " ID[\"EPSG\",8819]],\n" + " PARAMETER[\"False easting\",5,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",6,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Krovak\"],\n" + "PARAMETER[\"latitude_of_center\",49.5],\n" + "PARAMETER[\"longitude_of_center\",42.5],\n" + "PARAMETER[\"azimuth\",30.2881397222222],\n" + "PARAMETER[\"pseudo_standard_parallel_1\",78.5],\n" + "PARAMETER[\"scale_factor\",0.9999],\n" + "PARAMETER[\"false_easting\",5],\n" + "PARAMETER[\"false_northing\",6]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, krovak_export) { + auto conv = Conversion::createKrovak( + PropertyMap(), Angle(49.5), Angle(42.5), Angle(30.28813972222222), + Angle(78.5), Scale(0.9999), Length(5), Length(6)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=krovak +axis=swu +lat_0=49.5 +lon_0=42.5 +k=0.9999 +x_0=5 " + "+y_0=6"); + + EXPECT_EQ( + conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Krovak\",\n" + " METHOD[\"Krovak\",\n" + " ID[\"EPSG\",9819]],\n" + " PARAMETER[\"Latitude of projection centre\",49.5,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8811]],\n" + " PARAMETER[\"Longitude of origin\",42.5,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8833]],\n" + " PARAMETER[\"Co-latitude of cone axis\",30.2881397222222,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",1036]],\n" + " PARAMETER[\"Latitude of pseudo standard parallel\",78.5,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8818]],\n" + " PARAMETER[\"Scale factor on pseudo standard parallel\",0.9999,\n" + " SCALEUNIT[\"unity\",1],\n" + " ID[\"EPSG\",8819]],\n" + " PARAMETER[\"False easting\",5,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",6,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Krovak\"],\n" + "PARAMETER[\"latitude_of_center\",49.5],\n" + "PARAMETER[\"longitude_of_center\",42.5],\n" + "PARAMETER[\"azimuth\",30.2881397222222],\n" + "PARAMETER[\"pseudo_standard_parallel_1\",78.5],\n" + "PARAMETER[\"scale_factor\",0.9999],\n" + "PARAMETER[\"false_easting\",5],\n" + "PARAMETER[\"false_northing\",6]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, lambert_azimuthal_equal_area_export) { + auto conv = Conversion::createLambertAzimuthalEqualArea( + PropertyMap(), Angle(1), Angle(2), Length(3), Length(4)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=laea +lat_0=1 +lon_0=2 +x_0=3 +y_0=4"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Lambert Azimuthal Equal Area\",\n" + " METHOD[\"Lambert Azimuthal Equal Area\",\n" + " ID[\"EPSG\",9820]],\n" + " PARAMETER[\"Latitude of natural origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8801]],\n" + " PARAMETER[\"Longitude of natural origin\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"False easting\",3,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",4,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Lambert_Azimuthal_Equal_Area\"],\n" + "PARAMETER[\"latitude_of_center\",1],\n" + "PARAMETER[\"longitude_of_center\",2],\n" + "PARAMETER[\"false_easting\",3],\n" + "PARAMETER[\"false_northing\",4]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, miller_cylindrical_export) { + auto conv = Conversion::createMillerCylindrical(PropertyMap(), Angle(2), + Length(3), Length(4)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=mill +R_A +lon_0=2 +x_0=3 +y_0=4"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Miller Cylindrical\",\n" + " METHOD[\"Miller Cylindrical\"],\n" + " PARAMETER[\"Longitude of natural origin\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"False easting\",3,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",4,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Miller_Cylindrical\"],\n" + "PARAMETER[\"longitude_of_center\",2],\n" + "PARAMETER[\"false_easting\",3],\n" + "PARAMETER[\"false_northing\",4]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, mercator_variant_A_export) { + auto conv = Conversion::createMercatorVariantA( + PropertyMap(), Angle(0), Angle(1), Scale(2), Length(3), Length(4)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=merc +lon_0=1 +k=2 +x_0=3 +y_0=4"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Mercator (variant A)\",\n" + " METHOD[\"Mercator (variant A)\",\n" + " ID[\"EPSG\",9804]],\n" + " PARAMETER[\"Latitude of natural origin\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8801]],\n" + " PARAMETER[\"Longitude of natural origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"Scale factor at natural origin\",2,\n" + " SCALEUNIT[\"unity\",1],\n" + " ID[\"EPSG\",8805]],\n" + " PARAMETER[\"False easting\",3,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",4,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Mercator_1SP\"],\n" + "PARAMETER[\"central_meridian\",1],\n" + "PARAMETER[\"scale_factor\",2],\n" + "PARAMETER[\"false_easting\",3],\n" + "PARAMETER[\"false_northing\",4]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, mercator_variant_A_export_latitude_origin_non_zero) { + auto conv = Conversion::createMercatorVariantA( + PropertyMap(), Angle(10), Angle(1), Scale(2), Length(3), Length(4)); + + EXPECT_THROW(conv->exportToPROJString(PROJStringFormatter::create().get()), + FormattingException); +} + +// --------------------------------------------------------------------------- + +TEST(operation, wkt1_import_mercator_variant_A) { + auto wkt = "PROJCS[\"test\",\n" + " GEOGCS[\"WGS 84\",\n" + " DATUM[\"WGS 1984\",\n" + " SPHEROID[\"WGS 84\",6378137,298.257223563]],\n" + " PRIMEM[\"Greenwich\",0],\n" + " UNIT[\"degree\",0.0174532925199433]],\n" + " PROJECTION[\"Mercator_1SP\"],\n" + " PARAMETER[\"central_meridian\",1],\n" + " PARAMETER[\"scale_factor\",2],\n" + " PARAMETER[\"false_easting\",3],\n" + " PARAMETER[\"false_northing\",4],\n" + " UNIT[\"metre\",1]]"; + auto obj = WKTParser().createFromWKT(wkt); + auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + ASSERT_TRUE(crs != nullptr); + + auto conversion = crs->derivingConversion(); + auto convRef = Conversion::createMercatorVariantA( + PropertyMap().set(IdentifiedObject::NAME_KEY, "unnamed"), Angle(0), + Angle(1), Scale(2), Length(3), Length(4)); + + EXPECT_EQ(conversion->exportToWKT(WKTFormatter::create().get()), + convRef->exportToWKT(WKTFormatter::create().get())); +} + +// --------------------------------------------------------------------------- + +TEST(operation, mercator_variant_B_export) { + auto conv = Conversion::createMercatorVariantB( + PropertyMap(), Angle(1), Angle(2), Length(3), Length(4)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=merc +lat_ts=1 +lon_0=2 +x_0=3 +y_0=4"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Mercator (variant B)\",\n" + " METHOD[\"Mercator (variant B)\",\n" + " ID[\"EPSG\",9805]],\n" + " PARAMETER[\"Latitude of 1st standard parallel\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8823]],\n" + " PARAMETER[\"Longitude of natural origin\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"False easting\",3,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",4,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Mercator_2SP\"],\n" + "PARAMETER[\"standard_parallel_1\",1],\n" + "PARAMETER[\"central_meridian\",2],\n" + "PARAMETER[\"false_easting\",3],\n" + "PARAMETER[\"false_northing\",4]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, webmerc_export) { + auto conv = Conversion::createPopularVisualisationPseudoMercator( + PropertyMap(), Angle(0), Angle(2), Length(3), Length(4)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=webmerc +lat_0=0 +lon_0=2 +x_0=3 +y_0=4"); + + EXPECT_THROW( + conv->exportToPROJString( + PROJStringFormatter::create(PROJStringFormatter::Convention::PROJ_4) + .get()), + FormattingException); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Popular Visualisation Pseudo Mercator\",\n" + " METHOD[\"Popular Visualisation Pseudo Mercator\",\n" + " ID[\"EPSG\",1024]],\n" + " PARAMETER[\"Latitude of natural origin\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8801]],\n" + " PARAMETER[\"Longitude of natural origin\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"False easting\",3,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",4,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + auto projCRS = ProjectedCRS::create( + PropertyMap().set(IdentifiedObject::NAME_KEY, "Pseudo-Mercator"), + GeographicCRS::EPSG_4326, conv, + CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)); + + EXPECT_EQ( + projCRS->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJCS[\"Pseudo-Mercator\",\n" + " GEOGCS[\"WGS 84\",\n" + " DATUM[\"WGS_1984\",\n" + " SPHEROID[\"WGS 84\",6378137,298.257223563,\n" + " AUTHORITY[\"EPSG\",\"7030\"]],\n" + " AUTHORITY[\"EPSG\",\"6326\"]],\n" + " PRIMEM[\"Greenwich\",0,\n" + " AUTHORITY[\"EPSG\",\"8901\"]],\n" + " UNIT[\"degree\",0.0174532925199433,\n" + " AUTHORITY[\"EPSG\",\"9122\"]],\n" + " AXIS[\"Latitude\",NORTH],\n" + " AXIS[\"Longitude\",EAST],\n" + " AUTHORITY[\"EPSG\",\"4326\"]],\n" + " PROJECTION[\"Mercator_1SP\"],\n" + " PARAMETER[\"central_meridian\",2],\n" + " PARAMETER[\"scale_factor\",1],\n" + " PARAMETER[\"false_easting\",3],\n" + " PARAMETER[\"false_northing\",4],\n" + " UNIT[\"metre\",1,\n" + " AUTHORITY[\"EPSG\",\"9001\"]],\n" + " AXIS[\"Easting\",EAST],\n" + " AXIS[\"Northing\",NORTH],\n" + " EXTENSION[\"PROJ4\",\"+proj=merc " + "+a=6378137 +b=6378137 +lat_ts=0 +lon_0=2 " + "+x_0=3 +y_0=4 +k=1 +units=m " + "+nadgrids=@null +wktext +no_defs\"]]"); + + EXPECT_EQ( + projCRS->exportToPROJString( + PROJStringFormatter::create(PROJStringFormatter::Convention::PROJ_5) + .get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=webmerc " + "+lat_0=0 +lon_0=2 +x_0=3 +y_0=4 +ellps=WGS84"); + + EXPECT_EQ( + projCRS->exportToPROJString( + PROJStringFormatter::create(PROJStringFormatter::Convention::PROJ_4) + .get()), + "+proj=merc +a=6378137 +b=6378137 +lat_ts=0 +lon_0=2 +x_0=3 " + "+y_0=4 +k=1 +units=m +nadgrids=@null +wktext +no_defs"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, webmerc_import_from_GDAL_wkt1) { + + auto projCRS = ProjectedCRS::create( + PropertyMap().set(IdentifiedObject::NAME_KEY, "Pseudo-Mercator"), + GeographicCRS::EPSG_4326, + Conversion::createPopularVisualisationPseudoMercator( + PropertyMap(), Angle(0), Angle(0), Length(0), Length(0)), + CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)); + + auto wkt1 = projCRS->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()); + auto obj = WKTParser().createFromWKT(wkt1); + auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + ASSERT_TRUE(crs != nullptr); + + auto convGot = crs->derivingConversion(); + + EXPECT_EQ(convGot->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"unnamed\",\n" + " METHOD[\"Popular Visualisation Pseudo Mercator\",\n" + " ID[\"EPSG\",1024]],\n" + " PARAMETER[\"Latitude of natural origin\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8801]],\n" + " PARAMETER[\"Longitude of natural origin\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"False easting\",0,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",0,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, webmerc_import_from_GDAL_wkt1_EPSG_3785_deprecated) { + + auto wkt1 = + "PROJCS[\"Popular Visualisation CRS / Mercator (deprecated)\"," + " GEOGCS[\"Popular Visualisation CRS\"," + " DATUM[\"Popular_Visualisation_Datum\"," + " SPHEROID[\"Popular Visualisation Sphere\",6378137,0," + " AUTHORITY[\"EPSG\",\"7059\"]]," + " TOWGS84[0,0,0,0,0,0,0]," + " AUTHORITY[\"EPSG\",\"6055\"]]," + " PRIMEM[\"Greenwich\",0," + " AUTHORITY[\"EPSG\",\"8901\"]]," + " UNIT[\"degree\",0.0174532925199433," + " AUTHORITY[\"EPSG\",\"9122\"]]," + " AUTHORITY[\"EPSG\",\"4055\"]]," + " PROJECTION[\"Mercator_1SP\"]," + " PARAMETER[\"central_meridian\",0]," + " PARAMETER[\"scale_factor\",1]," + " PARAMETER[\"false_easting\",0]," + " PARAMETER[\"false_northing\",0]," + " UNIT[\"metre\",1," + " AUTHORITY[\"EPSG\",\"9001\"]]," + " AXIS[\"X\",EAST]," + " AXIS[\"Y\",NORTH]," + " EXTENSION[\"PROJ4\",\"+proj=merc +a=6378137 +b=6378137 " + "+lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m " + "+nadgrids=@null +wktext +no_defs\"]]"; + + auto obj = WKTParser().createFromWKT(wkt1); + auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + ASSERT_TRUE(crs != nullptr); + + EXPECT_EQ(crs->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=webmerc " + "+lat_0=0 +lon_0=0 +x_0=0 +y_0=0 " + "+ellps=WGS84"); + + EXPECT_EQ( + crs->exportToPROJString( + PROJStringFormatter::create(PROJStringFormatter::Convention::PROJ_4) + .get()), + "+proj=merc +a=6378137 +b=6378137 +lat_ts=0 +lon_0=0 +x_0=0 " + "+y_0=0 +k=1 +units=m +nadgrids=@null +wktext +no_defs"); + + auto convGot = crs->derivingConversion(); + + EXPECT_EQ(convGot->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"unnamed\",\n" + " METHOD[\"Popular Visualisation Pseudo Mercator\",\n" + " ID[\"EPSG\",1024]],\n" + " PARAMETER[\"Latitude of natural origin\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8801]],\n" + " PARAMETER[\"Longitude of natural origin\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"False easting\",0,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",0,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, webmerc_import_from_WKT2_EPSG_3785_deprecated) { + auto wkt2 = + "PROJCRS[\"Popular Visualisation CRS / Mercator\",\n" + " BASEGEODCRS[\"Popular Visualisation CRS\",\n" + " DATUM[\"Popular Visualisation Datum\",\n" + " ELLIPSOID[\"Popular Visualisation Sphere\",6378137,0,\n" + " LENGTHUNIT[\"metre\",1]]],\n" + " PRIMEM[\"Greenwich\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]]],\n" + " CONVERSION[\"Popular Visualisation Mercator\",\n" + " METHOD[\"Mercator (1SP) (Spherical)\",\n" + " ID[\"EPSG\",9841]],\n" + " PARAMETER[\"Latitude of natural origin\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8801]],\n" + " PARAMETER[\"Longitude of natural origin\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"Scale factor at natural origin\",1,\n" + " SCALEUNIT[\"unity\",1],\n" + " ID[\"EPSG\",8805]],\n" + " PARAMETER[\"False easting\",0,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",0,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]],\n" + " CS[Cartesian,2],\n" + " AXIS[\"easting (X)\",east,\n" + " ORDER[1],\n" + " LENGTHUNIT[\"metre\",1]],\n" + " AXIS[\"northing (Y)\",north,\n" + " ORDER[2],\n" + " LENGTHUNIT[\"metre\",1]]]"; + + auto obj = WKTParser().createFromWKT(wkt2); + auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + ASSERT_TRUE(crs != nullptr); + + EXPECT_EQ(crs->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=webmerc " + "+ellps=WGS84"); + + EXPECT_EQ( + crs->exportToPROJString( + PROJStringFormatter::create(PROJStringFormatter::Convention::PROJ_4) + .get()), + "+proj=merc +a=6378137 +b=6378137 +lat_ts=0 +lon_0=0 +x_0=0 " + "+y_0=0 +k=1 +units=m +nadgrids=@null +wktext +no_defs"); + + EXPECT_EQ( + crs->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT2_2015).get()), + wkt2); + + EXPECT_EQ( + crs->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJCS[\"Popular Visualisation CRS / Mercator\",\n" + " GEOGCS[\"Popular Visualisation CRS\",\n" + " DATUM[\"Popular_Visualisation_Datum\",\n" + " SPHEROID[\"Popular Visualisation Sphere\",6378137,0],\n" + " TOWGS84[0,0,0,0,0,0,0]],\n" + " PRIMEM[\"Greenwich\",0],\n" + " UNIT[\"degree\",0.0174532925199433],\n" + " AXIS[\"Latitude\",NORTH],\n" + " AXIS[\"Longitude\",EAST]],\n" + " PROJECTION[\"Mercator_1SP\"],\n" + " PARAMETER[\"central_meridian\",0],\n" + " PARAMETER[\"scale_factor\",1],\n" + " PARAMETER[\"false_easting\",0],\n" + " PARAMETER[\"false_northing\",0],\n" + " UNIT[\"metre\",1],\n" + " AXIS[\"Easting\",EAST],\n" + " AXIS[\"Northing\",NORTH],\n" + " EXTENSION[\"PROJ4\",\"+proj=merc +a=6378137 +b=6378137 +lat_ts=0 " + "+lon_0=0 +x_0=0 +y_0=0 +k=1 +units=m +nadgrids=@null +wktext " + "+no_defs\"]]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, mollweide_export) { + + auto conv = Conversion::createMollweide(PropertyMap(), Angle(1), Length(2), + Length(3)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=moll +lon_0=1 +x_0=2 +y_0=3"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Mollweide\",\n" + " METHOD[\"Mollweide\"],\n" + " PARAMETER[\"Longitude of natural origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"False easting\",2,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",3,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Mollweide\"],\n" + "PARAMETER[\"central_meridian\",1],\n" + "PARAMETER[\"false_easting\",2],\n" + "PARAMETER[\"false_northing\",3]"); +} +// --------------------------------------------------------------------------- + +TEST(operation, nzmg_export) { + auto conv = Conversion::createNewZealandMappingGrid( + PropertyMap(), Angle(1), Angle(2), Length(4), Length(5)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=nzmg +lat_0=1 +lon_0=2 +x_0=4 +y_0=5"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"New Zealand Map Grid\",\n" + " METHOD[\"New Zealand Map Grid\",\n" + " ID[\"EPSG\",9811]],\n" + " PARAMETER[\"Latitude of natural origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8801]],\n" + " PARAMETER[\"Longitude of natural origin\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"False easting\",4,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",5,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"New_Zealand_Map_Grid\"],\n" + "PARAMETER[\"latitude_of_origin\",1],\n" + "PARAMETER[\"central_meridian\",2],\n" + "PARAMETER[\"false_easting\",4],\n" + "PARAMETER[\"false_northing\",5]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, oblique_stereographic_export) { + auto conv = Conversion::createObliqueStereographic( + PropertyMap(), Angle(1), Angle(2), Scale(3), Length(4), Length(5)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=sterea +lat_0=1 +lon_0=2 +k=3 +x_0=4 +y_0=5"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Oblique Stereographic\",\n" + " METHOD[\"Oblique Stereographic\",\n" + " ID[\"EPSG\",9809]],\n" + " PARAMETER[\"Latitude of natural origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8801]],\n" + " PARAMETER[\"Longitude of natural origin\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"Scale factor at natural origin\",3,\n" + " SCALEUNIT[\"unity\",1],\n" + " ID[\"EPSG\",8805]],\n" + " PARAMETER[\"False easting\",4,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",5,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Oblique_Stereographic\"],\n" + "PARAMETER[\"latitude_of_origin\",1],\n" + "PARAMETER[\"central_meridian\",2],\n" + "PARAMETER[\"scale_factor\",3],\n" + "PARAMETER[\"false_easting\",4],\n" + "PARAMETER[\"false_northing\",5]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, orthographic_export) { + auto conv = Conversion::createOrthographic(PropertyMap(), Angle(1), + Angle(2), Length(4), Length(5)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=ortho +lat_0=1 +lon_0=2 +x_0=4 +y_0=5"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Orthographic\",\n" + " METHOD[\"Orthographic\",\n" + " ID[\"EPSG\",9840]],\n" + " PARAMETER[\"Latitude of natural origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8801]],\n" + " PARAMETER[\"Longitude of natural origin\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"False easting\",4,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",5,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Orthographic\"],\n" + "PARAMETER[\"latitude_of_origin\",1],\n" + "PARAMETER[\"central_meridian\",2],\n" + "PARAMETER[\"false_easting\",4],\n" + "PARAMETER[\"false_northing\",5]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, american_polyconic_export) { + auto conv = Conversion::createAmericanPolyconic( + PropertyMap(), Angle(1), Angle(2), Length(4), Length(5)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=poly +lat_0=1 +lon_0=2 +x_0=4 +y_0=5"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"American Polyconic\",\n" + " METHOD[\"American Polyconic\",\n" + " ID[\"EPSG\",9818]],\n" + " PARAMETER[\"Latitude of natural origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8801]],\n" + " PARAMETER[\"Longitude of natural origin\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"False easting\",4,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",5,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Polyconic\"],\n" + "PARAMETER[\"latitude_of_origin\",1],\n" + "PARAMETER[\"central_meridian\",2],\n" + "PARAMETER[\"false_easting\",4],\n" + "PARAMETER[\"false_northing\",5]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, polar_stereographic_variant_A_export) { + auto conv = Conversion::createPolarStereographicVariantA( + PropertyMap(), Angle(90), Angle(2), Scale(3), Length(4), Length(5)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=stere +lat_0=90 +lon_0=2 +k=3 +x_0=4 +y_0=5"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Polar Stereographic (variant A)\",\n" + " METHOD[\"Polar Stereographic (variant A)\",\n" + " ID[\"EPSG\",9810]],\n" + " PARAMETER[\"Latitude of natural origin\",90,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8801]],\n" + " PARAMETER[\"Longitude of natural origin\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"Scale factor at natural origin\",3,\n" + " SCALEUNIT[\"unity\",1],\n" + " ID[\"EPSG\",8805]],\n" + " PARAMETER[\"False easting\",4,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",5,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Polar_Stereographic\"],\n" + "PARAMETER[\"latitude_of_origin\",90],\n" + "PARAMETER[\"central_meridian\",2],\n" + "PARAMETER[\"scale_factor\",3],\n" + "PARAMETER[\"false_easting\",4],\n" + "PARAMETER[\"false_northing\",5]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, polar_stereographic_variant_B_export_positive_lat) { + auto conv = Conversion::createPolarStereographicVariantB( + PropertyMap(), Angle(70), Angle(2), Length(4), Length(5)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=stere +lat_0=90 +lat_ts=70 +lon_0=2 +x_0=4 +y_0=5"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Polar Stereographic (variant B)\",\n" + " METHOD[\"Polar Stereographic (variant B)\",\n" + " ID[\"EPSG\",9829]],\n" + " PARAMETER[\"Latitude of standard parallel\",70,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8832]],\n" + " PARAMETER[\"Longitude of origin\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8833]],\n" + " PARAMETER[\"False easting\",4,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",5,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Polar_Stereographic\"],\n" + "PARAMETER[\"latitude_of_origin\",70],\n" + "PARAMETER[\"central_meridian\",2],\n" + "PARAMETER[\"false_easting\",4],\n" + "PARAMETER[\"false_northing\",5]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, polar_stereographic_variant_B_export_negative_lat) { + auto conv = Conversion::createPolarStereographicVariantB( + PropertyMap(), Angle(-70), Angle(2), Length(4), Length(5)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=stere +lat_0=-90 +lat_ts=-70 +lon_0=2 +x_0=4 +y_0=5"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, wkt1_import_polar_stereographic_variantA) { + auto wkt = "PROJCS[\"test\",\n" + " GEOGCS[\"WGS 84\",\n" + " DATUM[\"WGS 1984\",\n" + " SPHEROID[\"WGS 84\",6378137,298.257223563]],\n" + " PRIMEM[\"Greenwich\",0],\n" + " UNIT[\"degree\",0.0174532925199433]],\n" + " PROJECTION[\"Polar_Stereographic\"],\n" + " PARAMETER[\"latitude_of_origin\",-90],\n" + " PARAMETER[\"central_meridian\",2],\n" + " PARAMETER[\"scale_factor\",3],\n" + " PARAMETER[\"false_easting\",4],\n" + " PARAMETER[\"false_northing\",5]" + " UNIT[\"metre\",1]]"; + auto obj = WKTParser().createFromWKT(wkt); + auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + ASSERT_TRUE(crs != nullptr); + + auto conversion = crs->derivingConversion(); + auto convRef = Conversion::createPolarStereographicVariantA( + PropertyMap().set(IdentifiedObject::NAME_KEY, "unnamed"), Angle(-90), + Angle(2), Scale(3), Length(4), Length(5)); + + EXPECT_EQ(conversion->exportToWKT(WKTFormatter::create().get()), + convRef->exportToWKT(WKTFormatter::create().get())); +} + +// --------------------------------------------------------------------------- + +TEST(operation, wkt1_import_polar_stereographic_variantB) { + auto wkt = "PROJCS[\"test\",\n" + " GEOGCS[\"WGS 84\",\n" + " DATUM[\"WGS 1984\",\n" + " SPHEROID[\"WGS 84\",6378137,298.257223563]],\n" + " PRIMEM[\"Greenwich\",0],\n" + " UNIT[\"degree\",0.0174532925199433]],\n" + " PROJECTION[\"Polar_Stereographic\"],\n" + " PARAMETER[\"latitude_of_origin\",-70],\n" + " PARAMETER[\"central_meridian\",2],\n" + " PARAMETER[\"scale_factor\",1],\n" + " PARAMETER[\"false_easting\",4],\n" + " PARAMETER[\"false_northing\",5]" + " UNIT[\"metre\",1]]"; + auto obj = WKTParser().createFromWKT(wkt); + auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + ASSERT_TRUE(crs != nullptr); + + auto conversion = crs->derivingConversion(); + auto convRef = Conversion::createPolarStereographicVariantB( + PropertyMap().set(IdentifiedObject::NAME_KEY, "unnamed"), Angle(-70), + Angle(2), Length(4), Length(5)); + + EXPECT_EQ(conversion->exportToWKT(WKTFormatter::create().get()), + convRef->exportToWKT(WKTFormatter::create().get())); +} + +// --------------------------------------------------------------------------- + +TEST(operation, wkt1_import_polar_stereographic_ambiguous) { + auto wkt = "PROJCS[\"test\",\n" + " GEOGCS[\"WGS 84\",\n" + " DATUM[\"WGS 1984\",\n" + " SPHEROID[\"WGS 84\",6378137,298.257223563]],\n" + " PRIMEM[\"Greenwich\",0],\n" + " UNIT[\"degree\",0.0174532925199433]],\n" + " PROJECTION[\"Polar_Stereographic\"],\n" + " PARAMETER[\"latitude_of_origin\",-70],\n" + " PARAMETER[\"central_meridian\",2],\n" + " PARAMETER[\"scale_factor\",3],\n" + " PARAMETER[\"false_easting\",4],\n" + " PARAMETER[\"false_northing\",5]" + " UNIT[\"metre\",1]]"; + auto obj = WKTParser().createFromWKT(wkt); + auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + ASSERT_TRUE(crs != nullptr); + + auto conversion = crs->derivingConversion(); + EXPECT_EQ(conversion->method()->nameStr(), "Polar_Stereographic"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, wkt1_import_equivalent_parameters) { + auto wkt = "PROJCS[\"test\",\n" + " GEOGCS[\"WGS 84\",\n" + " DATUM[\"WGS 1984\",\n" + " SPHEROID[\"WGS 84\",6378137,298.257223563]],\n" + " PRIMEM[\"Greenwich\",0],\n" + " UNIT[\"degree\",0.0174532925199433]],\n" + " PROJECTION[\"Hotine Oblique Mercator Two Point Natural " + "Origin\"],\n" + " PARAMETER[\"latitude_of_origin\",1],\n" + " PARAMETER[\"Latitude_Of_1st_Point\",2],\n" + " PARAMETER[\"Longitude_Of_1st_Point\",3],\n" + " PARAMETER[\"Latitude_Of_2nd_Point\",4],\n" + " PARAMETER[\"Longitude_Of 2nd_Point\",5],\n" + " PARAMETER[\"scale_factor\",6],\n" + " PARAMETER[\"false_easting\",7],\n" + " PARAMETER[\"false_northing\",8],\n" + " UNIT[\"metre\",1]]"; + auto obj = WKTParser().createFromWKT(wkt); + auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + ASSERT_TRUE(crs != nullptr); + + auto conversion = crs->derivingConversion(); + auto convRef = Conversion::createHotineObliqueMercatorTwoPointNaturalOrigin( + PropertyMap(), Angle(1), Angle(2), Angle(3), Angle(4), Angle(5), + Scale(6), Length(7), Length(8)); + + EXPECT_EQ( + conversion->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + convRef->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get())); +} + +// --------------------------------------------------------------------------- + +TEST(operation, robinson_export) { + + auto conv = Conversion::createRobinson(PropertyMap(), Angle(1), Length(2), + Length(3)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=robin +lon_0=1 +x_0=2 +y_0=3"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Robinson\",\n" + " METHOD[\"Robinson\"],\n" + " PARAMETER[\"Longitude of natural origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"False easting\",2,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",3,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Robinson\"],\n" + "PARAMETER[\"longitude_of_center\",1],\n" + "PARAMETER[\"false_easting\",2],\n" + "PARAMETER[\"false_northing\",3]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, sinusoidal_export) { + + auto conv = Conversion::createSinusoidal(PropertyMap(), Angle(1), Length(2), + Length(3)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=sinu +lon_0=1 +x_0=2 +y_0=3"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Sinusoidal\",\n" + " METHOD[\"Sinusoidal\"],\n" + " PARAMETER[\"Longitude of natural origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"False easting\",2,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",3,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Sinusoidal\"],\n" + "PARAMETER[\"longitude_of_center\",1],\n" + "PARAMETER[\"false_easting\",2],\n" + "PARAMETER[\"false_northing\",3]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, stereographic_export) { + auto conv = Conversion::createStereographic( + PropertyMap(), Angle(1), Angle(2), Scale(3), Length(4), Length(5)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=stere +lat_0=1 +lon_0=2 +k=3 +x_0=4 +y_0=5"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Stereographic\",\n" + " METHOD[\"Stereographic\"],\n" + " PARAMETER[\"Latitude of natural origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8801]],\n" + " PARAMETER[\"Longitude of natural origin\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"Scale factor at natural origin\",3,\n" + " SCALEUNIT[\"unity\",1],\n" + " ID[\"EPSG\",8805]],\n" + " PARAMETER[\"False easting\",4,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",5,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Stereographic\"],\n" + "PARAMETER[\"latitude_of_origin\",1],\n" + "PARAMETER[\"central_meridian\",2],\n" + "PARAMETER[\"scale_factor\",3],\n" + "PARAMETER[\"false_easting\",4],\n" + "PARAMETER[\"false_northing\",5]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, vandergrinten_export) { + + auto conv = Conversion::createVanDerGrinten(PropertyMap(), Angle(1), + Length(2), Length(3)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=vandg +R_A +lon_0=1 +x_0=2 +y_0=3"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Van Der Grinten\",\n" + " METHOD[\"Van Der Grinten\"],\n" + " PARAMETER[\"Longitude of natural origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"False easting\",2,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",3,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"VanDerGrinten\"],\n" + "PARAMETER[\"central_meridian\",1],\n" + "PARAMETER[\"false_easting\",2],\n" + "PARAMETER[\"false_northing\",3]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, wagner_export) { + + std::vector<std::string> numbers{"", "1", "2", "3", "4", "5", "6", "7"}; + std::vector<std::string> latinNumbers{"", "I", "II", "III", + "IV", "V", "VI", "VII"}; + + for (int i = 1; i <= 7; i++) { + if (i == 3) + continue; + auto conv = + (i == 1) + ? Conversion::createWagnerI(PropertyMap(), Angle(1), Length(2), + Length(3)) + : (i == 2) + ? Conversion::createWagnerII(PropertyMap(), Angle(1), + Length(2), Length(3)) + : (i == 4) + ? Conversion::createWagnerIV( + PropertyMap(), Angle(1), Length(2), Length(3)) + : (i == 5) ? Conversion::createWagnerV( + PropertyMap(), Angle(1), Length(2), + Length(3)) + : (i == 6) ? + + Conversion::createWagnerVI( + PropertyMap(), Angle(1), + Length(2), Length(3)) + : + + Conversion::createWagnerVII( + PropertyMap(), Angle(1), + Length(2), Length(3)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=wag" + numbers[i] + " +lon_0=1 +x_0=2 +y_0=3"); + + auto formatter = WKTFormatter::create(); + formatter->simulCurNodeHasId(); + EXPECT_EQ(conv->exportToWKT(formatter.get()), + "CONVERSION[\"Wagner " + latinNumbers[i] + + "\",\n" + " METHOD[\"Wagner " + + latinNumbers[i] + + "\"],\n" + " PARAMETER[\"Longitude of natural origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"False easting\",2,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",3,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ(conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL) + .get()), + "PROJECTION[\"Wagner_" + latinNumbers[i] + + "\"],\n" + "PARAMETER[\"central_meridian\",1],\n" + "PARAMETER[\"false_easting\",2],\n" + "PARAMETER[\"false_northing\",3]"); + } +} + +// --------------------------------------------------------------------------- + +TEST(operation, wagnerIII_export) { + + auto conv = Conversion::createWagnerIII(PropertyMap(), Angle(1), Angle(2), + Length(3), Length(4)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=wag3 +lat_ts=1 +lon_0=2 +x_0=3 +y_0=4"); + + auto formatter = WKTFormatter::create(); + formatter->simulCurNodeHasId(); + EXPECT_EQ(conv->exportToWKT(formatter.get()), + "CONVERSION[\"Wagner III\",\n" + " METHOD[\"Wagner III\"],\n" + " PARAMETER[\"Latitude of true scale\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " PARAMETER[\"Longitude of natural origin\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"False easting\",3,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",4,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Wagner_III\"],\n" + "PARAMETER[\"latitude_of_origin\",1],\n" + "PARAMETER[\"central_meridian\",2],\n" + "PARAMETER[\"false_easting\",3],\n" + "PARAMETER[\"false_northing\",4]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, qsc_export) { + + auto conv = Conversion::createQuadrilateralizedSphericalCube( + PropertyMap(), Angle(1), Angle(2), Length(3), Length(4)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=qsc +lat_0=1 +lon_0=2 +x_0=3 +y_0=4"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Quadrilateralized Spherical Cube\",\n" + " METHOD[\"Quadrilateralized Spherical Cube\"],\n" + " PARAMETER[\"Latitude of natural origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8801]],\n" + " PARAMETER[\"Longitude of natural origin\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"False easting\",3,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",4,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Quadrilateralized_Spherical_Cube\"],\n" + "PARAMETER[\"latitude_of_origin\",1],\n" + "PARAMETER[\"central_meridian\",2],\n" + "PARAMETER[\"false_easting\",3],\n" + "PARAMETER[\"false_northing\",4]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, sch_export) { + + auto conv = Conversion::createSphericalCrossTrackHeight( + PropertyMap(), Angle(1), Angle(2), Angle(3), Length(4)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=sch +plat_0=1 +plon_0=2 +phdg_0=3 +h_0=4"); + + auto formatter = WKTFormatter::create(); + formatter->simulCurNodeHasId(); + EXPECT_EQ(conv->exportToWKT(formatter.get()), + "CONVERSION[\"Spherical Cross-Track Height\",\n" + " METHOD[\"Spherical Cross-Track Height\"],\n" + " PARAMETER[\"Peg point latitude\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " PARAMETER[\"Peg point longitude\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " PARAMETER[\"Peg point heading\",3,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " PARAMETER[\"Peg point height\",4,\n" + " LENGTHUNIT[\"metre\",1]]]"); + + EXPECT_EQ( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + "PROJECTION[\"Spherical_Cross_Track_Height\"],\n" + "PARAMETER[\"peg_point_latitude\",1],\n" + "PARAMETER[\"peg_point_longitude\",2],\n" + "PARAMETER[\"peg_point_heading\",3],\n" + "PARAMETER[\"peg_point_height\",4]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, conversion_inverse) { + auto conv = Conversion::createTransverseMercator( + PropertyMap(), Angle(1), Angle(2), Scale(3), Length(4), Length(5)); + auto inv = conv->inverse(); + EXPECT_EQ(inv->inverse(), conv); + EXPECT_EQ(inv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Inverse of Transverse Mercator\",\n" + " METHOD[\"Inverse of Transverse Mercator\",\n" + " ID[\"INVERSE(EPSG)\",9807]],\n" + " PARAMETER[\"Latitude of natural origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8801]],\n" + " PARAMETER[\"Longitude of natural origin\",2,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"Scale factor at natural origin\",3,\n" + " SCALEUNIT[\"unity\",1],\n" + " ID[\"EPSG\",8805]],\n" + " PARAMETER[\"False easting\",4,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",5,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); + + EXPECT_EQ(inv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +inv +proj=tmerc +lat_0=1 +lon_0=2 +k_0=3 " + "+x_0=4 +y_0=5"); + + EXPECT_TRUE(inv->isEquivalentTo(inv.get())); + EXPECT_FALSE(inv->isEquivalentTo(createUnrelatedObject().get())); +} + +// --------------------------------------------------------------------------- + +TEST(operation, eqearth_export) { + + auto conv = Conversion::createEqualEarth(PropertyMap(), Angle(1), Length(2), + Length(3)); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=eqearth +lon_0=1 +x_0=2 +y_0=3"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"Equal Earth\",\n" + " METHOD[\"Equal Earth\",\n" + " ID[\"EPSG\",1078]],\n" + " PARAMETER[\"Longitude of natural origin\",1,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"False easting\",2,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",3,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]]"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, laborde_oblique_mercator) { + + // Content of EPSG:29701 "Tananarive (Paris) / Laborde Grid" + auto projString = "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=grad +xy_out=rad +step +inv " + "+proj=longlat +ellps=intl +pm=paris +step +proj=labrd " + "+lat_0=-18.9 +lon_0=44.1 +azi=18.9 +k=0.9995 " + "+x_0=400000 +y_0=800000 +ellps=intl +pm=paris +step " + "+proj=axisswap +order=2,1"; + auto obj = PROJStringParser().createFromPROJString(projString); + auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + ASSERT_TRUE(crs != nullptr); + EXPECT_EQ(crs->exportToPROJString(PROJStringFormatter::create().get()), + projString); +} + +// --------------------------------------------------------------------------- + +TEST(operation, PROJ_based) { + auto conv = SingleOperation::createPROJBased(PropertyMap(), "+proj=merc", + nullptr, nullptr); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=merc"); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"PROJ-based coordinate operation\",\n" + " METHOD[\"PROJ-based operation method\"],\n" + " PARAMETER[\"PROJ string\",\"+proj=merc\"]]"); + + EXPECT_EQ(conv->inverse()->exportToPROJString( + PROJStringFormatter::create().get()), + "+proj=pipeline +step +inv +proj=merc"); + + auto str = "+proj=pipeline +step +proj=unitconvert +xy_in=grad +xy_out=rad " + "+step +proj=axisswap +order=2,1 +step +proj=longlat " + "+ellps=clrk80ign +pm=paris +step +proj=axisswap +order=2,1"; + EXPECT_EQ( + SingleOperation::createPROJBased(PropertyMap(), str, nullptr, nullptr) + ->exportToPROJString(PROJStringFormatter::create().get()), + str); + + EXPECT_THROW(SingleOperation::createPROJBased(PropertyMap(), "+inv", + nullptr, nullptr) + ->exportToPROJString(PROJStringFormatter::create().get()), + FormattingException); + EXPECT_THROW( + SingleOperation::createPROJBased(PropertyMap(), "foo", nullptr, nullptr) + ->exportToPROJString(PROJStringFormatter::create().get()), + FormattingException); +} + +// --------------------------------------------------------------------------- + +TEST(operation, PROJ_based_empty) { + auto conv = + SingleOperation::createPROJBased(PropertyMap(), "", nullptr, nullptr); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + ""); + + EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()), + "CONVERSION[\"PROJ-based coordinate operation\",\n" + " METHOD[\"PROJ-based operation method\"],\n" + " PARAMETER[\"PROJ string\",\"\"]]"); + + EXPECT_THROW( + conv->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()), + FormattingException); + + EXPECT_EQ(conv->inverse()->exportToPROJString( + PROJStringFormatter::create().get()), + ""); +} + +// --------------------------------------------------------------------------- + +TEST(operation, PROJ_based_with_global_parameters) { + auto conv = SingleOperation::createPROJBased( + PropertyMap(), "+proj=pipeline +ellps=WGS84 +step +proj=longlat", + nullptr, nullptr); + + EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +ellps=WGS84 +step +proj=longlat"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, geogCRS_to_geogCRS) { + + auto op = CoordinateOperationFactory::create()->createOperation( + GeographicCRS::EPSG_4807, GeographicCRS::EPSG_4326); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ( + op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=grad +xy_out=rad +step +inv +proj=longlat " + "+ellps=clrk80ign +pm=paris +step +proj=unitconvert +xy_in=rad " + "+xy_out=deg +step +proj=axisswap +order=2,1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, geogCRS_to_geogCRS_context_default) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0); + ctxt->setSpatialCriterion( + CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION); + ctxt->setAllowUseIntermediateCRS(false); + + // Directly found in database + { + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem("4179"), // Pulkovo 42 + authFactory->createCoordinateReferenceSystem("4258"), // ETRS89 + ctxt); + ASSERT_EQ(list.size(), 2); + // Romania has a larger area than Poland (given our approx formula) + EXPECT_EQ(list[0]->getEPSGCode(), 15994); // Romania - 3m + EXPECT_EQ(list[1]->getEPSGCode(), 1644); // Poland - 1m + + EXPECT_EQ( + list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=cart " + "+ellps=krass +step +proj=helmert +x=2.3287 +y=-147.0425 " + "+z=-92.0802 +rx=0.3092483 +ry=-0.32482185 +rz=-0.49729934 " + "+s=5.68906266 +convention=coordinate_frame +step +inv " + "+proj=cart +ellps=GRS80 +step +proj=unitconvert +xy_in=rad " + "+xy_out=deg +step +proj=axisswap +order=2,1"); + } + + // Reverse case + { + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem("4258"), + authFactory->createCoordinateReferenceSystem("4179"), ctxt); + ASSERT_EQ(list.size(), 2); + // Romania has a larger area than Poland (given our approx formula) + EXPECT_EQ(list[0]->nameStr(), + "Inverse of Pulkovo 1942(58) to ETRS89 (4)"); // Romania - 3m + + EXPECT_EQ( + list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=cart " + "+ellps=GRS80 +step +inv +proj=helmert +x=2.3287 " + "+y=-147.0425 +z=-92.0802 +rx=0.3092483 +ry=-0.32482185 " + "+rz=-0.49729934 +s=5.68906266 +convention=coordinate_frame " + "+step +inv +proj=cart +ellps=krass +step +proj=unitconvert " + "+xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1"); + } +} + +// --------------------------------------------------------------------------- + +TEST(operation, geogCRS_to_geogCRS_context_filter_accuracy) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + { + auto ctxt = + CoordinateOperationContext::create(authFactory, nullptr, 1.0); + ctxt->setSpatialCriterion( + CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION); + + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem("4179"), + authFactory->createCoordinateReferenceSystem("4258"), ctxt); + ASSERT_EQ(list.size(), 1); + EXPECT_EQ(list[0]->getEPSGCode(), 1644); // Poland - 1m + } + { + auto ctxt = + CoordinateOperationContext::create(authFactory, nullptr, 0.9); + ctxt->setSpatialCriterion( + CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION); + + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem("4179"), + authFactory->createCoordinateReferenceSystem("4258"), ctxt); + ASSERT_EQ(list.size(), 0); + } +} + +// --------------------------------------------------------------------------- + +TEST(operation, geogCRS_to_geogCRS_context_filter_bbox) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + // INSERT INTO "area" VALUES('EPSG','1197','Romania','Romania - onshore and + // offshore.',43.44,48.27,20.26,31.41,0); + { + auto ctxt = CoordinateOperationContext::create( + authFactory, Extent::createFromBBOX(20.26, 43.44, 31.41, 48.27), + 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem("4179"), + authFactory->createCoordinateReferenceSystem("4258"), ctxt); + ASSERT_EQ(list.size(), 1); + EXPECT_EQ(list[0]->getEPSGCode(), 15994); // Romania - 3m + } + { + auto ctxt = CoordinateOperationContext::create( + authFactory, Extent::createFromBBOX(20.26 + .1, 43.44 + .1, + 31.41 - .1, 48.27 - .1), + 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem("4179"), + authFactory->createCoordinateReferenceSystem("4258"), ctxt); + ASSERT_EQ(list.size(), 1); + EXPECT_EQ(list[0]->getEPSGCode(), 15994); // Romania - 3m + } + { + auto ctxt = CoordinateOperationContext::create( + authFactory, Extent::createFromBBOX(20.26 - .1, 43.44 - .1, + 31.41 + .1, 48.27 + .1), + 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem("4179"), + authFactory->createCoordinateReferenceSystem("4258"), ctxt); + ASSERT_EQ(list.size(), 1); + EXPECT_EQ( + list[0]->exportToPROJString(PROJStringFormatter::create().get()), + ""); + } +} + +// --------------------------------------------------------------------------- + +TEST(operation, geogCRS_to_geogCRS_context_incompatible_area) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem("4267"), // NAD27 + authFactory->createCoordinateReferenceSystem("4258"), // ETRS 89 + ctxt); + ASSERT_EQ(list.size(), 1); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + ""); +} + +// --------------------------------------------------------------------------- + +TEST(operation, geogCRS_to_geogCRS_context_inverse_needed) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + { + auto ctxt = + CoordinateOperationContext::create(authFactory, nullptr, 0.0); + ctxt->setUsePROJAlternativeGridNames(false); + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem("4275"), // NTF + authFactory->createCoordinateReferenceSystem("4258"), // ETRS89 + ctxt); + ASSERT_EQ(list.size(), 3); + EXPECT_EQ( + list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=cart " + "+ellps=clrk80ign +step +proj=helmert +x=-168 +y=-60 +z=320 " + "+step +inv +proj=cart +ellps=GRS80 +step +proj=unitconvert " + "+xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1"); + EXPECT_EQ(list[1]->exportToPROJString( + PROJStringFormatter::create( + PROJStringFormatter::Convention::PROJ_5, + authFactory->databaseContext()) + .get()), + ""); + EXPECT_EQ(list[2]->exportToPROJString( + PROJStringFormatter::create( + PROJStringFormatter::Convention::PROJ_5, + authFactory->databaseContext()) + .get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step " + "+proj=hgridshift +grids=ntf_r93.gsb +step +proj=unitconvert " + "+xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1"); + } + { + auto ctxt = + CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem("4275"), // NTF + authFactory->createCoordinateReferenceSystem("4258"), // ETRS89 + ctxt); + ASSERT_EQ(list.size(), 2); + EXPECT_EQ( + list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step " + "+proj=hgridshift +grids=ntf_r93.gsb +step +proj=unitconvert " + "+xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1"); + } + { + auto ctxt = + CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem("4258"), // ETRS89 + authFactory->createCoordinateReferenceSystem("4275"), // NTF + ctxt); + ASSERT_EQ(list.size(), 2); + EXPECT_EQ( + list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +inv " + "+proj=hgridshift +grids=ntf_r93.gsb +step +proj=unitconvert " + "+xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1"); + } +} + +// --------------------------------------------------------------------------- + +TEST(operation, geogCRS_to_geogCRS_context_ntv1_ntv2_ctable2) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + ctxt->setSpatialCriterion( + CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION); + ctxt->setGridAvailabilityUse( + CoordinateOperationContext::GridAvailabilityUse:: + IGNORE_GRID_AVAILABILITY); + + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem("4267"), // NAD27 + authFactory->createCoordinateReferenceSystem("4269"), // NAD83 + ctxt); + ASSERT_EQ(list.size(), 6); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=hgridshift " + "+grids=ntv1_can.dat +step +proj=unitconvert +xy_in=rad " + "+xy_out=deg +step +proj=axisswap +order=2,1"); + EXPECT_EQ(list[1]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=hgridshift " + "+grids=ntv2_0.gsb +step +proj=unitconvert +xy_in=rad " + "+xy_out=deg +step +proj=axisswap +order=2,1"); + EXPECT_EQ(list[2]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=hgridshift " + "+grids=conus +step +proj=unitconvert +xy_in=rad +xy_out=deg " + "+step +proj=axisswap +order=2,1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, vertCRS_to_geogCRS_context) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + { + auto ctxt = + CoordinateOperationContext::create(authFactory, nullptr, 0.0); + ctxt->setUsePROJAlternativeGridNames(false); + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem( + "3855"), // EGM2008 height + authFactory->createCoordinateReferenceSystem("4979"), // WGS 84 + ctxt); + ASSERT_EQ(list.size(), 2); + EXPECT_EQ(list[1]->exportToPROJString( + PROJStringFormatter::create( + PROJStringFormatter::Convention::PROJ_5, + authFactory->databaseContext()) + .get()), + "+proj=vgridshift +grids=egm08_25.gtx"); + } + { + auto ctxt = + CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem( + "3855"), // EGM2008 height + authFactory->createCoordinateReferenceSystem("4979"), // WGS 84 + ctxt); + ASSERT_EQ(list.size(), 2); + EXPECT_EQ( + list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=vgridshift +grids=egm08_25.gtx"); + } + { + auto ctxt = + CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem("4979"), // WGS 84 + authFactory->createCoordinateReferenceSystem( + "3855"), // EGM2008 height + ctxt); + ASSERT_EQ(list.size(), 2); + EXPECT_EQ( + list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +inv +proj=vgridshift +grids=egm08_25.gtx"); + } +} + +// --------------------------------------------------------------------------- + +TEST(operation, geogCRS_to_geogCRS_noop) { + + auto op = CoordinateOperationFactory::create()->createOperation( + GeographicCRS::EPSG_4326, GeographicCRS::EPSG_4326); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->nameStr(), "Null geographic offset from WGS 84 to WGS 84"); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), ""); + EXPECT_EQ(op->inverse()->nameStr(), op->nameStr()); +} + +// --------------------------------------------------------------------------- + +TEST(operation, geogCRS_to_geogCRS_longitude_rotation) { + + auto src = GeographicCRS::create( + PropertyMap().set(IdentifiedObject::NAME_KEY, "A"), + GeodeticReferenceFrame::create(PropertyMap(), Ellipsoid::WGS84, + optional<std::string>(), + PrimeMeridian::GREENWICH), + EllipsoidalCS::createLatitudeLongitude(UnitOfMeasure::DEGREE)); + auto dest = GeographicCRS::create( + PropertyMap().set(IdentifiedObject::NAME_KEY, "B"), + GeodeticReferenceFrame::create(PropertyMap(), Ellipsoid::WGS84, + optional<std::string>(), + PrimeMeridian::PARIS), + EllipsoidalCS::createLatitudeLongitude(UnitOfMeasure::DEGREE)); + + auto op = CoordinateOperationFactory::create()->createOperation(src, dest); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=longlat " + "+ellps=WGS84 +pm=paris +step +proj=unitconvert +xy_in=rad " + "+xy_out=deg +step +proj=axisswap +order=2,1"); + EXPECT_EQ(op->inverse()->exportToWKT(WKTFormatter::create().get()), + CoordinateOperationFactory::create() + ->createOperation(dest, src) + ->exportToWKT(WKTFormatter::create().get())); + EXPECT_TRUE( + op->inverse()->isEquivalentTo(CoordinateOperationFactory::create() + ->createOperation(dest, src) + .get())); +} + +// --------------------------------------------------------------------------- + +TEST(operation, geogCRS_to_geogCRS_longitude_rotation_context) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem("4807"), // NTF(Paris) + authFactory->createCoordinateReferenceSystem("4275"), // NTF + ctxt); + ASSERT_EQ(list.size(), 2); + EXPECT_EQ(list[0]->nameStr(), "NTF (Paris) to NTF (1)"); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=grad +xy_out=rad +step +inv " + "+proj=longlat +ellps=clrk80ign +pm=paris +step " + "+proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap " + "+order=2,1"); + EXPECT_EQ(list[1]->nameStr(), "NTF (Paris) to NTF (2)"); + EXPECT_EQ(list[1]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=grad +xy_out=rad +step +inv " + "+proj=longlat +ellps=clrk80ign +pm=2.33720833333333 +step " + "+proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap " + "+order=2,1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, geogCRS_to_geogCRS_context_concatenated_operation) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem("4807"), // NTF(Paris) + authFactory->createCoordinateReferenceSystem("4171"), // RGF93 + ctxt); + ASSERT_EQ(list.size(), 3); + EXPECT_EQ(list[0]->nameStr(), "NTF (Paris) to RGF93 (2)"); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=grad +xy_out=rad +step +inv " + "+proj=longlat +ellps=clrk80ign +pm=paris +step +proj=hgridshift " + "+grids=ntf_r93.gsb +step +proj=unitconvert +xy_in=rad " + "+xy_out=deg +step +proj=axisswap +order=2,1"); + + EXPECT_TRUE(nn_dynamic_pointer_cast<ConcatenatedOperation>(list[0]) != + nullptr); + auto grids = list[0]->gridsNeeded(DatabaseContext::create()); + EXPECT_EQ(grids.size(), 1); +} + +// --------------------------------------------------------------------------- + +TEST(operation, geogCRS_to_geogCRS_context_same_grid_name) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem("4314"), // DHDN + authFactory->createCoordinateReferenceSystem("4258"), // ETRS89 + ctxt); + ASSERT_TRUE(!list.empty()); + EXPECT_EQ(list[0]->nameStr(), "DHDN to ETRS89 (8)"); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=hgridshift " + "+grids=BETA2007.gsb +step +proj=unitconvert +xy_in=rad " + "+xy_out=deg +step +proj=axisswap +order=2,1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, geogCRS_to_geogCRS_geographic_offset_context) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem("4120"), // NTF(Paris) + authFactory->createCoordinateReferenceSystem("4121"), // NTF + ctxt); + ASSERT_EQ(list.size(), 1); + EXPECT_EQ(list[0]->nameStr(), "Greek to GGRS87 (1)"); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=geogoffset " + "+dlat=-5.86 +dlon=0.28 +step +proj=unitconvert +xy_in=rad " + "+xy_out=deg +step +proj=axisswap +order=2,1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, geogCRS_to_geogCRS_CH1903_to_CH1903plus_context) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem("4149"), // CH1903 + authFactory->createCoordinateReferenceSystem("4150"), // CH1903+ + ctxt); + ASSERT_EQ(list.size(), 2); + + EXPECT_EQ(list[0]->nameStr(), + "CH1903 to ETRS89 (1) + Inverse of CH1903+ to ETRS89 (1)"); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + ""); + + EXPECT_EQ(list[1]->nameStr(), "CH1903 to CH1903+ (1)"); + EXPECT_EQ(list[1]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 " + "+step +proj=unitconvert +xy_in=deg +xy_out=rad " + "+step +proj=hgridshift +grids=CHENyx06a.gsb " + "+step +proj=unitconvert +xy_in=rad +xy_out=deg " + "+step +proj=axisswap +order=2,1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, geogCRS_to_geogCRS_3D) { + + auto geogcrs_m_obj = + PROJStringParser().createFromPROJString("+proj=longlat +vunits=m"); + auto geogcrs_m = nn_dynamic_pointer_cast<CRS>(geogcrs_m_obj); + ASSERT_TRUE(geogcrs_m != nullptr); + + auto geogcrs_ft_obj = + PROJStringParser().createFromPROJString("+proj=longlat +vunits=ft"); + auto geogcrs_ft = nn_dynamic_pointer_cast<CRS>(geogcrs_ft_obj); + ASSERT_TRUE(geogcrs_ft != nullptr); + + { + auto op = CoordinateOperationFactory::create()->createOperation( + NN_CHECK_ASSERT(geogcrs_m), NN_CHECK_ASSERT(geogcrs_ft)); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=unitconvert +z_in=m +z_out=ft"); + } + + { + auto op = CoordinateOperationFactory::create()->createOperation( + NN_CHECK_ASSERT(geogcrs_ft), NN_CHECK_ASSERT(geogcrs_m)); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=unitconvert +z_in=ft +z_out=m"); + } + + auto geogcrs_m_with_pm_obj = PROJStringParser().createFromPROJString( + "+proj=longlat +pm=paris +vunits=m"); + auto geogcrs_m_with_pm = + nn_dynamic_pointer_cast<CRS>(geogcrs_m_with_pm_obj); + ASSERT_TRUE(geogcrs_m_with_pm != nullptr); + + { + auto op = CoordinateOperationFactory::create()->createOperation( + NN_CHECK_ASSERT(geogcrs_m_with_pm), NN_CHECK_ASSERT(geogcrs_ft)); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=unitconvert +xy_in=deg +z_in=m " + "+xy_out=rad +z_out=m +step +inv +proj=longlat +ellps=WGS84 " + "+pm=paris +step +proj=unitconvert +xy_in=rad +z_in=m " + "+xy_out=deg +z_out=ft"); + } +} + +// --------------------------------------------------------------------------- + +TEST(operation, geocentricCRS_to_geogCRS_same_datum) { + + auto op = CoordinateOperationFactory::create()->createOperation( + createGeocentricDatumWGS84(), GeographicCRS::EPSG_4326); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +inv +proj=cart +ellps=WGS84 +step " + "+proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap " + "+order=2,1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, geocentricCRS_to_geogCRS_different_datum) { + + auto op = CoordinateOperationFactory::create()->createOperation( + createGeocentricDatumWGS84(), GeographicCRS::EPSG_4269); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->nameStr(), "Null geocentric translation from WGS 84 to NAD83 " + "(geocentric) + Conversion from NAD83 " + "(geocentric) to NAD83"); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +inv +proj=cart +ellps=GRS80 +step " + "+proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap " + "+order=2,1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, geogCRS_to_geocentricCRS_different_datum) { + + auto op = CoordinateOperationFactory::create()->createOperation( + GeographicCRS::EPSG_4269, createGeocentricDatumWGS84()); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->nameStr(), "Conversion from NAD83 to NAD83 (geocentric) + " + "Null geocentric translation from NAD83 " + "(geocentric) to WGS 84"); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=cart " + "+ellps=GRS80"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, geocentricCRS_to_geocentricCRS_noop) { + + auto op = CoordinateOperationFactory::create()->createOperation( + createGeocentricDatumWGS84(), createGeocentricDatumWGS84()); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->nameStr(), + "Null geocentric translation from WGS 84 to WGS 84"); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), ""); + EXPECT_EQ(op->inverse()->nameStr(), op->nameStr()); +} + +// --------------------------------------------------------------------------- + +TEST(operation, geocentricCRS_to_geogCRS_same_datum_context) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem("4326"), + // WGS84 geocentric + authFactory->createCoordinateReferenceSystem("4978"), ctxt); + ASSERT_EQ(list.size(), 1); + + EXPECT_EQ(list[0]->nameStr(), + "Conversion from WGS 84 (geog2D) to WGS 84 (geocentric)"); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=cart " + "+ellps=WGS84"); + + EXPECT_EQ(list[0]->inverse()->nameStr(), + "Conversion from WGS 84 (geocentric) to WGS 84 (geog2D)"); + EXPECT_EQ(list[0]->inverse()->exportToPROJString( + PROJStringFormatter::create().get()), + "+proj=pipeline +step +inv +proj=cart +ellps=WGS84 +step " + "+proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap " + "+order=2,1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, geocentricCRS_to_geogCRS_same_datum_context_all_auth) { + // This is to check we don't use OGC:CRS84 as a pivot + auto authFactoryEPSG = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto authFactoryAll = + AuthorityFactory::create(DatabaseContext::create(), std::string()); + auto ctxt = + CoordinateOperationContext::create(authFactoryAll, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + authFactoryEPSG->createCoordinateReferenceSystem("4326"), + // WGS84 geocentric + authFactoryEPSG->createCoordinateReferenceSystem("4978"), ctxt); + ASSERT_EQ(list.size(), 1); + + EXPECT_EQ(list[0]->nameStr(), + "Conversion from WGS 84 (geog2D) to WGS 84 (geocentric)"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, geocentricCRS_to_geocentricCRS_different_datum_context) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + // ITRF2000 (geocentric) + authFactory->createCoordinateReferenceSystem("4919"), + // ITRF2005 (geocentric) + authFactory->createCoordinateReferenceSystem("4896"), ctxt); + ASSERT_EQ(list.size(), 1); + EXPECT_EQ(list[0]->nameStr(), "ITRF2000 to ITRF2005 (1)"); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=helmert +x=-0.0001 " + "+y=0.0008 +z=0.0058 +rx=0 +ry=0 +rz=0 +s=-0.0004 +dx=0.0002 " + "+dy=-0.0001 +dz=0.0018 +drx=0 +dry=0 +drz=0 +ds=-8e-05 " + "+t_epoch=2000 +convention=position_vector"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, geogCRS_geocentricCRS_same_datum_to_context) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + // WGS84 geocentric + authFactory->createCoordinateReferenceSystem("4978"), + authFactory->createCoordinateReferenceSystem("4326"), ctxt); + ASSERT_EQ(list.size(), 1); + EXPECT_EQ(list[0]->nameStr(), + "Conversion from WGS 84 (geocentric) to WGS 84 (geog2D)"); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +inv +proj=cart +ellps=WGS84 +step " + "+proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap " + "+order=2,1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, + geogCRS_to_geogCRS_different_datum_though_geocentric_transform_context) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + // ITRF2000 (geog3D) + authFactory->createCoordinateReferenceSystem("7909"), + // ITRF2005 (geog3D) + authFactory->createCoordinateReferenceSystem("7910"), ctxt); + ASSERT_EQ(list.size(), 1); + EXPECT_EQ(list[0]->nameStr(), + "Conversion from ITRF2000 (geog3D) to ITRF2000 (geocentric) + " + "ITRF2000 to ITRF2005 (1) + " + "Conversion from ITRF2005 (geocentric) to ITRF2005 (geog3D)"); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +z_in=m +xy_out=rad +z_out=m " + "+step +proj=cart +ellps=GRS80 +step +proj=helmert +x=-0.0001 " + "+y=0.0008 +z=0.0058 +rx=0 +ry=0 +rz=0 +s=-0.0004 +dx=0.0002 " + "+dy=-0.0001 +dz=0.0018 +drx=0 +dry=0 +drz=0 +ds=-8e-05 " + "+t_epoch=2000 +convention=position_vector +step +inv " + "+proj=cart +ellps=GRS80 +step +proj=unitconvert +xy_in=rad " + "+z_in=m +xy_out=deg +z_out=m +step +proj=axisswap +order=2,1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, geogCRS_to_geocentricCRS_different_datum_context) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + // ITRF2000 (geog3D) + authFactory->createCoordinateReferenceSystem("7909"), + // ITRF2005 (geocentric) + authFactory->createCoordinateReferenceSystem("4896"), ctxt); + ASSERT_EQ(list.size(), 1); + EXPECT_EQ(list[0]->nameStr(), + "Conversion from ITRF2000 (geog3D) to ITRF2000 (geocentric) + " + "ITRF2000 to ITRF2005 (1)"); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +z_in=m +xy_out=rad +z_out=m " + "+step +proj=cart +ellps=GRS80 +step +proj=helmert +x=-0.0001 " + "+y=0.0008 +z=0.0058 +rx=0 +ry=0 +rz=0 +s=-0.0004 +dx=0.0002 " + "+dy=-0.0001 +dz=0.0018 +drx=0 +dry=0 +drz=0 +ds=-8e-05 " + "+t_epoch=2000 +convention=position_vector"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, geocentricCRS_to_geogCRS_different_datum_context) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + // ITRF2000 (geocentric) + authFactory->createCoordinateReferenceSystem("4919"), + // ITRF2005 (geog3D) + authFactory->createCoordinateReferenceSystem("7910"), ctxt); + ASSERT_EQ(list.size(), 1); + EXPECT_EQ(list[0]->nameStr(), + "ITRF2000 to ITRF2005 (1) + " + "Conversion from ITRF2005 (geocentric) to ITRF2005 (geog3D)"); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=helmert +x=-0.0001 " + "+y=0.0008 +z=0.0058 +rx=0 +ry=0 +rz=0 +s=-0.0004 +dx=0.0002 " + "+dy=-0.0001 +dz=0.0018 +drx=0 +dry=0 +drz=0 +ds=-8e-05 " + "+t_epoch=2000 +convention=position_vector +step +inv " + "+proj=cart +ellps=GRS80 +step +proj=unitconvert +xy_in=rad " + "+z_in=m +xy_out=deg +z_out=m +step +proj=axisswap +order=2,1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, esri_projectedCRS_to_geogCRS_with_ITRF_intermediate_context) { + auto dbContext = DatabaseContext::create(); + auto authFactoryEPSG = AuthorityFactory::create(dbContext, "EPSG"); + auto authFactoryESRI = AuthorityFactory::create(dbContext, "ESRI"); + auto ctxt = + CoordinateOperationContext::create(authFactoryEPSG, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + // NAD_1983_CORS96_StatePlane_North_Carolina_FIPS_3200_Ft_US (projected) + authFactoryESRI->createCoordinateReferenceSystem("103501"), + // ITRF2005 (geog3D) + authFactoryEPSG->createCoordinateReferenceSystem("7910"), ctxt); + ASSERT_EQ(list.size(), 1); + EXPECT_EQ(list[0]->nameStr(), + "Inverse of NAD_1983_CORS96_StatePlane_North_Carolina_" + "FIPS_3200_Ft_US + " + "Conversion from NAD83(CORS96) (geog2D) to NAD83(CORS96) " + "(geocentric) + Inverse of ITRF2000 to NAD83(CORS96) (1) + " + "ITRF2000 to ITRF2005 (1) + " + "Conversion from ITRF2005 (geocentric) to ITRF2005 (geog3D)"); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=unitconvert +xy_in=us-ft +z_in=us-ft " + "+xy_out=m +z_out=m +step +inv +proj=lcc +lat_0=33.75 +lon_0=-79 " + "+lat_1=34.3333333333333 +lat_2=36.1666666666667 " + "+x_0=609601.219202438 +y_0=0 +ellps=GRS80 +step +proj=cart " + "+ellps=GRS80 +step +inv +proj=helmert +x=0.9956 +y=-1.9013 " + "+z=-0.5215 +rx=0.025915 +ry=0.009426 +rz=0.011599 +s=0.00062 " + "+dx=0.0007 +dy=-0.0007 +dz=0.0005 +drx=6.7e-05 +dry=-0.000757 " + "+drz=-5.1e-05 +ds=-0.00018 +t_epoch=1997 " + "+convention=coordinate_frame +step +proj=helmert +x=-0.0001 " + "+y=0.0008 +z=0.0058 +rx=0 +ry=0 +rz=0 +s=-0.0004 +dx=0.0002 " + "+dy=-0.0001 +dz=0.0018 +drx=0 +dry=0 +drz=0 +ds=-8e-05 " + "+t_epoch=2000 +convention=position_vector +step +inv +proj=cart " + "+ellps=GRS80 +step +proj=unitconvert +xy_in=rad +z_in=m " + "+xy_out=deg +z_out=m +step +proj=axisswap +order=2,1"); +} + +// --------------------------------------------------------------------------- + +static ProjectedCRSNNPtr createUTM31_WGS84() { + return ProjectedCRS::create( + PropertyMap(), GeographicCRS::EPSG_4326, + Conversion::createUTM(PropertyMap(), 31, true), + CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)); +} + +// --------------------------------------------------------------------------- + +static ProjectedCRSNNPtr createUTM32_WGS84() { + return ProjectedCRS::create( + PropertyMap(), GeographicCRS::EPSG_4326, + Conversion::createUTM(PropertyMap(), 32, true), + CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)); +} + +// --------------------------------------------------------------------------- + +TEST(operation, geogCRS_to_projCRS) { + + auto op = CoordinateOperationFactory::create()->createOperation( + GeographicCRS::EPSG_4326, createUTM31_WGS84()); + ASSERT_TRUE(op != nullptr); + EXPECT_TRUE(std::dynamic_pointer_cast<Conversion>(op) != nullptr); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=utm " + "+zone=31 +ellps=WGS84"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, geogCRS_longlat_to_geogCS_latlong) { + + auto sourceCRS = GeographicCRS::OGC_CRS84; + auto targetCRS = GeographicCRS::EPSG_4326; + auto op = CoordinateOperationFactory::create()->createOperation(sourceCRS, + targetCRS); + ASSERT_TRUE(op != nullptr); + auto conv = std::dynamic_pointer_cast<Conversion>(op); + ASSERT_TRUE(conv != nullptr); + EXPECT_TRUE(op->sourceCRS() && + op->sourceCRS()->isEquivalentTo(sourceCRS.get())); + EXPECT_TRUE(op->targetCRS() && + op->targetCRS()->isEquivalentTo(targetCRS.get())); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=axisswap +order=2,1"); + auto convInverse = nn_dynamic_pointer_cast<Conversion>(conv->inverse()); + ASSERT_TRUE(convInverse != nullptr); + EXPECT_TRUE(convInverse->sourceCRS() && + convInverse->sourceCRS()->isEquivalentTo(targetCRS.get())); + EXPECT_TRUE(convInverse->targetCRS() && + convInverse->targetCRS()->isEquivalentTo(sourceCRS.get())); + EXPECT_EQ(conv->method()->exportToWKT(WKTFormatter::create().get()), + convInverse->method()->exportToWKT(WKTFormatter::create().get())); + EXPECT_TRUE(conv->method()->isEquivalentTo(convInverse->method().get())); +} + +// --------------------------------------------------------------------------- + +TEST(operation, geogCRS_longlat_to_geogCS_latlong_database) { + + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), std::string()); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + AuthorityFactory::create(DatabaseContext::create(), "OGC") + ->createCoordinateReferenceSystem("CRS84"), + AuthorityFactory::create(DatabaseContext::create(), "EPSG") + ->createCoordinateReferenceSystem("4326"), + ctxt); + ASSERT_EQ(list.size(), 1); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=axisswap +order=2,1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, geogCRS_longlat_to_projCRS) { + + auto op = CoordinateOperationFactory::create()->createOperation( + GeographicCRS::OGC_CRS84, createUTM31_WGS84()); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad " + "+step +proj=utm +zone=31 +ellps=WGS84"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, geogCRS_different_from_baseCRS_to_projCRS) { + + auto op = CoordinateOperationFactory::create()->createOperation( + GeographicCRS::EPSG_4807, createUTM31_WGS84()); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ( + op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=grad +xy_out=rad +step +inv +proj=longlat " + "+ellps=clrk80ign +pm=paris +step +proj=utm +zone=31 " + "+ellps=WGS84"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, + geogCRS_different_from_baseCRS_to_projCRS_context_compatible_area) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem("4807"), // NTF(Paris) + authFactory->createCoordinateReferenceSystem("32631"), // UTM31 WGS84 + ctxt); + ASSERT_EQ(list.size(), 4); + EXPECT_EQ( + list[0]->nameStr(), + "NTF (Paris) to NTF (1) + Inverse of WGS 84 to NTF (3) + UTM zone 31N"); + ASSERT_EQ(list[0]->coordinateOperationAccuracies().size(), 1); + EXPECT_EQ(list[0]->coordinateOperationAccuracies()[0]->value(), "1"); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=grad +xy_out=rad +step +inv " + "+proj=longlat +ellps=clrk80ign +pm=paris +step +proj=hgridshift " + "+grids=ntf_r93.gsb +step +proj=utm +zone=31 +ellps=WGS84"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, geocentricCRS_to_projCRS) { + + auto op = CoordinateOperationFactory::create()->createOperation( + createGeocentricDatumWGS84(), createUTM31_WGS84()); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +inv +proj=cart +ellps=WGS84 +step " + "+proj=utm +zone=31 +ellps=WGS84"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, projCRS_to_geogCRS) { + + auto op = CoordinateOperationFactory::create()->createOperation( + createUTM31_WGS84(), GeographicCRS::EPSG_4326); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +inv +proj=utm +zone=31 +ellps=WGS84 +step " + "+proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap " + "+order=2,1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, projCRS_to_projCRS) { + + auto op = CoordinateOperationFactory::create()->createOperation( + createUTM31_WGS84(), createUTM32_WGS84()); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +inv +proj=utm +zone=31 +ellps=WGS84 +step " + "+proj=utm +zone=32 +ellps=WGS84"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, projCRS_to_projCRS_different_baseCRS) { + + auto utm32 = ProjectedCRS::create( + PropertyMap(), GeographicCRS::EPSG_4807, + Conversion::createUTM(PropertyMap(), 32, true), + CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)); + + auto op = CoordinateOperationFactory::create()->createOperation( + createUTM31_WGS84(), utm32); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +inv +proj=utm +zone=31 +ellps=WGS84 +step " + "+proj=utm +zone=32 +ellps=clrk80ign +pm=paris"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, projCRS_to_projCRS_context_compatible_area) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem("32634"), // UTM 34 + authFactory->createCoordinateReferenceSystem( + "2171"), // Pulkovo 42 Poland I + ctxt); + ASSERT_EQ(list.size(), 1); + EXPECT_EQ(list[0]->nameStr(), + "Inverse of UTM zone 34N + Inverse of Pulkovo 1942(58) to WGS 84 " + "(1) + Poland zone I"); + ASSERT_EQ(list[0]->coordinateOperationAccuracies().size(), 1); + EXPECT_EQ(list[0]->coordinateOperationAccuracies()[0]->value(), "1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, projCRS_to_projCRS_context_compatible_area_bis) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem( + "3844"), // Pulkovo 42 Stereo 70 (Romania) + authFactory->createCoordinateReferenceSystem("32634"), // UTM 34 + ctxt); + ASSERT_EQ(list.size(), 1); + EXPECT_EQ(list[0]->nameStr(), "Inverse of Stereo 70 + " + "Pulkovo 1942(58) to WGS 84 " + "(19) + UTM zone 34N"); + ASSERT_EQ(list[0]->coordinateOperationAccuracies().size(), 1); + EXPECT_EQ(list[0]->coordinateOperationAccuracies()[0]->value(), "3"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, projCRS_to_projCRS_context_one_incompatible_area) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem("32631"), // UTM 31 + authFactory->createCoordinateReferenceSystem( + "2171"), // Pulkovo 42 Poland I + ctxt); + ASSERT_EQ(list.size(), 1); + EXPECT_EQ(list[0]->nameStr(), + "Inverse of UTM zone 31N + Inverse of Pulkovo 1942(58) to WGS 84 " + "(1) + Poland zone I"); + ASSERT_EQ(list[0]->coordinateOperationAccuracies().size(), 1); + EXPECT_EQ(list[0]->coordinateOperationAccuracies()[0]->value(), "1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, projCRS_to_projCRS_context_incompatible_areas) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem("32631"), // UTM 31 + authFactory->createCoordinateReferenceSystem("32633"), // UTM 33 + ctxt); + ASSERT_EQ(list.size(), 1); + EXPECT_EQ(list[0]->nameStr(), "Inverse of UTM zone 31N + UTM zone 33N"); + ASSERT_EQ(list[0]->coordinateOperationAccuracies().size(), 1); + EXPECT_EQ(list[0]->coordinateOperationAccuracies()[0]->value(), "0"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, projCRS_to_projCRS_north_pole_inverted_axis) { + + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), std::string()); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + AuthorityFactory::create(DatabaseContext::create(), "EPSG") + ->createCoordinateReferenceSystem("32661"), + AuthorityFactory::create(DatabaseContext::create(), "EPSG") + ->createCoordinateReferenceSystem("5041"), + ctxt); + ASSERT_EQ(list.size(), 1); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=axisswap +order=2,1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, projCRS_to_projCRS_south_pole_inverted_axis) { + + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), std::string()); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + AuthorityFactory::create(DatabaseContext::create(), "EPSG") + ->createCoordinateReferenceSystem("32761"), + AuthorityFactory::create(DatabaseContext::create(), "EPSG") + ->createCoordinateReferenceSystem("5042"), + ctxt); + ASSERT_EQ(list.size(), 1); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=axisswap +order=2,1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, boundCRS_of_geogCRS_to_geogCRS) { + auto boundCRS = BoundCRS::createFromTOWGS84( + GeographicCRS::EPSG_4807, std::vector<double>{1, 2, 3, 4, 5, 6, 7}); + auto op = CoordinateOperationFactory::create()->createOperation( + boundCRS, GeographicCRS::EPSG_4326); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ( + op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=grad +xy_out=rad +step +inv +proj=longlat " + "+ellps=clrk80ign +pm=paris +step +proj=cart +ellps=clrk80ign " + "+step +proj=helmert +x=1 +y=2 +z=3 +rx=4 +ry=5 +rz=6 +s=7 " + "+convention=position_vector +step " + "+inv +proj=cart +ellps=WGS84 +step +proj=unitconvert +xy_in=rad " + "+xy_out=deg +step +proj=axisswap +order=2,1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, boundCRS_of_geogCRS_to_unrelated_geogCRS) { + auto boundCRS = BoundCRS::createFromTOWGS84( + GeographicCRS::EPSG_4807, std::vector<double>{1, 2, 3, 4, 5, 6, 7}); + auto op = CoordinateOperationFactory::create()->createOperation( + boundCRS, GeographicCRS::EPSG_4269); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + CoordinateOperationFactory::create() + ->createOperation(GeographicCRS::EPSG_4807, + GeographicCRS::EPSG_4269) + ->exportToPROJString(PROJStringFormatter::create().get())); +} + +// --------------------------------------------------------------------------- + +TEST(operation, boundCRS_of_projCRS_to_geogCRS) { + auto utm31 = ProjectedCRS::create( + PropertyMap(), GeographicCRS::EPSG_4807, + Conversion::createUTM(PropertyMap(), 31, true), + CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)); + auto boundCRS = BoundCRS::createFromTOWGS84( + utm31, std::vector<double>{1, 2, 3, 4, 5, 6, 7}); + auto op = CoordinateOperationFactory::create()->createOperation( + boundCRS, GeographicCRS::EPSG_4326); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +inv +proj=utm +zone=31 +ellps=clrk80ign " + "+pm=paris " + "+step +proj=cart +ellps=clrk80ign +step +proj=helmert +x=1 +y=2 " + "+z=3 +rx=4 +ry=5 +rz=6 +s=7 +convention=position_vector +step " + "+inv +proj=cart +ellps=WGS84 " + "+step +proj=unitconvert +xy_in=rad +xy_out=deg +step " + "+proj=axisswap +order=2,1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, boundCRS_of_geogCRS_to_projCRS) { + auto boundCRS = BoundCRS::createFromTOWGS84( + GeographicCRS::EPSG_4807, std::vector<double>{1, 2, 3, 4, 5, 6, 7}); + auto utm31 = ProjectedCRS::create( + PropertyMap(), GeographicCRS::EPSG_4326, + Conversion::createUTM(PropertyMap(), 31, true), + CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)); + auto op = + CoordinateOperationFactory::create()->createOperation(boundCRS, utm31); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ( + op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=grad +xy_out=rad +step +inv +proj=longlat " + "+ellps=clrk80ign +pm=paris +step +proj=cart +ellps=clrk80ign " + "+step +proj=helmert +x=1 +y=2 +z=3 +rx=4 +ry=5 +rz=6 +s=7 " + "+convention=position_vector +step " + "+inv +proj=cart +ellps=WGS84 +step +proj=utm +zone=31 " + "+ellps=WGS84"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, geogCRS_to_boundCRS_of_geogCRS) { + auto boundCRS = BoundCRS::createFromTOWGS84( + GeographicCRS::EPSG_4807, std::vector<double>{1, 2, 3, 4, 5, 6, 7}); + auto op = CoordinateOperationFactory::create()->createOperation( + GeographicCRS::EPSG_4326, boundCRS); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=cart " + "+ellps=WGS84 +step +inv +proj=helmert +x=1 +y=2 +z=3 +rx=4 " + "+ry=5 +rz=6 +s=7 +convention=position_vector +step +inv " + "+proj=cart +ellps=clrk80ign +step +proj=longlat " + "+ellps=clrk80ign +pm=paris +step +proj=unitconvert +xy_in=rad " + "+xy_out=grad +step +proj=axisswap +order=2,1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, boundCRS_to_boundCRS) { + auto utm31 = ProjectedCRS::create( + PropertyMap(), GeographicCRS::EPSG_4807, + Conversion::createUTM(PropertyMap(), 31, true), + CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)); + auto utm32 = ProjectedCRS::create( + PropertyMap(), GeographicCRS::EPSG_4269, + Conversion::createUTM(PropertyMap(), 32, true), + CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)); + auto boundCRS1 = BoundCRS::createFromTOWGS84( + utm31, std::vector<double>{1, 2, 3, 4, 5, 6, 7}); + auto boundCRS2 = BoundCRS::createFromTOWGS84( + utm32, std::vector<double>{8, 9, 10, 11, 12, 13, 14}); + auto op = CoordinateOperationFactory::create()->createOperation(boundCRS1, + boundCRS2); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +inv +proj=utm +zone=31 +ellps=clrk80ign " + "+pm=paris " + "+step +proj=cart +ellps=clrk80ign +step +proj=helmert +x=1 +y=2 " + "+z=3 +rx=4 +ry=5 +rz=6 +s=7 +convention=position_vector +step " + "+inv +proj=helmert +x=8 +y=9 +z=10 +rx=11 +ry=12 +rz=13 +s=14 " + "+convention=position_vector +step +inv +proj=cart +ellps=GRS80 " + "+step +proj=utm +zone=32 +ellps=GRS80"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, boundCRS_to_boundCRS_noop_for_TOWGS84) { + auto boundCRS1 = BoundCRS::createFromTOWGS84( + GeographicCRS::EPSG_4807, std::vector<double>{1, 2, 3, 4, 5, 6, 7}); + auto boundCRS2 = BoundCRS::createFromTOWGS84( + GeographicCRS::EPSG_4269, std::vector<double>{1, 2, 3, 4, 5, 6, 7}); + auto op = CoordinateOperationFactory::create()->createOperation(boundCRS1, + boundCRS2); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ( + op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=grad +xy_out=rad +step +inv +proj=longlat " + "+ellps=clrk80ign +pm=paris +step +proj=cart +ellps=clrk80ign " + "+step +inv +proj=cart +ellps=GRS80 +step +proj=unitconvert " + "+xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, boundCRS_to_boundCRS_unralated_hub) { + auto boundCRS1 = BoundCRS::createFromTOWGS84( + GeographicCRS::EPSG_4807, std::vector<double>{1, 2, 3, 4, 5, 6, 7}); + auto boundCRS2 = BoundCRS::create( + GeographicCRS::EPSG_4269, GeographicCRS::EPSG_4979, + Transformation::createGeocentricTranslations( + PropertyMap(), GeographicCRS::EPSG_4269, GeographicCRS::EPSG_4979, + 1.0, 2.0, 3.0, std::vector<PositionalAccuracyNNPtr>())); + auto op = CoordinateOperationFactory::create()->createOperation(boundCRS1, + boundCRS2); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + CoordinateOperationFactory::create() + ->createOperation(boundCRS1->baseCRS(), boundCRS2->baseCRS()) + ->exportToPROJString(PROJStringFormatter::create().get())); +} + +// --------------------------------------------------------------------------- + +static VerticalCRSNNPtr createVerticalCRS() { + PropertyMap propertiesVDatum; + propertiesVDatum.set(Identifier::CODESPACE_KEY, "EPSG") + .set(Identifier::CODE_KEY, 5101) + .set(IdentifiedObject::NAME_KEY, "Ordnance Datum Newlyn"); + auto vdatum = VerticalReferenceFrame::create(propertiesVDatum); + PropertyMap propertiesCRS; + propertiesCRS.set(Identifier::CODESPACE_KEY, "EPSG") + .set(Identifier::CODE_KEY, 5701) + .set(IdentifiedObject::NAME_KEY, "ODN height"); + return VerticalCRS::create( + propertiesCRS, vdatum, + VerticalCS::createGravityRelatedHeight(UnitOfMeasure::METRE)); +} + +// --------------------------------------------------------------------------- + +TEST(operation, compoundCRS_to_geogCRS) { + + auto compound = CompoundCRS::create( + PropertyMap(), + std::vector<CRSNNPtr>{GeographicCRS::EPSG_4326, createVerticalCRS()}); + auto op = CoordinateOperationFactory::create()->createOperation( + compound, GeographicCRS::EPSG_4807); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + CoordinateOperationFactory::create() + ->createOperation(GeographicCRS::EPSG_4326, + GeographicCRS::EPSG_4807) + ->exportToPROJString(PROJStringFormatter::create().get())); +} + +// --------------------------------------------------------------------------- + +static BoundCRSNNPtr createBoundVerticalCRS() { + auto vertCRS = createVerticalCRS(); + auto transformation = + Transformation::createGravityRelatedHeightToGeographic3D( + PropertyMap(), vertCRS, GeographicCRS::EPSG_4979, "egm08_25.gtx", + std::vector<PositionalAccuracyNNPtr>()); + return BoundCRS::create(vertCRS, GeographicCRS::EPSG_4979, transformation); +} + +// --------------------------------------------------------------------------- + +TEST(operation, transformation_height_to_PROJ_string) { + auto transf = createBoundVerticalCRS()->transformation(); + EXPECT_EQ(transf->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=vgridshift +grids=egm08_25.gtx"); + + auto grids = transf->gridsNeeded(DatabaseContext::create()); + ASSERT_EQ(grids.size(), 1); + auto gridDesc = *(grids.begin()); + EXPECT_EQ(gridDesc.shortName, "egm08_25.gtx"); + EXPECT_EQ(gridDesc.packageName, "proj-datumgrid-world"); + EXPECT_TRUE(gridDesc.url.find( + "https://download.osgeo.org/proj/proj-datumgrid-world-") == + 0) + << gridDesc.url; + if (gridDesc.available) { + EXPECT_TRUE(!gridDesc.fullName.empty()) << gridDesc.fullName; + EXPECT_TRUE(gridDesc.fullName.find(gridDesc.shortName) != + std::string::npos) + << gridDesc.fullName; + } else { + EXPECT_TRUE(gridDesc.fullName.empty()) << gridDesc.fullName; + } + EXPECT_EQ(gridDesc.directDownload, true); + EXPECT_EQ(gridDesc.openLicense, true); +} + +// --------------------------------------------------------------------------- + +TEST(operation, transformation_ntv2_to_PROJ_string) { + auto transformation = Transformation::createNTv2( + PropertyMap(), GeographicCRS::EPSG_4807, GeographicCRS::EPSG_4326, + "foo.gsb", std::vector<PositionalAccuracyNNPtr>()); + EXPECT_EQ( + transformation->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=grad +xy_out=rad +step " + "+proj=hgridshift +grids=foo.gsb +step +proj=unitconvert " + "+xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, transformation_VERTCON_to_PROJ_string) { + auto verticalCRS1 = createVerticalCRS(); + + auto verticalCRS2 = VerticalCRS::create( + PropertyMap(), VerticalReferenceFrame::create(PropertyMap()), + VerticalCS::createGravityRelatedHeight(UnitOfMeasure::METRE)); + + // Use of this type of transformation is a bit of non-sense here + // since it should normally be used with NGVD29 and NAVD88 for VerticalCRS, + // and NAD27/NAD83 as horizontal CRS... + auto vtransformation = Transformation::createVERTCON( + PropertyMap(), verticalCRS1, verticalCRS2, "bla.gtx", + std::vector<PositionalAccuracyNNPtr>()); + EXPECT_EQ(vtransformation->exportToPROJString( + PROJStringFormatter::create().get()), + "+proj=vgridshift +grids=bla.gtx +multiplier=0.001"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, transformation_longitude_rotation_to_PROJ_string) { + + auto src = GeographicCRS::create( + PropertyMap(), GeodeticReferenceFrame::create( + PropertyMap(), Ellipsoid::WGS84, + optional<std::string>(), PrimeMeridian::GREENWICH), + EllipsoidalCS::createLatitudeLongitude(UnitOfMeasure::DEGREE)); + auto dest = GeographicCRS::create( + PropertyMap(), GeodeticReferenceFrame::create( + PropertyMap(), Ellipsoid::WGS84, + optional<std::string>(), PrimeMeridian::PARIS), + EllipsoidalCS::createLatitudeLongitude(UnitOfMeasure::DEGREE)); + auto transformation = Transformation::createLongitudeRotation( + PropertyMap(), src, dest, Angle(10)); + EXPECT_EQ( + transformation->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +inv " + "+proj=longlat +ellps=WGS84 +pm=10 +step +proj=unitconvert " + "+xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1"); + EXPECT_EQ(transformation->inverse()->exportToPROJString( + PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +inv " + "+proj=longlat +ellps=WGS84 +pm=-10 +step +proj=unitconvert " + "+xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, transformation_Geographic2D_offsets_to_PROJ_string) { + + auto transformation = Transformation::createGeographic2DOffsets( + PropertyMap(), GeographicCRS::EPSG_4326, GeographicCRS::EPSG_4326, + Angle(0.5), Angle(-1), {}); + EXPECT_EQ( + transformation->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=geogoffset " + "+dlat=1800 +dlon=-3600 +step +proj=unitconvert +xy_in=rad " + "+xy_out=deg +step +proj=axisswap +order=2,1"); + EXPECT_EQ(transformation->inverse()->exportToPROJString( + PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=geogoffset " + "+dlat=-1800 +dlon=3600 +step +proj=unitconvert +xy_in=rad " + "+xy_out=deg +step +proj=axisswap +order=2,1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, transformation_Geographic3D_offsets_to_PROJ_string) { + + auto transformation = Transformation::createGeographic3DOffsets( + PropertyMap(), GeographicCRS::EPSG_4326, GeographicCRS::EPSG_4326, + Angle(0.5), Angle(-1), Length(2), {}); + EXPECT_EQ( + transformation->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=geogoffset " + "+dlat=1800 +dlon=-3600 +dh=2 +step +proj=unitconvert +xy_in=rad " + "+xy_out=deg +step +proj=axisswap +order=2,1"); + EXPECT_EQ(transformation->inverse()->exportToPROJString( + PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=geogoffset " + "+dlat=-1800 +dlon=3600 +dh=-2 +step +proj=unitconvert " + "+xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, + transformation_Geographic2D_with_height_offsets_to_PROJ_string) { + + auto transformation = Transformation::createGeographic2DWithHeightOffsets( + PropertyMap(), + CompoundCRS::create(PropertyMap(), + {GeographicCRS::EPSG_4326, createVerticalCRS()}), + GeographicCRS::EPSG_4326, Angle(0.5), Angle(-1), Length(2), {}); + EXPECT_EQ( + transformation->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=geogoffset " + "+dlat=1800 +dlon=-3600 +dh=2 +step +proj=unitconvert +xy_in=rad " + "+xy_out=deg +step +proj=axisswap +order=2,1"); + EXPECT_EQ(transformation->inverse()->exportToPROJString( + PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=geogoffset " + "+dlat=-1800 +dlon=3600 +dh=-2 +step +proj=unitconvert " + "+xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, transformation_vertical_offset_to_PROJ_string) { + + auto transformation = Transformation::createVerticalOffset( + PropertyMap(), createVerticalCRS(), createVerticalCRS(), Length(1), {}); + EXPECT_EQ( + transformation->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=geogoffset +dh=1"); + EXPECT_EQ(transformation->inverse()->exportToPROJString( + PROJStringFormatter::create().get()), + "+proj=geogoffset +dh=-1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, compoundCRS_with_boundVerticalCRS_to_geogCRS) { + + auto compound = CompoundCRS::create( + PropertyMap(), std::vector<CRSNNPtr>{GeographicCRS::EPSG_4326, + createBoundVerticalCRS()}); + auto op = CoordinateOperationFactory::create()->createOperation( + compound, GeographicCRS::EPSG_4979); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=vgridshift " + "+grids=egm08_25.gtx +step +proj=unitconvert +xy_in=rad " + "+xy_out=deg +step +proj=axisswap +order=2,1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, compoundCRS_with_boundGeogCRS_to_geogCRS) { + + auto geogCRS = GeographicCRS::create( + PropertyMap(), GeodeticReferenceFrame::create( + PropertyMap(), Ellipsoid::WGS84, + optional<std::string>(), PrimeMeridian::GREENWICH), + EllipsoidalCS::createLatitudeLongitude(UnitOfMeasure::DEGREE)); + auto horizBoundCRS = BoundCRS::createFromTOWGS84( + geogCRS, std::vector<double>{1, 2, 3, 4, 5, 6, 7}); + auto compound = CompoundCRS::create( + PropertyMap(), + std::vector<CRSNNPtr>{horizBoundCRS, createVerticalCRS()}); + auto op = CoordinateOperationFactory::create()->createOperation( + compound, GeographicCRS::EPSG_4979); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=cart " + "+ellps=WGS84 +step +proj=helmert +x=1 +y=2 +z=3 +rx=4 +ry=5 " + "+rz=6 +s=7 +convention=position_vector +step +inv +proj=cart " + "+ellps=WGS84 +step +proj=unitconvert +xy_in=rad +xy_out=deg " + "+step +proj=axisswap +order=2,1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, compoundCRS_with_boundGeogCRS_and_boundVerticalCRS_to_geogCRS) { + + auto horizBoundCRS = BoundCRS::createFromTOWGS84( + GeographicCRS::EPSG_4807, std::vector<double>{1, 2, 3, 4, 5, 6, 7}); + auto compound = CompoundCRS::create( + PropertyMap(), + std::vector<CRSNNPtr>{horizBoundCRS, createBoundVerticalCRS()}); + auto op = CoordinateOperationFactory::create()->createOperation( + compound, GeographicCRS::EPSG_4979); + ASSERT_TRUE(op != nullptr); + // Not completely sure the order of horizontal and vertical operations + // makes sense + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=grad +xy_out=rad +step +inv " + "+proj=longlat +ellps=clrk80ign +pm=paris +step +proj=cart " + "+ellps=clrk80ign +step +proj=helmert +x=1 +y=2 +z=3 +rx=4 +ry=5 " + "+rz=6 +s=7 +convention=position_vector +step +inv +proj=cart " + "+ellps=WGS84 +step +proj=vgridshift +grids=egm08_25.gtx +step " + "+proj=unitconvert +xy_in=rad +xy_out=deg +step " + "+proj=axisswap +order=2,1"); + + auto grids = op->gridsNeeded(DatabaseContext::create()); + EXPECT_EQ(grids.size(), 1); + + auto opInverse = CoordinateOperationFactory::create()->createOperation( + GeographicCRS::EPSG_4979, compound); + ASSERT_TRUE(opInverse != nullptr); + EXPECT_TRUE(opInverse->inverse()->isEquivalentTo(op.get())); +} + +// --------------------------------------------------------------------------- + +TEST(operation, compoundCRS_with_boundProjCRS_and_boundVerticalCRS_to_geogCRS) { + + auto horizBoundCRS = BoundCRS::createFromTOWGS84( + ProjectedCRS::create( + PropertyMap(), GeographicCRS::EPSG_4807, + Conversion::createUTM(PropertyMap(), 31, true), + CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)), + std::vector<double>{1, 2, 3, 4, 5, 6, 7}); + auto compound = CompoundCRS::create( + PropertyMap(), + std::vector<CRSNNPtr>{horizBoundCRS, createBoundVerticalCRS()}); + auto op = CoordinateOperationFactory::create()->createOperation( + compound, GeographicCRS::EPSG_4979); + ASSERT_TRUE(op != nullptr); + // Not completely sure the order of horizontal and vertical operations + // makes sense + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +inv +proj=utm +zone=31 +ellps=clrk80ign " + "+pm=paris +step +proj=cart +ellps=clrk80ign +step +proj=helmert " + "+x=1 +y=2 +z=3 +rx=4 +ry=5 +rz=6 +s=7 " + "+convention=position_vector +step +inv +proj=cart +ellps=WGS84 " + "+step +proj=vgridshift +grids=egm08_25.gtx +step " + "+proj=unitconvert +xy_in=rad +xy_out=deg +step " + "+proj=axisswap +order=2,1"); + + auto opInverse = CoordinateOperationFactory::create()->createOperation( + GeographicCRS::EPSG_4979, compound); + ASSERT_TRUE(opInverse != nullptr); + EXPECT_TRUE(opInverse->inverse()->isEquivalentTo(op.get())); +} + +// --------------------------------------------------------------------------- + +TEST(operation, compoundCRS_to_compoundCRS) { + auto compound1 = CompoundCRS::create( + PropertyMap(), + std::vector<CRSNNPtr>{createUTM31_WGS84(), createVerticalCRS()}); + auto compound2 = CompoundCRS::create( + PropertyMap(), + std::vector<CRSNNPtr>{createUTM32_WGS84(), createVerticalCRS()}); + auto op = CoordinateOperationFactory::create()->createOperation(compound1, + compound2); + ASSERT_TRUE(op != nullptr); + auto opRef = CoordinateOperationFactory::create()->createOperation( + createUTM31_WGS84(), createUTM32_WGS84()); + ASSERT_TRUE(opRef != nullptr); + EXPECT_TRUE(op->isEquivalentTo(opRef.get())); +} + +// --------------------------------------------------------------------------- + +TEST(operation, compoundCRS_to_compoundCRS_with_vertical_transform) { + auto verticalCRS1 = createVerticalCRS(); + + auto verticalCRS2 = VerticalCRS::create( + PropertyMap(), VerticalReferenceFrame::create(PropertyMap()), + VerticalCS::createGravityRelatedHeight(UnitOfMeasure::METRE)); + + // Use of this type of transformation is a bit of non-sense here + // since it should normally be used with NGVD29 and NAVD88 for VerticalCRS, + // and NAD27/NAD83 as horizontal CRS... + auto vtransformation = Transformation::createVERTCON( + PropertyMap(), verticalCRS1, verticalCRS2, "bla.gtx", + std::vector<PositionalAccuracyNNPtr>()); + + auto compound1 = CompoundCRS::create( + PropertyMap(), + std::vector<CRSNNPtr>{ + ProjectedCRS::create( + PropertyMap(), GeographicCRS::EPSG_4326, + Conversion::createTransverseMercator(PropertyMap(), Angle(1), + Angle(2), Scale(3), + Length(4), Length(5)), + CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)), + BoundCRS::create(verticalCRS1, verticalCRS2, vtransformation)}); + auto compound2 = CompoundCRS::create( + PropertyMap(), + std::vector<CRSNNPtr>{createUTM32_WGS84(), verticalCRS2}); + + auto op = CoordinateOperationFactory::create()->createOperation(compound1, + compound2); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +inv +proj=tmerc +lat_0=1 +lon_0=2 +k_0=3 " + "+x_0=4 +y_0=5 +ellps=WGS84 +step " + "+proj=vgridshift +grids=bla.gtx +multiplier=0.001 +step " + "+proj=utm +zone=32 " + "+ellps=WGS84"); + { + auto formatter = PROJStringFormatter::create(); + formatter->setUseETMercForTMerc(true); + EXPECT_EQ(op->exportToPROJString(formatter.get()), + "+proj=pipeline +step +inv +proj=etmerc +lat_0=1 +lon_0=2 " + "+k_0=3 +x_0=4 +y_0=5 +ellps=WGS84 +step " + "+proj=vgridshift +grids=bla.gtx +multiplier=0.001 +step " + "+proj=utm +zone=32 " + "+ellps=WGS84"); + } + { + auto formatter = PROJStringFormatter::create(); + formatter->setUseETMercForTMerc(true); + EXPECT_EQ(op->inverse()->exportToPROJString(formatter.get()), + "+proj=pipeline +step +inv +proj=utm +zone=32 +ellps=WGS84 " + "+step +inv +proj=vgridshift +grids=bla.gtx " + "+multiplier=0.001 +step +proj=etmerc +lat_0=1 +lon_0=2 " + "+k_0=3 +x_0=4 +y_0=5 +ellps=WGS84"); + } + + auto opInverse = CoordinateOperationFactory::create()->createOperation( + compound2, compound1); + ASSERT_TRUE(opInverse != nullptr); + { + auto formatter = PROJStringFormatter::create(); + auto formatter2 = PROJStringFormatter::create(); + EXPECT_EQ(opInverse->inverse()->exportToPROJString(formatter.get()), + op->exportToPROJString(formatter2.get())); + } +} + +// --------------------------------------------------------------------------- + +TEST(operation, vertCRS_to_vertCRS) { + + auto vertcrs_m_obj = PROJStringParser().createFromPROJString("+vunits=m"); + auto vertcrs_m = nn_dynamic_pointer_cast<VerticalCRS>(vertcrs_m_obj); + ASSERT_TRUE(vertcrs_m != nullptr); + + auto vertcrs_ft_obj = PROJStringParser().createFromPROJString("+vunits=ft"); + auto vertcrs_ft = nn_dynamic_pointer_cast<VerticalCRS>(vertcrs_ft_obj); + ASSERT_TRUE(vertcrs_ft != nullptr); + + auto vertcrs_us_ft_obj = + PROJStringParser().createFromPROJString("+vunits=us-ft"); + auto vertcrs_us_ft = + nn_dynamic_pointer_cast<VerticalCRS>(vertcrs_us_ft_obj); + ASSERT_TRUE(vertcrs_us_ft != nullptr); + + { + auto op = CoordinateOperationFactory::create()->createOperation( + NN_CHECK_ASSERT(vertcrs_m), NN_CHECK_ASSERT(vertcrs_ft)); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=unitconvert +z_in=m +z_out=ft"); + } + { + auto op = CoordinateOperationFactory::create()->createOperation( + NN_CHECK_ASSERT(vertcrs_m), NN_CHECK_ASSERT(vertcrs_ft)); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->inverse()->exportToPROJString( + PROJStringFormatter::create().get()), + "+proj=unitconvert +z_in=ft +z_out=m"); + } + { + auto op = CoordinateOperationFactory::create()->createOperation( + NN_CHECK_ASSERT(vertcrs_ft), NN_CHECK_ASSERT(vertcrs_m)); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=unitconvert +z_in=ft +z_out=m"); + } + { + auto op = CoordinateOperationFactory::create()->createOperation( + NN_CHECK_ASSERT(vertcrs_ft), NN_CHECK_ASSERT(vertcrs_us_ft)); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=affine +s33=0.999998"); + } +} + +// --------------------------------------------------------------------------- + +TEST(operation, compoundCRS_to_geogCRS_3D) { + + auto compoundcrs_ft_obj = + PROJStringParser().createFromPROJString("+proj=merc +vunits=ft"); + auto compoundcrs_ft = nn_dynamic_pointer_cast<CRS>(compoundcrs_ft_obj); + ASSERT_TRUE(compoundcrs_ft != nullptr); + + auto geogcrs_m_obj = + PROJStringParser().createFromPROJString("+proj=longlat +vunits=m"); + auto geogcrs_m = nn_dynamic_pointer_cast<CRS>(geogcrs_m_obj); + ASSERT_TRUE(geogcrs_m != nullptr); + + { + auto op = CoordinateOperationFactory::create()->createOperation( + NN_CHECK_ASSERT(compoundcrs_ft), NN_CHECK_ASSERT(geogcrs_m)); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +inv +proj=merc +lon_0=0 +k=1 +x_0=0 " + "+y_0=0 +ellps=WGS84 +step +proj=unitconvert +xy_in=rad " + "+z_in=ft +xy_out=deg +z_out=m"); + } + + { + auto op = CoordinateOperationFactory::create()->createOperation( + NN_CHECK_ASSERT(geogcrs_m), NN_CHECK_ASSERT(compoundcrs_ft)); + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=unitconvert +xy_in=deg +z_in=m " + "+xy_out=rad +z_out=ft +step +proj=merc +lon_0=0 +k=1 +x_0=0 " + "+y_0=0 +ellps=WGS84"); + } +} + +// --------------------------------------------------------------------------- + +TEST(operation, IGNF_LAMB1_TO_EPSG_4326) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), std::string()); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + auto list = CoordinateOperationFactory::create()->createOperations( + AuthorityFactory::create(DatabaseContext::create(), "IGNF") + ->createCoordinateReferenceSystem("LAMB1"), + AuthorityFactory::create(DatabaseContext::create(), "EPSG") + ->createCoordinateReferenceSystem("4326"), + ctxt); + ASSERT_EQ(list.size(), 2); + + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +inv +proj=lcc +lat_1=49.5 +lat_0=49.5 " + "+lon_0=0 +k_0=0.99987734 +x_0=600000 +y_0=200000 " + "+ellps=clrk80ign +pm=paris +step +proj=hgridshift " + "+grids=ntf_r93.gsb +step +proj=unitconvert +xy_in=rad " + "+xy_out=deg +step +proj=axisswap +order=2,1"); + + EXPECT_EQ(list[1]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +inv +proj=lcc +lat_1=49.5 +lat_0=49.5 " + "+lon_0=0 +k_0=0.99987734 +x_0=600000 +y_0=200000 " + "+ellps=clrk80ign +pm=paris +step +proj=cart +ellps=clrk80ign " + "+step +proj=helmert +x=-168 +y=-60 +z=320 +step +inv +proj=cart " + "+ellps=WGS84 +step +proj=unitconvert +xy_in=rad +xy_out=deg " + "+step +proj=axisswap +order=2,1"); + + auto list2 = CoordinateOperationFactory::create()->createOperations( + AuthorityFactory::create(DatabaseContext::create(), "EPSG") + // NTF (Paris) / Lambert Nord France equivalent to IGNF:LAMB1 + ->createCoordinateReferenceSystem("27561"), + AuthorityFactory::create(DatabaseContext::create(), "EPSG") + ->createCoordinateReferenceSystem("4326"), + ctxt); + ASSERT_GE(list2.size(), 4U); + + EXPECT_EQ(replaceAll(list2[0]->exportToPROJString( + PROJStringFormatter::create().get()), + "0.999877341", "0.99987734"), + list[0]->exportToPROJString(PROJStringFormatter::create().get())); + + // The second entry in list2 (list2[1]) uses the + // weird +pm=2.33720833333333 from "NTF (Paris) to NTF (2)" + // and the third one uses the ESRI geographic offset method with another + // value + // so skip to the 4th method + EXPECT_EQ(replaceAll(list2[3]->exportToPROJString( + PROJStringFormatter::create().get()), + "0.999877341", "0.99987734"), + list[1]->exportToPROJString(PROJStringFormatter::create().get())); +} + +// --------------------------------------------------------------------------- + +TEST(operation, isPROJInstanciable) { + + { + auto transformation = Transformation::createGeocentricTranslations( + PropertyMap(), GeographicCRS::EPSG_4269, GeographicCRS::EPSG_4326, + 1.0, 2.0, 3.0, {}); + EXPECT_TRUE( + transformation->isPROJInstanciable(DatabaseContext::create())); + } + + // Missing grid + { + auto transformation = Transformation::createNTv2( + PropertyMap(), GeographicCRS::EPSG_4807, GeographicCRS::EPSG_4326, + "foo.gsb", std::vector<PositionalAccuracyNNPtr>()); + EXPECT_FALSE( + transformation->isPROJInstanciable(DatabaseContext::create())); + } + + // Unsupported method + { + auto transformation = Transformation::create( + PropertyMap(), GeographicCRS::EPSG_4269, GeographicCRS::EPSG_4326, + nullptr, OperationMethod::create( + PropertyMap(), std::vector<OperationParameterNNPtr>{}), + std::vector<GeneralParameterValueNNPtr>{}, + std::vector<PositionalAccuracyNNPtr>{}); + EXPECT_FALSE( + transformation->isPROJInstanciable(DatabaseContext::create())); + } +} + +// --------------------------------------------------------------------------- + +TEST(operation, createOperation_on_crs_with_canonical_bound_crs) { + auto boundCRS = BoundCRS::createFromTOWGS84( + GeographicCRS::EPSG_4267, std::vector<double>{1, 2, 3, 4, 5, 6, 7}); + auto crs = boundCRS->baseCRSWithCanonicalBoundCRS(); + { + auto op = CoordinateOperationFactory::create()->createOperation( + crs, GeographicCRS::EPSG_4326); + ASSERT_TRUE(op != nullptr); + EXPECT_TRUE(op->isEquivalentTo(boundCRS->transformation().get())); + { + auto wkt1 = op->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT2_2018) + .get()); + auto wkt2 = boundCRS->transformation()->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT2_2018) + .get()); + EXPECT_EQ(wkt1, wkt2); + } + } + { + auto op = CoordinateOperationFactory::create()->createOperation( + GeographicCRS::EPSG_4326, crs); + ASSERT_TRUE(op != nullptr); + EXPECT_TRUE( + op->isEquivalentTo(boundCRS->transformation()->inverse().get())); + { + auto wkt1 = op->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT2_2018) + .get()); + auto wkt2 = boundCRS->transformation()->inverse()->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT2_2018) + .get()); + EXPECT_EQ(wkt1, wkt2); + } + } +} + +// --------------------------------------------------------------------------- + +TEST(operation, mercator_variant_A_to_variant_B) { + auto projCRS = ProjectedCRS::create( + PropertyMap(), GeographicCRS::EPSG_4326, + Conversion::createMercatorVariantA(PropertyMap(), Angle(0), Angle(1), + Scale(0.9), Length(3), Length(4)), + CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)); + + auto conv = projCRS->derivingConversion(); + auto sameConv = + conv->convertToOtherMethod(EPSG_CODE_METHOD_MERCATOR_VARIANT_A); + ASSERT_TRUE(sameConv); + EXPECT_TRUE(sameConv->isEquivalentTo(conv.get())); + + auto targetConv = + conv->convertToOtherMethod(EPSG_CODE_METHOD_MERCATOR_VARIANT_B); + ASSERT_TRUE(targetConv); + + auto lat_1 = targetConv->parameterValueNumeric( + EPSG_CODE_PARAMETER_LATITUDE_1ST_STD_PARALLEL, UnitOfMeasure::DEGREE); + EXPECT_EQ(lat_1, 25.917499691810534) << lat_1; + + EXPECT_EQ(targetConv->parameterValueNumeric( + EPSG_CODE_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN, + UnitOfMeasure::DEGREE), + 1); + + EXPECT_EQ(targetConv->parameterValueNumeric( + EPSG_CODE_PARAMETER_FALSE_EASTING, UnitOfMeasure::METRE), + 3); + + EXPECT_EQ(targetConv->parameterValueNumeric( + EPSG_CODE_PARAMETER_FALSE_NORTHING, UnitOfMeasure::METRE), + 4); + + EXPECT_FALSE( + conv->isEquivalentTo(targetConv.get(), IComparable::Criterion::STRICT)); + EXPECT_TRUE(conv->isEquivalentTo(targetConv.get(), + IComparable::Criterion::EQUIVALENT)); + EXPECT_TRUE(targetConv->isEquivalentTo(conv.get(), + IComparable::Criterion::EQUIVALENT)); +} + +// --------------------------------------------------------------------------- + +TEST(operation, mercator_variant_A_to_variant_B_scale_1) { + auto projCRS = ProjectedCRS::create( + PropertyMap(), GeographicCRS::EPSG_4326, + Conversion::createMercatorVariantA(PropertyMap(), Angle(0), Angle(1), + Scale(1.0), Length(3), Length(4)), + CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)); + + auto targetConv = projCRS->derivingConversion()->convertToOtherMethod( + EPSG_CODE_METHOD_MERCATOR_VARIANT_B); + ASSERT_TRUE(targetConv); + + auto lat_1 = targetConv->parameterValueNumeric( + EPSG_CODE_PARAMETER_LATITUDE_1ST_STD_PARALLEL, UnitOfMeasure::DEGREE); + EXPECT_EQ(lat_1, 0.0) << lat_1; +} + +// --------------------------------------------------------------------------- + +TEST(operation, mercator_variant_A_to_variant_B_no_crs) { + auto targetConv = + Conversion::createMercatorVariantA(PropertyMap(), Angle(0), Angle(1), + Scale(1.0), Length(3), Length(4)) + ->convertToOtherMethod(EPSG_CODE_METHOD_MERCATOR_VARIANT_B); + EXPECT_FALSE(targetConv != nullptr); +} + +// --------------------------------------------------------------------------- + +TEST(operation, mercator_variant_A_to_variant_B_invalid_scale) { + auto projCRS = ProjectedCRS::create( + PropertyMap(), GeographicCRS::EPSG_4326, + Conversion::createMercatorVariantA(PropertyMap(), Angle(0), Angle(1), + Scale(0.0), Length(3), Length(4)), + CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)); + + auto targetConv = projCRS->derivingConversion()->convertToOtherMethod( + EPSG_CODE_METHOD_MERCATOR_VARIANT_B); + EXPECT_FALSE(targetConv != nullptr); +} + +// --------------------------------------------------------------------------- + +static GeographicCRSNNPtr geographicCRSInvalidEccentricity() { + return GeographicCRS::create( + PropertyMap(), + GeodeticReferenceFrame::create( + PropertyMap(), Ellipsoid::createFlattenedSphere( + PropertyMap(), Length(6378137), Scale(0.1)), + optional<std::string>(), PrimeMeridian::GREENWICH), + EllipsoidalCS::createLatitudeLongitude(UnitOfMeasure::DEGREE)); +} + +// --------------------------------------------------------------------------- + +TEST(operation, mercator_variant_A_to_variant_B_invalid_eccentricity) { + auto projCRS = ProjectedCRS::create( + PropertyMap(), geographicCRSInvalidEccentricity(), + Conversion::createMercatorVariantA(PropertyMap(), Angle(0), Angle(1), + Scale(1.0), Length(3), Length(4)), + CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)); + + auto targetConv = projCRS->derivingConversion()->convertToOtherMethod( + EPSG_CODE_METHOD_MERCATOR_VARIANT_B); + EXPECT_FALSE(targetConv != nullptr); +} + +// --------------------------------------------------------------------------- + +TEST(operation, mercator_variant_B_to_variant_A) { + auto projCRS = ProjectedCRS::create( + PropertyMap(), GeographicCRS::EPSG_4326, + Conversion::createMercatorVariantB(PropertyMap(), + Angle(25.917499691810534), Angle(1), + Length(3), Length(4)), + CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)); + auto targetConv = projCRS->derivingConversion()->convertToOtherMethod( + EPSG_CODE_METHOD_MERCATOR_VARIANT_A); + ASSERT_TRUE(targetConv); + + EXPECT_EQ(targetConv->parameterValueNumeric( + EPSG_CODE_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN, + UnitOfMeasure::DEGREE), + 0); + + EXPECT_EQ(targetConv->parameterValueNumeric( + EPSG_CODE_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN, + UnitOfMeasure::DEGREE), + 1); + + auto k_0 = targetConv->parameterValueNumeric( + EPSG_CODE_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN, + UnitOfMeasure::SCALE_UNITY); + EXPECT_EQ(k_0, 0.9) << k_0; + + EXPECT_EQ(targetConv->parameterValueNumeric( + EPSG_CODE_PARAMETER_FALSE_EASTING, UnitOfMeasure::METRE), + 3); + + EXPECT_EQ(targetConv->parameterValueNumeric( + EPSG_CODE_PARAMETER_FALSE_NORTHING, UnitOfMeasure::METRE), + 4); +} + +// --------------------------------------------------------------------------- + +TEST(operation, mercator_variant_B_to_variant_A_invalid_std1) { + auto projCRS = ProjectedCRS::create( + PropertyMap(), GeographicCRS::EPSG_4326, + Conversion::createMercatorVariantB(PropertyMap(), Angle(100), Angle(1), + Length(3), Length(4)), + CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)); + auto targetConv = projCRS->derivingConversion()->convertToOtherMethod( + EPSG_CODE_METHOD_MERCATOR_VARIANT_A); + EXPECT_FALSE(targetConv != nullptr); +} + +// --------------------------------------------------------------------------- + +TEST(operation, mercator_variant_B_to_variant_A_invalid_eccentricity) { + auto projCRS = ProjectedCRS::create( + PropertyMap(), geographicCRSInvalidEccentricity(), + Conversion::createMercatorVariantB(PropertyMap(), Angle(0), Angle(1), + Length(3), Length(4)), + CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)); + auto targetConv = projCRS->derivingConversion()->convertToOtherMethod( + EPSG_CODE_METHOD_MERCATOR_VARIANT_A); + EXPECT_FALSE(targetConv != nullptr); +} + +// --------------------------------------------------------------------------- + +TEST(operation, lcc2sp_to_lcc1sp) { + // equivalent to EPSG:2154 + auto projCRS = ProjectedCRS::create( + PropertyMap(), GeographicCRS::EPSG_4269, // something using GRS80 + Conversion::createLambertConicConformal_2SP( + PropertyMap(), Angle(46.5), Angle(3), Angle(49), Angle(44), + Length(700000), Length(6600000)), + CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)); + + auto conv = projCRS->derivingConversion(); + auto targetConv = conv->convertToOtherMethod( + EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP); + ASSERT_TRUE(targetConv); + + { + auto lat_0 = targetConv->parameterValueNumeric( + EPSG_CODE_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN, + UnitOfMeasure::DEGREE); + EXPECT_NEAR(lat_0, 46.519430223986866, 1e-12) << lat_0; + + auto lon_0 = targetConv->parameterValueNumeric( + EPSG_CODE_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN, + UnitOfMeasure::DEGREE); + EXPECT_NEAR(lon_0, 3.0, 1e-15) << lon_0; + + auto k_0 = targetConv->parameterValueNumeric( + EPSG_CODE_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN, + UnitOfMeasure::SCALE_UNITY); + EXPECT_NEAR(k_0, 0.9990510286374692, 1e-15) << k_0; + + auto x_0 = targetConv->parameterValueNumeric( + EPSG_CODE_PARAMETER_FALSE_EASTING, UnitOfMeasure::METRE); + EXPECT_NEAR(x_0, 700000, 1e-15) << x_0; + + auto y_0 = targetConv->parameterValueNumeric( + EPSG_CODE_PARAMETER_FALSE_NORTHING, UnitOfMeasure::METRE); + EXPECT_NEAR(y_0, 6602157.8388103368, 1e-7) << y_0; + } + + auto _2sp_from_1sp = targetConv->convertToOtherMethod( + EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP); + ASSERT_TRUE(_2sp_from_1sp); + + { + auto lat_0 = _2sp_from_1sp->parameterValueNumeric( + EPSG_CODE_PARAMETER_LATITUDE_FALSE_ORIGIN, UnitOfMeasure::DEGREE); + EXPECT_NEAR(lat_0, 46.5, 1e-15) << lat_0; + + auto lon_0 = _2sp_from_1sp->parameterValueNumeric( + EPSG_CODE_PARAMETER_LONGITUDE_FALSE_ORIGIN, UnitOfMeasure::DEGREE); + EXPECT_NEAR(lon_0, 3, 1e-15) << lon_0; + + auto lat_1 = _2sp_from_1sp->parameterValueNumeric( + EPSG_CODE_PARAMETER_LATITUDE_1ST_STD_PARALLEL, + UnitOfMeasure::DEGREE); + EXPECT_NEAR(lat_1, 49, 1e-15) << lat_1; + + auto lat_2 = _2sp_from_1sp->parameterValueNumeric( + EPSG_CODE_PARAMETER_LATITUDE_2ND_STD_PARALLEL, + UnitOfMeasure::DEGREE); + EXPECT_NEAR(lat_2, 44, 1e-15) << lat_2; + + auto x_0 = _2sp_from_1sp->parameterValueNumeric( + EPSG_CODE_PARAMETER_EASTING_FALSE_ORIGIN, UnitOfMeasure::METRE); + EXPECT_NEAR(x_0, 700000, 1e-15) << x_0; + + auto y_0 = _2sp_from_1sp->parameterValueNumeric( + EPSG_CODE_PARAMETER_NORTHING_FALSE_ORIGIN, UnitOfMeasure::METRE); + EXPECT_NEAR(y_0, 6600000, 1e-15) << y_0; + } + + EXPECT_FALSE( + conv->isEquivalentTo(targetConv.get(), IComparable::Criterion::STRICT)); + EXPECT_TRUE(conv->isEquivalentTo(targetConv.get(), + IComparable::Criterion::EQUIVALENT)); + EXPECT_TRUE(targetConv->isEquivalentTo(conv.get(), + IComparable::Criterion::EQUIVALENT)); +} + +// --------------------------------------------------------------------------- + +TEST(operation, lcc2sp_to_lcc1sp_phi0_eq_phi1_eq_phi2) { + auto projCRS = ProjectedCRS::create( + PropertyMap(), GeographicCRS::EPSG_4269, // something using GRS80 + Conversion::createLambertConicConformal_2SP( + PropertyMap(), Angle(46.5), Angle(3), Angle(46.5), Angle(46.5), + Length(700000), Length(6600000)), + CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)); + + auto conv = projCRS->derivingConversion(); + auto targetConv = conv->convertToOtherMethod( + EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP); + ASSERT_TRUE(targetConv); + + { + auto lat_0 = targetConv->parameterValueNumeric( + EPSG_CODE_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN, + UnitOfMeasure::DEGREE); + EXPECT_NEAR(lat_0, 46.5, 1e-15) << lat_0; + + auto lon_0 = targetConv->parameterValueNumeric( + EPSG_CODE_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN, + UnitOfMeasure::DEGREE); + EXPECT_NEAR(lon_0, 3.0, 1e-15) << lon_0; + + auto k_0 = targetConv->parameterValueNumeric( + EPSG_CODE_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN, + UnitOfMeasure::SCALE_UNITY); + EXPECT_NEAR(k_0, 1.0, 1e-15) << k_0; + + auto x_0 = targetConv->parameterValueNumeric( + EPSG_CODE_PARAMETER_FALSE_EASTING, UnitOfMeasure::METRE); + EXPECT_NEAR(x_0, 700000, 1e-15) << x_0; + + auto y_0 = targetConv->parameterValueNumeric( + EPSG_CODE_PARAMETER_FALSE_NORTHING, UnitOfMeasure::METRE); + EXPECT_NEAR(y_0, 6600000, 1e-15) << y_0; + } + + auto _2sp_from_1sp = targetConv->convertToOtherMethod( + EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP); + ASSERT_TRUE(_2sp_from_1sp); + + { + auto lat_0 = _2sp_from_1sp->parameterValueNumeric( + EPSG_CODE_PARAMETER_LATITUDE_FALSE_ORIGIN, UnitOfMeasure::DEGREE); + EXPECT_NEAR(lat_0, 46.5, 1e-15) << lat_0; + + auto lon_0 = _2sp_from_1sp->parameterValueNumeric( + EPSG_CODE_PARAMETER_LONGITUDE_FALSE_ORIGIN, UnitOfMeasure::DEGREE); + EXPECT_NEAR(lon_0, 3, 1e-15) << lon_0; + + auto lat_1 = _2sp_from_1sp->parameterValueNumeric( + EPSG_CODE_PARAMETER_LATITUDE_1ST_STD_PARALLEL, + UnitOfMeasure::DEGREE); + EXPECT_NEAR(lat_1, 46.5, 1e-15) << lat_1; + + auto lat_2 = _2sp_from_1sp->parameterValueNumeric( + EPSG_CODE_PARAMETER_LATITUDE_2ND_STD_PARALLEL, + UnitOfMeasure::DEGREE); + EXPECT_NEAR(lat_2, 46.5, 1e-15) << lat_2; + + auto x_0 = _2sp_from_1sp->parameterValueNumeric( + EPSG_CODE_PARAMETER_EASTING_FALSE_ORIGIN, UnitOfMeasure::METRE); + EXPECT_NEAR(x_0, 700000, 1e-15) << x_0; + + auto y_0 = _2sp_from_1sp->parameterValueNumeric( + EPSG_CODE_PARAMETER_NORTHING_FALSE_ORIGIN, UnitOfMeasure::METRE); + EXPECT_NEAR(y_0, 6600000, 1e-15) << y_0; + } + + EXPECT_TRUE(conv->isEquivalentTo(targetConv.get(), + IComparable::Criterion::EQUIVALENT)); + EXPECT_TRUE(targetConv->isEquivalentTo(conv.get(), + IComparable::Criterion::EQUIVALENT)); +} + +// --------------------------------------------------------------------------- + +TEST(operation, lcc2sp_to_lcc1sp_phi0_diff_phi1_and_phi1_eq_phi2) { + + auto projCRS = ProjectedCRS::create( + PropertyMap(), GeographicCRS::EPSG_4269, // something using GRS80 + Conversion::createLambertConicConformal_2SP( + PropertyMap(), Angle(46.123), Angle(3), Angle(46.4567), + Angle(46.4567), Length(700000), Length(6600000)), + CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)); + + auto conv = projCRS->derivingConversion(); + auto targetConv = conv->convertToOtherMethod( + EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP); + ASSERT_TRUE(targetConv); + + { + auto lat_0 = targetConv->parameterValueNumeric( + EPSG_CODE_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN, + UnitOfMeasure::DEGREE); + EXPECT_NEAR(lat_0, 46.4567, 1e-14) << lat_0; + + auto lon_0 = targetConv->parameterValueNumeric( + EPSG_CODE_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN, + UnitOfMeasure::DEGREE); + EXPECT_NEAR(lon_0, 3.0, 1e-15) << lon_0; + + auto k_0 = targetConv->parameterValueNumeric( + EPSG_CODE_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN, + UnitOfMeasure::SCALE_UNITY); + EXPECT_NEAR(k_0, 1.0, 1e-15) << k_0; + + auto x_0 = targetConv->parameterValueNumeric( + EPSG_CODE_PARAMETER_FALSE_EASTING, UnitOfMeasure::METRE); + EXPECT_NEAR(x_0, 700000, 1e-15) << x_0; + + auto y_0 = targetConv->parameterValueNumeric( + EPSG_CODE_PARAMETER_FALSE_NORTHING, UnitOfMeasure::METRE); + EXPECT_NEAR(y_0, 6637093.292952879, 1e-8) << y_0; + } + + auto _2sp_from_1sp = targetConv->convertToOtherMethod( + EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP); + ASSERT_TRUE(_2sp_from_1sp); + + { + auto lat_0 = _2sp_from_1sp->parameterValueNumeric( + EPSG_CODE_PARAMETER_LATITUDE_FALSE_ORIGIN, UnitOfMeasure::DEGREE); + EXPECT_NEAR(lat_0, 46.4567, 1e-14) << lat_0; + + auto lon_0 = _2sp_from_1sp->parameterValueNumeric( + EPSG_CODE_PARAMETER_LONGITUDE_FALSE_ORIGIN, UnitOfMeasure::DEGREE); + EXPECT_NEAR(lon_0, 3, 1e-15) << lon_0; + + auto lat_1 = _2sp_from_1sp->parameterValueNumeric( + EPSG_CODE_PARAMETER_LATITUDE_1ST_STD_PARALLEL, + UnitOfMeasure::DEGREE); + EXPECT_NEAR(lat_1, 46.4567, 1e-14) << lat_1; + + auto lat_2 = _2sp_from_1sp->parameterValueNumeric( + EPSG_CODE_PARAMETER_LATITUDE_2ND_STD_PARALLEL, + UnitOfMeasure::DEGREE); + EXPECT_NEAR(lat_2, 46.4567, 1e-14) << lat_2; + + auto x_0 = _2sp_from_1sp->parameterValueNumeric( + EPSG_CODE_PARAMETER_EASTING_FALSE_ORIGIN, UnitOfMeasure::METRE); + EXPECT_NEAR(x_0, 700000, 1e-15) << x_0; + + auto y_0 = _2sp_from_1sp->parameterValueNumeric( + EPSG_CODE_PARAMETER_NORTHING_FALSE_ORIGIN, UnitOfMeasure::METRE); + EXPECT_NEAR(y_0, 6637093.292952879, 1e-8) << y_0; + } + + EXPECT_TRUE(conv->isEquivalentTo(targetConv.get(), + IComparable::Criterion::EQUIVALENT)); + EXPECT_TRUE(targetConv->isEquivalentTo(conv.get(), + IComparable::Criterion::EQUIVALENT)); + + EXPECT_TRUE(_2sp_from_1sp->isEquivalentTo( + targetConv.get(), IComparable::Criterion::EQUIVALENT)); + EXPECT_TRUE(targetConv->isEquivalentTo(_2sp_from_1sp.get(), + IComparable::Criterion::EQUIVALENT)); + + EXPECT_TRUE(conv->isEquivalentTo(_2sp_from_1sp.get(), + IComparable::Criterion::EQUIVALENT)); +} + +// --------------------------------------------------------------------------- + +TEST(operation, lcc1sp_to_lcc2sp_invalid_eccentricity) { + auto projCRS = ProjectedCRS::create( + PropertyMap(), geographicCRSInvalidEccentricity(), + Conversion::createLambertConicConformal_1SP(PropertyMap(), Angle(40), + Angle(1), Scale(0.99), + Length(3), Length(4)), + CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)); + auto targetConv = projCRS->derivingConversion()->convertToOtherMethod( + EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP); + EXPECT_FALSE(targetConv != nullptr); +} + +// --------------------------------------------------------------------------- + +TEST(operation, lcc1sp_to_lcc2sp_invalid_scale) { + auto projCRS = ProjectedCRS::create( + PropertyMap(), GeographicCRS::EPSG_4326, + Conversion::createLambertConicConformal_1SP( + PropertyMap(), Angle(40), Angle(1), Scale(0), Length(3), Length(4)), + CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)); + auto targetConv = projCRS->derivingConversion()->convertToOtherMethod( + EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP); + EXPECT_FALSE(targetConv != nullptr); +} + +// --------------------------------------------------------------------------- + +TEST(operation, lcc1sp_to_lcc2sp_invalid_lat0) { + auto projCRS = ProjectedCRS::create( + PropertyMap(), GeographicCRS::EPSG_4326, + Conversion::createLambertConicConformal_1SP(PropertyMap(), Angle(100), + Angle(1), Scale(0.99), + Length(3), Length(4)), + CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)); + auto targetConv = projCRS->derivingConversion()->convertToOtherMethod( + EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP); + EXPECT_FALSE(targetConv != nullptr); +} + +// --------------------------------------------------------------------------- + +TEST(operation, lcc1sp_to_lcc2sp_null_lat0) { + auto projCRS = ProjectedCRS::create( + PropertyMap(), GeographicCRS::EPSG_4326, + Conversion::createLambertConicConformal_1SP(PropertyMap(), Angle(0), + Angle(1), Scale(0.99), + Length(3), Length(4)), + CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)); + auto targetConv = projCRS->derivingConversion()->convertToOtherMethod( + EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP); + EXPECT_FALSE(targetConv != nullptr); +} + +// --------------------------------------------------------------------------- + +TEST(operation, lcc2sp_to_lcc1sp_invalid_lat0) { + auto projCRS = ProjectedCRS::create( + PropertyMap(), GeographicCRS::EPSG_4326, + Conversion::createLambertConicConformal_2SP( + PropertyMap(), Angle(100), Angle(3), Angle(44), Angle(49), + Length(700000), Length(6600000)), + CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)); + auto targetConv = projCRS->derivingConversion()->convertToOtherMethod( + EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP); + EXPECT_FALSE(targetConv != nullptr); +} + +// --------------------------------------------------------------------------- + +TEST(operation, lcc2sp_to_lcc1sp_invalid_lat1) { + auto projCRS = ProjectedCRS::create( + PropertyMap(), GeographicCRS::EPSG_4326, + Conversion::createLambertConicConformal_2SP( + PropertyMap(), Angle(46.5), Angle(3), Angle(100), Angle(49), + Length(700000), Length(6600000)), + CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)); + auto targetConv = projCRS->derivingConversion()->convertToOtherMethod( + EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP); + EXPECT_FALSE(targetConv != nullptr); +} + +// --------------------------------------------------------------------------- + +TEST(operation, lcc2sp_to_lcc1sp_invalid_lat2) { + auto projCRS = ProjectedCRS::create( + PropertyMap(), GeographicCRS::EPSG_4326, + Conversion::createLambertConicConformal_2SP( + PropertyMap(), Angle(46.5), Angle(3), Angle(44), Angle(100), + Length(700000), Length(6600000)), + CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)); + auto targetConv = projCRS->derivingConversion()->convertToOtherMethod( + EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP); + EXPECT_FALSE(targetConv != nullptr); +} + +// --------------------------------------------------------------------------- + +TEST(operation, lcc2sp_to_lcc1sp_invalid_lat1_opposite_lat2) { + auto projCRS = ProjectedCRS::create( + PropertyMap(), GeographicCRS::EPSG_4326, + Conversion::createLambertConicConformal_2SP( + PropertyMap(), Angle(46.5), Angle(3), Angle(-49), Angle(49), + Length(700000), Length(6600000)), + CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)); + auto targetConv = projCRS->derivingConversion()->convertToOtherMethod( + EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP); + EXPECT_FALSE(targetConv != nullptr); +} + +// --------------------------------------------------------------------------- + +TEST(operation, lcc2sp_to_lcc1sp_invalid_lat1_and_lat2_close_to_zero) { + auto projCRS = ProjectedCRS::create( + PropertyMap(), GeographicCRS::EPSG_4326, + Conversion::createLambertConicConformal_2SP( + PropertyMap(), Angle(46.5), Angle(3), Angle(.0000000000000001), + Angle(.0000000000000002), Length(700000), Length(6600000)), + CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)); + auto targetConv = projCRS->derivingConversion()->convertToOtherMethod( + EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP); + EXPECT_FALSE(targetConv != nullptr); +} + +// --------------------------------------------------------------------------- + +TEST(operation, lcc2sp_to_lcc1sp_invalid_eccentricity) { + auto projCRS = ProjectedCRS::create( + PropertyMap(), geographicCRSInvalidEccentricity(), + Conversion::createLambertConicConformal_2SP( + PropertyMap(), Angle(46.5), Angle(3), Angle(44), Angle(49), + Length(700000), Length(6600000)), + CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)); + auto targetConv = projCRS->derivingConversion()->convertToOtherMethod( + EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP); + EXPECT_FALSE(targetConv != nullptr); +} |
