java 实现微信搜索附近人功能
最近给andorid做后台查询数据功能,有一个需求是模仿微信的查找附近人功能。 数据库中存储每个用户的经纬度信息及用户信息,通过当前用户传递过来的经纬度查询这个用户半径N公里以内的用户信息。
数据库表结构
| mu_id | 自增,主键 | 
| mu_u_id | 用户表的ID 外键 | 
| mu_longitud | 精度 | 
| mu_latitude | 纬度 | 
(还有其他的一些信息,这里就列举4个字段足矣)
首先需要一个方法,是把传递过来的经纬度按照半径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 };     
    }
这样四周的经纬度都已经生成了。       
 下一步是查询数据库中和四周经纬度匹配的数据。 如果数据量很大的话会很耗时间,而且会很消耗流量。所以需要用到分页查询 
代码如下
@RequestMapping(params = "doGetQuserNearList")   
@ResponseBody   
public JSONObject doGetQuserNearList( HttpServletRequest request) {   
    JSONObject jsonObject=new JSONObject();   
    String km=request.getParameter("km");//搜索范围  这里以米为单位   
    String useridString=request.getParameter("userid");   
    String precision=request.getParameter("precision");   
    String latitude=request.getParameter("latitude");   
    String rows=request.getParameter("rows");   
    String page=request.getParameter("page");   
    try {   
           
        if("".equals(useridString)||useridString==null){   
            jsonObject.put("status", "0");   
            jsonObject.put("msg", "用户id不能为空");   
            return jsonObject;   
        }   
           
        Session session= memberService.getSession();   
        Query query=null;   
         
     org.hibernate.SQLQuery sqlQuery= session.createSQLQuery("select * from q_member  where latitude <> 0 "   
                + "and _precision > :left_lat "   
                + "and _precision < :right_lat "   
                + "and latitude > :down_lon  "   
                + "and latitude < :top_lon  "   
                + "and id <> :userid "   
                +" order by ACOS(SIN((:lat * 3.1415) / 180 ) * SIN((latitude * 3.1415) / 180 )+COS((:lat * 3.1415) / 180 )*COS((latitude * 3.1415) / 180 )*COS((:lon * 3.1415) / 180 - (_precision * 3.1415) / 180 ) ) * 6380 asc limit :start,:size");   
         double[] lalon=Location.getAround(Double.valueOf(latitude), Double.valueOf(precision), Integer.valueOf(km));   
           
         sqlQuery.setParameter("left_lat", lalon[1]);       
         sqlQuery.setParameter("right_lat", lalon[3]);   
         sqlQuery.setParameter("down_lon", lalon[0]);   
         sqlQuery.setParameter("top_lon", lalon[2]);   
            
         sqlQuery.setParameter("lat", latitude);   
         sqlQuery.setParameter("lon", precision);   
         sqlQuery.setParameter("start", Integer.valueOf(page)-1);   
         sqlQuery.setParameter("size", Integer.valueOf(rows));   
         sqlQuery.setParameter("userid", useridString);   
            
            java.util.List<MemberEntity> memberEntities=sqlQuery.addEntity(MemberEntity.class).list();   
            
        //循环计算相对距离   
        for(MemberEntity memberEntity:memberEntities){   
           
            memberEntity.setDistance(Location.distance(Double.valueOf(latitude), Double.valueOf(precision), Double.valueOf(memberEntity.getLatitude()), Double.valueOf(memberEntity.getPrecision())));   
        }   
           
        
        jsonObject.put("status", "1");   
        jsonObject.put("msg", memberEntities);   
           
    } catch (Exception e) {   
        e.printStackTrace();   
        message = "获取失败";   
        jsonObject.put("status", "0");   
        jsonObject.put("msg", message);   
        throw new BusinessException(e.getMessage());   
    }   
       
    return jsonObject;   
}
我用的是Hibernate框架,  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));     
    }
这样既实现了分页处理,又实现了每条数据的经纬度与中心经纬度的直线距离(以米为单位)。      
   
已有 0 人发表留言,猛击->> 这里<<-参与讨论
ITeye推荐