package com.bbn.openmap.proj.coords;
import com.bbn.openmap.proj.Ellipsoid;
/**
* Helmert 7-parameter transformation
*
* http://www.posc.org/Epicentre.2_2/DataModel/ExamplesofUsage/eu_cs35.html
* https://www.og.berr.gov.uk/regulation/guidance/co_systems/co_sys_12.htm
* http://www.ogp.org.uk/pubs/373-10.pdf
*/
public class HelmertTransformation {
private static final int TYPE_POSITION_VECTOR = 1;
private static final int TYPE_COORDINATE_FRAME = 2;
/**
* Parameters to transform from WGS84 to ED50 north of 62deg N.
*/
public static final HelmertTransformation WGS84_TO_ED50_N62N = new HelmertTransformation(116.641d, 56.931d, 110.559d, -0.000004327d, -0.000004464d, 0.000004444d, ppmToM(3.520d), TYPE_POSITION_VECTOR);
/**
* Parameters to transform from ED50 to WGS84 north of 62deg N.
*/
public static final HelmertTransformation ED50_TO_WGS84_N62N = WGS84_TO_ED50_N62N.createReverse();
/**
* Parameters to transform from WGS84 to ED50 south of 62deg N.
*/
public static final HelmertTransformation WGS84_TO_ED50_S62N = new HelmertTransformation(90.365d, 101.130d, 123.384d, -0.000001614d, -0.000000373d, -0.000004334d, ppmToM(-1.994d), TYPE_POSITION_VECTOR);
/**
* Parameters to transform from ED50 to WGS84 south of 62deg N.
*/
private static final HelmertTransformation ED50_TO_WGS84_S62N = WGS84_TO_ED50_S62N.createReverse();
private final double dX;
private final double dY;
private final double dZ;
private final double rX;
private final double rY;
private final double rZ;
private final double neg_rX;
private final double neg_rY;
private final double neg_rZ;
private final double m;
private final int type;
public static final HelmertTransformation find(Ellipsoid source, Ellipsoid dest) {
// TODO: include projection center
if ((source == Ellipsoid.WGS_84) && (dest == Ellipsoid.INTERNATIONAL)) {
return WGS84_TO_ED50_S62N;
}
if ((source == Ellipsoid.INTERNATIONAL) && (dest == Ellipsoid.WGS_84)) {
return ED50_TO_WGS84_S62N;
}
throw new IllegalArgumentException("Unknown transformation from " + source + " to " + dest);
}
/**
*
* @param dX
* @param dY
* @param dZ
* @param rX
* @param rY
* @param rZ
* @param m a scale correction factor near 1. see {@link #ppmToM(double)}
* @param type
*/
private HelmertTransformation(double dX, double dY, double dZ, double rX, double rY, double rZ,
double m, int type) {
this.dX = dX;
this.dY = dY;
this.dZ = dZ;
this.rX = rX;
this.rY = rY;
this.rZ = rZ;
this.neg_rX = -1d * rX;
this.neg_rY = -1d * rY;
this.neg_rZ = -1d * rZ;
this.m = m;
this.type = type;
}
private HelmertTransformation createReverse() {
// m should be switched around 1. So 0.9 should be 1.1 and so on.
double newM = 2d - m;
return new HelmertTransformation(-1d * dX, -1d * dY, -1d * dZ, neg_rX, neg_rY, neg_rZ, newM, type);
}
public void apply(ECEFPoint coord) {
double x = coord.x_;
double y = coord.y_;
double z = coord.z_;
x *= m;
y *= m;
z *= m;
switch (type) {
case TYPE_POSITION_VECTOR:
x = x + (neg_rZ * y) + (rX * z);
y = (rZ * x) + y + (neg_rX * z);
z = (neg_rY * x) + (rX * y) + z;
break;
case TYPE_COORDINATE_FRAME:
x = x + (rZ * y) + (neg_rX * z);
y = (neg_rZ * x) + y + (rX * z);
z = (rY * x) + (neg_rX * y) + z;
break;
}
x += dX;
y += dY;
z += dZ;
coord.setECEF(x, y, z);
}
/**
* Convert the scale factor from parts per million.
*
* @param ppm scale factor in parts per million
* @return scale correction factor.
*/
private static final double ppmToM(double ppm) {
return 1d + (ppm / 1000000d);
}
}