Python 3.x 使用Cartopy进行紧凑布局而不传递w_pad参数?
当对Cartopy和多个子批次使用Python 3.x 使用Cartopy进行紧凑布局而不传递w_pad参数?,python-3.x,matplotlib,cartopy,Python 3.x,Matplotlib,Cartopy,当对Cartopy和多个子批次使用紧密布局时,结果不是紧密布局。例如: import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec import cartopy.crs as ccrs gs = gridspec.GridSpec(2,2) fig = plt.figure() for k in range(0,4): ax = plt.subplot(gs[k], projection = cc
紧密布局时,结果不是紧密布局。例如:
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import cartopy.crs as ccrs
gs = gridspec.GridSpec(2,2)
fig = plt.figure()
for k in range(0,4):
ax = plt.subplot(gs[k], projection = ccrs.PlateCarree())
ax.set_extent([0,50,0,90])
ax.coastlines()
gs.tight_layout(fig)
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
fig, ((ax1,ax2),(ax3,ax4)) = plt.subplots(2, 2,
subplot_kw=dict(projection=ccrs.PlateCarree()))
for ax in fig.axes:
ax.coastlines()
plt.tight_layout()
生成每个地图之间有较大间距的图形:
使用pyplot的紧密布局时也会出现同样的问题,无论我是否设置了ax.set\u extent()
,结果都不符合预期。例如:
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import cartopy.crs as ccrs
gs = gridspec.GridSpec(2,2)
fig = plt.figure()
for k in range(0,4):
ax = plt.subplot(gs[k], projection = ccrs.PlateCarree())
ax.set_extent([0,50,0,90])
ax.coastlines()
gs.tight_layout(fig)
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
fig, ((ax1,ax2),(ax3,ax4)) = plt.subplots(2, 2,
subplot_kw=dict(projection=ccrs.PlateCarree()))
for ax in fig.axes:
ax.coastlines()
plt.tight_layout()
虽然我可以在这两种情况下通过使用w_pad
和h_pad
参数对tight_layout
进行手动调整,但我必须根据子地块的数量和/或地图本身对每个地物进行手动调整。有没有办法在没有这些参数的情况下实现这一点,或者自动确定这些值应该是什么?或者自动设置数字大小
使用Linux 5.4.0-40-generic、Python 3.7.6 64位、Cartopy 0.18.0、Matplotlib 3.2.2
编辑:
问题是如何使用Cartopy复制tight_layout
功能,而无需根据绘图内容手动设置w_pad
或h_pad
目前,我正在创造许多不同的数字,改变:
- 投影:有些是平板投影,有些是极赤平投影
- 地图范围:整个地图,平台上的纬度/经度减小,极赤平极射图上的纬度减小
- 行数/列数不同:1到7行,1到5列
- 在第一列和最后一行添加纬度和经度标记、每列的标题、第一列的
y_标签
、以及总标题
- 根据图形,在每行的右侧添加一个颜色栏
最终结果应该是在每个子批次和不同图形之间具有一致的水平和垂直空间的图形,就像紧密布局的预期结果一样
包含上述所有内容的示例代码,如下@swatchai-answer:
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import cartopy.crs as ccrs
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
from cartopy.examples.waves import sample_data
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
ar = 1.0 # initial aspect ratio for first trial
wi = 5 # width in inches
hi = wi * ar # height in inches
# set number of rows/columns
rows, cols = 2,2
# set projection
# proj = ccrs.PlateCarree()
proj = ccrs.NorthPolarStereo()
# set lon/lat extent
# extent = [0,50,0,90] # example for PlateCarree
# extent = [-180,180,-90,90] # complete map
extent = [-180,180,30,90] # example for PolarStereo
gs = gridspec.GridSpec(rows, cols)
# Set figsize using wi and hi
fig = plt.figure(figsize=(wi, hi))
for r in range(0,rows):
for c in range(0,cols):
ax = plt.subplot(gs[r,c], projection = proj)
ax.set_extent(extent,ccrs.PlateCarree())
ax.coastlines()
# Add sample data
x, y, z = sample_data((20, 40))
z = z * -1.5 * y
cf = ax.contourf(x, y, z, transform=ccrs.PlateCarree())
# Add colorbar
if c == cols -1:
axins = inset_axes(ax,
bbox_to_anchor=(1.05, 0., 1, 1),
width='5%',height='98%',loc='lower left',
bbox_transform=ax.transAxes,
borderpad=0)
cbar = fig.colorbar(cf, cax=axins,orientation='vertical')
# Gridlines, labels, titles
if r == 0:
ax.set_title('column title')
if c == 0:
ax.set_ylabel('y label')
ax.set_yticks([])
if proj == ccrs.PlateCarree():
gl = ax.gridlines(draw_labels=False, crs=ccrs.PlateCarree())
gl.xformatter, gl.yformatter = LONGITUDE_FORMATTER, LATITUDE_FORMATTER
if r == rows-1:
gl.xlabels_bottom = True
if c == 0:
gl.ylabels_left = True
ax.set_ylabel('y label',labelpad=35)
ax.set_yticks([])
elif proj == ccrs.NorthPolarStereo():
ax.gridlines()
plt.suptitle('A figure title')
# Do this to get updated positions/dimensions
plt.draw()
# # Get proper ratio here
# # Computed from the last `ax` (being the current)
xmin, xmax = ax.get_xbound()
ymin, ymax = ax.get_ybound()
y2x_ratio = (ymax-ymin)/(xmax-xmin) * rows/cols
# # Apply new h/w aspect ratio by changing h
# # Also possible to change w using set_figwidth()
fig.set_figheight(wi * y2x_ratio)
gs.tight_layout(fig)
plt.show()
根据子地块/投影的数量,上面生成的图形具有非常不同的水平/垂直间隙。标签、标题等有时也会与整个图形不成比例(当紧凑布局
按预期工作时不会发生这种情况)。有些还可以旋转。例如,将上述代码与行一起使用,cols=3,4
,PlateCarree()
和绘制的完整地图,给出:
要获得更好的绘图,必须声明figsize
具有适当的宽度和高度,以使高宽比与子地块大小匹配。可以在代码中以编程方式计算比率,然后将其作为更新值应用,并根据需要获得绘图
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import cartopy.crs as ccrs
# use figsize=(w,h) of proper values can give good result
# the aspect ratio, h/w is important
# default is around 1, is good in general, but not in your case
ar = 1.0 # initial aspect ratio for first trial
wi = 5 # width in inches
hi = wi * ar # height in inches
gs = gridspec.GridSpec(2,2)
# Set figsize using wi and hi
fig = plt.figure(figsize=(wi, hi))
for k in range(0,4):
ax = plt.subplot(gs[k], projection = ccrs.PlateCarree())
# any of these projections are OK to use
# PlateCarree() Robinson() Mercator() TransverseMercator() Orthographic()
ax.set_extent([0,50,0,90])
ax.coastlines()
ax.gridlines()
# Do this to get updated positions/dimensions
plt.draw()
# Get proper ratio here
xmin, xmax = ax.get_xbound()
ymin, ymax = ax.get_ybound()
y2x_ratio = (ymax-ymin)/(xmax-xmin)
# This also gives same ratio
#x1, x2 = ax.get_xlim()
#y1, y2 = ax.get_ylim()
#y2x_ratio = (y2-y1)/(x2-x1)
print("y2x_ratio: "+ str(y2x_ratio))
#1.8258 with Robinson; 1.8 PlateCarree; 3.37 Mercator; 1.549 TransverseMercator
# Apply new h/w aspect ratio by changing h
# Also possible to change w using set_figwidth()
fig.set_figheight(wi * y2x_ratio)
gs.tight_layout(fig)
plt.show()
输出曲线图:
编辑1
最初的答案集中在2x2(行x列)的数组上。对于其他阵列,必须更改纵横比的计算以考虑它。下面是可运行代码和示例图的更新版本
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import cartopy.crs as ccrs
# use figsize=(w,h) of proper values can give good result
# the aspect ratio, h/w is important
# default is around 1, is good in general cases
ar = 1.0 # initial aspect ratio for first trial
wi = 5 # width of the whole figure in inches, ...
# set it wide enough to cover all columns of sub-plots
hi = wi * ar # height in inches
# set number of rows/columns
rows, cols = 4,3
gs = gridspec.GridSpec(rows, cols)
# Set figsize using wi and hi
fig = plt.figure(figsize=(wi, hi))
for k in range(0, rows*cols):
ax = plt.subplot(gs[k], projection = ccrs.Robinson())
# any of these projections are OK to use
# PlateCarree() Robinson() Mercator() Orthographic()
#ax.set_extent([0,50,0,90])
ax.coastlines()
ax.gridlines()
# Do this to get updated positions/dimensions
plt.draw()
# Get proper ratio here
# Computed from the last `ax` (being the current)
xmin, xmax = ax.get_xbound()
ymin, ymax = ax.get_ybound()
y2x_ratio = (ymax-ymin)/(xmax-xmin) * rows/cols
# Apply new h/w aspect ratio by changing h
# Also possible to change w using set_figwidth()
fig.set_figheight(wi * y2x_ratio)
gs.tight_layout(fig)
plt.show()
输出曲线图:
我很感激这个答案,但当我绘制整个地图时(没有ax.set\u extent
),我似乎无法做到这一点。当使用更多子批次(例如4行x 3列)时,这一点尤其明显。我还尝试添加一个figwidth(…)
和x2x\u ratio=1/y2x\u ratio
以及变量wi
的其他组合。这在使用ax.set\u extent
和不同数量的子地块的情况下很有帮助,但在绘制整个地图时似乎还不够。我很感谢更新。虽然这样做效果更好,但仍会根据投影、地图范围和子地块的数量留下不同的水平/垂直间距。我似乎也找不到一个好的fig.set_figwidth(…)
,因为改变它也会改变垂直间隙。也许我错过了什么?否则,将其应用于每个绘图似乎比仅使用自定义的w_pad
和h_pad
@lanadaquenada请在问题中明确说明您的所有要求更困难。像这样的自定义绘图有很多细节需要处理。我编辑了这个问题,希望它现在足够清晰。我忘了在我的代码中提到wi=5
是整个图形的总宽度(以英寸为单位),用户必须指定以容纳所有子绘图。在上一个绘图中,5显然太小,wi=17
应该可以生成更好的绘图。记住,你总是需要一些合理的尺寸来开始。你是如何得出17是这个特定情节的正确数字的?试错?如果是,那么使用此代码并手动调整宽度与手动调整紧密布局上的参数w_pad
和h_pad
相比会有什么好处?在这种特殊情况下,任何合理的大值,例如15-20都足以容纳4列绘图(或每列3+英寸)。大一点也不疼。如果不使用插入轴()
,步骤紧密布局()
将在大多数情况下很好地调整总体尺寸。使用插入轴()
会中断紧密布局()的功能,因此不允许使用其某些选项,包括w\u pad
和h\u pad
。图形的所有尺寸(w和h)必须由用户指定(或跳过并使用默认值)。我只定义了宽度
,而高度
是通过编程计算的。