天天看點

java 實作微信搜尋附近人功能

最近給andorid做背景查詢資料功能,有一個需求是模仿微信的查找附近人功能。 資料庫中存儲每個使用者的經緯度資訊及使用者資訊,通過目前使用者傳遞過來的經緯度查詢這個使用者半徑N公裡以内的使用者資訊。   

資料庫表結構

表資訊

表名 Mobile_User
mu_id 自增,主鍵
mu_u_id 使用者表的ID 外鍵
mu_longitud 精度
mu_latitude 緯度

(還有其他的一些資訊,這裡就列舉4個字段足矣)

首先需要一個方法,是把傳遞過來的經緯度按照半徑N公裡擴散,找出距離中心經緯度N公裡的上下左右經緯度值。效果如圖

java 實作微信搜尋附近人功能
java 實作微信搜尋附近人功能

 随手畫的  勿噴

以中心生成經緯度時 正上方和正下方的精度是不變的,隻有緯度變化。 生成左右時道理一樣,隻有精度變化,緯度是不變的。

是以隻需要生成上下的緯度,左右的精度就可以了。

參考了網上的文章,http://digdeeply.info/archives/06152067.html 這篇文章是用PHP實作的經緯度查詢。修改成java的 代碼如下

    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語句是根據下面的方法演變而來

方法是計算兩個經緯度之間的直線距離。

    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)); 

    }

這樣既實作了分頁處理,又實作了每條資料的經緯度與中心經緯度的直線距離(以米為機關)。