Python白化,绘制省份或区域contourf图,直接应用!

        

目录

裁剪函数

数据

Basemap库白化绘图

Cartopy库白化绘图

总结

完整程序


        那么,很好,不出意外,我的第二篇文章出来了,再跑ConvLSTM模型时,想着如何将一片区域的分布图绘制出来,这在气象领域的研究应该是非常常见的,这里将以江苏省地区分布图为例。

        对于使用python进行白化,我是参考的气象家园里平流层的萝卜写的一篇文章,他使用的是python里Basemap包来实现绘图,但Basemap好像不更新了。所以我结合了他的方法,使用cartopy库来实现地区白化。接下来直接给出两个程序代码。

裁剪函数

        这里直接调用了大佬的裁剪函数,需要注意的就是代码中注释的地方,索引值需要和shp文件对应才行。例如我用geopandas读取shp文件后如下图所示,region就是你想要绘制的区域,这里就是第二列的“省”。

##############################裁剪函数###############
import shapefile
from matplotlib.path import Path
from matplotlib.patches import PathPatch
def shp2clip(originfig,ax,shpfile,region):
    sf = shapefile.Reader(shpfile)
    for shape_rec in sf.shapeRecords():
        #if shape_rec.record[3] == region:  ####这里需要找到和region匹配的唯一标识符,record[]中必有一项是对应的。
        if shape_rec.record[1] == region:
            vertices = []
            codes = []
            pts = shape_rec.shape.points
            prt = list(shape_rec.shape.parts) + [len(pts)]
            for i in range(len(prt) - 1):
                for j in range(prt[i], prt[i+1]):
                    vertices.append((pts[j][0], pts[j][1]))
                codes += [Path.MOVETO]
                codes += [Path.LINETO] * (prt[i+1] - prt[i] -2)
                codes += [Path.CLOSEPOLY]
            clip = Path(vertices, codes)
            clip = PathPatch(clip, transform=ax.transData)
    for contour in originfig.collections:
        contour.set_clip_path(clip)
    return clip
#####################裁剪函数#######################

数据

        考虑到大家要用到的数据不一样,这里我就直接随机生成一些数据来演示。

        以江苏省为例,经度范围为116~122,纬度范围为30~35.5,分辨率为0.1✖0.1,经过meshgrid后编制成形状为(56,61)大小的网格。生成同样网格大小的数据data1(最小值为0,最大值为50)

lat = np.round(np.arange(30, 35.5 + 0.001, 0.1),3)
lon = np.round(np.arange(116, 122 + 0.001, 0.1),3)
# 生成二维网格
grid_lon, grid_lat = np.meshgrid(lon, lat)
grid_lat = np.flipud(grid_lat)


# 生成指定范围内的随机数
data1 = np.random.uniform(low=0.0, high=50.0, size=(56, 61))

Basemap库白化绘图

        替换一下变量,然后寻找经纬度的最大最小值,等会赋值给basemap函数对象。

xx = grid_lon
yy = grid_lat
lon1,lon2=lon[0],lon[-1]  #116, 122
lat1,lat2=lat[0],lat[-1]  #30,35.5

        数据准备好后,就可以直接绘图了,先设置一些基础的东西,包括图表题、副标题,图的位置,ax1子图等参数。

plt.rcParams['font.sans-serif'] = ['SimHei']  # 正常显示中文
plt.rcParams['axes.unicode_minus'] = False  # 正常显示正负号\

#开始画图
fig = plt.figure(figsize=(16, 14),dpi=800) #设置图片大小和分辨率
#添加第一个子图
ax1 = fig.add_axes([0.1, 0.6, 0.5, 0.3])
#图标题
ax1.set_title('(a)测试',loc='left',fontsize=26)
time='Time:'+'20**.**.**'
ax1.set_title(time, loc='right',fontsize=12)

        接下来就是调用basemap,绘制底图了。设置相应的绘图区域,读取需要的shp文件,设置一些边界线、填充色等等(shp文件文章最后给出)。

m1 = Basemap(llcrnrlon=lon1, llcrnrlat=lat1, urcrnrlon=lon2, urcrnrlat=lat2, projection = 'cyl', resolution='i')
m1.readshapefile(r'E:\***\chinamap\cnmap\province','whatevername',color='gray')#替换shp文件
#画海岸线
m1.drawcoastlines(color='grey', linewidth=0.1)
m1.drawmapboundary(fill_color='white')
m1.fillcontinents(color='lightgray',lake_color='#87CEFA')

        这步之后就可以得到这样的一个底图了。(觉得色彩搭配不好看的也可以重新设置参数)

        图片看上去好像还缺点经纬度的刻度值,那么我们使用Basemap库的drawparallels()drawmeridians()方法来绘制经纬度线,并根据参数设置标签、线宽、颜色和字体大小。

#画经纬度
parallels = np.arange(30.5, 36, 1)  # 设置纬度线范围和间隔
m1.drawparallels(parallels, labels=[True, False, False, True], linewidth=0.05, color='gray', fontsize=12)  # 绘制纬度线
meridians = np.arange(116, 122 + 1, 1)  # 设置经度线范围和间隔
m1.drawmeridians(meridians, labels=[True, False, False, True], linewidth=0.05, color='gray', fontsize=12)  # 绘制经度线

        到这里,就得到一个基本完整的底图了,只需要加上研究区域的contourf填色就变得完美了。接下来我们通过contourf直接绘图。

cs1 = m1.contourf(xx, yy, data1, levels = 15, cmap = "coolwarm", extend = "both")

        可以看到contourf填满了整个区域,而我们的目标区域只是江苏省,所以接下来进行就要对shp文件进行裁剪白化,得到自己想要的区域。调用前面开头加载的shp2clip函数,传入contourf图对象,坐标轴对象,shp文件以及最后想要的区域“江苏省”。

clip1 = shp2clip(cs1,ax1,r'E:\***\chinamap\cnmap\province','江苏省')

         到这里,我们就得到了一个江苏省的填色图,将data1数据换成你想要研究的数据,就会得到你需要的空间分布图。将lon和lat范围换成你需要的经纬度,就会得到你想要研究的区域空间分布图。

        添加上colorbar后,就是一个可以直接放入文章里的论文图。(完整代码在文章最后给出)

# 设置colorbar的具体位置
cax1 = fig.add_axes([0.2, 0.56, 0.3, 0.012])  # [left, bottom, width, height]
bar1 = plt.colorbar(cs1, cax=cax1, orientation='horizontal')
# 设置 colorbar 的参数
bar1.set_label('$NO_{2}$柱浓度 ($10^{-4}$ mol/m$^2$)')
bar1.ax.tick_params(labelsize=8)  # 设置刻度标签的字体大小
bar1.ax.set_facecolor('lightgray')  # 设置 colorbar 背景颜色



Cartopy库白化绘图

        同样的,还是先添加子图、添加标题等等,这里多的就是加上投影方式。

plt.rcParams['font.sans-serif'] = ['SimHei']  # 正常显示中文
plt.rcParams['axes.unicode_minus'] = False  # 正常显示正负号\

#开始画图
proj = ccrs.PlateCarree()  # 创建投影
fig = plt.figure(figsize=(16, 14),dpi=800) #设置图片大小和分辨率

#添加第一个子图
ax1 = fig.add_axes([0.01, 0.6, 0.5, 0.3],projection = proj)
#图标题
ax1.set_title('(b)测试',loc='left',fontsize=28)
time='Time:'+'20**.**.**'
ax1.set_title(time, loc='right',fontsize=16)

        下面就使用cartopy添加一些地理要素,比如湖泊、河流、海岸线等等,这里也设置了一下region区域,也就是研究区域。(如图b所示)

# 经纬度范围
region = [116, 122, 30, 35.5]
ax1.set_extent(region, crs=proj)#扩展边界,截取区域
ax1.add_feature(cfeature.COASTLINE.with_scale('50m'),linewidth=0.7, zorder=1)#添加海岸线
ax1.add_feature(cfeature.OCEAN.with_scale('50m'), zorder=1)
ax1.add_feature(cfeature.RIVERS.with_scale('50m'), zorder=1)#添加河流
ax1.add_feature(cfeature.LAKES.with_scale('50m'), zorder=1)#添加湖泊

        将中国地图中的省份边界数据加载到地图上,并以灰色绘制,边界线的宽度为0.7。这样可以在地图上清晰地显示中国各省份的边界。

SHP = r'E:\***\map\map\chinamap\cnmap'
ax1.add_geometries(Reader(os.path.join(SHP, 'province.shp')).geometries(), 
                  ccrs.PlateCarree(), facecolor='none', edgecolor='gray', linewidth=0.7)

        这里加一个网格线,需要的可以加,不需要也可以去掉。这里也是借助网格线gridlines编制网格,添加经纬度的刻度。这样也得到一个完整的带有省份边界的底图,加上contourf填色之后,就是一个完整的填色图了。

#网格线
g1 = ax1.gridlines(ylocs=np.arange(region[2]+0.5,region[3] +1, 1), xlocs=np.arange(region[0]-1, region[1] + 1, 2), 
                   draw_labels=True, linestyle='--', alpha=0.7)
g1.top_labels = False #取消上侧的经纬度刻度
g1.right_labels = False #取消右侧的经纬度刻度
g1.xformatter = LONGITUDE_FORMATTER #格式化程序,x轴设置为经度格式
g1.yformatter = LATITUDE_FORMATTER #格式化程序,y轴设置为纬度格式
g1.xlabel_style = {'size': 18, "color": 'k'}
g1.ylabel_style = {'size': 18, 'color': 'k'}

        这里我两句一起给出,效果和上面的一样,c1在全部空间上进行填色,clip1则是调用shp2clip函数对江苏省外的区域进行白化,得到江苏省的填色图。

c1 = ax1.contourf(grid_lon, grid_lat, data1, levels = 15, cmap = "coolwarm", extend = "both")
clip1=shp2clip(c1,ax1,r'E:\****\chinamap\cnmap\province','江苏省')

        加上colorbar后,你也会得到一个期刊论文图。

position=fig.add_axes([0.1, 0.55, 0.3, 0.01])
cb1 = fig.colorbar(c1,cax=position,orientation='horizontal',format='%.1f')
cb1.ax.tick_params(labelsize =16)

总结

        至此,将lon、lat、data1换成你自己的数据,就可以得到你需要的区域填色图。相比于ArcGIS,使用python也能够实现白化操作。(其实我是不想学ArcGIS)

        本篇内容是在网上搜索了大量的经验贴后,将其简单化、应用化得到的,可以确保你拿到就可以用到项目中。(其实他困扰了我好久,本以为研究生可以逃掉,没想到还是没逃掉)

        第一篇干货内容就交给python白化吧,下一篇的内容我还没有特别想写的。如果您有需要或者您有什么问题,请直接与我联系或在评论区说出来,我会及时回复、解决的。

完整程序

        为了方便您使用,文章末尾这里,将两个程序的完整代码分别给出。同时shp文件包也在文章末尾。(别忘了加载文章开头的裁剪函数哦!!!

basemap:

from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
import numpy as np
import netCDF4 as nc


lat = np.round(np.arange(30, 35.5 + 0.001, 0.1),3)
lon = np.round(np.arange(116, 122 + 0.001, 0.1),3)
# 生成二维网格
grid_lon, grid_lat = np.meshgrid(lon, lat)
grid_lat = np.flipud(grid_lat)


# 生成指定范围内的随机数
data1 = np.random.uniform(low=0.0, high=50.0, size=(56, 61))


xx = grid_lon
yy = grid_lat
lon1,lon2=lon[0],lon[-1]  #116, 122
lat1,lat2=lat[0],lat[-1]  #30,35.5


plt.rcParams['font.sans-serif'] = ['SimHei']  # 正常显示中文
plt.rcParams['axes.unicode_minus'] = False  # 正常显示正负号\

#开始画图
fig = plt.figure(figsize=(16, 14),dpi=800) #设置图片大小和分辨率
#添加第一个子图
ax1 = fig.add_axes([0.1, 0.6, 0.5, 0.3])
#图标题
ax1.set_title('(a)测试',loc='left',fontsize=26)
time='Time:'+'20**.**.**'
ax1.set_title(time, loc='right',fontsize=12)

m1 = Basemap(llcrnrlon=lon1, llcrnrlat=lat1, urcrnrlon=lon2, urcrnrlat=lat2, projection = 'cyl', resolution='i')
m1.readshapefile(r'E:\*****\chinamap\cnmap\province','whatevername',color='gray')#替换shp文件
#画海岸线
m1.drawcoastlines(color='grey', linewidth=0.1)
m1.drawmapboundary(fill_color='white')
m1.fillcontinents(color='lightgray',lake_color='#87CEFA')

#画经纬度
parallels = np.arange(30.5, 36, 1)  # 设置纬度线范围和间隔
m1.drawparallels(parallels, labels=[True, False, False, True], linewidth=0.05, color='gray', fontsize=12)  # 绘制纬度线
meridians = np.arange(116, 122 + 1, 1)  # 设置经度线范围和间隔
m1.drawmeridians(meridians, labels=[True, False, False, True], linewidth=0.05, color='gray', fontsize=12)  # 绘制经度线

cs1 = m1.contourf(xx, yy, data1, levels = 15, cmap = "coolwarm", extend = "both")

clip1 = shp2clip(cs1,ax1,r'E:\*****\province','江苏省')

# 设置colorbar的具体位置
cax1 = fig.add_axes([0.2, 0.56, 0.3, 0.012])  # [left, bottom, width, height]
bar1 = plt.colorbar(cs1, cax=cax1, orientation='horizontal')
# 设置 colorbar 的参数
bar1.set_label('$NO_{2}$柱浓度 ($10^{-4}$ mol/m$^2$)')
bar1.ax.tick_params(labelsize=8)  # 设置刻度标签的字体大小
bar1.ax.set_facecolor('lightgray')  # 设置 colorbar 背景颜色

plt.show()

cartopy:

import numpy as np
import pandas as pd
import netCDF4
import xarray as xr

import os
from matplotlib import pyplot as plt
import warnings
warnings.filterwarnings("ignore")
import matplotlib as mpl
import cartopy.crs as ccrs
import cartopy.io.shapereader as shpreader
import matplotlib.colors as colors
import cartopy.mpl.ticker as cticker
from matplotlib import ticker
from matplotlib.pyplot import MultipleLocator
import time
from cartopy.io.shapereader import Reader
import cartopy.feature as cfeature
from matplotlib.colors import Normalize
import matplotlib.cm as cm
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
from netCDF4 import Dataset
import matplotlib.ticker as mticker
from matplotlib.ticker import StrMethodFormatter

%matplotlib inline

np.set_printoptions(suppress=True) #取消科学计数法表示



lat = np.round(np.arange(30, 35.5 + 0.001, 0.1),3)
lon = np.round(np.arange(116, 122 + 0.001, 0.1),3)
# 生成二维网格
grid_lon, grid_lat = np.meshgrid(lon, lat)
grid_lat = np.flipud(grid_lat)


# 生成指定范围内的随机数
data1 = np.random.uniform(low=0.0, high=50.0, size=(56, 61))

plt.rcParams['font.sans-serif'] = ['SimHei']  # 正常显示中文
plt.rcParams['axes.unicode_minus'] = False  # 正常显示正负号\

#开始画图
proj = ccrs.PlateCarree()  # 创建投影
fig = plt.figure(figsize=(16, 14),dpi=800) #设置图片大小和分辨率

#添加第一个子图
ax1 = fig.add_axes([0.01, 0.6, 0.5, 0.3],projection = proj)
#图标题
ax1.set_title('(b)测试',loc='left',fontsize=28)
time='Time:'+'20**.**.**'
ax1.set_title(time, loc='right',fontsize=16)

# 经纬度范围
region = [116, 122, 30, 35.5]
ax1.set_extent(region, crs=proj)#扩展边界,截取区域
ax1.add_feature(cfeature.COASTLINE.with_scale('50m'),linewidth=0.7, zorder=1)#添加海岸线
ax1.add_feature(cfeature.OCEAN.with_scale('50m'), zorder=1)
ax1.add_feature(cfeature.RIVERS.with_scale('50m'), zorder=1)#添加河流
ax1.add_feature(cfeature.LAKES.with_scale('50m'), zorder=1)#添加湖泊

SHP = r'E:\****\map\chinamap\cnmap'
ax1.add_geometries(Reader(os.path.join(SHP, 'province.shp')).geometries(), 
                  ccrs.PlateCarree(), facecolor='none', edgecolor='gray', linewidth=0.7)

#网格线
g1 = ax1.gridlines(ylocs=np.arange(region[2]+0.5,region[3] +1, 1), xlocs=np.arange(region[0]-1, region[1] + 1, 2), 
                   draw_labels=True, linestyle='--', alpha=0.7)
g1.top_labels = False #取消上侧的经纬度刻度
g1.right_labels = False #取消右侧的经纬度刻度
g1.xformatter = LONGITUDE_FORMATTER #格式化程序,x轴设置为经度格式
g1.yformatter = LATITUDE_FORMATTER #格式化程序,y轴设置为纬度格式
g1.xlabel_style = {'size': 18, "color": 'k'}
g1.ylabel_style = {'size': 18, 'color': 'k'}

c1 = ax1.contourf(grid_lon, grid_lat, data1, levels = 15, cmap = "coolwarm", extend = "both")
clip1=shp2clip(c1,ax1,r'E:\*****\chinamap\cnmap\province','江苏省')

position=fig.add_axes([0.1, 0.55, 0.3, 0.01])
cb1 = fig.colorbar(c1,cax=position,orientation='horizontal',format='%.1f')
cb1.ax.tick_params(labelsize =16)

plt.show()

这里上传到CSDN下载中心,设置的可以免费下载,如不可以可以使用自己shp文件替代,或私信我免费发。


中国省界shp文件

  • 24
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Python中,使用contourf函数可以进行区域填充。在给定的代码中,使用了matplotlib库的contourf函数来绘制填色。\[1\]其中,cmap参数用于设置色条的颜色映射,norm参数用于设置色条表示的数值范围。可以通过设置level参数来确保像正确显示与数值对应的颜色。\[3\] 另外,可以使用colorbar函数来添加色条。在给定的代码中,使用plt.colorbar函数来绘制色条,并通过设置extend参数来指定色条的范围。\[2\]可以使用set_over方法或者clim函数来设置色条的最大值和最小值。\[2\] 综上所述,使用contourf函数可以实现Python中的区域填充,并可以通过设置参数来调整填充效果和色条的显示。 #### 引用[.reference_title] - *1* *3* [python绘制contourf填色,设置色标,解决填的颜色与实际数值不一致的问题](https://blog.csdn.net/m0_52014798/article/details/130480829)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [python绘制contourf填色,数值超出色条设定的范围时出现空白区域](https://blog.csdn.net/m0_52014798/article/details/130480106)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值