Geohash之范围搜索

作者: 牛老师讲webgis | 来源:发表于2017-12-16 11:43 被阅读91次

    概述

    很多时候,我们都会遇到这样的需求:查找某个点周边多少距离的点。从本质来说,是一个缓冲区分析+空间查找,本文结合Geohash来实现类似的功能。

    效果

    范围搜索结果
    说明:
    1. 红色的点和红色的圈是查找的中心点和距离(5km);
    2. 蓝色的点+粉色的点是通过geohash查找出来的点;
    3. 粉色的点是通过过滤后的点;

    实现

    本文实现是结合sqlite数据库实现的,实现的思路如下:

    1. 数据的初始化

    本示例所用的数据源于网络下载下来的shp数据,并做了解析入库,表结构如下:

    CREATE TABLE "geocode_point"
    (
      id NVARCHAR(50) PRIMARY KEY NOT NULL,
      poiname NVARCHAR(200),
      x NUMERIC,
      y NUMERIC,
      minzoom INTEGER(2),
      maxzoom INTEGER(2)
    , geohash NVARCHAR(20) NULL)
    
    数据展示

    2. 根据geohash查找点

    根据查找的距离范围,先获取geohash的位数,实现方法如下:

        /**
         * 获取距离有效位数
         * @param radius
         * @return
         */
        public int effectnum(double radius){
            int result = 0;
            if (radius <= 0) result = 0;
            else if (radius < 1) result = 10;
            else if (radius < 5) result = 9;
            else if (radius < 20) result = 8;
            else if (radius < 77) result = 7;
            else if (radius < 610) result = 6;
            else if (radius < 2400) result = 5;
            else if (radius < 20000) result = 4;
            else if (radius < 78000) result = 3;
            else if (radius < 630000) result = 2;
            else result = 0;
            return result;
        }
    

    再查找前面的值相同的记录,实现代码如下:

    int precision = geoHash.effectnum(dist);
    String strGeohash = geoHash.encode(lat, lon, 0);
    String filters = "where geohash like '"+strGeohash.substring(0, precision)+"%'";
    List _list = geoDao.getDataByFilter(points.getTableName(), points.getTableFields(), filters, new Object[]{});
    

    3. 计算满足条件的点

    由于是经纬度的数据,所以在计算两点距离的时候进行了坐标转换,将经纬度转换为了Web墨卡托,此举是结合geotools实现的。

    1)坐标转换
        public double[] lonlat2WebMactor(double lon, double lat){
            Point geom =new GeometryFactory().createPoint(new Coordinate(lon, lat));
            try{
                CoordinateReferenceSystem crsTarget = CRS.decode("EPSG:3857");
                // 投影转换
                MathTransform transform = CRS.findMathTransform(DefaultGeographicCRS.WGS84, crsTarget);
                Point webPoint = (Point)JTS.transform(geom, transform);
                return new double[]{webPoint.getX(), webPoint.getY()};
            }
            catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                return null;
            }
        }
    
    2)计算距离
        public double distance(String geohash1, String geohash2){
            Map lonlat1 = decode(geohash1),
                lonlat2 = decode(geohash2);
            double lat1 = (Double) lonlat1.get("lat"),
                    lon1 = (Double) lonlat1.get("lon");
            double lat2 = (Double) lonlat2.get("lat"),
                    lon2 = (Double) lonlat2.get("lon");
            double[] webPoint1 = lonlat2WebMactor(lon1, lat1),
                    webPoint2 = lonlat2WebMactor(lon2, lat2);
            return Math.sqrt(Math.pow(webPoint1[0]-webPoint2[0], 2d)+Math.pow(webPoint1[1]-webPoint2[1], 2d));
        };
    
    3)筛选满足条件的点

    将查询出来的结果做比较,筛选满足条件的点。

            for(int i=0;i<_list.size();i++){
                Map map = (Map)_list.get(i);
                String _geohash = map.get("geohash").toString();
                double _dist = geoHash.distance(strGeohash, _geohash);
                if(_dist<dist)list.add(map);
            }
            return list;
    

    将2和3串起来,实现代码如下:

        public List searchByDist(double lon, double lat, double dist){
            List list = new ArrayList();
            int precision = geoHash.effectnum(dist);
            String strGeohash = geoHash.encode(lat, lon, 0);
            String filters = "where geohash like '"+strGeohash.substring(0, precision)+"%'";
            List _list = geoDao.getDataByFilter(points.getTableName(), points.getTableFields(), filters, new Object[]{});
            System.out.println(JSONArray.toJSONString(_list));
            for(int i=0;i<_list.size();i++){
                Map map = (Map)_list.get(i);
                String _geohash = map.get("geohash").toString();
                double _dist = geoHash.distance(strGeohash, _geohash);
                if(_dist<dist)list.add(map);
            }
            return list;
        }
    

    技术博客
    CSDN:http://blog.csdn.NET/gisshixisheng
    博客园:http://www.cnblogs.com/lzugis/
    在线教程
    http://edu.csdn.Net/course/detail/799
    Github
    https://github.com/lzugis/
    联系方式

    类型 内容
    qq 1004740957
    公众号 lzugis15
    e-mail niujp08@qq.com
    webgis群 1004740957
    Android群 337469080
    GIS数据可视化群 458292378
    LZUGIS

    相关文章

      网友评论

        本文标题:Geohash之范围搜索

        本文链接:https://www.haomeiwen.com/subject/tylswxtx.html