获取两个地理点之间的距离

110

我希望制作一个应用程序,可以检查用户最近的位置。我可以轻松获取用户的位置信息,并且已经有了一份包含纬度和经度的地点列表。

如何才能知道当前用户位置与列表中最近地点的距离?

在谷歌API中我找不到相关的信息。

9个回答

173

2
可能比使用Location.DistanceBetween()慢,因为它使用了Location对象,但对于我的目的来说非常有效。 - ZoltanF
我需要导入哪个类以获取位置? 是 import android.location.Location; 吗? - Pranav MS
@PranavMS 是的,android.location.Location; - AndrewS
我认为distanceTo返回的是第一个点和最后一个点之间的距离,但是只在直线上,所以如果你从点a到b采取其他方向,它永远不会准确,这就是distanceBetween发挥作用的时候,你可以保存每个被创建的点之间的距离,然后通过最终参数results[]得到正确的距离。 - Gastón Saillén

122

37
distanceBetween是一个静态方法,接受两组纬度和经度点作为参数,因此您甚至不需要实例化一个位置对象 =) - Stan Kurdziel
4
我确定他是指distanceTo方法。 - laph
这非常棒且超级有帮助,但是构造函数中的String provider是什么? - miss.serena

34

一种近似解法(基于等经纬投影),速度更快(只需要1个三角函数和1个平方根)。

如果您的点不是太远,则此逼近值相关。与真实的haversine距离相比,它将始终高估。例如,如果您两点之间的纬度或经度差不超过4个小数位,则它将最多增加0.05382%到真实距离。

标准公式(Haversine)是精确的(即它适用于地球上任何经纬度对),但速度慢得多,因为它需要7个三角函数和2个平方根。如果您的两个点不是太远,并且绝对精度不是关键,则可以使用此近似版本(等经纬度投影),它更快,因为它仅使用一个三角函数和一个平方根。

// Approximate Equirectangular -- works if (lat1,lon1) ~ (lat2,lon2)
int R = 6371; // km
double x = (lon2 - lon1) * Math.cos((lat1 + lat2) / 2);
double y = (lat2 - lat1);
double distance = Math.sqrt(x * x + y * y) * R;
你可以通过以下两种方式进一步优化:
1. 如果你仅仅需要比较距离而没有其他的需求,可以去掉平方根部分(这种情况下直接比较两点距离的平方即可)。
2. 如果你需要计算一个点和多个点之间的距离,可以将余弦值提取出来(这种情况下以主点为中心进行等经线投影,因此可以为所有比较只计算一次余弦值)。
更多信息请参见:http://www.movable-type.co.uk/scripts/latlong.html 在多种语言中有一个漂亮的Haversine公式的参考实现,请查看:http://www.codecodex.com/wiki/Calculate_Distance_Between_Two_Points_on_a_Globe

非常感谢。但是,如果我需要获取一个位置周围的一组位置,应该使用while循环来检查每个位置是否在搜索位置内,并仅保留在范围内的位置吗? - themhz
你可以这样做,但那是一种暴力的方法,时间复杂度为O(n)。为了得到一个O(1)的解决方案,在计算精确解之前使用二维空间索引来修剪潜在匹配项。我们正在离开这个问题的范围 :) - Laurent Grégoire
这是一个非常好的总结,包含了很多可能的优化方法。谢谢!正是我所需要的。 - Sam Vloeberghs
我只是想知道这个公式是否适用于较大的距离。 - Sandipan Majhi
看答案,简而言之:不,它不能用于大距离。两点之间的距离越远,误差就越大,与精确的_Haversine_公式相比。 - Laurent Grégoire

12

有几种方法可以使用,但要确定哪种是最佳的,我们首先需要知道您是否知道用户的高度以及其他点的高度?

根据您所追求的精度级别,您可以查看Haversine或Vincenty公式...

这些页面详细介绍了公式,并且对于不那么数学倾向的人还提供了如何在脚本中实现它们的解释!

Haversine公式:http://www.movable-type.co.uk/scripts/latlong.html

Vincenty公式:http://www.movable-type.co.uk/scripts/latlong-vincenty.html

如果您对公式中的任何含义有问题,请留言,我会尽力回答:)


4

获取LatLng之间距离的方法有两种。

public static void distanceBetween (double startLatitude, double startLongitude, double endLatitude, double endLongitude, float[] results)

点击此处查看相关信息。

其次,praveen回答的是public float distanceTo (Location dest)方法。


3
private float getDistance(double lat1, double lon1, double lat2, double lon2) {
        float[] distance = new float[2];
        Location.distanceBetween(lat1, lon1, lat2, lon2, distance);
        return distance[0];
    }

2

只需使用以下方法,传递经度和纬度,即可获得以米为单位的距离:

private static double distance_in_meter(final double lat1, final double lon1, final double lat2, final double lon2) {
    double R = 6371000f; // Radius of the earth in m
    double dLat = (lat1 - lat2) * Math.PI / 180f;
    double dLon = (lon1 - lon2) * Math.PI / 180f;
    double a = Math.sin(dLat/2) * Math.sin(dLat/2) +
            Math.cos(latlong1.latitude * Math.PI / 180f) * Math.cos(latlong2.latitude * Math.PI / 180f) *
                    Math.sin(dLon/2) * Math.sin(dLon/2);
    double c = 2f * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
    double d = R * c;
    return d;
}

2
latlong1和latlong2未定义。 - Boy
1
latlong1和latlong2是什么? - Nisal Malinda Livera

1

a = sin²(Δφ/2) + cos φ1 ⋅ cos φ2 ⋅ sin²(Δλ/2)

c = 2 ⋅ atan2( √a, √(1−a) )

distance = R ⋅ c

其中,φ为纬度,λ为经度,R为地球半径(平均半径=6,371km);

注意,角度需要转换为弧度才能传递给三角函数!

fun distanceInMeter(firstLocation: Location, secondLocation: Location): Double {
    val earthRadius = 6371000.0
    val deltaLatitudeDegree = (firstLocation.latitude - secondLocation.latitude) * Math.PI / 180f
    val deltaLongitudeDegree = (firstLocation.longitude - secondLocation.longitude) * Math.PI / 180f
    val a = sin(deltaLatitudeDegree / 2).pow(2) +
            cos(firstLocation.latitude * Math.PI / 180f) * cos(secondLocation.latitude * Math.PI / 180f) *
            sin(deltaLongitudeDegree / 2).pow(2)
    val c = 2f * atan2(sqrt(a), sqrt(1 - a))
    return earthRadius * c
}


data class Location(val latitude: Double, val longitude: Double)

0

你可以使用Google Map API获取距离和时间(Google Map API链接)

只需将下载的JSON传递给此方法,即可获得两个经纬度之间的实时距离和时间

void parseJSONForDurationAndKMS(String json) throws JSONException {

    Log.d(TAG, "called parseJSONForDurationAndKMS");
    JSONObject jsonObject = new JSONObject(json);
    String distance;
    String duration;
    distance = jsonObject.getJSONArray("routes").getJSONObject(0).getJSONArray("legs").getJSONObject(0).getJSONObject("distance").getString("text");
    duration = jsonObject.getJSONArray("routes").getJSONObject(0).getJSONArray("legs").getJSONObject(0).getJSONObject("duration").getString("text");

    Log.d(TAG, "distance : " + distance);
    Log.d(TAG, "duration : " + duration);

    distanceBWLats.setText("Distance : " + distance + "\n" + "Duration : " + duration);


}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接