爱气象,爱气象家园! 

气象家园

 找回密码
 立即注册

QQ登录

只需一步,快速开始

新浪微博登陆

只需一步, 快速开始

搜索
查看: 24541|回复: 17

[源代码] Python截取不规则区域内格点

[复制链接]

新浪微博达人勋

发表于 2020-4-27 19:29:32 | 显示全部楼层 |阅读模式

登录后查看更多精彩内容~

您需要 登录 才可以下载或查看,没有帐号?立即注册 新浪微博登陆

x
本帖最后由 Masterpiece 于 2020-4-27 20:12 编辑

Python截取不规则区域内格点
grid_within.png
做EOF的时候,一般都要区域内的格点进行分析,以避免区域外的信息分担EOF权重。
取区域内的格点的做法是我的舍友涌哥教我的方法,大家可以借鉴一下思路和做法吧!

首先要准备好区域范围的shp文件,文件提供了边界的经纬度,在程序中用shapefile库进行读取:
  1. sf=shapefile.Reader("D:\\dt\\SC\\Sichuan_province")
  2. Shapes = sf.shapes()
  3. boundary = Shapes[0].points
复制代码
第二步就是关键了,这步是用matplotlib根据得到的经纬度点,建立一个path:
  1. from matplotlib.path import Path
  2. p= Path(boundary)
复制代码
这时候得到的p就是一个path边界的类变量,其中包含类函数contains_points来判断所给格点是否在区域内。例如:
  1. x=100
  2. y=30
  3. p.contains_points([[x,y]])
复制代码
如果这个(100,30)的点在path区域内,within得到值True,反之是False。

并且contains_points可以将复数个位置点以(n x 2)的数组的形式输入,返回一个对应的布尔数组,又例如:
  1. grid=np.array( [[[100,30],[100,50]],[[100,30],[100,30]]] )
  2. within=p.contains_points(grid.reshape(4,2))
复制代码
这时类函数返回within得到一个布尔数组:
  1. [ True False  True  True]
复制代码
这时候就可以利用这个布尔数组对numpy数组进行取数了。
比如我要画散点图,那必须知道散点对应的经纬度位置。先利用nc文件的lon、lat信息建立二维的经纬度格点:
  1. lons, lats = np.meshgrid(lon, lat)
复制代码
这时候lons和lats就是一个(n,m)的数组,对应着每个格点的经度或纬度,以lons为例:
  1. [[  0.    2.5   5.  ... 352.5 355.  357.5]
  2. [  0.    2.5   5.  ... 352.5 355.  357.5]
  3. [  0.    2.5   5.  ... 352.5 355.  357.5]
  4. ...
  5. [  0.    2.5   5.  ... 352.5 355.  357.5]
  6. [  0.    2.5   5.  ... 352.5 355.  357.5]
  7. [  0.    2.5   5.  ... 352.5 355.  357.5]]
复制代码
然后再利用布尔数组mask,将其中为True的对应格点的经度和纬度返回来,并且做一次坐标投影变换:
  1. grib=np.stack((lons,lats),axis=-1)
  2. mask=p.contains_points(grib.reshape(lat_num*lon_num,2))
  3. mask=mask.reshape(lat_num,lon_num)
  4. x,y=m(lons[mask],lats[mask])
复制代码
这时候在边界内的格点的图上投影坐标x,y就得到了,于是画散点图!
  1. m.scatter(x,y,s=50, marker='o', color='g', zorder=6)
复制代码
这样就可以在图上标出在区域内的格点位置了。
所以可以使用类似思路将数据提取出来。

开篇图片所使用的代码,以四川省为例。
所用到的文件我放在附件里边。
  1. import matplotlib.pyplot as plt
  2. from mpl_toolkits.basemap import Basemap
  3. import numpy as np
  4. import netCDF4 as nc
  5. from matplotlib.path import Path
  6. import shapefile

  7. af=nc.Dataset('test.nc')
  8. lat=af.variables['latitude'][:]
  9. lon=af.variables['longitude'][:]
  10. p72=af.variables['p72.162'][0,:,:]/100
  11. lat_num=len(lat)
  12. lon_num=len(lon)

  13. sf=shapefile.Reader("D:\\dt\\SC\\Sichuan_province")
  14. Shapes = sf.shapes()
  15. boundary = Shapes[0].points
  16. p= Path(boundary)

  17. lons, lats = np.meshgrid(lon, lat)

  18. grib=np.stack((lons,lats),axis=-1) #变为(n,m,2)的经纬度的点
  19. mask=p.contains_points(grib.reshape(lat_num*lon_num,2)) #变为(n*m,2)的经纬度的点,否则出错
  20. # 如果点在路径之内,对应的mask赋值为False,否则为Ture
  21. mask=mask.reshape(lat_num,lon_num)# 恢复到 (n,m)

  22. print(lons[mask])
  23. lon_leftup=90-2.5/2;lat_leftup=40+2.5/2
  24. lon_rightdown=120+2.5/2;lat_rightdown=20-2.5/2

  25. fig, ax = plt.subplots(figsize=(14,9))

  26. m = Basemap(projection='cyl',
  27.             llcrnrlat=lat_rightdown, urcrnrlat=lat_leftup,
  28.             llcrnrlon=lon_leftup, urcrnrlon=lon_rightdown,
  29.             resolution='i')
  30. m.drawcoastlines(linewidth=0.3, color='black')
  31. m.readshapefile('D:\\dt\\SC\\Sichuan_province', 'sc', color='k', linewidth=0.6)

  32. x,y=m(lons[mask],lats[mask])
  33. m.scatter(x,y,s=50, marker='o', color='g', zorder=6)

  34. x,y=m(lons[~mask],lats[~mask])
  35. m.scatter(x,y,s=50, marker='x', color='r', zorder=6)

  36. parallels = np.arange(20,45,5) #lat
  37. m.drawparallels(parallels,labels=[True,False,False,False],linewidth=0.6,dashes=[1,4])
  38. meridians = np.arange(90,125,5) #lon
  39. m.drawmeridians(meridians,labels=[False,False,False,True],linewidth=0.6,dashes=[1,4])

  40. plt.yticks(parallels,len(parallels)*[''])
  41. plt.xticks(meridians,len(meridians)*[''])

  42. plt.savefig('grid_within.png',dpi=300,bbox_inches='tight')

  43. plt.show()
复制代码

--所用文件--
grid_within.rar (136.94 KB, 下载次数: 41)
密码修改失败请联系微信:mofangbao

新浪微博达人勋

 楼主| 发表于 2020-5-3 21:44:32 | 显示全部楼层
言墨 发表于 2020-5-3 21:30
pre=af['pr'][0,mask]*24*3600  # pr(time,lat,lon)
cn=ax.contourf(lons[mask],lats[mask],pre,transform ...

有可能是lons[mask]这个的原因,因为mask取数之后,lons[mask]是一个一维的
密码修改失败请联系微信:mofangbao
回复 支持 1 反对 0

使用道具 举报

新浪微博达人勋

发表于 2020-4-28 15:35:52 | 显示全部楼层
感谢! 收藏了,以后肯定有机会用上的
密码修改失败请联系微信:mofangbao
回复 支持 反对

使用道具 举报

新浪微博达人勋

发表于 2020-4-28 16:28:08 | 显示全部楼层
好文章,以后一定会用到,现在强调精细化,各种形状的判断和操作会越来越多
密码修改失败请联系微信:mofangbao
回复 支持 反对

使用道具 举报

新浪微博达人勋

发表于 2020-4-29 16:33:11 | 显示全部楼层
mark
一下,以后有用
密码修改失败请联系微信:mofangbao
回复 支持 反对

使用道具 举报

新浪微博达人勋

发表于 2020-5-3 00:41:58 | 显示全部楼层
楼主你好,想请问一下,按照你的这个方法,想把数据提取出来的话具体应该怎么操作呢?我原本想着用lons[mask]作为数据的经纬度索引,却发现经纬度的索引应该是从零开始的整数,这个思路行不通。
TIM图片20200503003940.png
密码修改失败请联系微信:mofangbao
回复 支持 反对

使用道具 举报

新浪微博达人勋

 楼主| 发表于 2020-5-3 07:58:16 | 显示全部楼层
本帖最后由 Masterpiece 于 2020-5-3 08:05 编辑
失落的积雨云 发表于 2020-5-3 00:41
楼主你好,想请问一下,按照你的这个方法,想把数据提取出来的话具体应该怎么操作呢?我原本想着用lons[mas ...

numpy的数组不能用经纬度直接mask
如果要mask数据出来,直接:...['snow_depth'][ : , mask] 就好

密码修改失败请联系微信:mofangbao
回复 支持 反对

使用道具 举报

新浪微博达人勋

发表于 2020-5-3 16:04:05 | 显示全部楼层
Masterpiece 发表于 2020-5-3 07:58
numpy的数组不能用经纬度直接mask
如果要mask数据出来,直接:...['snow_depth'][ : , mask] 就好

问题解决了,非常感谢!
密码修改失败请联系微信:mofangbao
回复 支持 反对

使用道具 举报

新浪微博达人勋

发表于 2020-5-3 20:59:25 | 显示全部楼层
你好  请问22行的目的是什么呢
密码修改失败请联系微信:mofangbao
回复 支持 反对

使用道具 举报

新浪微博达人勋

 楼主| 发表于 2020-5-3 21:16:46 | 显示全部楼层
言墨 发表于 2020-5-3 20:59
你好  请问22行的目的是什么呢

这个的目的是生成一个经纬度(n,m,2)的数组,前两维是经向和纬向格点数,第三维是经度和纬度
因为contains_points需要是经度纬度成对成对的输入:
[[点A经度,点A纬度]
[点B经度,点B纬度]
[点C经度,点C纬度]
[点D经度,点D纬度]]

密码修改失败请联系微信:mofangbao
回复 支持 反对

使用道具 举报

新浪微博达人勋

发表于 2020-5-3 21:30:59 | 显示全部楼层
pre=af['pr'][0,mask]*24*3600  # pr(time,lat,lon)
cn=ax.contourf(lons[mask],lats[mask],pre,transform=proj,levels=levs)

楼主打扰了,咨询一个问题,我想把一个省的数据mask出来然后画shade图,这么写一直报错,一直不知道怎么回事,请您指导下。
报错内容为:IndexError: Index cannot be multidimensional
密码修改失败请联系微信:mofangbao
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册 新浪微博登陆

本版积分规则

Copyright ©2011-2014 bbs.06climate.com All Rights Reserved.  Powered by Discuz! (京ICP-10201084)

本站信息均由会员发表,不代表气象家园立场,禁止在本站发表与国家法律相抵触言论

快速回复 返回顶部 返回列表