如何在Python Folium中绘制符合墨卡托投影变形的曲线?
解决方案:在Folium中绘制沿地球曲面的曲线(大圆航线投影)
Folium默认绘制的是平面直线(直接连接经纬度点的直线,并非地球曲面大圆航线的投影曲线),要实现需求,核心是生成大圆航线的中间点,再通过连接这些点模拟自然曲线。以下是两种可行方案:
方法一:球面插值生成大圆航线点集(高效准确)
这是你提到的XYZ坐标转换思路的具体实现,通过球面线性插值(SLERP)生成地球曲面上的等距点,再转回经纬度连接,效率和准确性都优于贝塞尔曲线方案。
代码实现
import numpy as np import folium def latlon_to_xyz(lat, lon): # 经纬度转三维笛卡尔坐标(单位球模型) lat_rad = np.radians(lat) lon_rad = np.radians(lon) x = np.cos(lat_rad) * np.cos(lon_rad) y = np.cos(lat_rad) * np.sin(lon_rad) z = np.sin(lat_rad) return x, y, z def xyz_to_latlon(x, y, z): # 三维坐标转回经纬度 lon = np.degrees(np.arctan2(y, x)) lat = np.degrees(np.arcsin(z)) return lat, lon def generate_great_circle_points(start_lat, start_lon, end_lat, end_lon, num_points=50): # 生成大圆航线的中间插值点 start_x, start_y, start_z = latlon_to_xyz(start_lat, start_lon) end_x, end_y, end_z = latlon_to_xyz(end_lat, end_lon) t = np.linspace(0, 1, num_points) dot_product = start_x*end_x + start_y*end_y + start_z*end_z theta = np.arccos(dot_product) sin_theta = np.sin(theta) # 处理两点重合的特殊情况 if sin_theta < 1e-6: return [(start_lat, start_lon)] * num_points # 球面线性插值计算中间点 x = np.sin((1-t)*theta) * start_x / sin_theta + np.sin(t*theta) * end_x / sin_theta y = np.sin((1-t)*theta) * start_y / sin_theta + np.sin(t*theta) * end_y / sin_theta z = np.sin((1-t)*theta) * start_z / sin_theta + np.sin(t*theta) * end_z / sin_theta latitudes, longitudes = xyz_to_latlon(x, y, z) return list(zip(latitudes, longitudes)) # 示例:绘制北京到纽约的大圆航线 m = folium.Map(location=[30, 120], zoom_start=3) start_point = (39.9042, 116.4074) end_point = (40.7128, -74.0060) route_points = generate_great_circle_points(start_point[0], start_point[1], end_point[0], end_point[1], num_points=100) folium.PolyLine(route_points, color='blue', weight=2).add_to(m) m.save('great_circle_route.html')
说明
num_points可根据精度调整,50-100个点就能在墨卡托投影下呈现自然的曲面曲线;- 该方法基于球面几何计算,完全贴合地球曲面的航线路径,比贝塞尔曲线更准确。
方法二:调用Leaflet原生大圆航线功能
Folium底层依赖Leaflet.js,而Leaflet内置L.GreatCircle类可直接绘制大圆航线,无需手动计算插值点,适合快速实现需求。
代码实现
import folium m = folium.Map(location=[30, 120], zoom_start=3) # 注入Leaflet原生JS代码绘制大圆航线 custom_js = """ var start = L.latLng(39.9042, 116.4074); var end = L.latLng(40.7128, -74.0060); L.greatCircle(start, end, {color: 'red', weight: 2}).addTo(map); """ folium.Html(f'<script>{custom_js}</script>', script=True).add_to(m) m.save('great_circle_leaflet.html')
说明
- 直接利用Leaflet的原生能力,无需处理复杂数学计算;
- 注意Folium生成的地图在JS中的实例名为
map,若自定义了地图变量名需对应修改。
关于贝塞尔曲线方案的补充
贝塞尔曲线属于平面曲线,要匹配墨卡托投影下的大圆曲线,需要结合投影变形公式计算控制点,过程复杂且精度难以保证,同时生成曲线的效率远低于球面插值方案,不推荐使用。
内容的提问来源于stack exchange,提问作者Matthew H.
相关产品推荐
相关产品推荐

