GIS演算法基礎(四)平面座標變換(變換矩陣演算法實現)
目錄
一、平面直角座標系的建立
在平面上選一點作為直角座標的原點,過該原點作相互垂直的兩軸,就建立起了平面直角座標系,如上圖所示。
在程式碼中,我們可以用一個類表示一個點實體,他由一串座標組成,但是,如果這些點如果位於不同的座標系中,該怎麼轉換呢?通過對X,Y的操作,比如平移就在相應的X,Y分量上加偏移量,我們就可以實現。那如果,我們既要平移,又要旋轉,或者一系列的對點實體的操作,該怎麼實現?這個時候就可以用到平面座標變換矩陣。
二、平面座標變換矩陣
“變換矩陣是數學線性代數中的一個概念。線上性代數中,線性變換能夠用矩陣表示。如果T是一個把Rn對映到Rm的線性變換,且x是一個具有n個元素的列向量 ,那麼我們把m×n的矩陣A,稱為T的變換矩陣。”
-------
其實沒有這麼複雜,就是我們通過對一個座標串構成的矩陣與某個矩陣相乘,得到的新矩陣包含了我們所要的座標的資訊。這個"某個矩陣"在這裡就是螢幕座標變換矩陣。
怎麼構建 矩陣吧,矩陣的構建可以用二維陣列實現。這個不是演算法的重點,所以我就不po程式碼了,想看程式碼可以到我的github上看
https://github.com/XiaoZhong233/GIS_ALG/blob/master/src/scau/gz/zhw/BasicTransform.java
平面座標變換矩陣可由下式表示:
/**
* |a d g|
* T= |b e h| |a d| |g|
* |c f i| |b e| 負責對圖形的縮放,旋轉,對稱,錯切 。[c f] 負責對圖形進行平移變換 |h| 負責投影變換
*/
構建程式碼:
public class SurfaceTransformationMatrix {
private double a,b,c,d,e,f,g,h,i;
private double[][] data= {{a,d,g},{b,e,h},{c,f,i}};
private Matrix matrix;
public SurfaceTransformationMatrix() {
this.matrix = new Matrix(data);
}
public SurfaceTransformationMatrix(double[][] data) {
this.matrix = new Matrix(data);
}
public Matrix getMatrix() {
return matrix;
}
}
三、平移變換
公式如下:
(m,n)是變換後的座標,(x,y)是變換前的座標,tx,ty分別對應x軸,y軸的偏移量
構建程式碼:
public class TransformMatrix extends SurfaceTransformationMatrix{
private Matrix matrix;
public TransformMatrix(double Tx,double Ty) {
super(new double[][]{{1,0,0},{0,1,0},{Tx,Ty,1}});
this.matrix = super.getMatrix();
}
public Matrix getTransformMatrix() {
return matrix;
}
}
平移演算法:
/**
* 平移演算法
* @param point
* @param x x正方向偏移量
* @param y y正方向偏移量
* @return
*/
public static Point transform(Point point,double x,double y) {
Matrix matrix = new TransformMatrix(x, y).getTransformMatrix();
double [][] data= {{point.getX(),point.getY(),1}};
Matrix pointMatrix = new Matrix(data);
Matrix result = pointMatrix.RightMultiMatrix(matrix);
//System.out.println("平移後的點 :"+new Point(result.getMatrix()[0][0], result.getMatrix()[0][1]).toString());
return new Point(result.getMatrix()[0][0], result.getMatrix()[0][1]);
}
public static Line transform(Line line,double x,double y) {
Point start = line.getStart();
Point end = line.getEnd();
Point newStart = transform(start, x, y);
Point newEnd = transform(end, x, y);
return new Line(newStart,newEnd);
}
public static Polygon transform(Polygon polygon,double x,double y) {
Point[] points = polygon.getPoints();
Point[] result = new Point[points.length];
for(int i=0;i<points.length;i++) {
result[i]=transform(points[i], x, y);
//System.out.println("result :"+result[i].toString());
}
return new Polygon(result, polygon.isClose());
}
接下來的變換基本都和這個變換的例子差不多,無非是引數的變化
四、比例變換
變換公式:[x* y* 1] = [x y 1] x [{Sx,0,0},{0,Sy,0},{0,0,1}] = [Sx*x Sy*y 1]
因為公式沒找到圖,就用二維陣列來表示
x*,y*是x,y變換後的座標
變換關係如下
(1)當Sx = Sy = 1 時,為恆等比例變換,就是圖形不變
(2)當Sx = Sy > 1 時,圖形沿兩個座標軸方向等比例放大。
(3)當Sx = Sy < 1 時,圖形沿兩個座標軸方向等比例縮小。
(4)當Sx != Sy 時,圖形沿兩個座標軸方向做非均勻的比例變換。
構建程式碼:
public class ScaleMtrix extends SurfaceTransformationMatrix{
private Matrix matrix;
public ScaleMtrix(double Sx,double Sy) {
// TODO Auto-generated constructor stub
super(new double[][]{{Sx,0,0},{0,Sy,0},{0,0,1}});
this.matrix = super.getMatrix();
}
public Matrix getScaleMatrix() {
return matrix;
}
}
演算法:
/**
* 比例變換演算法
* x=y時,恆比例放大或縮小
* x!=y時,圖形沿兩個座標軸方向做非均勻比例變換
* @param point
* @param x
* @param y
* @return
*/
public static Point scale(Point point,double x,double y) {
Matrix matrix = new ScaleMtrix(x, y).getScaleMatrix();
double [][] data= {{point.getX(),point.getY(),1}};
Matrix pointMatrix = new Matrix(data);
Matrix result = pointMatrix.RightMultiMatrix(matrix);
return new Point(result.getMatrix()[0][0], result.getMatrix()[0][1]);
}
public static Line scale(Line line,double x,double y) {
Point start = line.getStart();
Point end = line.getEnd();
Point newStart = scale(start, x, y);
Point newEnd = scale(end, x, y);
return new Line(newStart,newEnd);
}
public static Polygon scale(Polygon polygon,double x,double y) {
Point[] points = polygon.getPoints();
Point[] result = new Point[points.length];
for(int i=0;i<points.length;i++) {
result[i]=scale(points[i], x, y);
//System.out.println("result :"+result[i].toString());
}
return new Polygon(result, polygon.isClose());
}
五、對稱變換
公式如下:
[x*,y*,1] = [x,y,1] x [{a,d,0},{b,e,0},{0,0,1}] = [ax+by dx+ey 1]
變換關係:
(1)當b=d=0,a=-1,e=1時,產生與y軸對稱的反射圖形
(2)當b=d=0,a=1,e=-1時,產生與x軸對稱的反射圖形
(3)當b=d=0,a=e=-1時,產生與原點對稱的反射圖形
(4)當b=d=1,a=e=0時,產生與直線y=x對稱的反射圖形
(5)當b=d=-1,a=e=0時,產生與直線y=-x對稱的反射圖形
構建程式碼:
/**
* 對稱變換矩陣
* @author Administrator
*
*/
public class SymmetryMatrix extends SurfaceTransformationMatrix{
private Matrix matrix;
public SymmetryMatrix(double a,double b,double d,double e) {
// TODO Auto-generated constructor stub
super(new double[][] {{a,d,0},{b,e,0},{0,0,1}});
this.matrix = super.getMatrix();
}
public Matrix getSymmetryMatrix() {
return matrix;
}
}
演算法:
/**
* 對稱變換
* @param point
* @param symmetryType 列舉型別
* @return
*/
public static Point symmetry(Point point,SymmetryType symmetryType) {
Matrix matrix;
switch (symmetryType) {
case xAxis:
matrix = new SymmetryMatrix(1, 0, 0, -1).getSymmetryMatrix();
break;
case yAxis:
matrix = new SymmetryMatrix(-1, 0, 0, 1).getSymmetryMatrix();
break;
case yx:
matrix = new SymmetryMatrix(0, 1, 1, 0).getSymmetryMatrix();
break;
case anti_yx:
matrix = new SymmetryMatrix(0, -1, -1, 0).getSymmetryMatrix();
break;
case origin:
matrix = new SymmetryMatrix(-1, 0, 0, -1).getSymmetryMatrix();
default:
matrix = new SymmetryMatrix(-1, 0, 0, -1).getSymmetryMatrix();
break;
}
double [][] data= {{point.getX(),point.getY(),1}};
Matrix pointMatrix = new Matrix(data);
Matrix result = pointMatrix.RightMultiMatrix(matrix);
return new Point(result.getMatrix()[0][0], result.getMatrix()[0][1]);
}
六、旋轉變換
公式如下:
[x*,y*,1] =
[x,y,1] x [{cosa,sina,0},{-sina,cosa,0},{0,0,1}] = [xcosa-ysina xsina+ycosa 1]
a是二維圖形繞原點順時針旋轉a角。
構建程式碼:
public class RotateMatrix extends SurfaceTransformationMatrix{
private Matrix matrix;
public RotateMatrix(double angle) {
// TODO Auto-generated constructor stub
super(new double[][] {{Math.cos(Math.toRadians(angle)),Math.sin(Math.toRadians(angle)),0},
{-Math.sin(Math.toRadians(angle)),Math.cos(Math.toRadians(angle)),0},
{0,0,1}});
this.matrix = super.getMatrix();
}
public Matrix getRotateMatrix() {
return matrix;
}
}
演算法:
/**
* 旋轉變換
* @param point
* @param angle 角度制單位
* @return
*/
public static Point rotate(Point point,double angle) {
Matrix matrix = new RotateMatrix(angle).getRotateMatrix();
double [][] data= {{point.getX(),point.getY(),1}};
Matrix pointMatrix = new Matrix(data);
Matrix result = pointMatrix.RightMultiMatrix(matrix);
return new Point(result.getMatrix()[0][0], result.getMatrix()[0][1]);
}
七、錯切變換
公式如下:
[x*,y*,1] = [x,y,1] * [{1,d,0},{b,1,0},{0,0,1}] = [x+by,dx+y,1]
x*,y*為變換後的座標。
變換關係如下:
(1)當d=0時,x*=x+by,y*=y,此時圖形的y座標不變,x座標隨初值(x,y)及變換系數b而作線性變換;若b>0,則圖形沿+x方向做錯切位移;b<0圖形沿-x方向做錯切位移。
(2)當b=0時,x*=x,y*=dx+y,此時圖形的x座標不變,y座標隨初值(x,y)及變換系數d做線性變換;如d>0,則圖形沿+y方向作錯切變換;d<0時,圖形沿-y方向做錯切位移。
(3)當b!=0時,且d!=0時,x*=x+by,y*=dx+y,圖形沿x,y兩個方向錯切位移。
構建程式碼:
public class MiscutMatrix extends SurfaceTransformationMatrix{
private Matrix matrix;
public MiscutMatrix(double d,double b) {
// TODO Auto-generated constructor stub
super(new double[][] {{1,d,0},{b,1,0},{0,0,1}});
this.matrix = super.getMatrix();
}
public Matrix getMiscutMatrix(){
return matrix;
}
}
演算法如下:
/**
* 錯切變換
* @param point
* @param b=0,y軸隨變換系數d變換 b>0,圖形沿+y方向做錯切變換,b<0,圖形沿-y方向做錯切變換
* @param d=0,y軸隨變換系數b變換 b>0,圖形沿+x方向做錯切變換,b<0,圖形沿-x方向做錯切變換
* b!=0 && d!=0時,x*=x+by y*=dx+y 圖形沿x,y兩個方向做錯切變換
* @return
*/
public static Point miscut(Point point,double b,double d) {
Matrix matrix = new MiscutMatrix(b,d).getMiscutMatrix();
double [][] data= {{point.getX(),point.getY(),1}};
Matrix pointMatrix = new Matrix(data);
Matrix result = pointMatrix.RightMultiMatrix(matrix);
return new Point(result.getMatrix()[0][0], result.getMatrix()[0][1]);
}
八、複合變換
複合變換是指圖形做一次以上的幾何變換,變換結果是每次變換矩陣相乘。
(1)、複合平移
直接上程式碼吧,就直接幾個平移矩陣相乘
/**
* 複合平移
* @param point
* @param matrixs
* @return
*/
public static Point complexTransform(Point point,TransformMatrix...matrixs) {
int len = matrixs.length;
Matrix matrix = matrixs[0].getTransformMatrix();
for(int i=1;i<len;i++) {
matrix = matrix.RightMultiMatrix(matrixs[i].getTransformMatrix());
}
double [][] data= {{point.getX(),point.getY(),1}};
Matrix pointMatrix = new Matrix(data);
Matrix result = pointMatrix.RightMultiMatrix(matrix);
return new Point(result.getMatrix()[0][0], result.getMatrix()[0][1]);
}
(2)複合比例變換
/**
* 複合比例變換
* @param point
* @param matrixs
* @return
*/
public static Point complexScale(Point point,ScaleMtrix...matrixs) {
int len = matrixs.length;
Matrix matrix = matrixs[0].getScaleMatrix();
for(int i=1;i<len;i++) {
matrix = matrix.RightMultiMatrix(matrixs[i].getScaleMatrix());
}
double [][] data= {{point.getX(),point.getY(),1}};
Matrix pointMatrix = new Matrix(data);
Matrix result = pointMatrix.RightMultiMatrix(matrix);
return new Point(result.getMatrix()[0][0], result.getMatrix()[0][1]);
}
(3)複合旋轉
/**
* 複合旋轉變換
* @param point
* @param matrixs
* @return
*/
public static Point complexRotate(Point point,RotateMatrix ...matrixs) {
int len = matrixs.length;
Matrix matrix = matrixs[0].getRotateMatrix();
for(int i=1;i<len;i++) {
matrix = matrix.RightMultiMatrix(matrixs[i].getRotateMatrix());
}
double [][] data= {{point.getX(),point.getY(),1}};
Matrix pointMatrix = new Matrix(data);
Matrix result = pointMatrix.RightMultiMatrix(matrix);
return new Point(result.getMatrix()[0][0], result.getMatrix()[0][1]);
}
比例,旋轉變換是與參考的有關的,上面的都是相對於原點的比例變換,如果要參考某個(m,n)點做比例 ,旋轉變換,其變換過程就是先把該座標系的原點移到(m,n)上來,然後做了旋轉或比例變換,然後再移回去。
(4)相對某點的比例變換
/**
* 相對於某點的比例變換
* @param point 待變換的點
* @param center 相對點
* @param x
* @param y
* @return
*/
public static Point scaleAround(Point point,Point center,double x,double y) {
TransformMatrix t1 = new TransformMatrix(-center.getX(), -center.getY());
ScaleMtrix scaleMtrix = new ScaleMtrix(x, y);
TransformMatrix t2 = new TransformMatrix(center.getX(), center.getY());
return complexTransmit(point, t1,scaleMtrix,t2);
}
(5)相對某點的選址變換
/**
* 圍繞某點的旋轉變換
* @param point 待變換的點
* @param center 相對點
* @param angle
* @return
*/
public static Point rotateAround(Point point,Point center,double angle) {
TransformMatrix t1 = new TransformMatrix(-center.getX(), -center.getY());
RotateMatrix rotateMatrix = new RotateMatrix(angle);
TransformMatrix t2 = new TransformMatrix(center.getX(), center.getY());
return complexTransmit(point, t1,rotateMatrix,t2);
}
最後 po上我github地址,有需要的同學可以看看
https://github.com/XiaoZhong233/GIS_ALG/blob/master/src/scau/gz/zhw/BasicTransform.java