Mysql 经纬度范围查询

Mysql 经纬度范围查询

 

 

 

给定经纬度与半径(距离)查询范围内的数据?

 

 

方案一:Mysql + 外接正方形

    实现思路:
        以当前经纬度为圆心,以给定距离为半径画圆,那么在这个圆内的所有用户信息就是符合结果的信息,直接检索圆内的用户坐标难以实现,我们可以通过获取这个圆的外接正方形。
通过外接正方形,获取经度和纬度的最大最小值,根据最大最小值可以将坐标在正方形内的用户信息搜索出来。
此时在外接正方形中不属于圆形区域的部分就属于多余的部分,这部分用户信息距离当前用户(圆心)的距离必定是大于给定半径的,故可以将其剔除,最终获得指定范围内的附近的人。

 

工具类:

public class GeoUtils {
   //地球半径(米)
   private static final double EARTH_RADIUS = 6371000d;
   //弧度
   public static final double RADIANS_TO_DEGREES = 180 / Math.PI;
   public static final double DEGREES_TO_RADIANS = Math.PI / 180;
 
 
   /**
    * 根据中心点经纬度,计算半径范围内的最大、最小经纬度范围
    *
    * @param centerLon 中心点经度
    * @param centerLat 中心点纬度
    * @param dist      半径范围(米)
    * @return
    */
   public static Map<String, Double> findPosition(double centerLon, double centerLat, double dist) {
 
      //先计算查询点的经纬度范围
      double lonRad = 2 * Math.asin(Math.sin(dist / (2 * EARTH_RADIUS)) / Math.cos(centerLat * Math.PI / 180));
      //角度转为弧度
      lonRad = lonRad * RADIANS_TO_DEGREES;
      double latRad = dist / EARTH_RADIUS;
      latRad = latRad * 180 / Math.PI;
      //经度最小值
      double minLon = centerLon - lonRad;
      //经度最大值
      double maxLon = centerLon + lonRad;
      //纬度最小值
      double minLat = centerLat - latRad;
      //纬度最大值
      double maxLat = centerLat + latRad;
 
      HashMap<String, Double> geoMap = new HashMap<>();
      geoMap.put("minLat", minLat);
      geoMap.put("maxLat", maxLat);
      geoMap.put("minLon", minLon);
      geoMap.put("maxLon", maxLon);
      return geoMap;
   }
   
   /**
    * 求地球表面两点间的距离
    *
    * @param centerLon 中心点经度
    * @param centerLat 中心点纬度
    * @param targetLon 目标经度
    * @param targetLat 目标纬度
    * @return
    */
   public static double getDistance(double centerLon, double centerLat, double targetLon, double targetLat) {
 
      //计算中心点经纬度弧度
      double centerLonRad = centerLon * DEGREES_TO_RADIANS;
      double centerLatRad = centerLat * DEGREES_TO_RADIANS;
      //计算目标点经纬度弧度
      double targetLonRad = targetLon * DEGREES_TO_RADIANS;
      double targetLatRad = targetLat * DEGREES_TO_RADIANS;
      //计算经纬度的差
      double diffX = Math.abs(centerLonRad - targetLonRad);
      double diffY = Math.abs(centerLatRad - targetLatRad);
      //计算正弦
      double hsinX = Math.sin(diffX / 2);
      double hsinY = Math.sin(diffY / 2);
      //计算余弦
      double centerLatRad_cos = Math.cos(centerLatRad);
      double targetLatRad_cos = Math.cos(targetLatRad);
 
      double h = power(hsinY, 2) + (centerLatRad_cos * targetLatRad_cos * power(hsinX, 2));
      //两点的距离
      double dist = EARTH_RADIUS * 2 * Math.atan2(Math.sqrt(h), Math.sqrt(1 - h));
      return dist;
   }
 
   /**
    * 经纬度剔除小数点(直接剔除不舍入),保留6位
    *
    * @param number 经度/纬度
    * @return
    */
   public static Double geoRound6Pla(Double number) {
      BigDecimal numDigd = new BigDecimal(number);
      number = numDigd.setScale(6, BigDecimal.ROUND_DOWN).doubleValue();
      return number;
   }
 
   public static void main(String[] args) {
      //中心点经纬度
      double x1 = 102.604646;
      double y1 = 24.239485;
 
      //目标点经纬度
      double x2 = 103.634895;
      double y2 = 24.778173;
 
      Map<String, Double> position = GeoUtils.findPosition(x1, y1, 130000 );
      double distance = GeoUtils.getDistance(x1, y1, x2, y2);
      System.out.println(position);
      System.out.println(distance);
   }
}

 

方案二:mysql自带函数st_distance()查询两个经纬度点的距离。 st_distance 为mysql5.6+版本中开始自带的经纬度计算函数。

st_distance 计算的结果单位是度,需要乘111195(地球半径6371000*3.1415926/180)是将值转化为米。

SELECT
 u.user_name,
 u.position_lng,
 u.position_lat,
(st_distance (point (u.position_lng, u.position_lat), point(113.210411, 23.122771) ) * 111195) AS distance
FROM
user AS u 
HAVING distance < 5000
ORDER BY distance

 

posted @ 2022-08-22 10:32  邓维-java  阅读(4304)  评论(0)    收藏  举报