matplotlib和Cartopy的点击取值

Python Cartopy

This article was last updated on <span id="expire-date"></span> days ago, the information described in the article may be outdated.

最近在研究如何从卫星数据中获取雾区数据的问题,有一个大难点就是双通道法反演时如何设置阈值的问题。最直接的方法就是先画一张不设置阈值的图,然后根据坐标打印一下数值,再设置一下阈值。但是这样来来回回要重复跑很多遍代码,为了简化工作量,便研究了一下如何通过鼠标点击图片即可打印出对应点的值。

matplotlib的点击事件

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

1
2
3
4
5
def onClick(event):
# do something
# for example, print x and y coordinates
print(event.xdata)
print(event.ydata)

绑定事件也很简单,例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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

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

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

1
2
3
4
5
6
7
8
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)])

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

1
fig.canvas.mpl_connect("button_press_event", OnClick(data))
image-20230722135305939

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

Cartopy地图取值

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

image-20230722140437147

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

现在我们改写一下OnClick类。

这里我的卫星数据是DataArray,因此取数据时需要用对应的sel方法并设置method="nearest"以寻找符合条件的距离最近的数值点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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}")

绑定的代码如下

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

然后应用,看一下效果。

image-20230722141345938

Author: Syize

Permalink: https://blog.syize.cn/2023/07/22/matplotlib-cartopy-on-click-event/

本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Syizeのblog

Comments