1. 程式人生 > >GIS演算法基礎(三)計算幾何基礎(下)

GIS演算法基礎(三)計算幾何基礎(下)

 

判斷線段在多邊形內的演算法:

 

演算法思路:

如果線段與多邊形內交,則線段一定在多邊形外;如果線段和多邊形的每一條邊都不內交,如果有交點,則線段和多邊形的交點一定是線段的端點或者多邊形的頂點,然後只需要判斷交點是否線上段上就可以了

演算法步驟:

  1. 判斷線段的兩個端點是否在多邊形內部
  2. 開始遍歷多邊形的每條邊
  3. 判斷線段的端點是否在多邊形上
  4. 判斷多邊形的端點是否線上段上
  5. 如果2、3條件都不滿足,判斷線段是否與多邊形的邊是否相交,如果相交則說明內交
  6. 如果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繪製結果:

影象走丟了

 

計算幾何還有一些別的演算法,不過畢竟簡單也挺好實現的,我就沒有寫出了