GIS算法基础(三)计算几何基础(下)
代码已经po上远程仓库:
https://github.com/XiaoZhong233/GIS_ALG/blob/master/src/scau/gz/zhw/CalculateBasic.java
目录
判断线段在多边形内的算法:
算法思路:
如果线段与多边形内交,则线段一定在多边形外;如果线段和多边形的每一条边都不内交,如果有交点,则线段和多边形的交点一定是线段的端点或者多边形的顶点,然后只需要判断交点是否在线段上就可以了
算法步骤:
- 判断线段的两个端点是否在多边形内部
- 开始遍历多边形的每条边
- 判断线段的端点是否在多边形上
- 判断多边形的端点是否在线段上
- 如果2、3条件都不满足,判断线段是否与多边形的边是否相交,如果相交则说明内交
- 如果5得出不相交,则判断各交点的中点是否在多边形内部
算法实现(JAVA):
public static boolean isSegmentAtPolygon(Line line,Polygon polygon) {
//判断线段的两端点是否在多边形内部
if(!isPointAtPolygon(polygon, line.getStart(), 0) || !isPointAtPolygon(polygon, line.getEnd(), 0)) {
System.out.println("端点不在多边形内部");
return false;
}
//交点集
List<Point> pointSet = new ArrayList<>();
//判断多边形的各条边与线段不内交
for(Line bian : polygon.getLines()) {
//判断线段的两端点是否在多边形上
if(isPointAtSegment(bian, line.getStart()) || isPointAtSegment(bian, line.getEnd())) {
if(isPointAtSegment(bian, line.getStart())) {
pointSet.add(line.getStart());
}
if(isPointAtSegment(bian, line.getEnd())) {
pointSet.add(line.getEnd());
}
//判断多边形的边的某个端点是否在线段上
}else if (isPointAtSegment(line, bian.getStart())||isPointAtSegment(line, bian.getEnd())) {
if(isPointAtSegment(line, bian.getStart())) {
pointSet.add(bian.getStart());
}
if(isPointAtSegment(line, bian.getEnd())) {
pointSet.add(bian.getEnd());
}
//判断是否相交,如果程序进行此次判断,则说明上两个判断都不满足,则说明线段与边内交
}else if (isTwoSegmentIntersect(bian, line)) {
System.out.println("线段与多边形内交");
return false;
}
}
//对交点集进行按照x,y排序,为了更方便的比较各交点的中点是否在多边形内
Comparator<Point> XYcomparator = new Comparator<Point>() {
//优先对X进行排序,x相等则对y进行排序
@Override
public int compare(Point arg0, Point arg1) {
// TODO Auto-generated method stub
if(arg0.getX()-arg1.getX()==0) {
return (int) (arg0.getY()-arg1.getY());
}else {
return (int) (arg0.getX()-arg1.getX());
}
}
};
Collections.sort(pointSet, XYcomparator);
//一次判断每两个相邻点的中点是否在多边形内
for(int i=0;i<pointSet.size();i++) {
//如果是最后一个点,循环结束
if(i==pointSet.size()-1) {
break;
}
Point a = pointSet.get(i);
Point b = pointSet.get(i+1);
Point center = new Point((a.getX()+b.getX())/2.0,(b.getY()+a.getY())/2.0);
if(!isPointAtPolygon(polygon, center, 0)) {
System.out.println("中点不在多边形内部");
return false;
}
}
return true;
}
说明:isTwoSegmentIntersect函数判断两线段是否相交,思路是对两线段进行快速排斥试验与跨越试验。
isPointAtPolygon函数是上文已经实现了的判断点是否在多边形内,这里使用的是射线法法进行判断
isPointAtSegment函数判断点是否在线段上,思路是判断点与线段一端点的叉积与点是否在线所围成的矩形中
下面附上isTwoSegmentIntersect函数实现与isPointAtSegment函数的实现
/**
* 判断两线段是否相交
* @param a
* @param b
* @param c
* @param d
* @return
*/
public static boolean isTwoSegmentIntersect(Point a,Point b,Point c,Point d) {
boolean flag1 = false;
boolean flag2 = false;
//快速排斥试验
if(Math.min(a.getY(), b.getY())<=Math.max(c.getY(), d.getY()) && Math.min(c.getX(), d.getX())<=Math.max(a.getX(), b.getX())
&& Math.min(c.getY(), d.getY())<= Math.max(a.getY(), b.getY()) && Math.min(a.getX(), b.getX())<= Math.max(c.getX(), d.getX())) {
flag1 = true;
}
Vector2D ab = getVector(a, b);
Vector2D ac = getVector(a, c);
Vector2D bd = getVector(b, d);
//跨立试验
if(ac.crossProduct(ab) * bd.crossProduct(ab) <=0) {
flag2 = true;
}
return flag1&&flag2;
}
/**
* 判断点是否在线段上
* @param p1 线段端点
* @param p2 线段端点
* @param q 需要判断的点
* @return
*/
public static boolean isPointAtSegment(Point p1,Point p2,Point q) {
//判断是否在线段围成的区域内
if(q.getX()<=Math.max(p1.getX(), p2.getX()) && q.getX()>=Math.min(p1.getX(), p2.getX())
&& q.getY()<= Math.max(p1.getY(), p2.getY()) && q.getY()>=Math.min(p1.getY(), p2.getY()))
{
Vector2D qp1 = getVector(q, p1);
Vector2D p2p1 = getVector(p2, p1);
return qp1.crossProduct(p2p1)==0?true:false;
}else {
return false;
}
}
测试结果
测试数据1、
多边形数据
Point aPoint = new Point(0, 0);
Point bPoint = new Point(20, -20);
Point cPoint = new Point(50, 30);
Point dPoint = new Point(30, 30);
Point ePoint = new Point(60, -20);
Point fPoint = new Point(80, 0);
Point gPoint = new Point(40, 70);
待测线段数据:
Point p1 = new Point(0, 0);
Point p2 = new Point(100, 100);
Line line1 = new Line(p1,p2);
GUI绘制结果:
计算几何还有一些别的算法,不过毕竟简单也挺好实现的,我就没有写出了