地图围栏算法——射线法判断点是否在指定的围栏区域内

//问题:在地图上,我们可能会绘制一个封闭的曲线,然后判断一个坐标点是在曲线内部还是在曲线外部。

问题简化一下,其实这就是判断在一个坐标轴上,一个坐标点是否在一段封闭曲线内,最简单的一种状态如下图。

其实从我们的角度来看,我们可以很轻松地指出,右上角的的点是否位于这个三角形内,但是如果没有这张图进行判断,那么难度就变得高许多了,最重要的是,我们的形状可能是不规则的多边形,形状如下图:

对于这类没有规则的多边形,我们没有简单的办法判断这个点是否在多边形内。(其实你可以试着想一下,会发现可以想到许多貌似可以解决的方法,但是一旦到了实际应用总会出问题)

实际上,这个问题的解决方案要用到一个原理,那就是,从一个点引出一条平行于x轴或y轴的射线,判断射线和多边形所有线段的交点(交点个数为1或者0),右边的链接对该方法进行了详细的叙述判断点在多边形内部还是外部

最后的结论可以总结如下:

(1)多边形与射线的交点个数为偶数,那么该点在曲线外部;

(2)多边形与射线的交点个数为奇数,那么该点在曲线内部;

(3)特殊情况,该点在曲线上;

实际非常好理解,考虑一个正方形,如果我们的点在曲线内部,那么射线就只会有一个出点,如果点在曲线外部,射线一定会有入点和出点。

对于地图来说,我们就该用代码来判断了,代码基本是copy过来的,但是因为关了浏览页,所以忘了代码原作者的名称,这里说声抱歉,如果您看到了请联系我。

	public String isInRegion(Point point, List<Point> region) {
		boolean flag = false;   //偶数次在外面,奇数次在里面
		double px = point.x, py = point.y;
		
		System.out.println(point.toString());
		
		for(int i = 0; i < region.size(); i++) {
			int j = i+1;
			//实际上就是没一条边都得算一下,首尾也有一条边得算在内
			if(i == region.size() - 1) {
				j = 0;
			}
			//取相邻的两个点
			double sx = region.get(i).x, sy = region.get(i).y, tx = region.get(j).x, ty = region.get(j).y;
			
			System.out.println("region i = " + region.get(i).toString());
			System.out.println("region j = " + region.get(j).toString());
			
			//判断一下点是不是和多边形的顶点重合
			if((sx == px && sy == py) || (tx == px && ty == py)) {
				return "on";
			}
			//不在顶点的话,判断一下是不是就在当前线上
			//做一条包含P点的,平行于y轴的直线
			//首先保证线段端点在射线两侧,和P点不是平行
			if((sy < py && ty >= py) || (sy >= py && ty < py)) {

				double diff = (px - sx) * (ty - sy) - (py - sy) * (tx - sx);
				//判断一下px点是不是在边界上
				if(diff == 0) {
					return "on";
				}
				
				else{
					//求一下交点
					double x = sx + (py - sy) * (tx - sx) / (ty - sy);
					//射线只往右边走
                                        //判断一下交点是否在当前点的右侧
					if(px > x) {
						flag = !flag;
					}
				}
			}
		}
		
		return flag?"in":"out";
	}

最后的返回值有3个,on = 在曲线上, in = 在曲线内, out = 在曲线外

这里我对算法做了一点改变,在判断当前点在边界上时,考虑到精度,如果用除法的话可能会导致计算得到坐标点和实际有一定偏差,因此,我增加了如下代码:

double diff = (px - sx) * (ty - sy) - (py - sy) * (tx - sx);

如果diff = 0,那么就说明,当前坐标点在曲线上,那么我们就可以直接返回一个on了。

  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值