/*********************************************************************** orthorot.c written by: Markus Metz loosely based on crs3d.c 2D/3D Transformation with orthogonal rotation ************************************************************************/ #include #include #include #include #include #include #include #include #include #include "crs.h" #define MSUCCESS 1 /* SUCCESS */ #define MNPTERR 0 /* NOT ENOUGH POINTS */ #define MUNSOLVABLE -1 /* NOT SOLVABLE */ #define MMEMERR -2 /* NOT ENOUGH MEMORY */ #define MPARMERR -3 /* PARAMETER ERROR */ #define MINTERR -4 /* INTERNAL ERROR */ /*********************************************************************** FUNCTION PROTOTYPES FOR STATIC (INTERNAL) FUNCTIONS ************************************************************************/ static int calccoef(struct Control_Points_3D *, double *, int); static int calcscale(struct Control_Points_3D *, double *); void transpose_matrix(int, int, double **, double **); void matmult(int, int, int, double **, double **, double **); void copy_matrix(int, int, double **, double **); void init_matrix(int, int, double **); void scale_matrix(int, int, double, double **, double **); void matrix_multiply(int, int, double **, double *, double *); void subtract_matrix(int, int, double **, double **, double **); double trace(int, int, double **); /*********************************************************************** TRANSFORM A SINGLE COORDINATE PAIR. ************************************************************************/ int CRS_georef_or(double e1, /* EASTING TO BE TRANSFORMED */ double n1, /* NORTHING TO BE TRANSFORMED */ double z1, /* HEIGHT TO BE TRANSFORMED */ double *e, /* EASTING, TRANSFORMED */ double *n, /* NORTHING, TRANSFORMED */ double *z, /* HEIGHT, TRANSFORMED */ double OR[] /* TRANSFORMATION COEFFICIENTS */ ) { *e = OR[9] + OR[12] * (OR[0] * e1 + OR[1] * n1 + OR[2] * z1); *n = OR[10] + OR[13] * (OR[3] * e1 + OR[4] * n1 + OR[5] * z1); *z = OR[11] + OR[14] * (OR[6] * e1 + OR[7] * n1 + OR[8] * z1); return MSUCCESS; } /*********************************************************************** COMPUTE THE FORWARD AND BACKWARD GEOREFFERENCING COEFFICIENTS BASED ON A SET OF CONTROL POINTS ORTHOGONAL TRANSFORMATION (ORTHOGONAL ROTATION MATRIX) Rotation matrix OR[0] OR[1] OR[2] OR[3] OR[4] OR[5] OR[6] OR[7] OR[8] OR[9] x shift OR[10] y shift OR[11] z shift OR[12] x or global scale OR[13] y scale OR[14] z scale ************************************************************************/ int CRS_compute_georef_equations_or(struct Control_Points_3D *cp, double OR12[], double OR21[]) { double *tempptr, *OR; int status, i, numactive; struct Control_Points_3D cpc, /* center points */ cpr; /* reduced to center */ cpc.count = 1; cpc.e1 = (double *)G_calloc(cpc.count, sizeof(double)); cpc.e2 = (double *)G_calloc(cpc.count, sizeof(double)); cpc.n1 = (double *)G_calloc(cpc.count, sizeof(double)); cpc.n2 = (double *)G_calloc(cpc.count, sizeof(double)); cpc.z1 = (double *)G_calloc(cpc.count, sizeof(double)); cpc.z2 = (double *)G_calloc(cpc.count, sizeof(double)); cpc.status = (int *)G_calloc(cpc.count, sizeof(int)); cpc.e1[0] = 0.0; cpc.e2[0] = 0.0; cpc.n1[0] = 0.0; cpc.n2[0] = 0.0; cpc.z1[0] = 0.0; cpc.z2[0] = 0.0; cpc.status[0] = 1; /* center points */ for (i = numactive = 0; i < cp->count; i++) { if (cp->status[i] > 0) { numactive++; cpc.e1[0] += cp->e1[i]; cpc.e2[0] += cp->e2[i]; cpc.n1[0] += cp->n1[i]; cpc.n2[0] += cp->n2[i]; cpc.z1[0] += cp->z1[i]; cpc.z2[0] += cp->z2[i]; } } /* this version of 3D transformation needs 3 control points */ if (numactive < 3) return MNPTERR; cpc.e1[0] /= numactive; cpc.e2[0] /= numactive; cpc.n1[0] /= numactive; cpc.n2[0] /= numactive; cpc.z1[0] /= numactive; cpc.z2[0] /= numactive; /* shift to center points */ cpr.count = numactive; cpr.e1 = (double *)G_calloc(cpr.count, sizeof(double)); cpr.e2 = (double *)G_calloc(cpr.count, sizeof(double)); cpr.n1 = (double *)G_calloc(cpr.count, sizeof(double)); cpr.n2 = (double *)G_calloc(cpr.count, sizeof(double)); cpr.z1 = (double *)G_calloc(cpr.count, sizeof(double)); cpr.z2 = (double *)G_calloc(cpr.count, sizeof(double)); cpr.status = (int *)G_calloc(cpr.count, sizeof(int)); for (i = numactive = 0; i < cp->count; i++) { if (cp->status[i] > 0) { cpr.e1[numactive] = cp->e1[i] - cpc.e1[0]; cpr.e2[numactive] = cp->e2[i] - cpc.e2[0]; cpr.n1[numactive] = cp->n1[i] - cpc.n1[0]; cpr.n2[numactive] = cp->n2[i] - cpc.n2[0]; cpr.z1[numactive] = cp->z1[i] - cpc.z1[0]; cpr.z2[numactive] = cp->z2[i] - cpc.z2[0]; cpr.status[numactive] = 1; numactive++; } } /* CALCULATE THE FORWARD TRANSFORMATION COEFFICIENTS */ OR = OR12; status = calccoef(&cpr, OR, 3); calcscale(&cpr, OR); /* CALCULATE FORWARD SHIFTS */ OR[9] = OR[10] = OR[11] = 0.0; for (i = numactive = 0; i < cp->count; i++) { if (cp->status[i] > 0) { OR[9] += cp->e2[i] - OR[12] * (OR[0] * cp->e1[i] + OR[1] * cp->n1[i] + OR[2] * cp->z1[i]); OR[10] += cp->n2[i] - OR[13] * (OR[3] * cp->e1[i] + OR[4] * cp->n1[i] + OR[5] * cp->z1[i]); OR[11] += cp->z2[i] - OR[14] * (OR[6] * cp->e1[i] + OR[7] * cp->n1[i] + OR[8] * cp->z1[i]); numactive++; } } OR[9] /= numactive; OR[10] /= numactive; OR[11] /= numactive; /* SWITCH THE 1 AND 2 EASTING, NORTHING, AND HEIGHT ARRAYS */ tempptr = cpr.e1; cpr.e1 = cpr.e2; cpr.e2 = tempptr; tempptr = cpr.n1; cpr.n1 = cpr.n2; cpr.n2 = tempptr; tempptr = cpr.z1; cpr.z1 = cpr.z2; cpr.z2 = tempptr; /* CALCULATE THE BACKWARD TRANSFORMATION COEFFICIENTS */ OR = OR21; status = calccoef(&cpr, OR, 3); if (status != MSUCCESS) return status; calcscale(&cpr, OR); /* SWITCH THE 1 AND 2 EASTING, NORTHING, AND HEIGHT ARRAYS BACK */ tempptr = cpr.e1; cpr.e1 = cpr.e2; cpr.e2 = tempptr; tempptr = cpr.n1; cpr.n1 = cpr.n2; cpr.n2 = tempptr; tempptr = cpr.z1; cpr.z1 = cpr.z2; cpr.z2 = tempptr; /* CALCULATE BACKWARD SHIFTS */ OR[9] = OR[10] = OR[11] = 0.0; for (i = numactive = 0; i < cp->count; i++) { if (cp->status[i] > 0) { OR[9] += cp->e1[i] - OR[12] * (OR[0] * cp->e2[i] + OR[1] * cp->n2[i] + OR[2] * cp->z2[i]); OR[10] += cp->n1[i] - OR[13] * (OR[3] * cp->e2[i] + OR[4] * cp->n2[i] + OR[5] * cp->z2[i]); OR[11] += cp->z1[i] - OR[14] * (OR[6] * cp->e2[i] + OR[7] * cp->n2[i] + OR[8] * cp->z2[i]); numactive++; } } OR[9] /= numactive; OR[10] /= numactive; OR[11] /= numactive; OR = OR12; G_debug(1, "********************************"); G_debug(1, "Forward transformation:"); G_debug(1, "Orthogonal rotation matrix:"); G_debug(1, "%.4f %.4f %.4f", OR[0], OR[1], OR[2]); G_debug(1, "%.4f %.4f %.4f", OR[3], OR[4], OR[5]); G_debug(1, "%.4f %.4f %.4f", OR[6], OR[7], OR[8]); G_debug(1, "x, y, z shift: %.4f %.4f %.4f", OR[9], OR[10], OR[11]); G_debug(1, "x, y, z scale: %.4f %.4f %.4f", OR[12], OR[13], OR[14]); OR = OR21; G_debug(1, "********************************"); G_debug(1, "Backward transformation:"); G_debug(1, "Orthogonal rotation matrix:"); G_debug(1, "%.4f %.4f %.4f", OR[0], OR[1], OR[2]); G_debug(1, "%.4f %.4f %.4f", OR[3], OR[4], OR[5]); G_debug(1, "%.4f %.4f %.4f", OR[6], OR[7], OR[8]); G_debug(1, "x, y, z shift: %.4f %.4f %.4f", OR[9], OR[10], OR[11]); G_debug(1, "x, y, z scale: %.4f %.4f %.4f", OR[12], OR[13], OR[14]); return status; } /*********************************************************************** COMPUTE THE GEOREFFERENCING COEFFICIENTS BASED ON A SET OF CONTROL POINTS ************************************************************************/ static int calccoef(struct Control_Points_3D *cp, double OR[], int ndims) { double **src_mat = NULL; double **src_mat_T = NULL; double **dest_mat = NULL; double **dest_mat_T = NULL; double **src_dest_mat = NULL; double *S_vec = NULL; double **R_mat = NULL; double **R_mat_T = NULL; double **mat_mn1 = NULL; double **mat_mn2 = NULL; double **mat_nm1 = NULL; double **mat_nm2 = NULL; double **mat_nn1 = NULL; double **E_mat = NULL; double **P_mat = NULL; double **Q_mat = NULL; double *D_vec = NULL; double *one_vec = NULL; double trace1 = 0.0; double trace2 = 0.0; int numactive; /* NUMBER OF ACTIVE CONTROL POINTS */ int m, n, i, j; int status; /* CALCULATE THE NUMBER OF VALID CONTROL POINTS */ for (i = numactive = 0; i < cp->count; i++) { if (cp->status[i] > 0) numactive++; } m = numactive; n = ndims; src_mat = G_alloc_matrix(m, n); dest_mat = G_alloc_matrix(m, n); for (i = numactive = 0; i < cp->count; i++) { if (cp->status[i] > 0) { src_mat[numactive][0] = cp->e1[i]; src_mat[numactive][1] = cp->n1[i]; src_mat[numactive][2] = cp->z1[i]; dest_mat[numactive][0] = cp->e2[i]; dest_mat[numactive][1] = cp->n2[i]; dest_mat[numactive][2] = cp->z2[i]; numactive++; } } D_vec = G_alloc_vector(ndims); src_mat_T = G_alloc_matrix(n, m); dest_mat_T = G_alloc_matrix(n, m); src_dest_mat = G_alloc_matrix(n, n); R_mat = G_alloc_matrix(n, n); R_mat_T = G_alloc_matrix(n, n); mat_mn1 = G_alloc_matrix(m, n); mat_mn2 = G_alloc_matrix(m, n); mat_nm1 = G_alloc_matrix(n, m); mat_nm2 = G_alloc_matrix(n, m); mat_nn1 = G_alloc_matrix(n, n); E_mat = G_alloc_matrix(m, m); P_mat = G_alloc_matrix(ndims, ndims); Q_mat = G_alloc_matrix(ndims, ndims); transpose_matrix(m, n, dest_mat, dest_mat_T); for (i = 0; i < m; i++) { for (j = 0; j < m; j++) { if (i != j) { E_mat[i][j] = -1.0 / (double)m; } else{ E_mat[i][j] = 1.0 - 1.0 / (double)m; } } } matmult(n, m, m, dest_mat_T, E_mat, mat_nm1); matmult(n, m, n, mat_nm1, src_mat, src_dest_mat); copy_matrix(n, n, src_dest_mat, P_mat); copy_matrix(n, n, src_dest_mat, mat_nn1); status = G_math_svduv(D_vec, mat_nn1, P_mat, n, Q_mat, n); if (status == 0) status = MSUCCESS; transpose_matrix(n, n, P_mat, mat_nn1); /* rotation matrix */ matmult(n, n, n, Q_mat, mat_nn1, R_mat_T); transpose_matrix(n, n, R_mat_T, R_mat); /* scale */ matmult(n, n, n, src_dest_mat, R_mat_T, mat_nn1); trace1 = trace(n, n, mat_nn1); transpose_matrix(m, n, src_mat, src_mat_T); matmult(n, m, m, src_mat_T, E_mat, mat_nm1); matmult(n, m, n, mat_nm1, src_mat, mat_nn1); trace2 = trace(n, n, mat_nn1); OR[14] = trace1 / trace2; /* shifts */ matmult(m, n, n, src_mat, R_mat_T, mat_mn1); scale_matrix(m, n, OR[14], mat_mn1, mat_mn2); subtract_matrix(m, n, dest_mat, mat_mn2, mat_mn1); scale_matrix(m, n, 1.0 / m, mat_mn1, mat_mn2); transpose_matrix(m, n, mat_mn2, mat_nm1); S_vec = G_alloc_vector(n); one_vec = G_alloc_vector(m); for (i = 0; i < m; i++){ one_vec[i] = 1.0; } matrix_multiply(n, m, mat_nm1, one_vec, S_vec); /* matrix to vector */ for (i = 0; i < ndims; i++) { for (j = 0; j < ndims; j++) { OR[i * ndims + j] = R_mat[i][j]; } } G_free_matrix(src_mat); G_free_matrix(src_mat_T); G_free_matrix(dest_mat); G_free_matrix(dest_mat_T); G_free_matrix(src_dest_mat); G_free_vector(D_vec); G_free_matrix(E_mat); G_free_matrix(P_mat); G_free_matrix(Q_mat); G_free_matrix(R_mat); G_free_matrix(R_mat_T); G_free_matrix(mat_mn1); G_free_matrix(mat_mn2); G_free_matrix(mat_nm1); G_free_matrix(mat_nm2); G_free_matrix(mat_nn1); G_free_vector(S_vec); G_free_vector(one_vec); return status; } /*********************************************************************** CALCULATE SCALE ************************************************************************/ static int calcscale(struct Control_Points_3D *cp, double OR[]) { double sumX, sumY, sumsqX, sumsqY, sumXY; int numactive; /* NUMBER OF ACTIVE CONTROL POINTS */ int i; /* CALCULATE SCALE */ sumX = sumY = sumsqX = sumsqY = sumXY = 0.0; for (i = numactive = 0; i < cp->count; i++) { if (cp->status[i] > 0) { double c1, c2; c1 = OR[0] * cp->e1[i] + OR[1] * cp->n1[i] + OR[2] * cp->z1[i]; c2 = cp->e2[i]; sumX += c1; sumY += c2; sumsqX += c1 * c1; sumsqY += c2 * c2; sumXY += c1 * c2; c1 = OR[3] * cp->e1[i] + OR[4] * cp->n1[i] + OR[5] * cp->z1[i]; c2 = cp->n2[i]; sumX += c1; sumY += c2; sumsqX += c1 * c1; sumsqY += c2 * c2; sumXY += c1 * c2; c1 = OR[6] * cp->e1[i] + OR[7] * cp->n1[i] + OR[8] * cp->z1[i]; c2 = cp->z2[i]; sumX += c1; sumY += c2; sumsqX += c1 * c1; sumsqY += c2 * c2; sumXY += c1 * c2; numactive++; } } OR[12] = (sumXY - sumX * sumY / numactive) / (sumsqX - sumX * sumX / numactive); if (fabs(OR[12] - OR[14]) > 10 * GRASS_EPSILON) { G_debug(1, "Scale mismatch: %.4f %.4f", OR[12], OR[14]); OR[12] = OR[14]; } OR[13] = OR[14] = OR[12]; return MSUCCESS; } void transpose_matrix(int m, int n, double **src_matrix, double **dest_matrix) { int i, j; for(i = 0; i < m; i++) { for(j = 0; j < n; j++) { dest_matrix[j][i] = src_matrix[i][j]; } } } void matmult(int m, int n, int p, double **mat1, double **mat2, double **mat3) { int i, j, k; double sum; /* input mat1: m x n */ /* input mat2: n x p */ /* output mat3: m x p */ for (i = 0; i < m; i++) { for (j = 0; j < p; j++) { sum = 0.0; for (k = 0; k < n; k++) { sum += mat1[i][k] * mat2[k][j]; } mat3[i][j] = sum; } } } void copy_matrix(int n, int m, double **src_matrix, double **dest_matrix) { int i, j; for (i = 0; i < n; i++) { for (j = 0; j < m; j++) { dest_matrix[i][j] = src_matrix[i][j]; } } } double trace(int n, int m, double **matrix) { int i; double t = 0.0; for (i = 0; i < n; i++) { t += matrix[i][i]; } return t; } void init_matrix(int n, int m, double **matrix) { int i, j; for (i = 0; i < n; i++) { for (j = 0; j < m; j++) { matrix[i][j] = 0.0; } } } void scale_matrix(int n, int m, double scal, double **src_matrix, double **dest_matrix) { int i, j; for (i = 0; i < n; i++) { for (j = 0; j < m; j++) { dest_matrix[i][j] = scal * src_matrix[i][j]; } } } void subtract_matrix(int n, int m, double **mat1, double **mat2, double **mat3) { int i, j; for (i = 0; i < n; i++) { for (j = 0; j < m; j++) { mat3[i][j] = mat1[i][j] - mat2[i][j]; } } } void matrix_multiply(int n, int m, double **mat, double *iv, double *ov) { int i, j; for (i = 0; i < n; i++) { ov[i] = 0.0; for(j = 0; j < m; j++) { ov[i] += mat[i][j] * iv[j]; } } }