Skip to content

Commit

Permalink
WKT1 import: better deal when the angular unit of the GEOGCS[] of the…
Browse files Browse the repository at this point in the history
… PROJCS[] doesn't match the one from the database
  • Loading branch information
rouault committed Aug 1, 2022
1 parent e06520d commit 066736e
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 20 deletions.
2 changes: 2 additions & 0 deletions include/proj/io.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,8 @@ class PROJ_GCC_DLL WKTParser {
PROJ_DLL WKTParser &setStrict(bool strict);
PROJ_DLL std::list<std::string> warningList() const;

PROJ_DLL WKTParser &setUnsetIdentifiersIfIncompatibleDef(bool unset);

PROJ_DLL util::BaseObjectNNPtr
createFromWKT(const std::string &wkt); // throw(ParsingException)

Expand Down
8 changes: 8 additions & 0 deletions src/iso19111/c_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,10 @@ PJ *proj_create(PJ_CONTEXT *ctx, const char *text) {
* <ul>
* <li>STRICT=YES/NO. Defaults to NO. When set to YES, strict validation will
* be enabled.</li>
* <li>UNSET_IDENTIFIERS_IF_INCOMPATIBLE_DEF=YES/NO. Defaults to YES.
* When set to YES, object identifiers are unset when there is
* a contradiction between the definition from WKT and the one from
* the database./<li>
* </ul>
* @param out_warnings Pointer to a PROJ_STRING_LIST object, or NULL.
* If provided, *out_warnings will contain a list of warnings, typically for
Expand Down Expand Up @@ -639,6 +643,10 @@ PJ *proj_create_from_wkt(PJ_CONTEXT *ctx, const char *wkt,
const char *value;
if ((value = getOptionValue(*iter, "STRICT="))) {
parser.setStrict(ci_equal(value, "YES"));
} else if ((value = getOptionValue(
*iter, "UNSET_IDENTIFIERS_IF_INCOMPATIBLE_DEF="))) {
parser.setUnsetIdentifiersIfIncompatibleDef(
ci_equal(value, "YES"));
} else {
std::string msg("Unknown option :");
msg += *iter;
Expand Down
54 changes: 45 additions & 9 deletions src/iso19111/io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1280,6 +1280,7 @@ struct WKTParser::Private {
};

bool strict_ = true;
bool unsetIdentifiersIfIncompatibleDef_ = true;
std::list<std::string> warningList_{};
std::vector<double> toWGS84Parameters_{};
std::string datumPROJ4Grids_{};
Expand Down Expand Up @@ -1503,6 +1504,20 @@ WKTParser &WKTParser::setStrict(bool strict) {

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

/** \brief Set whether object identifiers should be unset when there is
* a contradiction between the definition from WKT and the one from
* the database.
*
* At time of writing, this only applies to the base geographic CRS of a
* projected CRS, when comparing its coordinate system.
*/
WKTParser &WKTParser::setUnsetIdentifiersIfIncompatibleDef(bool unset) {
d->unsetIdentifiersIfIncompatibleDef_ = unset;
return *this;
}

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

/** \brief Return the list of warnings found during parsing.
*
* \note The list might be non-empty only is setStrict(false) has been called.
Expand Down Expand Up @@ -3084,19 +3099,40 @@ WKTParser::Private::buildGeodeticCRS(const WKTNodeNNPtr &node) {
!ellipsoidalCS->_isEquivalentTo(
dbCRS->coordinateSystem().get(),
util::IComparable::Criterion::EQUIVALENT)) {
emitRecoverableWarning(
"Coordinate system of GeographicCRS in the WKT "
"definition is different from the one of the "
"authority. Unsetting the identifier to avoid "
"confusion");
props.unset(Identifier::CODESPACE_KEY);
props.unset(Identifier::AUTHORITY_KEY);
props.unset(IdentifiedObject::IDENTIFIERS_KEY);
if (unsetIdentifiersIfIncompatibleDef_) {
emitRecoverableWarning(
"Coordinate system of GeographicCRS in the WKT "
"definition is different from the one of the "
"authority. Unsetting the identifier to avoid "
"confusion");
props.unset(Identifier::CODESPACE_KEY);
props.unset(Identifier::AUTHORITY_KEY);
props.unset(IdentifiedObject::IDENTIFIERS_KEY);
}
crs = GeographicCRS::create(props, datum, datumEnsemble,
NN_NO_CHECK(ellipsoidalCS));
} else if (dbCRS) {
auto csFromDB = dbCRS->coordinateSystem();
auto csFromDBAltered = csFromDB;
if (!isNull(nodeP->lookForChild(WKTConstants::UNIT))) {
csFromDBAltered =
csFromDB->alterAngularUnit(angularUnit);
if (unsetIdentifiersIfIncompatibleDef_ &&
!csFromDBAltered->_isEquivalentTo(
csFromDB.get(),
util::IComparable::Criterion::EQUIVALENT)) {
emitRecoverableWarning(
"Coordinate system of GeographicCRS in the WKT "
"definition is different from the one of the "
"authority. Unsetting the identifier to avoid "
"confusion");
props.unset(Identifier::CODESPACE_KEY);
props.unset(Identifier::AUTHORITY_KEY);
props.unset(IdentifiedObject::IDENTIFIERS_KEY);
}
}
crs = GeographicCRS::create(props, datum, datumEnsemble,
dbCRS->coordinateSystem());
csFromDBAltered);
}
}
return crs;
Expand Down
33 changes: 33 additions & 0 deletions test/unit/test_c_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,39 @@ TEST_F(CApi, proj_create_from_wkt) {
EXPECT_NE(errorList, nullptr);
proj_string_list_destroy(errorList);
}
{
PROJ_STRING_LIST warningList = nullptr;
PROJ_STRING_LIST errorList = nullptr;
const char *const options[] = {
"UNSET_IDENTIFIERS_IF_INCOMPATIBLE_DEF=NO", nullptr};
auto wkt = "PROJCS[\"Merchich / Nord Maroc\","
" GEOGCS[\"Merchich\","
" DATUM[\"Merchich\","
" SPHEROID[\"Clarke 1880 (IGN)\","
"6378249.2,293.466021293627]],"
" PRIMEM[\"Greenwich\",0],"
" UNIT[\"grad\",0.015707963267949,"
" AUTHORITY[\"EPSG\",\"9105\"]],"
" AUTHORITY[\"EPSG\",\"4261\"]],"
" PROJECTION[\"Lambert_Conformal_Conic_1SP\"],"
" PARAMETER[\"latitude_of_origin\",37],"
" PARAMETER[\"central_meridian\",-6],"
" PARAMETER[\"scale_factor\",0.999625769],"
" PARAMETER[\"false_easting\",500000],"
" PARAMETER[\"false_northing\",300000],"
" UNIT[\"metre\",1,"
" AUTHORITY[\"EPSG\",\"9001\"]],"
" AXIS[\"Easting\",EAST],"
" AXIS[\"Northing\",NORTH]]";
auto obj = proj_create_from_wkt(m_ctxt, wkt, options, &warningList,
&errorList);
ObjectKeeper keeper(obj);
EXPECT_NE(obj, nullptr);
EXPECT_EQ(warningList, nullptr);
proj_string_list_destroy(warningList);
EXPECT_EQ(errorList, nullptr);
proj_string_list_destroy(errorList);
}
{
PROJ_STRING_LIST warningList = nullptr;
PROJ_STRING_LIST errorList = nullptr;
Expand Down
95 changes: 84 additions & 11 deletions test/unit/test_io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1260,19 +1260,92 @@ TEST(wkt_parse, wkt1_projected_wrong_axis_geogcs) {
" UNIT[\"metre\",1,\n"
" AUTHORITY[\"EPSG\",\"9001\"]],\n"
" AUTHORITY[\"EPSG\",\"32631\"]]";
WKTParser parser;
parser.setStrict(false).attachDatabaseContext(DatabaseContext::create());
auto obj = parser.createFromWKT(wkt);
EXPECT_TRUE(!parser.warningList().empty());
auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
ASSERT_TRUE(crs != nullptr);
{
WKTParser parser;
parser.setStrict(false).attachDatabaseContext(
DatabaseContext::create());
auto obj = parser.createFromWKT(wkt);
EXPECT_TRUE(!parser.warningList().empty());
auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
ASSERT_TRUE(crs != nullptr);

EXPECT_TRUE(crs->baseCRS()->identifiers().empty());
EXPECT_TRUE(crs->baseCRS()->identifiers().empty());

auto cs = crs->baseCRS()->coordinateSystem();
ASSERT_EQ(cs->axisList().size(), 2U);
EXPECT_EQ(cs->axisList()[0]->direction(), AxisDirection::EAST);
EXPECT_EQ(cs->axisList()[1]->direction(), AxisDirection::NORTH);
auto cs = crs->baseCRS()->coordinateSystem();
ASSERT_EQ(cs->axisList().size(), 2U);
EXPECT_EQ(cs->axisList()[0]->direction(), AxisDirection::EAST);
EXPECT_EQ(cs->axisList()[1]->direction(), AxisDirection::NORTH);
}
{
WKTParser parser;
parser.setStrict(false)
.setUnsetIdentifiersIfIncompatibleDef(false)
.attachDatabaseContext(DatabaseContext::create());
auto obj = parser.createFromWKT(wkt);
EXPECT_TRUE(parser.warningList().empty());
auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
ASSERT_TRUE(crs != nullptr);

EXPECT_TRUE(!crs->baseCRS()->identifiers().empty());
}
}

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

TEST(wkt_parse, wkt1_projected_wrong_angular_unit) {
auto wkt = "PROJCS[\"Merchich / Nord Maroc\","
" GEOGCS[\"Merchich\","
" DATUM[\"Merchich\","
" SPHEROID[\"Clarke 1880 (IGN)\","
"6378249.2,293.466021293627]],"
" PRIMEM[\"Greenwich\",0],"
" UNIT[\"grad\",0.015707963267949,"
" AUTHORITY[\"EPSG\",\"9105\"]],"
" AUTHORITY[\"EPSG\",\"4261\"]],"
" PROJECTION[\"Lambert_Conformal_Conic_1SP\"],"
" PARAMETER[\"latitude_of_origin\",37],"
" PARAMETER[\"central_meridian\",-6],"
" PARAMETER[\"scale_factor\",0.999625769],"
" PARAMETER[\"false_easting\",500000],"
" PARAMETER[\"false_northing\",300000],"
" UNIT[\"metre\",1,"
" AUTHORITY[\"EPSG\",\"9001\"]],"
" AXIS[\"Easting\",EAST],"
" AXIS[\"Northing\",NORTH]]";
{
WKTParser parser;
parser.setStrict(false).attachDatabaseContext(
DatabaseContext::create());
auto obj = parser.createFromWKT(wkt);
EXPECT_TRUE(!parser.warningList().empty());
auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
ASSERT_TRUE(crs != nullptr);

// No base CRS identifiers
EXPECT_TRUE(crs->baseCRS()->identifiers().empty());

auto cs = crs->baseCRS()->coordinateSystem();
ASSERT_EQ(cs->axisList().size(), 2U);
EXPECT_NEAR(cs->axisList()[0]->unit().conversionToSI(),
UnitOfMeasure::GRAD.conversionToSI(), 1e-10);
}
{
WKTParser parser;
parser.setUnsetIdentifiersIfIncompatibleDef(false)
.attachDatabaseContext(DatabaseContext::create());
auto obj = parser.createFromWKT(wkt);
EXPECT_TRUE(parser.warningList().empty());
auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
ASSERT_TRUE(crs != nullptr);

// Base CRS identifier preserved
EXPECT_TRUE(!crs->baseCRS()->identifiers().empty());

auto cs = crs->baseCRS()->coordinateSystem();
ASSERT_EQ(cs->axisList().size(), 2U);
EXPECT_NEAR(cs->axisList()[0]->unit().conversionToSI(),
UnitOfMeasure::GRAD.conversionToSI(), 1e-10);
}
}

// ---------------------------------------------------------------------------
Expand Down

0 comments on commit 066736e

Please sign in to comment.