当前位置:   article > 正文

Java根据坐标经纬度计算两点距离(5种方法)、校验经纬度是否在圆/多边形区域内的算法推荐_java根据经纬度计算距离

java根据经纬度计算距离

目录

前言

一、根据坐标经纬度计算两点距离(5种方法)

1.方法一

2.方法二

3.方法三

4.方法四

5.方法五

5.1 POM引入第三方依赖

5.2 代码

6.测试结果对比

二、校验经纬度是否在制定区域内

1.判断一个坐标是否在圆形区域内

2.判断一个坐标是否在一个多边形区域内

3.结果

总结


前言

        在开发项目中会用到根据两点坐标计算之间距离的算法,网上也找了很多的方法,多多少少会存在一些问题的。以下方法已经在我本地运行通过,利用百度地图拾取坐标系统和百度地图测距工具进行测试,现将其整理了一下。以供大家参考:


一、根据坐标经纬度计算两点距离

1.方法一

  1. package com.test.java.util;
  2. /**
  3. * 坐标位置相关util
  4. */
  5. public class PositionUtil {
  6. /**
  7. * 赤道半径(单位:米)
  8. */
  9. private static final double EQUATOR_RADIUS = 6378137;
  10. /**
  11. * 方法一:(反余弦计算方式)
  12. *
  13. * @param longitude1 第一个点的经度
  14. * @param latitude1 第一个点的纬度
  15. * @param longitude2 第二个点的经度
  16. * @param latitude2 第二个点的纬度
  17. * @return 返回距离,单位m
  18. */
  19. public static double getDistance1(double longitude1, double latitude1, double longitude2, double latitude2) {
  20. // 纬度
  21. double lat1 = Math.toRadians(latitude1);
  22. double lat2 = Math.toRadians(latitude2);
  23. // 经度
  24. double lon1 = Math.toRadians(longitude1);
  25. double lon2 = Math.toRadians(longitude2);
  26. // 纬度之差
  27. double a = lat1 - lat2;
  28. // 经度之差
  29. double b = lon1 - lon2;
  30. // 计算两点距离的公式
  31. double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(b / 2), 2)));
  32. // 弧长乘赤道半径, 返回单位: 米
  33. s = s * EQUATOR_RADIUS;
  34. return s;
  35. }
  36. }

2.方法二

  1. package com.test.java.util;
  2. /**
  3. * 坐标位置相关util
  4. */
  5. public class PositionUtil {
  6. /**
  7. * 地球平均半径(单位:米)
  8. */
  9. private static final double EARTH_AVG_RADIUS = 6371000;
  10. /**
  11. * 方法二:(反余弦计算方式)
  12. *
  13. * @param longitude1 第一点的经度
  14. * @param latitude1 第一点的纬度
  15. * @param longitude2 第二点的经度
  16. * @param latitude2 第二点的纬度
  17. * @return 返回的距离,单位m
  18. */
  19. public static double getDistance3(double longitude1, double latitude1, double longitude2, double latitude2) {
  20. // 经纬度(角度)转弧度。弧度作为作参数,用以调用Math.cos和Math.sin
  21. // A经弧度
  22. double radiansAX = Math.toRadians(longitude1);
  23. // A纬弧度
  24. double radiansAY = Math.toRadians(latitude1);
  25. // B经弧度
  26. double radiansBX = Math.toRadians(longitude2);
  27. // B纬弧度
  28. double radiansBY = Math.toRadians(latitude2);
  29. // 公式中“cosβ1cosβ2cos(α1-α2)+sinβ1sinβ2”的部分,得到∠AOB的cos值
  30. double cos = Math.cos(radiansAY) * Math.cos(radiansBY) * Math.cos(radiansAX - radiansBX) + Math.sin(radiansAY) * Math.sin(radiansBY);
  31. // System.out.println("cos = " + cos); // 值域[-1,1]
  32. // 反余弦值
  33. double acos = Math.acos(cos);
  34. // System.out.println("acos = " + acos); // 值域[0,π]
  35. // System.out.println("∠AOB = " + Math.toDegrees(acos)); // 球心角 值域[0,180]
  36. // 最终结果
  37. return EARTH_AVG_RADIUS * acos;
  38. }
  39. }

3.方法三

基于谷歌地图的计算公式计算距离

  1. package com.test.java.util;
  2. /**
  3. * 坐标位置相关util
  4. */
  5. public class PositionUtil {
  6. /**
  7. * 地球平均半径(单位:米)
  8. */
  9. private static final double EARTH_AVG_RADIUS = 6371000;
  10. /**
  11. * 经纬度转化为弧度(rad)
  12. *
  13. * @param d 经度/纬度
  14. */
  15. private static double rad(double d) {
  16. return d * Math.PI / 180.0;
  17. }
  18. /**
  19. * 方法三:(基于googleMap中的算法得到两经纬度之间的距离,计算精度与谷歌地图的距离精度差不多。)
  20. *
  21. * @param longitude1 第一点的经度
  22. * @param latitude1 第一点的纬度
  23. * @param longitude2 第二点的经度
  24. * @param latitude2 第二点的纬度
  25. * @return 返回的距离,单位m
  26. */
  27. public static double getDistance2(double longitude1, double latitude1, double longitude2, double latitude2) {
  28. double radLat1 = rad(latitude1);
  29. double radLat2 = rad(latitude2);
  30. double a = radLat1 - radLat2;
  31. double b = rad(longitude1) - rad(longitude2);
  32. 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)));
  33. s = s * EARTH_AVG_RADIUS;
  34. s = Math.round(s * 10000d) / 10000d;
  35. return s;
  36. }
  37. }

4.方法四

基于高德地图

  1. package com.test.java.util;
  2. /**
  3. * 计算距离
  4. */
  5. public class PositionUtil {
  6. /**
  7. * 方法四:(高德地图计算方法)
  8. *
  9. * @param longitude1 第一点的经度
  10. * @param latitude1 第一点的纬度
  11. * @param longitude2 第二点的经度
  12. * @param latitude2 第二点的纬度
  13. * @return 返回的距离,单位m
  14. */
  15. public static Double getDistance4(double longitude1, double latitude1, double longitude2, double latitude2) {
  16. if (longitude1 == 0 || latitude1 == 0 || latitude2 == 0 || longitude2 == 0) {
  17. return -1.0;
  18. }
  19. longitude1 *= 0.01745329251994329;
  20. latitude1 *= 0.01745329251994329;
  21. longitude2 *= 0.01745329251994329;
  22. latitude2 *= 0.01745329251994329;
  23. double var1 = Math.sin(longitude1);
  24. double var2 = Math.sin(latitude1);
  25. double var3 = Math.cos(longitude1);
  26. double var4 = Math.cos(latitude1);
  27. double var5 = Math.sin(longitude2);
  28. double var6 = Math.sin(latitude2);
  29. double var7 = Math.cos(longitude2);
  30. double var8 = Math.cos(latitude2);
  31. double[] var10 = new double[3];
  32. double[] var20 = new double[3];
  33. var10[0] = var4 * var3;
  34. var10[1] = var4 * var1;
  35. var10[2] = var2;
  36. var20[0] = var8 * var7;
  37. var20[1] = var8 * var5;
  38. var20[2] = var6;
  39. return Math.asin(Math.sqrt((var10[0] - var20[0]) * (var10[0] - var20[0]) + (var10[1] - var20[1]) * (var10[1] - var20[1]) + (var10[2] - var20[2]) * (var10[2] - var20[2])) / 2.0) * 1.27420015798544E7;
  40. // 结果四舍五入 保留2位小数
  41. //return new BigDecimal(distance).setScale(2, RoundingMode.HALF_UP).doubleValue();
  42. }
  43. }

5.方法五

该方法是利用第三方jar包计算

5.1 POM引入第三方依赖

  1. <!--用于计算两点之间的距离-->
  2. <dependency>
  3. <groupId>org.gavaghan</groupId>
  4. <artifactId>geodesy</artifactId>
  5. <version>1.1.3</version>
  6. </dependency>

5.2 代码

  1. package com.test.java.util;
  2. import org.gavaghan.geodesy.Ellipsoid;
  3. import org.gavaghan.geodesy.GeodeticCalculator;
  4. import org.gavaghan.geodesy.GeodeticCurve;
  5. import org.gavaghan.geodesy.GlobalCoordinates;
  6. /**
  7. * 坐标位置相关util
  8. */
  9. public class PositionUtil {
  10. /**
  11. * 方法四:(利用第三方jar包计算)
  12. * 计算两个经纬度之间的距离
  13. *
  14. * @param longitude1 第一点的经度
  15. * @param latitude1 第一点的纬度
  16. * @param longitude2 第二点的经度
  17. * @param latitude2 第二点的纬度
  18. * @param ellipsoid 计算方式
  19. * @return 返回的距离,单位m
  20. */
  21. public static double getDistance4(double longitude1, double latitude1, double longitude2, double latitude2, Ellipsoid ellipsoid) {
  22. // 创建GeodeticCalculator,调用计算方法,传入坐标系、经纬度用于计算距离
  23. GlobalCoordinates firstPoint = new GlobalCoordinates(latitude1, longitude1);
  24. GlobalCoordinates secondPoint = new GlobalCoordinates(latitude2, longitude2);
  25. GeodeticCurve geoCurve = new GeodeticCalculator().calculateGeodeticCurve(ellipsoid, firstPoint, secondPoint);
  26. return geoCurve.getEllipsoidalDistance();
  27. }
  28. }

6.测试结果对比

这里我直接一起调用者4种方法,这样看结果也更加直观些。

  1. public static void main(String[] args) {
  2. double longitude1 = 117.344733;
  3. double latitude1 = 31.912334;
  4. double longitude2 = 117.272186;
  5. double latitude2 = 31.79422;
  6. double distance1 = PositionUtil.getDistance1(longitude1, latitude1, longitude2, latitude2);
  7. double distance2 = PositionUtil.getDistance2(longitude1, latitude1, longitude2, latitude2);
  8. double distance3 = PositionUtil.getDistance3(longitude1, latitude1, longitude2, latitude2);
  9. double distance4 = PositionUtil.getDistance4(longitude1, latitude1, longitude2, latitude2);
  10. double distance5 = PositionUtil.getDistance4(longitude1, latitude1, longitude2, latitude2, Ellipsoid.Sphere);
  11. double distance6 = PositionUtil.getDistance4(longitude1, latitude1, longitude2, latitude2, Ellipsoid.WGS84);
  12. System.out.println("方法1算出的距离:" + distance1);
  13. System.out.println("方法2算出的距离:" + distance2);
  14. System.out.println("方法3算出的距离:" + distance3);
  15. System.out.println("方法4算出的距离:" + distance4);
  16. System.out.println("方法4-Sphere算出的距离:" + distance5);
  17. System.out.println("方法4-WGS84算出的距离:" + distance6);
  18. }

可以看出,这几个方法算出的距离误差相对较小。而且main方法中提供的测试数据也是我自身的真实数据,结合百度地图的测距工具进行的测试。有需要的小伙伴,可以自行选择合适的方法。

二、校验经纬度是否在制定区域内

怎么样判断一个坐标点在指定的区域内?其中区域又会分为:圆,多边形和不规则的多边形。

1.判断一个坐标是否在圆形区域内

计算这个坐标点和圆心之间的距离,然后跟圆的半径进行比较,如果比半径大,就不在圆形区域内,如果小于等于圆的半径,则该坐标点在圆形区域内。

  1. package com.test.java.util;
  2. import org.apache.commons.lang3.StringUtils;
  3. /**
  4. * 计算距离
  5. */
  6. public class PositionUtil {
  7. /**
  8. * 赤道半径(单位:米)
  9. */
  10. private static final double EQUATOR_RADIUS = 6378137;
  11. /**
  12. * 方法一:(反余弦计算方式)
  13. *
  14. * @param longitude1 第一个点的经度
  15. * @param latitude1 第一个点的纬度
  16. * @param longitude2 第二个点的经度
  17. * @param latitude2 第二个点的纬度
  18. * @return 返回距离,单位m
  19. */
  20. public static double getDistance1(double longitude1, double latitude1, double longitude2, double latitude2) {
  21. // 纬度
  22. double lat1 = Math.toRadians(latitude1);
  23. double lat2 = Math.toRadians(latitude2);
  24. // 经度
  25. double lon1 = Math.toRadians(longitude1);
  26. double lon2 = Math.toRadians(longitude2);
  27. // 纬度之差
  28. double a = lat1 - lat2;
  29. // 经度之差
  30. double b = lon1 - lon2;
  31. // 计算两点距离的公式
  32. double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(b / 2), 2)));
  33. // 弧长乘赤道半径, 返回单位: 米
  34. s = s * EQUATOR_RADIUS;
  35. return s;
  36. }
  37. /**
  38. * 判断坐标点是否在圆形区域内
  39. * 计算这个坐标点和圆心点之间的距离,然后跟圆的半径进行比较,如果比半径大,就不在圆形区域内,如果小于等于圆的半径,则该坐标点在圆形区域内
  40. *
  41. * @param longitude1 第一点的经度
  42. * @param latitude1 第一点的纬度
  43. * @param longitude2 第二点的经度
  44. * @param latitude2 第二点的纬度
  45. * @param radius 圆形范围半径(单位:米)
  46. * @return true:不在区域内; false:在区域内
  47. */
  48. public static boolean isInCircle(double longitude1, double latitude1, double longitude2, double latitude2, String radius) {
  49. if (StringUtils.isBlank(radius)) {
  50. throw new RuntimeException("请输入范围半径");
  51. }
  52. return getDistance1(longitude1, latitude1, longitude2, latitude2) > Double.parseDouble(radius);
  53. }
  54. }

2.判断一个坐标是否在一个多边形区域内

这里用到JAVA的一个类GeneralPath(由直线和二次和三次(B?zier)曲线构成的几何路径。 它可以包含多个子路径)使用这个类,结合传入的各顶点参数,画一个几何图形,并通过它自身的contains方法,判断该点是否在这个几何图形内。

  1. package com.test.java.util;
  2. import org.apache.commons.lang3.StringUtils;
  3. import java.awt.geom.GeneralPath;
  4. import java.awt.geom.Point2D;
  5. import java.util.ArrayList;
  6. import java.util.List;
  7. /**
  8. * 计算距离
  9. */
  10. public class PositionUtil {
  11. /**
  12. * 判断坐标点是否在多边形区域内
  13. *
  14. * @param pointLon 要判断的点的经度
  15. * @param pointLat 要判断的点的纬度
  16. * @param lon 区域各顶点的经度数组
  17. * @param lat 区域各顶点的纬度数组
  18. * @return true:范围内; false:范围外
  19. */
  20. public static boolean isInPolygon(double pointLon, double pointLat, double[] lon, double[] lat) {
  21. // 将要判断的横纵坐标组成一个点
  22. Point2D.Double point = new Point2D.Double(pointLon, pointLat);
  23. // 将区域各顶点的横纵坐标放到一个点集合里面
  24. List<Point2D.Double> pointList = new ArrayList<>();
  25. double polygonPointToX;
  26. double polygonPointToY;
  27. for (int i = 0; i < lon.length; i++) {
  28. polygonPointToX = lon[i];
  29. polygonPointToY = lat[i];
  30. Point2D.Double polygonPoint = new Point2D.Double(polygonPointToX, polygonPointToY);
  31. pointList.add(polygonPoint);
  32. }
  33. return check(point, pointList);
  34. }
  35. /**
  36. * 坐标点是否在多边形内
  37. *
  38. * @param point 要判断的点的横纵坐标
  39. * @param polygon 组成的顶点坐标集合
  40. */
  41. private static boolean check(Point2D.Double point, List<Point2D.Double> polygon) {
  42. GeneralPath generalPath = new GeneralPath();
  43. Point2D.Double first = polygon.get(0);
  44. // 通过移动到指定坐标(以双精度指定),将一个点添加到路径中
  45. generalPath.moveTo(first.x, first.y);
  46. polygon.remove(0);
  47. for (Point2D.Double d : polygon) {
  48. // 通过绘制一条从当前坐标到新指定坐标(以双精度指定)的直线,将一个点添加到路径中。
  49. generalPath.lineTo(d.x, d.y);
  50. }
  51. // 将几何多边形封闭
  52. generalPath.lineTo(first.x, first.y);
  53. generalPath.closePath();
  54. // 测试指定的 Point2D 是否在 Shape 的边界内。
  55. return generalPath.contains(point);
  56. }
  57. }

3.结果

  1. public static void main(String[] args) {
  2. double distance1 = PositionUtil.getDistance1(longitude1, latitude1, longitude2, latitude2);
  3. System.out.println("坐标与圆心的距离:" + distance1);
  4. String radius1 = "10000";
  5. boolean inCircle1 = PositionUtil.isInCircle(longitude1, latitude1, longitude2, latitude2, radius1);
  6. System.out.println("校验坐标是否在圆形范围内:" + inCircle1);
  7. String radius = "15000";
  8. boolean inCircle2 = PositionUtil.isInCircle(longitude1, latitude1, longitude2, latitude2, radius);
  9. System.out.println("校验坐标是否在圆形范围内:" + inCircle2);
  10. double pointLon = 117.274984;
  11. double pointLat = 31.790718;
  12. // 坐标在多边形范围内的参数:
  13. double[] lon = {117.272559, 117.276224, 117.278649, 117.273924};
  14. double[] lat = {31.791247, 31.792812, 31.78982, 31.788539};
  15. // 坐标在多边形范围外的参数:
  16. double[] lon1 = {117.291001, 117.299705, 117.298035, 117.291216};
  17. double[] lat1 = {31.806576, 31.806814, 31.802319, 31.802196};
  18. boolean a = PositionUtil.isInPolygon(pointLon, pointLat, lon, lat);
  19. boolean b = PositionUtil.isInPolygon(pointLon, pointLat, lon1, lat1);
  20. System.out.println("校验坐标是否在多边形范围内:" + a);
  21. System.out.println("校验坐标是否在多边形范围内:" + b);
  22. }

 


总结

        这样的计算方式得到的距离并非是真实的距离,可以说是逻辑距离(直线距离),但其距离也已经很准确。不过毕竟是通过逻辑计算得到的距离,若要求高准确性的距离信息的话,还是借助第三方的地图api接口获取比较合适。

如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、评论、收藏➕关注,您的支持是我坚持写作最大的动力。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/空白诗007/article/detail/736613
推荐阅读
相关标签
  

闽ICP备14008679号