项目github地址:bitcarmanlee easy-algorithm-interview-and-practice
欢迎大家star,留言,一起学习进步
在做LBS,POI相关项目时,经常需要判断一个点是否在某个区域中的问题。在实际场景中,这个区域肯定是没有任何规律的不规则形状。针对这种场景,搜索了几种对应的解法。重点是,最后给大家奉上能工作的源码!有需要的同学们千万不能错过。
1.射线法,PNPoly算法
本算法是由W. Randolph Franklin提出的。算法的思路如下:
从待测点出发作一条射线,可沿y轴方向也可没x轴方向,然后判断这条射线与不规则区域的交点数量。如果点的两边交点的个数都是奇数个则该点在多边形内,否则在多边形外。
这个算法对于任意不规则图形都适用。
关于本算法更详细的介绍,请参考参考文献1。
2.多边形面积算法
非凹多边形,凹多边形需要切割为凸多边形。
第四点分别与三角形的两个点组成的面积分别设为S1,S2,S3,只要S1+S2+S3>原来的三角形面积就不在三角形范围中.可以使用海伦公式 。
3.可用的解决方案
重点来了…
在实际的项目过程中,不管多精巧的算法,实现功能完成项目是第一位的。算法再好,还得自己去实现去测试,尤其是各种边界条件测试,绝对是要吐血。所以,不管什么算法,啥也不说了,先上代码。
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
/**
* Created by wanglei on 17-2-14.
*/
public class MapTools {
public static List genPointList() {
List list = new ArrayList();
Point2D.Double p1 = new Point2D.Double(0.0,0.0);
Point2D.Double p2 = new Point2D.Double(0.0,0.5);
Point2D.Double p3 = new Point2D.Double(0.0,1.0);
Point2D.Double p4 = new Point2D.Double(1.0,0.5);
Point2D.Double p5 = new Point2D.Double(1.0,1.0);
Point2D.Double p6 = new Point2D.Double(1.0,0.5);
Point2D.Double p7 = new Point2D.Double(1.0,0.0);
Point2D.Double p8 = new Point2D.Double(0.5,0.0);
Point2D.Double[] points = {p1,p2,p3,p4,p5,p6,p7,p8};
for (int i = 0; i < points.length; i++) {
list.add(points[i]);
}
return list;
}
public static boolean checkWithPath(Point2D.Double point, List<Point2D.Double> polygon) {
GeneralPath p = new GeneralPath();
Point2D.Double first = polygon.get(0);
p.moveTo(first.x,first.y);
for(Point2D.Double d: polygon) {
p.lineTo(d.x,d.y);
}
p.lineTo(first.x,first.y);
p.closePath();
return p.contains(point);
}
public static void main(String[] args) {
List list = genPointList();
Point2D.Double p1 = new Point2D.Double(1.5,1.5);
Point2D.Double p2 = new Point2D.Double(0.2,0.3);
Point2D.Double p3 = new Point2D.Double(1.5,0.1);
Point2D.Double p4 = new Point2D.Double(0.8,0.0);
Point2D.Double p5 = new Point2D.Double(0.08,0.01);
List<Point2D.Double> testList = new ArrayList();
testList.add(p1);
testList.add(p2);
testList.add(p3);
testList.add(p4);
testList.add(p5);
for(Point2D.Double each:testList) {
boolean flag = checkWithPath(each,list);
System.out.println(each.toString() + " is in polygon: " + flag);
}
}
}
上面的代码主要是用到了java.awt.geom
包。java.awt.geom
是jdk自带的类包,不用任何额外的安装。GeneralPath 类表示根据直线、二次曲线和三次 (Bézier) 曲线构造的几何路径。它可以包含多个子路径。GeneralPath继承自java.awt.geom.Path2D。那么这个Path2D是干嘛的呢?看看jdk里面的一段注解:
/**
* The {@code Path2D} class provides a simple, yet flexible
* shape which represents an arbitrary geometric path.
* It can fully represent any path which can be iterated by the
* {@link PathIterator} interface including all of its segment
* types and winding rules and it implements all of the
* basic hit testing methods of the {@link Shape} interface.
* <p>
第一句话就告诉了我们Path2D是干嘛的:Path2D类提供了一个简单,灵活的形状类,用来表示任意几何路径。
测试代码中,生成了一个边长为1的正方形矩形区域,其中一个顶点为(0,0)。最后的测试结果为:
Point2D.Double[1.5, 1.5] is in polygon: false
Point2D.Double[0.2, 0.3] is in polygon: true
Point2D.Double[1.5, 0.1] is in polygon: false
Point2D.Double[0.8, 0.0] is in polygon: true
Point2D.Double[0.08, 0.01] is in polygon: true
测试结果准确无误,完美地满足了我们的需求!
时间关系,没有来得及具体研究jdk里源码的实现细节,后续可以进一步研究Path2D的实现算法。
参考文档:
1.http://blog.csdn.net/hjh2005/article/details/9246967
2.http://www.apihome.cn/api/java/GeneralPath.html
3.jdk源码