CartoPy GeoAxes 之间的 PyPlot ConnectionPatch

PyPlot ConnectionPatch between CartoPy GeoAxes

ConnectionPatch is a useful way to draw a line between two points on two different axes (demo). Is it possible to use this class when one (or both) of the axes is of Cartopy GeoAxes type? A related answer 提出了一种解决方法,但我宁愿避免这种情况。

我无法回答您关于使用 class 东西的问题。但是,如果您有兴趣绘制 2 个不同的 Cartopy geoaxes 之间的线,或者 matplotlib 轴和 geoaxe 之间的线,可以通过一些坐标转换来实现。这是一个可运行的代码和输出图。我在代码中写了一些注释来帮助解释重要步骤。

有关坐标系和变换的更多信息:

Cartopy https://scitools.org.uk/cartopy/docs/latest/tutorials/understanding_transform.html

由于 Cartopy 是建立在 Matplotlib 之上的,您需要查看 Matplotlib 中的相关主题。

Matplotlib https://matplotlib.org/3.2.1/tutorials/advanced/transforms_tutorial.html

import cartopy
import cartopy.mpl.geoaxes
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.inset_locator import inset_axes

fig, ax = plt.subplots()
fig.set_size_inches([8,8])  # 9,6; 8,9; 8,3 all OK

# Plot simple line on main axes
ax.plot([4,5,3,1,2])

p1 = [0.5,3.0]  # Bangkok text location
p2 = [0.5,2.75] # Himalaya text location
# Plot texts (Bangkok, Himalaya) on the main axes
ax.text(*p1, "Bangkok", ha='right')
ax.text(*p2, "Himalaya", ha='right')

# Ploting on UR inset map (cartopy) on the main axes (ax)
bkk_lon, bkk_lat = 100, 13       # Bangkok
hml_lon, hml_lat = 83.32, 29.22  # Everest peak

# Create cartopy geoaxes inset axes as part of the main axes 'ax'
axins = inset_axes(ax, width="40%", height="30%", loc="upper right", 
                   axes_class = cartopy.mpl.geoaxes.GeoAxes, 
                   axes_kwargs = dict(map_projection = cartopy.crs.PlateCarree()))

# Set map limits on that axes (for Thailand)
llx, lly = 95, 0
urx, ury = 110, 25
axins.set_xlim((llx, urx))
axins.set_ylim((lly, ury))
# Plot coastlines
axins.add_feature(cartopy.feature.COASTLINE)

# Plot line across the inset mao, LL to UR; OK
#ll_p, ur_p = [llx,urx], [lly,ury]
#axins.plot(ll_p, ur_p, "r--")

axins.plot(bkk_lon, bkk_lat, 'ro', transform=cartopy.crs.PlateCarree()) # OK!

# Create another inset map on the main axes (ax)
axins2 = inset_axes(ax, width="40%", height="30%", loc="lower left", 
               axes_class = cartopy.mpl.geoaxes.GeoAxes, 
               axes_kwargs = dict(map_projection = cartopy.crs.PlateCarree()))

# Set map limits on that axes (second inset map)
llx2, lly2 = -60, -20
urx2, ury2 = 120, 90
axins2.set_xlim((llx2, urx2))
axins2.set_ylim((lly2, ury2))
axins2.add_feature(cartopy.feature.COASTLINE)

# Plot line from UK to BKK, OK
#p21, p22 = [0, 100], [40, 13]
#axins2.plot(p21, p22, "r--")

# Plot blue dot at Himalaya
axins2.plot(hml_lon, hml_lat, "bo")

plt.draw() # Do this to get updated position

# Do coordinate transformation to get BKK, HML locations in display coordinates
# from axins_data_xy to dp_xy
dpxy_bkk_axins = axins.transData.transform((bkk_lon, bkk_lat)) # get display coordinates
# from axins2_data_xy to dp_xy
dpxy_bkk_axins2 = axins2.transData.transform((hml_lon, hml_lat)) # get display coordinates

# Do coordinate transformation to get BKK, HML locations in data coordinates of the main axes 'ax'
# from both dp_xy to main_ax_data
ur_bkk = ax.transData.inverted().transform( dpxy_bkk_axins )
ll_hml = ax.transData.inverted().transform( dpxy_bkk_axins2 )

# Prep coordinates for line connecting BKK to HML
xs = ur_bkk[0], ll_hml[0]
ys = ur_bkk[1], ll_hml[1]


xs = ur_bkk[0], ll_hml[0]
ys = ur_bkk[1], ll_hml[1]
ax.plot(xs, ys, 'g--') # from Bkk to Himalaya of different inset maps

# Plot lines from texts (on main axes) to locations on maps
ax.plot([p1[0], ur_bkk[0]], [p1[1], ur_bkk[1]], 'y--')
ax.plot([p2[0], ll_hml[0]], [p2[1], ll_hml[1]], 'y--')

# Set cartopy inset background invisible
axins.background_patch.set_visible(False)
axins2.background_patch.set_visible(False)

plt.show()

输出图:-