色标相对于地轴的正确放置(Cartopy) [英] Correct placement of colorbar relative to geo axes (cartopy)

查看:437
本文介绍了色标相对于地轴的正确放置(Cartopy)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用Cartopy,我想完全控制我的颜色条的去向.通常,我以当前轴的位置为基础,然后为颜色栏创建新的轴.这对于标准matplotlib轴来说效果很好,但在使用Cartopy和geo_axes时效果不佳,因为这会使轴变形.

Using Cartopy, I would like to have full control of where my colorbar goes. Usually I do this by getting the current axes position as basis and then create new axes for the colorbar. This works well for standard matplotlib axes but not when using Cartopy and geo_axes, because this will distort the axes.

所以,我的问题是:如何获得geo_axes的确切位置?

So, my question is: how do I get the exact position of my geo_axes?

这是基于Cartopy docs http://的代码示例scitools.org.uk/cartopy/docs/latest/matplotlib/advanced_plotting.html :

Here is a code example based on the Cartopy docs http://scitools.org.uk/cartopy/docs/latest/matplotlib/advanced_plotting.html:

import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import os
from netCDF4 import Dataset as netcdf_dataset
from cartopy import config

def main():
    fname = os.path.join(config["repo_data_dir"],
                     'netcdf', 'HadISST1_SST_update.nc'
                     )

    dataset = netcdf_dataset(fname)

    sst = dataset.variables['sst'][0, :, :]
    lats = dataset.variables['lat'][:]
    lons = dataset.variables['lon'][:]

    #my preferred way of creating plots (even if it is only one plot)
    ef, ax = plt.subplots(1,1,figsize=(10,5),subplot_kw={'projection': ccrs.PlateCarree()})
    ef.subplots_adjust(hspace=0,wspace=0,top=0.925,left=0.1)

    #get size and extent of axes:
    axpos = ax.get_position()
    pos_x = axpos.x0+axpos.width + 0.01# + 0.25*axpos.width
    pos_y = axpos.y0
    cax_width = 0.04
    cax_height = axpos.height
    #create new axes where the colorbar should go.
    #it should be next to the original axes and have the same height!
    pos_cax = ef.add_axes([pos_x,pos_y,cax_width,cax_height])

    im = ax.contourf(lons, lats, sst, 60, transform=ccrs.PlateCarree())

    ax.coastlines()

    plt.colorbar(im, cax=pos_cax)

    ax.coastlines(resolution='110m')
    ax.gridlines()
    ax.set_extent([-20, 60, 33, 63])

    #when using this line the positioning of the colorbar is correct,
    #but the image gets distorted.
    #when omitting this line, the positioning of the colorbar is wrong,
    #but the image is well represented (not distorted).
    ax.set_aspect('auto', adjustable=None)

    plt.savefig('sst_aspect.png')
    plt.close()



if __name__ == '__main__': main()

使用"set_aspect"时的结果图:

Resulting Figure, when using "set_aspect":

结果图,当省略"set_aspect"时:

Resulting Figure, when omitting "set_aspect":

基本上,我想获取第一个图形(正确放置的颜色栏),但不使用"set_aspect". 我想通过一些转换就可以做到这一点,但是到目前为止我还没有找到解决方案.

Basically, I'd like to obtain the first figure (correctly placed colorbar) but without using the "set_aspect". I guess this should be possible with some transformations, but I didn't find a solution so far.

谢谢!

推荐答案

好问题!感谢您提供的代码和图片,这使问题更容易理解,并且使快速迭代可能的解决方案变得容易.

Great question! Thanks for the code, and pictures, it makes the problem a lot easier to understand as well as making it easier to quickly iterate on possible solutions.

这里的问题本质上是一个matplotlib问题. Cartopy调用ax.set_aspect('equal'),因为这是投影定义的笛卡尔单元的一部分.

The problem here is essentially a matplotlib one. Cartopy calls ax.set_aspect('equal') as this is part of the the Cartesian units of a projection's definition.

Matplotlib的相等长宽比功能会调整轴的大小以匹配x和y限制,而不是更改限制以适合轴矩形.因此,轴不会填满图中分配给它的空间.如果以交互方式调整图形的大小,则将看到轴所占用的空间量根据调整图形大小的方向而有所不同.

Matplotlib's equal aspect ratio functionality resizes the axes to match the x and y limits, rather than changing the limits to fit to the axes rectangle. It is for this reason that the axes does not fill the space allocated to it on the figure. If you interactively resize the figure you will see that the amount of space that the axes occupies varies depending on the aspect that you resize your figure to.

识别轴位置的最简单方法是使用您一直在使用的ax.get_position()方法.但是,正如我们现在所知道的,此位置"随图形的大小而变化.因此,一种解决方案是每次调整图形大小时重新计算颜色栏的位置.

The simplest way of identifying the location of an axes is with the ax.get_position() method you have already been using. However, as we now know, this "position" changes with the size of the figure. One solution therefore is to re-calculate the position of the colorbar each time the figure is resized.

matplotlib事件机制具有一个"resize_event",每次都会触发调整了图的大小.如果我们将这种机制用于您的颜色条,则我们的事件可能类似于:

The matplotlib event machinery has a "resize_event" which is triggered each time a figure is resized. If we use this machinery for your colorbar, our event might look something like:

def resize_colobar(event):
    # Tell matplotlib to re-draw everything, so that we can get
    # the correct location from get_position.
    plt.draw()

    posn = ax.get_position()
    colorbar_ax.set_position([posn.x0 + posn.width + 0.01, posn.y0,
                             0.04, axpos.height])

fig.canvas.mpl_connect('resize_event', resize_colobar)

因此,如果我们将其与Cartopy和您的原始问题联系起来,现在可以根据地理轴的位置调整颜色条的大小.完成此操作的完整代码如下:

So if we relate this back to cartopy, and your original question, it is now possible to resize the colorbar based on the position of the geo-axes. The full code to do this might look like:

import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import os
from netCDF4 import Dataset as netcdf_dataset
from cartopy import config


fname = os.path.join(config["repo_data_dir"],
                 'netcdf', 'HadISST1_SST_update.nc'
                 )
dataset = netcdf_dataset(fname)
sst = dataset.variables['sst'][0, :, :]
lats = dataset.variables['lat'][:]
lons = dataset.variables['lon'][:]

fig, ax = plt.subplots(1, 1, figsize=(10,5),
                       subplot_kw={'projection': ccrs.PlateCarree()})

# Add the colorbar axes anywhere in the figure. Its position will be
# re-calculated at each figure resize. 
cbar_ax = fig.add_axes([0, 0, 0.1, 0.1])

fig.subplots_adjust(hspace=0, wspace=0, top=0.925, left=0.1)

sst_contour = ax.contourf(lons, lats, sst, 60, transform=ccrs.PlateCarree())


def resize_colobar(event):
    plt.draw()

    posn = ax.get_position()
    cbar_ax.set_position([posn.x0 + posn.width + 0.01, posn.y0,
                          0.04, posn.height])

fig.canvas.mpl_connect('resize_event', resize_colobar)

ax.coastlines()

plt.colorbar(sst_contour, cax=cbar_ax)


ax.gridlines()
ax.set_extent([-20, 60, 33, 63])

plt.show()

这篇关于色标相对于地轴的正确放置(Cartopy)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆