最新消息

欢迎来到 DH Hub!

我们是 南信大 DH 互联网技术社团

在这里收集当下火热的技术文章,并且分到对应板块内,作为社团的技术积累,供历届社友学习

本站不开放注册,访客可以正常浏览

  • 由于一些解析原因,建议先在本地编辑器写完以后再上传
  • 由于服务器原因,在编辑主题 / 回复时会有一定卡顿,请谅解。

展示 matplotlib和Cartopy的点击取值

主题 作者
Syize?
荣誉成员
09
6
3
文章类型
完全原创 —— 转载请标注作者
最近在研究如何从卫星数据中获取雾区数据的问题,有一个大难点就是双通道法反演时如何设置阈值的问题。最直接的方法就是先画一张不设置阈值的图,然后根据坐标打印一下数值,再设置一下阈值。但是这样来来回回要重复跑很多遍代码,为了简化工作量,便研究了一下如何通过鼠标点击图片即可打印出对应点的值。

matplotlib的点击事件​

在使用matplotlib绘图的时候,用户其实是可以定义事件监听函数的,例如窗口的ResizeEvent事件,鼠标的MouseEvent事件,所有的事件类型可在matplotlib手册里找到。为了实现点击取值,我们需要定义一个回调函数来在事件触发时调用。

Python:
def onClick(event):
    # do something
    # for example, print x and y coordinates
    print(event.xdata)
    print(event.ydata)

绑定事件也很简单,例如

Python:
from matplotlib import pyplot as plt
import numpy as np


def onClick(event):
    print(event.xdata)
    print(event.ydata)


data = np.random.randint(0, 200, 100).reshape(10, 10)
fig = plt.figure(figsize=(8, 8))
plt.pcolormesh(data)
fig.canvas.mpl_connect("button_press_event", onClick)

plt.show()

这里button_press_event表示鼠标的点击事件,但是没有区分右键和左键。如果想要绑定其他事件的话,需要更换该字符串。

image-20230722134307268.jpg

打印出来的x和y的数值和图片里显示一致

但是我们想要的是对应点的数值,需要用取到的坐标去数据里取对应的值。然而回调函数只能传入一个必须参数,因此这里可以对回调函数重写为一个类,并使用Python的魔术方法__call__使其可以被直接调用。

Python:
class OnClick:
    def __init__(self, data):
        self.data = data
        
    def __call__(self, event):
        x = event.xdata
        y = event.ydata
        print(self.data[int(x), int(y)])

需要注意这里取到的x和y坐标是浮点数,我们需要对其进行取整才能得到对应的数值的索引。于是绑定的代码改写如下

Python:
fig.canvas.mpl_connect("button_press_event", OnClick(data))

image-20230722135305939.jpg

对比一下旁边的colorbar,深蓝色是小于25的,说明取值没有问题。

Cartopy地图取值​

解决了matplotlib点击取值的问题,接下来需要解决Cartopy地图点击取值的问题。因为Cartopy在绘图时对地理坐标系进行了操作,所以回调函数取到的坐标并不是地理坐标,而是图片的显示坐标。简单绘制个卫星数据举个例子。

image-20230722140437147.jpg


那么如何获取到地理坐标呢?这个时候需要使用cartopy.mpl.geoaxes.GeoAxes对象(也就是你的ax)里面的projection.transform_point方法。这个方法接受三个参数,分别是x坐标,y坐标以及你绘图时使用的proj。

现在我们改写一下OnClick类。
这里我的卫星数据是DataArray,因此取数据时需要用对应的sel方法并设置method="nearest"以寻找符合条件的距离最近的数值点。
Python:
class OnClick:
    def __init__(self, data: DataArray, ax: GeoAxes, proj):
        self.data = data
        self.ax = ax
        self.proj = proj

    def __call__(self, event):
        # get display coordinates
        x = event.xdata
        y = event.ydata
        # calculate the coordinates
        lon, lat = self.ax.projection.transform_point(x, y, src_crs=self.proj)
        # find the nearest point
        print(f"lon: {lon}, lat: {lat}, data: {self.data.sel({'latitude': lat, 'longitude': lon}, method='nearest').data}")

绑定的代码如下

Python:
ax.pcolormesh(VIS['longitude'], VIS['latitude'], VIS, transform=proj)
fig.canvas.mpl_connect("button_press_event", OnClick(VIS, ax, proj))

然后应用,看一下效果。

image-20230722141345938.jpg


本文章搬运自 Syizeのblog
 
顶部