对 lon\lat 点的排序列表,从最近的开始

Posted

技术标签:

【中文标题】对 lon\\lat 点的排序列表,从最近的开始【英文标题】:Sort list of lon\lat points, start with nearest对 lon\lat 点的排序列表,从最近的开始 【发布时间】:2011-07-20 18:11:06 【问题描述】:

我有来自 GPS 的位置(lon_base、lat_base)。 我有一个位置列表(lon1、lat1|lon2、lat2|lon3、lat3...) 这个列表很长,遍布世界各地。

我的问题是: 1. 如何从该列表中仅获取距离我的 lon_base\lat_base 1 英里的 lon\lat? 2. 如何从近到远排序?

提前致谢!

【问题讨论】:

【参考方案1】:
public static List<Location> sortLocations(List<Location> locations, final double myLatitude,final double myLongitude) 
    Comparator comp = new Comparator<Location>() 
        @Override
        public int compare(Location o, Location o2) 
            float[] result1 = new float[3];
            android.location.Location.distanceBetween(myLatitude, myLongitude, o.Lat, o.Long, result1);
            Float distance1 = result1[0];

            float[] result2 = new float[3];
            android.location.Location.distanceBetween(myLatitude, myLongitude, o2.Lat, o2.Long, result2);
            Float distance2 = result2[0];

            return distance1.compareTo(distance2);
        
    ;


    Collections.sort(locations, comp);
    return locations;

位置列表是包含您自己的位置类的列表,而不是 android.location.Location。

【讨论】:

【参考方案2】:

您可以使用great circle distance 来计算您知道经纬度坐标的两点之间的距离。 formulae 很容易编码:

static double distance(double fromLat, double fromLon, double toLat, double toLon) 
    double radius = 6378137;   // approximate Earth radius, *in meters*
    double deltaLat = toLat - fromLat;
    double deltaLon = toLon - fromLon;
    double angle = 2 * Math.asin( Math.sqrt(
        Math.pow(Math.sin(deltaLat/2), 2) + 
        Math.cos(fromLat) * Math.cos(toLat) * 
        Math.pow(Math.sin(deltaLon/2), 2) ) );
    return radius * angle;

【讨论】:

【参考方案3】:

您想定义自己的Comparator,通常看起来像这样:

LonLat myHouse = /* whatever */ ;
Comparable comp = new Comparable () 
    LonLat a;
    int compareTo (Object b) 
        int aDist = calcDistance(a, myHouse) ;
        int bDist = calcDistance(b, myHouse) ;
        return aDist - bDist;
    
;
myLonLatList.sort(lonLatList, comp);

其中calcDistance() 只是计算两点之间的距离。如果您使用的是 Android,我认为 Google 地图在其 API 中的某处有一个功能可以为您执行此操作。

编辑:你会希望你的 calcDistance() 函数看起来像 ChrisJ 的 distance 函数。

-tjw

【讨论】:

这段代码中的“a”值是什么。如果您提供示例代码,对我很有帮助。【参考方案4】:

您可以使用followig 近似值(因为1 英里远小于地球的半径)来计算距您的基地的距离:

dx = cos(phi_base) * (theta - theta_base)
dy = phi - phi_base

dist = sqrt(dx*dx+dy*dy) 

与:phi = 纬度和theta = 经度

如果thetaphi 以度为单位,则结果以60 海里为单位。 对于纬度与您的基准纬度相差​​很大的点,结果将是非常错误的,但如果您只想知道哪些点距离您的基准约 1 英里,这并不重要。

对于大多数编程语言,您必须将 phi_base 转换为弧度(乘以 pi/180)才能将其用于 cos()

(注意:如果您的基经非常接近 180° 或 -180°,则必须特别小心,但可能并非如此 :-)

使用计算出的距离作为排序键来对您的点进行排序。

如果您必须更精确(例如,如果您想知道距离您家大约 2000 英里的所有点),则必须使用 Great Circle Distance 的公式来计算球体上两点的精确距离.

【讨论】:

【参考方案5】:

据此link 我制定了工作方法。上面的答案是错误的,因为它不会将 lat/lng 度转换为弧度。

    private double getDistance(double fromLat, double fromLon, double toLat, double toLon)
        double radius = 6371;   // Earth radius in km
        double deltaLat = Math.toRadians(toLat - fromLat);
        double deltaLon = Math.toRadians(toLon - fromLon);
        double lat1 = Math.toRadians(fromLat);
        double lat2 = Math.toRadians(toLat);
        double aVal = Math.sin(deltaLat/2) * Math.sin(deltaLat/2) +
            Math.sin(deltaLon/2) * Math.sin(deltaLon/2) * Math.cos(lat1) * Math.cos(lat2);
        double cVal = 2*Math.atan2(Math.sqrt(aVal), Math.sqrt(1-aVal));  

        double distance = radius*cVal;
        Log.d("distance","radius * angle = " +distance);
        return distance;
    

【讨论】:

以上是关于对 lon\lat 点的排序列表,从最近的开始的主要内容,如果未能解决你的问题,请参考以下文章

如何从 terra SpatRaster 构建 lon、lat、值数据框

如何加快搜索最近的地理点?

使用校准点在地图上将经度和纬度转换为X Y.

从 shapefile 将 EPSG 3035 转换为 EPSG 4326(lon lat)

组 lat/lon ,如果它们出现在边界框内

如何将 lon/lat 坐标转换为地球表面上 N-E 米的距离?