社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

利用Python将WRFOUT等nc文件处理成系统可视化的json文件,内容包括500mb等高线、风羽集合、等压线集合、台风路径

气象学家 • 1 年前 • 553 次点击  

  第一时间获取气象科研资讯

气象学家公众号交流群

加入



      去年一月份,根据某项目的要求,我们需要从预报的WRFOUT文件中提取500mb等压线、风羽集合、等压线集合以及台风路径,并以json文件格式呈现。这种json文件可以直接在网页端显示,或者在我们自行创建的系统中展示。它具有较高的地理匹配度,与以往使用Python绘制图片的方法有明显不同。我将提供处理500mb等压线等内容的代码注解,所有代码都将通过百度网盘分享。

       首先是导入各种python库:

import xarray as xrimport numpy as npimport mathimport netCDF4 as ncfrom netCDF4 import Dataset,num2date,date2numfrom wrf import getvar,ll_to_xy,interplevel,vinterp,get_cartopy,latlon_coords,cartopy_xlim,cartopy_ylimfrom math import atan2,piimport pandas as pdimport datetimeimport wrf import geopandas as gpdimport salemfrom salem.utils import get_demo_filefrom matplotlib import colorsimport matplotlib.pyplot as pltimport matplotlib as mplfrom datetime import datetime,timedeltafrom cartopy.io.shapereader import Readerimport cartopy.crs as crsimport matplotlib.ticker as mtickerfrom salem.utils import get_demo_filefrom scipy.interpolate import griddatafrom salem import cache_dir, sample_data_dirimport json

       其次通过读取WRFOUT数据提取500mb等高线、风羽集合、等压线集合的数据:

Ht_500mb=[]Wspd10=[]Wdir10=[]Slp=[]timerange=pd.date_range(start='2019-08-05 18:00:00',end='2019-08-11 12:00:00',freq='H')for t in range(0,len(timerange),6):    print(str(timerange[t]))    t1=str(timerange[t]).replace(' ','_').replace(':','_')    ncfile = Dataset("./wrfout_d02_2019-08-05_18:00:00")    lat=getvar(ncfile, "XLAT",timeidx=0).data    lon=getvar(ncfile, "XLONG",timeidx=0).data    time=getvar(ncfile,"times",timeidx=t).data    z = getvar(ncfile, "z",timeidx=t)    p = getvar(ncfile, "pressure",timeidx=t)    # Compute the 500 MB Geopotential Height    ht_500mb = (interplevel(z, p, 500.)/10).data    slp=getvar(ncfile,"slp",timeidx=t).data    wspd10=getvar(ncfile,"wspd10",timeidx=t).data    wdir10=getvar(ncfile,"wdir10",timeidx=t).data    llat1=np.arange(1.51,40.34,0.03)
llon1=np.arange(108.00,130.93,0.03) llon,llat=np.meshgrid(llon1,llat1)
llat2=np.arange(1.51,40.34,1) llon2=np.arange(108.00,130.93,1) llon_wind,llat_wind=np.meshgrid(llon2,llat2) #插值数据到指定的经纬度 ht_500mb_grid=griddata((lat.ravel(),lon.ravel()),ht_500mb.ravel(),(llat,llon), method='linear') wspd10_grid=griddata((lat.ravel(),lon.ravel()),wspd10.ravel(),(llat_wind,llon_wind), method='linear') wdir10_grid=griddata((lat.ravel(),lon.ravel()),wdir10.ravel(),(llat_wind,llon_wind), method='linear') slp_grid=griddata((lat.ravel(),lon.ravel()),slp.ravel(),(llat,llon), method='linear') Ht_500mb.append(ht_500mb_grid[np.newaxis,:]) Wspd10.append(wspd10_grid[np.newaxis,:]) Wdir10.append(wdir10_grid[np.newaxis,:]) Slp.append(slp_grid[np.newaxis,:])Ht_500mb=np.concatenate(Ht_500mb, axis=0)Wspd10=np.concatenate(Wspd10, axis=0)Wdir10=np.concatenate(Wdir10, axis=0)Slp=np.concatenate(Slp, axis=0)
timerange=pd.date_range(start='2019-08-05 18:00:00',end='2019-08-11 12:00:00',freq='6H')ht500mb=xr.Dataset()ht500mb['time']=(['time'],timerange)ht500mb['latitude']=(['latitude'],llat1)ht500mb['longitude']=(['longitude'],llon1)ht500mb['ht_500mb']=(['time','latitude','longitude'],Ht_500mb)ht500mb.to_netcdf('./ht_500mb_d02.nc')  spd=xr.Dataset()spd['time']=(['time'],timerange)spd['latitude']=(['latitude'],llat2)spd['longitude']=(['longitude'],llon2)spd['SPD10']=(['time','latitude','longitude'],Wspd10)spd['DIR10']=(['time','latitude','longitude'],Wdir10)spd.to_netcdf('./spd_d02.nc') ps=xr.Dataset()ps['time']=(['time'],timerange)ps['latitude']=(['latitude'],llat1)ps['longitude']=(['longitude'],llon1)ps['PS']=(['time','latitude','longitude'],Slp)ps.to_netcdf('./slp_d02.nc')

      利用salem掩膜陆地数据(可选可不选,看个人需求并输出风羽json文件

#风jsonspd_cut=xr.open_dataset('./spd_d02.nc')#2022.1.17add#spd_cut=spd.salem.roi(shape=shp)#spd_cut.to_netcdf('./spd_d02_cut.nc') #spd_cut=xr.open_dataset('./spd_d02_cut.nc') spd10_cut=spd_cut['SPD10'].datadir10_cut=spd_cut['DIR10'].datalon=spd_cut['longitude'].datalat=spd_cut['latitude'].datalon,lat=np.meshgrid(lon,lat)for j in range(len(timerange)):  data=pd.DataFrame()  data['SPD10']=spd10_cut[j,:,:].ravel()  data['DIR10']=dir10_cut[j,:,:].ravel()  data['lat']=lat.ravel()  data['lon']=lon.ravel()  data1=data.dropna(axis=0,how='any').values  windjson=[]  for  i in range(data1.shape[0]):    paths = {}    paths['coordinates']=[data1[i,3],data1[i,2]]    paths['angle']=data1[i,1]    paths['power']=data1[i,0]    windjson.append(paths)  buffer={}  buffer["currentDirection"]=[windjson]  filename='./json/2022_风{:}.json'.format(str(timerange[j]).replace(' ','_').replace(':','_'))  with open(filename,'w') as file_obj:    json.dump(buffer,file_obj, indent=4, separators=(',', ': '))

      利用salem掩膜陆地数据(可选可不选,看个人需求)并输出500mb等高线和等压线集合json文件,在这一步之前,需要介绍从等压线绘图中提取等压线数据的函数文件:

def get_contour_slp(c):   contours = []   idx = 0   for cc,vl in zip(c.collections,c.levels):      # print(cc.get_paths())      # for each separate section of the contour line      for pp in cc.get_paths():          # print(pp)          paths = {}          paths["type"]="Feature"
paths["properties"]={"value":vl} # vl 是属性值 xy = [] # for each segment of that section for vv in pp.iter_segments(): xy.append([np.round(vv[0][0],5),np.round(vv[0][1],5)]) #vv[0] 是等值线上一个点的坐标,是 1 个 形如 array[12.0,13.5] 的 ndarray。 if xy[0]==xy[-1]: paths["geometry"]={"type":'Polygon','coordinates':[]} paths["geometry"]["coordinates"]=[xy] else: paths["geometry"]={"type":'LineString','coordinates':[]} paths["geometry"]["coordinates"]=xy contours.append(paths) idx +=1 return contours#############################def get_contour_ht500mb(c): contours = [] idx = 0 # for each contour line #print(cn.levels) for cc,vl in zip(c.collections,c.levels): # print(cc.get_paths()) # for each separate section of the contour line for pp in cc.get_paths(): # print(pp) paths = {} paths["type"]="Feature"
paths["properties"]={"value":vl} # vl 是属性值 xy = [] # for each segment of that section for vv in pp.iter_segments(): xy.append([float(vv[0][0]),float(vv[0][1])]) #vv[0] 是等值线上一个点的坐标,是 1 个 形如 array[12.0,13.5] 的 ndarray。 if xy[0]==xy[-1]: paths["geometry"]={"type":'Polygon','coordinates':[]} paths["geometry"]["coordinates"]=[xy] else: paths["geometry"]={"type":'LineString','coordinates':[]} paths["geometry"]["coordinates"]=xy contours.append(paths) idx +=1 return contours

      最后提取500mb等高线和等压线集合json文件代码:

shp = gpd.read_file('./ne_10m_ocean_scale_rank/ne_10m_ocean_scale_rank.shp')ht500mb1=xr.open_dataset('./ht_500mb_d02.nc')#2022.1.17add#ht500mb_cut=ht500mb.salem.roi(shape=shp)#ht500mb_cut.to_netcdf('./ht_500mb_d02_cut.nc') #ht500mb_cut=xr.open_dataset('./ht_500mb_d02_cut.nc') #ht500mb=ht500mb_cut['ht_500mb'].data#lon=ht500mb_cut['longitude'].data#lat=ht500mb_cut['latitude'].data
#2022.1.17addht500mb=ht500mb1['ht_500mb'].datalon=ht500mb1['longitude'].datalat=ht500mb1['latitude'].data
ps1=xr.open_dataset('./slp_d02.nc')#2022.1.17add#ps_cut=ps.salem.roi(shape=shp)#ps_cut.to_netcdf('./slp_d02_cut.nc') #ps_cut=xr.open_dataset('./slp_d02_cut.nc') #slp=ps_cut['PS'].data
#2022.1.17addslp=ps1['PS'].data# contour 函数是绘制等值线的方法,它将根据输入的矩阵自动计算等值线的位置for i in range(len(timerange)): fig = plt.figure(figsize=(8, 10)) ax = fig.add_subplot(projection=crs.PlateCarree()) ocean=Reader(r'./ne_10m_land_scale_rank/ne_10m_land_scale_rank.shp').geometries() ax.add_geometries(ocean,crs.PlateCarree(),facecolor='none',edgecolor='k',linewidth=0.5) c_ht500mb = ax.contour(lon, # x轴坐标 lat, # y轴坐标 ht500mb[i,:,:], # 选取550hpa位置的位势高度数据
# slp[-1,:,:], levels=16, # 等值线数量 colors='blue', linewidths=1) # plt.show() def get_contour_ht500mb(c): contours = [] idx = 0 # for each contour line #print(cn.levels) for cc,vl in zip(c.collections,c.levels): # print(cc.get_paths()) # for each separate section of the contour line for pp in cc.get_paths(): # print(pp) paths = {} paths["type"]="Feature"
paths["properties"]={"value":vl} # vl 是属性值 xy = [] # for each segment of that section for vv in pp.iter_segments(): xy.append([float(vv[0][0]),float(vv[0][1])]) #vv[0] 是等值线上一个点的坐标,是 1 个 形如 array[12.0,13.5] 的 ndarray。 if xy[0]==xy[-1]: paths["geometry"]={"type":'Polygon','coordinates':[]} paths["geometry"]["coordinates"]=[xy] else: paths["geometry"]={"type":'LineString','coordinates':[]} paths["geometry"]["coordinates"]=xy contours.append(paths) idx +=1 return contours buffer={} buffer["type"]="FeatureCollection" buffer["features"]=get_contour_ht500mb(c_ht500mb) filename='./json/2022_500毫巴{:}.json'.format(str(timerange[i]).replace(' ','_').replace(':','_')) with open(filename,'w') as file_obj: json.dump(buffer,file_obj, indent=4, separators=(',', ': ')) fig = plt.figure(figsize=(8, 10)) ax = fig.add_subplot(projection=crs.PlateCarree()) ocean=Reader(r'./ne_10m_land_scale_rank/ne_10m_land_scale_rank.shp').geometries() ax.add_geometries(ocean,crs.PlateCarree(),facecolor='none',edgecolor='k',linewidth=0.5) c_slp = ax.contour(lon, # x轴坐标 lat, # y轴坐标 # ht500mb[-1,:,:], # 选取550hpa位置的位势高度数据 slp[i,:,:], levels=16, # 等值线数量 colors='blue', linewidths=1) def get_contour_slp(c): contours = [] idx = 0 for cc,vl in zip(c.collections,c.levels): # print(cc.get_paths()) # for each separate section of the contour line for pp in cc.get_paths(): # print(pp) paths = {} paths["type"]="Feature"
paths["properties"]={"value":vl} # vl 是属性值 xy = [] # for each segment of that section for vv in pp.iter_segments(): xy.append([np.round(vv[0][0],5),np.round(vv[0][1],5)]) #vv[0] 是等值线上一个点的坐标,是 1 个 形如 array[12.0,13.5] 的 ndarray。 if xy[0]==xy[-1]: paths["geometry"]={"type":'Polygon','coordinates':[]} paths["geometry"]["coordinates"]=[xy] else: paths["geometry"]={"type":'LineString','coordinates':[]} paths["geometry"]["coordinates"]=xy contours.append(paths) idx +=1 return contours buffer={} buffer["type"]="FeatureCollection" buffer["features"]=get_contour_slp(c_slp) filename='./json/2022_气压{:}.json'.format(str(timerange[i]).replace(' ','_').replace(':','_')) with open(filename,'w') as file_obj: json.dump(buffer,file_obj, indent=4, separators=(',', ': ')) #高低气压坐标以及数值.json high_value=[] high_points=[] low_value=[] low_points=[] highlow={'low':{},'high':{}} for path in get_contour_slp(c_slp): #print(path) if path['properties']['value'] in c_slp.levels[-2:]: if path['geometry']['coordinates'][0] == path['geometry']['coordinates'][-1]: print(path) high_value.append(path['properties']['value']) high_points.append([np.mean(np.array(path['geometry']['coordinates']).reshape(-1,2)[:,0]),np.mean(np.array(path['geometry']['coordinates']).reshape(-1,2)[:,1])]) if path['properties']['value'] in c_slp.levels[:2]: if path['geometry']['coordinates'][0] == path['geometry']['coordinates'][-1]: print(path) low_value.append(path['properties']['value']) low_points.append([np.mean(np.array(path['geometry']['coordinates']).reshape(-1,2)[:,0]),np.mean(np.array(path['geometry']['coordinates']).reshape(-1,2)[:,1])]) highlow['high']={'value':high_value,'points':high_points} highlow['low']={'value':low_value,'points':low_points} filename='./json/2022_高低气压坐标以及数值{:}.json'.format(str(timerange[i]).replace(' ','_').replace(':','_')) with open(filename,'w') as file_obj: json.dump(highlow,file_obj, indent=4, separators=(',', ': '))

      在这个http://geojson.io/网站上我们可以将500mb等高线和等压线的json文件导入,来看到我们的效果图:

      其他的json文件目前还不支持在该网站的显示,需要系统自行适配。台风路径的json文件绘制代码过于冗长,其原理类似,即将最低气压坐标点放入json的字典中(后续将单独讲解一下),并根据10米最大风速规则添加上台风属于'热带低压'、'热带风暴'、'强热带风暴'、'台风'、'强台风'或'超强台风'中的哪一种?另外,还绘制了水汽通量图和台风路径图来验证提取的数据是否准确?







声明:欢迎转载、转发本号原创内容,可留言区留言或者后台联系小编(微信:gavin7675)进行授权。气象学家公众号转载信息旨在传播交流,其内容由作者负责,不代表本号观点。文中部分图片来源于网络,如涉及作品内容、版权和其他问题,请后台联系小编处理。


往期推荐

 获取ERA5-Land陆面高分辨率再分析数据(32TB)

 1942-2022年中国地面气象站观测数据免费分享

 获取全球GPM降水数据,半小时/逐日(4TB)

  获取1998-2019 TRMM 3B42逐日降水数据

★ 获取最新版本CMIP6降尺度数据集30TB

★ 获取ERA5常用变量再分析数据26TB

 EC数据商店推出Python在线处理工具箱

★ EC打造实用气象Python工具Metview

★ 机器学习简介及在短临天气预警中的应用

★ Nature-地球系统科学领域的深度学习及理解

★ 采用神经网络与深度学习来预报降水、温度

★ 灵魂拷问:ChatGPT对气象人的饭碗是福是祸?

★  气象局是做啥的?气象局的薪水多少?

★ 一位气象学家尝试ChatGPT复现Nature子刊的研究,他真的会面临失业吗?!


Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/163192
 
553 次点击