From 16eb426ca6e4839cd5100730579d2a74cd03a3ba Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Sat, 23 Nov 2024 13:36:21 +0100 Subject: [PATCH] WKT1 ESRI export: fix wrong mapping of Lambert Cylindrical Equal Area to Behrmann --- scripts/build_esri_projection_mapping.py | 43 +++++++++++--------- src/iso19111/operation/conversion.cpp | 10 +++++ src/iso19111/operation/esriparammappings.cpp | 40 +++++++++--------- test/unit/test_io.cpp | 35 ++++++++++++++-- 4 files changed, 84 insertions(+), 44 deletions(-) diff --git a/scripts/build_esri_projection_mapping.py b/scripts/build_esri_projection_mapping.py index 5b1a2535db..58c1d59984 100644 --- a/scripts/build_esri_projection_mapping.py +++ b/scripts/build_esri_projection_mapping.py @@ -34,6 +34,15 @@ # Map methods from pe_list_projection.csv to WKT2 naming config_str = """ + +- Equidistant_Cylindrical: + WKT2_name: EPSG_NAME_METHOD_EQUIDISTANT_CYLINDRICAL + Params: + - False_Easting: EPSG_NAME_PARAMETER_FALSE_EASTING + - False_Northing: EPSG_NAME_PARAMETER_FALSE_NORTHING + - Central_Meridian: EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN + - Standard_Parallel_1: EPSG_NAME_PARAMETER_LATITUDE_1ST_STD_PARALLEL + - Plate_Carree: WKT2_name: - EPSG_NAME_METHOD_EQUIDISTANT_CYLINDRICAL @@ -45,14 +54,6 @@ Cond: - EPSG_NAME_PARAMETER_LATITUDE_1ST_STD_PARALLEL = 0 -- Equidistant_Cylindrical: - WKT2_name: EPSG_NAME_METHOD_EQUIDISTANT_CYLINDRICAL - Params: - - False_Easting: EPSG_NAME_PARAMETER_FALSE_EASTING - - False_Northing: EPSG_NAME_PARAMETER_FALSE_NORTHING - - Central_Meridian: EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN - - Standard_Parallel_1: EPSG_NAME_PARAMETER_LATITUDE_1ST_STD_PARALLEL - - Miller_Cylindrical: WKT2_name: PROJ_WKT2_NAME_METHOD_MILLER_CYLINDRICAL Params: @@ -168,18 +169,6 @@ - False_Northing: EPSG_NAME_PARAMETER_FALSE_NORTHING - Central_Meridian: EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN -- Behrmann: - WKT2_name: EPSG_NAME_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA - Params: - - False_Easting: EPSG_NAME_PARAMETER_FALSE_EASTING - - False_Northing: EPSG_NAME_PARAMETER_FALSE_NORTHING - - Central_Meridian: - Name: EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN - Default: 0.0 - - Standard_Parallel_1: - Name: EPSG_NAME_PARAMETER_LATITUDE_1ST_STD_PARALLEL - Default: 30.0 - - Winkel_I: WKT2_name: "Winkel I" Params: @@ -365,6 +354,20 @@ - Central_Meridian: EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN - Standard_Parallel_1: EPSG_NAME_PARAMETER_LATITUDE_1ST_STD_PARALLEL +- Behrmann: + WKT2_name: EPSG_NAME_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA + Params: + - False_Easting: EPSG_NAME_PARAMETER_FALSE_EASTING + - False_Northing: EPSG_NAME_PARAMETER_FALSE_NORTHING + - Central_Meridian: + Name: EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN + Default: 0.0 + - Standard_Parallel_1: + Name: EPSG_NAME_PARAMETER_LATITUDE_1ST_STD_PARALLEL + Default: 30.0 + Cond: + - EPSG_NAME_PARAMETER_LATITUDE_1ST_STD_PARALLEL = 30 + # No example in pe_list_projection.csv: temptative mapping ! - Hotine_Oblique_Mercator_Two_Point_Center: WKT2_name: PROJ_WKT2_NAME_METHOD_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN diff --git a/src/iso19111/operation/conversion.cpp b/src/iso19111/operation/conversion.cpp index 60d79d6242..d26578b4d7 100644 --- a/src/iso19111/operation/conversion.cpp +++ b/src/iso19111/operation/conversion.cpp @@ -3206,6 +3206,16 @@ static void getESRIMethodNameAndParams(const Conversion *conv, } else { esriMethodName = "Stereographic_South_Pole"; } + } else if (esriMapping->epsg_code == + EPSG_CODE_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA) { + if (std::abs(conv->parameterValueNumeric( + EPSG_CODE_PARAMETER_LATITUDE_1ST_STD_PARALLEL, + common::UnitOfMeasure::DEGREE) - + 30.0) < 1e-10) { + esriMethodName = "Behrmann"; + } else { + esriMethodName = "Cylindrical_Equal_Area"; + } } } } diff --git a/src/iso19111/operation/esriparammappings.cpp b/src/iso19111/operation/esriparammappings.cpp index 415828c5e0..88f3f02e50 100644 --- a/src/iso19111/operation/esriparammappings.cpp +++ b/src/iso19111/operation/esriparammappings.cpp @@ -46,24 +46,24 @@ namespace operation { //! @cond Doxygen_Suppress -const ESRIParamMapping paramsESRI_Plate_Carree[] = { +const ESRIParamMapping paramsESRI_Equidistant_Cylindrical[] = { {"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}, + {"Standard_Parallel_1", EPSG_NAME_PARAMETER_LATITUDE_1ST_STD_PARALLEL, + EPSG_CODE_PARAMETER_LATITUDE_1ST_STD_PARALLEL, "0.0", false}, {nullptr, nullptr, 0, "0.0", false}}; -const ESRIParamMapping paramsESRI_Equidistant_Cylindrical[] = { +const ESRIParamMapping paramsESRI_Plate_Carree[] = { {"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}, - {"Standard_Parallel_1", EPSG_NAME_PARAMETER_LATITUDE_1ST_STD_PARALLEL, - EPSG_CODE_PARAMETER_LATITUDE_1ST_STD_PARALLEL, "0.0", false}, {nullptr, nullptr, 0, "0.0", false}}; static const ESRIParamMapping paramsESRI_Miller_Cylindrical[] = { @@ -221,17 +221,6 @@ static const ESRIParamMapping paramsESRI_Gall_Stereographic[] = { EPSG_CODE_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN, "0.0", false}, {nullptr, nullptr, 0, "0.0", false}}; -static const ESRIParamMapping paramsESRI_Behrmann[] = { - {"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", true}, - {"Standard_Parallel_1", EPSG_NAME_PARAMETER_LATITUDE_1ST_STD_PARALLEL, - EPSG_CODE_PARAMETER_LATITUDE_1ST_STD_PARALLEL, "30.0", true}, - {nullptr, nullptr, 0, "0.0", false}}; - static const ESRIParamMapping paramsESRI_Winkel_I[] = { {"False_Easting", EPSG_NAME_PARAMETER_FALSE_EASTING, EPSG_CODE_PARAMETER_FALSE_EASTING, "0.0", false}, @@ -487,6 +476,17 @@ static const ESRIParamMapping paramsESRI_Cylindrical_Equal_Area[] = { EPSG_CODE_PARAMETER_LATITUDE_1ST_STD_PARALLEL, "0.0", false}, {nullptr, nullptr, 0, "0.0", false}}; +static const ESRIParamMapping paramsESRI_Behrmann[] = { + {"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", true}, + {"Standard_Parallel_1", EPSG_NAME_PARAMETER_LATITUDE_1ST_STD_PARALLEL, + EPSG_CODE_PARAMETER_LATITUDE_1ST_STD_PARALLEL, "30.0", true}, + {nullptr, nullptr, 0, "0.0", false}}; + static const ESRIParamMapping paramsESRI_Hotine_Oblique_Mercator_Two_Point_Center[] = { {"False_Easting", EPSG_NAME_PARAMETER_EASTING_PROJECTION_CENTRE, @@ -990,14 +990,14 @@ static const ESRIParamMapping paramsESRI_Peirce_Quincuncial_alt2[] = { {nullptr, nullptr, 0, "0.0", false}}; static const ESRIMethodMapping esriMappings[] = { + {"Equidistant_Cylindrical", EPSG_NAME_METHOD_EQUIDISTANT_CYLINDRICAL, + EPSG_CODE_METHOD_EQUIDISTANT_CYLINDRICAL, + paramsESRI_Equidistant_Cylindrical}, {"Plate_Carree", EPSG_NAME_METHOD_EQUIDISTANT_CYLINDRICAL, EPSG_CODE_METHOD_EQUIDISTANT_CYLINDRICAL, paramsESRI_Plate_Carree}, {"Plate_Carree", EPSG_NAME_METHOD_EQUIDISTANT_CYLINDRICAL_SPHERICAL, EPSG_CODE_METHOD_EQUIDISTANT_CYLINDRICAL_SPHERICAL, paramsESRI_Plate_Carree}, - {"Equidistant_Cylindrical", EPSG_NAME_METHOD_EQUIDISTANT_CYLINDRICAL, - EPSG_CODE_METHOD_EQUIDISTANT_CYLINDRICAL, - paramsESRI_Equidistant_Cylindrical}, {"Miller_Cylindrical", PROJ_WKT2_NAME_METHOD_MILLER_CYLINDRICAL, 0, paramsESRI_Miller_Cylindrical}, {"Mercator", EPSG_NAME_METHOD_MERCATOR_VARIANT_B, @@ -1021,8 +1021,6 @@ static const ESRIMethodMapping esriMappings[] = { {"Eckert_VI", PROJ_WKT2_NAME_METHOD_ECKERT_VI, 0, paramsESRI_Eckert_VI}, {"Gall_Stereographic", PROJ_WKT2_NAME_METHOD_GALL_STEREOGRAPHIC, 0, paramsESRI_Gall_Stereographic}, - {"Behrmann", EPSG_NAME_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA, - EPSG_CODE_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA, paramsESRI_Behrmann}, {"Winkel_I", "Winkel I", 0, paramsESRI_Winkel_I}, {"Winkel_II", "Winkel II", 0, paramsESRI_Winkel_II}, {"Lambert_Conformal_Conic", EPSG_NAME_METHOD_LAMBERT_CONIC_CONFORMAL_1SP, @@ -1070,6 +1068,8 @@ static const ESRIMethodMapping esriMappings[] = { {"Cylindrical_Equal_Area", EPSG_NAME_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA, EPSG_CODE_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA, paramsESRI_Cylindrical_Equal_Area}, + {"Behrmann", EPSG_NAME_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA, + EPSG_CODE_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA, paramsESRI_Behrmann}, {"Hotine_Oblique_Mercator_Two_Point_Center", PROJ_WKT2_NAME_METHOD_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN, 0, paramsESRI_Hotine_Oblique_Mercator_Two_Point_Center}, diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp index 9cf4bfcea1..e356bc9a3b 100644 --- a/test/unit/test_io.cpp +++ b/test/unit/test_io.cpp @@ -7791,10 +7791,15 @@ static const struct { TEST(wkt_parse, esri_projcs) { for (const auto &projDef : esriProjDefs) { - std::string wkt("PROJCS[\"unnamed\",GEOGCS[\"unnamed\"," - "DATUM[\"unnamed\",SPHEROID[\"unnamed\"," - "6378137.0,298.257223563]],PRIMEM[\"Greenwich\",0.0]," - "UNIT[\"Degree\",0.0174532925199433]],PROJECTION[\""); + std::string wkt("PROJCS[\""); + if (strcmp(projDef.esriProjectionName, "Plate_Carree") == 0) + wkt += "Plate Carree"; + else + wkt += "unnamed"; + wkt += "\",GEOGCS[\"unnamed\"," + "DATUM[\"unnamed\",SPHEROID[\"unnamed\"," + "6378137.0,298.257223563]],PRIMEM[\"Greenwich\",0.0]," + "UNIT[\"Degree\",0.0174532925199433]],PROJECTION[\""; wkt += projDef.esriProjectionName; wkt += "\"],"; for (const auto ¶m : projDef.esriParams) { @@ -7829,6 +7834,28 @@ TEST(wkt_parse, esri_projcs) { EXPECT_EQ(measure.value(), projDef.wkt2Params[i].second) << wkt; } } + + auto wkt1Esri = crs->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT1_ESRI).get()); + const char *expectedESRIProjectionName = projDef.esriProjectionName; + // Not totally sure about the below exceptions. They just capture the + // current state of things. + if (strcmp(projDef.esriProjectionName, "Transverse_Mercator_Complex") == + 0) + expectedESRIProjectionName = "Transverse_Mercator"; + else if (strcmp(projDef.esriProjectionName, + "Equidistant_Cylindrical_Ellipsoidal") == 0) + expectedESRIProjectionName = "Equidistant_Cylindrical"; + else if (strcmp(projDef.esriProjectionName, "Mercator_Variant_C") == 0) + expectedESRIProjectionName = "Mercator"; + else if (strcmp(projDef.esriProjectionName, "Gnomonic_Ellipsoidal") == + 0) + expectedESRIProjectionName = "Gnomonic"; + EXPECT_TRUE(wkt1Esri.find(std::string("PROJECTION[\"") + .append(expectedESRIProjectionName)) != + std::string::npos) + << "input: " << wkt << std::endl + << "output: " << wkt1Esri; } }