Skip to content

[ENH]: axins border coordinates inside ax #30176

Closed
@zfrhv

Description

@zfrhv

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:

Image

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions