/***************************************************************************/ /***************************************************************************/ /* CRS.C - Center for Remote Sensing rectification routines Written By: Brian J. Buckley At: The Center for Remote Sensing Michigan State University 302 Berkey Hall East Lansing, MI 48824 (517)353-7195 Written: 12/19/91 Last Update: 12/26/91 Brian J. Buckley Last Update: 1/24/92 Brian J. Buckley Added printout of trnfile. Triggered by BDEBUG. Last Update: 1/27/92 Brian J. Buckley Fixed bug so that only the active control points were used. */ /***************************************************************************/ /***************************************************************************/ #include #include #include #include #include #include /* #define MSDOS 1 */ /* #define BDEBUG */ #ifdef BDEBUG FILE *fp; #endif #ifdef MSDOS #include "stdlib.h" #include "dummy.h" typedef double DOUBLE; #else #include typedef double DOUBLE; #endif #include "crs.h" /* STRUCTURE FOR USE INTERNALLY WITH THESE FUNCTIONS. THESE FUNCTIONS EXPECT SQUARE MATRICES SO ONLY ONE VARIABLE IS GIVEN (N) FOR THE MATRIX SIZE */ struct MATRIX { int n; /* SIZE OF THIS MATRIX (N x N) */ DOUBLE *v; }; /* CALCULATE OFFSET INTO ARRAY BASED ON R/C */ #define M(row,col) m->v[(((row)-1)*(m->n))+(col)-1] /***************************************************************************/ /* */ /***************************************************************************/ #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 */ /***************************************************************************/ #ifdef MSDOS static int calccoef(struct Control_Points *,double *,double *,int); static int calcls(struct Control_Points *,struct MATRIX *, DOUBLE *,DOUBLE *,double *,double *); static int exactdet(struct Control_Points *,struct MATRIX *, DOUBLE *,DOUBLE *,double *,double *); static int solvemat(struct MATRIX *,DOUBLE *,DOUBLE *,double *,double *); static DOUBLE term(int,double,double); #ifdef BDEBUG static int checkgeoref(struct Control_Points *,double *,double *,int,int, FILE *fp); #endif #else static int calccoef(); static int calcls(); static int exactdet(); static int solvemat(); static DOUBLE term(); #ifdef BDEBUG static int checkgeoref(); #endif #endif /***************************************************************************/ /* USE THIS TRANSFORMATION FUNCTION IF YOU WANT TO DO ARRAYS. */ /***************************************************************************/ #ifdef BETTERGEOREF extern void CRS_georef(int,DOUBLE *,DOUBLE *,DOUBLE *,DOUBLE *,int); void CRS_georef2 ( int order, /* ORDER OF TRANSFORMATION TO BE PERFORMED, MUST MATCH THE ORDER USED TO CALCULATE THE COEFFICIENTS */ double E[], /* EASTING COEFFICIENTS */ double N[], /* NORTHING COEFFICIENTS */ double e[], /* EASTINGS TO BE TRANSFORMED */ double n[], /* NORTHINGS TO BE TRANSFORMED */ int numpts /* NUMBER OF POINTS TO BE TRANSFORMED */ ) { DOUBLE e3, e2n, e2, en2, en, e1, n3, n2, n1; int i; switch(order) { case 3: for(i = 0 ; i < numpts ; i++) { e1 = e[i]; n1 = n[i]; e2 = e1 * e1; en = e1 * n1; n2 = n1 * n1; e3 = e1 * e2; e2n = e2 * n1; en2 = e1 * n2; n3 = n1 * n2; e[i] = E[0] + E[1] * e1 + E[2] * n1 + E[3] * e2 + E[4] * en + E[5] * n2 + E[6] * e3 + E[7] * e2n + E[8] * en2 + E[9] * n3; n[i] = N[0] + N[1] * e1 + N[2] * n1 + N[3] * e2 + N[4] * en + N[5] * n2 + N[6] * e3 + N[7] * e2n + N[8] * en2 + N[9] * n3; } break; case 2: for(i = 0 ; i < numpts ; i++) { e1 = e[i]; n1 = n[i]; e2 = e1 * e1; n2 = n1 * n1; en = e1 * n1; e[i] = E[0] + E[1] * e1 + E[2] * n1 + E[3] * e2 + E[4] * en + E[5] * n2; n[i] = N[0] + N[1] * e1 + N[2] * n1 + N[3] * e2 + N[4] * en + N[5] * n2; } break; case 1: for(i = 0 ; i < numpts ; i++) { e1 = e[i]; n1 = n[i]; e[i] = E[0] + E[1] * e1 + E[2] * n1; n[i] = N[0] + N[1] * e1 + N[2] * n1; } break; } } #endif /***************************************************************************/ /* TRANSFORM A SINGLE COORDINATE PAIR. */ /***************************************************************************/ int CRS_georef ( double e1, /* EASTINGS TO BE TRANSFORMED */ double n1, /* NORTHINGS TO BE TRANSFORMED */ double *e, /* EASTINGS TO BE TRANSFORMED */ double *n, /* NORTHINGS TO BE TRANSFORMED */ double E[], /* EASTING COEFFICIENTS */ double N[], /* NORTHING COEFFICIENTS */ int order /* ORDER OF TRANSFORMATION TO BE PERFORMED, MUST MATCH THE ORDER USED TO CALCULATE THE COEFFICIENTS */ ) { DOUBLE e3, e2n, en2, n3, e2, en, n2; switch(order) { case 1: *e = E[0] + E[1] * e1 + E[2] * n1; *n = N[0] + N[1] * e1 + N[2] * n1; break; case 2: e2 = e1 * e1; n2 = n1 * n1; en = e1 * n1; *e = E[0] + E[1] * e1 + E[2] * n1 + E[3] * e2 + E[4] * en + E[5] * n2; *n = N[0] + N[1] * e1 + N[2] * n1 + N[3] * e2 + N[4] * en + N[5] * n2; break; case 3: e2 = e1 * e1; en = e1 * n1; n2 = n1 * n1; e3 = e1 * e2; e2n = e2 * n1; en2 = e1 * n2; n3 = n1 * n2; *e = E[0] + E[1] * e1 + E[2] * n1 + E[3] * e2 + E[4] * en + E[5] * n2 + E[6] * e3 + E[7] * e2n + E[8] * en2 + E[9] * n3; *n = N[0] + N[1] * e1 + N[2] * n1 + N[3] * e2 + N[4] * en + N[5] * n2 + N[6] * e3 + N[7] * e2n + N[8] * en2 + N[9] * n3; break; default: return(MPARMERR); break; } return(MSUCCESS); } /***************************************************************************/ /* COMPUTE THE GEOREFFERENCING COEFFICIENTS BASED ON A SET OF CONTROL POINTS */ /***************************************************************************/ int CRS_compute_georef_equations (struct Control_Points *cp, double E12[], double N12[], double E21[], double N21[], int order) { double *tempptr; int status; if(order < 1 || order > MAXORDER) return(MPARMERR); #ifdef BDEBUG fp = fopen("error.dat","w"); if(fp == NULL) return(-1); #endif /* CALCULATE THE FORWARD TRANSFORMATION COEFFICIENTS */ status = calccoef(cp,E12,N12,order); if(status != MSUCCESS) return(status); #ifdef BDEBUG checkgeoref(cp,E12,N12,order,1,fp); #endif /* SWITCH THE 1 AND 2 EASTING AND NORTHING ARRAYS */ tempptr = cp->e1; cp->e1 = cp->e2; cp->e2 = tempptr; tempptr = cp->n1; cp->n1 = cp->n2; cp->n2 = tempptr; /* CALCULATE THE BACKWARD TRANSFORMATION COEFFICIENTS */ status = calccoef(cp,E21,N21,order); #ifdef BDEBUG checkgeoref(cp,E21,N21,order,0,fp); fclose(fp); #endif /* SWITCH THE 1 AND 2 EASTING AND NORTHING ARRAYS BACK */ tempptr = cp->e1; cp->e1 = cp->e2; cp->e2 = tempptr; tempptr = cp->n1; cp->n1 = cp->n2; cp->n2 = tempptr; return(status); } /***************************************************************************/ /* COMPUTE THE GEOREFFERENCING COEFFICIENTS BASED ON A SET OF CONTROL POINTS */ /***************************************************************************/ static int calccoef (struct Control_Points *cp, double E[], double N[], int order) { struct MATRIX m; DOUBLE *a; DOUBLE *b; int numactive; /* NUMBER OF ACTIVE CONTROL POINTS */ int status, i; /* CALCULATE THE NUMBER OF VALID CONTROL POINTS */ for(i = numactive = 0 ; i < cp->count ; i++) { if(cp->status[i] > 0) numactive++; } /* CALCULATE THE MINIMUM NUMBER OF CONTROL POINTS NEEDED TO DETERMINE A TRANSFORMATION OF THIS ORDER */ m.n = ((order + 1) * (order + 2)) / 2; if(numactive < m.n) return(MNPTERR); /* INITIALIZE MATRIX */ m.v = (DOUBLE *)G_calloc(m.n*m.n,sizeof(DOUBLE)); if(m.v == NULL) { return(MMEMERR); } a = (DOUBLE *)G_calloc(m.n,sizeof(DOUBLE)); if(a == NULL) { G_free((char *)m.v); return(MMEMERR); } b = (DOUBLE *)G_calloc(m.n,sizeof(DOUBLE)); if(b == NULL) { G_free((char *)m.v); G_free((char *)a); return(MMEMERR); } if(numactive == m.n) status = exactdet(cp,&m,a,b,E,N); else status = calcls(cp,&m,a,b,E,N); G_free((char *)m.v); G_free((char *)a); G_free((char *)b); return(status); } /***************************************************************************/ /* CALCULATE THE TRANSFORMATION COEFFICIENTS WITH EXACTLY THE MINIMUM NUMBER OF CONTROL POINTS REQUIRED FOR THIS TRANSFORMATION. */ /***************************************************************************/ static int exactdet ( struct Control_Points *cp, struct MATRIX *m, DOUBLE a[], DOUBLE b[], double E[], /* EASTING COEFFICIENTS */ double N[] /* NORTHING COEFFICIENTS */ ) { int pntnow, currow, j; currow = 1; for(pntnow = 0 ; pntnow < cp->count ; pntnow++) { if(cp->status[pntnow] > 0) { /* POPULATE MATRIX M */ #ifdef BDEBUG fprintf(fp,"%2d ",pntnow+1); #endif for(j = 1 ; j <= m->n ; j++) { M(currow,j) = term(j,cp->e1[pntnow],cp->n1[pntnow]); #ifdef BDEBUG fprintf(fp,"%+14.7le ",M(currow,j)); if(j == 5) fprintf(fp,"\n "); #endif } #ifdef BDEBUG fprintf(fp,"\n"); #endif /* POPULATE MATRIX A AND B */ a[currow-1] = cp->e2[pntnow]; b[currow-1] = cp->n2[pntnow]; #ifdef BDEBUG fprintf(fp," %+14.7le ",a[currow-1]); fprintf(fp,"%+14.7le\n",b[currow-1]); #endif currow++; } #ifdef BDEBUG else { fprintf(fp,"%2d UNUSED\n",pntnow+1); } #endif } if(currow - 1 != m->n) return(MINTERR); return(solvemat(m,a,b,E,N)); } /***************************************************************************/ /* CALCULATE THE TRANSFORMATION COEFFICIENTS WITH MORE THAN THE MINIMUM NUMBER OF CONTROL POINTS REQUIRED FOR THIS TRANSFORMATION. THIS ROUTINE USES THE LEAST SQUARES METHOD TO COMPUTE THE COEFFICIENTS. */ /***************************************************************************/ static int calcls ( struct Control_Points *cp, struct MATRIX *m, DOUBLE a[], DOUBLE b[], double E[], /* EASTING COEFFICIENTS */ double N[] /* NORTHING COEFFICIENTS */ ) { int i, j, n, numactive = 0; /* INITIALIZE THE UPPER HALF OF THE MATRIX AND THE TWO COLUMN VECTORS */ for(i = 1 ; i <= m->n ; i++) { for(j = i ; j <= m->n ; j++) M(i,j) = 0.0; a[i-1] = b[i-1] = 0.0; } /* SUM THE UPPER HALF OF THE MATRIX AND THE COLUMN VECTORS ACCORDING TO THE LEAST SQUARES METHOD OF SOLVING OVER DETERMINED SYSTEMS */ for(n = 0 ; n < cp->count ; n++) { if(cp->status[n] > 0) { numactive++; for(i = 1 ; i <= m->n ; i++) { for(j = i ; j <= m->n ; j++) M(i,j) += term(i,cp->e1[n],cp->n1[n]) * term(j,cp->e1[n],cp->n1[n]); a[i-1] += cp->e2[n] * term(i,cp->e1[n],cp->n1[n]); b[i-1] += cp->n2[n] * term(i,cp->e1[n],cp->n1[n]); } } } if(numactive <= m->n) return(MINTERR); /* TRANSPOSE VALUES IN UPPER HALF OF M TO OTHER HALF */ for(i = 2 ; i <= m->n ; i++) { for(j = 1 ; j < i ; j++) M(i,j) = M(j,i); } return(solvemat(m,a,b,E,N)); } /***************************************************************************/ /* CALCULATE THE X/Y TERM BASED ON THE TERM NUMBER ORDER\TERM 1 2 3 4 5 6 7 8 9 10 1 e0n0 e1n0 e0n1 2 e0n0 e1n0 e0n1 e2n0 e1n1 e0n2 3 e0n0 e1n0 e0n1 e2n0 e1n1 e0n2 e3n0 e2n1 e1n2 e0n3 */ /***************************************************************************/ static DOUBLE term (int term, double e, double n) { switch(term) { case 1: return((DOUBLE)1.0); case 2: return((DOUBLE)e); case 3: return((DOUBLE)n); case 4: return((DOUBLE)(e*e)); case 5: return((DOUBLE)(e*n)); case 6: return((DOUBLE)(n*n)); case 7: return((DOUBLE)(e*e*e)); case 8: return((DOUBLE)(e*e*n)); case 9: return((DOUBLE)(e*n*n)); case 10: return((DOUBLE)(n*n*n)); } return((DOUBLE)0.0); } /***************************************************************************/ /* SOLVE FOR THE 'E' AND 'N' COEFFICIENTS BY USING A SOMEWHAT MODIFIED GAUSSIAN ELIMINATION METHOD. | M11 M12 ... M1n | | E0 | | a0 | | M21 M22 ... M2n | | E1 | = | a1 | | . . . . | | . | | . | | Mn1 Mn2 ... Mnn | | En-1 | | an-1 | and | M11 M12 ... M1n | | N0 | | b0 | | M21 M22 ... M2n | | N1 | = | b1 | | . . . . | | . | | . | | Mn1 Mn2 ... Mnn | | Nn-1 | | bn-1 | */ /***************************************************************************/ static int solvemat (struct MATRIX *m, DOUBLE a[], DOUBLE b[], double E[], double N[]) { int i, j, i2, j2, imark; DOUBLE factor, temp; DOUBLE pivot; /* ACTUAL VALUE OF THE LARGEST PIVOT CANDIDATE */ for(i = 1 ; i <= m->n ; i++) { j = i; /* find row with largest magnitude value for pivot value */ pivot = M(i,j); imark = i; for(i2 = i + 1 ; i2 <= m->n ; i2++) { temp = fabs(M(i2,j)); if(temp > fabs(pivot)) { pivot = M(i2,j); imark = i2; } } /* if the pivot is very small then the points are nearly co-linear */ /* co-linear points result in an undefined matrix, and nearly */ /* co-linear points results in a solution with rounding error */ if(pivot == 0.0) return(MUNSOLVABLE); /* if row with highest pivot is not the current row, switch them */ if(imark != i) { for(j2 = 1 ; j2 <= m->n ; j2++) { temp = M(imark,j2); M(imark,j2) = M(i,j2); M(i,j2) = temp; } temp = a[imark-1]; a[imark-1] = a[i-1]; a[i-1] = temp; temp = b[imark-1]; b[imark-1] = b[i-1]; b[i-1] = temp; } /* compute zeros above and below the pivot, and compute values for the rest of the row as well */ for(i2 = 1 ; i2 <= m->n ; i2++) { if(i2 != i) { factor = M(i2,j) / pivot; for(j2 = j ; j2 <= m->n ; j2++) M(i2,j2) -= factor * M(i,j2); a[i2-1] -= factor * a[i-1]; b[i2-1] -= factor * b[i-1]; } } } /* SINCE ALL OTHER VALUES IN THE MATRIX ARE ZERO NOW, CALCULATE THE COEFFICIENTS BY DIVIDING THE COLUMN VECTORS BY THE DIAGONAL VALUES. */ for(i = 1 ; i <= m->n ; i++) { E[i-1] = a[i-1] / M(i,i); N[i-1] = b[i-1] / M(i,i); } return(MSUCCESS); } /***************************************************************************/ /* */ /***************************************************************************/ #ifdef BDEBUG static int checkgeoref (struct Control_Points *cp, double E[], double N[], int order, int forward, FILE *fp) { DOUBLE xrms, yrms, dx, dy, dx2, dy2, totaldist, dist; double tempx, tempy; int i, n, numactive; n = ((order + 1) * (order + 2)) / 2; if(forward) fprintf(fp,"FORWARD:\n"); else fprintf(fp,"BACKWARD:\n"); fprintf(fp,"%d order\n",order); for(i = 0 ; i < n ; i++) fprintf(fp,"%+.17E %+.17E\n",E[i],N[i]); xrms = yrms = dx2 = dy2 = totaldist = 0.0; numactive = 0; for(i = 0 ; i < cp->count ; i++) { fprintf(fp,"\nCONTROL POINT: %d\n",i+1); fprintf(fp,"%20s: %+.20lE %+.20lE\n","ORIGINAL POINT", cp->e1[i],cp->n1[i]); fprintf(fp,"%20s: %+.20lE %+.20lE\n","DESIRED POINT", cp->e2[i],cp->n2[i]); if(cp->status[i] > 0) { numactive++; CRS_georef(cp->e1[i],cp->n1[i],&tempx,&tempy,E,N,order); fprintf(fp,"%20s: %+.20lE %+.20lE\n","CALCULATED POINT",tempx,tempy); dx = tempx - cp->e2[i]; dy = tempy - cp->n2[i]; fprintf(fp,"%20s: %+.20lE %+.20lE\n","RESIDUAL ERROR",dx,dy); dx2 = dx * dx; dy2 = dy * dy; dist = sqrt(dx2 + dy2); fprintf(fp,"%20s: %+.20lE\n","DISTANCE (RMS) ERROR",dist); xrms += dx2; yrms += dy2; totaldist += dist; } else fprintf(fp,"NOT USED\n"); } xrms = sqrt(xrms / (DOUBLE)numactive); yrms = sqrt(yrms / (DOUBLE)numactive); fprintf(fp,"\n%20s: %+.20lE %+.20lE\n","RMS ERROR",xrms,yrms); fprintf(fp,"\n%20s: %+.20lE\n","TOTAL RMS ERROR",sqrt(xrms*xrms+yrms*yrms)); fprintf(fp,"\n%20s: %+.20lE\n","AVG. DISTANCE ERROR",totaldist/numactive); return(0); } #endif