首页 > 解决方案 > 鼠标悬停时用绘图注释底图

问题描述

我正在绘制带有底图的欧洲地图,并尝试使其尽可能具有交互性。我希望这张地图显示一些选定国家的一些数据(例如:能源消耗)作为给定年份的颜色。如果我用鼠标将鼠标悬停在一个国家上,我希望出现一个图,向我展示这种消费在过去几年中是如何演变的。我的问题是:如何用这样的图注释底图。我找到了一种用文本注释的方法(见下文),但不是用绘图。

这是我到目前为止所取得的成就。首先,我生成底图。

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
import pandas as pd
import shapefile

from matplotlib.collections import PolyCollection
from matplotlib.patches import Polygon
from matplotlib import cm

%matplotlib nbagg

fig = plt.figure(figsize=(8,6.4))
ax = plt.subplot(111)

m = Basemap(projection='lcc', resolution='l', lat_0 = 50, lon_0 = 15, \
              llcrnrlon = -9, llcrnrlat = 32, urcrnrlon= 40, urcrnrlat = 63) 

请注意,为了只为某些选定的国家着色,我使用的 shapefile 不是来自底图本身,而是来自natural earth website。该文件在ne_10m_admin_0_countries此处具有名称。

sfile = 'ne_10m_admin_0_countries/ne_10m_admin_0_countries' 
m.readshapefile(sfile, 'areas', linewidth=0.1)  

shp = shapefile.Reader(sfile) 
shapes = shp.shapes() 
records = shp.records() 

请注意,我使用底图来绘制所有国家/地区,但稍后我会在循环中为我感兴趣的国家/地区着色。在进入该循环之前,我创建了一个列表,其中将包含我绘制的国家的多边形补丁以及链接到它们的注释

poly_with_annotation = []

数据可以在 dataframe 中找到df_mean,其中每一列是不同的国家,每一行是不同的年份。(这里dict_country只是一个字典,包含国家名称和要查看的shapefile记录之间的对应关系。这无关紧要,基本上我们只是绘制我们感兴趣的国家。)

for column, country in dict_country.items(): 
        record = records[records_dict[country]] 
        shape = shapes[records_dict[country]]
    
        lons,lats = zip(*shape.points)
        data = np.array(m(lons, lats)).T
        
        if len(shape.parts) == 1: 
            segs = [data,]
        else: # Some countries have islands or are given as several polygons
            segs = []
            for i in range(1,len(shape.parts)):
                index = shape.parts[i-1]
                index2 = shape.parts[i]
                segs.append(data[index:index2])
            segs.append(data[index2:])

        polys = PolyCollection(segs, antialiaseds=(1,))
        polys.set_facecolors(cm.Reds(df_mean.loc[default_year,column]/df_mean.max().max()))
        polys.set_edgecolors('k')
        polys.set_linewidth(0.3)
        ax.add_collection(polys)
        

现在是重要的部分。我知道如何用文本注释它。这是我所拥有的:

        lon0 = 10
        lat0 = 50
        annotation = ax.annotate(\
                                 "Mouse over country %s" % country, \
                                 xy=m(lon0,lat0), xycoords='data', \
                                 xytext=m(lon0+2,lat0+2), textcoords='data', \
                                 horizontalalignment="left", \
                                 bbox=dict(boxstyle="round", facecolor="w", \
                                           edgecolor="0.5", alpha=0.9) \
                                )
        # by default, disable the annotation visibility
        annotation.set_visible(False)
        poly_with_annotation.append([polys, annotation])
        # When my mouse hovers above a given patch (country), execute the on_move function
        on_move_id = fig.canvas.mpl_connect('motion_notify_event', on_move)
        

其中on_move函数定义为

def on_move(event):
    visibility_changed = False
    for poly, annotation in poly_with_annotation:
        should_be_visible = (poly.contains(event)[0] == True) 
        if should_be_visible != annotation.get_visible(): 
            visibility_changed = True # on change la visibilité de l'annotation
            annotation.set_visible(should_be_visible)
    if visibility_changed:        
        plt.draw()

这很好用(见图,我的鼠标在法国上空)。

在此处输入图像描述

但我希望这样而不是“将鼠标悬停在国家 FRA”上,它显示了这个国家多年来数据的演变图(类似于df_mean.plot(y='FRA')

请注意,这受到以下答案的启发:

标签: pythonmatplotlibevent-handlingpopupmatplotlib-basemap

解决方案


推荐阅读