Description
Problem
When using inset_axes
from mpl_toolkits.axes_grid1.inset_locator
getting the x,y coordinates of the axins border is very difficult.
Example situation: if I want to plot dashed lines from the zoomed area to the sub-graph:
For that I need to know the ax's x,y coordinates of the axins border.
I couldnt find any simple solution so I made a helper function myself.
Proposed solution
Create helper function inside mpl_toolkits.axes_grid1
:
def axins_bbox(ax, axins):
"""
Finds (x,y) coordinates of "axins" window inside "ax"
"""
# draw the canvas to update the BBox
ax.figure.canvas.draw()
ax_bbox = ax.get_position()
axins_bbox = axins.get_position()
parent_width = ax_bbox.x1 - ax_bbox.x0
parent_height = ax_bbox.y1 - ax_bbox.y0
x_range = ax.get_xlim()
y_range = ax.get_ylim()
axins_relative_x0 = (axins_bbox.x0 - ax_bbox.x0) / parent_width * (x_range[1] - x_range[0]) + x_range[0]
axins_relative_x1 = (axins_bbox.x1 - ax_bbox.x0) / parent_width * (x_range[1] - x_range[0]) + x_range[0]
axins_relative_y0 = (axins_bbox.y0 - ax_bbox.y0) / parent_height * (y_range[1] - y_range[0]) + y_range[0]
axins_relative_y1 = (axins_bbox.y1 - ax_bbox.y0) / parent_height * (y_range[1] - y_range[0]) + y_range[0]
return Bbox([[axins_relative_x0, axins_relative_y0], [axins_relative_x1, axins_relative_y1]])
Or maybe instead of calling axins_bbox(ax, axins)
we can do something like ax.get_bbox(axins)
(optional) Function for the dashed lines (not sure if ax should be passed or the function should return objects that the user will have to draw himself):
def draw_zoom_lines(ax, axins, p1=None, p2=None):
"""
Draw dashed lines around zoomed area, and to the sub-plot window.
p1, p2: The coordinates of the "zoom rectangle" to draw.
"""
# if zoomed area is not passed then assume that axins borders are the zoom area
if p1 == None or p2 == None:
xlim = axins.get_xlim()
ylim = axins.get_ylim()
p1 = [xlim[0], ylim[0]]
p2 = [xlim[1], ylim[1]]
# draw the zoomed area (rectangle)
ax.plot([p1[0], p1[0]], [p1[1], p2[1]], color='black', linestyle='-')
ax.plot([p2[0], p2[0]], [p1[1], p2[1]], color='black', linestyle='-')
ax.plot([p1[0], p2[0]], [p1[1], p1[1]], color='black', linestyle='-')
ax.plot([p1[0], p2[0]], [p2[1], p2[1]], color='black', linestyle='-')
# get relative bbox
cord_bbox = axins_bbox(ax, axins)
b1, b2 = [[cord_bbox.x0, cord_bbox.y0], [cord_bbox.x1, cord_bbox.y1]]
# set the x,y ranges so that dashed lines wont effect the graph xlims and ylims (the plotting area)
# (not 100% sure whats causing the rescaling, if you know better please tell. because touching ax is not ideal)
ax.set_xlim(ax.get_xlim())
ax.set_ylim(ax.get_ylim())
# draw the dashed lines to the zoom area
ax.plot([p1[0], b1[0]], [p1[1], b1[1]], color='gray', linestyle='--')
ax.plot([p1[0], b1[0]], [p2[1], b2[1]], color='gray', linestyle='--')
ax.plot([p2[0], b2[0]], [p1[1], b1[1]], color='gray', linestyle='--')
ax.plot([p2[0], b2[0]], [p2[1], b2[1]], color='gray', linestyle='--')
Instead of plotting many lines we can use the plot rectangle feature (not sure which one is more efficient).