使用mpl_toolkit.basemap的pcolormesh()动画出现属性错误
我正在尝试在一个 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 个回答
看起来 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
错误报告不太好。如果可以的话,建议升级到最新版本。
一些随机的故障排除:
在更新了 matplotlib
和 basemap
并尝试在我现有的绘图程序中实现这个功能时,我收到了以下错误:
ValueError: All values in the dash list must be positive
我最开始以为是我的 pcolormesh()
对象的问题,但我花了太长时间才发现是因为我之前在 m.drawmeridians()
调用中把 dash
属性设置为 dashes=[1,0]
,这是为了画实线。在新版本的 matplotlib
中,对虚线的处理方式发生了变化,导致了这个错误。现在设置实线的推荐方法是 dashes=(None,None)
,我对此不太满意。
最终的动画效果:
上面输出的代码示例:
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)