GIS算法基础(五)矢量数据向栅格数据的转换(点,线算法实现)
矢量结构数据与栅格结构数据的相互转换,是地理信息系统的基本功能之一,已发展形成了许多高效的转换算法。
源码已经放在github上了,需要朋友自取。
https://github.com/XiaoZhong233/GIS_ALG/blob/master/src/scau/gz/zhw/Raster.java
目录
一、矢量点的栅格化
矢量点的栅格化: 实质是将点的坐标x、y换算为栅格行、列号。
注意:栅格的坐标原点是从左上角开始的,行数I向下递增,列数J向左递增
(X0,Y0)代表栅格的原点,dx,dy代表分别代表栅格的长和宽,也就是大小
实际代码中,这个x0,yo因为已知了栅格大小,所以可以求的出来。
在矢量数据向栅格数据转换的过程中,栅格数据可以用二维数组来表达。
/**
*
* @param point 带转化的矢量点
* @param xOffset 起始栅格点的x位移量
* @param yOffset 起始栅格点的y位移量
* @return 0-行 1-列
*/
public int[] transformRasterPoint(Point point,double xOffset,double yOffset) {
double x0=size+xOffset+this.xOffset;//栅格起始点坐标
double y0=ROW+yOffset+this.yOffset;
int[] rowAndColumn = new int[2];
//像元大小默认为1
rowAndColumn[0] = (int) (1+ (Math.floor(Math.abs((y0-point.getY())/size))));
rowAndColumn[1] = (int)(1+(Math.floor(Math.abs((point.getX()-x0))/size)));
return rowAndColumn;
}
测试结果:
输入:
Raster raster = new Raster(30,30); //创建一个30x30的栅格
Point p = new Point(.5, .5);
System.out.println(p);
int[] a = raster.transformRasterPoint(p, 0, 0);
System.out.println(String.format("I=%d,J=%d", a[0],a[1]));
输出:
(0.500000,0.500000)
I=30,J=1
二、矢量线的栅格化
①八方向栅格化
八方向栅格法很容易理解,就是已知直线的倾角情况,在每行或每列只有一个像元被涂黑。
算法思想:
- 一条线段有两个端点:P1(x1,y1)、P2(x2,y2),先分别确定其行列号(I1,J1)及(I2,J2),然后求出这两个端点位置的行数差和列数差。
- 若行数差大于列数差,则逐行求出本行中心线与过这两个端点的直线的交点(X,Y),得到交点行、列号,将交点“涂黑”:Y=yi行中心线,X=(yi-y1)k+x1;式中k=(x2-x1)/(y2-y1),这个公式不看也罢,其实就是两直线求交点的方程
- 若行数差小于或等于列数差,则逐列求出本列中心线与过这两个端点的直线的交点(X,Y),得到交点行、列号,将交点“涂黑” :X=xi行中心线,Y=(xj-x1)k’+y1;式中k’=(y2-y1) /(x2-x1)
算法实现:
public void RasterLine(Point start,Point end,int type,double xOffset,double yOffset) {
//开始点与结束点的起始坐标
int[] startPoint = transformRasterPoint(start, 0, 0);
int[] endPoint = transformRasterPoint(end, 0, 0);
//
System.out.println(String.format("开始点:(%d,%d)", startPoint[0],startPoint[1]));
System.out.println(String.format("结束点:(%d,%d)", endPoint[0],endPoint[1]));
//涂黑,开始点与终止点(值设为1)
setValue(startPoint[0], startPoint[1]);
setValue(endPoint[0], endPoint[1]);
Line line = new Line(start, end);
//如果是垂直或者水平者直接涂黑
if(line.isHorizontal() || Double.doubleToLongBits(start.getX())==Double.doubleToLongBits(end.getX())) {
if(line.isHorizontal()) {
int i = startPoint[0];
int j0 = Math.min(startPoint[1], endPoint[1]);
int j1 = Math.max(startPoint[1], endPoint[1]);
while(j0!=j1) {
setValue(i, j0);
j0++;
}
}else {
int i = startPoint[1];
int j0 = Math.min(startPoint[0], endPoint[0]);
int j1 = Math.max(startPoint[0], endPoint[0]);
while(j0!=j1) {
setValue(j0, i);
j0++;
}
}
return;
}
//行差与列差
int difOfRow = Math.abs(startPoint[0]-endPoint[0]);
int difOfColumn = Math.abs(startPoint[1]-endPoint[1]);
//若行差大于列差,则逐行求出本行中心线与过两个端点的直线的交点
//反之则反之
if(difOfRow>difOfColumn) {
//求每行中心线
int num = Math.min(startPoint[0], endPoint[0]); //用于记录当前行
while(num<=Math.max(startPoint[0], endPoint[0])) {
//因为只需要求出行,所以设列为1即可
Point temPoint=transformVetorPoint(num, 1, 0, 0);
//中心线
double y = temPoint.getY();
//求交点
double x=line.getXByY(y);
Point result = new Point(x, y);
//矢量点转栅格
int[] resultRasterPoint=transformRasterPoint(result, 0, 0);
//"涂黑"栅格
setValue(resultRasterPoint[0], resultRasterPoint[1]);
num++;
}
}else {
//求每列中心线
int num = Math.min(startPoint[1], endPoint[1]);
while(num<Math.max(startPoint[1], endPoint[1])) {
Point temPoint = transformVetorPoint(1, num, 0, 0);
double x = temPoint.getX();
double y = line.getYByX(x);
Point result = new Point(x, y);
int[] resultRasterPoint = transformRasterPoint(result, 0, 0);
setValue(resultRasterPoint[0], resultRasterPoint[1]);
num++;
}
}
}
测试:
输入:
p(3,9),q(15,30)组成的线段,在30x30的栅格上显示
输出
这个渲染的代码就不放出来了,有兴趣可以到我的github上看。
②全路径栅格化
算法思想
说白了就是固定行,求列,固定列,求行的过程。
但值得注意的是,书上并没有给出计算行号的过程:
实际上也不难推出,因为求起止行列的过程就是已知tan角求不同边的过程
固定行,求起止列就是求上图中a的过程 a = tana/b
固定列,求起止行就是求b的过程, b=tana * b
因此可以推出
起始行号:
终止行号:
m是栅格的边长,j是当前处理的列,y0是栅格坐标原点在直角坐标系中的纵坐标值
算法实现:
public void RasterLine(Point start,Point end,int type,double xOffset,double yOffset) {
//开始点与结束点的起始坐标
int[] startPoint = transformRasterPoint(start, 0, 0);
int[] endPoint = transformRasterPoint(end, 0, 0);
//
System.out.println(String.format("开始点:(%d,%d)", startPoint[0],startPoint[1]));
System.out.println(String.format("结束点:(%d,%d)", endPoint[0],endPoint[1]));
//涂黑,开始点与终止点(值设为1)
setValue(startPoint[0], startPoint[1]);
setValue(endPoint[0], endPoint[1]);
Line line = new Line(start, end);
//如果是垂直或者水平者直接涂黑
if(line.isHorizontal() || Double.doubleToLongBits(start.getX())==Double.doubleToLongBits(end.getX())) {
if(line.isHorizontal()) {
int i = startPoint[0];
int j0 = Math.min(startPoint[1], endPoint[1]);
int j1 = Math.max(startPoint[1], endPoint[1]);
while(j0!=j1) {
setValue(i, j0);
j0++;
}
}else {
int i = startPoint[1];
int j0 = Math.min(startPoint[0], endPoint[0]);
int j1 = Math.max(startPoint[0], endPoint[0]);
while(j0!=j1) {
setValue(j0, i);
j0++;
}
}
return;
}
//计算栅格原点在直角坐标系中的坐标
double x0=1+xOffset+this.xOffset;
double y0=ROW+yOffset+this.yOffset;
//△x>=△y,则计算列号,反之则计算行号
double difX = Math.abs(start.getX()-end.getX());
double difY = Math.abs(start.getY()-end.getY());
double k = line.getSlope();
if(difX>=difY) {
//开始点与结束点的起始坐标
if(Double.doubleToLongBits(end.getY())<Double.doubleToLongBits(start.getY())) {
startPoint = transformRasterPoint(start, 0, 0);
endPoint = transformRasterPoint(end, 0, 0);
line = new Line(start,end);
}else {
startPoint = transformRasterPoint(end, 0, 0);
endPoint = transformRasterPoint(start, 0, 0);
line = new Line(end,start);
}
k = line.getSlope();
//System.out.println(k);
//用于记录当前行
int i = startPoint[0];
//终止行
int j = endPoint[0];
//计算起始列号
int j0 = (int)Math.floor(((((y0-(i-1)*size-line.getStart().getY())/k)+line.getStart().getX()-x0)/size))+1;
//计算终止列号
int j1 = (int)Math.floor((((y0-i*size-line.getStart().getY())/k)+line.getStart().getX()-x0)/size)+1;
//System.out.println(String.format("s%d,%d", j0,j1));
while(i!=j) {
//i行从j0-j1涂黑
int ja,jb;
ja=j0;
jb=j1;
System.out.println(String.format("%d,%d", j0,j1));
while(ja!=jb) {
if(ja<jb) {
//System.out.println(String.format("i=%d ja=%d", i,ja));
setValue(i, ja);
ja++;
}
else {
//System.out.println(String.format("i=%d jb=%d", i,jb));
setValue(i, jb);
jb++;
}
}
i++;
//本行终止列号等于下一行的起始列号
j0=j1;
//计算下一行终止列号
j1 = (int)Math.floor((((y0-i*size-line.getStart().getY())/k)+line.getStart().getX()-x0)/size)+1;
//System.out.println(String.format("%d,%d", j0,j1));
}
}else {
//System.out.println("第二种");
//开始点与结束点的起始坐标
if(Double.doubleToLongBits(end.getX())>Double.doubleToLongBits(start.getX())) {
startPoint = transformRasterPoint(start, 0, 0);
endPoint = transformRasterPoint(end, 0, 0);
line = new Line(start,end);
}else {
startPoint = transformRasterPoint(end, 0, 0);
endPoint = transformRasterPoint(start, 0, 0);
line = new Line(end,start);
}
k = line.getSlope();
//System.out.println(k);
//记录当前列
int js = startPoint[1];
//记录终止列
int je = endPoint[1];
//计算起始行号
int is = (int)Math.floor(((line.getStart().getX()-x0-(js-1)*size)*k+y0-line.getStart().getY())/size)+1;
//计算终止行号
int ie = (int)Math.floor(((line.getStart().getX()-x0-js*size)*k+y0-line.getStart().getY())/size)+1;
//System.out.println(String.format("first %d,%d", is,ie));
while(js!=je) {
int ia,ib;
//从is-ie行涂黑
ia=is;
ib=ie;
while(ia!=ib) {
if(ia<ib) {
//System.out.println(ia);
setValue(ia, js);
ia++;
}
else {
//System.out.println(ib);
setValue(ib, js);
ib++;
}
}
js++;
is=ie;
ie = (int)Math.floor(((line.getStart().getX()-x0-js*size)*k+y0-line.getStart().getY())/size)+1;
//System.out.println(String.format("%d,%d", is,ie));
}
}
测试结果:
输入:
p(3,9),q(15,30)组成的线段,在30x30的栅格上显示
输出:
同时,由于输入的线段是相同的,也可以比对一下八方向栅格化和全路径栅格化的栅格化效果。
八方向的特点是每行或每列只有一个栅格被涂黑,所以看起来比较细长。而全路径栅格化,由于计算过程是把所有经过的栅格都”涂黑“了,因此当要以任何方向探测栅格影响的存在或者想要知道矢量可能出现在哪些栅格所覆盖的范围时,就可以用全路径栅格化。