Skip to content

Commit

Permalink
Merge pull request #3691 from OSGeo/backport-3685-to-9.2
Browse files Browse the repository at this point in the history
[Backport 9.2] EngineeringCRS: make proj_create_engineering_crs() set a datum name, …
  • Loading branch information
rouault committed Apr 1, 2023
2 parents 1a8b8c2 + cb79c5c commit d08d94b
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 10 deletions.
38 changes: 38 additions & 0 deletions include/proj/internal/datum_internal.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/******************************************************************************
*
* Project: PROJ
* Purpose: ISO19111:2019 implementation
* Author: Even Rouault <even dot rouault at spatialys dot com>
*
******************************************************************************
* Copyright (c) 2023, 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.
****************************************************************************/

#ifndef FROM_PROJ_CPP
#error This file should only be included from a PROJ cpp file
#endif

#ifndef DATUM_INTERNAL_HH_INCLUDED
#define DATUM_INTERNAL_HH_INCLUDED

#define UNKNOWN_ENGINEERING_DATUM "Unknown engineering datum"

#endif // DATUM_INTERNAL_HH_INCLUDED
4 changes: 3 additions & 1 deletion src/iso19111/c_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
#include "proj/metadata.hpp"
#include "proj/util.hpp"

#include "proj/internal/datum_internal.hpp"
#include "proj/internal/internal.hpp"
#include "proj/internal/io_internal.hpp"

Expand Down Expand Up @@ -4332,7 +4333,8 @@ PJ *proj_create_engineering_crs(PJ_CONTEXT *ctx, const char *crs_name) {
return pj_obj_create(
ctx, EngineeringCRS::create(
createPropertyMapName(crs_name),
EngineeringDatum::create(PropertyMap()),
EngineeringDatum::create(
createPropertyMapName(UNKNOWN_ENGINEERING_DATUM)),
CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)));
} catch (const std::exception &e) {
proj_log_error(ctx, __FUNCTION__, e.what());
Expand Down
53 changes: 50 additions & 3 deletions src/iso19111/crs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@

#include "proj/internal/coordinatesystem_internal.hpp"
#include "proj/internal/crs_internal.hpp"
#include "proj/internal/datum_internal.hpp"
#include "proj/internal/internal.hpp"
#include "proj/internal/io_internal.hpp"

Expand Down Expand Up @@ -6953,7 +6954,7 @@ void EngineeringCRS::_exportToWKT(io::WKTFormatter *formatter) const {
formatter->addQuotedString(nameStr());
const auto &datumName = datum()->nameStr();
if (isWKT2 ||
(!datumName.empty() && datumName != "Unknown engineering datum")) {
(!datumName.empty() && datumName != UNKNOWN_ENGINEERING_DATUM)) {
datum()->_exportToWKT(formatter);
}
if (!isWKT2) {
Expand Down Expand Up @@ -7006,8 +7007,54 @@ bool EngineeringCRS::_isEquivalentTo(
const util::IComparable *other, util::IComparable::Criterion criterion,
const io::DatabaseContextPtr &dbContext) const {
auto otherEngineeringCRS = dynamic_cast<const EngineeringCRS *>(other);
return otherEngineeringCRS != nullptr &&
SingleCRS::baseIsEquivalentTo(other, criterion, dbContext);
if (otherEngineeringCRS == nullptr ||
(criterion == util::IComparable::Criterion::STRICT &&
!ObjectUsage::_isEquivalentTo(other, criterion, dbContext))) {
return false;
}

// Check datum
const auto &thisDatum = datum();
const auto &otherDatum = otherEngineeringCRS->datum();
if (!thisDatum->_isEquivalentTo(otherDatum.get(), criterion, dbContext)) {
return false;
}

// Check coordinate system
const auto &thisCS = coordinateSystem();
const auto &otherCS = otherEngineeringCRS->coordinateSystem();
if (!(thisCS->_isEquivalentTo(otherCS.get(), criterion, dbContext))) {
const auto thisCartCS = dynamic_cast<cs::CartesianCS *>(thisCS.get());
const auto otherCartCS = dynamic_cast<cs::CartesianCS *>(otherCS.get());
const auto &thisAxisList = thisCS->axisList();
const auto &otherAxisList = otherCS->axisList();
// Check particular case of
// https://github.com/r-spatial/sf/issues/2049#issuecomment-1486600723
if (criterion != util::IComparable::Criterion::STRICT && thisCartCS &&
otherCartCS && thisAxisList.size() == 2 &&
otherAxisList.size() == 2 &&
((&thisAxisList[0]->direction() ==
&cs::AxisDirection::UNSPECIFIED &&
&thisAxisList[1]->direction() ==
&cs::AxisDirection::UNSPECIFIED) ||
(&otherAxisList[0]->direction() ==
&cs::AxisDirection::UNSPECIFIED &&
&otherAxisList[1]->direction() ==
&cs::AxisDirection::UNSPECIFIED)) &&
((thisAxisList[0]->nameStr() == "X" &&
otherAxisList[0]->nameStr() == "Easting" &&
thisAxisList[1]->nameStr() == "Y" &&
otherAxisList[1]->nameStr() == "Northing") ||
(otherAxisList[0]->nameStr() == "X" &&
thisAxisList[0]->nameStr() == "Easting" &&
otherAxisList[1]->nameStr() == "Y" &&
thisAxisList[1]->nameStr() == "Northing"))) {
return true;
}
return false;
}

return true;
}

// ---------------------------------------------------------------------------
Expand Down
14 changes: 10 additions & 4 deletions src/iso19111/datum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include "proj/metadata.hpp"
#include "proj/util.hpp"

#include "proj/internal/datum_internal.hpp"
#include "proj/internal/internal.hpp"
#include "proj/internal/io_internal.hpp"

Expand Down Expand Up @@ -2654,12 +2655,17 @@ void EngineeringDatum::_exportToJSON(
bool EngineeringDatum::_isEquivalentTo(
const util::IComparable *other, util::IComparable::Criterion criterion,
const io::DatabaseContextPtr &dbContext) const {
auto otherTD = dynamic_cast<const EngineeringDatum *>(other);
if (otherTD == nullptr ||
!Datum::_isEquivalentTo(other, criterion, dbContext)) {
auto otherDatum = dynamic_cast<const EngineeringDatum *>(other);
if (otherDatum == nullptr) {
return false;
}
return true;
if (criterion != util::IComparable::Criterion::STRICT &&
(nameStr().empty() || nameStr() == UNKNOWN_ENGINEERING_DATUM) &&
(otherDatum->nameStr().empty() ||
otherDatum->nameStr() == UNKNOWN_ENGINEERING_DATUM)) {
return true;
}
return Datum::_isEquivalentTo(other, criterion, dbContext);
}
//! @endcond

Expand Down
4 changes: 2 additions & 2 deletions src/iso19111/io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
#include "operation/parammappings.hpp"

#include "proj/internal/coordinatesystem_internal.hpp"
#include "proj/internal/datum_internal.hpp"
#include "proj/internal/internal.hpp"
#include "proj/internal/io_internal.hpp"

Expand Down Expand Up @@ -5180,8 +5181,7 @@ WKTParser::Private::buildEngineeringCRSFromLocalCS(const WKTNodeNNPtr &node) {
// has a tradition of emitting just LOCAL_CS["foo"]
[]() {
PropertyMap map;
map.set(IdentifiedObject::NAME_KEY,
"Unknown engineering datum");
map.set(IdentifiedObject::NAME_KEY, UNKNOWN_ENGINEERING_DATUM);
return map;
}());
return EngineeringCRS::create(buildProperties(node), datum, cs);
Expand Down
45 changes: 45 additions & 0 deletions test/unit/test_crs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5968,6 +5968,51 @@ TEST(crs, engineeringCRS_WKT1) {

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

TEST(crs, engineeringCRS_unknown_equivalence) {

// Test equivalent of CRS definition got from GPKG (wkt2) and its equivalent
// from GeoTIFF (wkt1)
// Cf https://github.com/r-spatial/sf/issues/2049#issuecomment-1486600723
auto wkt1 = "ENGCRS[\"Undefined Cartesian SRS with unknown unit\",\n"
" EDATUM[\"\"],\n"
" CS[Cartesian,2],\n"
" AXIS[\"(E)\",east,\n"
" ORDER[1],\n"
" LENGTHUNIT[\"metre\",1,\n"
" ID[\"EPSG\",9001]]],\n"
" AXIS[\"(N)\",north,\n"
" ORDER[2],\n"
" LENGTHUNIT[\"metre\",1,\n"
" ID[\"EPSG\",9001]]]]";

auto wkt2 = "ENGCRS[\"Undefined Cartesian SRS with unknown unit\",\n"
" EDATUM[\"Unknown engineering datum\"],\n"
" CS[Cartesian,2],\n"
" AXIS[\"x\",unspecified,\n"
" ORDER[1],\n"
" LENGTHUNIT[\"metre\",1,\n"
" ID[\"EPSG\",9001]]],\n"
" AXIS[\"y\",unspecified,\n"
" ORDER[2],\n"
" LENGTHUNIT[\"metre\",1,\n"
" ID[\"EPSG\",9001]]]]";

auto obj1 = WKTParser().createFromWKT(wkt1);
auto crs1 = nn_dynamic_pointer_cast<CRS>(obj1);
ASSERT_TRUE(crs1 != nullptr);

auto obj2 = WKTParser().createFromWKT(wkt2);
auto crs2 = nn_dynamic_pointer_cast<CRS>(obj2);
ASSERT_TRUE(crs2 != nullptr);

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

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

static ParametricCSNNPtr createParametricCS() {

return ParametricCS::create(
Expand Down

0 comments on commit d08d94b

Please sign in to comment.