imagematplotlibseabornvisualizationoverlay

Overlapping a Seaborn circular bar chart on map


I have a map image, and I have a circular Seaborn bar plot. The map looks like this:

enter image description here

and the Seaborn plot looks like this:

enter image description here

How can I overlap the two, and offset the Seaborn plot on the map to match specific coordinates? For instance, I want something that looks like the following image, where the circular bar chart is offset on the image with known coordinates indicating a specific Lat,Lon location.

enter image description here

I've tried using code from this example:

img = plt.imread('map.jpg')
fig, ax = plt.subplots(figsize=(6, 6))
plt.style.use('dark_background')
sns.barplot(x='Provider', y='Var_diff', hue='Provider', data=one_stat, ax=ax, 
palette='gist_rainbow', legend=False)
# Turn off surrounding circular frame
ax.set_frame_on(False)
# Turn off axis labels
ax.set(xlabel=None); ax.set(ylabel=None)
ax.set(xticks=[]); ax.set(yticks=[])
ax.imshow(img, extent=[-18, 18, -9, 9])

, but the same argument (subplot_kw=dict(polar=True)) used to make the barplot circular in the figure and axis definition (e.g., fig, ax = plt.subplots(figsize=(6, 6), subplot_kw=dict(polar=True))) curve the background image to match the circular bar plot:

enter image description here

and taking the argument out results in a purely rectangular plot:

enter image description here

Is there a way to define two different figures and axes and overlap them? Or how could I achieve the final result?

Solution

Using @Stef's response, my final plot command ended up being:

img = plt.imread('map.jpg')
fig, ax_bg = plt.subplots()
ax_bars = fig.add_axes(rect=(0.2, 0.2, 0.2, 0.2), polar=True)
plt.style.use('dark_background')
sns.barplot(x='Provider', y='Var_diff', hue='Provider', 
data=one_stat, ax=ax_bars, palette='gist_rainbow', legend=False)
# Turn off surrounding circular frame
ax_bars.set_frame_on(False)
# Turn off axis labels
ax_bars.set(xlabel=None); ax_bars.set(ylabel=None)
ax_bars.set(xticks=[]); ax_bars.set(yticks=[])
ax_bg.imshow(img, extent=[-180, 180, -90, 90])

which produces

enter image description here


Solution

  • You can first create the axes for the background image and then create another axes for the polar bar chart on top of it. The only difficulty is to get the rect coords for the bars axes. If you don't want to use trial and error you can first transform from data to display coords ax_bg.transData.transform(...) and then back to figure coords fig.transFigure.inverted().transform(...) for the rect coords of the bars axes.

    fig, ax_bg = plt.subplots()
    ax_bars = fig.add_axes(rect=(0.7, 0.2, .2, .2), polar=True)
    ax_bars.set_axis_off()
    
    ax_bars.bar(range(4), [2, .1, 3, 4])
    

    enter image description here