文档章节

使用Path2D和凸包算法实现地理围栏服务

王小念博客
 王小念博客
发布于 05/23 11:20
字数 1478
阅读 1104
收藏 48

前言 

地理围栏(Geo-fencing)是LBS的一种新应用,就是用一个虚拟的栅栏围出一个虚拟地理边界。在物流配送行业应用比较广,划分每个配送网点或者商家配送的范围,提高配送员的配送效率和服务的范围。

 

1.使用Path2D创建一个多边形

Path2D类是java.awt.geom包提供的工具包,可表示任意几何路径的简单而灵活的形状。它可以完全表示PathIterator接口可以迭代的任何路径, 包括其所有段类型和绕组规则,并且它实现了Shape接口的所有基本命中测试方法。

使用Path2D.Float带有可表示且能使用浮点精度的数据的时候。使用Path2D.Double 对于需要双精度的准确性或范围的数据。

先通过高德地图在线编辑一个多边形覆盖图,然后获取到有序的坐标

https://lbs.amap.com/api/javascript-api/example/overlayers/polygon-draw-and-edit

代码示例如下:

//传参 有序的坐标范围
public static Path2D.Double create(List<PointDouble> polygon) {
    //创建path2D对象
    Path2D.Double generalPath = new Path2D.Double();
    //获取有序坐标范围的第一个坐标
    PointDouble first = polygon.get(0);
    //通过移动到指定坐标(以双精度指定),将一个点添加到路径中
    generalPath.moveTo(first.getX(), first.getY());
    //删除有序坐标范围第一个
    polygon.remove(0);
    //遍历有序坐标范围后面的坐标
    for (PointDouble d : polygon) {
        // 通过绘制一条从当前坐标到新指定坐标(以双精度指定)的直线,将一个点添加到路径中。
        generalPath.lineTo(d.getX(), d.getY());
    }
    // 将几何多边形封闭
    generalPath.lineTo(first.getX(), first.getY());
    //关闭路径
    generalPath.closePath();
    return generalPath;
}


以上用到了的方法详解

moveTo(double x, double y)

通过移动到以double精度指定的指定坐标,向路径添加一个点。

lineTo(double x, double y)

通过从当前坐标绘制直线到以double精度指定的新指定坐标,将路径添加到路径。

closePath()

通过将直线绘制回最后一个坐标来关闭当前子路径moveTo

 

 

2.判断某个坐标是否在Path2D

代码示例

PointDouble point = new PointDouble(116.403322,39.920255);
//生成好的多边形是不是包含某个坐标
path2d.contains(point)

以上用到了的方法详解

contains(double x, double y)

测试指定坐标是否在边界内Shape

 

3.判断某个矩形区域是否在Path2D

contains(PathIterator pi, double x, double y, double w, double h)

测试指定的矩形区域是否完全位于指定的闭合边界内PathIterator

 

4.使用凸包算法把指定Path2D转换成一块大的覆盖面

凸包(Convex Hull)是一个计算几何(图形学)中的概念。在一个实数向量空间V中,对于给定集合X,所有包含X的凸集的交集S被称为X的凸包。X的凸包可以用X内所有点(X1,...Xn)的凸组合来构造.在二维欧几里得空间中,凸包可想象为一条刚好包著所有点的橡皮圈。用不严谨的话来讲,给定二维平面上的点集,凸包就是将最外层的点连接起来构成的凸多边形,它能包含点集中所有的点。

代码示例:

/**
 * 凸包算法
 *
 * @author wangnian
 */
public class ConvexUtil {
    /**
     * 凸包算法,返回点集合中凸多边形的点集合
     *
     * @param pointList
     * @return
     */
    public static List<PointDouble> getConvexPoint(List<PointDouble> pointList) {
        //用于计算最终返回结果中是凸包中点的个数
        List<PointDouble> resultList = new ArrayList<>();
        for (int i = 0; i < pointList.size(); i++) {
            for (int j = 0; j < pointList.size(); j++) {
                if (j == i) {
                    //除去选中作为确定直线的第一个点
                    continue;
                }
                //存放点到直线距离所使用判断公式的结果
                double[] judge = new double[pointList.size()];
                for (int k = 0; k < pointList.size(); k++) {
                    double a = pointList.get(j).getX() - pointList.get(i).getY();
                    double b = pointList.get(i).getX() - pointList.get(j).getX();
                    double c = (pointList.get(i).getX()) * (pointList.get(j).getY()) - (pointList.get(i).getY()) * (pointList.get(j).getX());
                    //根据公式计算具体判断结果
                    judge[k] = a * (pointList.get(k).getX()) + b * (pointList.get(k).getY()) - c;
                }
                // 如果点均在直线的一边,则相应的A[i]是凸包中的点
                if (JudgeArray(judge)) {
                    resultList.add(pointList.get(i));
                    break;
                }
            }
        }
        return resultList;
    }


    /**
     * 判断数组中元素是否全部大于等于0或者小于等于0,如果是则返回true,否则返回false
     *
     * @param Array
     * @return
     */
    private static boolean JudgeArray(double[] Array) {
        boolean judge = false;
        int len1 = 0, len2 = 0;
        for (int i = 0; i < Array.length; i++) {
            if (Array[i] >= 0) {
                len1++;
            }
        }
        for (int j = 0; j < Array.length; j++) {
            if (Array[j] <= 0) {
                len2++;
            }
        }
        if (len1 == Array.length || len2 == Array.length) {
            judge = true;
        }
        return judge;
    }

}


 

 

5. 根据当前地图窗口查询所有相交Path2D

  根据当前地图显示范围获取到northeast东北角和southwest西南角的坐标位置,查询相交的所有Path2D

高德地图示例地址:

https://lbs.amap.com/api/javascript-api/example/map/map-bounds/?sug_index=2

 

代码示例

//获取西南的纬度  也就是X
double lng = southwest.getLng();
//获取西南的经度  也就是Y
double lat = southwest.getLat();
//东北角的X减去西南角的X ,得到宽
double w = northeast.getLng() - southwest.getLng();
//东北角的Y减去西南角的Y ,得到高
double h = northeast.getLat() - southwest.getLat();


//判断是否相交
Path2D.intersects(lng, lat, w, h)

以上用到了的方法详解

intersects(double x, double y, double w, double h)

测试内部是否与Shape指定矩形区域的内部相交。

 

提示: 以上只是一些关键的局部代码,在实际应用中,需要将所有的范围对象按照凸包算法或者其他纬度的行政区域进行分类并缓存,方便快速遍历查询。

 

欢迎大家关注凯京技术团队  https://my.oschina.net/keking  很多专业的干活技术分享。