如何在Basemap中按公里设置markersize以绘制指定半径的圆
解决Basemap中scatter标记对应实际5公里半径的问题
要让Matplotlib Basemap里的圆形标记对应实际5公里的半径,关键是把实际地理距离转换成Matplotlib散点图的s参数(标记面积,单位是平方点)。下面是具体的思路和修改后的代码:
核心思路
Basemap的width/height参数以米为单位,投影后的坐标(x,y)也是米为单位。我们可以通过以下步骤计算对应s值:
- 把目标点转换成投影坐标,再计算一个向北偏移5公里的点的投影坐标(LCC投影中y轴向北递增)。
- 将这两个点的投影坐标转换成屏幕像素坐标,算出它们的像素距离——这就是5公里对应的屏幕半径。
- 把像素距离转换成Matplotlib的「点(points)」单位,再用圆的面积公式计算
s参数(因为s是标记的面积)。
修改后的完整代码
import matplotlib.pyplot as plt import numpy as np from mpl_toolkits.basemap import Basemap # 地图尺寸:400公里宽,320公里高 width, height = 400000, 320000 fig, ax = plt.subplots(figsize=(20,10)) # 目标点经纬度列表 lonA = [2.631547, 2.861595, 2.931014] latA = [51.120983, 51.209122, 51.238868] # 创建LCC投影的Basemap实例 m = Basemap(width=width, height=height, projection='lcc', resolution='h', lat_0=52.35, lon_0=4.5) m.drawmapboundary(fill_color='turquoise') m.fillcontinents(color='white', lake_color='aqua') m.drawcountries(linestyle='--') # 计算5公里半径对应的scatter s参数 target_radius_m = 5000 # 5公里 = 5000米 # 取第一个点作为比例尺参考(点分布较近时,该值可通用) x, y = m(lonA[0], latA[0]) # 向北偏移5000米的投影坐标 x1, y1 = x, y + target_radius_m # 转换为屏幕像素坐标 trans = ax.transData pixel_xy = trans.transform((x, y)) pixel_xy1 = trans.transform((x1, y1)) # 计算像素半径 radius_pixel = np.linalg.norm(np.array(pixel_xy1) - np.array(pixel_xy)) # 像素转Matplotlib点:1英寸=72点,DPI是当前画布的像素密度 dpi = fig.dpi radius_points = radius_pixel * 72 / dpi # scatter的s是面积(平方点),所以用圆面积公式计算 s_size = np.pi * (radius_points) ** 2 # 绘制散点标记 scatter2 = m.scatter([], [], s=s_size, c='white', marker='o', label='Aurelia aurita', zorder=3, alpha=0.5, edgecolor='steelblue') # 设置所有点的坐标 z, a = m(lonA[0:3], latA[0:3]) scatter2.set_offsets(np.c_[z, a]) plt.legend() plt.show()
注意事项
- 如果你的点分布范围很大(跨越大比例尺变化区域),建议给每个点单独计算
s值,因为LCC投影的比例尺会随纬度变化。但你的点都在荷兰附近,离标准纬线lat_0=52.35很近,比例尺差异极小,用同一个s值足够准确。 - 调整画布的
figsize或DPI后,需要重新计算s值,因为屏幕像素密度改变了。
内容的提问来源于stack exchange,提问作者Jellyse
相关产品推荐
相关产品推荐

