Matplotlib pick_event在GeoDataFrame中无法触发的问题求助
问题:GeoPandas点图层无法触发Matplotlib pick_event事件
我通过GeoPandas绘制英国地图,并在地图上叠加parkrun跑步地点的坐标点,希望实现交互式选中这些坐标点后展示对应地点的信息,但调用fig.canvas.mpl_connect('pick_event', onclick)后功能完全无法正常工作,恳请帮忙排查原因。
可复现代码
import pandas as pd import geopandas as gpd import numpy as np import matplotlib.pyplot as plt ##### Create hardcoded dataset ###### df = pd.DataFrame.from_dict({0: {'parkrun_name': 'Beeston', 'location_name': 'Weirfields Recreation Ground', 'longitude': -1.201737, 'latitude': 52.913592, 'Run Date': '12/11/2022', 'Run Number': 370.0, 'Pos': 107.0, 'Time': '26:05', 'Age Grade': '49.46%', 'PB?': np.nan}, 1: {'parkrun_name': 'Colwick', 'location_name': 'Colwick Country Park, Nottingham', 'longitude': -1.09786, 'latitude': 52.945171, 'Run Date': '22/10/2022', 'Run Number': 511.0, 'Pos': 127.0, 'Time': '29:44', 'Age Grade': '43.39%', 'PB?': np.nan}, 2: {'parkrun_name': 'Exmouth', 'location_name': 'Exmouth', 'longitude': -3.412392, 'latitude': 50.614697, 'Run Date': '24/12/2022', 'Run Number': 189.0, 'Pos': 197.0, 'Time': '25:44', 'Age Grade': '50.13%', 'PB?': 'PB'}, }, orient='index') ###### Read in the UK Map File ###### uk = gpd.read_file("uk.geojson") ###### Convert df to geodf ###### gdf = gpd.GeoDataFrame( df, geometry=gpd.points_from_xy(df.longitude, df.latitude) ) gdf.crs = "EPSG:4326" gdf = gdf.to_crs(uk.crs)
###### Plot interactive map ###### %matplotlib widget fig = plt.figure(figsize=(10,10)) ax = fig.add_subplot(111) uk.plot(ax=ax, alpha=0.8) def onclick(event): ax = plt.gca() # ax.set_title(f"You selected {event.x}, {event.y}") line = event.artist xdata, ydata = line.get_data() ind = event.ind tx = f"{np.array([xdata[ind], ydata[ind]]).T}" ax.set_title(f"{tx}") gdf[gdf['Run Date'].isna()].plot(ax=ax, color='black', marker='.', markersize=8, alpha=0.2, picker=20) gdf[gdf['Run Date'].notna()].plot(ax=ax, color='#AAFF00', marker='.', markersize=50, picker=20) ax.set_xlim(-7,2) ax.set_ylim(49,60) cid = fig.canvas.mpl_connect('pick_event', onclick)
问题排查与解决方案
核心问题1:坐标范围不匹配
你将gdf转换为了UK地图的CRS(通常是OSGB36,单位为米),但后续设置的ax.set_xlim(-7,2)和ax.set_ylim(49,60)是WGS84经纬度范围,这会导致所有点被移出可视区域,自然无法点击触发事件。
核心问题2:事件处理函数适配错误
GeoPandas绘制的点图层是PathCollection对象,而非代码中假设的Line2D对象,因此调用line.get_data()会报错(在widget环境下可能不显示错误信息),需要改用get_offsets()获取点坐标,再结合索引匹配原始数据。
修改后的代码
###### Plot interactive map ###### %matplotlib widget fig = plt.figure(figsize=(10,10)) ax = fig.add_subplot(111) # 保持CRS一致,将UK地图转换为WGS84与gdf匹配 uk = uk.to_crs("EPSG:4326") uk.plot(ax=ax, alpha=0.8) # 存储每个图层对应的子集数据,方便点击时查询 layer_data = [] # 绘制无Run Date的点 na_layer = gdf[gdf['Run Date'].isna()].plot(ax=ax, color='black', marker='.', markersize=8, alpha=0.2, picker=20) layer_data.append(gdf[gdf['Run Date'].isna()]) # 绘制有Run Date的点 not_na_layer = gdf[gdf['Run Date'].notna()].plot(ax=ax, color='#AAFF00', marker='.', markersize=50, picker=20) layer_data.append(gdf[gdf['Run Date'].notna()]) # 对应WGS84的坐标范围,无需修改 ax.set_xlim(-7,2) ax.set_ylim(49,60) def onclick(event): ax = plt.gca() # 获取点击的图层索引 layer_idx = [na_layer, not_na_layer].index(event.artist) # 获取点击的点索引 point_idx = event.ind[0] # 获取对应的数据行 selected_row = layer_data[layer_idx].iloc[point_idx] # 设置标题显示地点信息 ax.set_title(f"选中地点:{selected_row['parkrun_name']}\n跑步日期:{selected_row['Run Date']}") cid = fig.canvas.mpl_connect('pick_event', onclick)
关键修改说明
- 将UK地图转换为WGS84(EPSG:4326),与
gdf保持CRS一致,确保坐标范围匹配,点能显示在可视区域内。 - 存储每个绘制图层对应的子集数据,方便点击时快速查询。
- 修改
onclick函数,通过图层索引和点索引匹配原始数据,展示具体的地点信息。
内容的提问来源于stack exchange,提问作者Liam Gower
相关产品推荐
相关产品推荐

