Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit bc39b1c

Browse filesBrowse files
authored
Merge pull request #11026 from jklymak/enh-axes-add-axes
ENH add an inset_axes to the axes class
2 parents d23ebc9 + a5b27e9 commit bc39b1c
Copy full SHA for bc39b1c

File tree

Expand file treeCollapse file tree

7 files changed

+373
-3
lines changed
Filter options
Expand file treeCollapse file tree

7 files changed

+373
-3
lines changed

‎.flake8

Copy file name to clipboardExpand all lines: .flake8
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ per-file-ignores =
251251
examples/subplots_axes_and_figures/demo_constrained_layout.py: E402
252252
examples/subplots_axes_and_figures/demo_tight_layout.py: E402
253253
examples/subplots_axes_and_figures/two_scales.py: E402
254+
examples/subplots_axes_and_figures/zoom_inset_axes.py: E402
254255
examples/tests/backend_driver_sgskip.py: E402, E501
255256
examples/text_labels_and_annotations/annotation_demo.py: E501
256257
examples/text_labels_and_annotations/custom_legends.py: E402

‎doc/api/axes_api.rst

Copy file name to clipboardExpand all lines: doc/api/axes_api.rst
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,9 @@ Text and Annotations
181181
Axes.text
182182
Axes.table
183183
Axes.arrow
184+
Axes.inset_axes
185+
Axes.indicate_inset
186+
Axes.indicate_inset_zoom
184187

185188

186189
Fields

‎examples/axes_grid1/inset_locator_demo.py

Copy file name to clipboardExpand all lines: examples/axes_grid1/inset_locator_demo.py
+3-3Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
"""
77

88
###############################################################################
9-
# The `.inset_locator`'s `~.inset_axes` allows to easily place insets in the
10-
# corners of the axes by specifying a width and height and optionally
11-
# a location (loc) which accepts locations as codes, similar to
9+
# The `.inset_locator`'s `~.axes_grid1.inset_axes` allows to easily place
10+
# insets in the corners of the axes by specifying a width and height and
11+
# optionally a location (loc) which accepts locations as codes, similar to
1212
# `~matplotlib.axes.Axes.legend`.
1313
# By default, the inset is offset by some points from the axes - this is
1414
# controlled via the `borderpad` parameter.
+60Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
"""
2+
======================
3+
Zoom region inset axes
4+
======================
5+
6+
Example of an inset axes and a rectangle showing where the zoom is located.
7+
8+
"""
9+
10+
import matplotlib.pyplot as plt
11+
import numpy as np
12+
13+
14+
def get_demo_image():
15+
from matplotlib.cbook import get_sample_data
16+
import numpy as np
17+
f = get_sample_data("axes_grid/bivariate_normal.npy", asfileobj=False)
18+
z = np.load(f)
19+
# z is a numpy array of 15x15
20+
return z, (-3, 4, -4, 3)
21+
22+
fig, ax = plt.subplots(figsize=[5, 4])
23+
24+
# make data
25+
Z, extent = get_demo_image()
26+
Z2 = np.zeros([150, 150], dtype="d")
27+
ny, nx = Z.shape
28+
Z2[30:30 + ny, 30:30 + nx] = Z
29+
30+
ax.imshow(Z2, extent=extent, interpolation="nearest",
31+
origin="lower")
32+
33+
# inset axes....
34+
axins = ax.inset_axes([0.5, 0.5, 0.47, 0.47])
35+
axins.imshow(Z2, extent=extent, interpolation="nearest",
36+
origin="lower")
37+
# sub region of the original image
38+
x1, x2, y1, y2 = -1.5, -0.9, -2.5, -1.9
39+
axins.set_xlim(x1, x2)
40+
axins.set_ylim(y1, y2)
41+
axins.set_xticklabels('')
42+
axins.set_yticklabels('')
43+
44+
ax.indicate_inset_zoom(axins)
45+
46+
plt.show()
47+
48+
#############################################################################
49+
#
50+
# ------------
51+
#
52+
# References
53+
# """"""""""
54+
#
55+
# The use of the following functions and methods is shown in this example:
56+
57+
import matplotlib
58+
matplotlib.axes.Axes.inset_axes
59+
matplotlib.axes.Axes.indicate_inset_zoom
60+
matplotlib.axes.Axes.imshow

‎lib/matplotlib/axes/_axes.py

Copy file name to clipboardExpand all lines: lib/matplotlib/axes/_axes.py
+249Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,37 @@ def _plot_args_replacer(args, data):
8484
"multiple plotting calls instead.")
8585

8686

87+
def _make_inset_locator(bounds, trans, parent):
88+
"""
89+
Helper function to locate inset axes, used in
90+
`.Axes.inset_axes`.
91+
92+
A locator gets used in `Axes.set_aspect` to override the default
93+
locations... It is a function that takes an axes object and
94+
a renderer and tells `set_aspect` where it is to be placed.
95+
96+
Here *rect* is a rectangle [l, b, w, h] that specifies the
97+
location for the axes in the transform given by *trans* on the
98+
*parent*.
99+
"""
100+
_bounds = mtransforms.Bbox.from_bounds(*bounds)
101+
_trans = trans
102+
_parent = parent
103+
104+
def inset_locator(ax, renderer):
105+
bbox = _bounds
106+
bb = mtransforms.TransformedBbox(bbox, _trans)
107+
tr = _parent.figure.transFigure.inverted()
108+
bb = mtransforms.TransformedBbox(bb, tr)
109+
return bb
110+
111+
return inset_locator
112+
113+
87114
# The axes module contains all the wrappers to plotting functions.
88115
# All the other methods should go in the _AxesBase class.
89116

117+
90118
class Axes(_AxesBase):
91119
"""
92120
The :class:`Axes` contains most of the figure elements:
@@ -390,6 +418,227 @@ def legend(self, *args, **kwargs):
390418
def _remove_legend(self, legend):
391419
self.legend_ = None
392420

421+
def inset_axes(self, bounds, *, transform=None, zorder=5,
422+
**kwargs):
423+
"""
424+
Add a child inset axes to this existing axes.
425+
426+
Warnings
427+
--------
428+
429+
This method is experimental as of 3.0, and the API may change.
430+
431+
Parameters
432+
----------
433+
434+
bounds : [x0, y0, width, height]
435+
Lower-left corner of inset axes, and its width and height.
436+
437+
transform : `.Transform`
438+
Defaults to `ax.transAxes`, i.e. the units of *rect* are in
439+
axes-relative coordinates.
440+
441+
zorder : number
442+
Defaults to 5 (same as `.Axes.legend`). Adjust higher or lower
443+
to change whether it is above or below data plotted on the
444+
parent axes.
445+
446+
**kwargs
447+
448+
Other *kwargs* are passed on to the `axes.Axes` child axes.
449+
450+
Returns
451+
-------
452+
453+
Axes
454+
The created `.axes.Axes` instance.
455+
456+
Examples
457+
--------
458+
459+
This example makes two inset axes, the first is in axes-relative
460+
coordinates, and the second in data-coordinates::
461+
462+
fig, ax = plt.suplots()
463+
ax.plot(range(10))
464+
axin1 = ax.inset_axes([0.8, 0.1, 0.15, 0.15])
465+
axin2 = ax.inset_axes(
466+
[5, 7, 2.3, 2.3], transform=ax.transData)
467+
468+
"""
469+
if transform is None:
470+
transform = self.transAxes
471+
label = kwargs.pop('label', 'inset_axes')
472+
473+
# This puts the rectangle into figure-relative coordinates.
474+
inset_locator = _make_inset_locator(bounds, transform, self)
475+
bb = inset_locator(None, None)
476+
477+
inset_ax = Axes(self.figure, bb.bounds, zorder=zorder,
478+
label=label, **kwargs)
479+
480+
# this locator lets the axes move if in data coordinates.
481+
# it gets called in `ax.apply_aspect() (of all places)
482+
inset_ax.set_axes_locator(inset_locator)
483+
484+
self.add_child_axes(inset_ax)
485+
486+
return inset_ax
487+
488+
def indicate_inset(self, bounds, inset_ax=None, *, transform=None,
489+
facecolor='none', edgecolor='0.5', alpha=0.5,
490+
zorder=4.99, **kwargs):
491+
"""
492+
Add an inset indicator to the axes. This is a rectangle on the plot
493+
at the position indicated by *bounds* that optionally has lines that
494+
connect the rectangle to an inset axes
495+
(`.Axes.inset_axes`).
496+
497+
Warnings
498+
--------
499+
500+
This method is experimental as of 3.0, and the API may change.
501+
502+
503+
Parameters
504+
----------
505+
506+
bounds : [x0, y0, width, height]
507+
Lower-left corner of rectangle to be marked, and its width
508+
and height.
509+
510+
inset_ax : `.Axes`
511+
An optional inset axes to draw connecting lines to. Two lines are
512+
drawn connecting the indicator box to the inset axes on corners
513+
chosen so as to not overlap with the indicator box.
514+
515+
transform : `.Transform`
516+
Transform for the rectangle co-ordinates. Defaults to
517+
`ax.transAxes`, i.e. the units of *rect* are in axes-relative
518+
coordinates.
519+
520+
facecolor : Matplotlib color
521+
Facecolor of the rectangle (default 'none').
522+
523+
edgecolor : Matplotlib color
524+
Color of the rectangle and color of the connecting lines. Default
525+
is '0.5'.
526+
527+
alpha : number
528+
Transparency of the rectangle and connector lines. Default is 0.5.
529+
530+
zorder : number
531+
Drawing order of the rectangle and connector lines. Default is 4.99
532+
(just below the default level of inset axes).
533+
534+
**kwargs
535+
Other *kwargs* are passed on to the rectangle patch.
536+
537+
Returns
538+
-------
539+
540+
rectangle_patch: `.Patches.Rectangle`
541+
Rectangle artist.
542+
543+
connector_lines: 4-tuple of `.Patches.ConnectionPatch`
544+
One for each of four connector lines. Two are set with visibility
545+
to *False*, but the user can set the visibility to True if the
546+
automatic choice is not deemed correct.
547+
548+
"""
549+
550+
# to make the axes connectors work, we need to apply the aspect to
551+
# the parent axes.
552+
self.apply_aspect()
553+
554+
if transform is None:
555+
transform = self.transData
556+
label = kwargs.pop('label', 'indicate_inset')
557+
558+
xy = (bounds[0], bounds[1])
559+
rectpatch = mpatches.Rectangle(xy, bounds[2], bounds[3],
560+
facecolor=facecolor, edgecolor=edgecolor, alpha=alpha,
561+
zorder=zorder, label=label, transform=transform, **kwargs)
562+
self.add_patch(rectpatch)
563+
564+
if inset_ax is not None:
565+
# want to connect the indicator to the rect....
566+
567+
pos = inset_ax.get_position() # this is in fig-fraction.
568+
coordsA = 'axes fraction'
569+
connects = []
570+
xr = [bounds[0], bounds[0]+bounds[2]]
571+
yr = [bounds[1], bounds[1]+bounds[3]]
572+
for xc in range(2):
573+
for yc in range(2):
574+
xyA = (xc, yc)
575+
xyB = (xr[xc], yr[yc])
576+
connects += [mpatches.ConnectionPatch(xyA, xyB,
577+
'axes fraction', 'data',
578+
axesA=inset_ax, axesB=self, arrowstyle="-",
579+
zorder=zorder, edgecolor=edgecolor, alpha=alpha)]
580+
self.add_patch(connects[-1])
581+
# decide which two of the lines to keep visible....
582+
pos = inset_ax.get_position()
583+
bboxins = pos.transformed(self.figure.transFigure)
584+
rectbbox = mtransforms.Bbox.from_bounds(
585+
*bounds).transformed(transform)
586+
if rectbbox.x0 < bboxins.x0:
587+
sig = 1
588+
else:
589+
sig = -1
590+
if sig*rectbbox.y0 < sig*bboxins.y0:
591+
connects[0].set_visible(False)
592+
connects[3].set_visible(False)
593+
else:
594+
connects[1].set_visible(False)
595+
connects[2].set_visible(False)
596+
597+
return rectpatch, connects
598+
599+
def indicate_inset_zoom(self, inset_ax, **kwargs):
600+
"""
601+
Add an inset indicator rectangle to the axes based on the axis
602+
limits for an *inset_ax* and draw connectors between *inset_ax*
603+
and the rectangle.
604+
605+
Warnings
606+
--------
607+
608+
This method is experimental as of 3.0, and the API may change.
609+
610+
Parameters
611+
----------
612+
613+
inset_ax : `.Axes`
614+
Inset axes to draw connecting lines to. Two lines are
615+
drawn connecting the indicator box to the inset axes on corners
616+
chosen so as to not overlap with the indicator box.
617+
618+
**kwargs
619+
Other *kwargs* are passed on to `.Axes.inset_rectangle`
620+
621+
Returns
622+
-------
623+
624+
rectangle_patch: `.Patches.Rectangle`
625+
Rectangle artist.
626+
627+
connector_lines: 4-tuple of `.Patches.ConnectionPatch`
628+
One for each of four connector lines. Two are set with visibility
629+
to *False*, but the user can set the visibility to True if the
630+
automatic choice is not deemed correct.
631+
632+
"""
633+
634+
xlim = inset_ax.get_xlim()
635+
ylim = inset_ax.get_ylim()
636+
rect = [xlim[0], ylim[0], xlim[1] - xlim[0], ylim[1] - ylim[0]]
637+
rectpatch, connects = self.indicate_inset(
638+
rect, inset_ax, **kwargs)
639+
640+
return rectpatch, connects
641+
393642
def text(self, x, y, s, fontdict=None, withdash=False, **kwargs):
394643
"""
395644
Add text to the axes.

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.