如何更改现有轴的matplotlib子图投影?

76

我正在尝试构建一个简单的函数,它接受一个子图实例(matplotlib.axes._subplots.AxesSubplot),并将其投影转换为另一种投影方式,例如转换为cartopy.crs.CRS中的一种投影方式。

这个想法大致上是这样的:

import cartopy.crs as ccrs
import matplotlib.pyplot as plt

def make_ax_map(ax, projection=ccrs.PlateCarree()):
    # set ax projection to the specified projection
    ...
    # other fancy formatting
    ax2.coastlines()
    ...

# Create a grid of plots
fig, (ax1, ax2) = plt.subplots(ncols=2)
# the first subplot remains unchanged
ax1.plot(np.random.rand(10))
# the second one gets another projection
make_ax_map(ax2)

当然,我可以使用fig.add_subplot()函数:

fig = plt.figure(figsize=(10,5))
ax1 = fig.add_subplot(121)
ax1.plot(np.random.rand(10))

ax2 = fig.add_subplot(122,projection=ccrs.PlateCarree())
ax2.coastlines()

我想知道是否有一种适当的matplotlib方法可以在定义完子图轴投影后进行更改。不幸的是,阅读matplotlib API没有帮助。

4个回答

83

你无法更改现有轴的投影方式,原因如下所述。但是解决底层问题的方法很简单,只需使用plt.subplots()中描述的subplot_kw参数,该参数在matplotlib文档这里有介绍。例如,如果您希望所有子图都具有cartopy.crs.PlateCarree投影,则可以执行以下操作:

import matplotlib.pyplot as plt
import cartopy.crs as ccrs

# Create a grid of plots
fig, (ax1, ax2) = plt.subplots(ncols=2, subplot_kw={'projection': ccrs.PlateCarree()})

关于实际问题,当你创建一个坐标轴集合并指定一个投影时,确定了你得到的坐标轴类型,每种投影类型的坐标轴类型都不同。例如:

import matplotlib.pyplot as plt
import cartopy.crs as ccrs

ax1 = plt.subplot(311)
ax2 = plt.subplot(312, projection='polar')
ax3 = plt.subplot(313, projection=ccrs.PlateCarree())

print(type(ax1))
print(type(ax2))
print(type(ax3))

这段代码将打印以下内容

<class 'matplotlib.axes._subplots.AxesSubplot'>
<class 'matplotlib.axes._subplots.PolarAxesSubplot'>
<class 'cartopy.mpl.geoaxes.GeoAxesSubplot'>

注意每个轴实际上都是不同类的一个实例。


6
谢谢!为了确认,projection关键字确定所有子图的类,因此在subplot_kw中没有办法传递多个投影方式?例如,对于由plt.subplots(ncols=2)创建的一个子图集,第一列使用projection='polar',第二列使用projection=ccrs.PlateCarree(),是不可行的。 - Denis Sergeev
2
subplot_kw 中的关键字会传递给每个轴,因此我认为您无法实现所描述的功能。subplots 函数是一个方便的包装器,用于满足基本用例,如果您需要更多内容,可以使用 add_subplot 或类似函数编写自己的包装器函数。 - ajdawson
1
有没有办法用ImageGrid实现这个?能够像ImageGrid一样控制地图间距和颜色条位置会很好,但我没有看到在网格中设置子图投影的方法。 - Daniel Watkins
3
可以使用ImageGrid(或AxesGrid)中的axes_class关键字实现。在cartopy的画廊中有一个例子。还有一个PR提交给了cartopy(虽然没有被接受),可以用来创建自定义的GeoAxesGrid - Denis Sergeev

10

假设用于2D绘图的有多个轴,例如...

fig = matplotlib.pyplot.Figure()
axs = fig.subplots(3, 4) # prepare for multiple subplots
# (some plotting here)
axs[0,0].plot([1,2,3])

...可以简单地销毁其中之一,并用具有3D投影的新物品替换它:

axs[2,3].remove()
ax = fig.add_subplot(3, 4, 12, projection='3d')
ax.plot_surface(...)

请注意,与 Python 的其他部分不同,add_subplot 使用行列索引,从1开始(而不是0开始)。

编辑:更改了有关索引的拼写错误。


2
你可以使用以下函数,它可以删除轴并按照指定的投影生成轴,类似于 dominecf 的回答,但具有自动检索特定子图参数(行、列和索引)的优点。
import matplotlib.pyplot as plt

def update_projection(ax, axi, projection='3d', fig=None):
    if fig is None:
        fig = plt.gcf()
    rows, cols, start, stop = axi.get_subplotspec().get_geometry()
    ax.flat[start].remove()
    ax.flat[start] = fig.add_subplot(rows, cols, start+1, projection=projection)

并生成包括所有可用投影的绘图

import matplotlib.projections
import numpy as np

# test data
x = np.linspace(-np.pi, np.pi, 10)

# plot all projections available
projections = matplotlib.projections.get_projection_names()

fig, ax = plt.subplots(nrows=1, ncols=len(projections), figsize=[3.5*len(projections), 4], squeeze=False)
for i, pro_i in enumerate(projections):
    update_projection(ax, ax.flat[i], pro_i)
    ax.flat[i].set_title(pro_i)
    try:
        ax.flat[i].grid(True)
        ax.flat[i].plot(x, x)
    except Exception as a:
        print(pro_i, a)
    
plt.tight_layout(pad=.5)

enter image description here


1

以下是对这个问题的回答:

在Python中,如何继承和覆盖一个类实例上的方法,并将这个新版本分配给与旧版本相同的名称?

我发现了一种在创建后更改轴的投影的方法,它似乎至少在下面的简单示例中有效,但我不知道这种解决方案是否是最佳方法。

from matplotlib.axes import Axes
from matplotlib.projections import register_projection

class CustomAxe(Axes):
    name = 'customaxe'

    def plotko(self, x):
        self.plot(x, 'ko')
        self.set_title('CustomAxe')

register_projection(CustomAxe)


if __name__ == '__main__':
    import matplotlib.pyplot as plt

    fig = plt.figure()

    ## use this syntax to create a customaxe directly
    # ax = fig.add_subplot(111, projection="customaxe")

    ## change the projection after creation
    ax = plt.gca()
    ax.__class__ = CustomAxe

    ax.plotko(range(10))    
    plt.show()

无法工作... - Ali

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接