Skip to content

Commit

Permalink
Merge pull request #3200 from rouault/vertoffset
Browse files Browse the repository at this point in the history
Implement Vertical Offset and slope transformation method
  • Loading branch information
rouault authored May 16, 2022
2 parents 0d97cb7 + 8bdd05f commit d9ccb4b
Show file tree
Hide file tree
Showing 12 changed files with 416 additions and 5 deletions.
84 changes: 84 additions & 0 deletions data/sql/other_transformation.sql

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions docs/source/operations/transformations/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ systems are based on different datums.
molobadekas
hgridshift
tinshift
vertoffset
vgridshift
xyzgridshift
89 changes: 89 additions & 0 deletions docs/source/operations/transformations/vertoffset.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
.. _vertoffset:

================================================================================
Vertical Offset And Slope
================================================================================

.. versionadded:: 9.1.0

The Vertical Offset And Slope offsets transformation adds an offset to an
orthometric height, taking account a constant offset and a inclinated plane,
defined by its slope along latitude and longitude axis.

+---------------------+----------------------------------------------------------+
| **Alias** | vertoffset |
+---------------------+----------------------------------------------------------+
| **Domain** | 3D |
+---------------------+----------------------------------------------------------+
| **Input type** | Geodetic coordinates (horizontal), meters (vertical) |
+---------------------+----------------------------------------------------------+
| **output type** | Geodetic coordinates (horizontal), meters (vertical) |
+---------------------+----------------------------------------------------------+

It is documented as coordinate operation method code 1046 in the EPSG dataset (:cite:`IOGP2018`).
It is typically used in Europe, to relate national vertical systems to
pan-European vertical systems (EVRF200, EVRF2007).

Examples
###############################################################################

Vertical offset from LN02 height to EVRF2000 height with horizontal coordinates in ETRS89::

+proj=vertoffset +lat_0=46.9166666666666666 +lon_0=8.183333333333334 \
+dh=-0.245 +slope_lat=-0.210 +slope_lon=-0.032 +ellps=GRS80

Parameters
################################################################################

Required
-------------------------------------------------------------------------------

.. option:: +lat_0=<value>

Latitude of origin of the inclinated plane.

.. option:: +lon_0=<value>

Longitude of origin of the inclinated plane

.. option:: +dh=<value>

Offset in height, expressed in meter, to add.

.. option:: +slope_lat=<value>

Slope parameter in the latitude domain, expressed in arc-seconds.

.. option:: +slope_lon=<value>

Slope parameter in the longitude domain, expressed in arc-seconds.

.. include:: ../options/ellps.rst

Formula
################################################################################

The :math:`Z_{dest}` destination elevation is obtained from the
:math:`Z_{src}` source elevation with:

.. math::
\begin{align}
Z_{dest} = Z_{src} + \left( dh + slope_{lat} * {\rho}_0 * (\phi - {\phi}_0) + slope_{lon} * {\nu}_0 * (\lambda - {\lambda}_0) * cos(\phi) \right)
\end{align}
where:

* :math:`dh`, :math:`slope_{lat}` and :math:`slope_{lon}` are the above mentioned parameters
* :math:`{\lambda}_0`, :math:`{\phi}_0` is the longitude, latitude of the point of origin of the inclinate plane (``+lon_0``, ``+lat_0``)
* :math:`\lambda`, :math:`\phi` is the longitude, latitude of the point to evaluate
* :math:`{\rho}_0` is the radius of curvature of the meridian at latitude :math:`{\phi}_0`
* :math:`{\nu}_0` is the radius of curvature on the prime vertical (i.e. perpendicular to the meridian) at latitude :math:`{\phi}_0`

The reverse formula is:

.. math::
\begin{align}
Z_{src} = Z_{dest} - \left( dh + slope_{lat} * {\rho}_0 * (\phi - {\phi}_0) + slope_{lon} * {\nu}_0 * (\lambda - {\lambda}_0) * cos(\phi) \right)
\end{align}
14 changes: 12 additions & 2 deletions scripts/build_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,8 @@ def fill_other_transformation(proj_db_cursor):
# 9660: Geographic3D offsets
# 1068: Height Depth Reversal
# 1069: Change of Vertical Unit
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: Vertical Offset and Slope
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)")
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 Down Expand Up @@ -700,11 +701,19 @@ def fill_other_transformation(proj_db_cursor):
param_value = [None for i in range(max_n_params)]
param_uom_auth_name = [None for i in range(max_n_params)]
param_uom_code = [None for i in range(max_n_params)]
interpolation_crs_auth_name = None
interpolation_crs_code = None

iterator = proj_db_cursor.execute("SELECT sort_order, cop.parameter_code, parameter_name, parameter_value, uom_code from epsg_coordoperationparam cop LEFT JOIN epsg_coordoperationparamvalue copv LEFT JOIN epsg_coordoperationparamusage copu ON cop.parameter_code = copv.parameter_code AND copu.parameter_code = copv.parameter_code WHERE copu.coord_op_method_code = copv.coord_op_method_code AND coord_op_code = ? AND copv.coord_op_method_code = ? ORDER BY sort_order", (code, method_code))
for (order, parameter_code, parameter_name, parameter_value, uom_code) in iterator:
assert order <= max_n_params
assert order == expected_order
if method_code == 1046 and order == 6: # Vertical offset and slope
assert parameter_code == 1037 # EPSG code for Horizontal CRS
interpolation_crs_auth_name = EPSG_AUTHORITY
interpolation_crs_code = str(int(parameter_value)) # needed to avoid codes like XXXX.0
break

param_auth_name[order - 1] = EPSG_AUTHORITY
param_code[order - 1] = parameter_code
param_name[order - 1] = parameter_name
Expand All @@ -713,6 +722,7 @@ def fill_other_transformation(proj_db_cursor):
param_uom_code[order - 1] = uom_code
expected_order += 1


arg = (EPSG_AUTHORITY, code, name,
remarks,
EPSG_AUTHORITY, method_code, method_name,
Expand All @@ -733,7 +743,7 @@ def fill_other_transformation(proj_db_cursor):
param_uom_auth_name[5], param_uom_code[5], param_auth_name[6],
param_code[6], param_name[6], param_value[6],
param_uom_auth_name[6], param_uom_code[6],
None, None, # interpolation CRS
interpolation_crs_auth_name, interpolation_crs_code,
coord_tfm_version,
deprecated)

Expand Down
22 changes: 22 additions & 0 deletions src/iso19111/operation/parammappings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -966,6 +966,7 @@ const struct MethodNameCode methodNameCodes[] = {
METHOD_NAME_CODE(GEOGRAPHIC2D_WITH_HEIGHT_OFFSETS),
METHOD_NAME_CODE(GEOGRAPHIC3D_OFFSETS),
METHOD_NAME_CODE(VERTICAL_OFFSET),
METHOD_NAME_CODE(VERTICAL_OFFSET_AND_SLOPE),
METHOD_NAME_CODE(NTV2),
METHOD_NAME_CODE(NTV1),
METHOD_NAME_CODE(NADCON),
Expand Down Expand Up @@ -1050,6 +1051,8 @@ const struct ParamNameCode paramNameCodes[] = {
PARAM_NAME_CODE(ORDINATE_2_EVAL_POINT),
PARAM_NAME_CODE(ORDINATE_3_EVAL_POINT),
PARAM_NAME_CODE(GEOCENTRIC_TRANSLATION_FILE),
PARAM_NAME_CODE(INCLINATION_IN_LATITUDE),
PARAM_NAME_CODE(INCLINATION_IN_LONGITUDE),
};

const ParamNameCode *getParamNameCodes(size_t &nElts) {
Expand Down Expand Up @@ -1256,6 +1259,21 @@ static const ParamMapping *const paramsGeographic3DOffsets[] = {
static const ParamMapping *const paramsVerticalOffsets[] = {
&paramVerticalOffset, nullptr};

static const ParamMapping paramInclinationInLatitude = {
EPSG_NAME_PARAMETER_INCLINATION_IN_LATITUDE,
EPSG_CODE_PARAMETER_INCLINATION_IN_LATITUDE, nullptr,
common::UnitOfMeasure::Type::ANGULAR, nullptr};

static const ParamMapping paramInclinationInLongitude = {
EPSG_NAME_PARAMETER_INCLINATION_IN_LONGITUDE,
EPSG_CODE_PARAMETER_INCLINATION_IN_LONGITUDE, nullptr,
common::UnitOfMeasure::Type::ANGULAR, nullptr};

static const ParamMapping *const paramsVerticalOffsetAndSlope[] = {
&paramOrdinate1EvalPoint, &paramOrdinate2EvalPoint,
&paramVerticalOffset, &paramInclinationInLatitude,
&paramInclinationInLongitude, nullptr};

static const ParamMapping paramLatitudeLongitudeDifferenceFile = {
EPSG_NAME_PARAMETER_LATITUDE_LONGITUDE_DIFFERENCE_FILE,
EPSG_CODE_PARAMETER_LATITUDE_LONGITUDE_DIFFERENCE_FILE, nullptr,
Expand Down Expand Up @@ -1450,6 +1468,10 @@ static const MethodMapping otherMethodMappings[] = {
{EPSG_NAME_METHOD_VERTICAL_OFFSET, EPSG_CODE_METHOD_VERTICAL_OFFSET,
nullptr, nullptr, nullptr, paramsVerticalOffsets},

{EPSG_NAME_METHOD_VERTICAL_OFFSET_AND_SLOPE,
EPSG_CODE_METHOD_VERTICAL_OFFSET_AND_SLOPE, nullptr, nullptr, nullptr,
paramsVerticalOffsetAndSlope},

{EPSG_NAME_METHOD_NTV2, EPSG_CODE_METHOD_NTV2, nullptr, nullptr, nullptr,
paramsNTV2},

Expand Down
60 changes: 60 additions & 0 deletions src/iso19111/operation/transformation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2991,6 +2991,66 @@ void Transformation::_exportToPROJString(
return;
}

if (methodEPSGCode == EPSG_CODE_METHOD_VERTICAL_OFFSET_AND_SLOPE) {

const crs::CRS *srcCRS = sourceCRS().get();
const crs::CRS *tgtCRS = targetCRS().get();

const auto sourceCRSCompound =
dynamic_cast<const crs::CompoundCRS *>(srcCRS);
const auto targetCRSCompound =
dynamic_cast<const crs::CompoundCRS *>(tgtCRS);
if (sourceCRSCompound && targetCRSCompound &&
sourceCRSCompound->componentReferenceSystems()[0]->_isEquivalentTo(
targetCRSCompound->componentReferenceSystems()[0].get(),
util::IComparable::Criterion::EQUIVALENT)) {
srcCRS = sourceCRSCompound->componentReferenceSystems()[1].get();
tgtCRS = targetCRSCompound->componentReferenceSystems()[1].get();
}

auto sourceCRSVert = dynamic_cast<const crs::VerticalCRS *>(srcCRS);
if (!sourceCRSVert) {
throw io::FormattingException(
"Can apply Vertical offset and slope only to VerticalCRS");
}

auto targetCRSVert = dynamic_cast<const crs::VerticalCRS *>(tgtCRS);
if (!targetCRSVert) {
throw io::FormattingException(
"Can apply Vertical offset and slope only to VerticalCRS");
}

const auto latitudeEvaluationPoint =
parameterValueNumeric(EPSG_CODE_PARAMETER_ORDINATE_1_EVAL_POINT,
common::UnitOfMeasure::DEGREE);
const auto longitudeEvaluationPoint =
parameterValueNumeric(EPSG_CODE_PARAMETER_ORDINATE_2_EVAL_POINT,
common::UnitOfMeasure::DEGREE);
const auto offsetHeight =
parameterValueNumericAsSI(EPSG_CODE_PARAMETER_VERTICAL_OFFSET);
const auto inclinationLatitude =
parameterValueNumeric(EPSG_CODE_PARAMETER_INCLINATION_IN_LATITUDE,
common::UnitOfMeasure::ARC_SECOND);
const auto inclinationLongitude =
parameterValueNumeric(EPSG_CODE_PARAMETER_INCLINATION_IN_LONGITUDE,
common::UnitOfMeasure::ARC_SECOND);

formatter->startInversion();
sourceCRSVert->addLinearUnitConvert(formatter);
formatter->stopInversion();

formatter->addStep("vertoffset");
formatter->addParam("lat_0", latitudeEvaluationPoint);
formatter->addParam("lon_0", longitudeEvaluationPoint);
formatter->addParam("dh", offsetHeight);
formatter->addParam("slope_lat", inclinationLatitude);
formatter->addParam("slope_lon", inclinationLongitude);

targetCRSVert->addLinearUnitConvert(formatter);

return;
}

// Substitute grid names with PROJ friendly names.
if (formatter->databaseContext()) {
auto alternate = substitutePROJAlternativeGridNames(
Expand Down
1 change: 1 addition & 0 deletions src/lib_proj.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ set(SRC_LIBPROJ_TRANSFORMATIONS
transformations/xyzgridshift.cpp
transformations/defmodel.cpp
transformations/tinshift.cpp
transformations/vertoffset.cpp
)

set(SRC_LIBPROJ_ISO19111
Expand Down
1 change: 1 addition & 0 deletions src/pj_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ PROJ_HEAD(vandg, "van der Grinten (I)")
PROJ_HEAD(vandg2, "van der Grinten II")
PROJ_HEAD(vandg3, "van der Grinten III")
PROJ_HEAD(vandg4, "van der Grinten IV")
PROJ_HEAD(vertoffset, "Vertical Offset and Slope")
PROJ_HEAD(vitk1, "Vitkovsky I")
PROJ_HEAD(vgridshift, "Vertical grid shift")
PROJ_HEAD(wag1, "Wagner I (Kavrayskiy VI)")
Expand Down
9 changes: 9 additions & 0 deletions src/proj_constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,9 @@
#define EPSG_CODE_METHOD_VERTICAL_OFFSET 9616
#define EPSG_NAME_METHOD_VERTICAL_OFFSET "Vertical Offset"

#define EPSG_CODE_METHOD_VERTICAL_OFFSET_AND_SLOPE 1046
#define EPSG_NAME_METHOD_VERTICAL_OFFSET_AND_SLOPE "Vertical Offset and Slope"

#define EPSG_CODE_METHOD_GEOGRAPHIC2D_OFFSETS 9619
#define EPSG_NAME_METHOD_GEOGRAPHIC2D_OFFSETS "Geographic2D offsets"

Expand All @@ -646,6 +649,12 @@
#define EPSG_NAME_PARAMETER_GEOID_UNDULATION "Geoid undulation"
#define EPSG_CODE_PARAMETER_GEOID_UNDULATION 8604

#define EPSG_NAME_PARAMETER_INCLINATION_IN_LATITUDE "Inclination in latitude"
#define EPSG_CODE_PARAMETER_INCLINATION_IN_LATITUDE 8730

#define EPSG_NAME_PARAMETER_INCLINATION_IN_LONGITUDE "Inclination in longitude"
#define EPSG_CODE_PARAMETER_INCLINATION_IN_LONGITUDE 8731

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

#define EPSG_CODE_METHOD_AFFINE_PARAMETRIC_TRANSFORMATION 9624
Expand Down
Loading

0 comments on commit d9ccb4b

Please sign in to comment.