一、电子围栏的实现(一):矩形、圆与多边形的处理
矩形区域处理
针对矩形区域处理,复杂度较低。设矩形ABCD的四个顶点分别为A、B、C、D,坐标分别为A(x1,y1)、B(x2,y2)、C(x3,y3)、D(x4,y4),设点E(x5,y5)是需要判断的点。那么判断点E是否在矩形区域的方法是:x5>x3且x5<x2,同时满足y5>y3且y5<y2,当然我们也可以把A、D两个点作为参照
/**
* 判断是否在矩形范围内
* @param lat 测试点纬度
* @param lng 测试点经度
* @param minLat 纬度范围限制1
* @param maxLat 纬度范围限制2
* @param minLng 经度范围限制1
* @param maxLng 经度范围限制2
* @return
*/
public static boolean isInRectangleArea(double lat, double lng, double minLat, double maxLat, double minLng, double maxLng){
if(isInRange(lat, minLat, maxLat)){//如果在维度范围内
if(minLng*maxLng>0){
if(isInRange(lng, minLng, maxLng)){
return true;
}else{
return false;
}
}else{
if(Math.abs(minLng)+Math.abs(maxLng)<180){
if(isInRange(lng, minLng, maxLng)){
return true;
}else{
return false;
}
}else{
double left = Math.max(minLng, maxLng);
double right = Math.min(minLng, maxLng);
if(isInRange(lng, left, 180) || isInRange(lng, right, -180)){
return true;
}else{
return false;
}
}
}
}else{
return false;
}
}
圆形区域处理
对于圆形区域,我们假设地球是正圆的,同时假设圆心为B(Xb,Yb),A(Xa,Ya)为判断的点,Xb,Yb为B点经纬度,Xa,Ya为A点经纬度,A,B是地球表面的两个点,如图2中的a所示,已知A、B两点的经度后,我们可以计算CD的地面长度为:
CD不但可以平移至B点,也可以 平移至A点,当然算出的结果不一样,这里将式(6)中的Yb取值为(Ya+Yb)/2,即去平均值,效果较佳。
/**
* 地球半径
*/
private static double EARTH_RADIUS = 6378138.0;
private static double rad(double d){
return d * Math.PI / 180.0;
}
/**
* 计算是否在圆上(单位/千米)
* @param radius 半径
* @param lat1 维度
* @param lng1 经度
* @return
*/
public static boolean isInCircle(double radius, double lat1, double lng1, double lat2, double lng2){
double radLat1 = rad(lat1);
double radLat2 = rad(lat2);
double a = radLat1 - radLat2;
double b = rad(lng1) - rad(lng2);
double s = 2 * Math.asin(Math.sqrt(Math.pow(
Math.sin(a/2), 2) +
Math.cos(radLat1)*Math.cos(radLat2)*Math.pow(Math.sin(b/2), 2)));
s = s * EARTH_RADIUS;
s = Math.round(s * 10000) / 10000;
if(s > radius){//不在圆上
return false;
}else{
return true;
}
}
多边形区域处理
如图3,判断点p在多边形内的方法是:用点p的水平坐标去和多边形相交,得到若干个交点,如果点p两侧的交点数量都是奇数个时,说明p点在多边形内,即铅垂线内点法。使用这种方法,适合任意多边形,包括凸多边形和凹多边形,同时适用于有孔的多边形。下面给出这种算法的Java实现
/**
* 判断点是否在多边形内
* @param polygon 多边形
* @param point 检测点
* @return 点在多边形内返回true,否则返回false
*/
public static boolean IsPtInPoly(List<Point2D.Double> polygon, Point2D.Double point){
int N = polygon.size();
boolean boundOrVertex = true;//如果点位于多边形的顶点或边上,也算做点在多边形内,直接返回true
int intersectCount = 0;//cross points count of x--交叉点计数X
double precision = 2e-10;//浮点类型计算时候与0比较时候的容差
Point2D.Double p1, p2;//neighbour bound vertices--临近绑定顶点
Point2D.Double p = point;//当前点
p1 = polygon.get(0);//left vertex--左顶点
for (int i = 1; i <= N; ++i) {//check all rays--检查所有射线
if(p.equals(p1))
return boundOrVertex;//p is an vertex--p是一个顶点
p2 = polygon.get(i % N);//right vertex--右顶点
if(p.x < Math.min(p1.x, p2.x) || p.x > Math.max(p1.x, p2.x)){//ray is outside of our interests--射线不在我们的兴趣范围之内
p1 = p2;
continue;//next ray left point--下一条射线的左边点
}
if(p.x > Math.min(p1.x, p2.x) && p.x < Math.max(p1.x, p2.x)){//ray is crossing over by the algorithm(common part of)--射线被算法穿越(常见的一部分)
if(p.y <= Math.max(p1.y, p2.y)){//x is before of ray--x在射线之前
if(p1.x == p2.x && p.y >= Math.min(p1.y, p2.y)){//overlies on a horizontal ray--在一条水平射线上
return boundOrVertex;
}
if(p1.y == p2.y){//ray is vertical--射线是垂直的
if(p1.y == p.y){//overlies on a vertical ray--覆盖在垂直光线上
return boundOrVertex;
}else{//before ray--射线之前
++intersectCount;
}
}else{//cross point on the left side--左边的交叉点
double xinters = (p.x - p1.x) * (p2.y - p1.y) / (p2.x - p1.x) + p1.y;//cross point of y--y的交叉点
if(Math.abs(p.y - xinters) < precision){//overlies on a ray--覆盖在射线
return boundOrVertex;
}
if(p.y < xinters){//before ray--射线之前
++intersectCount;
}
}
}
}else{//special case when ray is crossing through the vertex--特殊情况下,当射线穿过顶点
if(p.x == p2.x && p.y <= p2.y){//p crossing over p2--p交叉p2
Point2D.Double p3 = polygon.get((i+1) % N);//next vertex--下一个顶点
if(p.x >= Math.min(p1.x, p3.x) && p.x <=Math.max(p1.x, p3.x)){//p.x lies between p1.x & p3.x--p.x在p1.x和p3.x之间
++intersectCount;
}else{
intersectCount += 2;
}
}
}
p1 = p2;//next ray left point--下一条射线的左边点
}
if(intersectCount % 2 == 0){//偶数在多边形外
return false;
}else{//奇数在多边形内
return true;
}
}
补充:铅垂线内点法
铅垂线内点法的基本思想是从待判别点向外引垂线,计算其与多边形交点的个数,若交点个数为奇数,则点在多边形内;若交点个数为偶数,则该点在多边形外,如下图上面两图。
这种方法不但适合于任意凸、凹多边形,而且适合于甚至于有孔的多边形,当然, 如果铅垂线恰好教育多边形的顶点或边上时,这时只需计算一侧的交点个数即可,如上图下面两图。
二、电子围栏的实现(二):几何路径法
注意:此方法复杂的多边形判断失败
在Java语言的开发环境中,提供了一种便捷的方法。类java.awt…geom.GeneralPath提供了根据直线、二次曲线和三次曲线构造的几何路径的方法,同时GeneralPath类继承类Path2D,通过Path2D类提供的方法contains(double x,double y)来判断某个点是否在曲线或直线构成的边界内。
package com.test;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
public class ElectronicFence {
/**
* 测试一个点是否在多边形内
* @param args
*/
public static void main(String[] args) {
//点在多边形内
Point2D.Double point = new Point2D.Double(116.395, 39.910);
//点在多边形外
// Point2D.Double point = new Point2D.Double(116.404072, 39.916605);
List<Point2D.Double> polygon = new ArrayList<Point2D.Double>();
polygon.add(new Point2D.Double(116.395, 39.910));
polygon.add(new Point2D.Double(116.394, 39.914));
polygon.add(new Point2D.Double(116.403, 39.920));
polygon.add(new Point2D.Double(116.402, 39.914));
polygon.add(new Point2D.Double(116.410, 39.913));
if(polygon.contains(point)){
System.out.println("点在多边形内");
}else{
System.out.println("点在多边形外");
}
}
/**
* 判断点是否在多边形内
* 步骤:
* ①声明一个“画笔”
* ②将“画笔”移动到多边形的第一个顶点
* ③用“画笔”按顺序将多边形的顶点连接起来
* ④用“画笔”将多边形的第一个点连起来,最终形成一个封闭的多边形
* ⑤用contains()方法判断点是否在多边形区域内
* @param polygon 多边形
* @param point 检测点
* @return 点在多边形内返回true,否则返回false
*/
public static boolean contains(List<Point2D.Double> polygon, Point2D.Double point){
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);
}
}
其中p为构造的GeneralPath对象,polygon包含了多边形顶点的List对象,每个顶点被定义为Point2D.Double类型。point则是要判断的点,其类型也为Point2D.Double。p.contains(point)返回true则表示点point包含在多边形polygon内。
作者:Johnson8702
原文:https://blog.csdn.net/johnson8702/article/details/82145220
原文:https://blog.csdn.net/johnson8702/article/details/82150371