如何用Python Plotly绘制平滑无断裂的渐变颜色跨国贸易路线?
解决Plotly绘制渐变贸易路线时的断裂问题
你的问题根源在于直接对经纬度做线性插值:地球是球面,平面坐标系下的线性插值路径,在地图投影(比如你用的equirectangular)里会呈现出断裂的折线,跨半球的长距离路线(比如巴西到中国)这个问题尤其突出。要实现平滑的渐变贸易路线,得先计算球面大圆航线的插值点,再基于这些点来做颜色渐变分段。
解决方案步骤
- 替换原来的线性插值函数,改用大圆航线插值:把经纬度转成弧度,计算球面上的最短路径点,再转回度数,这样得到的路线在地图上是平滑的曲线。
- 基于大圆插值点,分段设置从红到蓝的渐变颜色,保持原来的线宽逻辑。
修改后的完整代码
import numpy as np import plotly.graph_objects as go import matplotlib.colors as mcolors import pandas as pd # 计算大圆航线的插值点(球面最短路径) def interpolate_great_circle(lon1, lat1, lon2, lat2, num_points=10): # 转成弧度 lon1_rad = np.radians(lon1) lat1_rad = np.radians(lat1) lon2_rad = np.radians(lon2) lat2_rad = np.radians(lat2) # 计算大圆航线的中间点 delta_lon = lon2_rad - lon1_rad a = np.sin((lat2_rad - lat1_rad)/2)**2 + np.cos(lat1_rad) * np.cos(lat2_rad) * np.sin(delta_lon/2)**2 c = 2 * np.arctan2(np.sqrt(a), np.sqrt(1-a)) # 生成插值比例 fractions = np.linspace(0, 1, num_points) lons_rad = [] lats_rad = [] for f in fractions: A = np.sin((1-f)*c) / np.sin(c) B = np.sin(f*c) / np.sin(c) x = A * np.cos(lat1_rad) * np.cos(lon1_rad) + B * np.cos(lat2_rad) * np.cos(lon2_rad) y = A * np.cos(lat1_rad) * np.sin(lon1_rad) + B * np.cos(lat2_rad) * np.sin(lon2_rad) z = A * np.sin(lat1_rad) + B * np.sin(lat2_rad) lat_rad = np.arctan2(z, np.sqrt(x**2 + y**2)) lon_rad = np.arctan2(y, x) lons_rad.append(lon_rad) lats_rad.append(lat_rad) # 转回度数 lons = np.degrees(lons_rad) lats = np.degrees(lats_rad) return lons, lats # 测试用数据 Def_trade_Kastner = pd.DataFrame({ 'Producer country': ['Brazil', 'Indonesia'], 'Consumer country': ['China', 'United States'], 'Relative Emissions': [10, 2.047066], 'start_lat': [-10.333333, -2.483383], 'start_lon': [-53.200000, 117.890285], 'end_lat': [35.000074, 39.78373], 'end_lon': [104.999927, -100.445882] }) # 创建图表 fig = go.Figure() num_segments = 5 # 渐变分段数 colors = [mcolors.to_hex(c) for c in np.linspace(mcolors.to_rgba('red'), mcolors.to_rgba('blue'), num_segments)] for i in range(len(Def_trade_Kastner)): lon1, lat1 = Def_trade_Kastner['start_lon'][i], Def_trade_Kastner['start_lat'][i] lon2, lat2 = Def_trade_Kastner['end_lon'][i], Def_trade_Kastner['end_lat'][i] # 用大圆插值替换线性插值 lons, lats = interpolate_great_circle(lon1, lat1, lon2, lat2, num_points=num_segments) for j in range(num_segments - 1): fig.add_trace( go.Scattergeo( lon=[lons[j], lons[j + 1]], lat=[lats[j], lats[j + 1]], mode='lines', line=dict(width=Def_trade_Kastner['Relative Emissions'][i], color=colors[j]), showlegend=False ) ) fig.update_layout( title_text='Deforestation', showlegend=False, geo=dict( visible=False, showcountries=True, projection_type='equirectangular', landcolor='rgb(243, 243, 243)', countrycolor='rgb(204, 204, 204)', ), autosize=False, width=1200, height=700, ) fig.show()
关键说明
interpolate_great_circle函数:通过球面三角学计算大圆航线的中间点,确保路线符合地球球面的最短路径,在地图上显示为平滑曲线。- 渐变逻辑保持不变:把大圆路径分成
num_segments段,每段对应从红到蓝的一个颜色值,实现起点到终点的渐变效果。 - 线宽依然使用
Relative Emissions字段,保持原有的数据映射逻辑。
内容的提问来源于stack exchange,提问作者Ep1c1aN
相关产品推荐
相关产品推荐

