Geohash之范围搜索

概述

很多时候,我们都会遇到这样的需求:查找某个点周边多少距离的点。从本质来说,是一个缓冲区分析+空间查找,本文结合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;
    }

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏SeanCheney的专栏

《Pandas Cookbook》第11章 用Matplotlib、Pandas、Seaborn进行可视化

21630
来自专栏后端技术探索

(答案来了)两道腾讯面试题目

前天推送的文章《两道腾讯技术面试题(二面经历)》,收到了不少留言,感兴趣的可以去哪篇文章下查看精选留言,有一多半同学没有正确理解题目,可分享的留言寥寥无几,根据...

12310
来自专栏hanlp学习笔记

hanlp中的N最短路径分词

N-最短路径 是中科院分词工具NLPIR进行分词用到的一个重要算法,张华平、刘群老师在论文《基于N-最短路径方法的中文词语粗分模型》中做了比较详细的介绍。该算法...

17300
来自专栏小樱的经验随笔

详解斯坦纳点及斯坦纳树及模版归纳总结

①什么是斯坦纳点?   假设原来已经给定了个点,库朗等指出需要引进的点数至多为,此种点称为斯坦纳点。过每一斯坦纳点,至多有三条边通过。若为三条边,则它们两两交成...

92460
来自专栏程序生活

Python json 模块dumps、dump、loads、load的使用

本文主要讲下json.dumps和json.dump、json.loads和json.load的区别,因为经常需要加载json文件,读取数据,傻傻分不清...

9110
来自专栏大数据风控

R中的数据结构(Array,Factor,List,DataFrame)

1、R中的数据结构-Array #一维数组 x1 <- 1:5; x2 <- c(1,3,5,7,9) x3 <- array(c(2, 4, 6, 8, 10...

24190
来自专栏决胜机器学习

PHP数据结构(十一) ——图的连通性问题与最小生成树算法(2)

PHP数据结构(十一)——图的连通性问题与最小生成树算法(2) (原创内容,转载请注明来源,谢谢) 再次遇到微信公众号限制字数3000字的问题。因此将...

458100
来自专栏数据结构与算法

BZOJ5027: 数学题

Description 给出a,b,c,x1,x2,y1,y2,求满足ax+by+c=0,且x∈[x1,x2],y∈[y1,y2]的整数解有多少对? Inpu...

313100
来自专栏计算机视觉与深度学习基础

Leetcode 239. Sliding Window Maximum

Given an array nums, there is a sliding window of size k which is moving from t...

21650
来自专栏小樱的经验随笔

ECJTUACM16 Winter vacation training #4 题解&源码

https://vjudge.net/contest/149692#overview 这周一VJ比赛,题解&源码已完成! A.....................

394110

扫码关注云+社区

领取腾讯云代金券