zoukankan      html  css  js  c++  java
  • java实现搜索附近地点或人的功能

    前言

        当前大多数app都有查找附近的功能, 简单的有查找周围的运动场馆, 复杂的有滴滴, 摩拜查找周围的车辆. 本文主要阐述查找附近地点的一般实现. 

    方案比较

    方案1 (性能还不错)

        数据库直接存经纬度, 然后计算矩形边界值, 走索引查询

    方案2 (还没试过)

        将经纬度转换成 一个值, 然后进行比较查询 genhash

        http://blog.csdn.net/newjueqi/article/details/18989867

    方案3 (据说高性能, 性能怎样?待测试)

        mongodb 地理类型, 高性能 http://www.tuicool.com/articles/Jfu6fy

        sqlserver 地理数据 geography https://msdn.microsoft.com/en-us/library/ff929109.aspx

    方案1的实现(本文主要阐述此方案)

    实现环境: java+MySQL

    场景模拟:  张三用户在成都天府五街查询周围10公里内的地点

    1. 首先建立经纬度数据, 比如常见地点的经纬度数据库, 我这里是网上下载的一个shop_area 表数据,里面包含了一些常见地点的经纬度 ,如下图

    2. 然后根据张三用户所在的经纬度, 以及他要查询的距离10公里, 得到查询范围矩形的四个顶点, 如下图: 

    计算这四个点的 mybatis sql: 

    <select id="getCurrentLocationRectangle" parameterType="LocationFilter" resultType="LocationFilter">
        SELECT
            #{myLongitude} - #{distance} / ABS(COS(RADIANS(#{myLatitude})) * 69) AS LongitudeMin,
            #{myLongitude} + #{distance} / ABS(COS(RADIANS(#{myLatitude})) * 69) AS LongitudeMax,
            #{myLatitude} - (#{distance} / 69)                                   AS LatitudeMin,
            #{myLatitude} + (#{distance} / 69)                                   AS LatitudeMax
    </select>

    3. 将刚才得到的矩形四个点的值代入如下sql 来进行经纬度查询过滤, 记得经纬度字段要建立索引

     mybatis sql: 

    <select id="getUserNearbyAreaList" parameterType="com.anuo.app.modules.coach.entity.CoachFilter" resultType="ShopArea">
        SELECT *
        FROM (
            SELECT
              a.*,
              GetDistance(#{myLatitude}, #{myLongitude}, a.lat, a.lng) AS distance
            FROM shop_area a
            WHERE
                a.lat BETWEEN #{latitudeMin} AND #{latitudeMax}
                AND a.lng BETWEEN #{longitudeMin} AND #{longitudeMax}
        ) z
        <where>
            <if test="distance > 0 ">
                AND z.distance &lt; #{distance}
            </if>
        </where>
        ORDER BY z.distance
        LIMIT #{pageStart},#{pageSize}
    </select>

    因为先是通过四个点走索引过滤的经纬度数据, 所以大大提升了效率. 并且将我们想要的10公里范围内的经纬度数据过滤了出来, 虽然多查询了点数据(见下图四个叉叉处), 见下面第四步, 将多余的剔除掉

    4.上面sql中的   AND z.distance &lt; #{distance}  即 AND z.distance 小于指定距离 #{distance}, 是将下图画叉叉部分的经纬度数据剔除,这些数据是多余的, 因为为我们要查询的是圆圈内的数据

    这里用到了一个MySQL  GetDistance 函数, 代码如下

    DELIMITER $$
    
    USE `anuoapp`$$
    
    DROP FUNCTION IF EXISTS `GetDistance`$$
    
    CREATE DEFINER=`root`@`localhost` FUNCTION `GetDistance`(
        myLatitude DECIMAL(11,8),#我当前位置的纬度
        myLongitude DECIMAL(11,8),#我当前位置的经度
        latitude DECIMAL(11,8),
        Longitude  DECIMAL(11,8)
      ) RETURNS DOUBLE
    BEGIN
        RETURN (
          6371 * ACOS(
            COS(RADIANS(myLatitude)) * COS(RADIANS(latitude)) * COS(RADIANS(Longitude) - RADIANS(myLongitude)) +
            SIN(RADIANS(myLatitude)) * SIN(RADIANS(latitude))
          )
        );
      END$$
    
    DELIMITER ;

     最后查询出张三在成都天府五街周围10公里内的地点

    请求url: http://localhost:8080/v1/apiGetUserNearbyArea

    请求体: 

    {
      "Token":"6850d1c361e9478ca1e94496ec6b27f9",
      "Version": "1.8.0",
      "Entities": [
        {
        	"myLatitude":30.54286,
        	"myLongitude":104.075569,
        	"distance":10,
        	"pageSize":10,
        	"pageNumber":1
        }
      ],
      "IsMobile": true,
      "PageIndex": 1,
      "IsInnerTest": true,
      "IsGetIp": false,
      "PageSize": 38,
      "IsEncrypt": true,
      "Parameters": {}
    } 

    响应:

    {
        "success": true,
        "totalRow": 11,
        "entities": [
            {
                "id": "510122004",
                "areaname": "中和街道",
                "parentid": 510122,
                "shortname": "中和街道",
                "lng": "104.082375",
                "lat": "30.559141",
                "level": true,
                "position": "tr_0 tr_510000 tr_510100 tr_510122",
                "sort": 25,
                "distance": 1.9241037391984028
            },
            {
                "id": "510107063",
                "areaname": "石羊场街道",
                "parentid": 510107,
                "shortname": "石羊场街道",
                "lng": "104.048271",
                "lat": "30.590687",
                "level": true,
                "position": "tr_0 tr_510000 tr_510100 tr_510107",
                "sort": 12,
                "distance": 5.925643914100619
            },
            {
                "id": "510122122",
                "areaname": "万安镇",
                "parentid": 510122,
                "shortname": "万安镇",
                "lng": "104.112701",
                "lat": "30.487444",
                "level": true,
                "position": "tr_0 tr_510000 tr_510100 tr_510122",
                "sort": 18,
                "distance": 7.114938271111233
            },
            {
                "id": "510122120",
                "areaname": "新兴镇",
                "parentid": 510122,
                "shortname": "新兴镇",
                "lng": "104.149757",
                "lat": "30.52656",
                "level": true,
                "position": "tr_0 tr_510000 tr_510100 tr_510122",
                "sort": 21,
                "distance": 7.332851650201873
            },
            {
                "id": "510107007",
                "areaname": "火车南站街道",
                "parentid": 510107,
                "shortname": "火车南站街道",
                "lng": "104.082924",
                "lat": "30.619801",
                "level": true,
                "position": "tr_0 tr_510000 tr_510100 tr_510107",
                "sort": 7,
                "distance": 8.5843717771867
            }
        ]
    }

    完整源码见:  

    https://gitee.com/anuo/anuoapp

    在此项目中搜索 apiGetUserNearbyArea 接口即可定位

    完整数据库见: 

    https://pan.baidu.com/s/1o9lUJMU

  • 相关阅读:
    THD 变量存入threads中
    一个简单的optimizer_trace示例
    LINUX HOOK
    网易杭研后台技术中心的博客
    INNOSQL.官网
    淘宝 印风 UDF
    mysql原创博客
    mysql 主从图
    THD
    开源利器函数调用图
  • 原文地址:https://www.cnblogs.com/Gkey55/p/8443137.html
Copyright © 2011-2022 走看看