前言
地图白化是一种绘制地图的技术,它可以实现对感兴趣区域以外的数据进行遮盖或填充白色的效果,从而突出显示目标区域的特征。
地图白化的原理是利用 shapefile 文件中的多边形坐标来创建一个剪切路径,然后将这个路径应用到 matplotlib 的绘图对象上,使得只有路径内的数据可见,路径外的数据被隐藏或覆盖。
气象家园的五星上将兰溪之水说过,其实所谓的“白化”一般就两种途径:①mask数据;②mask图形
方法一 set_clip_path方法
matplotlib 官方函数示例
import numpy as np
import matplotlib.cm as cm
import matplotlib.pyplot as plt
from matplotlib.path import Path
from matplotlib.patches import PathPatch
delta = 0.025
x = y = np.arange(-3.0, 3.0, delta)
Y = np.meshgrid(x, y)
Z1 = np.exp(-X**2 - Y**2)
Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
Z = (Z1 - Z2) * 2
path = Path([[0, 1], [1, 0], [0, -1],
0], [0, 1]])
patch = PathPatch(path, facecolor ='none')
ax = plt.subplots()
im = ax.imshow(Z,
interpolation ='bilinear',
cmap = cm.gray,
origin ='lower',
extent =[-3, 3, -3, 3],
clip_path = patch,
clip_on = True)
Example\n\n', fontweight ="bold")
代码的目的是生成一个二维网格图,并在其中添加一个路径(path)作为剪裁(clip)区域,只显示路径内部的部分。
原图如下
ax = plt.subplots()
interpolation ='bilinear',
cmap = cm.gray,
origin ='lower',
extent =[-3, 3, -3, 3])
属于mask图片类型
读取era5数据
import xarray as xr
nc = xr.open_dataset('/home/mw/input/1107125177/2023110720.nc')
数据处理
data= nc.z[0,2,:,:]
lon=data.longitude
lat=data.latitude
导入库与生成路径
import cartopy.io.shapereader as shpreader
from cartopy.mpl.patch import geos_to_path
import matplotlib.pyplot as plt
from matplotlib.path import Path
import cartopy.crs as ccrs
import cmaps
# 读取shp文件
shp_path = '/home/mw/input/china1656/china_map/china_map/China_Province_2022.shp'
shp = shpreader.Reader(shp_path)
geo_list = list(shp.geometries())
proj = ccrs.PlateCarree()
poly = geo_list[17]
path = Path.make_compound_path(*geos_to_path(poly))
绘图部分
In [16]:
fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(projection=ccrs.PlateCarree())
cf = ax.contourf(lon, lat, data/98 ,levels=np.arange(520,600,4),transform=proj, cmap=cmaps.radar)
cr = ax.contour(lon,lat, data/98, colors='k',levels=np.arange(520,600,4))
ax.clabel(cr, inline=1, fontsize=10, fmt="%i")
ax.add_geometries(shp.geometries(), proj, facecolor='none', edgecolor='k')
ax.set_extent([100,120,20,40])
# 白化中国以外的区域
for col in cf.collections:
col.set_clip_path(path, transform=ax.transData)
# 色标
cb = plt.colorbar(cf, orientation='vertical', shrink=0.8)
plt.show()
这时候有不懂事的小朋友要问了,你这geo_list是什么,根本看不懂
哎呀,别急,马上给你print出来
geo_list[17]
你问我为什么知道广东省的序号是17,那当然是用geopandas看的
shp = gpd.read_file("/home/mw/input/china1656/china_map/china_map/China_Province_2022.shp")
shp
省 | 省级码 | 省类型 | ENG_NAME | VAR_NAME | FIRST_GID | FIRST_TYPE | year | geometry | |
---|---|---|---|---|---|---|---|---|---|
0 | 北京市 | 110000 | 直辖市 | Beijing | Běi Jīng | 110000 | Municipality | 2022 | POLYGON ((117.38335 40.22647, 117.38557 40.224... |
1 | 天津市 | 120000 | 直辖市 | Tianjin | Tiān Jīn | 120000 | Municipality | 2022 | POLYGON ((117.56937 40.19153, 117.56744 40.189... |
2 | 河北省 | 130000 | 省 | Hebei | Hé Běi | 130000 | Province | 2022 | MULTIPOLYGON (((118.26945 38.98097, 118.26871 ... |
3 | 山西省 | 140000 | 省 | Shanxi | Shān Xī | 140000 | Province | 2022 | POLYGON ((114.13714 40.73445, 114.13860 40.732... |
4 | 内蒙古自治区 | 150000 | 自治区 | Neimenggu | Nèi Měng Gǔ | 150000 | Autonomous Region | 2022 | POLYGON ((121.49813 53.32607, 121.50116 53.321... |
5 | 辽宁省 | 210000 | 省 | Liaoning | Liáo Níng | 210000 | Province | 2022 | MULTIPOLYGON (((121.03521 38.87021, 121.03528 ... |
6 | 吉林省 | 220000 | 省 | Jilin | Jí Lín | 220000 | Province | 2022 | POLYGON ((123.90309 46.29744, 123.90283 46.294... |
7 | 黑龙江省 | 230000 | 省 | Heilongjiang | Hēi Lóng Jiāng | 230000 | Province | 2022 | POLYGON ((123.40249 53.53506, 123.40471 53.535... |
8 | 上海市 | 310000 | 直辖市 | Shanghai | Shàng Hǎi | 310000 | Municipality | 2022 | MULTIPOLYGON (((121.87476 31.63516, 121.87542 ... |
9 | 浙江省 | 330000 | 省 | Zhejiang | Zhè Jiāng | 330000 | Province | 2022 | MULTIPOLYGON (((120.47933 27.15321, 120.48163 ... |
10 | 安徽省 | 340000 | 省 | Anhui | ān Huī | 340000 | Province | 2022 | POLYGON ((116.42485 34.65234, 116.43225 34.642... |
11 | 福建省 | 350000 | 省 | Fujian | Fú Jiàn | 350000 | Province | 2022 | MULTIPOLYGON (((117.29228 23.59563, 117.29206 ... |
12 | 江西省 | 360000 | 省 | Jiangxi | Jiāng Xī | 360000 | Province | 2022 | POLYGON ((116.68416 30.07160, 116.68576 30.070... |
13 | 山东省 | 370000 | 省 | Shandong | Shān Dōng | 370000 | Province | 2022 | MULTIPOLYGON (((119.92414 35.62384, 119.92294 ... |
14 | 河南省 | 410000 | 省 | Henan | Hé Nán | 410000 | Province | 2022 | MULTIPOLYGON (((111.02770 33.17911, 111.02767 ... |
15 | 湖北省 | 420000 | 省 | Hubei | Hú Běi | 420000 | Province | 2022 | MULTIPOLYGON (((113.12740 29.43223, 113.11645 ... |
16 | 湖南省 | 430000 | 省 | Hunan | Hú Nán | 430000 | Province | 2022 | MULTIPOLYGON (((109.47771 26.84005, 109.47793 ... |
17 | 广东省 | 440000 | 省 | Guangdong | Guǎng Dōng | 440000 | Province | 2022 | MULTIPOLYGON (((110.59023 20.37852, 110.59232 ... |
18 | 广西壮族自治区 | 450000 | 自治区 | Guangxi | Guǎng Xī | 450000 | Autonomous Region | 2022 | MULTIPOLYGON (((109.20674 20.91898, 109.20686 ... |
19 | 海南省 | 460000 | 省 | Hainan | Hǎi Nán | 460000 | Province | 2022 | MULTIPOLYGON (((112.04381 3.83812, 112.01370 3... |
20 | 重庆市 | 500000 | 直辖市 | Chongqing | Chóng Qìng | 500000 | Municipality | 2022 | POLYGON ((109.57960 31.72849, 109.58644 31.725... |
21 | 四川省 | 510000 | 省 | Sichuan | Sì Chuān | 510000 | Province | 2022 | POLYGON ((102.95840 34.27996, 102.95933 34.270... |
22 | 贵州省 | 520000 | 省 | Guizhou | Guì Zhōu | 520000 | Province | 2022 | MULTIPOLYGON (((105.09467 24.92520, 105.09458 ... |
23 | 云南省 | 530000 | 省 | Yunnan | Yún Nán | 530000 | Province | 2022 | POLYGON ((99.11276 29.21149, 99.11737 29.20723... |
24 | 西藏自治区 | 540000 | 自治区 | Xizang | Xī Zàng | 540000 | Autonomous Region | 2022 | POLYGON ((88.38821 36.47854, 88.38945 36.47845... |
25 | 陕西省 | 610000 | 省 | Shaanxi | Shǎn Xī | 610000 | Province | 2022 | POLYGON ((108.13454 36.57919, 108.13418 36.580... |
26 | 甘肃省 | 620000 | 省 | Gansu | Gān Sù | 620000 | Province | 2022 | POLYGON ((97.19051 42.76287, 97.23601 42.67222... |
27 | 青海省 | 630000 | 省 | Qinghai | Qīng Hǎi | 630000 | Province | 2022 | POLYGON ((100.91694 38.17344, 100.91780 38.173... |
28 | 宁夏回族自治区 | 640000 | 自治区 | Ningxia | Níng Xià Huí Zú | 640000 | Autonomous Region | 2022 | MULTIPOLYGON (((106.06218 35.43728, 106.06239 ... |
29 | 新疆维吾尔自治区 | 650000 | 自治区 | Xinjiang | Xīn Jiāng | 650000 | Autonomous Region | 2022 | POLYGON ((87.79720 49.18060, 87.81916 49.17268... |
30 | 台湾省 | 710000 | 省 | Taiwan | Tái Wān | 710000 | Province | 2022 | MULTIPOLYGON (((123.69793 25.92930, 123.69726 ... |
31 | 香港特别行政区 | 810000 | 特别行政区 | HongKong | Hong Kong | 810000 | Special District | 2022 | MULTIPOLYGON (((114.22665 22.54375, 114.22661 ... |
32 | 澳门特别行政区 | 820000 | 特别行政区 | Aomen | ào Mén | 820000 | Special District | 2022 | MULTIPOLYGON (((113.55346 22.21547, 113.55374 ... |
33 | 江苏省 | 320000 | 省 | Jiangsu | Jiāng Sū | 320000 | Province | 2022 | MULTIPOLYGON (((121.56617 32.22928, 121.56693 ... |
这时这个方法就显示出弊端,读者的shp千变万化,没安装geopandas又对自己的shp列表不熟,则要一一排查geolist
方法二 rioxarray的rio.clip
import os
import numpy as np
import geopandas as gpd
import rioxarray
import xarray as xr
from shapely.geometry import mapping
import matplotlib.pyplot as plt
# 读取中国省份地图数据
shp = shpreader.Reader(shp_path)
shpgpd = gpd.read_file(shp_path)
# 设置数据投影
data.rio.write_crs("epsg:4326", inplace=True)
# 进行裁剪并绘图
cliped = data.rio.clip(shpgpd.geometry.apply(mapping), shpgpd.crs, drop=False)
fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(projection=ccrs.PlateCarree())
cf = ax.contourf(lon, lat, cliped/98,levels=np.arange(520,600,4), transform=proj, cmap=cmaps.radar)
cr = ax.contour(lon,lat, cliped/98,levels=np.arange(520,600,4), colors='k')
ax.clabel(cr, inline=1, fontsize=10, fmt="%i")
ax.add_geometries(shp.geometries(), proj, facecolor='none', edgecolor='k')
ax.set_extent([100,120,20,40])
cb = plt.colorbar(cf, orientation='vertical', shrink=0.8)
plt.show()
以上就是比较典型的mask数据类,此类锯齿较明显
拓展:一个省怎么画:使用裁剪后的行政区json进行绘图
gd = shpgpd[shpgpd['省'] == '广东省']
gd.to_file('/home/mw/project/gd.geojson', driver='GeoJSON')
# 读取中国省份地图数据
js_path = '/home/mw/project/gd.geojson'
gpdjs = gpd.read_file(js_path)
# 设置数据投影
data.rio.write_crs("epsg:4326", inplace=True)
# 进行裁剪并绘图
cliped = data.rio.clip(gpdjs.geometry.apply(mapping), gpdjs.crs, drop=False)
fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(projection=ccrs.PlateCarree())
cf = ax.contourf(lon, lat, cliped/98,levels=np.arange(520,600,4), transform=proj, cmap=cmaps.radar)
cr = ax.contour(lon,lat, cliped/98,levels=np.arange(520,600,4), colors='k')
ax.clabel(cr, inline=1, fontsize=10, fmt="%i")
ax.add_geometries(shp.geometries(), proj, facecolor='none', edgecolor='k')
ax.set_extent([100,120,20,40])
plt.show()
方法一参考云台书使:气象绘图加强版(三十)——白化补叙
方法二参考:rioxarray官方文档