diff --git a/scripts/build_esri_projection_mapping.py b/scripts/build_esri_projection_mapping.py index 32920a599b..e1fa3400f1 100644 --- a/scripts/build_esri_projection_mapping.py +++ b/scripts/build_esri_projection_mapping.py @@ -289,6 +289,15 @@ - Scale_Factor: EPSG_NAME_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN - Latitude_Of_Origin: EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN +- Polar_Stereographic_Variant_A: + WKT2_name: EPSG_NAME_METHOD_POLAR_STEREOGRAPHIC_VARIANT_A + Params: + - False_Easting: EPSG_NAME_PARAMETER_FALSE_EASTING + - False_Northing: EPSG_NAME_PARAMETER_FALSE_NORTHING + - Central_Meridian: EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN + - Scale_Factor: EPSG_NAME_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN + - Latitude_Of_Origin: EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN + - Equidistant_Conic: WKT2_name: PROJ_WKT2_NAME_METHOD_EQUIDISTANT_CONIC Params: @@ -740,7 +749,6 @@ # Missing mappings # Transverse_Mercator_NGA_2014: utm -- tricky mapping from Central_Meridian to zone -# Polar_Stereographic_Variant_A: ups -- tricky mapping from Latitude_Of_Origin to "+south" when required # Transverse Mercator: alias for Transverse_Mercator, as seen in ESRI:102470 - ESRI:102489 diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp index e63117b200..d7fcea4933 100644 --- a/src/iso19111/io.cpp +++ b/src/iso19111/io.cpp @@ -4388,15 +4388,7 @@ WKTParser::Private::buildProjectedCRS(const WKTNodeNNPtr &node) { const std::string projCRSName = stripQuotes(nodeP->children()[0]); if (esriStyle_ && dbContext_) { - // It is likely that the ESRI definition of EPSG:32661 (UPS North) & - // EPSG:32761 (UPS South) uses the easting-northing order, instead - // of the EPSG northing-easting order - // so don't substitute names to avoid confusion. - if (projCRSName == "UPS_North") { - props.set(IdentifiedObject::NAME_KEY, "WGS 84 / UPS North (E,N)"); - } else if (projCRSName == "UPS_South") { - props.set(IdentifiedObject::NAME_KEY, "WGS 84 / UPS South (E,N)"); - } else if (cartesianCS) { + if (cartesianCS) { std::string outTableName; std::string authNameFromAlias; std::string codeFromAlias; diff --git a/src/iso19111/operation/conversion.cpp b/src/iso19111/operation/conversion.cpp index 10d4114e14..69d86da6fa 100644 --- a/src/iso19111/operation/conversion.cpp +++ b/src/iso19111/operation/conversion.cpp @@ -3085,6 +3085,19 @@ static void getESRIMethodNameAndParams(const Conversion *conv, esriParams = paramsESRI_Rectified_Skew_Orthomorphic_Center; esriMethodName = "Rectified_Skew_Orthomorphic_Center"; } + } else if (esriMapping->epsg_code == + EPSG_CODE_METHOD_POLAR_STEREOGRAPHIC_VARIANT_A) { + // Quite empiric, but looking at pe_list_projcs.csv, the only + // CRS that use Polar_Stereographic_Variant_A are EPSG:5041 and 5042 + if (l_targetCRS && + // EPSG:5041 + (l_targetCRS->nameStr() == "WGS 84 / UPS North (E,N)" || + // EPSG:5042 + l_targetCRS->nameStr() == "WGS 84 / UPS South (E,N)")) { + esriMethodName = "Polar_Stereographic_Variant_A"; + } else { + esriMethodName = "Stereographic"; + } } else if (esriMapping->epsg_code == EPSG_CODE_METHOD_POLAR_STEREOGRAPHIC_VARIANT_B) { if (conv->parameterValueNumericAsSI( diff --git a/src/iso19111/operation/esriparammappings.cpp b/src/iso19111/operation/esriparammappings.cpp index a6f6c6a120..278992adae 100644 --- a/src/iso19111/operation/esriparammappings.cpp +++ b/src/iso19111/operation/esriparammappings.cpp @@ -385,6 +385,19 @@ static const ESRIParamMapping paramsESRI_Stereographic[] = { EPSG_CODE_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN, "0.0", false}, {nullptr, nullptr, 0, "0.0", false}}; +static const ESRIParamMapping paramsESRI_Polar_Stereographic_Variant_A[] = { + {"False_Easting", EPSG_NAME_PARAMETER_FALSE_EASTING, + EPSG_CODE_PARAMETER_FALSE_EASTING, "0.0", false}, + {"False_Northing", EPSG_NAME_PARAMETER_FALSE_NORTHING, + EPSG_CODE_PARAMETER_FALSE_NORTHING, "0.0", false}, + {"Central_Meridian", EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN, + EPSG_CODE_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN, "0.0", false}, + {"Scale_Factor", EPSG_NAME_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN, + EPSG_CODE_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN, "0.0", false}, + {"Latitude_Of_Origin", EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN, + EPSG_CODE_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN, "0.0", false}, + {nullptr, nullptr, 0, "0.0", false}}; + static const ESRIParamMapping paramsESRI_Equidistant_Conic[] = { {"False_Easting", EPSG_NAME_PARAMETER_FALSE_EASTING, EPSG_CODE_PARAMETER_FALSE_EASTING, "0.0", false}, @@ -1033,6 +1046,10 @@ static const ESRIMethodMapping esriMappings[] = { paramsESRI_Hotine_Oblique_Mercator_Two_Point_Natural_Origin}, {"Stereographic", PROJ_WKT2_NAME_METHOD_STEREOGRAPHIC, 0, paramsESRI_Stereographic}, + {"Polar_Stereographic_Variant_A", + EPSG_NAME_METHOD_POLAR_STEREOGRAPHIC_VARIANT_A, + EPSG_CODE_METHOD_POLAR_STEREOGRAPHIC_VARIANT_A, + paramsESRI_Polar_Stereographic_Variant_A}, {"Equidistant_Conic", PROJ_WKT2_NAME_METHOD_EQUIDISTANT_CONIC, 0, paramsESRI_Equidistant_Conic}, {"Cassini", EPSG_NAME_METHOD_CASSINI_SOLDNER, diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp index f2e6d3adc4..2d8626e29b 100644 --- a/test/unit/test_io.cpp +++ b/test/unit/test_io.cpp @@ -7307,6 +7307,7 @@ TEST(wkt_parse, wkt1_esri_normalize_unit) { // --------------------------------------------------------------------------- TEST(wkt_parse, wkt1_esri_ups_north) { + // EPSG:32661 auto wkt = "PROJCS[\"UPS_North\",GEOGCS[\"GCS_WGS_1984\"," "DATUM[\"D_WGS_1984\"," "SPHEROID[\"WGS_1984\",6378137.0,298.257223563]]," @@ -7319,24 +7320,32 @@ TEST(wkt_parse, wkt1_esri_ups_north) { "PARAMETER[\"Latitude_Of_Origin\",90.0]," "UNIT[\"Meter\",1.0]]"; - auto obj = WKTParser() - .attachDatabaseContext(DatabaseContext::create()) - .createFromWKT(wkt); + auto dbContext = DatabaseContext::create(); + auto obj = WKTParser().attachDatabaseContext(dbContext).createFromWKT(wkt); auto crs = nn_dynamic_pointer_cast(obj); ASSERT_TRUE(crs != nullptr); - EXPECT_EQ(crs->nameStr(), "WGS 84 / UPS North (E,N)"); + EXPECT_EQ(crs->nameStr(), "WGS 84 / UPS North (N,E)"); EXPECT_EQ(crs->coordinateSystem()->axisList()[0]->direction(), AxisDirection::SOUTH); + // Yes, inconsistency between the name (coming from EPSG) and the fact + // that with ESRI CRS, we always output E, N axis order EXPECT_EQ(crs->coordinateSystem()->axisList()[0]->abbreviation(), "E"); EXPECT_EQ(crs->coordinateSystem()->axisList()[1]->direction(), AxisDirection::SOUTH); EXPECT_EQ(crs->coordinateSystem()->axisList()[1]->abbreviation(), "N"); + + EXPECT_EQ( + crs->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_ESRI, dbContext) + .get()), + wkt); } // --------------------------------------------------------------------------- TEST(wkt_parse, wkt1_esri_ups_south) { + // EPSG:32671 auto wkt = "PROJCS[\"UPS_South\",GEOGCS[\"GCS_WGS_1984\"," "DATUM[\"D_WGS_1984\"," "SPHEROID[\"WGS_1984\",6378137.0,298.257223563]]," @@ -7349,9 +7358,84 @@ TEST(wkt_parse, wkt1_esri_ups_south) { "PARAMETER[\"Latitude_Of_Origin\",-90.0]," "UNIT[\"Meter\",1.0]]"; - auto obj = WKTParser() - .attachDatabaseContext(DatabaseContext::create()) - .createFromWKT(wkt); + auto dbContext = DatabaseContext::create(); + auto obj = WKTParser().attachDatabaseContext(dbContext).createFromWKT(wkt); + auto crs = nn_dynamic_pointer_cast(obj); + ASSERT_TRUE(crs != nullptr); + + EXPECT_EQ(crs->nameStr(), "WGS 84 / UPS South (N,E)"); + EXPECT_EQ(crs->coordinateSystem()->axisList()[0]->direction(), + AxisDirection::NORTH); + // Yes, inconsistency between the name (coming from EPSG) and the fact + // that with ESRI CRS, we always output E, N axis order + EXPECT_EQ(crs->coordinateSystem()->axisList()[0]->abbreviation(), "E"); + EXPECT_EQ(crs->coordinateSystem()->axisList()[1]->direction(), + AxisDirection::NORTH); + EXPECT_EQ(crs->coordinateSystem()->axisList()[1]->abbreviation(), "N"); + + EXPECT_EQ( + crs->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_ESRI, dbContext) + .get()), + wkt); +} + +// --------------------------------------------------------------------------- + +TEST(wkt_parse, wkt1_esri_wgs_1984_ups_north_E_N) { + // EPSG:5041 + auto wkt = "PROJCS[\"WGS_1984_UPS_North_(E-N)\"," + "GEOGCS[\"GCS_WGS_1984\",DATUM[\"D_WGS_1984\"," + "SPHEROID[\"WGS_1984\",6378137.0,298.257223563]]," + "PRIMEM[\"Greenwich\",0.0]," + "UNIT[\"Degree\",0.0174532925199433]]," + "PROJECTION[\"Polar_Stereographic_Variant_A\"]," + "PARAMETER[\"False_Easting\",2000000.0]," + "PARAMETER[\"False_Northing\",2000000.0]," + "PARAMETER[\"Central_Meridian\",0.0]," + "PARAMETER[\"Scale_Factor\",0.994]," + "PARAMETER[\"Latitude_Of_Origin\",90.0]," + "UNIT[\"Meter\",1.0]]"; + + auto dbContext = DatabaseContext::create(); + auto obj = WKTParser().attachDatabaseContext(dbContext).createFromWKT(wkt); + auto crs = nn_dynamic_pointer_cast(obj); + ASSERT_TRUE(crs != nullptr); + + EXPECT_EQ(crs->nameStr(), "WGS 84 / UPS North (E,N)"); + EXPECT_EQ(crs->coordinateSystem()->axisList()[0]->direction(), + AxisDirection::SOUTH); + EXPECT_EQ(crs->coordinateSystem()->axisList()[0]->abbreviation(), "E"); + EXPECT_EQ(crs->coordinateSystem()->axisList()[1]->direction(), + AxisDirection::SOUTH); + EXPECT_EQ(crs->coordinateSystem()->axisList()[1]->abbreviation(), "N"); + + EXPECT_EQ( + crs->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_ESRI, dbContext) + .get()), + wkt); +} + +// --------------------------------------------------------------------------- + +TEST(wkt_parse, wkt1_esri_wgs_1984_ups_south_E_N) { + // EPSG:5042 + auto wkt = "PROJCS[\"WGS_1984_UPS_South_(E-N)\"," + "GEOGCS[\"GCS_WGS_1984\",DATUM[\"D_WGS_1984\"," + "SPHEROID[\"WGS_1984\",6378137.0,298.257223563]]," + "PRIMEM[\"Greenwich\",0.0]," + "UNIT[\"Degree\",0.0174532925199433]]," + "PROJECTION[\"Polar_Stereographic_Variant_A\"]," + "PARAMETER[\"False_Easting\",2000000.0]," + "PARAMETER[\"False_Northing\",2000000.0]," + "PARAMETER[\"Central_Meridian\",0.0]," + "PARAMETER[\"Scale_Factor\",0.994]," + "PARAMETER[\"Latitude_Of_Origin\",-90.0]," + "UNIT[\"Meter\",1.0]]"; + + auto dbContext = DatabaseContext::create(); + auto obj = WKTParser().attachDatabaseContext(dbContext).createFromWKT(wkt); auto crs = nn_dynamic_pointer_cast(obj); ASSERT_TRUE(crs != nullptr); @@ -7362,6 +7446,12 @@ TEST(wkt_parse, wkt1_esri_ups_south) { EXPECT_EQ(crs->coordinateSystem()->axisList()[1]->direction(), AxisDirection::NORTH); EXPECT_EQ(crs->coordinateSystem()->axisList()[1]->abbreviation(), "N"); + + EXPECT_EQ( + crs->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_ESRI, dbContext) + .get()), + wkt); } // ---------------------------------------------------------------------------