当前位置:   article > 正文

论文发表必备技能:空间数据(tif图像、shp边界)多子图可视化-基于cartopy与matplotlib的python包_matplotlib 绘制 tiff

matplotlib 绘制 tiff

零、背景

众所周知,我正在写毕业论文,刚画完一张2x2子图布局的偏相关系数空间分布图

我的变量简介:具体地,我有三个因变量分别是SOS, EOS, LOS,四个自变量分别是tem, pre, IET, IEP,因此,我需要画三张大图,每张图内包含四张子图,即三个因变量与四个自变量分别的偏相关系数分布图。

结构:我的代码总共分为三部分,包括①导入必备包以及函数准备、②数据准备、以及③画图。其内容涉及到,可视化tif图像、shp边界、绘制colorbar、在图框上显示经纬度等优雅操作。

现新鲜出炉,分享给大家,热忱期盼交流指正~

一、导入必备包以及函数准备

  1. from osgeo import gdal
  2. import os
  3. from matplotlib.ticker import FuncFormatter
  4. import matplotlib as mpl
  5. import numpy as np
  6. from cartopy.mpl.ticker import LatitudeFormatter, LongitudeFormatter
  7. from matplotlib.ticker import MultipleLocator
  8. from cartopy.io.shapereader import Reader
  9. from cartopy import crs as ccrs
  10. from matplotlib.ticker import MaxNLocator
  11. from matplotlib.colors import Normalize
  12. import matplotlib.patches as patches
  13. import matplotlib
  14. import matplotlib.pyplot as plt
  15. matplotlib.use('TkAgg')
  16. '''
  17. 目标:获取所有.tif图像路径
  18. 输入:tif所在文件夹绝对路径
  19. 输出:包含该文件夹中所有tif文件绝对路径的list
  20. '''
  21. def get_paths(input_path):
  22. file_list = [os.path.join(input_path, file) for file in os.listdir(input_path) if file.endswith(".tif")]
  23. return file_list
  24. '''
  25. 读写遥感数据
  26. '''
  27. class Grid:
  28. # 读取图像文件
  29. def read_img(self, filename):
  30. data = gdal.Open(filename) # 打开文件
  31. im_width = data.RasterXSize # 读取图像行数
  32. im_height = data.RasterYSize # 读取图像列数
  33. im_geotrans = data.GetGeoTransform()
  34. # 仿射矩阵,左上角像素的大地坐标和像素分辨率。
  35. # 共有六个参数,分表代表左上角x坐标;东西方向上图像的分辨率;如果北边朝上,地图的旋转角度,0表示图像的行与x轴平行;左上角y坐标;
  36. # 如果北边朝上,地图的旋转角度,0表示图像的列与y轴平行;南北方向上地图的分辨率。
  37. im_proj = data.GetProjection() # 地图投影信息
  38. im_data = data.ReadAsArray(0, 0, im_width, im_height) # 此处读取整张图像
  39. # ReadAsArray(<xoff>, <yoff>, <xsize>, <ysize>)
  40. # 读出从(xoff,yoff)开始,大小为(xsize,ysize)的矩阵。
  41. del data # 释放内存,如果不释放,在arcgis,envi中打开该图像时会显示文件被占用
  42. return im_proj, im_geotrans, im_data
  43. def write_img(self, filename, im_proj, im_geotrans, im_data):
  44. # filename-创建的新影像
  45. # im_geotrans,im_proj该影像的参数,im_data,被写的影像
  46. # 写文件,以写成tiff为例
  47. # gdal数据类型包括
  48. # gdal.GDT_Byte,
  49. # gdal .GDT_UInt16, gdal.GDT_Int16, gdal.GDT_UInt32, gdal.GDT_Int32,
  50. # gdal.GDT_Float32, gdal.GDT_Float64
  51. # 判断栅格数据的类型
  52. if 'int8' in im_data.dtype.name:
  53. datatype = gdal.GDT_Byte
  54. elif 'int16' in im_data.dtype.name:
  55. datatype = gdal.GDT_UInt16
  56. else:
  57. datatype = gdal.GDT_Float32
  58. if len(im_data.shape) == 3: # len(im_data.shape)表示矩阵的维数
  59. im_bands, im_height, im_width = im_data.shape # (维数,行数,列数)
  60. else:
  61. im_bands, (im_height, im_width) = 1, im_data.shape # 一维矩阵
  62. # 创建文件
  63. driver = gdal.GetDriverByName('GTiff') # 数据类型必须有,因为要计算需要多大内存空间
  64. data = driver.Create(filename, im_width, im_height, im_bands, datatype)
  65. data.SetGeoTransform(im_geotrans) # 写入仿射变换参数
  66. data.SetProjection(im_proj) # 写入投影
  67. if im_bands == 1:
  68. data.GetRasterBand(1).WriteArray(im_data) # 写入数组数据
  69. else:
  70. for i in range(im_bands):
  71. data.GetRasterBand(i + 1).WriteArray(im_data[i])
  72. del data
  73. a = Grid()
  74. '''
  75. 目标:寻找映射最大值、最小值
  76. 输入:四个数组(子图中绘制的四个tif)
  77. 输出:四个数组全体的最大值、最小值
  78. '''
  79. def find_global_max_min(arr1, arr2, arr3, arr4):
  80. # 初始化最大值和最小值
  81. global_max = None
  82. global_min = None
  83. # 将四个数组放入列表中
  84. arrays = [arr1, arr2, arr3, arr4]
  85. # 遍历每个数组并查找最大值和最小值
  86. for arr in arrays:
  87. local_max = np.nanmax(arr)
  88. local_min = np.nanmin(arr)
  89. # 更新全局最大值和最小值
  90. if global_max is None or local_max > global_max:
  91. global_max = local_max
  92. if global_min is None or local_min < global_min:
  93. global_min = local_min
  94. return global_max, global_min
  95. '''
  96. 输入:sos_data_lists, eos_data_lists, los_data_lists
  97. 输出:数组正值与负值占比
  98. '''
  99. def calculate_percentage(data_list):
  100. result = []
  101. for data in data_list:
  102. positive_count = 0
  103. negative_count = 0
  104. total_count = 0
  105. for row in data:
  106. for value in row:
  107. if -1 <= value <= 1:
  108. total_count += 1
  109. if value > 0:
  110. positive_count += 1
  111. elif value < 0:
  112. negative_count += 1
  113. if total_count == 0:
  114. result.append("No valid data in the array")
  115. else:
  116. positive_percentage = (positive_count / total_count) * 100
  117. negative_percentage = (negative_count / total_count) * 100
  118. result.append(positive_percentage)
  119. result.append(negative_percentage)
  120. return result

二、数据准备

  1. #tif数据所在文件夹
  2. sos_paths = get_paths(r'D:\code\python\phenology\gee\partial_corr\result\corr\sos')
  3. eos_paths = get_paths(r'D:\code\python\phenology\gee\partial_corr\result\corr\eos')
  4. los_paths = get_paths(r'D:\code\python\phenology\gee\partial_corr\result\corr\los')
  5. #获取数组
  6. #sos
  7. proj, geotrans,sos_tem_corr = a.read_img(sos_paths[-1])
  8. _,_,sos_pre_corr = a.read_img(sos_paths[-2])
  9. _,_,sos_iet_corr = a.read_img(sos_paths[-3])
  10. _,_,sos_iep_corr = a.read_img(sos_paths[-4])
  11. #eos
  12. _,_,eos_tem_corr = a.read_img(eos_paths[-1])
  13. _,_,eos_pre_corr = a.read_img(eos_paths[-2])
  14. _,_,eos_iet_corr = a.read_img(eos_paths[-3])
  15. _,_,eos_iep_corr = a.read_img(eos_paths[-4])
  16. #los
  17. _,_,los_tem_corr = a.read_img(los_paths[-1])
  18. _,_,los_pre_corr = a.read_img(los_paths[-2])
  19. _,_,los_iet_corr = a.read_img(los_paths[-3])
  20. _,_,los_iep_corr = a.read_img(los_paths[-4])
  21. #按制图顺序,整理数据顺序
  22. #sos
  23. sos_data_lists = [sos_tem_corr, sos_pre_corr,sos_iet_corr,sos_iep_corr]
  24. sos_data_lists = np.array(sos_data_lists)
  25. #eos
  26. eos_data_lists = [eos_tem_corr, eos_pre_corr,eos_iet_corr,eos_iep_corr]
  27. eos_data_lists = np.array(eos_data_lists)
  28. #los
  29. los_data_lists = [los_tem_corr, los_pre_corr,los_iet_corr,los_iep_corr]
  30. los_data_lists = np.array(los_data_lists)
  31. #寻找sos/eos/los偏相关系数最大最小值
  32. sos_max, sos_min = find_global_max_min(sos_tem_corr, sos_pre_corr, sos_iet_corr,sos_iep_corr)
  33. eos_max, eos_min = find_global_max_min(eos_tem_corr, eos_pre_corr, eos_iet_corr,eos_iep_corr)
  34. los_max, los_min = find_global_max_min(los_tem_corr, los_pre_corr, los_iet_corr,los_iep_corr)
  35. print("SOS 数据集全局最大值:", sos_max, "\nSOS 数据集全局最小值:", sos_min, "\nEOS 数据集全局最大值:", eos_max, "\nEOS 数据集全局最小值:", eos_min, "\nLOS 数据集全局最大值:", los_max, "\nLOS 数据集全局最小值:", los_min)
  36. #边界数据
  37. shp_path = r'D:\Hydro_essay\bound\bound_without_proj.shp'
  38. #获取各子图正值、负值占比
  39. #sos-tem/pre/iet/iep
  40. sos_perc = calculate_percentage(sos_data_lists)
  41. sos_p_perc = []#4个值
  42. sos_p_perc.extend([sos_perc[0], sos_perc[2], sos_perc[4], sos_perc[6]])
  43. sos_n_perc = []#4个值
  44. sos_n_perc.extend([sos_perc[1], sos_perc[3], sos_perc[5], sos_perc[7]])
  45. #eos-tem/pre/iet/iep
  46. eos_perc = calculate_percentage(eos_data_lists)
  47. eos_p_perc = []#4个值
  48. eos_p_perc.extend([eos_perc[0], eos_perc[2], eos_perc[4], eos_perc[6]])
  49. eos_n_perc = []#4个值
  50. eos_n_perc.extend([eos_perc[1], eos_perc[3], eos_perc[5], eos_perc[7]])
  51. #los-tem/pre/iet/iep
  52. los_perc = calculate_percentage(los_data_lists)
  53. los_p_perc = []#4个值
  54. los_p_perc.extend([los_perc[0], los_perc[2], los_perc[4], los_perc[6]])
  55. los_n_perc = []#4个值
  56. los_n_perc.extend([los_perc[1], los_perc[3], los_perc[5], los_perc[7]])

三、开始画图

这部分我将colorbar的title写为中文的(可能是因为要毕业了吧)。如果你需要英文出图,只需要替换一下fontdict即可。

  1. # 字体和样式设置
  2. size1 = 4
  3. fontdict1 = {'weight': 'bold', 'size': 4, 'color': 'k', 'family': 'Times New Roman'}
  4. fontdict2 = {'size': 12, 'color': 'k', 'family': 'Times New Roman'}
  5. fontdict3 = {'size': 6, 'color': 'k', 'family': 'Times New Roman'}
  6. fontdict4 = {'weight': 'bold', 'size': 3.9, 'color': 'k', 'family': 'Times New Roman'}
  7. fontdict5 = {'weight': '1000', 'size': 4.5, 'color': 'k', 'family': "SimSun"}#colorbar
  8. fontdict6 = {'weight': '1000', 'size': 3.9, 'color': 'k', 'family': "SimSun"}#text
  9. #全体字体
  10. mpl.rcParams.update({
  11. 'text.usetex': False,
  12. 'font.family': 'serif',
  13. 'font.serif': ['Times New Roman'],
  14. 'font.size': size1,
  15. 'mathtext.fontset': 'stix',
  16. })
  17. # 投影设置
  18. proj = ccrs.PlateCarree()
  19. # 图框经纬度范围
  20. extent = [102.7, 104.7, 27.2, 29.2]#注意四个数字的排序!
  21. #tif图像经纬度设置
  22. extent_tif = [102.84521586, 104.43331875, 27.37916667, 28.90833333]
  23. # 图布大小设置
  24. fig = plt.figure(figsize=(25, 16), dpi=300)
  25. # 子图布局参数
  26. left_margin_adjusted = 0.345 #左列子图左侧图框距图布边儿距离
  27. bottom_margin_adjusted = 0.098 #下列子图下侧图框距图布边儿距离
  28. colorbar_left_adjusted = 0.82 # 色标的左边界位置调整
  29. # 建立轴
  30. ax_a = fig.add_axes([0.05, 0.52, 0.39, 0.39], projection=proj)
  31. ax_b = fig.add_axes([left_margin_adjusted, 0.52, 0.39, 0.39], projection=proj)
  32. ax_c = fig.add_axes([0.05, bottom_margin_adjusted, 0.39, 0.39], projection=proj)
  33. ax_d = fig.add_axes([left_margin_adjusted, bottom_margin_adjusted, 0.39, 0.39], projection=proj)
  34. # 设置轴上tick的长度和宽度,以及经纬度数字与轴的距离
  35. tick_length = 1.2 # 刻度线的长度
  36. tick_width = 0.4 # 刻度线的宽度
  37. label_pad = 1 # 刻度标签与轴的距离
  38. axis_line_width = 0.35 # 轴线(ticks)宽度
  39. # 设置归一化对象,该对象用于统一不同图像的色标-修改xos_min;xos_max替换数据1/4!!!
  40. norm = Normalize(vmin=sos_min, vmax=sos_max)
  41. #坐标设置
  42. #我希望左上子图只有左侧和上侧显示坐标;右上子图只有右侧和上侧显示坐标等...四个子图各不相同
  43. # 左上子图设置
  44. ax_a.tick_params(axis='x', which='both', bottom=False, top=True, labelbottom=False, labeltop=True,length=tick_length, width=tick_width, pad=label_pad)
  45. ax_a.tick_params(axis='y', which='both', left=True, right=False, labelleft=True, labelright=False,length=tick_length, width=tick_width, pad=label_pad)
  46. ax_a.xaxis.set_ticks_position('top')
  47. ax_a.yaxis.set_ticks_position('left')
  48. # 右上子图设置
  49. ax_b.tick_params(axis='x', which='both', bottom=False, top=True, labelbottom=False, labeltop=True,length=tick_length, width=tick_width, pad=label_pad)
  50. ax_b.tick_params(axis='y', which='both', left=False, right=True, labelleft=False, labelright=True,length=tick_length, width=tick_width, pad=label_pad)
  51. ax_b.xaxis.set_ticks_position('top')
  52. ax_b.yaxis.set_ticks_position('right')
  53. # 左下子图设置
  54. ax_c.tick_params(axis='x', which='both', bottom=True, top=False, labelbottom=True, labeltop=False,length=tick_length, width=tick_width, pad=label_pad)
  55. ax_c.tick_params(axis='y', which='both', left=True, right=False, labelleft=True, labelright=False,length=tick_length, width=tick_width, pad=label_pad)
  56. ax_c.xaxis.set_ticks_position('bottom')
  57. ax_c.yaxis.set_ticks_position('left')
  58. # 右下子图设置
  59. ax_d.tick_params(axis='x', which='both', bottom=True, top=False, labelbottom=True, labeltop=False,length=tick_length, width=tick_width, pad=label_pad)
  60. ax_d.tick_params(axis='y', which='both', left=False, right=True, labelleft=False, labelright=True,length=tick_length, width=tick_width, pad=label_pad)
  61. ax_d.xaxis.set_ticks_position('bottom')
  62. ax_d.yaxis.set_ticks_position('right')
  63. #子图title(我要画三张图,所以三个list)
  64. sos_titles = ['(a) SOS_tem','(b) SOS_pre','(c) SOS_IET','(d) SOS_IEP']
  65. eos_titles = ['(a) EOS_tem','(b) EOS_pre','(c) EOS_IET','(d) EOS_IEP']
  66. los_titles = ['(a) LOS_tem','(b) LOS_pre','(c) LOS_IET','(d) LOS_IEP']
  67. # 四个子图设置坐标线、经纬度、以及绘制shp文件和tif文件
  68. for idx,ax in enumerate([ax_a, ax_b, ax_c, ax_d]):
  69. print(idx)
  70. ax.set_extent(extent, crs=proj) # 设置地图显示的范围
  71. ax.set_xticks(np.arange(int(np.floor(extent[0])), int(np.ceil(extent[1])) + 1, 1), crs=proj)
  72. ax.set_yticks(np.arange(int(np.floor(extent[2])), int(np.ceil(extent[3])) + 1, 1), crs=proj)
  73. ax.xaxis.set_major_formatter(LongitudeFormatter(zero_direction_label=False))
  74. ax.yaxis.set_major_formatter(LatitudeFormatter())
  75. ax.set_extent(extent, crs=ccrs.PlateCarree())
  76. #ax.gridlines(crs=ccrs.PlateCarree(), draw_labels=False,linewidth=.8, linestyle=':', color='k', alpha=0.5)
  77. # 设置刻度只显示整数
  78. ax.xaxis.set_major_locator(MaxNLocator(integer=True))
  79. ax.yaxis.set_major_locator(MaxNLocator(integer=True))
  80. # 设置轴线宽度
  81. for spine in ax.spines.values():
  82. spine.set_linewidth(axis_line_width)
  83. #小刻度线
  84. xminorticks = MultipleLocator(1)
  85. yminorticks = MultipleLocator(1)
  86. ax.xaxis.set_minor_locator(xminorticks)
  87. ax.yaxis.set_minor_locator(yminorticks)
  88. # 绘制矢量边界
  89. ax.add_geometries(Reader(shp_path).geometries(), ccrs.PlateCarree(), facecolor='none', edgecolor='k', linewidth=0.25)
  90. #绘制tif-修改xos_data_lists替换数据2/4!!!
  91. ax.imshow(sos_data_lists[idx], extent=extent_tif, cmap='coolwarm', norm=norm, transform=proj, interpolation='nearest')
  92. #注:在使用imshow或contourf函数时,指定的extent必须准确匹配tif数据的实际地理范围,因此指定新的extent
  93. # 设置标题,并调整位置-修改xos_titles替换数据3/4!!!
  94. ax.text(0.5, 0.92, sos_titles[idx], transform=ax.transAxes, fontdict=fontdict1, ha='center')
  95. #添加正值负值比例-修改xos_p_perc;xos_n_perc[idx]替换数据4/4!!!
  96. # Red rectangle for > 0
  97. red_patch = patches.Rectangle((0.5, 0.18), 0.075, 0.035, transform=ax.transAxes, color='red', zorder=5)
  98. ax.add_patch(red_patch)
  99. red_ratio = sos_p_perc[idx]
  100. blue_ratio = sos_n_perc[idx]
  101. ax.text(0.6, 0.19, f'{red_ratio:.2f}% ', transform=ax.transAxes, fontdict=fontdict4, va='center')
  102. ax.text(0.8, 0.19, f'(正值)', transform=ax.transAxes, fontdict=fontdict6, va='center')
  103. # Blue rectangle for < 0
  104. blue_patch = patches.Rectangle((0.5, 0.10), 0.075, 0.035, transform=ax.transAxes, color='blue', zorder=5)
  105. ax.add_patch(blue_patch)
  106. ax.text(0.6, 0.11, f'{blue_ratio:.2f}% ', transform=ax.transAxes, fontdict=fontdict4, va='center')
  107. ax.text(0.8, 0.11, f'(负值)', transform=ax.transAxes, fontdict=fontdict6, va='center')
  108. print('drawing colorbar')
  109. # 设置色标位置和大小
  110. colorbar_width = 0.015
  111. colorbar_height = 0.8
  112. ax_colorbar = fig.add_axes([colorbar_left_adjusted-0.08, bottom_margin_adjusted, colorbar_width, colorbar_height])
  113. # 创建色标
  114. sm = mpl.cm.ScalarMappable(cmap='coolwarm', norm=norm)
  115. sm.set_array([])
  116. cbar = plt.colorbar(sm, cax=ax_colorbar)
  117. # 设置色标刻度
  118. cbar.set_ticks([-0.8, -0.6, -0.4, -0.2, 0, 0.2, 0.4, 0.6, 0.8])
  119. # 设置色标刻度标签格式为一位小数
  120. cbar.ax.yaxis.set_major_formatter(FuncFormatter(lambda x, _: f'{x:.1f}'))
  121. # 设置色标的刻度参数和移除边框
  122. cbar.ax.tick_params(axis='y', which='both', length=tick_length, width=tick_width, pad=label_pad)
  123. cbar.outline.set_linewidth(0) # 移除色标的边框
  124. # 设置色标标签
  125. cbar.set_label('偏相关系数', fontdict=fontdict5)
  126. plt.show()

四、结果展示

再来1个英文版本的吧(证明我都做啦哈哈)

五、全部代码集合

  1. #%%
  2. from osgeo import gdal
  3. import os
  4. from matplotlib.ticker import FuncFormatter
  5. import matplotlib as mpl
  6. import numpy as np
  7. from cartopy.mpl.ticker import LatitudeFormatter, LongitudeFormatter
  8. from matplotlib.ticker import MultipleLocator
  9. from cartopy.io.shapereader import Reader
  10. from cartopy import crs as ccrs
  11. from matplotlib.ticker import MaxNLocator
  12. from matplotlib.colors import Normalize
  13. import matplotlib.patches as patches
  14. import matplotlib
  15. import matplotlib.pyplot as plt
  16. matplotlib.use('TkAgg')
  17. '''
  18. 目标:获取所有.tif图像路径
  19. 输入:tif所在文件夹绝对路径
  20. 输出:包含该文件夹中所有tif文件绝对路径的list
  21. '''
  22. def get_paths(input_path):
  23. file_list = [os.path.join(input_path, file) for file in os.listdir(input_path) if file.endswith(".tif")]
  24. return file_list
  25. '''
  26. 读写遥感数据
  27. '''
  28. class Grid:
  29. # 读取图像文件
  30. def read_img(self, filename):
  31. data = gdal.Open(filename) # 打开文件
  32. im_width = data.RasterXSize # 读取图像行数
  33. im_height = data.RasterYSize # 读取图像列数
  34. im_geotrans = data.GetGeoTransform()
  35. # 仿射矩阵,左上角像素的大地坐标和像素分辨率。
  36. # 共有六个参数,分表代表左上角x坐标;东西方向上图像的分辨率;如果北边朝上,地图的旋转角度,0表示图像的行与x轴平行;左上角y坐标;
  37. # 如果北边朝上,地图的旋转角度,0表示图像的列与y轴平行;南北方向上地图的分辨率。
  38. im_proj = data.GetProjection() # 地图投影信息
  39. im_data = data.ReadAsArray(0, 0, im_width, im_height) # 此处读取整张图像
  40. # ReadAsArray(<xoff>, <yoff>, <xsize>, <ysize>)
  41. # 读出从(xoff,yoff)开始,大小为(xsize,ysize)的矩阵。
  42. del data # 释放内存,如果不释放,在arcgis,envi中打开该图像时会显示文件被占用
  43. return im_proj, im_geotrans, im_data
  44. def write_img(self, filename, im_proj, im_geotrans, im_data):
  45. # filename-创建的新影像
  46. # im_geotrans,im_proj该影像的参数,im_data,被写的影像
  47. # 写文件,以写成tiff为例
  48. # gdal数据类型包括
  49. # gdal.GDT_Byte,
  50. # gdal .GDT_UInt16, gdal.GDT_Int16, gdal.GDT_UInt32, gdal.GDT_Int32,
  51. # gdal.GDT_Float32, gdal.GDT_Float64
  52. # 判断栅格数据的类型
  53. if 'int8' in im_data.dtype.name:
  54. datatype = gdal.GDT_Byte
  55. elif 'int16' in im_data.dtype.name:
  56. datatype = gdal.GDT_UInt16
  57. else:
  58. datatype = gdal.GDT_Float32
  59. if len(im_data.shape) == 3: # len(im_data.shape)表示矩阵的维数
  60. im_bands, im_height, im_width = im_data.shape # (维数,行数,列数)
  61. else:
  62. im_bands, (im_height, im_width) = 1, im_data.shape # 一维矩阵
  63. # 创建文件
  64. driver = gdal.GetDriverByName('GTiff') # 数据类型必须有,因为要计算需要多大内存空间
  65. data = driver.Create(filename, im_width, im_height, im_bands, datatype)
  66. data.SetGeoTransform(im_geotrans) # 写入仿射变换参数
  67. data.SetProjection(im_proj) # 写入投影
  68. if im_bands == 1:
  69. data.GetRasterBand(1).WriteArray(im_data) # 写入数组数据
  70. else:
  71. for i in range(im_bands):
  72. data.GetRasterBand(i + 1).WriteArray(im_data[i])
  73. del data
  74. a = Grid()
  75. '''
  76. 目标:寻找映射最大值、最小值
  77. 输入:四个数组(子图中绘制的四个tif)
  78. 输出:四个数组全体的最大值、最小值
  79. '''
  80. def find_global_max_min(arr1, arr2, arr3, arr4):
  81. # 初始化最大值和最小值
  82. global_max = None
  83. global_min = None
  84. # 将四个数组放入列表中
  85. arrays = [arr1, arr2, arr3, arr4]
  86. # 遍历每个数组并查找最大值和最小值
  87. for arr in arrays:
  88. local_max = np.nanmax(arr)
  89. local_min = np.nanmin(arr)
  90. # 更新全局最大值和最小值
  91. if global_max is None or local_max > global_max:
  92. global_max = local_max
  93. if global_min is None or local_min < global_min:
  94. global_min = local_min
  95. return global_max, global_min
  96. '''
  97. 输入:sos_data_lists, eos_data_lists, los_data_lists
  98. 输出:数组正值与负值占比
  99. '''
  100. def calculate_percentage(data_list):
  101. result = []
  102. for data in data_list:
  103. positive_count = 0
  104. negative_count = 0
  105. total_count = 0
  106. for row in data:
  107. for value in row:
  108. if -1 <= value <= 1:
  109. total_count += 1
  110. if value > 0:
  111. positive_count += 1
  112. elif value < 0:
  113. negative_count += 1
  114. if total_count == 0:
  115. result.append("No valid data in the array")
  116. else:
  117. positive_percentage = (positive_count / total_count) * 100
  118. negative_percentage = (negative_count / total_count) * 100
  119. result.append(positive_percentage)
  120. result.append(negative_percentage)
  121. return result
  122. #step1:数据准备
  123. #tif数据所在文件夹
  124. sos_paths = get_paths(r'D:\code\python\phenology\gee\partial_corr\result\corr\sos')
  125. eos_paths = get_paths(r'D:\code\python\phenology\gee\partial_corr\result\corr\eos')
  126. los_paths = get_paths(r'D:\code\python\phenology\gee\partial_corr\result\corr\los')
  127. #获取数组
  128. #sos
  129. proj, geotrans,sos_tem_corr = a.read_img(sos_paths[-1])
  130. _,_,sos_pre_corr = a.read_img(sos_paths[-2])
  131. _,_,sos_iet_corr = a.read_img(sos_paths[-3])
  132. _,_,sos_iep_corr = a.read_img(sos_paths[-4])
  133. #eos
  134. _,_,eos_tem_corr = a.read_img(eos_paths[-1])
  135. _,_,eos_pre_corr = a.read_img(eos_paths[-2])
  136. _,_,eos_iet_corr = a.read_img(eos_paths[-3])
  137. _,_,eos_iep_corr = a.read_img(eos_paths[-4])
  138. #los
  139. _,_,los_tem_corr = a.read_img(los_paths[-1])
  140. _,_,los_pre_corr = a.read_img(los_paths[-2])
  141. _,_,los_iet_corr = a.read_img(los_paths[-3])
  142. _,_,los_iep_corr = a.read_img(los_paths[-4])
  143. #按制图顺序,整理数据顺序
  144. #sos
  145. sos_data_lists = [sos_tem_corr, sos_pre_corr,sos_iet_corr,sos_iep_corr]
  146. sos_data_lists = np.array(sos_data_lists)
  147. #eos
  148. eos_data_lists = [eos_tem_corr, eos_pre_corr,eos_iet_corr,eos_iep_corr]
  149. eos_data_lists = np.array(eos_data_lists)
  150. #los
  151. los_data_lists = [los_tem_corr, los_pre_corr,los_iet_corr,los_iep_corr]
  152. los_data_lists = np.array(los_data_lists)
  153. #寻找sos/eos/los偏相关系数最大最小值
  154. sos_max, sos_min = find_global_max_min(sos_tem_corr, sos_pre_corr, sos_iet_corr,sos_iep_corr)
  155. eos_max, eos_min = find_global_max_min(eos_tem_corr, eos_pre_corr, eos_iet_corr,eos_iep_corr)
  156. los_max, los_min = find_global_max_min(los_tem_corr, los_pre_corr, los_iet_corr,los_iep_corr)
  157. print("SOS 数据集全局最大值:", sos_max, "\nSOS 数据集全局最小值:", sos_min, "\nEOS 数据集全局最大值:", eos_max, "\nEOS 数据集全局最小值:", eos_min, "\nLOS 数据集全局最大值:", los_max, "\nLOS 数据集全局最小值:", los_min)
  158. #边界数据
  159. shp_path = r'D:\Hydro_essay\bound\bound_without_proj.shp'
  160. #获取各子图正值、负值占比
  161. #sos-tem/pre/iet/iep
  162. sos_perc = calculate_percentage(sos_data_lists)
  163. sos_p_perc = []#4个值
  164. sos_p_perc.extend([sos_perc[0], sos_perc[2], sos_perc[4], sos_perc[6]])
  165. sos_n_perc = []#4个值
  166. sos_n_perc.extend([sos_perc[1], sos_perc[3], sos_perc[5], sos_perc[7]])
  167. #eos-tem/pre/iet/iep
  168. eos_perc = calculate_percentage(eos_data_lists)
  169. eos_p_perc = []#4个值
  170. eos_p_perc.extend([eos_perc[0], eos_perc[2], eos_perc[4], eos_perc[6]])
  171. eos_n_perc = []#4个值
  172. eos_n_perc.extend([eos_perc[1], eos_perc[3], eos_perc[5], eos_perc[7]])
  173. #los-tem/pre/iet/iep
  174. los_perc = calculate_percentage(los_data_lists)
  175. los_p_perc = []#4个值
  176. los_p_perc.extend([los_perc[0], los_perc[2], los_perc[4], los_perc[6]])
  177. los_n_perc = []#4个值
  178. los_n_perc.extend([los_perc[1], los_perc[3], los_perc[5], los_perc[7]])
  179. #%%
  180. #step2:开始画图
  181. # 字体和样式设置
  182. size1 = 4
  183. fontdict1 = {'weight': 'bold', 'size': 4, 'color': 'k', 'family': 'Times New Roman'}
  184. fontdict2 = {'size': 12, 'color': 'k', 'family': 'Times New Roman'}
  185. fontdict3 = {'size': 6, 'color': 'k', 'family': 'Times New Roman'}
  186. fontdict4 = {'weight': 'bold', 'size': 3.9, 'color': 'k', 'family': 'Times New Roman'}
  187. fontdict5 = {'weight': '1000', 'size': 4.5, 'color': 'k', 'family': "SimSun"}#colorbar
  188. fontdict6 = {'weight': '1000', 'size': 3.9, 'color': 'k', 'family': "SimSun"}#text
  189. #全体字体
  190. mpl.rcParams.update({
  191. 'text.usetex': False,
  192. 'font.family': 'serif',
  193. 'font.serif': ['Times New Roman'],
  194. 'font.size': size1,
  195. 'mathtext.fontset': 'stix',
  196. })
  197. # 投影设置
  198. proj = ccrs.PlateCarree()
  199. # 图框经纬度范围
  200. extent = [102.7, 104.7, 27.2, 29.2]#注意四个数字的排序!
  201. #tif图像经纬度设置
  202. extent_tif = [102.84521586, 104.43331875, 27.37916667, 28.90833333]
  203. # 图布大小设置
  204. fig = plt.figure(figsize=(25, 16), dpi=300)
  205. # 子图布局参数
  206. left_margin_adjusted = 0.345 #左列子图左侧图框距图布边儿距离
  207. bottom_margin_adjusted = 0.098 #下列子图下侧图框距图布边儿距离
  208. colorbar_left_adjusted = 0.82 # 色标的左边界位置调整
  209. # 建立轴
  210. ax_a = fig.add_axes([0.05, 0.52, 0.39, 0.39], projection=proj)
  211. ax_b = fig.add_axes([left_margin_adjusted, 0.52, 0.39, 0.39], projection=proj)
  212. ax_c = fig.add_axes([0.05, bottom_margin_adjusted, 0.39, 0.39], projection=proj)
  213. ax_d = fig.add_axes([left_margin_adjusted, bottom_margin_adjusted, 0.39, 0.39], projection=proj)
  214. # 设置轴上tick的长度和宽度,以及经纬度数字与轴的距离
  215. tick_length = 1.2 # 刻度线的长度
  216. tick_width = 0.4 # 刻度线的宽度
  217. label_pad = 1 # 刻度标签与轴的距离
  218. axis_line_width = 0.35 # 轴线(ticks)宽度
  219. # 设置归一化对象,该对象用于统一不同图像的色标-修改xos_min;xos_max替换数据1/4!!!
  220. norm = Normalize(vmin=sos_min, vmax=sos_max)
  221. #坐标设置
  222. #我希望左上子图只有左侧和上侧显示坐标;右上子图只有右侧和上侧显示坐标等...四个子图各不相同
  223. # 左上子图设置
  224. ax_a.tick_params(axis='x', which='both', bottom=False, top=True, labelbottom=False, labeltop=True,length=tick_length, width=tick_width, pad=label_pad)
  225. ax_a.tick_params(axis='y', which='both', left=True, right=False, labelleft=True, labelright=False,length=tick_length, width=tick_width, pad=label_pad)
  226. ax_a.xaxis.set_ticks_position('top')
  227. ax_a.yaxis.set_ticks_position('left')
  228. # 右上子图设置
  229. ax_b.tick_params(axis='x', which='both', bottom=False, top=True, labelbottom=False, labeltop=True,length=tick_length, width=tick_width, pad=label_pad)
  230. ax_b.tick_params(axis='y', which='both', left=False, right=True, labelleft=False, labelright=True,length=tick_length, width=tick_width, pad=label_pad)
  231. ax_b.xaxis.set_ticks_position('top')
  232. ax_b.yaxis.set_ticks_position('right')
  233. # 左下子图设置
  234. ax_c.tick_params(axis='x', which='both', bottom=True, top=False, labelbottom=True, labeltop=False,length=tick_length, width=tick_width, pad=label_pad)
  235. ax_c.tick_params(axis='y', which='both', left=True, right=False, labelleft=True, labelright=False,length=tick_length, width=tick_width, pad=label_pad)
  236. ax_c.xaxis.set_ticks_position('bottom')
  237. ax_c.yaxis.set_ticks_position('left')
  238. # 右下子图设置
  239. ax_d.tick_params(axis='x', which='both', bottom=True, top=False, labelbottom=True, labeltop=False,length=tick_length, width=tick_width, pad=label_pad)
  240. ax_d.tick_params(axis='y', which='both', left=False, right=True, labelleft=False, labelright=True,length=tick_length, width=tick_width, pad=label_pad)
  241. ax_d.xaxis.set_ticks_position('bottom')
  242. ax_d.yaxis.set_ticks_position('right')
  243. #子图title(我要画三张图,所以三个list)
  244. sos_titles = ['(a) SOS_tem','(b) SOS_pre','(c) SOS_IET','(d) SOS_IEP']
  245. eos_titles = ['(a) EOS_tem','(b) EOS_pre','(c) EOS_IET','(d) EOS_IEP']
  246. los_titles = ['(a) LOS_tem','(b) LOS_pre','(c) LOS_IET','(d) LOS_IEP']
  247. # 四个子图设置坐标线、经纬度、以及绘制shp文件和tif文件
  248. for idx,ax in enumerate([ax_a, ax_b, ax_c, ax_d]):
  249. print(idx)
  250. ax.set_extent(extent, crs=proj) # 设置地图显示的范围
  251. ax.set_xticks(np.arange(int(np.floor(extent[0])), int(np.ceil(extent[1])) + 1, 1), crs=proj)
  252. ax.set_yticks(np.arange(int(np.floor(extent[2])), int(np.ceil(extent[3])) + 1, 1), crs=proj)
  253. ax.xaxis.set_major_formatter(LongitudeFormatter(zero_direction_label=False))
  254. ax.yaxis.set_major_formatter(LatitudeFormatter())
  255. ax.set_extent(extent, crs=ccrs.PlateCarree())
  256. #ax.gridlines(crs=ccrs.PlateCarree(), draw_labels=False,linewidth=.8, linestyle=':', color='k', alpha=0.5)
  257. # 设置刻度只显示整数
  258. ax.xaxis.set_major_locator(MaxNLocator(integer=True))
  259. ax.yaxis.set_major_locator(MaxNLocator(integer=True))
  260. # 设置轴线宽度
  261. for spine in ax.spines.values():
  262. spine.set_linewidth(axis_line_width)
  263. #小刻度线
  264. xminorticks = MultipleLocator(1)
  265. yminorticks = MultipleLocator(1)
  266. ax.xaxis.set_minor_locator(xminorticks)
  267. ax.yaxis.set_minor_locator(yminorticks)
  268. # 绘制矢量边界
  269. ax.add_geometries(Reader(shp_path).geometries(), ccrs.PlateCarree(), facecolor='none', edgecolor='k', linewidth=0.25)
  270. #绘制tif-修改xos_data_lists替换数据2/4!!!
  271. ax.imshow(sos_data_lists[idx], extent=extent_tif, cmap='coolwarm', norm=norm, transform=proj, interpolation='nearest')
  272. #注:在使用imshow或contourf函数时,指定的extent必须准确匹配tif数据的实际地理范围,因此指定新的extent
  273. # 设置标题,并调整位置-修改xos_titles替换数据3/4!!!
  274. ax.text(0.5, 0.92, sos_titles[idx], transform=ax.transAxes, fontdict=fontdict1, ha='center')
  275. #添加正值负值比例-修改xos_p_perc;xos_n_perc[idx]替换数据4/4!!!
  276. # Red rectangle for > 0
  277. red_patch = patches.Rectangle((0.5, 0.18), 0.075, 0.035, transform=ax.transAxes, color='red', zorder=5)
  278. ax.add_patch(red_patch)
  279. red_ratio = sos_p_perc[idx]
  280. blue_ratio = sos_n_perc[idx]
  281. ax.text(0.6, 0.19, f'{red_ratio:.2f}% ', transform=ax.transAxes, fontdict=fontdict4, va='center')
  282. ax.text(0.8, 0.19, f'(正值)', transform=ax.transAxes, fontdict=fontdict6, va='center')
  283. # Blue rectangle for < 0
  284. blue_patch = patches.Rectangle((0.5, 0.10), 0.075, 0.035, transform=ax.transAxes, color='blue', zorder=5)
  285. ax.add_patch(blue_patch)
  286. ax.text(0.6, 0.11, f'{blue_ratio:.2f}% ', transform=ax.transAxes, fontdict=fontdict4, va='center')
  287. ax.text(0.8, 0.11, f'(负值)', transform=ax.transAxes, fontdict=fontdict6, va='center')
  288. print('drawing colorbar')
  289. # 设置色标位置和大小
  290. colorbar_width = 0.015
  291. colorbar_height = 0.8
  292. ax_colorbar = fig.add_axes([colorbar_left_adjusted-0.08, bottom_margin_adjusted, colorbar_width, colorbar_height])
  293. # 创建色标
  294. sm = mpl.cm.ScalarMappable(cmap='coolwarm', norm=norm)
  295. sm.set_array([])
  296. cbar = plt.colorbar(sm, cax=ax_colorbar)
  297. # 设置色标刻度
  298. cbar.set_ticks([-0.8, -0.6, -0.4, -0.2, 0, 0.2, 0.4, 0.6, 0.8])
  299. # 设置色标刻度标签格式为一位小数
  300. cbar.ax.yaxis.set_major_formatter(FuncFormatter(lambda x, _: f'{x:.1f}'))
  301. # 设置色标的刻度参数和移除边框
  302. cbar.ax.tick_params(axis='y', which='both', length=tick_length, width=tick_width, pad=label_pad)
  303. cbar.outline.set_linewidth(0) # 移除色标的边框
  304. # 设置色标标签
  305. cbar.set_label('偏相关系数', fontdict=fontdict5)
  306. plt.show()

完结撒花~欢迎交流和指正!

PS:图中的操作用Arcgis好像很快/(ㄒoㄒ)/~~

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号