概述
很多时候,我们都会遇到这样的需求:查找某个点周边多少距离的点。从本质来说,是一个缓冲区分析+空间查找,本文结合Geohash来实现类似的功能。
效果
范围搜索结果说明:
- 红色的点和红色的圈是查找的中心点和距离(5km);
- 蓝色的点+粉色的点是通过geohash查找出来的点;
- 粉色的点是通过过滤后的点;
实现
本文实现是结合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/
联系方式
类型 | 内容 |
---|---|
1004740957 | |
公众号 | lzugis15 |
niujp08@qq.com | |
webgis群 | 1004740957 |
Android群 | 337469080 |
GIS数据可视化群 | 458292378 |
网友评论