目錄
前言
一、根據(jù)坐標(biāo)經(jīng)緯度計(jì)算兩點(diǎn)距離(5種方法)
1.方法一
2.方法二
3.方法三
4.方法四
5.方法五
5.1 POM引入第三方依賴
5.2 代碼
6.測(cè)試結(jié)果對(duì)比
二、校驗(yàn)經(jīng)緯度是否在制定區(qū)域內(nèi)
1.判斷一個(gè)坐標(biāo)是否在圓形區(qū)域內(nèi)
2.判斷一個(gè)坐標(biāo)是否在一個(gè)多邊形區(qū)域內(nèi)
3.結(jié)果
總結(jié)
前言
????????在開發(fā)項(xiàng)目中會(huì)用到根據(jù)兩點(diǎn)坐標(biāo)計(jì)算之間距離的算法,網(wǎng)上也找了很多的方法,多多少少會(huì)存在一些問題的。以下方法已經(jīng)在我本地運(yùn)行通過,利用百度地圖拾取坐標(biāo)系統(tǒng)和百度地圖測(cè)距工具進(jìn)行測(cè)試,現(xiàn)將其整理了一下。以供大家參考:
一、根據(jù)坐標(biāo)經(jīng)緯度計(jì)算兩點(diǎn)距離
1.方法一
package com.test.java.util;
/**
* 坐標(biāo)位置相關(guān)util
*/
public class PositionUtil {
/**
* 赤道半徑(單位:米)
*/
private static final double EQUATOR_RADIUS = 6378137;
/**
* 方法一:(反余弦計(jì)算方式)
*
* @param longitude1 第一個(gè)點(diǎn)的經(jīng)度
* @param latitude1 第一個(gè)點(diǎn)的緯度
* @param longitude2 第二個(gè)點(diǎn)的經(jīng)度
* @param latitude2 第二個(gè)點(diǎn)的緯度
* @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;
// 計(jì)算兩點(diǎn)距離的公式
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)));
// 弧長(zhǎng)乘赤道半徑, 返回單位: 米
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;
/**
* 方法二:(反余弦計(jì)算方式)
*
* @param longitude1 第一點(diǎn)的經(jīng)度
* @param latitude1 第一點(diǎn)的緯度
* @param longitude2 第二點(diǎn)的經(jīng)度
* @param latitude2 第二點(diǎn)的緯度
* @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.方法三
基于谷歌地圖的計(jì)算公式計(jì)算距離
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)緯度之間的距離,計(jì)算精度與谷歌地圖的距離精度差不多。)
*
* @param longitude1 第一點(diǎn)的經(jīng)度
* @param latitude1 第一點(diǎn)的緯度
* @param longitude2 第二點(diǎn)的經(jīng)度
* @param latitude2 第二點(diǎn)的緯度
* @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;
/**
* 計(jì)算距離
*/
public class PositionUtil {
/**
* 方法四:(高德地圖計(jì)算方法)
*
* @param longitude1 第一點(diǎn)的經(jīng)度
* @param latitude1 第一點(diǎn)的緯度
* @param longitude2 第二點(diǎn)的經(jīng)度
* @param latitude2 第二點(diǎn)的緯度
* @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包計(jì)算
5.1 POM引入第三方依賴
<!--用于計(jì)算兩點(diǎn)之間的距離-->
<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ì)算)
* 計(jì)算兩個(gè)經(jīng)緯度之間的距離
*
* @param longitude1 第一點(diǎn)的經(jīng)度
* @param latitude1 第一點(diǎn)的緯度
* @param longitude2 第二點(diǎn)的經(jīng)度
* @param latitude2 第二點(diǎn)的緯度
* @param ellipsoid 計(jì)算方式
* @return 返回的距離,單位m
*/
public static double getDistance4(double longitude1, double latitude1, double longitude2, double latitude2, Ellipsoid ellipsoid) {
// 創(chuàng)建GeodeticCalculator,調(diào)用計(jì)算方法,傳入坐標(biāo)系、經(jīng)緯度用于計(jì)算距離
GlobalCoordinates firstPoint = new GlobalCoordinates(latitude1, longitude1);
GlobalCoordinates secondPoint = new GlobalCoordinates(latitude2, longitude2);
GeodeticCurve geoCurve = new GeodeticCalculator().calculateGeodeticCurve(ellipsoid, firstPoint, secondPoint);
return geoCurve.getEllipsoidalDistance();
}
}
6.測(cè)試結(jié)果對(duì)比
這里我直接一起調(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);
}
可以看出,這幾個(gè)方法算出的距離誤差相對(duì)較小。而且main方法中提供的測(cè)試數(shù)據(jù)也是我自身的真實(shí)數(shù)據(jù),結(jié)合百度地圖的測(cè)距工具進(jìn)行的測(cè)試。有需要的小伙伴,可以自行選擇合適的方法。
二、校驗(yàn)經(jīng)緯度是否在制定區(qū)域內(nèi)
怎么樣判斷一個(gè)坐標(biāo)點(diǎn)在指定的區(qū)域內(nèi)?其中區(qū)域又會(huì)分為:圓,多邊形和不規(guī)則的多邊形。
1.判斷一個(gè)坐標(biāo)是否在圓形區(qū)域內(nèi)
計(jì)算這個(gè)坐標(biāo)點(diǎn)和圓心之間的距離,然后跟圓的半徑進(jìn)行比較,如果比半徑大,就不在圓形區(qū)域內(nèi),如果小于等于圓的半徑,則該坐標(biāo)點(diǎn)在圓形區(qū)域內(nèi)。
package com.test.java.util;
import org.apache.commons.lang3.StringUtils;
/**
* 計(jì)算距離
*/
public class PositionUtil {
/**
* 赤道半徑(單位:米)
*/
private static final double EQUATOR_RADIUS = 6378137;
/**
* 方法一:(反余弦計(jì)算方式)
*
* @param longitude1 第一個(gè)點(diǎn)的經(jīng)度
* @param latitude1 第一個(gè)點(diǎn)的緯度
* @param longitude2 第二個(gè)點(diǎn)的經(jīng)度
* @param latitude2 第二個(gè)點(diǎn)的緯度
* @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;
// 計(jì)算兩點(diǎn)距離的公式
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)));
// 弧長(zhǎng)乘赤道半徑, 返回單位: 米
s = s * EQUATOR_RADIUS;
return s;
}
/**
* 判斷坐標(biāo)點(diǎn)是否在圓形區(qū)域內(nèi)
* 計(jì)算這個(gè)坐標(biāo)點(diǎn)和圓心點(diǎn)之間的距離,然后跟圓的半徑進(jìn)行比較,如果比半徑大,就不在圓形區(qū)域內(nèi),如果小于等于圓的半徑,則該坐標(biāo)點(diǎn)在圓形區(qū)域內(nèi)
*
* @param longitude1 第一點(diǎn)的經(jīng)度
* @param latitude1 第一點(diǎn)的緯度
* @param longitude2 第二點(diǎn)的經(jīng)度
* @param latitude2 第二點(diǎn)的緯度
* @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("請(qǐng)輸入范圍半徑");
}
return getDistance1(longitude1, latitude1, longitude2, latitude2) > Double.parseDouble(radius);
}
}
2.判斷一個(gè)坐標(biāo)是否在一個(gè)多邊形區(qū)域內(nèi)
這里用到JAVA的一個(gè)類GeneralPath(由直線和二次和三次(B?zier)曲線構(gòu)成的幾何路徑。?它可以包含多個(gè)子路徑)使用這個(gè)類,結(jié)合傳入的各頂點(diǎn)參數(shù),畫一個(gè)幾何圖形,并通過它自身的contains方法,判斷該點(diǎn)是否在這個(gè)幾何圖形內(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;
/**
* 計(jì)算距離
*/
public class PositionUtil {
/**
* 判斷坐標(biāo)點(diǎn)是否在多邊形區(qū)域內(nèi)
*
* @param pointLon 要判斷的點(diǎn)的經(jīng)度
* @param pointLat 要判斷的點(diǎn)的緯度
* @param lon 區(qū)域各頂點(diǎn)的經(jīng)度數(shù)組
* @param lat 區(qū)域各頂點(diǎn)的緯度數(shù)組
* @return true:范圍內(nèi); false:范圍外
*/
public static boolean isInPolygon(double pointLon, double pointLat, double[] lon, double[] lat) {
// 將要判斷的橫縱坐標(biāo)組成一個(gè)點(diǎn)
Point2D.Double point = new Point2D.Double(pointLon, pointLat);
// 將區(qū)域各頂點(diǎn)的橫縱坐標(biāo)放到一個(gè)點(diǎn)集合里面
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)點(diǎn)是否在多邊形內(nèi)
*
* @param point 要判斷的點(diǎn)的橫縱坐標(biāo)
* @param polygon 組成的頂點(diǎn)坐標(biāo)集合
*/
private static boolean check(Point2D.Double point, List<Point2D.Double> polygon) {
GeneralPath generalPath = new GeneralPath();
Point2D.Double first = polygon.get(0);
// 通過移動(dòng)到指定坐標(biāo)(以雙精度指定),將一個(gè)點(diǎn)添加到路徑中
generalPath.moveTo(first.x, first.y);
polygon.remove(0);
for (Point2D.Double d : polygon) {
// 通過繪制一條從當(dāng)前坐標(biāo)到新指定坐標(biāo)(以雙精度指定)的直線,將一個(gè)點(diǎn)添加到路徑中。
generalPath.lineTo(d.x, d.y);
}
// 將幾何多邊形封閉
generalPath.lineTo(first.x, first.y);
generalPath.closePath();
// 測(cè)試指定的 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("校驗(yàn)坐標(biāo)是否在圓形范圍內(nèi):" + inCircle1);
String radius = "15000";
boolean inCircle2 = PositionUtil.isInCircle(longitude1, latitude1, longitude2, latitude2, radius);
System.out.println("校驗(yàn)坐標(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("校驗(yàn)坐標(biāo)是否在多邊形范圍內(nèi):" + a);
System.out.println("校驗(yàn)坐標(biāo)是否在多邊形范圍內(nèi):" + b);
}
?
總結(jié)
????????這樣的計(jì)算方式得到的距離并非是真實(shí)的距離,可以說是邏輯距離(直線距離),但其距離也已經(jīng)很準(zhǔn)確。不過畢竟是通過邏輯計(jì)算得到的距離,若要求高準(zhǔn)確性的距離信息的話,還是借助第三方的地圖api接口獲取比較合適。文章來源:http://www.zghlxwxcb.cn/news/detail-690868.html
如果這篇文章對(duì)您有所幫助,或者有所啟發(fā)的話,求一鍵三連:點(diǎn)贊、評(píng)論、收藏?關(guān)注,您的支持是我堅(jiān)持寫作最大的動(dòng)力。文章來源地址http://www.zghlxwxcb.cn/news/detail-690868.html
到了這里,關(guān)于Java根據(jù)坐標(biāo)經(jīng)緯度計(jì)算兩點(diǎn)距離(5種方法)、校驗(yàn)經(jīng)緯度是否在圓/多邊形區(qū)域內(nèi)的算法推薦的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!