获取距离某坐标附近一定范围内的点的两种方式

场景:数据库中有一些点坐标,需要查找出距离当前位置2千米范围内的坐标

方式1:根据两个经纬度计算距离,Oracle/MySql计算地表两点之间的距离:

Oracle:

  • 创建获取弧度的函数:
CREATE OR REPLACE FUNCTION RAD(d number) RETURN NUMBER
is
PI number :=3.141592625;
begin
return  d* PI/180.0;
end ;
  • 创建根据两个坐标计算距离的函数
CREATE OR REPLACE FUNCTION GetDistance(LngBegin number,
                                        LatBegin number,
                                        LngEnd number,
                                        LatEnd number) RETURN NUMBER is
  earth_padius number := 6378.137;
  radLat1      number := rad(LngBegin);
  radLat2      number := rad(LngEnd);
  a            number := radLat1 - radLat2;
  b            number := rad(LatBegin) - rad(LatEnd);
  s            number := 0;
begin
  s := 2 *
       Asin(Sqrt(power(sin(a / 2), 2) +
                 cos(radLat1) * cos(radLat2) * power(sin(b / 2), 2)));
  s := s * earth_padius;
  s := Round(s * 10000)/10;
  return s;
end;
  • 查询距离
SELECT
   GetDistance(#{lon},#{lat},SHOP_LON,SHOP_LAT) AS Mdistance 
FROM Shop

MySql:
查询距离sql:

SELECT
	ROUND( 6378.138 * 2 * ASIN( SQRT( POW( SIN(( #{lat} * PI() / 180 - SHOP_LAT * PI() / 180) / 2 ),
         2 ) + COS(#{lat} * PI() / 180) * COS(SHOP_LAT * PI() / 180) * POW( SIN( ( #{lon} * PI() / 180 - SHOP_LON * PI() / 180 ) / 2 ),
         2 ) ) ) * 1000 ) AS Mdistance
FROM Shop

当然,也可将计算方法封装为函数


拿到距离后根据条件取值,因为是sql查询,若数据量大时,效率是一个问题

方式2:根据经纬度和距离计算出矩形范围,取范围内的值:

  • 提供工具类如下:
public class GeoUtil
{
    /**
     * 地球半径:6378.137KM
     */
    private static double EARTH_RADIUS = 6378.137;
    
    /**
     * 根据经纬度和距离返回一个矩形范围
     * @param lng 经度
     * @param lat 纬度
     * @param distance 距离(单位为米)
     * @return [lng1,lat1, lng2,lat2] 矩形的左下角(lng1,lat1)和右上角(lng2,lat2)
     */
    public static double[] getRectangle(double lng, double lat, long distance)
    {
        float delta = 111000;
        if (lng != 0 && lat != 0)
        {
            double lng1 = lng - distance / Math.abs(Math.cos(Math.toRadians(lat)) * delta);
            double lng2 = lng + distance / Math.abs(Math.cos(Math.toRadians(lat)) * delta);
            double lat1 = lat - (distance / delta);
            double lat2 = lat + (distance / delta);
            return new double[] {lng1, lat1, lng2, lat2};
        }
        else
        {
            double lng1 = lng - distance / delta;
            double lng2 = lng + distance / delta;
            double lat1 = lat - (distance / delta);
            double lat2 = lat + (distance / delta);
            return new double[] {lng1, lat1, lng2, lat2};
        }
    }
    
    /**
     * 得到两点间的距离 米
     * @param lat1
     * @param lng1
     * @param lat2
     * @param lng2
     * @return
     */
    public static double getDistanceOfMeter(double lat1, double lng1, double lat2, double lng2)
    {
        double radLat1 = rad(lat1);
        double radLat2 = rad(lat2);
        double a = radLat1 - radLat2;
        double b = rad(lng1) - rad(lng2);
        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
        s = s * EARTH_RADIUS;
        s = Math.round(s * 10000) / 10;
        return s;
    }
    
    private static double rad(double d)
    {
        return d * Math.PI / 180.0;
    }
}

这种方式得到的是一个范围,查询时无需计算根据大小即可取出范围内的点,但是就没有具体的距离值了。在数据量大时效率较高。同时比较符合app的某些业务场景,例如每次展示手机屏幕范围内的点(手机是方的)

原文链接:https://blog.csdn.net/qq_34928194/article/details/107813361

最后修改日期:2020年8月6日