在地图上计算两点之间的距离其实就是计算地球弧度距离,将用角度表示的角转换为近似相等的用弧度表示的角。

首先需要获取某个地点的经纬度,这里可以借用百度地图 API 进行计算得到。

通过百度 API 求取距离

  1. import org.apache.http.client.methods.HttpUriRequest;
  2. import org.apache.http.client.methods.RequestBuilder;
  3. import org.apache.http.impl.client.CloseableHttpClient;
  4. import org.apache.http.impl.client.HttpClientBuilder;
  5. import java.io.BufferedReader;
  6. import java.io.IOException;
  7. import java.io.InputStream;
  8. import java.io.InputStreamReader;
  9. /**
  10. * 百度 API 经纬度计算位置
  11. * </p>
  12. * 发送 GET 请求借助 httpclient,需要在 POM 中引入:
  13. * <a href="http://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient"></a>
  14. *
  15. * @author MinGRn <br > MinGRn97@gmail.com
  16. * @date 01/10/2018 15:17
  17. */
  18. public class DistanceUtil {
  19. /** 百度获取位置经纬度 URL */
  20. private static final String LNG_LAT_POINT_URL = "http://api.map.baidu.com/geocoder/v2/?output=json&ak=RguGdBfvanKG10lrLHtUAtka&address=";
  21. /** 百度Map距离求取 URL */
  22. private static final String WAY_POINTS_DISTANCE_URL = "http://api.map.baidu.com/telematics/v3/distance?output=json&ak=RguGdBfvanKG10lrLHtUAtka&waypoints=";
  23. /**
  24. * 获取具体位置的经纬度
  25. * </p>
  26. * 如: location = 上海市徐汇区零陵小区
  27. * result:{"status":0,"result":{"location":{"lng":121.45078363819293,"lat":31.193489388753848},"precise":1,"confidence":70,"comprehension":100,"level":"地产小区"}}
  28. *
  29. * @param location 地理位置
  30. */
  31. private static void getLngAndLat(String location) {
  32. try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) {
  33. HttpUriRequest uriRequest = RequestBuilder.get(LNG_LAT_POINT_URL + location).build();
  34. http4BaiduApi(httpClient, uriRequest);
  35. } catch (IOException e) {
  36. // Handler IO Exception
  37. }
  38. }
  39. /**
  40. * 计算两个坐标点的距离
  41. * <br>
  42. * 返回结果: 距离(米)
  43. *
  44. * @param startLng 起点经度
  45. * @param startLat 起点纬度
  46. * @param endLng 终点经度
  47. * @param endLat 终点纬度
  48. */
  49. private static void twoLocationDistance(Double startLng, Double startLat, Double endLng, Double endLat) {
  50. try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) {
  51. String builderPoint = startLng + "," + startLat + ";" + endLng + "," + endLat;
  52. HttpUriRequest uriRequest = RequestBuilder.get(WAY_POINTS_DISTANCE_URL + builderPoint).build();
  53. http4BaiduApi(httpClient, uriRequest);
  54. } catch (IOException e) {
  55. // Handler IO Exception
  56. }
  57. }
  58. private static void http4BaiduApi(CloseableHttpClient httpClient, HttpUriRequest uriRequest) {
  59. StringBuilder builder = new StringBuilder();
  60. try (InputStream inputStream = httpClient.execute(uriRequest).getEntity().getContent()) {
  61. String line;
  62. BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
  63. while ((line = br.readLine()) != null) {
  64. builder.append(line);
  65. }
  66. System.out.println(builder.toString());
  67. } catch (IOException e) {
  68. // Handler IO Exception
  69. }
  70. }
  71. }

现在进行测试:

  1. public static void main(String[] args){
  2. getLngAndLat("上海市徐汇区零陵小区");
  3. }

输出结果为:

  1. result:{"status":0,"result":{"location":{"lng":121.45078363819293,"lat":31.193489388753848},"precise":1,"confidence":70,"comprehension":100,"level":"地产小区"}}

这样就可以得到具体某个位置的经纬度了,得到两个地区的经纬度后调用 twoLocationDistance(Double startLng, Double startLat, Double endLng, Double endLat) 方法来获取距离信息:

  1. public static void main(String[] args){
  2. twoLocationDistance(121.455244, 31.234076, 121.488301, 31.237534);
  3. }

可以看到具体数据结果为:{"status":"Success","results":[3166.3643236737]}。说明两点距离是 3166 米。

通过弧度角进行求取距离

现在来看下怎么自己通过计算地球弧度距离,直接看如下这个工具类:

  1. import java.math.BigDecimal;
  2. import java.math.RoundingMode;
  3. /**
  4. * 根据圆周率计算两点地图经纬度距离
  5. * </p>
  6. *
  7. * @author MinGRn <br > MinGRn97@gmail.com
  8. */
  9. public class PI4MapHelper {
  10. /**
  11. * 地球半径(km)
  12. */
  13. private static final double EARTH_RADIUS = 6378.137;
  14. /**
  15. * 等同于 Math.toRadians(), 将用角度表示的角转换为近似相等的用弧度表示的角
  16. * </p>
  17. * JS 没有 Math.toRadians(),如在 JS 直接使用该方法即可。
  18. *
  19. * <pre class="java">
  20. * Math.toRadians(startLat);
  21. * </pre>
  22. * <pre class="JavaScript">
  23. * radians(startLat)
  24. * </pre>
  25. *
  26. * @param angle 角度
  27. */
  28. private static double radians(double angle) {
  29. return angle * Math.PI / 180.0;
  30. }
  31. /**
  32. * 计算两个坐标点的距离
  33. * </p>
  34. * 距离四舍五入保留一位小数,单位KM
  35. *
  36. * @param startLng 起点经度
  37. * @param startLat 起点纬度
  38. * @param endLng 终点经度
  39. * @param endLat 终点纬度
  40. * @return 距离(千米)
  41. */
  42. private static double twoLocationDistance(double startLng, double startLat, double endLng, double endLat) {
  43. double startRadLat = Math.toRadians(startLat), endRadLat = Math.toRadians(endLat);
  44. double radLatDiff = startRadLat - endRadLat, radLngDiff = Math.toRadians(startLng) - Math.toRadians(endLng);
  45. double distance = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(radLatDiff / 2), 2) +
  46. Math.cos(startRadLat) * Math.cos(endRadLat) * Math.pow(Math.sin(radLngDiff / 2), 2)));
  47. distance = distance * EARTH_RADIUS;
  48. //distance = Math.round(distance * 10000) / 10000;
  49. //距离四舍五入保留一位小数
  50. distance = new BigDecimal(distance).setScale(1, RoundingMode.HALF_UP).doubleValue();
  51. return distance;
  52. }
  53. }

进行测试:

  1. public static void main(String[] args) {
  2. long startTime = System.currentTimeMillis();
  3. System.out.println("耗时:" + (System.currentTimeMillis() - startTime) + "毫秒");
  4. double dist = twoLocationDistance(121.455244, 31.234076, 121.488301, 31.237534);
  5. System.out.println("两点相距:" + dist + "千米");
  6. }

可以看到输出结果为:耗时:0毫秒 两点相距:3.2千米。可以看到两个结果相同,只是这里进行了四舍五入处理。所以,计算两个经纬度的距离可以直接使用该工具类进行计算。

另外,如果通过调用百度 API 进行求取需要发送 GET 请求,这里借助的工具是 httpclient 可以在 MAVEN 中央仓库进行下载:

  1. <dependency>
  2. <groupId>org.apache.httpcomponents</groupId>
  3. <artifactId>httpclient</artifactId>
  4. <version>4.5.6</version>
  5. </dependency>

参看资料: