Skip to content

Commit

Permalink
Implement EPSG:9656 'Cartesian Grid Offsets' operation method, and im…
Browse files Browse the repository at this point in the history
…port related records
  • Loading branch information
rouault committed Mar 19, 2024
1 parent 7eb1c3a commit 1805920
Show file tree
Hide file tree
Showing 11 changed files with 429 additions and 5 deletions.
140 changes: 140 additions & 0 deletions data/sql/other_transformation.sql

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions include/proj/coordinateoperation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1683,6 +1683,12 @@ class PROJ_GCC_DLL Transformation : public SingleOperation {
const common::Angle &offsetLong, const common::Length &offsetHeight,
const std::vector<metadata::PositionalAccuracyNNPtr> &accuracies);

PROJ_DLL static TransformationNNPtr createCartesianGridOffsets(
const util::PropertyMap &properties, const crs::CRSNNPtr &sourceCRSIn,
const crs::CRSNNPtr &targetCRSIn, const common::Length &eastingOffset,
const common::Length &northingOffset,
const std::vector<metadata::PositionalAccuracyNNPtr> &accuracies);

PROJ_DLL static TransformationNNPtr createVerticalOffset(
const util::PropertyMap &properties, const crs::CRSNNPtr &sourceCRSIn,
const crs::CRSNNPtr &targetCRSIn, const common::Length &offsetHeight,
Expand Down
7 changes: 4 additions & 3 deletions scripts/build_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -829,8 +829,9 @@ def fill_other_transformation(proj_db_cursor):
# 1068: Height Depth Reversal
# 1069: Change of Vertical Unit
# 1046: Vertical Offset and Slope
# 9621 : Similarity transformation
proj_db_cursor.execute("SELECT coord_op_code, coord_op_name, coord_op_method_code, coord_op_method_name, source_crs_code, target_crs_code, coord_op_accuracy, coord_tfm_version, epsg_coordoperation.deprecated, epsg_coordoperation.remarks FROM epsg.epsg_coordoperation LEFT JOIN epsg.epsg_coordoperationmethod USING (coord_op_method_code) WHERE coord_op_method_code IN (9601, 9616, 9618, 9619, 9624, 9660, 1068, 1069, 1046, 9621)")
# 9621: Similarity transformation
# 9656: Cartesian Grid Offsets
proj_db_cursor.execute("SELECT coord_op_code, coord_op_name, coord_op_method_code, coord_op_method_name, source_crs_code, target_crs_code, coord_op_accuracy, coord_tfm_version, epsg_coordoperation.deprecated, epsg_coordoperation.remarks FROM epsg.epsg_coordoperation LEFT JOIN epsg.epsg_coordoperationmethod USING (coord_op_method_code) WHERE coord_op_method_code IN (9601, 9616, 9618, 9619, 9624, 9660, 1068, 1069, 1046, 9621, 9656)")
for (code, name, method_code, method_name, source_crs_code, target_crs_code, coord_op_accuracy, coord_tfm_version, deprecated, remarks) in proj_db_cursor.fetchall():

# 1068 and 1069 are Height Depth Reversal and Change of Vertical Unit
Expand All @@ -854,7 +855,7 @@ def fill_other_transformation(proj_db_cursor):
target_crs_code = target_codes[0][0]

# Engineering CRS
if source_crs_code in (5800, 5817):
if source_crs_code in (5800, 5817, 6715):
print("Skipping transformation %s as source CRS (%d) is not handled" % (name, source_crs_code))
continue

Expand Down
1 change: 1 addition & 0 deletions scripts/reference_exported_symbols.txt
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,7 @@ osgeo::proj::operation::SingleOperation::~SingleOperation()
osgeo::proj::operation::SingleOperation::substitutePROJAlternativeGridNames(dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::io::DatabaseContext> >) const
osgeo::proj::operation::SingleOperation::validateParameters() const
osgeo::proj::operation::Transformation::createAbridgedMolodensky(osgeo::proj::util::PropertyMap const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::crs::CRS> > const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::crs::CRS> > const&, double, double, double, double, double, std::vector<dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::metadata::PositionalAccuracy> >, std::allocator<dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::metadata::PositionalAccuracy> > > > const&)
osgeo::proj::operation::Transformation::createCartesianGridOffsets(osgeo::proj::util::PropertyMap const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::crs::CRS> > const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::crs::CRS> > const&, osgeo::proj::common::Length const&, osgeo::proj::common::Length const&, std::vector<dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::metadata::PositionalAccuracy> >, std::allocator<dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::metadata::PositionalAccuracy> > > > const&)
osgeo::proj::operation::Transformation::createChangeVerticalUnit(osgeo::proj::util::PropertyMap const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::crs::CRS> > const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::crs::CRS> > const&, osgeo::proj::common::Scale const&, std::vector<dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::metadata::PositionalAccuracy> >, std::allocator<dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::metadata::PositionalAccuracy> > > > const&)
osgeo::proj::operation::Transformation::createCoordinateFrameRotation(osgeo::proj::util::PropertyMap const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::crs::CRS> > const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::crs::CRS> > const&, double, double, double, double, double, double, double, std::vector<dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::metadata::PositionalAccuracy> >, std::allocator<dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::metadata::PositionalAccuracy> > > > const&)
osgeo::proj::operation::Transformation::createGeocentricTranslations(osgeo::proj::util::PropertyMap const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::crs::CRS> > const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::crs::CRS> > const&, double, double, double, std::vector<dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::metadata::PositionalAccuracy> >, std::allocator<dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::metadata::PositionalAccuracy> > > > const&)
Expand Down
16 changes: 16 additions & 0 deletions src/iso19111/operation/parammappings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1011,6 +1011,7 @@ const struct MethodNameCode methodNameCodesList[] = {
METHOD_NAME_CODE(GEOGRAPHIC2D_OFFSETS),
METHOD_NAME_CODE(GEOGRAPHIC2D_WITH_HEIGHT_OFFSETS),
METHOD_NAME_CODE(GEOGRAPHIC3D_OFFSETS),
METHOD_NAME_CODE(CARTESIAN_GRID_OFFSETS),
METHOD_NAME_CODE(VERTICAL_OFFSET),
METHOD_NAME_CODE(VERTICAL_OFFSET_AND_SLOPE),
METHOD_NAME_CODE(NTV2),
Expand Down Expand Up @@ -1336,6 +1337,17 @@ static const ParamMapping paramVerticalOffset = {
static const ParamMapping *const paramsGeographic3DOffsets[] = {
&paramLatitudeOffset, &paramLongitudeOffset, &paramVerticalOffset, nullptr};

static const ParamMapping paramEastingOffset = {
EPSG_NAME_PARAMETER_EASTING_OFFSET, EPSG_CODE_PARAMETER_EASTING_OFFSET,
nullptr, common::UnitOfMeasure::Type::LINEAR, nullptr};

static const ParamMapping paramNorthingOffset = {
EPSG_NAME_PARAMETER_NORTHING_OFFSET, EPSG_CODE_PARAMETER_NORTHING_OFFSET,
nullptr, common::UnitOfMeasure::Type::LINEAR, nullptr};

static const ParamMapping *const paramsCartesianGridOffsets[] = {
&paramEastingOffset, &paramNorthingOffset, nullptr};

static const ParamMapping *const paramsVerticalOffsets[] = {
&paramVerticalOffset, nullptr};

Expand Down Expand Up @@ -1569,6 +1581,10 @@ static const MethodMapping otherMethodMappings[] = {
EPSG_CODE_METHOD_GEOGRAPHIC3D_OFFSETS, nullptr, nullptr, nullptr,
paramsGeographic3DOffsets},

{EPSG_NAME_METHOD_CARTESIAN_GRID_OFFSETS,
EPSG_CODE_METHOD_CARTESIAN_GRID_OFFSETS, nullptr, nullptr, nullptr,
paramsCartesianGridOffsets},

{EPSG_NAME_METHOD_VERTICAL_OFFSET, EPSG_CODE_METHOD_VERTICAL_OFFSET,
nullptr, nullptr, nullptr, paramsVerticalOffsets},

Expand Down
66 changes: 66 additions & 0 deletions src/iso19111/operation/singleoperation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3829,6 +3829,72 @@ bool SingleOperation::exportToPROJStringGeneric(
return true;
}

if (methodEPSGCode == EPSG_CODE_METHOD_CARTESIAN_GRID_OFFSETS) {
double eastingOffset = parameterValueNumeric(
EPSG_CODE_PARAMETER_EASTING_OFFSET, common::UnitOfMeasure::METRE);
double northingOffset = parameterValueNumeric(
EPSG_CODE_PARAMETER_NORTHING_OFFSET, common::UnitOfMeasure::METRE);

const auto checkIfCompatEngineeringCRS = [](const crs::CRSPtr &crs) {
auto engineeringCRS =
dynamic_cast<const crs::EngineeringCRS *>(crs.get());
if (engineeringCRS) {
auto cs = dynamic_cast<cs::CartesianCS *>(
engineeringCRS->coordinateSystem().get());
if (!cs) {
throw io::FormattingException(
"Can apply Cartesian grid offsets only to "
"EngineeringCRS with CartesianCS");
}
const auto &unit = cs->axisList()[0]->unit();
if (!unit._isEquivalentTo(
common::UnitOfMeasure::METRE,
util::IComparable::Criterion::EQUIVALENT)) {
// Could be enhanced to support other units...
throw io::FormattingException(
"Can apply Cartesian grid offsets only to "
"EngineeringCRS with CartesianCS with metre unit");
}
} else {
throw io::FormattingException(
"Can apply Cartesian grid offsets only to ProjectedCRS or "
"EngineeringCRS");
}
};

auto l_sourceCRS = sourceCRS();
auto sourceCRSProj =
dynamic_cast<const crs::ProjectedCRS *>(l_sourceCRS.get());
if (!sourceCRSProj) {
checkIfCompatEngineeringCRS(l_sourceCRS);
}

auto l_targetCRS = targetCRS();
auto targetCRSProj =
dynamic_cast<const crs::ProjectedCRS *>(l_targetCRS.get());
if (!targetCRSProj) {
checkIfCompatEngineeringCRS(l_targetCRS);
}

if (sourceCRSProj) {
formatter->startInversion();
sourceCRSProj->addUnitConvertAndAxisSwap(formatter, false);
formatter->stopInversion();
}

if (eastingOffset != 0.0 || northingOffset != 0.0) {
formatter->addStep("affine");
formatter->addParam("xoff", eastingOffset);
formatter->addParam("yoff", northingOffset);
}

if (targetCRSProj) {
targetCRSProj->addUnitConvertAndAxisSwap(formatter, false);
}

return true;
}

if (methodEPSGCode == EPSG_CODE_METHOD_VERTICAL_OFFSET) {

const crs::CRS *srcCRS = sourceCRS().get();
Expand Down
49 changes: 49 additions & 0 deletions src/iso19111/operation/transformation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1273,6 +1273,38 @@ TransformationNNPtr Transformation::createGeographic2DWithHeightOffsets(

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

/** \brief Instantiate a transformation with method Cartesian grid offsets
*
* This method is defined as
* <a href="https://epsg.org/coord-operation-method_9656/index.html">
* EPSG:9656</a>.
*
* @param properties See \ref general_properties of the Transformation.
* At minimum the name should be defined.
* @param sourceCRSIn Source CRS.
* @param targetCRSIn Target CRS.
* @param eastingOffset Easting offset to add.
* @param northingOffset Northing offset to add.
* @param accuracies Vector of positional accuracy (might be empty).
* @return new Transformation.
* @since PROJ 9.5.0
*/
TransformationNNPtr Transformation::createCartesianGridOffsets(
const util::PropertyMap &properties, const crs::CRSNNPtr &sourceCRSIn,
const crs::CRSNNPtr &targetCRSIn, const common::Length &eastingOffset,
const common::Length &northingOffset,
const std::vector<metadata::PositionalAccuracyNNPtr> &accuracies) {
return create(
properties, sourceCRSIn, targetCRSIn, nullptr,
createMethodMapNameEPSGCode(EPSG_CODE_METHOD_CARTESIAN_GRID_OFFSETS),
VectorOfParameters{
createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_EASTING_OFFSET),
createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_NORTHING_OFFSET)},
VectorOfValues{eastingOffset, northingOffset}, accuracies);
}

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

/** \brief Instantiate a transformation with method Vertical Offset.
*
* This method is defined as
Expand Down Expand Up @@ -1650,6 +1682,23 @@ TransformationNNPtr Transformation::inverseAsTransformation() const {
newOffsetHeight, coordinateOperationAccuracies()));
}

if (methodEPSGCode == EPSG_CODE_METHOD_CARTESIAN_GRID_OFFSETS) {
const auto &eastingOffset =
parameterValueMeasure(EPSG_CODE_PARAMETER_EASTING_OFFSET);
const common::Length newEastingOffset(negate(eastingOffset.value()),
eastingOffset.unit());

const auto &northingOffset =
parameterValueMeasure(EPSG_CODE_PARAMETER_NORTHING_OFFSET);
const common::Length newNorthingOffset(negate(northingOffset.value()),
northingOffset.unit());
return Private::registerInv(
this, createCartesianGridOffsets(
createPropertiesForInverse(this, false, false),
l_targetCRS, l_sourceCRS, newEastingOffset,
newNorthingOffset, coordinateOperationAccuracies()));
}

if (methodEPSGCode == EPSG_CODE_METHOD_VERTICAL_OFFSET) {

const auto &offsetHeight =
Expand Down
11 changes: 11 additions & 0 deletions src/proj_constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,17 @@

/* ------------------------------------------------------------------------ */

#define EPSG_CODE_METHOD_CARTESIAN_GRID_OFFSETS 9656
#define EPSG_NAME_METHOD_CARTESIAN_GRID_OFFSETS "Cartesian Grid Offsets"

#define EPSG_CODE_PARAMETER_EASTING_OFFSET 8728
#define EPSG_NAME_PARAMETER_EASTING_OFFSET "Easting offset"

#define EPSG_CODE_PARAMETER_NORTHING_OFFSET 8729
#define EPSG_NAME_PARAMETER_NORTHING_OFFSET "Northing offset"

/* ------------------------------------------------------------------------ */

#define EPSG_NAME_METHOD_GEOCENTRIC_TOPOCENTRIC \
"Geocentric/topocentric conversions"
#define EPSG_CODE_METHOD_GEOCENTRIC_TOPOCENTRIC 9836
Expand Down
2 changes: 0 additions & 2 deletions test/unit/test_factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,6 @@ TEST(factory, AuthorityFactory_identifyBodyFromSemiMajorAxis) {
TEST(factory, AuthorityFactory_createEllipsoid) {
auto factory = AuthorityFactory::create(DatabaseContext::create(), "EPSG");
EXPECT_THROW(factory->createEllipsoid("-1"), NoSuchAuthorityCodeException);
EXPECT_TRUE(nn_dynamic_pointer_cast<Ellipsoid>(
factory->createObject("7030")) != nullptr);
auto ellipsoid = factory->createEllipsoid("7030");
ASSERT_EQ(ellipsoid->identifiers().size(), 1U);
EXPECT_EQ(ellipsoid->identifiers()[0]->code(), "7030");
Expand Down
74 changes: 74 additions & 0 deletions test/unit/test_operation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5843,3 +5843,77 @@ TEST(operation, PointMotionOperation_with_epochs) {
"+step +proj=unitconvert +xy_in=rad +z_in=m +xy_out=deg +z_out=m "
"+step +proj=axisswap +order=2,1");
}

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

TEST(operation, export_of_Cartesian_Grid_Offsets_with_EngineeringCRS) {

auto wkt =
"COORDINATEOPERATION[\"CIG85 to GDA94 / MGA zone 48\",\n"
" VERSION[\"GA-Cxr\"],\n"
" SOURCECRS[\n"
" ENGCRS[\"Christmas Island Grid 1985\",\n"
" EDATUM[\"Christmas Island Datum 1985\"],\n"
" CS[Cartesian,2],\n"
" AXIS[\"(E)\",east,\n"
" ORDER[1],\n"
" LENGTHUNIT[\"metre\",1]],\n"
" AXIS[\"(N)\",north,\n"
" ORDER[2],\n"
" LENGTHUNIT[\"metre\",1]],\n"
" ID[\"EPSG\",6715]]],\n"
" TARGETCRS[\n"
" PROJCRS[\"GDA94 / MGA zone 48\",\n"
" BASEGEOGCRS[\"GDA94\",\n"
" DATUM[\"Geocentric Datum of Australia 1994\",\n"
" ELLIPSOID[\"GRS 1980\",6378137,298.257222101,\n"
" LENGTHUNIT[\"metre\",1]]],\n"
" PRIMEM[\"Greenwich\",0,\n"
" ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
" ID[\"EPSG\",4283]],\n"
" CONVERSION[\"Map Grid of Australia zone 48\",\n"
" METHOD[\"Transverse Mercator\",\n"
" ID[\"EPSG\",9807]],\n"
" PARAMETER[\"Latitude of natural origin\",0,\n"
" ANGLEUNIT[\"degree\",0.0174532925199433],\n"
" ID[\"EPSG\",8801]],\n"
" PARAMETER[\"Longitude of natural origin\",105,\n"
" ANGLEUNIT[\"degree\",0.0174532925199433],\n"
" ID[\"EPSG\",8802]],\n"
" PARAMETER[\"Scale factor at natural origin\",0.9996,\n"
" SCALEUNIT[\"unity\",1],\n"
" ID[\"EPSG\",8805]],\n"
" PARAMETER[\"False easting\",500000,\n"
" LENGTHUNIT[\"metre\",1],\n"
" ID[\"EPSG\",8806]],\n"
" PARAMETER[\"False northing\",10000000,\n"
" LENGTHUNIT[\"metre\",1],\n"
" ID[\"EPSG\",8807]]],\n"
" CS[Cartesian,2],\n"
" AXIS[\"(E)\",east,\n"
" ORDER[1],\n"
" LENGTHUNIT[\"metre\",1]],\n"
" AXIS[\"(N)\",north,\n"
" ORDER[2],\n"
" LENGTHUNIT[\"metre\",1]],\n"
" ID[\"EPSG\",28348]]],\n"
" METHOD[\"Cartesian Grid Offsets\",\n"
" ID[\"EPSG\",9656]],\n"
" PARAMETER[\"Easting offset\",550015,\n"
" LENGTHUNIT[\"metre\",1],\n"
" ID[\"EPSG\",8728]],\n"
" PARAMETER[\"Northing offset\",8780001,\n"
" LENGTHUNIT[\"metre\",1],\n"
" ID[\"EPSG\",8729]],\n"
" OPERATIONACCURACY[5],\n"
" ID[\"EPSG\",6724]]";

auto obj = WKTParser().createFromWKT(wkt);
auto transf = nn_dynamic_pointer_cast<Transformation>(obj);
ASSERT_TRUE(transf != nullptr);
EXPECT_EQ(transf->exportToPROJString(PROJStringFormatter::create().get()),
"+proj=affine +xoff=550015 +yoff=8780001");
EXPECT_EQ(transf->inverse()->exportToPROJString(
PROJStringFormatter::create().get()),
"+proj=affine +xoff=-550015 +yoff=-8780001");
}
Loading

0 comments on commit 1805920

Please sign in to comment.