Cartopy虽然对地理图形的绘制提供了极大的方便,但是个人感觉还是有很多反人类的地方,尤其是在同一个代码中绘制多副图形的时候,比如说地理坐标轴的设置,每幅子图都要设置一遍,最终搞得代码十分的冗长。为了解决这类问题,自定义的封装函数就起了很大作用,只需在最开始定义好,接下来每次使用直接调用即可,再也不用每次画图都重新设置什么刻度,海岸线之类的了。
其实用到的就是python的def功能,通过def自定义函数,return返回所需值,可以极大的简洁重复代码。
比如说绘制下面这幅图,我分别使用未封装的代码和使用封装后的代码绘制,方便对比。
example
未使用封装函数的完整代码:
#公共设置(地图投影,地图边界,坐标刻度地理格式)
proj = ccrs.PlateCarree(central_longitude=80)
img_extent = [0,160, 0, 80]
lon_formatter = cticker.LongitudeFormatter()
lat_formatter = cticker.LatitudeFormatter()
fig2 = plt.figure(figsize=(15,15))
#子图1
f2_ax1 = fig2.add_axes([0.1, 0.1, 0.4, 0.3],projection = proj)
#边界,海岸线,湖泊,坐标刻度,坐标格式
f2_ax1.set_extent(img_extent, crs=ccrs.PlateCarree())
f2_ax1.add_feature(cfeature.COASTLINE.with_scale('50m'))
f2_ax1.add_feature(cfeature.LAKES, alpha=0.5)
f2_ax1.set_xticks(np.arange(leftlon,rightlon+20,20), crs=ccrs.PlateCarree())
f2_ax1.set_yticks(np.arange(lowerlat,upperlat+20,20), crs=ccrs.PlateCarree())
f2_ax1.xaxis.set_major_formatter(lon_formatter)
f2_ax1.yaxis.set_major_formatter(lat_formatter)
#图序
f2_ax1.set_title('(a) ',loc='left')
#填色
f2_ax1.contourf(lon,lat, r, levels=np.arange(-0.9,1.0,0.1),extend='both', zorder=0, transform=ccrs.PlateCarree(), cmap=plt.cm.RdBu_r)
#子图2,同上
f2_ax2 = fig2.add_axes([0.6, 0.1, 0.4, 0.3],projection = proj)
f2_ax2.set_extent(img_extent, crs=ccrs.PlateCarree())
f2_ax2.add_feature(cfeature.COASTLINE.with_scale('50m'))
f2_ax2.add_feature(cfeature.LAKES, alpha=0.5)
f2_ax2.set_xticks(np.arange(leftlon,rightlon+20,20), crs=ccrs.PlateCarree())
f2_ax2.set_yticks(np.arange(lowerlat,upperlat+20,20), crs=ccrs.PlateCarree())
f2_ax2.xaxis.set_major_formatter(lon_formatter)
f2_ax2.yaxis.set_major_formatter(lat_formatter)
f2_ax2.set_title('(b) ',loc='left')
f2_ax2.contourf(lon,lat, r, levels=np.arange(-0.9,1.0,0.1),extend='both', zorder=0, transform=ccrs.PlateCarree(), cmap=plt.cm.RdBu_r)
可以看到,对底图设置的越精细所需的代码越长,但是绘制多图时均为重复代码,那么再看看封装后是怎样的:
使用封装函数:
首先将重复部分进行封装,那我们先分析重复部分的内容,包含了边界,海岸线,湖泊,坐标刻度,坐标格式等内容,需要需要传递的主要是地图的边界信息和坐标的间隔信息,以及对应的子图
def contour_map(fig,img_extent,spec):
fig.set_extent(img_extent, crs=ccrs.PlateCarree())
fig.add_feature(cfeature.COASTLINE.with_scale('50m'))
fig.add_feature(cfeature.LAKES, alpha=0.5)
fig.set_xticks(np.arange(leftlon,rightlon+spec,spec), crs=ccrs.PlateCarree())
fig.set_yticks(np.arange(lowerlat,upperlat+spec,spec), crs=ccrs.PlateCarree())
lon_formatter = cticker.LongitudeFormatter()
lat_formatter = cticker.LatitudeFormatter()
fig.xaxis.set_major_formatter(lon_formatter)
fig.yaxis.set_major_formatter(lat_formatter)
封装好后,后面只需直接调用即可。需要注意的是,这里并没有return,是因为函数中直接作用在了传递进去的fig上,因此无需再return。
#公共设置
proj = ccrs.PlateCarree(central_longitude=80)
leftlon, rightlon, lowerlat, upperlat = (0,160,0,80)
img_extent = [leftlon, rightlon, lowerlat, upperlat]
fig2 = plt.figure(figsize=(15,15))
#子图1
f2_ax1 = fig2.add_axes([0.1, 0.1, 0.4, 0.3],projection = proj)
#调用封装函数
contour_map(f2_ax1,img_extent,20)
f2_ax1.set_title('(a) ',loc='left')
f2_ax1.contourf(lon,lat, r, levels=np.arange(-0.9,1.0,0.1),extend='both', zorder=0, transform=ccrs.PlateCarree(), cmap=plt.cm.RdBu_r)
#子图2
f2_ax2 = fig2.add_axes([0.6, 0.1, 0.4, 0.3],projection = proj)
#调用封装函数
contour_map(f2_ax2,img_extent,20)
f2_ax2.set_title('(b) ',loc='left')
f2_ax2.contourf(lon,lat, r, levels=np.arange(-0.9,1.0,0.1),extend='both', zorder=0, transform=ccrs.PlateCarree(), cmap=plt.cm.RdBu_r)
仅仅是两幅子图,并没有缩减太多的代码,但是当子图较多时,这个方法就十分实用了。尤其是在jupyter notebook中,仅需要在最开始的代码块中随着各种库的一起引入,后边无论新建多长的代码块,都能使用,可以说只需要定义一次,在整个科研阶段中都可以随意调用。
实际上这里只是以绘图的方式举例def功能,还有很多地方也可以用到类似的思路,比如说自己写了一个算法,比如说是滑动t检验,你需要对多个序列进行检验,如果每次都写一遍滑动t的代码,那整个脚本的长度就太可怕了,完全可以用def的方法,只定义一次,接下来每个序列只需要一行调用函数就可以得到想要的结果,并且可以通过return的设置,返回所需要的各种中间变量。