这是我用echarts做前端开发遇到的问题,似乎echarts没有提供在3d地球坐标面上画圆的方法。

 而我的需求是要在地球上画出一个卫星通信的覆盖范围的圆形区域。

 (效果展示)

前期的尝试:

最开始的时候我用网上的公式将经纬度乘系数转化为千米之后直接计算采样半径,然后在圆周中采样64个点,通过圆心坐标和半径以及采样角度,计算出圆弧上的采样点的经纬度。

这样做画出的圆在赤道附近是可以画出较为标准的圆形的,但是如果圆靠近极点的话,纬度较高的部分会逐渐聚合为一个点,并且如果极点在覆盖范围内的话,图形会变成一个8字,变得极其扭曲。

 之所以会发生这样的事,是因为直接把经纬度换算成距离计算,相当于把地球看做一个长方形画布,并在画布上绘制一个圆再投影到3D地球上,这样做是无法避免极点带来的问题的,因为如果覆盖范围包含极点的话,生成的接近90度的采样点都会被聚拢到极点处,这样无法将极点绕开。

正确做法:

准备工作

首先我们需要将地球看做一个球面坐标系,我们将球心到北极的射线作为Z轴,将赤道面经度-180(180)上过地球圆心的射线作为X轴正方向,将经度-90作为Y轴正方向(直观解释见下图)

 这样的话对于地球上的任意一个经纬度点都可以映射到这个球面坐标系中表示,由于最后采样点是由经纬度构成(其实就是角度),所以并不需要半径的信息,因此不妨设我们是在单位圆上进行计算。

 这个公式是极坐标转直角坐标,其中点的极坐标为:θ = 90-纬度,φ = 180+经度,r设为1

经过这一步,每一个经纬度点都可以在我们的直角坐标系中映射为一个(x,y,z)坐标点。

另外我们需要了解三维坐标旋转矩阵,通过矩阵运算,可以将三维中的一个向量绕轴旋转,见这里:三维坐标旋转矩阵

 了解了这些之后,我们可以开始设计我们的画圆方法了,思路如下。

绘制方法和代码

思路:首先我绘制一个在极轴上方绘制一个预定半径的圆(计算圆上一定数量的采样点),然后通过旋转矩阵,分别将这些点(向量)先绕Y轴旋转 θ = 90-纬度 的角度,再将旋转后的向量绕Z轴旋转 φ = 180+经度 的角度。最后再将这些转化后的采样点通过echarts的“type: 'lines3D'”三维曲线绘制出来即可。

这里需要注意的几点是:

1.使用js的Math计算时请使用弧度值(PI*角度值/180)

2.初始圆上点的Z值需要做一个割的运算(并不是圆的半径),所以我在后续代码中乘了一个cos

首先我们定义旋转函数:

rotateVector(axis, a, vector){

//axis:选择哪个轴

//a:旋转角度(注意是弧度值)

//vector:需要旋转的三维向量

var matrix = []

if(axis == 'z'){

matrix = [

[Math.cos(a), -Math.sin(a), 0],

[Math.sin(a), Math.cos(a), 0],

[0, 0, 1],

]

}

else if(axis == 'y'){

matrix = [

[Math.cos(a), 0, Math.sin(a)],

[0, 1, 0],

[-Math.sin(a), 0, Math.cos(a)],

]

}

var i, j, vector_new = [];

for(i=0; i<3; i++){

var value=0;

for(j=0; j<3; j++){

value += matrix[i][j] * vector[j]

}

vector_new.push(value)

}

return vector_new

},

然后写生成圆形采样点的函数

getCircleArea(place, radius) {

var lon=place[0], lat=place[1], base_height=0;

var r = radius, R = 6371;

// generate a circle

var sample_rate = 64, i=0;

var circle = [];

var point_lon, point_lat;

var f0, x0, y0, z0;

var a, a_lat = Math.PI/2 - Math.PI*lat/180, a_lon = Math.PI + Math.PI*lon/180;

var point_lon, point_lat;

for(i=0; i<=sample_rate; i++){

a = 2*Math.PI*i/sample_rate

f0 = Math.asin(r/R)

x0 = Math.sin(f0)*Math.cos(a)

y0 = Math.sin(f0)*Math.sin(a)

z0 = Math.cos(f0)

var vector = [x0, y0, z0]

vector = this.rotateVector('y', a_lat, vector)

vector = this.rotateVector('z', a_lon, vector)

point_lat = 90 - Math.acos(vector[2])*180/Math.PI

var lon_angle = Math.atan(vector[1]/vector[0])

if(vector[0]<0)

lon_angle += Math.PI

point_lon = -180 + lon_angle*180/Math.PI

circle.push([point_lon, point_lat, base_height])

}

return [circle]

},

最后调用函数并在经纬度上绘制圆圈

//已有地球基础图myChart

//place:[经度,纬度]

var circle = this.getCircleArea(place, radius);

myChart.setOption({

series: [{

type: 'lines3D',

coordinateSystem: 'globe',

polyline: true,

lineStyle: {

color: '#F56C6C',

width: 2,

opacity: 0.8

},

data: (this.settings.show_coverage)?circle:[],

}, // 覆盖范围的圈圈

]

});

现在可以以地球任何一个经纬度为圆心在3D地球上画圆了

 图中我以卫星所在经纬度为圆心,在地球表面绘制的卫星通讯范围的圆圈。^-^

用这种方法其实可以画出任意一种标准的形状,举一反三一下..

相关文章

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。