GIS演算法基礎(三)計算幾何基礎(下)
阿新 • • 發佈:2018-12-08
判斷線段在多邊形內的演算法:
演算法思路:
如果線段與多邊形內交,則線段一定在多邊形外;如果線段和多邊形的每一條邊都不內交,如果有交點,則線段和多邊形的交點一定是線段的端點或者多邊形的頂點,然後只需要判斷交點是否線上段上就可以了
演算法步驟:
- 判斷線段的兩個端點是否在多邊形內部
- 開始遍歷多邊形的每條邊
- 判斷線段的端點是否在多邊形上
- 判斷多邊形的端點是否線上段上
- 如果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函式判斷點是否線上段上,思路是判斷點與線段一端點的叉積與點是否線上所圍成的矩形中
這些方法的實現我在計算幾何基礎(上),計算幾何基礎(中)中已經實現過了
https://blog.csdn.net/weixin_41154636/article/details/82968255
https://blog.csdn.net/weixin_41154636/article/details/82986381
下面附上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繪製結果:
計算幾何還有一些別的演算法,不過畢竟簡單也挺好實現的,我就沒有寫出了