Cartopy画地图第八天(冷空气南下,NCL色标使用)

Cartopy画地图第八天(冷空气南下,NCL色标使用)

相信很多朋友都看过这张漂亮的图吧

Cartopy画地图第八天(冷空气南下,NCL色标使用)
这是中国气象爱好者公众号画的一张反映冷空气的图片,当时就被惊艳到了,本帖就来复刻一下这张图。
本次的知识点大概总结一下就是:
1.画风的箭头流场
2.单画某一根等值线,比如588,0℃
3.引入NCL色标
4.制作gif图片
先上个效果图
Cartopy画地图第八天(冷空气南下,NCL色标使用)

; 一、分析图片

中国气象爱好者的图片拿到手以后开始分析
1.500的位势高度画等值线
2.850的温度填色
3.0℃等值线
4.588画等值线
5.850风场的箭头流场
6.注意投影方式
其实也不难,一个一个画就好了。

二、函数准备

(一)、使用NLC色标的函数

import re
import matplotlib.cm
from matplotlib import colors
import matplotlib.pyplot as plt
import numpy as np
class Colormap(colors.ListedColormap):
    def __init__(self, c, name='from_list', n=None):
        self.colors = c
        self.name = name
        self.N = n
        super(Colormap, self).__init__(self.colors, name=self.name, N=self.N)

    def __getitem__(self, item):
        return Colormap(self.colors[item], name='sliced_' + self.name)

    def show(self):
        a = np.outer(np.ones(10), np.arange(0, 1, 0.001))
        plt.figure(figsize=(2.5, 0.5))
        plt.subplots_adjust(top=0.95, bottom=0.05, left=0.01, right=0.99)
        plt.subplot(111)
        plt.axis('off')
        plt.imshow(a, aspect='auto', cmap=self, origin='lower')
        plt.text(0.5, 0.5, self._name,
                 verticalalignment='center', horizontalalignment='center',
                 fontsize=12, transform=plt.gca().transAxes)
        plt.show()
def coltbl(cmap_file):
        pattern = re.compile(r'(\d\.?\d*)\s+(\d\.?\d*)\s+(\d\.?\d*).*')
        with open(cmap_file) as cmap:
            cmap_buff = cmap.read()
        cmap_buff = re.compile('ncolors.*\n').sub('', cmap_buff)
        if re.search(r'\s*\d\.\d*', cmap_buff):
            return np.asarray(pattern.findall(cmap_buff), 'f4')
        else:
            return np.asarray(pattern.findall(cmap_buff), 'u1') / 255.

def get_my_cmaps(cname):
    try:
        if cname in matplotlib.cm._cmap_registry:
            return matplotlib.cm.get_cmap(cname)
    except:
        pass
    cmap_file = os.path.join( cname+ ".rgb")
    cmap = Colormap(coltbl(cmap_file), name=cname)
    matplotlib.cm.register_cmap(name=cname, cmap=cmap)
    return cmap

一个类,俩函数,大家可以研究一下怎么实现引入NCL色标的,这里因为篇幅的关系,就不介绍了,有兴趣的朋友可以留言,下次单独出一期来介绍引入NCL色标的原理。

(二)、数据读取的函数

本次数据使用的是Micaps导出的二进制欧细资料,读取函数如下

from read_mdfs import MDFS_Grid
from scipy.interpolate import interpolate
def get_lat_lon(filepath):
    a = MDFS_Grid(filepath)
    lon = a.data['Lon']
    lat = a.data['Lat'][::-1]
    lon_scipy = np.arange(lon.min(), lon.max(), 0.05)
    lat_scipy = np.arange(lat.min(), lat.max(), 0.05)
    return lon,lat,lon_scipy,lat_scipy
def read_data_hgt_or_tmp(filepath,lon,lat,lon_scipy,lat_scipy):
    a = MDFS_Grid(filepath)
    data = a.data['Grid'][::8,::8]
    spline = interpolate.RectBivariateSpline(lat[::8], lon[::8],data,)
    data = spline(lat_scipy,lon_scipy)
    return data[::-1,:]
def read_data(filepath):
    a = MDFS_Grid(filepath)
    data = a.data['Grid']
    return data[::-1,:]

(三)、地图数据读取

后面画图为了做gif图片,所以要画好几次地图,但是读取地图数据可以一次完成,想办法减少运行时间是个好习惯哦。

with open('CN-border-La.dat') as src:
    context = src.read()
    blocks = [cnt for cnt in context.split('>') if len(cnt) > 0]
    borders = [np.fromstring(block, dtype=float, sep=' ') for block in blocks]

(四)、其他准备

fig = plt.figure(figsize=(47, 30), dpi=30)
frames = []
TIMES = ['000','003','006','009','012']

三、循环画图

(一)、读取数据

for TIME in TIMES:

    file_path = r"ECMWF_HR\ECMWF_HR_HGT_500_21122420." + TIME
    lon ,lat,lon_scipy,lat_scipy = get_lat_lon(file_path)
    hgt_500 = read_data_hgt_or_tmp(file_path,lon ,lat,lon_scipy,lat_scipy)

    file_path = r"ECMWF_HR\ECMWF_HR_TMP_850_21122420." + TIME
    tem_850 = read_data_hgt_or_tmp(file_path,lon ,lat,lon_scipy,lat_scipy)

    file_path = r"ECMWF_HR\ECMWF_HR_UGRD_850_21122420." + TIME
    u_850 = read_data(file_path)

    file_path = r"ECMWF_HR\ECMWF_HR_VGRD_850_21122420." + TIME
    v_850 = read_data(file_path)

(二)、画图和地图设置


    ax = fig.add_axes([0.02, 0.05, 0.9, 0.9], projection=ccrs.LambertConformal(central_latitude=90, central_longitude=105))
    extent = [80,130,20,55]
    ax.set_extent(extent,crs=ccrs.Geodetic())
    gl = ax.gridlines( draw_labels=True, linewidth=2, color='k', alpha=0.5, linestyle='--')
    gl.xformatter = LONGITUDE_FORMATTER
    gl.yformatter = LATITUDE_FORMATTER
    gl.xlabel_style = {'size': 30}
    gl.ylabel_style = {'size': 30}
    resolution_map = '50m'
    ax.add_feature(cfeature.OCEAN.with_scale(resolution_map))
    ax.add_feature(cfeature.LAND.with_scale(resolution_map))
    ax.add_feature(cfeature.RIVERS.with_scale(resolution_map))
    ax.add_feature(cfeature.LAKES.with_scale(resolution_map))

    for line in borders:
        ax.plot(line[0::2], line[1::2], '-', color='k',transform=ccrs.Geodetic())

这些也不介绍了,在之前的帖子有介绍,可以往前学习。

(三)、500位势高度和588、0℃线绘制


    ct = ax.contour(lon_scipy, lat_scipy, hgt_500, 20, colors='k',linewidths=3, transform=ccrs.PlateCarree())
    ax.clabel(ct, inline=True, fontsize=30, fmt='%d')

    clev = [588]
    ct = ax.contour(lon_scipy, lat_scipy, hgt_500, clev, colors='r',linewidths=3, transform=ccrs.PlateCarree())
    ax.clabel(ct, inline=True, fontsize=30, fmt='%d')

    clev = [0.0]
    ct = ax.contour(lon_scipy, lat_scipy, tem_850, clev, colors='w',linewidths=5, transform=ccrs.PlateCarree())
    ax.clabel(ct, inline=True, fontsize=30, fmt='%d')

(四)、850温度绘制


    clevs = range(-45,46,3)

    my_cmap = get_my_cmaps("MPL_hsv")

    my_cmap = my_cmap.reversed()

    cf = ax.contourf(lon_scipy, lat_scipy, tem_850, clevs, transform=ccrs.PlateCarree(), cmap=my_cmap)

    position = fig.add_axes([0.92, 0.05, 0.02, 0.9])
    cb = fig.colorbar(cf, cax=position, orientation='vertical',ticks=range(-45,46,3))
    cb.set_label('Temperature ℃', fontdict={'size': 30})
    cb.ax.tick_params(which='major', direction='in', length=6, labelsize=30)

这里使用了 get_my_cmaps()函数,这是自己写的,在文章开头有说明,要使用他得到NCL的色标MPL_hsv,同时需要在文件夹中放入MPL_hsv.rgb文件,有需要的朋友可以留下邮箱,作者给你们发。

(五)、850风场箭头绘制


    step = 5
    ax.quiver(lon[::step],lat[::step],u_850[::step,::step],v_850[::step,::step],color='w',scale=600, width=0.002, pivot='mid', transform=ccrs.PlateCarree())

(六)、其他设置,制作GIF


    ax.text(0.85, -0.03, '21122420+' + TIME,transform=ax.transAxes, fontsize=50)

    ax.set_title('ECMWF 850mb Temperature(shaded),Wind(vector)&500mb Geopotential Height(contour)',color='k',fontsize= 60)

    plt.savefig(TIME+'.png', dpi=30)

    plt.clf()

    frames.append(imageio.imread(TIME+'.png'))

imageio.mimsave('aa.gif', frames, 'GIF', duration=1.0)

三、完整代码

from read_mdfs import MDFS_Grid
import re
import matplotlib.cm
from matplotlib import colors
import matplotlib.pyplot as plt
import numpy as np
from scipy.interpolate import interpolate

import cartopy.crs as ccrs
from cartopy.mpl.gridliner import LATITUDE_FORMATTER, LONGITUDE_FORMATTER
import cartopy.feature as cfeature
import os
import imageio

class Colormap(colors.ListedColormap):
    def __init__(self, c, name='from_list', n=None):
        self.colors = c
        self.name = name
        self.N = n
        super(Colormap, self).__init__(self.colors, name=self.name, N=self.N)

    def __getitem__(self, item):
        return Colormap(self.colors[item], name='sliced_' + self.name)

    def show(self):
        a = np.outer(np.ones(10), np.arange(0, 1, 0.001))
        plt.figure(figsize=(2.5, 0.5))
        plt.subplots_adjust(top=0.95, bottom=0.05, left=0.01, right=0.99)
        plt.subplot(111)
        plt.axis('off')
        plt.imshow(a, aspect='auto', cmap=self, origin='lower')
        plt.text(0.5, 0.5, self._name,
                 verticalalignment='center', horizontalalignment='center',
                 fontsize=12, transform=plt.gca().transAxes)
        plt.show()
def coltbl(cmap_file):
        pattern = re.compile(r'(\d\.?\d*)\s+(\d\.?\d*)\s+(\d\.?\d*).*')
        with open(cmap_file) as cmap:
            cmap_buff = cmap.read()
        cmap_buff = re.compile('ncolors.*\n').sub('', cmap_buff)
        if re.search(r'\s*\d\.\d*', cmap_buff):
            return np.asarray(pattern.findall(cmap_buff), 'f4')
        else:
            return np.asarray(pattern.findall(cmap_buff), 'u1') / 255.

def get_my_cmaps(cname):
    try:
        if cname in matplotlib.cm._cmap_registry:
            return matplotlib.cm.get_cmap(cname)
    except:
        pass
    cmap_file = os.path.join( cname+ ".rgb")
    cmap = Colormap(coltbl(cmap_file), name=cname)
    matplotlib.cm.register_cmap(name=cname, cmap=cmap)
    return cmap

def get_lat_lon(filepath):
    a = MDFS_Grid(filepath)
    lon = a.data['Lon']
    lat = a.data['Lat'][::-1]
    lon_scipy = np.arange(lon.min(), lon.max(), 0.05)
    lat_scipy = np.arange(lat.min(), lat.max(), 0.05)
    return lon,lat,lon_scipy,lat_scipy
def read_data_hgt_or_tmp(filepath,lon,lat,lon_scipy,lat_scipy):
    a = MDFS_Grid(filepath)
    data = a.data['Grid'][::8,::8]
    spline = interpolate.RectBivariateSpline(lat[::8], lon[::8],data,)
    data = spline(lat_scipy,lon_scipy)
    return data[::-1,:]
def read_data(filepath):
    a = MDFS_Grid(filepath)
    data = a.data['Grid']
    return data[::-1,:]

with open('CN-border-La.dat') as src:
    context = src.read()
    blocks = [cnt for cnt in context.split('>') if len(cnt) > 0]
    borders = [np.fromstring(block, dtype=float, sep=' ') for block in blocks]

fig = plt.figure(figsize=(47, 30), dpi=30)

frames = []

TIMES = ['000','003','006','009','012']

for TIME in TIMES:

    file_path = r"ECMWF_HR\ECMWF_HR_HGT_500_21122420." + TIME
    lon ,lat,lon_scipy,lat_scipy = get_lat_lon(file_path)
    hgt_500 = read_data_hgt_or_tmp(file_path,lon ,lat,lon_scipy,lat_scipy)

    file_path = r"ECMWF_HR\ECMWF_HR_TMP_850_21122420." + TIME
    tem_850 = read_data_hgt_or_tmp(file_path,lon ,lat,lon_scipy,lat_scipy)

    file_path = r"ECMWF_HR\ECMWF_HR_UGRD_850_21122420." + TIME
    u_850 = read_data(file_path)

    file_path = r"ECMWF_HR\ECMWF_HR_VGRD_850_21122420." + TIME
    v_850 = read_data(file_path)

    ax = fig.add_axes([0.02, 0.05, 0.9, 0.9], projection=ccrs.LambertConformal(central_latitude=90, central_longitude=105))
    extent = [80,130,20,55]
    ax.set_extent(extent,crs=ccrs.Geodetic())
    gl = ax.gridlines( draw_labels=True, linewidth=2, color='k', alpha=0.5, linestyle='--')
    gl.xformatter = LONGITUDE_FORMATTER
    gl.yformatter = LATITUDE_FORMATTER
    gl.xlabel_style = {'size': 30}
    gl.ylabel_style = {'size': 30}
    resolution_map = '50m'
    ax.add_feature(cfeature.OCEAN.with_scale(resolution_map))
    ax.add_feature(cfeature.LAND.with_scale(resolution_map))
    ax.add_feature(cfeature.RIVERS.with_scale(resolution_map))
    ax.add_feature(cfeature.LAKES.with_scale(resolution_map))

    for line in borders:
        ax.plot(line[0::2], line[1::2], '-', color='k',transform=ccrs.Geodetic())

    ct = ax.contour(lon_scipy, lat_scipy, hgt_500, 20, colors='k',linewidths=3, transform=ccrs.PlateCarree())
    ax.clabel(ct, inline=True, fontsize=30, fmt='%d')

    clev = [588]
    ct = ax.contour(lon_scipy, lat_scipy, hgt_500, clev, colors='r',linewidths=3, transform=ccrs.PlateCarree())
    ax.clabel(ct, inline=True, fontsize=30, fmt='%d')

    clev = [0.0]
    ct = ax.contour(lon_scipy, lat_scipy, tem_850, clev, colors='w',linewidths=5, transform=ccrs.PlateCarree())
    ax.clabel(ct, inline=True, fontsize=30, fmt='%d')

    clevs = range(-45,46,3)

    my_cmap = get_my_cmaps("MPL_hsv")

    my_cmap = my_cmap.reversed()

    cf = ax.contourf(lon_scipy, lat_scipy, tem_850, clevs, transform=ccrs.PlateCarree(), cmap=my_cmap)

    position = fig.add_axes([0.92, 0.05, 0.02, 0.9])
    cb = fig.colorbar(cf, cax=position, orientation='vertical',ticks=range(-45,46,3))
    cb.set_label('Temperature ℃', fontdict={'size': 30})
    cb.ax.tick_params(which='major', direction='in', length=6, labelsize=30)

    step = 5
    ax.quiver(lon[::step],lat[::step],u_850[::step,::step],v_850[::step,::step],color='w',scale=600, width=0.002, pivot='mid', transform=ccrs.PlateCarree())

    ax.text(0.85, -0.03, '21122420+' + TIME,transform=ax.transAxes, fontsize=50)

    ax.set_title('ECMWF 850mb Temperature(shaded),Wind(vector)&500mb Geopotential Height(contour)',color='k',fontsize= 60)

    plt.savefig(TIME+'.png', dpi=30)

    plt.clf()

    frames.append(imageio.imread(TIME+'.png'))

imageio.mimsave('aa.gif', frames, 'GIF', duration=1.0)

这样,图片就复刻成功了

Cartopy画地图第八天(冷空气南下,NCL色标使用)
需要数据文件和代码的请留言邮箱,作者给发

Original: https://blog.csdn.net/weixin_42372313/article/details/122150138
Author: 野生的气象小流星
Title: Cartopy画地图第八天(冷空气南下,NCL色标使用)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/766670/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球