如何在Plotly正射投影图上叠加绘制形状?
在Plotly正射投影地球图上绘制Az-El基准覆盖形状的解决方案
核心思路:坐标转换+形状叠加
要实现需求,关键是完成两次坐标转换,再将生成的形状顶点叠加到目标scattergeo点的3D坐标上,最终用fig.add_mesh3d绘制。
1. 地理坐标转3D笛卡尔坐标
先把scattergeo点的经纬度转成地球表面的(x,y,z),作为形状的中心点:
import numpy as np def latlon_to_xyz(lat, lon, R=1): # R取Plotly geo场景的等效半径,默认用1即可适配正射投影 lat_rad = np.radians(lat) lon_rad = np.radians(lon) x = R * np.cos(lat_rad) * np.cos(lon_rad) y = R * np.cos(lat_rad) * np.sin(lon_rad) z = R * np.sin(lat_rad) return x, y, z
2. Az-El坐标转局部3D形状顶点
以圆锥为例,根据方位角、仰角、圆锥半角、高度生成局部坐标的顶点,再转换到全局坐标:
def azel_to_cone_points(az, el, cone_half_angle, height, num_points=50): # 转换为弧度 az_rad = np.radians(az) el_rad = np.radians(el) # 圆锥指向的单位向量 dir_x = np.cos(el_rad) * np.sin(az_rad) dir_y = np.cos(el_rad) * np.cos(az_rad) dir_z = np.sin(el_rad) # 生成圆锥底面的圆周点 theta = np.linspace(0, 2*np.pi, num_points) base_radius = height * np.tan(np.radians(cone_half_angle)) # 计算局部坐标:底面点围绕指向向量旋转 local_x = dir_x * height + base_radius * (np.cos(theta)*np.cos(el_rad)*np.cos(az_rad) - np.sin(theta)*np.sin(az_rad)) local_y = dir_y * height + base_radius * (np.cos(theta)*np.cos(el_rad)*np.sin(az_rad) + np.sin(theta)*np.cos(az_rad)) local_z = dir_z * height + base_radius * np.cos(theta)*np.sin(el_rad) # 加入圆锥顶点(局部原点) return np.vstack([[0,0,0], np.column_stack([local_x, local_y, local_z])])
3. 将形状居中到scattergeo点
拿到目标点的(x0,y0,z0)后,把局部顶点加上这个偏移量,再用add_mesh3d绘制:
# 假设你有一个scattergeo点的经纬度:target_lat, target_lon x0, y0, z0 = latlon_to_xyz(target_lat, target_lon) # 定义圆锥参数:方位角30°,仰角45°,半角20°,高度0.2(相对于地球半径) cone_local_points = azel_to_cone_points(az=30, el=45, cone_half_angle=20, height=0.2) # 转换为全局3D坐标 global_x = x0 + cone_local_points[:,0] global_y = y0 + cone_local_points[:,1] global_z = z0 + cone_local_points[:,2] # 添加到已有图表 fig.add_mesh3d( x=global_x, y=global_y, z=global_z, opacity=0.3, color='rgba(255, 0, 0, 0.5)', showscale=False )
4. 半球绘制的类似逻辑
如果要画半球,只需生成以Az-El方向为中心轴的半球表面点,再做坐标旋转和全局偏移:
def azel_hemisphere_points(az, el, radius, num_points=100): az_rad = np.radians(az) el_rad = np.radians(el) # 生成半球的球面坐标(只取仰角>=el_rad的部分) theta = np.linspace(el_rad, np.pi/2, num_points//2) phi = np.linspace(0, 2*np.pi, num_points) theta, phi = np.meshgrid(theta, phi) # 局部坐标(默认z轴向上) local_x = radius * np.sin(theta) * np.cos(phi) local_y = radius * np.sin(theta) * np.sin(phi) local_z = radius * np.cos(theta) # 旋转矩阵:把局部z轴转到Az-El方向 rot_x = np.array([[1,0,0], [0,np.cos(-el_rad), -np.sin(-el_rad)], [0,np.sin(-el_rad), np.cos(-el_rad)]]) rot_z = np.array([[np.cos(az_rad), -np.sin(az_rad), 0], [np.sin(az_rad), np.cos(az_rad), 0], [0,0,1]]) rot_matrix = rot_z @ rot_x # 应用旋转并返回 local_points = np.column_stack([local_x.flatten(), local_y.flatten(), local_z.flatten()]) return local_points @ rot_matrix.T
参考文档建议
- 直接查Plotly官方的
mesh3dAPI文档:重点关注顶点输入、三角面定义(i/j/k参数)和透明度、颜色等视觉配置 - 了解Plotly正射投影的坐标系统:确保地球半径的缩放值和你转换时用的
R参数匹配,避免形状偏移
内容的提问来源于stack exchange,提问作者Alexandru Burlacu
相关产品推荐
相关产品推荐

