matplotlib官网
气象绘图参考网站
色标
地图选择器
ChinaMap
主要库cartopy绘制地图、matplotlib绘图
matplotlib 是从MATLAB启发而创建的,这俩的命令几乎一模一样,所以概念基本完全相同,可参考MATLAB官方文档
关键概念
Axes (笛卡尔坐标区):
Axis (坐标轴)
Subplot (子图)
Plot (线图)
Figure (图窗)
绘图安装的库
conda install -c conda-forge numpy scipy pandas xarray geopandas matplotlib cartopy scikit-image metpy cfgrib
cartopy需要0.18.0conda install -c conda-forge cartopy=0.18.0
import matplotlib.pyplot as plt
Figure创建窗口,类似于画板,Axes/Subplot创建子图,类似于画纸。所有的绘画只能在子图上进行。plt表示当前子图,若没有就创建一个子图
Figure:面板(图),matplotlib中的所有图像都是位于figure对象中,一个图像只能有一个figure对象。
Subplot:子图,figure对象下创建一个或多个subplot对象(即axes)用于绘制图像。
配置参数:
axex: 设置坐标轴边界和表面的颜色、坐标刻度值大小和网格的显示
figure: 控制dpi、边界颜色、图形大小、和子区( subplot)设置
font: 字体集(font family)、字体大小和样式设置
grid: 设置网格颜色和线性
legend: 设置图例和其中的文本的显示
line: 设置线条(颜色、线型、宽度等)和标记
patch: 是填充2D空间的图形对象,如多边形和圆。控制线宽、颜色和抗锯齿设置等。
savefig: 可以对保存的图形进行单独设置。例如,设置渲染的文件的背景为白色。
verbose: 设置matplotlib在执行期间信息输出,如silent、helpful、debug和debug-annoying。
xticks和yticks: 为x,y轴的主刻度和次刻度设置颜色、大小、方向,以及标签大小。
1、坐标轴曲线
x = np.arange(-5, 5, 0.1)
y = x * 3
fig = plt.figure() # 1个窗口简化写法
# fig = plt.figure(num=1, figsize=(15, 8),dpi=80) #详细写法,设置大小,分辨率
ax1 = fig.add_subplot(2,1,1) # 同fig.add_subplot(211)通过fig添加子图,参数:行数,列数,第几个。1个可省略
# 括号里面的值前两个是轴域原点坐标(从左下角计算的),后两个是显示坐标轴的长度。
ax1 = fig.add_axes([0.1, 0.1, 0.8, 0.8]) #add_axes()生成子图的灵活性更强,完全可以实现add_subplot()方法的功能
# ax2 = fig.add_subplot(2,1,2) #通过fig添加子图,参数:行数,列数,第几个。1个可省略
# ax1 = fig.add_subplot(111, facecolor='r') #设置背景颜色
plt.plot(x, y) # 画图
plt.scatter(x, y, c='r') # 设置颜色
plt.xlabel('X Axis') #设置x轴名称 ax1.set_xlabel
plt.ylabel('Y Axis') #设置Y轴名称 ax1.set_ylabel
plt.title('this is a demo') # 设置标题
plt.xlim((-5, 5)) # 设置横轴范围 ax1.set_xlim 可略,默认x,y 所有值范围
plt.ylim((-15, 15)) # 设置纵轴范围 ax1.set_ylim 可略
plt.savefig('d:/f.png') # 保存图片,plt.savefig('aa.jpg', dpi=400, bbox_inches='tight') dpi分辨率,bbox_inches子图周边白色空间的大小
# 左右下上4个轴
# 设置轴的位置
ax1.spines['left'].set_position('center')
# 设置轴的颜色
ax1.spines['right'].set_color('none')
# 设置轴的位置
ax1.spines['bottom'].set_position('center')
# 设置轴的颜色
ax1.spines['top'].set_color('none')
# 显示网格。which参数的值为major(只绘制大刻度)、minor(只绘制小刻度)、both,默认值为major。axis为'x','y','both'
ax1.grid(b=True, which='major', axis='both', alpha=0.5, color='skyblue', linestyle='--', linewidth=2)
# ax1.set_xticks([]) # 去除坐标轴刻度
ax1.set_xticks((-5, -3, -1, 1, 3, 5)) # 设置坐标轴刻度
ax1.text(2.8, 7, r'y=3*x') # 指定位置显示文字,plt.text()
axes1 = plt.axes([.2, .3, .1, .1], facecolor='y') # 在当前窗口添加一个子图,rect=[左, 下, 宽, 高],是使用的绝对布局,不和以存在窗口挤占空间
axes1.plot(x, y) # 在图上画子图
ax1.annotate('important point', xy=(2, 6), xytext=(3, 1.5), # 添加标注,参数:注释文本、指向点、文字位置、箭头属性
arrowprops=dict(facecolor='black', shrink=0.05),)
plt.show() #所有窗口运行
ax.invert_yaxis() # 反转y轴
2、绘制轮廓
1、 类似于等高线(等值线) matplotlib.pyplot.contour
# 高斯滤波,K为黑色轮廓
cs = ax.contour(dat.latitude.values, dat.level.values,
gaussian_filter(dat.values, 2), colors='k')
2、填充轮廓(色斑图) contourf
ax.contourf(abn.latitude.values, abn.level.values,
abn.values, extend='both', levels=level_abn,
cmap=cmap_abn, norm=norm_abn)
3、风杆图
default_barb_style = {'length': 5,
'sizes': {'emptybarb': 0.25, 'spacing': 0.2, 'height': 0.5},
'barb_increments': {'half': 2, 'full': 4, 'flag': 20},
'flagcolor': 'grey'
}
ax.barbs(lon, lat, u, v, transform=ccrs.PlateCarree(), **default_barb_style)
4、色标
import matplotlib.pyplot as plt
# norm = mpl.colors.Normalize(vmin=-10, vmax=10)
lvs = np.arange(-10, 11, 1)
cf = ax.contourf(lon_var, lat_var,
2 * dat_a.to(units('delta_degC/sec')) * 1e4, extend='both',
levels=lvs, cmap=plt.get_cmap('RdBu_r'), # norm=norm,
transform=ccrs.PlateCarree())
cax = add_axes(fig, ax_provision)
cb = fig.colorbar(cf, cax=cax, extend='both', fraction=0.03,
extendfrac='auto', extendrect=True, drawedges=True)
cb.set_ticks(lvs) # 设置色标刻度
cb.ax.tick_params(labelsize=14)
norm :用于规范化–设置颜色条最大最小值
RdBu_r是RdBu色带的倒转
5、设置y轴比例 matplotlib.axes.Axes.set_yscale
# 仅绘制正值 正整数 int序列
ax.set_yscale('log') # 默认值:'clip'
b=蓝色 g=绿色 r=红色 y=黄色 c=青色 k=黑色 m=洋红色 w=白色
6、10的-4次方描述
f'$10^{{-4}}$'
7、中文显示问题
import matplotlib
# 指定默认字体
matplotlib.rcParams['font.sans-serif'] = ['SimHei']
matplotlib.rcParams['font.family'] ='sans-serif'
# 解决负号'-'显示为方块的问题
matplotlib.rcParams['axes.unicode_minus'] = False
查看centos是否有中文
fc-list :lang=zh #fc-list :lang=zh family
>>> import matplotlib
>>> matplotlib.matplotlib_fname()
'/data/software/anaconda3/envs/py36/lib/python3.6/site-packages/matplotlib/mpl-data/matplotlibrc'
字体目录
/data/software/anaconda3/envs/py36/lib/python3.6/site-packages/matplotlib/mpl-data/fonts/ttf/
当前字体种类
from matplotlib import font_manager
for font in font_manager.fontManager.ttflist:
print(font.name, '-', font.fname)
注意:matplotlib版本影响字体属性
[l.set_family("Times New Roman") for l in cb.ax.yaxis.get_ticklabels()]
#gl.xlabel_style = {'size': 14, 'fontfamily': 'Times New Roman'}
gl.xlabel_style = {'size': 14, 'family': 'Times New Roman'}
gl.ylabel_style = {'size': 14, 'family': 'Times New Roman'}
注: https://mathsyouth.github.io/2019/06/12/macos-matplotlib
Cartopy
1、安装Cartopy库0.18.0,必须的库Shapely、pyshp
conda install -c conda-forge cartopy=0.18.0
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
plt.figure(figsize=(6, 3))
ax = plt.axes(projection=ccrs.PlateCarree(central_longitude=180))
ax.coastlines(resolution='110m')
ax.gridlines()
可以运行即可
2、下载地图数据 http://www.naturalearthdata.com/downloads/
有三种分辨率的shape地图数据,分别下载三种分辨率中的physical数据中的Coastline和Land数据,
如:ne_10m_coastline.zip,解压后copy到文件夹C:\Users\Administrator.local\share\cartopy\shapefiles\natural_earth\physical
运行1中的程序后,程序会自动创建physical文件夹,默认使用110m分辨率
下载最新版本失败,就下载历史版本
注:如果想使用自己的地图,比如我有一个带我国法定边界的地图shape文件,名为:World.shp、World.shx、World.dbf、World.prj,重新命名为10m_coastline或10m_land文件(这里要根据该shape文件是line型还是polygon型),替换原目录下的shape文件,画图时使用scale为10m即调用到了自己的地图
色斑图示例
import os
import logging
from pathlib import Path
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import font_manager
import cartopy.crs as ccrs
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
from matplotlib import cm
import geopandas as gpd
import config
MODULE_NAME = os.path.split(__file__)[-1].split(".")[0]
logger = logging.getLogger(f'module.utils.{MODULE_NAME}')
gdf = gpd.GeoDataFrame.from_file(os.path.join(config.JSON_ROOT, 'shandong', 'shandong.json'))
plt.rcParams['font.family'] = 'sans-serif'
plt.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans', 'Microsoft YaHei', 'TimesNewRoman', 'Arial']
plt.rcParams['axes.unicode_minus'] = False
font_dirs = [os.path.join(config.STATIC_PATH, 'font'), ]
font_files = font_manager.findSystemFonts(fontpaths=font_dirs)
if hasattr(font_manager.fontManager, 'addfont') and callable(font_manager.fontManager.addfont):
[font_manager.fontManager.addfont(font) for font in font_files]
else:
font_list = font_manager.createFontList(font_files)
font_manager.fontManager.ttflist.extend(font_list)
def run():
micaps4 = '/test/2021012510'
time = Path(micaps4).name
file = open(micaps4, mode='rt', encoding='gbk')
tmp = file.readlines()
row = int(tmp[0].strip().split()[16]) # 行数
column = int(tmp[0].strip().split()[15]) # 列数
min_lat = float(tmp[0].strip().split()[13])
max_lat = float(tmp[0].strip().split()[14])
min_lon = float(tmp[0].strip().split()[11])
max_lon = float(tmp[0].strip().split()[12])
lat_step = float(tmp[0].strip().split()[10])
lon_step = float(tmp[0].strip().split()[9])
logger.debug(f'micaps4 :{row, column, min_lat, max_lat, min_lon, max_lon, lat_step, lon_step}')
lons = np.linspace(min_lon, max_lon, column)
lats = np.linspace(min_lat, max_lat, row)
tmp.pop(0)
grid_data = np.zeros((row, column))
for i in range(0, row):
aa = tmp[i].strip().split()
for j in range(len(aa)):
grid_data[i][j] = float(aa[j])
logger.debug(f'plot region: {(min_lon, max_lon, min_lat, max_lat)}')
fig = plt.figure(dpi=150)
ax = fig.add_subplot(1, 1, 1, projection=ccrs.PlateCarree())
ax.set_extent((min_lon, max_lon, min_lat, max_lat), crs=ccrs.PlateCarree())
# ax.coastlines(linewidth=0.5) #边界
gridlines_style = {'draw_labels': True, 'linestyle': '--', 'alpha': 0.7}
gl = ax.gridlines(
ylocs=np.linspace(min_lat, max_lat, 1),
xlocs=np.linspace(min_lon, max_lon, 1),
**gridlines_style
)
gl.xlabels_top = False # 关闭顶端的经纬度标签 gl.top_labels = False
gl.ylabels_right = False # 关闭右侧的经纬度标签 gl.right_labels = False
gl.xformatter = LONGITUDE_FORMATTER # x轴设为经度的格式
gl.yformatter = LATITUDE_FORMATTER # y轴设为纬度的格式
name_station = {"济南": [117, 36.65]}
for key, value in name_station.items(): # 打印站点
ax.scatter(value[0], value[1], marker='.', s=60, color="k", zorder=3)
ax.text(value[0] - 0.07, value[1] + 0.03, key, fontsize=7, color="k")
title_main = f'山东省{time} 时温度色斑图'
title_sub = f"副标题xxx"
ax.set_title(f'{title_main} \n {title_sub}', fontsize=18)
ax.set_title(f'阈值: ', loc='right', fontsize=16)
ax.add_geometries(gdf['geometry'], crs=ccrs.PlateCarree(), facecolor='', edgecolor='#2B2B2B')
# ax.add_geometries(gdf['geometry'][0], crs=ccrs.PlateCarree(), facecolor='', edgecolor='#2B2B2B') #仅显示济南市
cf = ax.contourf(lons, lats, grid_data,
transform=ccrs.PlateCarree(),
cmap=cm.get_cmap('bwr'))
cb = fig.colorbar(cf, orientation='horizontal', shrink=0.8)
cb.ax.tick_params(labelsize=14)
[l.set_family("Times New Roman") for l in cb.ax.yaxis.get_ticklabels()]
gl.xlabel_style = {'size': 14, 'family': 'Times New Roman'}
gl.ylabel_style = {'size': 14, 'family': 'Times New Roman'}
[i.set_linewidth(2) for i in ax.spines.values()]
file_name = f'{time}.png'
save_file = Path(config.ROOT_PATH) / 'png' / 'seban' / file_name
if not save_file.parent.exists():
save_file.parent.mkdir(parents=True)
fig.savefig(save_file, dpi=100)
错误
1、linux运行py弹窗:需要Xmanager软件来处理X11转发请求
import matplotlib
matplotlib.use('Agg') #使用非交互式后端,避免创建窗口
import matplotlib.pyplot as plt # matplotlib.use('Agg')必须在本句执行前运行
参考:
https://www.zhihu.com/question/51745620
https://www.jianshu.com/p/da385a35f68d
https://scitools.org.uk/cartopy/docs/latest/matplotlib/advanced_plotting.html
http://bbs.06climate.com/forum.php?mod=viewthread&tid=33601&extra=page%3D1
2、Reinstalling the application may fix this problem. 已放弃(吐核)
通过strace python xxx.py
跟踪
stat("/data/software/anaconda3/envs/py36/bin/qt.conf", {st_mode=S_IFREG|0775, st_size=201, ...}) = 0
stat("/data/software/anaconda3/envs/py36/bin/qt.conf", {st_mode=S_IFREG|0775, st_size=201, ...}) = 0
stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=405, ...}) = 0
stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=405, ...}) = 0
stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=405, ...}) = 0
stat("/opt/anaconda1anaconda2anaconda3/plugins", 0x7fffc834d1a0) = -1 ENOENT (No such file or directory) ##注意该处,路径错误
lstat("/data", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat("/data/software", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat("/data/software/anaconda3", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat("/data/software/anaconda3/envs", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat("/data/software/anaconda3/envs/py36", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat("/data/software/anaconda3/envs/py36/bin", {st_mode=S_IFDIR|0755, st_size=12288, ...}) = 0
stat("/data/software/anaconda3/envs/py36/bin", {st_mode=S_IFDIR|0755, st_size=12288, ...}) = 0
stat("/data/software/anaconda3/envs/py36/bin/platforms/.", 0x7fffc834d1d0) = -1 ENOENT (No such file or directory)
open("/opt/anaconda1anaconda2anaconda3/qtlogging.ini", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/root/.config/QtProject/qtlogging.ini", 0x7fffc834d100) = -1 ENOENT (No such file or directory)
stat("/etc/xdg/QtProject/qtlogging.ini", 0x7fffc834d100) = -1 ENOENT (No such file or directory)
open("/dev/tty", O_RDONLY|O_CLOEXEC) = 4
fcntl(4, F_SETFD, FD_CLOEXEC) = 0
close(4) = 0
write(2, "This application failed to start"..., 155This application failed to start because it could not find or load the Qt platform plugin "xcb"
in "".
Reinstalling the application may fix this problem.
) = 155
rt_sigprocmask(SIG_UNBLOCK, [ABRT], NULL, 8) = 0
tgkill(159944, 159944, SIGABRT) = 0
--- SIGABRT {si_signo=SIGABRT, si_code=SI_TKILL, si_pid=159944, si_uid=0} ---
+++ killed by SIGABRT (core dumped) +++
已放弃(吐核)
缺少PyQt5库
"qt.qpa.screen: QXcbConnection: C"..., 75qt.qpa.screen: QXcbConnection: Could not connect to display localhost:10.0
解决方案很简单:
export DISPLAY=':0.0'
如果不行的话则尝试
export DISPLAY=':1.0'或export DISPLAY=':2.0'
至于为何时1.0或2.0或0.0,
可以在终端执行:
echo ${DISPLAY}
显示的是什么配置成什么就行了
3、matplotlib错误ValueError: Expected 2-dimensional array, got 1
#ax.add_geometries(gdf['geometry'], crs=ccrs.PlateCarree(), facecolor='', edgecolor='#2B2B2B')
ax.add_geometries(gdf['geometry'], crs=ccrs.PlateCarree(), facecolor='none', edgecolor='#2B2B2B')