使用 Basemap 和 Cartopy 绘制子图实例
平时绘制地图时,经常会将多个图放到同一个 figure 中,而这些图的地图范围通常是相同的,所以可以设置共享 x-y 轴。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from matplotlib import cm,colors
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
import numpy as np
import netCDF4 as nc
fip = "F:\\"
fin1 = "wrfout_v2_Lambert.nc"
ti = [6,7,8,9]
xs = 0; xe = -1
ys = 0; ye = -1
zs = 0; ze = -1
data = nc.Dataset(fip + fin1, "r")
truelat = data.TRUELAT1
truelat = data.TRUELAT2
stalon = data.CEN_LON
stalat = data.CEN_LAT
fn = "Arial"
fs = 10
ld = 0.
nrows = 2
ncols = 2
def sharexy(ax, nrows, ncols, i, sharex = True, sharey = True):
if sharex:
if i+1 in np.arange(1, (nrows - 1)*ncols + 1):
labelsx = [0, 0, 0, 0]
else:
labelsx = [0, 0, 0, 1]
else:
labelsx = None
if sharey:
if i+1 not in np.arange(1, ncols*nrows, ncols):
labelsy = [0, 0, 0, 0]
else:
labelsy = [1, 0, 0, 0]
else:
labelsy = None
return labelsx, labelsy
fig,axes = plt.subplots(nrows = nrows, ncols = ncols, subplot_kw= dict(aspect = 'auto'))
for i, ax in zip(np.arange(0, nrows*ncols), axes.flat):
rain = data.variables["RAINC"][ti[i], xs:xe, ys:ye]
lat = data.variables["XLAT"][ti[i], xs:xe, ys:ye]
lon = data.variables["XLONG"][ti[i], xs:xe, ys:ye]
map = Basemap(ax= ax, projection="lcc", llcrnrlon = lon[-1,0], llcrnrlat = lat[0,0],\
urcrnrlon = lon[0,-1], urcrnrlat=lat[-1,-1], lat_0 = stalat,\
lon_0 = stalon, resolution ="h")
x, y = map(lon, lat)
# 添加经度,纬度坐标,海岸线,国界线
labelsx, labelsy = sharexy(ax, nrows, ncols, i)
# labels 参数用于控制经纬度 labels 的显示
map.drawmeridians(np.arange(int(lon[-1,0]), int(lon[0,-1])+1, 2), labels = [0,0,0,1], \
fontname = fn, fontsize= fs, linewidth = ld, ax = ax)
map.drawparallels(np.arange(int(lat[0,0]), int(lat[-1,-1])+1), labels = [1,0,0,0], \
fontname = fn, fontsize= fs, linewidth = ld, ax = ax)
con = map.contourf(x, y, rain)
ax.set_adjustable('box-forced') # 防止绘图和坐标区域不一致
fig.colorbar(con, ax=axes.ravel().tolist())
#plt.savefig("panel.eps")
plt.show()
sharexy 函数用以设置 basemap 地图共享 x-y 轴。
未共享x-y轴
将上述语句替换为以下两句即可共享x-y轴
map.drawmeridians(np.arange(int(lon[-1,0]), int(lon[0,-1])+1, 2), labels = labelsx, \
fontname = fn, fontsize= fs, linewidth = ld, ax = ax)
map.drawparallels(np.arange(int(lat[0,0]), int(lat[-1,-1])+1), labels = labelsy, \
fontname = fn, fontsize= fs, linewidth = ld, ax = ax)
共享 x-y轴
共享 x-y 轴后,中间空白间隔太大,可以使用 subplots_adjust 方法控制
将以下语句放到 fig.colorbar 命令前一行(具有相同的缩进)
fig.subplots_adjust(top = 0.9, bottom = 0.1, left = 0.12, right = 0.77,
hspace = 0.05, wspace = 0.05)
如果感觉 colorbar 和 panel 之间的间隔大的话, 可以设置 pad 参数并传递给 colorbar 方法。
最后说一下:一定会有人好奇,为什么不使用 subplots 的 sharex 和 sharey 参数来控制 x-y 轴共享。下面就上一张使用这种方法的图看看什么效果
可以看到并没有产生任何影响,drawmeridians 和 drawparallels 方法的 labels 参数起到了关键的作用,使 subplots 的 sharex 和 sharey 参数效果失效了。
注意:
以上图中的 colorbar 和 panel 图的对齐程度并不是很好,需要出图后再进行调整,或是直接设置 figsize 为合适的大小(但很难控制),即使传递 aspect 参数给 subplots 方法也没什么效果。而 cartopy 可以很好的解决以上遇到的问题。
下面上一张 cartopy 绘制子图的效果图
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# plot dbz-panel by WRF model data using cartopy
from matplotlib import cm,colors
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
from cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter
import netCDF4 as nc
fip = "/home/storm/python/sample_file/"
fin1 = "wrfout_v2_Lambert.nc"
ti = [6,7,8,9]
xs = 0; xe = -1
ys = 0; ye = -1
zs = 0; ze = -1
data = nc.Dataset(fip + fin1, "r")
truelat1 = data.TRUELAT1
truelat2 = data.TRUELAT2
stalon = data.CEN_LON
stalat = data.CEN_LAT
fn = "Arial"
fs = 10
ld = 0.
nrows = 2
ncols = 2
fig,axes = plt.subplots(nrows = nrows, ncols = ncols, sharex = True, sharey = True, \
subplot_kw=dict(projection=ccrs.PlateCarree(), aspect = 'auto'))
for i, ax in zip(np.arange(0, nrows*ncols), axes.flat):
rain = data.variables["RAINC"][ti[i],xs:xe,ys:ye]
lat = data.variables["XLAT"][ti[i], xs:xe, ys:ye]
lon = data.variables["XLONG"][ti[i], xs:xe, ys:ye]
ax.set_xticks(range(int(lon[-1,0]), int(lon[0,-1])+1, 2))
ax.set_yticks(range(int(lat[0,0]), int(lat[-1,-1])+1, 1))
lon_formatter = LongitudeFormatter(number_format='.0f',
degree_symbol='',
dateline_direction_label=True)
lat_formatter = LatitudeFormatter(number_format='.0f',
degree_symbol='')
ax.xaxis.set_major_formatter(lon_formatter)
ax.yaxis.set_major_formatter(lat_formatter)
con = ax.contourf(lon, lat, rain)
ax.hold(True)
ax.coastlines(resolution = '10m')
# 设置坐标轴范围
ax.set_xlim([lon[-1,0], lon[0,-1]])
ax.set_ylim([lat[0,0], lat[-1,-1]])