Skip to content

Commit

Permalink
Merge pull request #2166 from rouault/lcea_ellipsoidal_wkt1_ingestion
Browse files Browse the repository at this point in the history
Ingestion of WKT1_GDAL: correctly map 'Cylindrical_Equal_Area'
  • Loading branch information
rouault committed Apr 19, 2020
2 parents dca2457 + 2998a46 commit b16b966
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 44 deletions.
6 changes: 0 additions & 6 deletions Doxyfile
Expand Up @@ -230,12 +230,6 @@ TAB_SIZE = 4

ALIASES =

# This tag can be used to specify a number of word-keyword mappings (TCL only).
# A mapping has the form "name=value". For example adding "class=itcl::class"
# will allow you to use the command class in the itcl::class meaning.

TCL_SUBST =

# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
# only. Doxygen will then generate output that is more tailored for C. For
# instance, some of the names that are used will be different. The list of all
Expand Down
10 changes: 4 additions & 6 deletions include/proj/internal/esri_projection_mappings.hpp
Expand Up @@ -929,9 +929,8 @@ 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_SPHERICAL,
EPSG_CODE_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA_SPHERICAL,
paramsESRI_Behrmann},
{"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,
Expand Down Expand Up @@ -973,9 +972,8 @@ static const ESRIMethodMapping esriMappings[] = {
EPSG_NAME_METHOD_LAMBERT_AZIMUTHAL_EQUAL_AREA,
EPSG_CODE_METHOD_LAMBERT_AZIMUTHAL_EQUAL_AREA,
paramsESRI_Lambert_Azimuthal_Equal_Area},
{"Cylindrical_Equal_Area",
EPSG_NAME_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA_SPHERICAL,
EPSG_CODE_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA_SPHERICAL,
{"Cylindrical_Equal_Area", EPSG_NAME_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA,
EPSG_CODE_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA,
paramsESRI_Cylindrical_Equal_Area},
{"Hotine_Oblique_Mercator_Two_Point_Center",
PROJ_WKT2_NAME_METHOD_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN, 0,
Expand Down
4 changes: 2 additions & 2 deletions scripts/build_esri_projection_mapping.py
Expand Up @@ -169,7 +169,7 @@
- Central_Meridian: EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN
- Behrmann:
WKT2_name: EPSG_NAME_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA_SPHERICAL
WKT2_name: EPSG_NAME_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA
Params:
- False_Easting: EPSG_NAME_PARAMETER_FALSE_EASTING
- False_Northing: EPSG_NAME_PARAMETER_FALSE_NORTHING
Expand Down Expand Up @@ -349,7 +349,7 @@
- Latitude_Of_Origin: EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN
- Cylindrical_Equal_Area:
WKT2_name: EPSG_NAME_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA_SPHERICAL
WKT2_name: EPSG_NAME_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA
Params:
- False_Easting: EPSG_NAME_PARAMETER_FALSE_EASTING
- False_Northing: EPSG_NAME_PARAMETER_FALSE_NORTHING
Expand Down
8 changes: 8 additions & 0 deletions src/iso19111/coordinateoperation.cpp
Expand Up @@ -1797,6 +1797,14 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other,
EPSG_CODE_METHOD_LAMBERT_AZIMUTHAL_EQUAL_AREA &&
methodEPSGCode ==
EPSG_CODE_METHOD_LAMBERT_AZIMUTHAL_EQUAL_AREA_SPHERICAL) ||
(methodEPSGCode ==
EPSG_CODE_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA &&
otherMethodEPSGCode ==
EPSG_CODE_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA_SPHERICAL) ||
(otherMethodEPSGCode ==
EPSG_CODE_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA &&
methodEPSGCode ==
EPSG_CODE_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA_SPHERICAL) ||
(methodEPSGCode == EPSG_CODE_METHOD_EQUIDISTANT_CYLINDRICAL &&
otherMethodEPSGCode ==
EPSG_CODE_METHOD_EQUIDISTANT_CYLINDRICAL_SPHERICAL) ||
Expand Down
85 changes: 58 additions & 27 deletions src/iso19111/io.cpp
Expand Up @@ -1298,19 +1298,22 @@ struct WKTParser::Private {
static std::string projectionGetParameter(const WKTNodeNNPtr &projCRSNode,
const char *paramName);

ConversionNNPtr buildProjection(const WKTNodeNNPtr &projCRSNode,
ConversionNNPtr buildProjection(const GeodeticCRSNNPtr &baseGeodCRS,
const WKTNodeNNPtr &projCRSNode,
const WKTNodeNNPtr &projectionNode,
const UnitOfMeasure &defaultLinearUnit,
const UnitOfMeasure &defaultAngularUnit);

ConversionNNPtr
buildProjectionStandard(const WKTNodeNNPtr &projCRSNode,
buildProjectionStandard(const GeodeticCRSNNPtr &baseGeodCRS,
const WKTNodeNNPtr &projCRSNode,
const WKTNodeNNPtr &projectionNode,
const UnitOfMeasure &defaultLinearUnit,
const UnitOfMeasure &defaultAngularUnit);

ConversionNNPtr
buildProjectionFromESRI(const WKTNodeNNPtr &projCRSNode,
buildProjectionFromESRI(const GeodeticCRSNNPtr &baseGeodCRS,
const WKTNodeNNPtr &projCRSNode,
const WKTNodeNNPtr &projectionNode,
const UnitOfMeasure &defaultLinearUnit,
const UnitOfMeasure &defaultAngularUnit);
Expand Down Expand Up @@ -3178,9 +3181,40 @@ bool WKTParser::Private::hasWebMercPROJ4String(

// ---------------------------------------------------------------------------

static const MethodMapping *
selectSphericalOrEllipsoidal(const MethodMapping *mapping,
const GeodeticCRSNNPtr &baseGeodCRS) {
if (mapping->epsg_code ==
EPSG_CODE_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA_SPHERICAL ||
mapping->epsg_code == EPSG_CODE_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA) {
mapping = getMapping(
baseGeodCRS->ellipsoid()->isSphere()
? EPSG_CODE_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA_SPHERICAL
: EPSG_CODE_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA);
} else if (mapping->epsg_code ==
EPSG_CODE_METHOD_LAMBERT_AZIMUTHAL_EQUAL_AREA_SPHERICAL ||
mapping->epsg_code ==
EPSG_CODE_METHOD_LAMBERT_AZIMUTHAL_EQUAL_AREA) {
mapping = getMapping(
baseGeodCRS->ellipsoid()->isSphere()
? EPSG_CODE_METHOD_LAMBERT_AZIMUTHAL_EQUAL_AREA_SPHERICAL
: EPSG_CODE_METHOD_LAMBERT_AZIMUTHAL_EQUAL_AREA);
} else if (mapping->epsg_code ==
EPSG_CODE_METHOD_EQUIDISTANT_CYLINDRICAL_SPHERICAL ||
mapping->epsg_code == EPSG_CODE_METHOD_EQUIDISTANT_CYLINDRICAL) {
mapping =
getMapping(baseGeodCRS->ellipsoid()->isSphere()
? EPSG_CODE_METHOD_EQUIDISTANT_CYLINDRICAL_SPHERICAL
: EPSG_CODE_METHOD_EQUIDISTANT_CYLINDRICAL);
}
return mapping;
}

// ---------------------------------------------------------------------------

ConversionNNPtr WKTParser::Private::buildProjectionFromESRI(
const WKTNodeNNPtr &projCRSNode, const WKTNodeNNPtr &projectionNode,
const UnitOfMeasure &defaultLinearUnit,
const GeodeticCRSNNPtr &baseGeodCRS, const WKTNodeNNPtr &projCRSNode,
const WKTNodeNNPtr &projectionNode, const UnitOfMeasure &defaultLinearUnit,
const UnitOfMeasure &defaultAngularUnit) {
const std::string esriProjectionName =
stripQuotes(projectionNode->GP()->children()[0]);
Expand All @@ -3190,7 +3224,7 @@ ConversionNNPtr WKTParser::Private::buildProjectionFromESRI(
// on the parameters / their values
const auto esriMappings = getMappingsFromESRI(esriProjectionName);
if (esriMappings.empty()) {
return buildProjectionStandard(projCRSNode, projectionNode,
return buildProjectionStandard(baseGeodCRS, projCRSNode, projectionNode,
defaultLinearUnit, defaultAngularUnit);
}

Expand Down Expand Up @@ -3273,6 +3307,8 @@ ConversionNNPtr WKTParser::Private::buildProjectionFromESRI(
}
assert(wkt2_mapping);

wkt2_mapping = selectSphericalOrEllipsoidal(wkt2_mapping, baseGeodCRS);

PropertyMap propertiesMethod;
propertiesMethod.set(IdentifiedObject::NAME_KEY, wkt2_mapping->wkt2_name);
if (wkt2_mapping->epsg_code != 0) {
Expand Down Expand Up @@ -3360,19 +3396,18 @@ ConversionNNPtr WKTParser::Private::buildProjectionFromESRI(

// ---------------------------------------------------------------------------

ConversionNNPtr
WKTParser::Private::buildProjection(const WKTNodeNNPtr &projCRSNode,
const WKTNodeNNPtr &projectionNode,
const UnitOfMeasure &defaultLinearUnit,
const UnitOfMeasure &defaultAngularUnit) {
ConversionNNPtr WKTParser::Private::buildProjection(
const GeodeticCRSNNPtr &baseGeodCRS, const WKTNodeNNPtr &projCRSNode,
const WKTNodeNNPtr &projectionNode, const UnitOfMeasure &defaultLinearUnit,
const UnitOfMeasure &defaultAngularUnit) {
if (projectionNode->GP()->childrenSize() == 0) {
ThrowNotEnoughChildren(WKTConstants::PROJECTION);
}
if (esriStyle_) {
return buildProjectionFromESRI(projCRSNode, projectionNode,
return buildProjectionFromESRI(baseGeodCRS, projCRSNode, projectionNode,
defaultLinearUnit, defaultAngularUnit);
}
return buildProjectionStandard(projCRSNode, projectionNode,
return buildProjectionStandard(baseGeodCRS, projCRSNode, projectionNode,
defaultLinearUnit, defaultAngularUnit);
}

Expand All @@ -3397,8 +3432,8 @@ WKTParser::Private::projectionGetParameter(const WKTNodeNNPtr &projCRSNode,
// ---------------------------------------------------------------------------

ConversionNNPtr WKTParser::Private::buildProjectionStandard(
const WKTNodeNNPtr &projCRSNode, const WKTNodeNNPtr &projectionNode,
const UnitOfMeasure &defaultLinearUnit,
const GeodeticCRSNNPtr &baseGeodCRS, const WKTNodeNNPtr &projCRSNode,
const WKTNodeNNPtr &projectionNode, const UnitOfMeasure &defaultLinearUnit,
const UnitOfMeasure &defaultAngularUnit) {
std::string wkt1ProjectionName =
stripQuotes(projectionNode->GP()->children()[0]);
Expand Down Expand Up @@ -3516,6 +3551,9 @@ ConversionNNPtr WKTParser::Private::buildProjectionStandard(
std::string projectionName(wkt1ProjectionName);
const MethodMapping *mapping =
tryToIdentifyWKT1Method ? getMappingFromWKT1(projectionName) : nullptr;
if (mapping) {
mapping = selectSphericalOrEllipsoidal(mapping, baseGeodCRS);
}

// For Krovak, we need to look at axis to decide between the Krovak and
// Krovak East-North Oriented methods
Expand Down Expand Up @@ -3768,7 +3806,8 @@ WKTParser::Private::buildProjectedCRS(const WKTNodeNNPtr &node) {
auto conversion =
!isNull(conversionNode)
? buildConversion(conversionNode, linearUnit, angularUnit)
: buildProjection(node, projectionNode, linearUnit, angularUnit);
: buildProjection(baseGeodCRS, node, projectionNode, linearUnit,
angularUnit);

// No explicit AXIS node ? (WKT1)
if (isNull(nodeP->lookForChild(WKTConstants::AXIS))) {
Expand Down Expand Up @@ -8677,6 +8716,9 @@ CRSNNPtr PROJStringParser::Private::buildProjectedCRS(
auto &step = steps_[iStep];
auto mappings = getMappingsFromPROJName(step.name);
const MethodMapping *mapping = mappings.empty() ? nullptr : mappings[0];
if (mapping) {
mapping = selectSphericalOrEllipsoidal(mapping, geogCRS);
}

assert(isProjectedStep(step.name));
assert(iUnitConvert < 0 ||
Expand Down Expand Up @@ -8722,8 +8764,6 @@ CRSNNPtr PROJStringParser::Private::buildProjectedCRS(
}
} else if (step.name == "aeqd" && hasParamValue(step, "guam")) {
mapping = getMapping(EPSG_CODE_METHOD_GUAM_PROJECTION);
} else if (step.name == "cea" && !geogCRS->ellipsoid()->isSphere()) {
mapping = getMapping(EPSG_CODE_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA);
} else if (step.name == "geos" && getParamValue(step, "sweep") == "x") {
mapping =
getMapping(PROJ_WKT2_NAME_METHOD_GEOSTATIONARY_SATELLITE_SWEEP_X);
Expand Down Expand Up @@ -8829,15 +8869,6 @@ CRSNNPtr PROJStringParser::Private::buildProjectedCRS(
axisType = AxisType::SOUTH_POLE;
}
}
if (geogCRS->ellipsoid()->isSphere()) {
mapping = getMapping(
EPSG_CODE_METHOD_LAMBERT_AZIMUTHAL_EQUAL_AREA_SPHERICAL);
}
} else if (step.name == "eqc") {
if (geogCRS->ellipsoid()->isSphere()) {
mapping =
getMapping(EPSG_CODE_METHOD_EQUIDISTANT_CYLINDRICAL_SPHERICAL);
}
}

UnitOfMeasure unit = buildUnit(step, "units", "to_meter");
Expand Down
50 changes: 48 additions & 2 deletions test/unit/test_io.cpp
Expand Up @@ -4475,7 +4475,7 @@ static const struct {

{"Behrmann",
{{"False_Easting", 1}, {"False_Northing", 2}, {"Central_Meridian", 3}},
"Lambert Cylindrical Equal Area (Spherical)",
"Lambert Cylindrical Equal Area",
{
{"Latitude of 1st standard parallel", 30},
{"Longitude of natural origin", 3},
Expand Down Expand Up @@ -4776,7 +4776,7 @@ static const struct {
{"False_Northing", 2},
{"Central_Meridian", 3},
{"Standard_Parallel_1", 4}},
"Lambert Cylindrical Equal Area (Spherical)",
"Lambert Cylindrical Equal Area",
{
{"Latitude of 1st standard parallel", 4},
{"Longitude of natural origin", 3},
Expand Down Expand Up @@ -8128,6 +8128,30 @@ TEST(io, projparse_aeqd_guam) {

// ---------------------------------------------------------------------------

TEST(io, projparse_cea_spherical) {
auto obj = PROJStringParser().createFromPROJString(
"+proj=cea +R=6371228 +type=crs");
auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
ASSERT_TRUE(crs != nullptr);
EXPECT_EQ(crs->derivingConversion()->method()->getEPSGCode(),
EPSG_CODE_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA_SPHERICAL);

auto crs2 = ProjectedCRS::create(
PropertyMap(), crs->baseCRS(),
Conversion::createLambertCylindricalEqualArea(
PropertyMap(), Angle(0), Angle(0), Length(0), Length(0)),
crs->coordinateSystem());
EXPECT_EQ(crs2->derivingConversion()->method()->getEPSGCode(),
EPSG_CODE_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA);

EXPECT_TRUE(
crs->isEquivalentTo(crs2.get(), IComparable::Criterion::EQUIVALENT));
EXPECT_TRUE(
crs2->isEquivalentTo(crs.get(), IComparable::Criterion::EQUIVALENT));
}

// ---------------------------------------------------------------------------

TEST(io, projparse_cea_ellipsoidal) {
auto obj = PROJStringParser().createFromPROJString(
"+proj=cea +ellps=GRS80 +type=crs");
Expand Down Expand Up @@ -8651,6 +8675,17 @@ TEST(io, projparse_laea_spherical) {

// ---------------------------------------------------------------------------

TEST(io, projparse_laea_ellipsoidal) {
auto obj = PROJStringParser().createFromPROJString(
"+proj=laea +ellps=WGS84 +type=crs");
auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
ASSERT_TRUE(crs != nullptr);
EXPECT_EQ(crs->derivingConversion()->method()->getEPSGCode(),
EPSG_CODE_METHOD_LAMBERT_AZIMUTHAL_EQUAL_AREA);
}

// ---------------------------------------------------------------------------

TEST(io, projparse_eqc_spherical) {
auto obj = PROJStringParser().createFromPROJString(
"+proj=eqc +R=6371228 +type=crs");
Expand All @@ -8675,6 +8710,17 @@ TEST(io, projparse_eqc_spherical) {

// ---------------------------------------------------------------------------

TEST(io, projparse_eqc_ellipsoidal) {
auto obj = PROJStringParser().createFromPROJString(
"+proj=eqc +ellps=WGS84 +type=crs");
auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
ASSERT_TRUE(crs != nullptr);
EXPECT_EQ(crs->derivingConversion()->method()->getEPSGCode(),
EPSG_CODE_METHOD_EQUIDISTANT_CYLINDRICAL);
}

// ---------------------------------------------------------------------------

TEST(io, projparse_non_earth_ellipsoid) {
std::string input("+proj=merc +lon_0=0 +k=1 +x_0=0 +y_0=0 +R=1 +units=m "
"+no_defs +type=crs");
Expand Down
2 changes: 1 addition & 1 deletion travis/osx/before_install.sh
Expand Up @@ -8,7 +8,7 @@ brew update
brew install ccache
#brew upgrade sqlite3
#brew upgrade libtiff
brew install doxygen
brew install doxygen graphviz
#brew install md5sha1sum
#brew reinstall python
brew reinstall wget
Expand Down

0 comments on commit b16b966

Please sign in to comment.