- java版源碼
- js版源碼
- 在線繪制預(yù)覽效果
- 關(guān)于計算的精確度
前些時間在更新我的坐標(biāo)邊界查詢工具的時候,需要用到經(jīng)緯度坐標(biāo)點的距離計算,和以坐標(biāo)點為中心生成一個指定距離為半徑的圓,搜了一下沒有找到現(xiàn)成簡單又合適的代碼,于是把自己壓箱底的代碼翻出來了,簡化完善了一下,嘿,代碼量也不大,還挺好用。
本方法是通過計算得到圓上的多個坐標(biāo)點,來得到的一個近似的圓形面,只要坐標(biāo)點夠多,這個圓就能足夠圓;有了這些坐標(biāo)點就很容易表示成不同的格式,比如:GeoJSON文本
、WKT文本
、Geometry實例
。
源自 坐標(biāo)邊界查詢工具 開源庫:https://github.com/xiangyuecn/AreaCity-Query-Geometry (github可以換成gitee),高性能的坐標(biāo)數(shù)據(jù)、邊界數(shù)據(jù)查詢工具,Java開源程序、帶http查詢接口,內(nèi)存占用低(1秒可查1萬個以上坐標(biāo)對應(yīng)的城市信息)。
java版源碼
public static void main(String[] args) {
//計算天壇到天安門的距離
System.out.println(Distance(116.410622, 39.881773, 116.397476, 39.908647));
//生成天壇1公里范圍的圓形面
System.out.println(CreateSimpleCircleWKT(116.410622, 39.881773, 1000, 24));
}
/** 計算兩個坐標(biāo)的距離,單位米 **/
static public double Distance(double lng1, double lat1, double lng2, double lat2) {
//采用Haversine formula算法,高德地圖的js計算代碼,比較簡潔 https://www.cnblogs.com/ggz19/p/7551088.html
double d=Math.PI/180;
double f=lat1*d, h=lat2*d;
double i=lng2*d - lng1*d;
double e=(1 - Math.cos(h - f) + (1 - Math.cos(i)) * Math.cos(f) * Math.cos(h)) / 2;
return 2 * 6378137 * Math.asin(Math.sqrt(e));
}
/** 以坐標(biāo)點為中心,簡單粗略的創(chuàng)建一個指定半徑的圓,半徑單位米,pointCount為構(gòu)建圓的坐標(biāo)點數(shù)(比如24個點,點越多越圓,最少3個點),返回構(gòu)成圓的坐標(biāo)點數(shù)組 **/
static public double[][] CreateSimpleCircle(double lng, double lat, double radius, int pointCount) {
//球面坐標(biāo)不會算,轉(zhuǎn)換成三角坐標(biāo)簡單點,經(jīng)度代表值大約:0.01≈1km 0.1≈10km 1≈100km 10≈1000km
double km=radius/1000;
double a=km<5?0.01 :km<50?0.1 :km<500?1 :10;
double b=Distance(lng, lat, lng+a, lat);
double c=Distance(lng, lat, lng, lat+a);
double rb=radius/b*a;
double rc=radius/c*a;
double[][] arr=new double[pointCount+1][];
double n=0,step=360.0/pointCount,N=360-step/2; //注意浮點數(shù)±0.000000001的差異
for(int i=0;n<N;i++,n+=step){
double x=lng+rb*Math.cos(n*Math.PI/180);
double y=lat+rc*Math.sin(n*Math.PI/180);
arr[i]=new double[] { x, y };
}
arr[pointCount]=new double[] { arr[0][0], arr[0][1] }; //閉環(huán)
return arr;
}
/**
以坐標(biāo)點為中心,簡單粗略的創(chuàng)建一個指定半徑的圓,半徑單位米,pointCount為構(gòu)建圓的坐標(biāo)點數(shù)(比如24個點,點越多越圓,最少3個點),返回圓的WKT(Well Known Text)文本
,WKT圖形繪制預(yù)覽工具:https://xiangyuecn.gitee.io/areacity-jsspider-statsgov/assets/geo-echarts.html
**/
static public String CreateSimpleCircleWKT(double lng, double lat, double radius, int pointCount) {
double[][] points=CreateSimpleCircle(lng, lat, radius, pointCount);
DecimalFormat df=new DecimalFormat("0.######");
StringBuilder wkt=new StringBuilder("POLYGON((");
for(int i=0;i<points.length;i++) {
if(i>0)wkt.append(",");
wkt.append(df.format(points[i][0])+" "+df.format(points[i][1]));
}
wkt.append("))");
return wkt.toString();
}
js版源碼
//測試:計算天壇到天安門的距離
console.log(Distance(116.410622, 39.881773, 116.397476, 39.908647));
//測試:生成天壇1公里范圍的圓形面
console.log(CreateSimpleCircleWKT(116.410622, 39.881773, 1000, 24));
/** 計算兩個坐標(biāo)的距離,單位米 **/
function Distance(lng1, lat1, lng2, lat2) {
//采用Haversine formula算法,高德地圖的js計算代碼,比較簡潔 https://www.cnblogs.com/ggz19/p/7551088.html
var d=Math.PI/180;
var f=lat1*d, h=lat2*d;
var i=lng2*d - lng1*d;
var e=(1 - Math.cos(h - f) + (1 - Math.cos(i)) * Math.cos(f) * Math.cos(h)) / 2;
return 2 * 6378137 * Math.asin(Math.sqrt(e));
}
/** 以坐標(biāo)點為中心,簡單粗略的創(chuàng)建一個指定半徑的圓,半徑單位米,pointCount為構(gòu)建圓的坐標(biāo)點數(shù)(比如24個點,點越多越圓,最少3個點),返回構(gòu)成圓的坐標(biāo)點數(shù)組 **/
function CreateSimpleCircle(lng, lat, radius, pointCount){
//球面坐標(biāo)不會算,轉(zhuǎn)換成三角坐標(biāo)簡單點,經(jīng)度代表值大約:0.01≈1km 0.1≈10km 1≈100km 10≈1000km
var km=radius/1000;
var a=km<5?0.01 :km<50?0.1 :km<500?1 :10;
var b=Distance(lng, lat, lng+a, lat);
var c=Distance(lng, lat, lng, lat+a);
var rb=radius/b*a;
var rc=radius/c*a;
var arr=[];
var n=0,step=360.0/pointCount,N=360-step/2; //注意浮點數(shù)±0.000000001的差異
for(var i=0;n<N;i++,n+=step){
var x=lng+rb*Math.cos(n*Math.PI/180);
var y=lat+rc*Math.sin(n*Math.PI/180);
arr[i]=[x, y];
}
arr.push([arr[0][0], arr[0][1]]); //閉環(huán)
return arr;
};
/**
以坐標(biāo)點為中心,簡單粗略的創(chuàng)建一個指定半徑的圓,半徑單位米,pointCount為構(gòu)建圓的坐標(biāo)點數(shù)(比如24個點,點越多越圓,最少3個點),返回圓的WKT(Well Known Text)文本
,WKT圖形繪制預(yù)覽工具:https://xiangyuecn.gitee.io/areacity-jsspider-statsgov/assets/geo-echarts.html
**/
function CreateSimpleCircleWKT(lng, lat, radius, pointCount){
var points=CreateSimpleCircle(lng, lat, radius, pointCount);
var wkt=["POLYGON(("];
for(var i=0;i<points.length;i++) {
wkt.push((i>0?",":"")+(+points[i][0].toFixed(6))+" "+(+points[i][1].toFixed(6)));
}
wkt.push("))");
return wkt.join("");
};
在線繪制預(yù)覽效果
生成了圓形面的WKT文本后,可以粘貼進在線預(yù)覽頁面繪制顯示:https://xiangyuecn.gitee.io/areacity-jsspider-statsgov/assets/geo-echarts.html ,方便代碼調(diào)試,地圖上有測距功能,可以測量圓面的準(zhǔn)確度;頁面上的畫圓功能,采用的就是js版的代碼。
關(guān)于計算的精確度
兩個經(jīng)緯度坐標(biāo)的距離計算,采用的Haversine formula
算法,下面的計算代碼中高德地圖的api同樣是Haversine formula
算法,和百度地圖的計算結(jié)果誤差在0.2%
以內(nèi):
bMap=window.BMapGL&&BMapGL.Map.prototype||{getDistance:function(a,b){return BMap.Map.prototype.getDistance(new BMap.Point(a.lng,a.lat),new BMap.Point(b.lng,b.lat)) }};
//地圖api計算【緯度】之間的距離,每一度之間的距離是相同的
bMap.getDistance({lng:111,lat:15},{lng:111,lat:16}) //百度111194.86米
new AMap.LngLat(111,15).distance(new AMap.LngLat(111,16)) //高德111319.49米
bMap.getDistance({lng:121,lat:55},{lng:121,lat:56}) //百度111194.78米
new AMap.LngLat(121,55).distance(new AMap.LngLat(121,56)) //高德111319.49米
//地圖api計算【經(jīng)度】之間的距離,會隨著緯度的不同而不同
bMap.getDistance({lng:111,lat:15},{lng:112,lat:15}) //百度107405.91米
new AMap.LngLat(111,15).distance(new AMap.LngLat(112,15)) //高德107526.28米
bMap.getDistance({lng:111,lat:55},{lng:112,lat:55}) //百度63778.21米
new AMap.LngLat(111,55).distance(new AMap.LngLat(112,55)) //高德63849.69米
//經(jīng)度在相同緯度下每一度之間的距離是相同的
bMap.getDistance({lng:121,lat:55},{lng:122,lat:55}) //百度63778.21米
new AMap.LngLat(121,55).distance(new AMap.LngLat(122,55)) //高德63849.69米
對于構(gòu)成圓的坐標(biāo)點的計算,使用的以前壓箱底的代碼,計算比較簡單,經(jīng)度和緯度分別計算出一度的距離長度,在等比例的換算出半徑對應(yīng)的度數(shù)大小,比如算出來的經(jīng)度1°是100km,那么20km半徑對應(yīng)的度數(shù)就是1° * 20 / 100 = 0.2°
。
- 緯度每一度之間的距離都是固定的長度,等比例換算后的結(jié)果是沒有誤差的。
- 經(jīng)度每一度之間的距離長度會隨著緯度變化,低緯度長高緯度短,圓面的上下緯度不同,半徑小的時候誤差小,半徑越大誤差越大。
所以按等比例換算在緯度上沒有問題,但經(jīng)度上會產(chǎn)生一定的誤差,但只要圓的半徑不超過10km,誤差就能控制在0.5%
以內(nèi),可通過下面代碼直接計算觀察到:
//經(jīng)度之間的距離,不同緯度下的誤差計算
d1=new AMap.LngLat(111,15.0).distance(new AMap.LngLat(111.2,15.0));
d2=new AMap.LngLat(111,15.2).distance(new AMap.LngLat(111.2,15.2));
console.log(d2, (d1-d2)/d2*100+"%") //低緯度下經(jīng)度0.2°距離≈20km,緯度0.2°導(dǎo)致0.09%誤差
d1=new AMap.LngLat(111,55.0).distance(new AMap.LngLat(111.2,55.0));
d2=new AMap.LngLat(111,55.2).distance(new AMap.LngLat(111.2,55.2));
console.log(d2, (d1-d2)/d2*100+"%") //高緯度下經(jīng)度0.2°距離≈10km,緯度0.2°導(dǎo)致0.50%誤差
d1=new AMap.LngLat(111,15).distance(new AMap.LngLat(112,15));
d2=new AMap.LngLat(111,16).distance(new AMap.LngLat(112,16));
console.log(d2, (d1-d2)/d2*100+"%") //低緯度下經(jīng)度1°距離≈100km,緯度1°導(dǎo)致0.49%誤差
d1=new AMap.LngLat(111,55).distance(new AMap.LngLat(112,55));
d2=new AMap.LngLat(111,56).distance(new AMap.LngLat(112,56));
console.log(d2, (d1-d2)/d2*100+"%") //高緯度下經(jīng)度1°距離≈60km,緯度1°導(dǎo)致2.57%誤差
由于是通過圓上的坐標(biāo)點連起來得到的一個圓,本質(zhì)上是一個近似圓的多邊形,只要坐標(biāo)點足夠多就越接近圓,當(dāng)坐標(biāo)點少的情況下肉眼可見的不是那么圓(最少3個坐標(biāo)點,三角形),誤差也會很大。但考慮到點數(shù)越多,會導(dǎo)致使用上很多地方的計算量會變的很大,所以構(gòu)成圓的坐標(biāo)點數(shù)也是一個綜合考慮的數(shù)量,我選擇24個坐標(biāo)點構(gòu)成一個圓,每個象限6個點,點之間角度為15°。文章來源:http://www.zghlxwxcb.cn/news/detail-475415.html
【完】文章來源地址http://www.zghlxwxcb.cn/news/detail-475415.html
到了這里,關(guān)于經(jīng)緯度坐標(biāo)為中心點生成米距離長度半徑的圓形面,含java js源碼+在線繪制,代碼簡單零依賴的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!