图形裁剪白化
图形裁剪白化也是一个经常留言的东西,这里就将常见的问题铺开讲解。
图形裁剪白化如何实现
在matplotlib中,裁剪白化的实现,依靠的是artist的set_clip_path方法。几乎所有的绘图命令的返回值都是artist,所以使用set_clip_path方法才是可行的。如果一个东西没有set_clip_path方法,那么就不能进行白化操作。
set_clip_path如何使用
我们在之前的推文中已经反复强调,函数或者方法要求什么,我们就提供什么。那么set_clip_path参数要求什么呢,首先就是用来裁剪的依据。在气象上,绝大部分裁剪依据就是地图的行政区划,所以问题转化为如何提取行政区划,提取行政区划可以使用geopandas和cartopy。set_clip_path参数要求的第二个就是transform,坐标变换,这个在气象上,绝大多数时候都是ax.transData。所以关键问题进入到path的获取。
path如何获取
对于气象方向来说,path就是行政区划的边界线。实际上大家使用的多是shp文件,而shp文件可以通过geopandas或者cartopy来读取。但是具体到操作上,推荐geopandas,因为他可以生成表格透视表。
geopandas读取shp文件的接口是read_file
import geopandas as gpd gdf=gpd.read_file(shp_path)
这里有乱码,修改encoding参数来使他显示为中文。encoding常用参数有utf-8、gbk、gb2312、gb18030、ascii等,如果全都不行,那就是文件制作时读取与写入方式有问题。
gdf=gpd.read_file(shp_path,encoding='utf-8' )
我们这里想要黑龙江省与辽宁省的行政区划,所以提取这两个省数据:
two_provinces=gdf[gdf['NAME' ].isin(['黑龙江省' ,'辽宁省' ])]
我们得到想要的path了吗?并没有,我们可以发现得到的只是包含两个省数据的GeoDataFrame而已。
type(two_provinces)
geopandas.geodataframe.GeoDataFrame
我们发现two_provinces的最后一列就是行政区划的几何图像,所以来提取最后一列的数据。
two_provinces['geometry' ]
type(two_provinces['geometry' ])
geopandas.geoseries.GeoSeries
我们提取几何图形,发现仍然不是path,不过我们知道cartopy有个方法可以将geometry变为path,因为方法要求参数为列表、元组或者单个的几何图形,所以先列表化再参与转化:
import cartopy.mpl.patch as cmp paths=cmp.geos_to_path(list(two_provinces['geometry' ]))
但是我们还没有达到目的,paths其实是一个存储许多path的列表,set_clip_path要求的是path:
type(paths)
list
不过matplotlib.path.Path提供了一个函数将多个path合成为一个path:
clip_path=mpath.Path.make_compound_path(*paths) type(clip_path)
matplotlib.path.Path
终于将shp文件的geometry转化为Path。
contourf图如何白化
参考前面已发布的推文。
scatter如何白化
查阅matplotlib文档,可知scatter的返回值为Pathcollection,这是个artist,可以执行set_clip_path方法。
lon,lat=np.meshgrid(ds.r.longitude.values,ds.r.latitude.values) ax1.scatter(lon[::2 ,::2 ],lat[::2 ,::2 ],transform=ccrs.PlateCarree(),marker='s' ,s=2 ,c=ds.r[0 ,5 ].values[::2 ,::2 ]) ac=ax2.scatter(lon[::2 ,::2 ],lat[::2 ,::2 ],transform=ccrs.PlateCarree(),marker='s' ,s=2 ,c=ds.r[0 ,5 ].values[::2 ,::2 ]) ac.set_clip_path(clip_path,transform=ax2.transData)
pcolormesh如何白化
查阅matplotlib文档,可知pcolormesh的返回值为matplotlib.collections.QuadMesh,这是个artist,可以执行set_clip_path方法。
ax1.pcolormesh(ds.r.longitude.values,ds.r.latitude.values,ds.r[0 ,5 ].values,transform=ccrs.PlateCarree()) ap=ax2.pcolormesh(ds.r.longitude.values,ds.r.latitude.values,ds.r[0 ,5 ].values,transform=ccrs.PlateCarree()) ap.set_clip_path(clip_path,transform=ax2.transData)
barbs如何白化
查阅matplotlib文档,可知barbs的返回值为PolyCollection,这是个artist,可以执行set_clip_path方法。
ax1.barbs(ds.longitude.values[::4 ], ds.latitude.values[::4 ], ds.u[0 ,5 ].values[::4 ,::4 ], ds.v[0 ,5 ].values[::4 ,::4 ], barb_increments={'half' :2 ,'full' :4 ,'flag' :20 }, length=4 ,linewidth=0.5 ,sizes={'emptybarb' :0 }, transform=ccrs.PlateCarree()) ab=ax2.barbs(ds.longitude.values[::4 ], ds.latitude.values[::4 ], ds.u[0 ,5 ].values[::4 ,::4 ], ds.v[0 ,5 ].values[::4 ,::4 ], barb_increments={'half' :2 ,'full' :4 ,'flag' :20 }, length=4 ,linewidth=0.5 ,sizes={'emptybarb' :0 }, transform=ccrs.PlateCarree()) ab.set_clip_path(clip_path,transform=ax2.transData)
quiver如何白化
查阅matplotlib文档,可知quiver的返回值为PolyCollection,这是个artist,可以执行set_clip_path方法。
ax1.quiver(ds.longitude.values[::4 ], ds.latitude.values[::4 ], ds.u[0 ,5 ].values[::4 ,::4 ], ds.v[0 ,5 ].values[::4 ,::4 ],scale=150 ,width=0.004 , transform=ccrs.PlateCarree()) aq=ax2.quiver(ds.longitude.values[::4 ], ds.latitude.values[::4 ], ds.u[0 ,5 ].values[::4 ,::4 ], ds.v[0 ,5 ].values[::4 ,::4 ],scale=150 ,width=0.004 , transform=ccrs.PlateCarree()) aq.set_clip_path(clip_path,transform=ax2.transData)
imshow如何白化
查阅matplotlib文档,可知imshow的返回值为image,这是个artist,可以执行set_clip_path方法。
imgarray=plt.imread(r"C:\Users\宫阙.jpg" ) ax1.imshow(imgarray, extent=ax1.get_extent(), transform=ccrs.PlateCarree()) ai=ax2.imshow(imgarray, extent=ax2.get_extent(), transform=ccrs.PlateCarree()) ai.set_clip_path(clip_path,transform=ax2.transData)
stremplot如何白化
stremplot的白化较为费劲,查阅文档可知streamplot返回两个返回值,首先对线条进行白化。
ax1.streamplot(ds.longitude.values[::4 ], ds.latitude.values[::4 ], ds.u[0 ,5 ].values[::4 ,::4 ], ds.v[0 ,5 ].values[::4 ,::4 ],linewidth=0.5 ,color='k' ,arrowsize=0.5 , transform=ccrs.PlateCarree()) ap=ax2.streamplot(ds.longitude.values[::4 ], ds.latitude.values[::4 ], ds.u[0 ,5 ].values[::4 ,::4 ], ds.v[0 ,5 ].values[::4 ,::4 ],linewidth=0.5 ,color='k' ,arrowsize=0.5 , transform=ccrs.PlateCarree()) ap.lines.set_clip_path(clip_path,transform=ax2.transData)
然后再对残留的小箭头白化:
for art in ax2.get_children(): if not isinstance(art, matplotlib.patches.FancyArrowPatch): continue else : art.set_clip_path(clip_path,transform=ax2.transData)
contour的文字如何白化
contour的标签命令为clabel,会返回任意多个Text,但是matplotlib直到今天也没有修复这个文本不能被clip的bug,所以我们只能用别的办法实现,通过判断文本的落区是否在地图内部来控制显隐。
a1=ax1.contour(ds.longitude.values, ds.latitude.values, ds.r[0 ,5 ].values,linewidths=0.5 , transform=ccrs.PlateCarree()) a1t=ax1.clabel(a1,fontsize=4 ,colors='k' )for t in a1t: t.set_bbox({'fc' : 'w' ,'ec' :'k' ,'pad' :1.25 ,'linewidth' :0.35 }) ac=ax2.contour(ds.longitude.values, ds.latitude.values, ds.r[0 ,5 ].values,linewidths=0.5 , transform=ccrs.PlateCarree()) ctext=ax2.clabel(ac,fontsize=4 ,colors='k' )for t in ctext: t.set_bbox({'fc' : 'w' ,'ec' :'k' ,'pad' :1.25 ,'linewidth' :0.35 })for col in ac.collections: col.set_clip_path(clip_path,transform=ax2.transData)import shapelyfor t in ctext: if not shapely.geometry.Polygon(clip_path.vertices).contains(shapely.geometry.Point(t.get_position())): t.set_visible(False )
总结
对于matplotlib的绝大多数绘图命令,都是可以通过set_clip_path来控制其裁剪功能,从上面这些例子中可发现其运行之规律,掌握规律与方法,就可在各种图像裁剪中游刃有余。