aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/PJ_helmert.c101
1 files changed, 73 insertions, 28 deletions
diff --git a/src/PJ_helmert.c b/src/PJ_helmert.c
index 6dee7e5d..0576b5e5 100644
--- a/src/PJ_helmert.c
+++ b/src/PJ_helmert.c
@@ -1,13 +1,14 @@
/***********************************************************************
- Helmert transformations, 3- and 7-parameter
+ 3-, 4-and 7-parameter shifts, and their 6-, 8-
+ and 14-parameter kinematic counterparts.
Thomas Knudsen, 2016-05-24
************************************************************************
- Implements 3-, 7- and 14-parameter Helmert transformations for 3D
- data.
+ Implements 3(6)-, 4(8) and 7(14)-parameter Helmert transformations for
+ 3D data.
Primarily useful for implementation of datum shifts in transformation
pipelines.
@@ -16,7 +17,7 @@
Thomas Knudsen, thokn@sdfe.dk, 2016-05-24/06-05
Kristian Evers, kreve@sdfe.dk, 2017-05-01
-Last update: 2017-05-08
+Last update: 2017-05-15
************************************************************************
* Copyright (c) 2016, Thomas Knudsen / SDFE
@@ -48,7 +49,7 @@ Last update: 2017-05-08
#include <assert.h>
#include <stddef.h>
#include <errno.h>
-PROJ_HEAD(helmert, "3-, 7- and 14-parameter Helmert shift");
+PROJ_HEAD(helmert, "3(6)-, 4(8)- and 7(14)-parameter Helmert shift");
static XYZ helmert_forward_3d (LPZ lpz, PJ *P);
static LPZ helmert_reverse_3d (XYZ xyz, PJ *P);
@@ -91,9 +92,12 @@ struct pj_opaque_helmert {
double scale;
double scale_0;
double dscale;
+ double theta;
+ double theta_0;
+ double dtheta;
double R[3][3];
double epoch, t_obs;
- int no_rotation, approximate, transpose;
+ int no_rotation, approximate, transpose, fourparam;
};
@@ -147,6 +151,8 @@ static void update_parameters(PJ *P) {
Q->scale = Q->scale_0 + Q->dscale * dt;
+ Q->theta = Q->theta_0 + Q->dtheta * dt;
+
/* debugging output */
if (pj_err_level(P, PJ_ERR_TELL) >= PJ_LOG_TRACE) {
pj_log_trace(P, "Transformation parameters for observation epoch %g:", Q->t_obs);
@@ -157,6 +163,7 @@ static void update_parameters(PJ *P) {
pj_log_trace(P, "rx: %g", Q->opk.o);
pj_log_trace(P, "ry: %g", Q->opk.p);
pj_log_trace(P, "rz: %g", Q->opk.k);
+ pj_log_trace(P, "theta: %g", Q->theta);
}
return;
}
@@ -320,20 +327,20 @@ static void build_rot_matrix(PJ *P) {
/***********************************************************************/
static XY helmert_forward (LP lp, PJ *P) {
-/************************************************************************
+/***********************************************************************/
+ struct pj_opaque_helmert *Q = (struct pj_opaque_helmert *) P->opaque;
+ PJ_TRIPLET point;
+ double x, y, cr, sr;
+ point.lp = lp;
- The 2D implementation is a slightly silly stop-gap, returning a
- 2D sub-space of the 3D transformation.
+ cr = cos(Q->theta) * Q->scale;
+ sr = sin(Q->theta) * Q->scale;
+ x = point.xy.x;
+ y = point.xy.y;
- In order to follow the "principle of least astonishment", the 2D
- interface should provide an implementation of the 4 parameter
- 2D Helmert shift.
+ point.xy.x = cr*x + sr*y + Q->xyz_0.x;
+ point.xy.y = -sr*x + cr*y + Q->xyz_0.y;
-************************************************************************/
- PJ_TRIPLET point;
- point.lp = lp;
- point.lpz.z = 0;
- point.xyz = helmert_forward_3d (point.lpz, P);
return point.xy;
}
@@ -341,10 +348,19 @@ static XY helmert_forward (LP lp, PJ *P) {
/***********************************************************************/
static LP helmert_reverse (XY xy, PJ *P) {
/***********************************************************************/
+ struct pj_opaque_helmert *Q = (struct pj_opaque_helmert *) P->opaque;
PJ_TRIPLET point;
+ double x, y, sr, cr;
point.xy = xy;
- point.xyz.z = 0;
- point.lpz = helmert_reverse_3d (point.xyz, P);
+
+ cr = cos(Q->theta) / Q->scale;
+ sr = sin(Q->theta) / Q->scale;
+ x = point.xy.x - Q->xyz_0.x;
+ y = point.xy.y - Q->xyz_0.y;
+
+ point.xy.x = x*cr - y*sr;
+ point.xy.y = x*sr + y*cr;
+
return point.lp;
}
@@ -356,6 +372,13 @@ static XYZ helmert_forward_3d (LPZ lpz, PJ *P) {
PJ_TRIPLET point;
double X, Y, Z, scale;
+ point.lpz = lpz;
+
+ if (Q->fourparam) {
+ point.xy = helmert_forward(point.lp, P);
+ return point.xyz;
+ }
+
if (Q->no_rotation) {
point.xyz.x = lpz.lam + Q->xyz.x;
point.xyz.y = lpz.phi + Q->xyz.y;
@@ -369,7 +392,6 @@ static XYZ helmert_forward_3d (LPZ lpz, PJ *P) {
Y = lpz.phi;
Z = lpz.z;
- point.lpz = lpz;
point.xyz.x = scale * ( R00 * X + R01 * Y + R02 * Z);
point.xyz.y = scale * ( R10 * X + R11 * Y + R12 * Z);
@@ -392,6 +414,11 @@ static LPZ helmert_reverse_3d (XYZ xyz, PJ *P) {
point.xyz = xyz;
+ if (Q->fourparam) {
+ point.lp = helmert_reverse(point.xy, P);
+ return point.lpz;
+ }
+
if (Q->no_rotation) {
point.xyz.x = xyz.x - Q->xyz.x;
point.xyz.y = xyz.y - Q->xyz.y;
@@ -490,6 +517,10 @@ PJ *PROJECTION(helmert) {
if (pj_param (P->ctx, P->params, "trz").i)
Q->opk_0.k = pj_param (P->ctx, P->params, "drz").f * ARCSEC_TO_RAD;
+ if (pj_param (P->ctx, P->params, "ttheta").i) {
+ Q->theta_0 = pj_param (P->ctx, P->params, "dtheta").f * ARCSEC_TO_RAD;
+ Q->fourparam = 1;
+ }
/* Scale */
if (pj_param (P->ctx, P->params, "ts").i)
@@ -516,6 +547,8 @@ PJ *PROJECTION(helmert) {
if (pj_param (P->ctx, P->params, "tdrz").i)
Q->dopk.k = pj_param (P->ctx, P->params, "ddrz").f * ARCSEC_TO_RAD;
+ if (pj_param (P->ctx, P->params, "tdtheta").i)
+ Q->dtheta = pj_param (P->ctx, P->params, "ddtheta").f * ARCSEC_TO_RAD;
/* Scale rate */
if (pj_param (P->ctx, P->params, "tds").i)
@@ -540,6 +573,7 @@ PJ *PROJECTION(helmert) {
Q->xyz = Q->xyz_0;
Q->opk = Q->opk_0;
Q->scale = Q->scale_0;
+ Q->theta = Q->theta_0;
/* Let's help with debugging */
if (pj_err_level(P, PJ_ERR_TELL) >= PJ_LOG_DEBUG) {
@@ -574,7 +608,7 @@ int pj_helmert_selftest (void) {return 0;}
#else
-static int test (char *args, PJ_TRIPLET in, PJ_TRIPLET expect) {
+static int test (char *args, PJ_TRIPLET in, PJ_TRIPLET expect, double tol) {
PJ_TRIPLET out;
PJ *P = pj_init_plus (args);
@@ -582,7 +616,7 @@ static int test (char *args, PJ_TRIPLET in, PJ_TRIPLET expect) {
return 5;
out.xyz = pj_fwd3d (in.lpz, P);
- if (pj_xyz_dist (out.xyz, expect.xyz) > 1e-4) {
+ if (pj_xyz_dist (out.xyz, expect.xyz) > tol) {
pj_log_error(P, "Tolerance of forward calculation not met");
pj_log_error(P, " Expect: %10.10f, %10.10f, %10.10f", expect.xyz.x, expect.xyz.y, expect.xyz.z);
pj_log_error(P, " Out: %10.10f, %10.10f, %10.10f", out.xyz.x, out.xyz.y, out.xyz.z);
@@ -591,7 +625,7 @@ static int test (char *args, PJ_TRIPLET in, PJ_TRIPLET expect) {
}
out.lpz = pj_inv3d (out.xyz, P);
- if (pj_xyz_dist (out.xyz, in.xyz) > 1e-4) {
+ if (pj_xyz_dist (out.xyz, in.xyz) > tol) {
pj_log_error(P, "Tolerance of inverse calculation not met");
pj_log_error(P, " In: %10.10f, %10.10f, %10.10f", in.xyz.x, in.xyz.y, in.xyz.z);
pj_log_error(P, " Out: %10.10f, %10.10f, %10.10f", out.xyz.x, out.xyz.y, out.xyz.z);
@@ -662,11 +696,23 @@ int pj_helmert_selftest (void) {
" +epoch=1988.0 +transpose"
);
+ /* This example is from "A mathematical relationship between NAD27 and NAD83 (91)
+ State Plane coordinates in Southeastern Wisconsin":
- /* Run tests 1-3 */
- ret = test (args1, in1, expect1); if (ret) return ret;
- ret = test (args2, in2, expect2); if (ret) return ret + 10;
- ret = test (args3, in3, expect3); if (ret) return ret + 20;
+ http://www.sewrpc.org/SEWRPCFiles/Publications/TechRep/tr-034-Mathematical-Relationship-Between-NAD27-and-NAD83-91-State-Plane-Coordinates-Southeastern-Wisconsin.pdf
+
+ The test data is taken from p. 29. Here we are using point 203 and converting it
+ from NAD27 (ft) -> NAD83 (m). The paper reports a difference of 0.0014 m from measured
+ to computed coordinates, hence the test tolerance is set accordingly. */
+ PJ_TRIPLET in5 = {{2546506.957, 542256.609, 0}};
+ PJ_TRIPLET expect5 = {{766563.675, 165282.277, 0}};
+ char args5[] = " +proj=helmert +x=-9597.3572 +y=.6112 +s=0.304794780637 +theta=-1.244048";
+
+ /* Run tests 1-3 & 5 */
+ ret = test (args1, in1, expect1, 1e-4); if (ret) return ret;
+ ret = test (args2, in2, expect2, 1e-4); if (ret) return ret + 10;
+ ret = test (args3, in3, expect3, 1e-4); if (ret) return ret + 20;
+ ret = test (args5, in5, expect5, 0.001); if (ret) return ret + 40;
/* Run test 4 */
out = pj_trans(helmert, PJ_FWD, in4);
@@ -685,7 +731,6 @@ int pj_helmert_selftest (void) {
pj_log_error(helmert, " Out: %10.10f, %10.10f, %10.10f", out.coo.xyz.x, out.coo.xyz.y, out.coo.xyz.z);
return 32;
}
-
pj_free(helmert);
return 0;