最近给andorid做后台查询数据功能,有一个需求是模仿微信的查找附近人功能。 数据库中存储每个用户的经纬度信息及用户信息,通过当前用户传递过来的经纬度查询这个用户半径N公里以内的用户信息。
数据库表结构
表名 | Mobile_User |
---|---|
mu_id | 自增,主键 |
mu_u_id | 用户表的ID 外键 |
mu_longitud | 精度 |
mu_latitude | 纬度 |
首先需要一个方法,是把传递过来的经纬度按照半径N公里扩散,找出距离中心经纬度N公里的上下左右经纬度值。效果如图
随手画的 勿喷
以中心生成经纬度时 正上方和正下方的精度是不变的,只有纬度变化。 生成左右时道理一样,只有精度变化,纬度是不变的。
所以只需要生成上下的纬度,左右的精度就可以了。
参考了网上的文章,http://digdeeply.info/archives/06152067.html 这篇文章是用PHP实现的经纬度查询。修改成java的 代码如下
- /**
- * 生成以中心点为中心的四方形经纬度
- *
- * @param lat 纬度
- * @param lon 精度
- * @param raidus 半径(以米为单位)
- * @return
- */
- public static double[] getAround(double lat, double lon, int raidus) {
- Double latitude = lat;
- Double longitude = lon;
- Double degree = (24901 * 1609) / 360.0;
- double raidusMile = raidus;
- Double dpmLat = 1 / degree;
- Double radiusLat = dpmLat * raidusMile;
- Double minLat = latitude - radiusLat;
- Double maxLat = latitude + radiusLat;
- Double mpdLng = degree * Math.cos(latitude * (Math.PI / 180));
- Double dpmLng = 1 / mpdLng;
- Double radiusLng = dpmLng * raidusMile;
- Double minLng = longitude - radiusLng;
- Double maxLng = longitude + radiusLng;
- return new double[] { minLat, minLng, maxLat, maxLng };
- }
/**
* 生成以中心点为中心的四方形经纬度
*
* @param lat 纬度
* @param lon 精度
* @param raidus 半径(以米为单位)
* @return
*/
public static double[] getAround(double lat, double lon, int raidus) {
Double latitude = lat;
Double longitude = lon;
Double degree = (24901 * 1609) / 360.0;
double raidusMile = raidus;
Double dpmLat = 1 / degree;
Double radiusLat = dpmLat * raidusMile;
Double minLat = latitude - radiusLat;
Double maxLat = latitude + radiusLat;
Double mpdLng = degree * Math.cos(latitude * (Math.PI / 180));
Double dpmLng = 1 / mpdLng;
Double radiusLng = dpmLng * raidusMile;
Double minLng = longitude - radiusLng;
Double maxLng = longitude + radiusLng;
return new double[] { minLat, minLng, maxLat, maxLng };
}
这样四周的经纬度都已经生成了。
下一步是查询数据库中和四周经纬度匹配的数据。 如果数据量很大的话会很耗时间,而且会很消耗流量。所以需要用到分页查询
代码如下
- select * from mobile_user
- where mu_latitude <> 0
- and mu_longitud > #left_lat#
- and mu_longitud < #right_lat#
- and mu_latitude > #down_lon#
- and mu_latitude < #top_lon#
- and mu_u_id <> #uid#
- order by ACOS(SIN((#lat# * 3.1415) / 180 ) * SIN((mu_latitude * 3.1415) / 180 )
- +COS((#lat# * 3.1415) / 180 ) * COS((mu_latitude * 3.1415) / 180 )
- *COS((#lon# * 3.1415) / 180 - (mu_longitud * 3.1415) / 180 ) )
- * 6380 asc limit #start#,#end#
select * from mobile_user
where mu_latitude <> 0
and mu_longitud > #left_lat#
and mu_longitud < #right_lat#
and mu_latitude > #down_lon#
and mu_latitude < #top_lon#
and mu_u_id <> #uid#
order by ACOS(SIN((#lat# * 3.1415) / 180 ) * SIN((mu_latitude * 3.1415) / 180 )
+COS((#lat# * 3.1415) / 180 ) * COS((mu_latitude * 3.1415) / 180 )
*COS((#lon# * 3.1415) / 180 - (mu_longitud * 3.1415) / 180 ) )
* 6380 asc limit #start#,#end#
我用的是ibatis框架,sql里以#开始并结束的 是我传递过来的参数。 sql语句计算了每条数据和中心经纬度的距离并且以最近进行排序。 sql语句是根据下面的方法演变而来
方法是计算两个经纬度之间的直线距离。
- /**
- * 计算中心经纬度与目标经纬度的距离(米)
- *
- * @param centerLon
- * 中心精度
- * @param centerLan
- * 中心纬度
- * @param targetLon
- * 需要计算的精度
- * @param targetLan
- * 需要计算的纬度
- * @return 米
- */
- private static double distance(double centerLon, double centerLat, double targetLon, double targetLat) {
- double jl_jd = 102834.74258026089786013677476285;// 每经度单位米;
- double jl_wd = 111712.69150641055729984301412873;// 每纬度单位米;
- double b = Math.abs((centerLat - targetLat) * jl_jd);
- double a = Math.abs((centerLon - targetLon) * jl_wd);
- return Math.sqrt((a * a + b * b));
- }
/**
* 计算中心经纬度与目标经纬度的距离(米)
*
* @param centerLon
* 中心精度
* @param centerLan
* 中心纬度
* @param targetLon
* 需要计算的精度
* @param targetLan
* 需要计算的纬度
* @return 米
*/
private static double distance(double centerLon, double centerLat, double targetLon, double targetLat) {
double jl_jd = 102834.74258026089786013677476285;// 每经度单位米;
double jl_wd = 111712.69150641055729984301412873;// 每纬度单位米;
double b = Math.abs((centerLat - targetLat) * jl_jd);
double a = Math.abs((centerLon - targetLon) * jl_wd);
return Math.sqrt((a * a + b * b));
}
这样既实现了分页处理,又实现了每条数据的经纬度与中心经纬度的直线距离(以米为单位)。
最后就是组成json数组返回给android使用了。
做个笔记,欢迎补充。