判断校验点是否在多边形电子围栏范围内

光线投射法:从目标点出发引一条射线,看这条射线和多边形所有边的交点数目。如果有奇数个交点,则说明在内部,如果有偶数个交点,则说明在外部。

具体实现及备注在算法中

public class LocationUtil {

    public static void main(String[] args) {
        // 被检测的经纬度点
        CurrentPoint currentPoint=new CurrentPoint(116.08992734704537d,30.11111719172894d);
        // 商业区域(百度多边形区域经纬度集合)
        String partitionLocation = "117_31,127_31,120_33";
        System.out.println(isInPolygon(currentPoint, partitionLocation));
    }


    /**
     * 判断当前位置是否在多边形区域内
     * <p>
     * 经度为X轴 纬度为Y轴
     *
     * @param currentPoint      当前点
     * @param partitionLocation 区域顶点 格式:经度_纬度,经度_纬度,经度_纬度
     * @return
     */

    public static boolean isInPolygon(CurrentPoint currentPoint, String partitionLocation) {
        Point2D.Double point = new Point2D.Double(currentPoint.getLng(), currentPoint.getLat());
        List<Point2D.Double> pointList = new ArrayList();
        String[] strList = partitionLocation.split(",");
        for (String str : strList) {
            String[] points = str.split("_");
            Point2D.Double polygonPoint = new Point2D.Double(Double.parseDouble(points[0]), Double.parseDouble(points[1]));
            pointList.add(polygonPoint);
        }

        return isPtInPoly(point, pointList);

    }


    /**
     * 判断点是否在多边形内,如果点位于多边形的顶点或边上,也算做点在多边形内,直接返回true
     * 原理:基于检测点向Y轴正方向做一条衍射线,和多边形的交点为奇数时则在多边形内,反之则不在
     *
     * @param point 检测点
     * @param pts   多边形的顶点
     * @return 点在多边形内返回true, 否则返回false
     */
    private static boolean isPtInPoly(Point2D.Double point, List<Point2D.Double> pts) {
        int N = pts.size();
        boolean boundOrVertex = true; //如果点位于多边形的顶点或边上,也算做点在多边形内,直接返回true
        int intersectCount = 0;//x的交叉点计数
        double precision = 2e-10; //浮点类型计算时候与0.较时候的容差
        Point2D.Double p1, p2;//相邻边界顶点
        Point2D.Double p = point; //当前点
        p1 = pts.get(0);
        for (int i = 1; i <= N; ++i) {
            if (p.equals(p1)) {
                return boundOrVertex;
            }
            p2 = pts.get(i % N);
            if (p.x < Math.min(p1.x, p2.x) || p.x > Math.max(p1.x, p2.x)) {//X轴坐标不在两者之前,则一定无交点
                p1 = p2;
                continue;
            }

            if (p.x > Math.min(p1.x, p2.x) && p.x < Math.max(p1.x, p2.x)) {
                if (p.y <= Math.max(p1.y, p2.y)) {//校验点Y轴坐标大于两者最大值则无交点,小于才有可能存在交点

                    if (p1.x == p2.x && p.y >= Math.min(p1.y, p2.y)) {// 当前逻辑永远不成立,算法当前判断毫无意义
                        return boundOrVertex;
                    }

                    if (p1.y == p2.y) {//p1 p2 平行于X轴
                        if (p1.y == p.y) {//校验点p在这条边上则直接返回成功 否则记录交点+1
                            return boundOrVertex;
                        } else {
                            ++intersectCount;
                        }
                    } else {
                        double xinters = (p.x - p1.x) * (p2.y - p1.y) / (p2.x - p1.x) + p1.y;//计算斜率 p p1 的斜率 大于 p2 p1 的斜率则一定存在交点
                        if (Math.abs(p.y - xinters) < precision) {//p p1 的斜率 等于 p2 p1 的斜率 则p 在 p1 p2 这条边上,则一定在多边形内
                            return boundOrVertex;
                        }
                        if (p.y < xinters) {
                            ++intersectCount;
                        }
                    }
                }
            } else {// p 与 p1 或 p2 中的一个点X轴坐标相等,也就是p向Y轴正方向做一条衍射线,正好与定点相交场景
                if (p.x == p2.x && p.y <= p2.y) {//p在p2点的正下方时,引入p3点
                    Point2D.Double p3 = pts.get((i + 1) % N);
                    if (p.x >= Math.min(p1.x, p3.x) && p.x <= Math.max(p1.x, p3.x)) {//p.x 在 p1.x 和 p3.x 之间则该点的记为1个交点,p目前在多边行内部,反之为2个
                        ++intersectCount;
                    } else {
                        intersectCount += 2;

                    }
                }
            }
            p1 = p2;//以p2为起点继续判定边与校验点的交点
        }

        if (intersectCount % 2 == 0) {//偶数在多边形外
            return false;
        } else { //奇数在多边形内
            return true;
        }

    }

}

补充:

上述算法当多边形存在覆盖时,判断将存在巨大漏洞,如下图所示:

 

此时我这边的解决方案是在保存多边形时校验是否存在覆盖场景,存在则提示用户调整多边形。

判断原理:任何不相邻的两条边没有交点,算法实现如下:

 /**
     * 判断多边形不相邻的线段是否存在交点
     *
     * @param partitionLocation  区域顶点 格式:经度_纬度,经度_纬度,经度_纬度
     * @return
     */
    public static boolean linesIntersect(String partitionLocation) {
        List<Point2D.Double> pointList = new ArrayList();
        String[] strList = partitionLocation.split(",");
        for (String str : strList) {
            if (StringUtils.isNotEmpty(str)) {
                String[] points = str.split("_");
                Point2D.Double polygonPoint = new Point2D.Double(Double.parseDouble(points[0]), Double.parseDouble(points[1]));
                pointList.add(polygonPoint);
            }
        }

        int size = pointList.size();
        for (int i = 0; i < size; i++) {
            Point2D.Double point1 = pointList.get(i % size);
            Point2D.Double point2 = pointList.get((i + 1) % size);
            for (int j = i + 2; j < size; j++) {
                //最后一条边必与第一条边有交集,直接跳过
                if (i == 0 && j == size - 1) {
                    continue;
                }
                //判断是否存在交点,存在则返回true
                Point2D.Double point3 = pointList.get(j % size);
                Point2D.Double point4 = pointList.get((j + 1) % size);
                if (Line2D.linesIntersect(point1.x, point1.y, point2.x, point2.y, point3.x, point3.y, point4.x, point4.y)) {
                    return true;
                }
            }
        }
        return false;
    }

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值