2015-05-04 41 views
9

Cartopy kullanarak, renk çubuğumun nereye gittiğini tam olarak kontrol etmek istiyorum. Genellikle şu anki eksenlerin konumunu temel alarak ve sonra renk çubuğu için yeni eksenler oluşturarak yapıyorum. Bu, standart matplotlib eksenleri için iyi çalışır, ancak Cartopy ve geo_axes kullanırken değil, çünkü bu eksenleri bozar.Renk çubuklarının coğrafi eksenlere göre doğru yerleştirilmesi (kartoplu)

Yani, sorum şu: geo_axes'imin tam konumunu nasıl alabilirim? "Set_aspect" kullanıldığında

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() 

, Şekil Sonuç: Burada

Cartopy dokümanlar http://scitools.org.uk/cartopy/docs/latest/matplotlib/advanced_plotting.html göre bir kod örneği Şekil Sonuç enter image description here

, "set_aspect" atlama zaman: enter image description here

Temel olarak, ilk rakamı (doğru yerleştirilmiş colorbar) ancak "set_aspect" kullanmadan almak istiyorum. Sanırım bu bazı dönüşümler ile mümkün olmalıydı, ancak şimdiye kadar bir çözüm bulamadım.

Teşekkürler!

cevap

12

Harika soru! Kod ve resimler için teşekkürler, sorunu daha kolay anlamanın yanı sıra olası çözümleri hızlı bir şekilde yinelemeyi kolaylaştırır.

Burada sorun aslında bir matplotlib biridir. Cartopy, bir projeksiyonun tanımının Kartezyen birimlerinin bir parçası olduğu için ax.set_aspect('equal')'u çağırır.

Matplotlib'in eşit en boy oranı işlevi, eksenleri dikdörtgene sığacak şekilde değiştirmek yerine, x ve y sınırlarına uyacak şekilde eksenleri yeniden boyutlandırır. Bu nedenle, eksenler, şekil üzerinde kendisine tahsis edilen alanı doldurmaz. Rakamı etkileşimli olarak yeniden boyutlandırırsanız, eksenlerin kapladığı alan miktarının, şeklinizi yeniden boyutlandırdığınız boyuta göre değiştiğini görürsünüz.

Eksenlerin yerini belirlemenin en basit yolu, daha önce kullanmakta olduğunuz ax.get_position() yöntemidir. Ancak, şimdi bildiğimiz gibi, bu "konum" figürün büyüklüğü ile değişir. Bu nedenle, bir çözüm, şekil her yeniden boyutlandırıldığında, renk çubuğunun konumunu yeniden hesaplamaktır.

matplotlib event machinery, bir şekil yeniden boyutlandırıldığında her seferinde tetiklenen bir "resize_event" değerine sahiptir. Bu cartopy için geri ve baştaki soru ilgilidir Yani, eğer konumuna dayalı Colorbar yeniden boyutlandırmak için artık mümkün

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) 

: Biz Colorbar için bu makine kullanırsanız gibi bizim olay görünebilir jeo-eksenler. mpl_toolkits.axes_grid1 matplotlib en çok test parçası olmadığını akılda

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() 
+0

Büyük cevap! Bu çözümü asla kendi başıma bulamazdım! – user3497890

+0

Belki kısa yan soru eğer yapabilir: mümkün mü başka bir yere resize_colorbar tanımını koymak ve argümanlar olarak (bu fonksiyon yeniden olması için) balta ve cbar_ax geçmek? – user3497890

+0

Güncelleme: plt.show() doğru sonucu verir iken, plt.savefig ('sst.png') değil (Colorbar [0,0,0.1,0.1] orijinal konumunda kalır). Arka ucunu matplotlib.use ('Agg') olarak değiştirmeye çalıştı ancak bu yardımcı olmuyor. Savefig ile çalışmayı nasıl başarabilirim? – user3497890

0

Rulman, bunun ne istediğinizi elde etmek için işlevselliğini kullanabilirsiniz: Bunu yapmak için tam kod gibi görünebilir.aksi takdirde Colorbar

import numpy as np 
import matplotlib.pyplot as plt 
from mpl_toolkits.axes_grid1 import make_axes_locatable 

def sample_data_3d(shape): 
    """Returns `lons`, `lats`, and fake `data` 

    adapted from: 
    http://scitools.org.uk/cartopy/docs/v0.15/examples/axes_grid_basic.html 
    """ 


    nlons, nlats = shape 
    lats = np.linspace(-np.pi/2, np.pi/2, nlats) 
    lons = np.linspace(0, 2 * np.pi, nlons) 
    lons, lats = np.meshgrid(lons, lats) 
    wave = 0.75 * (np.sin(2 * lats) ** 8) * np.cos(4 * lons) 
    mean = 0.5 * np.cos(2 * lats) * ((np.sin(2 * lats)) ** 2 + 2) 

    lats = np.rad2deg(lats) 
    lons = np.rad2deg(lons) 
    data = wave + mean 

    return lons, lats, data 


# get data 
lons, lats, data = sample_data_3d((180, 90)) 


# set up the plot 
proj = ccrs.PlateCarree() 

f, ax = plt.subplots(1, 1, subplot_kw=dict(projection=proj)) 
h = ax.pcolormesh(lons, lats, data, transform=proj, cmap='RdBu') 
ax.coastlines() 

# following https://matplotlib.org/2.0.2/mpl_toolkits/axes_grid/users/overview.html#colorbar-whose-height-or-width-in-sync-with-the-master-axes 
# we need to set axes_class=plt.Axes, else it attempts to create 
# a GeoAxes as colorbar 

divider = make_axes_locatable(ax) 
ax_cb = divider.new_horizontal(size="5%", pad=0.1, axes_class=plt.Axes) 


f.add_axes(ax_cb) 
plt.colorbar(h, cax=ax_cb) 

Colorbar with make_axes_locatable

gibi bir GeoAxes oluşturmaya çalışır, bu axes_class=plt.Axes olarak ayarlanmış olması gerekir,

Biz mpl_toolkits belgelerinde verilen Example kullanabilirsiniz, ancak axes_class ihtiyacı açıkça ayarlanması

Ayrıca mpl_toolkits.axes_grid1 den AxesGrid kullanan cartopy example not edin.