该接口采用数据库函数读取封装形式
优点:采用ORACLE控件计算函数,距离读取精确,编码简单;缺点:大批量数据(数据达到几十万之后)做附近位置读取时导致数据读取缓慢
实现代码:
1.数据库函数创建:
create or replace function get_distance(
jd1 number,
wd1 number,
jd2 number,
wd2 number
)
return number is--返回米
begin
if jd1 is not null and wd1 is not null and jd2 is not null and wd2 is not null then
return ROUND(sdo_geom.sdo_distance(mdsys.sdo_geometry(2001,
8307,
mdsys.sdo_point_type(jd1,
wd1,
0),
null,
null),
mdsys.sdo_geometry(2001,
8307,
mdsys.sdo_point_type(jd2,
wd2,
0),
null,
null),
0.005));
else
--return 0;
--return 99999;
return null;
end if;
end;
2.一般附近数据查询有范围限制,如果直接使用函数进行距离范围限制会导致数据查询缓慢,因此我们先取当前坐标点范围4个坐标点内的大概范围数据,再对这个范围内数据做距离计算
1)取当前坐标周围4个角落的经纬度
/**
* @title
* @date 2017年3月15日
* @author niuchuang
* @param longitude 经度
* @param latitude 纬度
* @param dis 距离 米
* @return
*/
public static Map<String,Double> getDtFwSj(Double longitude, Double latitude, Double dis) {
Map<String,Double> _map=new HashMap<String,Double>();
if (longitude!=null && latitude!=null && dis!=null) {
// 先计算查询点的经纬度范围
double r = 6371;// 地球半径千米
// double dis = 0.5;// 0.5千米距离
dis = dis / 1000;// 米转换千米
double dlng = 2 * Math.asin(Math.sin(dis / (2 * r)) / Math.cos(latitude * Math.PI / 180));
dlng = dlng * 180 / Math.PI;// 角度转为弧度
/*
double dlng = 2 * Math.asin(Math.sin(dis / (2 * r)) / Math.cos(latitude));
// 转换弧度
dlng = dlng * (180/Math.PI);
*/
double dlat = dis / r;
// 转换弧度
dlat = dlat * 180 / Math.PI;
// 处理余弦值为负数
double minlng = dlng > 0 ? longitude - dlng : longitude + dlng;
double maxlng = dlng > 0 ? longitude + dlng : longitude - dlng;
double minlat = dlat > 0 ? latitude - dlat : latitude + dlat;
double maxlat = dlat > 0 ? latitude + dlat : latitude - dlat;
_map.put("minjd", minlng);
_map.put("maxjd", maxlng);
_map.put("minwd", minlat);
_map.put("maxwd", maxlat);
}
return _map;
}
2)SQL查询封装
public String getSql(Map paramMap,List<Object[]> _valueObject){
Object valueObject[]=new Object[]{};
//附近条件
Double longitude=MapUtils.getDouble(paramMap, "longitude"),latitude=MapUtils.getDouble(paramMap, "latitude"),dis=MapUtils.getDouble(paramMap, "dis");
String colum="id,qymc,qybm,address,jd,wd";
StringBuilder sql=new StringBuilder();
sql.append("select ").append(colum).append(" from b_qyxx cyqy where 1=1");
//4个坐标点内数据限制,大概的数据查询限制,避免直接使用数据库函数导致查询缓慢
Map<String,Double> dtfw=WxApiUtil.getDtFwSj(longitude, latitude, dis);
if (longitude!=null && latitude!=null && dis!=null && dis!=-1) {
double minlng = MapUtils.getDouble(dtfw, "minjd", 0d);
double maxlng = MapUtils.getDouble(dtfw, "maxjd", 0d);
double minlat = MapUtils.getDouble(dtfw, "minwd", 0d);
double maxlat = MapUtils.getDouble(dtfw, "maxwd", 0d);
sql.append(" and (cyqy.jd between ? and ?) and (cyqy.wd between ? and ?)");
valueObject = ArrayUtils.add(valueObject, minlng);
valueObject = ArrayUtils.add(valueObject, maxlng);
valueObject = ArrayUtils.add(valueObject, minlat);
valueObject = ArrayUtils.add(valueObject, maxlat);
}
//对模糊查询进行转译,避免查传入“%”出查询出没有“%”的数据
String qymc=MapUtils.getString(paramMap, "qymc");
if (StringUtils.isNotBlank(qymc)){
sql.append(" and qymc like ? escape '\\'");
valueObject = ArrayUtils.add(valueObject, "%"+qymc.replaceAll("\\%", "\\\\%")+"%");
}
//该处共使用3层SQL查询封装;第一层:读取当前坐标点范围附近4个人坐标点内数据,进行简单的数据过滤,避免直接使用函数造成查询缓慢;第二层:在第一层的基础上通过数据库函数度取出距离信息;第三层:
//在前两层的基础上,通过第二层得到的距离对范围再做一次精确限制(未在第二层做限制是因为如果做限制需调用两次函数),后面直接加距离排序即可
sql=new StringBuilder("select sel.*,abs(get_distance(jd,wd,"+longitude+","+latitude+")) jl_length from ("+sql+") sel");
sql=new StringBuilder("select dp.* from ("+sql.toString()+") dp where jl_length<="+dis);
//sql=new StringBuilder("select dp.* from (select sel.*,abs(get_distance(jd,wd,"+longitude+","+latitude+")) jl_length from ("+sql+") sel) dp where jl_length<="+dis);
sql.append(" order by jl_length asc ");
_valueObject.add(valueObject);
return sql.toString();
}