使用mpl_toolkit.basemap的pcolormesh()动画出现属性错误

4 投票
1 回答
2145 浏览
提问于 2025-04-18 00:14

我正在尝试在一个 basemap 地图上动画展示一些密度数据。按照[这个StackOverflow问题][1]的方法,我遇到了以下错误:

/usr/local/lib/python2.7/dist-packages/matplotlib/collections.pyc in update_scalarmappable(self)
    627         if self._A is None:
    628             return
--> 629         if self._A.ndim > 1:
    630             raise ValueError('Collections can only map rank 1 arrays')
    631         if not self.check_update("array"):

AttributeError: 'list' object has no attribute 'ndim'

如果我在 init() 函数中用空值设置数据,像这样 self.quad.set_array(self.z.ravel()),那么我最后会得到两个绘制的地图,但没有任何数据在动画中显示。

如果有人能帮我指出我哪里做错了,我将非常感激。谢谢!

示例代码

def plot_pcolor(lons,lats):

    class UpdateQuad(object):

        def __init__(self,ax, map_object, lons, lats):
                self.ax = ax
                self.m  = map_object
                self.lons = lons
                self.lats = lats

                self.ydim, self.xdim = lons.shape

                self.z = np.zeros((self.ydim-1,self.xdim-1))

                x, y = self.m(lons, lats)
                self.quad = ax.pcolormesh(x, y, self.z, cmap=plt.cm.Reds)

        def init(self):
            print 'update init'
            self.quad.set_array([])
            return self.quad

        def __call__(self,i):

            data = np.zeros((self.ydim-1,self.xdim-1))

            for i in range(self.ydim-1):
                for j in range(self.xdim-1):
                    data[i,j]=random.random()+4

            self.quad.set_array(data.ravel())
            return self.quad

    fig = plt.figure()
    ax = fig.add_axes([0.1,0.1,0.8,0.8])

    m = Basemap(width=2000000,height=2000000,
                resolution='l', projection='laea',\
                lat_ts=10.,\
                lat_0=64.,lon_0=10., ax=ax)

    m.fillcontinents()

    ud = UpdateQuad(ax, m, lons, lats)

    anim = animation.FuncAnimation(fig, ud, init_func=ud.init,
                                   frames=20,  blit=False)

    plt.show()

if __name__ == '__main__':
    import matplotlib.pyplot as plt
    import matplotlib.animation as animation
    from mpl_toolkits.basemap import Basemap
    import numpy as np
    import random

    lons = np.linspace(-5.,25., num = 25)[:50]
    lats = np.linspace(56., 71., num = 25)[:50]
    lons,lats =  np.meshgrid(lons,lats)

    plot_pcolor(lons,lats)

1 个回答

6

看起来 set_data 方法应该需要一个 ndarray(我也不太明白我之前跟的例子为什么能正常工作)。

所以在 init() 函数里,你应该用 quad.set_array(np.array([])) 而不是 quad.set_array([])

其他需要注意的问题

  • 如前所述,你还需要在 FuncAnimation() 调用中设置 blit=False

  • 我在把 quad 的 artist 属性 animated 设置为 True 时也遇到了问题。最好保持默认设置,也就是 quad.set_animated(False)

  • 如果你在第一次调用 pcolormesh() 时没有通过 norm 指定边界,它会根据你传入的数据(在我这里是空的)来设置边界,这导致我得到了空白的动画。根据你稍后要动画化的数据来设置边界,可以避免这个问题。

  • pcolormesh() 需要数据字段的边界位置,这在数据数组的 y 和 x 维度上应该加 1。如果数据数组的大小等于或大于位置数据的维度,pcolormesh() 会忽略任何超出这个边界的数据显示。我原以为我的数据只会偏移一个网格单元,但在我传入正确的边界位置之前,一切都很混乱。关于计算这些边界位置的另一个问题可以在这里找到 点击这里

  • 旧版本的 matplotlib 错误报告不太好。如果可以的话,建议升级到最新版本。

一些随机的故障排除

在更新了 matplotlibbasemap 并尝试在我现有的绘图程序中实现这个功能时,我收到了以下错误:

ValueError: All values in the dash list must be positive

我最开始以为是我的 pcolormesh() 对象的问题,但我花了太长时间才发现是因为我之前在 m.drawmeridians() 调用中把 dash 属性设置为 dashes=[1,0],这是为了画实线。在新版本的 matplotlib 中,对虚线的处理方式发生了变化,导致了这个错误。现在设置实线的推荐方法是 dashes=(None,None),我对此不太满意。

最终的动画效果

pcolormesh 动画

上面输出的代码示例

def plot_pcolor(lons,lats):

    class UpdateQuad(object):

        def __init__(self,ax, map_object, lons, lats):
            self.ax = ax
            self.m  = map_object
            self.lons = lons
            self.lats = lats
            vmin = 0
            vmax = 1
            self.ydim, self.xdim = lons.shape

            self.z = np.zeros((self.ydim-1,self.xdim-1))

            levels = MaxNLocator(nbins=15).tick_values(vmin,vmax)
            cmap = plt.cm.cool
            norm = BoundaryNorm(levels, ncolors=cmap.N, clip=True)
            x, y = self.m(lons, lats)

            self.quad = self.ax.pcolormesh(x, y, self.z, alpha=0.9,
                                           norm=norm, cmap=cmap,
                                           vmin=vmin, vmax=vmax)
        def init(self):
            print 'update init'
            self.quad.set_array(np.asarray([]))
            return self.quad

        def __call__(self,i):

            for i in range(self.ydim-1):
                for j in range(self.xdim-1):
                    self.z[i,j]=random.random()

            self.quad.set_array(self.z.ravel())

            return self.quad


    fig, ax = plt.subplots()

    m = Basemap(width=2000000,height=2000000,
                resolution='l', projection='laea',\
                lat_ts=10.,\
                lat_0=64.,lon_0=10., ax=ax)

    m.fillcontinents()

    ud = UpdateQuad(ax, m, lons, lats)

    anim = animation.FuncAnimation(fig, ud, init_func=ud.init,
                                   frames=20,  blit=False)

    fig.tight_layout()

    plt.show()

    return ud.quad

if __name__ == '__main__':
    import matplotlib.pyplot as plt
    import matplotlib.animation as animation
    from mpl_toolkits.basemap import Basemap
    import numpy as np
    import random
    from matplotlib.colors import BoundaryNorm
    from matplotlib.ticker import MaxNLocator

    lons = np.linspace(-5.,25., num = 25)[:50]
    lats = np.linspace(56., 71., num = 25)[:50]
    lons,lats =  np.meshgrid(lons,lats)

    quad = plot_pcolor(lons,lats)

撰写回答