在BaseMap上绘图 - 意外结果

1 投票
1 回答
2045 浏览
提问于 2025-04-18 18:21

我正在运行以下代码,这应该是一些基本的地图绘制内容:

from mpl_toolkits.basemap import Basemap
import numpy as np
import matplotlib.pyplot as plt

#basic stuff from the examples
plt.close('all')
m = Basemap(projection='merc',llcrnrlat=-80,urcrnrlat=80,\
            llcrnrlon=-180,urcrnrlon=180,lat_ts=20,resolution='l')
m.drawcoastlines()
m.fillcontinents(color='white',lake_color='aqua')
# draw parallels and meridians.
m.drawparallels(np.arange(-90.,91.,30.))
m.drawmeridians(np.arange(-180.,181.,60.))
m.drawmapboundary(fill_color='aqua')

lon=[
 -44.897539694478894,
 -79.56264363246461,
 -108.31264586027467,
 -129.5832433799378,
 -149.11755440233293,
 173.04624586158417,
 57.26114485166647,
 26.06650557322952,
 6.8910540489469785,
 -15.059586144625898]

lat=[
 -23.30021206811055,
 -22.174848810053106,
 -6.169632450760373,
 18.199421172044598,
 45.95724594253466,
 72.89364342463014,
 69.39230460744983,
 41.88542137864501,
 14.50656439517308,
 -8.974170076274387]

m.plot(lon,lat,latlon=True,c='orange')
m.plot(lon[0:5],lat[0:5],latlon=True,c='green')
m.plot(lon[5:-1],lat[5:-1],latlon=True,c='blue')

plt.show()

如你所见,我在绘制一张基本的地图,然后根据经纬度在上面标记一些东西。一整套点是用橙色绘制的,然后这些点的子集用绿色和蓝色绘制。因此,我本来期待橙色的标记和蓝色、绿色的标记会重合。但实际上,我得到的结果是这样的:sample output

根据我的理解,正确的标记应该是蓝色和绿色的那部分。不同的点选择不会出现同样的问题。似乎当绘制的线在某种意义上绕过地图边界时,plot() 会感到困惑。

我是不是做错了什么,还是说这是一个basemap的bug?

1 个回答

2

这真有意思。这里有一个例子,进一步说明了这个问题:

from mpl_toolkits.basemap import Basemap
import numpy as np
import matplotlib.pyplot as plt

m = Basemap(projection='merc',llcrnrlat=-80,urcrnrlat=80,\
            llcrnrlon=-180,urcrnrlon=180,lat_ts=20,resolution='l')
m.drawparallels(np.arange(-90.,91.,30.))
m.drawmeridians(np.arange(-180.,181.,60.))

lon=[
 -44.897539694478894,
 -79.56264363246461,
 -108.31264586027467,
 -129.5832433799378,
 -149.11755440233293,
 173.04624586158417,
 57.26114485166647,
 26.06650557322952,
 6.8910540489469785,
 -15.059586144625898]

lat=[
 -23.30021206811055,
 -22.174848810053106,
 -6.169632450760373,
 18.199421172044598,
 45.95724594253466,
 72.89364342463014,
 69.39230460744983,
 41.88542137864501,
 14.50656439517308,
 -8.974170076274387]

m.plot(lon, lat, 'ro', markersize=14, mec='none', latlon=True)
m.plot(lon[:-1], lat[:-1], 'bo', markersize=10, mec='none', latlon=True)
m.plot(lon[1:], lat[1:], 'go', markersize=6, mec='none', latlon=True)

数据是一样的,但如果从某个点省略一个样本,结果就会让这些点的位置错位:

enter image description here

红色、绿色和蓝色的图应该重合在一起。看起来很奇怪,似乎在某些系列中,X和Y坐标之间的位置出现了偏差。

甚至红色的点看起来也不在它们应该在的位置上。要么这是个bug,要么文档里有些奇怪的地方。(我用的是matplotlib 1.3.1和basemap 1.0.6。)

不过,投影转换本身似乎工作得很好:

c = m(lon, lat)
cp = m(lon[:-1], lat[:-1])

plt.plot(c[0], c[1], 'kx')
plt.plot(cp[0], cp[1], 'o', mfc='none', mec='k')

这会生成:

enter image description here

现在这些点的位置是正确的(圈和叉),而选择绘制的点不会改变它们的位置。

可以看到,所有图片中的X坐标(纬度)都是正确的,但不知为何,纵坐标(经度)看起来几乎是随机的。真奇怪。(无论如何,解决方法在上面。)


更新:我想我找到了这个bug。Basemapplot方法首先会把数据移动,使其从图的边缘开始。例如:

In [27]: m.shiftdata(lon)
Out[27]: 
array([ 173.04624586,   57.26114485,   26.06650557,    6.89105405,
        -15.05958614,  -44.89753969,  -79.56264363, -108.31264586,
       -129.58324338, -149.1175544 ])

这样做是可以的,因为现在线条会是连续的。不幸的是,纬度数据没有被移动,结果正是上面所显示的那样。

如果同时移动纬度和经度:

lons, lats = m.shiftdata(lon, lat)

然后用移动后的版本来绘制数据,一切就正常了。

在我看来,似乎plotscatter方法周围的装饰器_transform1d有些麻烦。通过把这个装饰器改成_transform来进行猴子补丁(__init.py__,今天的git中3239和3277行)应该会有帮助,但这可能会破坏plotscatter的其他功能。

我认为最好的解决方案是在绘图之前先对数据进行排序,使用上面提到的shiftdata。(当然,也要提交一个bug报告。)

撰写回答