Java根據(jù)坐標(biāo)經(jīng)緯度計算兩點距離5種方法及校驗經(jīng)緯度是否在圓/多邊形區(qū)域內(nèi)的算法推薦
前言
在開發(fā)項目中會用到根據(jù)兩點坐標(biāo)計算之間距離的算法,網(wǎng)上也找了很多的方法,多多少少會存在一些問題的。以下方法已經(jīng)在我本地運行通過,利用百度地圖拾取坐標(biāo)系統(tǒng)和百度地圖測距工具進(jìn)行測試,現(xiàn)將其整理了一下。以供大家參考:
一、根據(jù)坐標(biāo)經(jīng)緯度計算兩點距離
1.方法一
package com.test.java.util; /** * 坐標(biāo)位置相關(guān)util */ public class PositionUtil { /** * 赤道半徑(單位:米) */ private static final double EQUATOR_RADIUS = 6378137; /** * 方法一:(反余弦計算方式) * * @param longitude1 第一個點的經(jīng)度 * @param latitude1 第一個點的緯度 * @param longitude2 第二個點的經(jīng)度 * @param latitude2 第二個點的緯度 * @return 返回距離,單位m */ public static double getDistance1(double longitude1, double latitude1, double longitude2, double latitude2) { // 緯度 double lat1 = Math.toRadians(latitude1); double lat2 = Math.toRadians(latitude2); // 經(jīng)度 double lon1 = Math.toRadians(longitude1); double lon2 = Math.toRadians(longitude2); // 緯度之差 double a = lat1 - lat2; // 經(jīng)度之差 double b = lon1 - lon2; // 計算兩點距離的公式 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))); // 弧長乘赤道半徑, 返回單位: 米 s = s * EQUATOR_RADIUS; return s; } }
2.方法二
package com.test.java.util; /** * 坐標(biāo)位置相關(guān)util */ public class PositionUtil { /** * 地球平均半徑(單位:米) */ private static final double EARTH_AVG_RADIUS = 6371000; /** * 方法二:(反余弦計算方式) * * @param longitude1 第一點的經(jīng)度 * @param latitude1 第一點的緯度 * @param longitude2 第二點的經(jīng)度 * @param latitude2 第二點的緯度 * @return 返回的距離,單位m */ public static double getDistance3(double longitude1, double latitude1, double longitude2, double latitude2) { // 經(jīng)緯度(角度)轉(zhuǎn)弧度?;《茸鳛樽鲄?shù),用以調(diào)用Math.cos和Math.sin // A經(jīng)弧度 double radiansAX = Math.toRadians(longitude1); // A緯弧度 double radiansAY = Math.toRadians(latitude1); // B經(jīng)弧度 double radiansBX = Math.toRadians(longitude2); // B緯弧度 double radiansBY = Math.toRadians(latitude2); // 公式中“cosβ1cosβ2cos(α1-α2)+sinβ1sinβ2”的部分,得到∠AOB的cos值 double cos = Math.cos(radiansAY) * Math.cos(radiansBY) * Math.cos(radiansAX - radiansBX) + Math.sin(radiansAY) * Math.sin(radiansBY); // System.out.println("cos = " + cos); // 值域[-1,1] // 反余弦值 double acos = Math.acos(cos); // System.out.println("acos = " + acos); // 值域[0,π] // System.out.println("∠AOB = " + Math.toDegrees(acos)); // 球心角 值域[0,180] // 最終結(jié)果 return EARTH_AVG_RADIUS * acos; } }
3.方法三
基于谷歌地圖的計算公式計算距離
package com.test.java.util; /** * 坐標(biāo)位置相關(guān)util */ public class PositionUtil { /** * 地球平均半徑(單位:米) */ private static final double EARTH_AVG_RADIUS = 6371000; /** * 經(jīng)緯度轉(zhuǎn)化為弧度(rad) * * @param d 經(jīng)度/緯度 */ private static double rad(double d) { return d * Math.PI / 180.0; } /** * 方法三:(基于googleMap中的算法得到兩經(jīng)緯度之間的距離,計算精度與谷歌地圖的距離精度差不多。) * * @param longitude1 第一點的經(jīng)度 * @param latitude1 第一點的緯度 * @param longitude2 第二點的經(jīng)度 * @param latitude2 第二點的緯度 * @return 返回的距離,單位m */ public static double getDistance2(double longitude1, double latitude1, double longitude2, double latitude2) { double radLat1 = rad(latitude1); double radLat2 = rad(latitude2); double a = radLat1 - radLat2; double b = rad(longitude1) - rad(longitude2); 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_AVG_RADIUS; s = Math.round(s * 10000d) / 10000d; return s; } }
4.方法四
基于高德地圖
package com.test.java.util; /** * 計算距離 */ public class PositionUtil { /** * 方法四:(高德地圖計算方法) * * @param longitude1 第一點的經(jīng)度 * @param latitude1 第一點的緯度 * @param longitude2 第二點的經(jīng)度 * @param latitude2 第二點的緯度 * @return 返回的距離,單位m */ public static Double getDistance4(double longitude1, double latitude1, double longitude2, double latitude2) { if (longitude1 == 0 || latitude1 == 0 || latitude2 == 0 || longitude2 == 0) { return -1.0; } longitude1 *= 0.01745329251994329; latitude1 *= 0.01745329251994329; longitude2 *= 0.01745329251994329; latitude2 *= 0.01745329251994329; double var1 = Math.sin(longitude1); double var2 = Math.sin(latitude1); double var3 = Math.cos(longitude1); double var4 = Math.cos(latitude1); double var5 = Math.sin(longitude2); double var6 = Math.sin(latitude2); double var7 = Math.cos(longitude2); double var8 = Math.cos(latitude2); double[] var10 = new double[3]; double[] var20 = new double[3]; var10[0] = var4 * var3; var10[1] = var4 * var1; var10[2] = var2; var20[0] = var8 * var7; var20[1] = var8 * var5; var20[2] = var6; 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; // 結(jié)果四舍五入 保留2位小數(shù) //return new BigDecimal(distance).setScale(2, RoundingMode.HALF_UP).doubleValue(); } }
5.方法五
該方法是利用第三方j(luò)ar包計算
5.1 POM引入第三方依賴
<!--用于計算兩點之間的距離--> <dependency> <groupId>org.gavaghan</groupId> <artifactId>geodesy</artifactId> <version>1.1.3</version> </dependency>
5.2 代碼
package com.test.java.util; import org.gavaghan.geodesy.Ellipsoid; import org.gavaghan.geodesy.GeodeticCalculator; import org.gavaghan.geodesy.GeodeticCurve; import org.gavaghan.geodesy.GlobalCoordinates; /** * 坐標(biāo)位置相關(guān)util */ public class PositionUtil { /** * 方法四:(利用第三方j(luò)ar包計算) * 計算兩個經(jīng)緯度之間的距離 * * @param longitude1 第一點的經(jīng)度 * @param latitude1 第一點的緯度 * @param longitude2 第二點的經(jīng)度 * @param latitude2 第二點的緯度 * @param ellipsoid 計算方式 * @return 返回的距離,單位m */ public static double getDistance4(double longitude1, double latitude1, double longitude2, double latitude2, Ellipsoid ellipsoid) { // 創(chuàng)建GeodeticCalculator,調(diào)用計算方法,傳入坐標(biāo)系、經(jīng)緯度用于計算距離 GlobalCoordinates firstPoint = new GlobalCoordinates(latitude1, longitude1); GlobalCoordinates secondPoint = new GlobalCoordinates(latitude2, longitude2); GeodeticCurve geoCurve = new GeodeticCalculator().calculateGeodeticCurve(ellipsoid, firstPoint, secondPoint); return geoCurve.getEllipsoidalDistance(); } }
6.測試結(jié)果對比
這里我直接一起調(diào)用者4種方法,這樣看結(jié)果也更加直觀些。
public static void main(String[] args) { double longitude1 = 117.344733; double latitude1 = 31.912334; double longitude2 = 117.272186; double latitude2 = 31.79422; double distance1 = PositionUtil.getDistance1(longitude1, latitude1, longitude2, latitude2); double distance2 = PositionUtil.getDistance2(longitude1, latitude1, longitude2, latitude2); double distance3 = PositionUtil.getDistance3(longitude1, latitude1, longitude2, latitude2); double distance4 = PositionUtil.getDistance4(longitude1, latitude1, longitude2, latitude2); double distance5 = PositionUtil.getDistance4(longitude1, latitude1, longitude2, latitude2, Ellipsoid.Sphere); double distance6 = PositionUtil.getDistance4(longitude1, latitude1, longitude2, latitude2, Ellipsoid.WGS84); System.out.println("方法1算出的距離:" + distance1); System.out.println("方法2算出的距離:" + distance2); System.out.println("方法3算出的距離:" + distance3); System.out.println("方法4算出的距離:" + distance4); System.out.println("方法4-Sphere算出的距離:" + distance5); System.out.println("方法4-WGS84算出的距離:" + distance6); }
可以看出,這幾個方法算出的距離誤差相對較小。而且main方法中提供的測試數(shù)據(jù)也是我自身的真實數(shù)據(jù),結(jié)合百度地圖的測距工具進(jìn)行的測試。有需要的小伙伴,可以自行選擇合適的方法。
二、校驗經(jīng)緯度是否在制定區(qū)域內(nèi)
怎么樣判斷一個坐標(biāo)點在指定的區(qū)域內(nèi)?其中區(qū)域又會分為:圓,多邊形和不規(guī)則的多邊形。
1.判斷一個坐標(biāo)是否在圓形區(qū)域內(nèi)
計算這個坐標(biāo)點和圓心之間的距離,然后跟圓的半徑進(jìn)行比較,如果比半徑大,就不在圓形區(qū)域內(nèi),如果小于等于圓的半徑,則該坐標(biāo)點在圓形區(qū)域內(nèi)。
package com.test.java.util; import org.apache.commons.lang3.StringUtils; /** * 計算距離 */ public class PositionUtil { /** * 赤道半徑(單位:米) */ private static final double EQUATOR_RADIUS = 6378137; /** * 方法一:(反余弦計算方式) * * @param longitude1 第一個點的經(jīng)度 * @param latitude1 第一個點的緯度 * @param longitude2 第二個點的經(jīng)度 * @param latitude2 第二個點的緯度 * @return 返回距離,單位m */ public static double getDistance1(double longitude1, double latitude1, double longitude2, double latitude2) { // 緯度 double lat1 = Math.toRadians(latitude1); double lat2 = Math.toRadians(latitude2); // 經(jīng)度 double lon1 = Math.toRadians(longitude1); double lon2 = Math.toRadians(longitude2); // 緯度之差 double a = lat1 - lat2; // 經(jīng)度之差 double b = lon1 - lon2; // 計算兩點距離的公式 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))); // 弧長乘赤道半徑, 返回單位: 米 s = s * EQUATOR_RADIUS; return s; } /** * 判斷坐標(biāo)點是否在圓形區(qū)域內(nèi) * 計算這個坐標(biāo)點和圓心點之間的距離,然后跟圓的半徑進(jìn)行比較,如果比半徑大,就不在圓形區(qū)域內(nèi),如果小于等于圓的半徑,則該坐標(biāo)點在圓形區(qū)域內(nèi) * * @param longitude1 第一點的經(jīng)度 * @param latitude1 第一點的緯度 * @param longitude2 第二點的經(jīng)度 * @param latitude2 第二點的緯度 * @param radius 圓形范圍半徑(單位:米) * @return true:不在區(qū)域內(nèi); false:在區(qū)域內(nèi) */ public static boolean isInCircle(double longitude1, double latitude1, double longitude2, double latitude2, String radius) { if (StringUtils.isBlank(radius)) { throw new RuntimeException("請輸入范圍半徑"); } return getDistance1(longitude1, latitude1, longitude2, latitude2) > Double.parseDouble(radius); } }
2.判斷一個坐標(biāo)是否在一個多邊形區(qū)域內(nèi)
這里用到JAVA的一個類GeneralPath(由直線和二次和三次(B?zier)曲線構(gòu)成的幾何路徑。 它可以包含多個子路徑)使用這個類,結(jié)合傳入的各頂點參數(shù),畫一個幾何圖形,并通過它自身的contains方法,判斷該點是否在這個幾何圖形內(nèi)。
package com.test.java.util; import org.apache.commons.lang3.StringUtils; import java.awt.geom.GeneralPath; import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.List; /** * 計算距離 */ public class PositionUtil { /** * 判斷坐標(biāo)點是否在多邊形區(qū)域內(nèi) * * @param pointLon 要判斷的點的經(jīng)度 * @param pointLat 要判斷的點的緯度 * @param lon 區(qū)域各頂點的經(jīng)度數(shù)組 * @param lat 區(qū)域各頂點的緯度數(shù)組 * @return true:范圍內(nèi); false:范圍外 */ public static boolean isInPolygon(double pointLon, double pointLat, double[] lon, double[] lat) { // 將要判斷的橫縱坐標(biāo)組成一個點 Point2D.Double point = new Point2D.Double(pointLon, pointLat); // 將區(qū)域各頂點的橫縱坐標(biāo)放到一個點集合里面 List<Point2D.Double> pointList = new ArrayList<>(); double polygonPointToX; double polygonPointToY; for (int i = 0; i < lon.length; i++) { polygonPointToX = lon[i]; polygonPointToY = lat[i]; Point2D.Double polygonPoint = new Point2D.Double(polygonPointToX, polygonPointToY); pointList.add(polygonPoint); } return check(point, pointList); } /** * 坐標(biāo)點是否在多邊形內(nèi) * * @param point 要判斷的點的橫縱坐標(biāo) * @param polygon 組成的頂點坐標(biāo)集合 */ private static boolean check(Point2D.Double point, List<Point2D.Double> polygon) { GeneralPath generalPath = new GeneralPath(); Point2D.Double first = polygon.get(0); // 通過移動到指定坐標(biāo)(以雙精度指定),將一個點添加到路徑中 generalPath.moveTo(first.x, first.y); polygon.remove(0); for (Point2D.Double d : polygon) { // 通過繪制一條從當(dāng)前坐標(biāo)到新指定坐標(biāo)(以雙精度指定)的直線,將一個點添加到路徑中。 generalPath.lineTo(d.x, d.y); } // 將幾何多邊形封閉 generalPath.lineTo(first.x, first.y); generalPath.closePath(); // 測試指定的 Point2D 是否在 Shape 的邊界內(nèi)。 return generalPath.contains(point); } }
3.結(jié)果
public static void main(String[] args) { double distance1 = PositionUtil.getDistance1(longitude1, latitude1, longitude2, latitude2); System.out.println("坐標(biāo)與圓心的距離:" + distance1); String radius1 = "10000"; boolean inCircle1 = PositionUtil.isInCircle(longitude1, latitude1, longitude2, latitude2, radius1); System.out.println("校驗坐標(biāo)是否在圓形范圍內(nèi):" + inCircle1); String radius = "15000"; boolean inCircle2 = PositionUtil.isInCircle(longitude1, latitude1, longitude2, latitude2, radius); System.out.println("校驗坐標(biāo)是否在圓形范圍內(nèi):" + inCircle2); double pointLon = 117.274984; double pointLat = 31.790718; // 坐標(biāo)在多邊形范圍內(nèi)的參數(shù): double[] lon = {117.272559, 117.276224, 117.278649, 117.273924}; double[] lat = {31.791247, 31.792812, 31.78982, 31.788539}; // 坐標(biāo)在多邊形范圍外的參數(shù): double[] lon1 = {117.291001, 117.299705, 117.298035, 117.291216}; double[] lat1 = {31.806576, 31.806814, 31.802319, 31.802196}; boolean a = PositionUtil.isInPolygon(pointLon, pointLat, lon, lat); boolean b = PositionUtil.isInPolygon(pointLon, pointLat, lon1, lat1); System.out.println("校驗坐標(biāo)是否在多邊形范圍內(nèi):" + a); System.out.println("校驗坐標(biāo)是否在多邊形范圍內(nèi):" + b); }
總結(jié)
這樣的計算方式得到的距離并非是真實的距離,可以說是邏輯距離(直線距離),但其距離也已經(jīng)很準(zhǔn)確。不過畢竟是通過邏輯計算得到的距離,若要求高準(zhǔn)確性的距離信息的話,還是借助第三方的地圖api接口獲取比較合適。
到此這篇關(guān)于Java根據(jù)坐標(biāo)經(jīng)緯度計算兩點距離5種方法及校驗經(jīng)緯度是否在圓/多邊形區(qū)域內(nèi)的算法推薦的文章就介紹到這了,更多相關(guān)Java根據(jù)坐標(biāo)經(jīng)緯度計算兩點距離內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
idea中acitviti使用acitBPM插件出現(xiàn)亂碼問題及解決方法
這篇文章主要介紹了idea中acitviti使用acitBPM插件出現(xiàn)亂碼問題及解決方法,通過將File Encodings內(nèi)容設(shè)置為UTF-8,本文通過圖文展示,需要的朋友可以參考下2021-06-06Java實現(xiàn)人機(jī)對戰(zhàn)猜拳游戲
這篇文章主要為大家詳細(xì)介紹了Java實現(xiàn)人機(jī)對戰(zhàn)猜拳游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-07-072024版本IDEA創(chuàng)建Servlet模板的圖文教程
新版IDEA?2024.1.4中,用戶需要自行創(chuàng)建Servlet模板以解決Web項目無法通過右鍵創(chuàng)建Servlet的問題,本文詳細(xì)介紹了添加ServletAnnotatedClass.java模板的步驟,幫助用戶快速配置并使用新的Servlet模板,需要的朋友可以參考下2024-10-10java連接池Druid獲取連接getConnection示例詳解
這篇文章主要為大家介紹了java連接池Druid獲取連接getConnection示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09