Search code examples
pythonmatplotlibz-ordercartopy

Cartopy coastlines hidden by inset_axes use of Axes.pie


I am producing a map of the world with pie charts in individual model grid boxes. I make the map and coastlines using cartopy. The pie charts I produce using inset_axes. Unfortunately the pie charts hide the coastlines and I'd like to see them clearly.

Minimum working example:

import cartopy.crs as ccrs
import numpy as np
import cartopy.feature as feature
import matplotlib.pyplot as plt

def plot_pie_inset(dataframe_pie,ilat_pie,ilon_pie,axis_main,width_local,alpha_local):
    ax_sub= inset_axes(axis_main, width=width_local, height=width_local, loc=3, bbox_to_anchor=(ilat_pie, ilon_pie),bbox_transform=axis_main.figure.transFigure, borderpad=0.0)
    wedges,texts= ax_sub.pie(dataframe_pie,colors=colors_dual)
    for w in wedges:
        w.set_linewidth(0.02)
        w.set_alpha(alpha_local)
        w.set_zorder(1)
    plt.axis('equal')

colors_dual=['RosyBrown','LightBlue']
lat_list= np.arange(0.2,0.7,0.05)

fig= plt.figure()
ax_main= plt.subplot(1,1,1,projection=ccrs.PlateCarree())
ax_main.coastlines(zorder=3)
for ilat in np.arange(len(lat_list)):
    plot_pie_inset([75,25],lat_list[ilat],0.72,ax_main,0.2,0.9)

plt.show()

I can see the coastlines by making the pie charts partially transparent by reducing the alpha value. However, this makes the colors somewhat muted. My aim is to have the coastlines as the topmost layer.

I have attempted to use 'zorder' to force the coastlines to the top layer. However, 'zorder' cannot be passed to inset_axes, nor to ax.pie so I've made the patches of color in pie charts translucent. This fails because the ax_main.coastlines does not have its own 'zorder'. The coastline zorder seems to be tied to that of ax_main. There is no benefit in increasing the zorder of ax_main.

Any suggestions greatly welcomed.


Solution

  • The problem is that each axes either lies on top or below another axes. So changing the zorder of artists within axes, does not help here. In principle, one could set the zorder of the axes themselves, putting the inset axes behind the main axes.

    ax_sub.set_zorder(axis_main.get_zorder()-1)
    

    Cartopy's GeoAxes uses its own background patch. This would then need to be set to invisble.

    ax_main.background_patch.set_visible(False)
    

    Complete example:

    import cartopy.crs as ccrs
    import numpy as np
    import matplotlib.pyplot as plt
    from mpl_toolkits.axes_grid1.inset_locator import inset_axes
    
    def plot_pie_inset(dataframe_pie,ilat_pie,ilon_pie,axis_main,width_local,alpha_local):
        ax_sub= inset_axes(axis_main, width=width_local, height=width_local, loc=3, 
                           bbox_to_anchor=(ilat_pie, ilon_pie),
                           bbox_transform=axis_main.transAxes, 
                           borderpad=0.0)
        wedges,texts= ax_sub.pie(dataframe_pie,colors=colors_dual)
        for w in wedges:
            w.set_linewidth(0.02)
            w.set_alpha(alpha_local)
            w.set_zorder(1)
        plt.axis('equal')
        # Put insets behind main axes
        ax_sub.set_zorder(axis_main.get_zorder()-1)
    
    colors_dual=['RosyBrown','LightBlue']
    lat_list= np.arange(0.2,0.7,0.05)
    
    fig= plt.figure()
    ax_main= plt.subplot(1,1,1,projection=ccrs.PlateCarree())
    ax_main.coastlines()
    
    # set background patch invisible, such that axes becomes transparent
    # since the GeoAxes from cartopy uses a different patch as background
    # the following does not work
    # ax_main.patch.set_visible(False)
    # so we need to set the GeoAxes' background_patch invisible
    ax_main.background_patch.set_visible(False)
    
    for ilat in np.arange(len(lat_list)):
        plot_pie_inset([75,25],lat_list[ilat],0.72,ax_main,0.2,0.9)
    
    plt.show()
    

    enter image description here