在繪制圖形,尤其是涉及地理信息的分布圖時,為了使圖形更加美觀或者更有效地突出重點信息,我們常常需要對一些非關(guān)鍵要素進行掩蓋或隱藏處理。這種做法不僅能夠更快地抓住圖表的核心內(nèi)容,還能增強視覺效果。例如,在制作某個省份的氣溫分布圖時,我們的主要目的是展示該省份內(nèi)部不同地點的氣溫變化情況。此時,如果將地圖上屬于其他省份的數(shù)據(jù)也一并顯示出來,則可能會分散讀者注意力,并且使得圖表顯得雜亂無章。為了解決這個問題,接可能需要“掩膜”來實現(xiàn)只保留特定區(qū)域(如目標省份)的數(shù)據(jù)而屏蔽掉其余部分。 在剛開始的時候,我一直使用的是下面的語句: countries=BasicReader('./data.shp') geo_list=list(countries.geometries()) poly=geo_list path=Path.make_compound_path(*geos_to_path(poly)) ...省略畫圖部分... contour = ax.contourf(...) ...省略畫圖部分... for col in contour.collections: col.set_clip_path(path,ccrs.PlateCarree()._as_mpl_transform(ax))
簡單來說,就是以下4步: - 定義地理邊界 → 從 Shapefile 讀取國家邊界
- 轉(zhuǎn)換路徑格式 → Matplotlib 可識別的
Path 對象 - 繪制數(shù)據(jù) → 使用
contourf 繪制原始等值線圖(可能覆蓋整個矩形區(qū)域) - 應用裁剪 → 只保留國家邊界內(nèi)的可見部分
但最近在用的時候,卻發(fā)現(xiàn)以往很好用的代碼報錯了,報錯信息如下: AttributeError:'GeoContourSet' object has no attribute 'collections'
經(jīng)檢測,應該是matplotlib版本問題,因此,又把裁剪代碼改成下列形式使用: for artist in ax_main.get_children(): if hasattr(artist,'get_paths')and len(artist.get_paths())>0: artist.set_clip_path(path, ax_main.transData)
- 裁剪路徑(Clip Path):類似蒙版,只有路徑內(nèi)的圖形會顯示,路徑外的部分被隱藏。
- 坐標系對齊 (
**transData** *): *路徑的坐標必須與圖形元素的坐標系一致。使用 ax_main.transData 表示路徑的坐標值基于數(shù)據(jù)范圍(例如經(jīng)緯度、物理單位)。 - 篩選條件 (
get_paths ):支持的圖形類型: ContourSet (等值線填充)、 PolyCollection (多邊形集合)、 LineCollection (線條集合)等。
此時,又可以畫出這種圖了,又能用100年了。 但又想畫出南海小地圖感覺,剛開始我以為,只需要簡單加個范圍限制就可以: south_china_sea_bbox =[105,125,0,30] ax_south_china.set_extent(south_china_sea_bbox, crs=ccrs.PlateCarree())
然后畫成了這樣,裁剪區(qū)域后,設置范圍并沒有按預想的演示。 苦思冥想一會,加上數(shù)據(jù)篩選解決問題。 combined_mask = (lon >= min_lon) & (lon <= max_lon) & (lat >= min_lat) & (lat <= max_lat) south_data = np.full_like(data, np.nan) south_data[combined_mask] = data[combined_mask]
最后完整畫圖代碼: from cartopy.io.shapereader import BasicReader from matplotlib.path import Path from cartopy.mpl.patch import geos_to_path import numpy as np import matplotlib.pyplot as plt import cartopy.crs as ccrs from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER import netCDF4 as nc import matplotlib as mpl import warnings
warnings.filterwarnings("ignore") mpl.rcParams['font.sans-serif'] = [u'SimHei'] mpl.rcParams['axes.unicode_minus'] = False binary_colors = ['#CE4A4B', '#D68F3B'] binary_cmap = mpl.colors.ListedColormap(binary_colors)
def picture(lon, lat, data, name):
countries = BasicReader("./中華人民共和國.shp") geo_list = list(countries.geometries()) poly = geo_list path = Path.make_compound_path(*geos_to_path(poly))
south_china_sea_bbox = [105, 125, 0, 30] china_bbox = [70, 140, 0, 55] min_lon, max_lon, min_lat, max_lat = south_china_sea_bbox
fig = plt.figure(figsize=(12, 8)) ax_main = fig.add_subplot(111, projection=ccrs.PlateCarree())
contour = ax_main.contourf(lon, lat, data, transform=ccrs.PlateCarree(), cmap=binary_cmap, levels=[-0.5, 0.5, 1.5], vmin=-0.5, vmax=1.5, extend='neither') ax_main.add_geometries(countries.geometries(), linewidth=1., edgecolor='black', crs=ccrs.PlateCarree(), facecolor='none') ax_main.set_extent(china_bbox, crs=ccrs.PlateCarree()) g1 = ax_main.gridlines(draw_labels=True, linewidth=1, color='gray', alpha=0.5, linestyle='--') g1.top_labels = False g1.right_labels = False g1.xformatter = LONGITUDE_FORMATTER g1.yformatter = LATITUDE_FORMATTER g1.rotate_labels = False for artist in ax_main.get_children(): if hasattr(artist, 'get_paths') and len(artist.get_paths()) > 0: artist.set_clip_path(path, ax_main.transData)
ax_main.set_title(name, fontsize=16, pad=20) ax_south_china = fig.add_axes([0.60, 0.24, 0.15, 0.15], projection=ccrs.PlateCarree()) combined_mask = (lon >= min_lon) & (lon <= max_lon) & (lat >= min_lat) & (lat <= max_lat) south_data = np.full_like(data, np.nan) south_data[combined_mask] = data[combined_mask] contour_south = ax_south_china.contourf(lon, lat, south_data, transform=ccrs.PlateCarree(), cmap=binary_cmap, levels=[-0.5, 0.5, 1.5], vmin=-0.5, vmax=1.5, extend='neither')
ax_south_china.add_geometries(countries.geometries(), linewidth=1., edgecolor='black', crs=ccrs.PlateCarree(), facecolor='none') ax_south_china.set_extent(south_china_sea_bbox, crs=ccrs.PlateCarree()) for artist in ax_south_china.get_children(): if hasattr(artist, 'get_paths') and len(artist.get_paths()) > 0: artist.set_clip_path(path, ax_south_china.transData)
如果可以安裝新庫的話,推薦一個更簡單的方法:cnmaps庫,讓地圖處理更簡單。
|