/*
* TransformationWeightedHelmert.java
*
* Created on March 23, 2005, 12:10 PM
*/
package ika.transformation;
import java.io.*;
/**
* Planar (2D) Weighted Helmert Transformation with four parameters:<br>
* translation in horizontal and vertical direction, rotation
* and scale factor. Important: This is not a general purpose implementation, but
* has been optimized for speed for use by ika.mapanalyst.Isolines
* This implementation does not retain the horizontal and vertical offsets.
* @author Bernhard Jenny, Institute of Cartography, ETH Zurich
*/
public class TransformationWeightedHelmert extends Transformation
implements Serializable {
private static final long serialVersionUID = 7956296477523422791L;
double a1, a2;
/**
* Returns the name of the transformation.
* @return The name.
*/
public String getName() {
return "Weighted Helmert";
}
/**
* Returns a short description of this transformation.
* getShortDescription can be called before the transformation is
* initialized using initWithPoints
* @return The description.
*/
public String getShortDescription() {
String str = "\nAn individual weight can be asigned to each point.";
return str;
}
private double [][] ATP(double[][] srcPts, int nbrPts) {
double[][] res = new double [4][nbrPts * 2];
for (int c = 0, c_x_2 = 0; c < nbrPts; c++, c_x_2+=2) {
final double w = srcPts[c][2];
final double xw = srcPts[c][0] * w;
final double yw = srcPts[c][1] * w;
res[0][c_x_2] = res[1][c_x_2+1] = w;
res[2][c_x_2] = xw;
res[2][c_x_2+1] = yw;
res[3][c_x_2] = -yw;
res[3][c_x_2+1] = xw;
}
return res;
}
private double[][] ATP_x_A(double[][] ATP, double[][] srcPts, int nbrPts) {
// ATPA is 4 x 4 big.
double[][] res = new double[4][4];
final int m = ATP[0].length;
for (int r = 0; r < 4; r++) {
double[] res_row = res[r];
double[] ATP_row = ATP[r];
for (int k = 0; k < m; k+=2) {
res_row[0] += ATP_row[k];
res_row[1] += ATP_row[k+1];
}
for (int k = 0; k < nbrPts; k++) {
double[] srcPts_row = srcPts[k];
double ptX = srcPts_row[0];
double ptY = srcPts_row[1];
res_row[2] += ATP_row[k*2] * ptX + ATP_row[k*2+1] * ptY;
res_row[3] += - ATP_row[k*2] * ptY + ATP_row[k*2+1] * ptX;
}
}
return res;
}
/* compute a1 and a2 of the transformation matrix.
* Not the complete transformation matrix!
*/
private double[] QATP_x_l_a1_a2(double[][] QATP, double[][] points) {
final int n_half = QATP[0].length / 2;
double[] res = new double[2];
for (int row = 2; row < 4; ++row) {
final double[] QATP_row = QATP[row];
final int r = row - 2;
for (int i = 0; i < n_half; ++i) {
res[r] += QATP_row[i*2] * points[i][0]
+ QATP_row[i*2+1] * points[i][1];
}
}
return res;
}
/**
* Initialize the transformation with two sets of control points.
* The control points are not copied, nor is a reference to them
* retained. Instead, the transformation parameters are
* immmediately computed by initWithPoints.
* @param destSet A two-dimensional array (nx2) containing the x and y
* coordinates of the points of the destination set.
* The destSet must be of exactly the same size as sourceSet.
* @param sourceSet A two-dimensional array (nx3) containing the x and y
* coordinates of the points of the source set, and the weight of each point.
* The sourceSet must be of exactly the same size as destSet.
*/
public final void initWithPoints(double[][] destSet, double[][] sourceSet,
int nbrPts, float[] scaleRot){
// Compute transformation parameters
final double[][] ATP = this.ATP(sourceSet, nbrPts);
final double[][] N = this.ATP_x_A (ATP, sourceSet, nbrPts);
final double[][] Q = ika.utils.MatrixUtils.invertMatrix4x4 (N);
final double[][] QATP = ika.utils.MatrixUtils.matrix_x_matrix(Q, ATP);
final double[] x = this.QATP_x_l_a1_a2(QATP, destSet);
final float scale = (float)Math.sqrt(x[0]*x[0]+x[1]*x[1]);
scaleRot[0] = scale;
final float rotDegree = (float)Math.toDegrees(Math.atan2(x[1], x[0]));
// Math.atan2 returns values in the range -pi..+pi
scaleRot[1] = (rotDegree < 0.f) ? rotDegree + 360.f : rotDegree;
// copy paramters to object variables
this.a1 = x[0];
this.a2 = x[1];
}
protected final void initWithPoints(double[][] destSet, double[][] sourceSet){
float[] dummyScaleRot = new float[2];
this.initWithPoints(destSet, sourceSet, destSet.length, dummyScaleRot);
}
public java.awt.geom.AffineTransform getAffineTransform() {
return null;
}
public String getReport(boolean invert) {
return "";
}
public final double getRotation() {
double rot = Math.atan2(a2, a1);
if (rot < 0.)
rot += Math.PI * 2.;
return rot;
}
public final double getScale() {
return Math.sqrt(a1*a1+a2*a2);
}
public String getShortReport(boolean invert) {
return "";
}
public double getSigma0() {
return 0;
}
public final double[] transform(double[] point) {
return new double[] {a1*point[0]-a2*point[1], a2*point[0]+a1*point[1]};
}
}