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 77ca117

Browse filesBrowse files
authored
Merge pull request #9078 from idahj/NEW_AnchoredDirectionArrows
New anchored direction arrows
2 parents 7f0632d + 4ba7f54 commit 77ca117
Copy full SHA for 77ca117

File tree

Expand file treeCollapse file tree

6 files changed

+344
-5
lines changed
Filter options
Expand file treeCollapse file tree

6 files changed

+344
-5
lines changed
+15Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
Add ``AnchoredDirectionArrows`` feature to mpl_toolkits
2+
--------------------------------------------------------
3+
4+
A new mpl_toolkits class
5+
:class:`~mpl_toolkits.axes_grid1.anchored_artists.AnchoredDirectionArrows`
6+
draws a pair of orthogonal arrows to inidcate directions on a 2D plot. A
7+
minimal working example takes in the transformation object for the coordinate
8+
system (typically ax.transAxes), and arrow labels. There are several optional
9+
parameters that can be used to alter layout. For example, the arrow pairs can
10+
be rotated and the color can be changed. By default the labels and arrows have
11+
the same color, but the class may also pass arguments for costumizing arrow
12+
and text layout, these are passed to :class:`matplotlib.text.TextPath` and
13+
`matplotlib.patches.FancyArrowPatch`. Location, length and width for both
14+
arrow tail and head can be adjusted, the the direction arrows and labels can
15+
have a frame. Padding and separation parameters can be adjusted.
+78Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
"""
2+
=============================
3+
Demo Anchored Direction Arrow
4+
=============================
5+
6+
"""
7+
import matplotlib.pyplot as plt
8+
import numpy as np
9+
from mpl_toolkits.axes_grid1.anchored_artists import AnchoredDirectionArrows
10+
import matplotlib.font_manager as fm
11+
12+
fig, ax = plt.subplots()
13+
ax.imshow(np.random.random((10, 10)))
14+
15+
# Simple example
16+
simple_arrow = AnchoredDirectionArrows(ax.transAxes, 'X', 'Y')
17+
ax.add_artist(simple_arrow)
18+
19+
# High contrast arrow
20+
high_contrast_part_1 = AnchoredDirectionArrows(
21+
ax.transAxes,
22+
'111', r'11$\overline{2}$',
23+
loc=1,
24+
arrow_props={'ec': 'w', 'fc': 'none', 'alpha': 1,
25+
'lw': 2}
26+
)
27+
ax.add_artist(high_contrast_part_1)
28+
29+
high_contrast_part_2 = AnchoredDirectionArrows(
30+
ax.transAxes,
31+
'111', r'11$\overline{2}$',
32+
loc=1,
33+
arrow_props={'ec': 'none', 'fc': 'k'},
34+
text_props={'ec': 'w', 'fc': 'k', 'lw': 0.4}
35+
)
36+
ax.add_artist(high_contrast_part_2)
37+
38+
# Rotated arrow
39+
fontprops = fm.FontProperties(family='serif')
40+
41+
roatated_arrow = AnchoredDirectionArrows(
42+
ax.transAxes,
43+
'30', '120',
44+
loc='center',
45+
color='w',
46+
angle=30,
47+
fontproperties=fontprops
48+
)
49+
ax.add_artist(roatated_arrow)
50+
51+
# Altering arrow directions
52+
a1 = AnchoredDirectionArrows(
53+
ax.transAxes, 'A', 'B', loc='lower center',
54+
length=-0.15,
55+
sep_x=0.03, sep_y=0.03,
56+
color='r'
57+
)
58+
ax.add_artist(a1)
59+
60+
a2 = AnchoredDirectionArrows(
61+
ax.transAxes, 'A', ' B', loc='lower left',
62+
aspect_ratio=-1,
63+
sep_x=0.01, sep_y=-0.02,
64+
color='orange'
65+
)
66+
ax.add_artist(a2)
67+
68+
69+
a3 = AnchoredDirectionArrows(
70+
ax.transAxes, ' A', 'B', loc='lower right',
71+
length=-0.15,
72+
aspect_ratio=-1,
73+
sep_y=-0.1, sep_x=0.04,
74+
color='cyan'
75+
)
76+
ax.add_artist(a3)
77+
78+
plt.show()

‎lib/mpl_toolkits/axes_grid1/anchored_artists.py

Copy file name to clipboardExpand all lines: lib/mpl_toolkits/axes_grid1/anchored_artists.py
+224-4Lines changed: 224 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import six
22

3-
from matplotlib import docstring
3+
from matplotlib import docstring, transforms
44
from matplotlib.offsetbox import (AnchoredOffsetbox, AuxTransformBox,
55
DrawingArea, TextArea, VPacker)
6-
from matplotlib.patches import Rectangle, Ellipse
7-
6+
from matplotlib.patches import (Rectangle, Ellipse, ArrowStyle,
7+
FancyArrowPatch, PathPatch)
8+
from matplotlib.text import TextPath
89

910
__all__ = ['AnchoredDrawingArea', 'AnchoredAuxTransformBox',
10-
'AnchoredEllipse', 'AnchoredSizeBar']
11+
'AnchoredEllipse', 'AnchoredSizeBar', 'AnchoredDirectionArrows']
1112

1213

1314
class AnchoredDrawingArea(AnchoredOffsetbox):
@@ -372,3 +373,222 @@ def __init__(self, transform, size, label, loc,
372373
child=self._box,
373374
prop=fontproperties,
374375
frameon=frameon, **kwargs)
376+
377+
378+
class AnchoredDirectionArrows(AnchoredOffsetbox):
379+
@docstring.dedent
380+
def __init__(self, transform, label_x, label_y, length=0.15,
381+
fontsize=0.08, loc=2, angle=0, aspect_ratio=1, pad=0.4,
382+
borderpad=0.4, frameon=False, color='w', alpha=1,
383+
sep_x=0.01, sep_y=0, fontproperties=None, back_length=0.15,
384+
head_width=10, head_length=15, tail_width=2,
385+
text_props=None, arrow_props=None,
386+
**kwargs):
387+
"""
388+
Draw two perpendicular arrows to indicate directions.
389+
390+
Parameters
391+
----------
392+
transform : `matplotlib.transforms.Transform`
393+
The transformation object for the coordinate system in use, i.e.,
394+
:attr:`matplotlib.axes.Axes.transAxes`.
395+
396+
label_x, label_y : string
397+
Label text for the x and y arrows
398+
399+
length : int or float, optional
400+
Length of the arrow, given in coordinates of
401+
*transform*.
402+
Defaults to 0.15.
403+
404+
fontsize : int, optional
405+
Size of label strings, given in coordinates of *transform*.
406+
Defaults to 0.08.
407+
408+
loc : int, optional
409+
Location of the direction arrows. Valid location codes are::
410+
411+
'upper right' : 1,
412+
'upper left' : 2,
413+
'lower left' : 3,
414+
'lower right' : 4,
415+
'right' : 5,
416+
'center left' : 6,
417+
'center right' : 7,
418+
'lower center' : 8,
419+
'upper center' : 9,
420+
'center' : 10
421+
422+
Defaults to 2.
423+
424+
angle : int or float, optional
425+
The angle of the arrows in degrees.
426+
Defaults to 0.
427+
428+
aspect_ratio : int or float, optional
429+
The ratio of the length of arrow_x and arrow_y.
430+
Negative numbers can be used to change the direction.
431+
Defaults to 1.
432+
433+
pad : int or float, optional
434+
Padding around the labels and arrows, in fraction of the font
435+
size. Defaults to 0.4.
436+
437+
borderpad : int or float, optional
438+
Border padding, in fraction of the font size.
439+
Defaults to 0.4.
440+
441+
frameon : bool, optional
442+
If True, draw a box around the arrows and labels.
443+
Defaults to False.
444+
445+
color : str, optional
446+
Color for the arrows and labels.
447+
Defaults to white.
448+
449+
alpha : int or float, optional
450+
Alpha values of the arrows and labels
451+
Defaults to 1.
452+
453+
sep_x, sep_y : int or float, optional
454+
Separation between the arrows and labels in coordinates of
455+
*transform*. Defaults to 0.01 and 0.
456+
457+
fontproperties : `matplotlib.font_manager.FontProperties`, optional
458+
Font properties for the label text.
459+
460+
back_length : float, optional
461+
Fraction of the arrow behind the arrow crossing.
462+
Defaults to 0.15.
463+
464+
head_width : int or float, optional
465+
Width of arrow head, sent to ArrowStyle.
466+
Defaults to 10.
467+
468+
head_length : int or float, optional
469+
Length of arrow head, sent to ArrowStyle.
470+
Defaults to 15.
471+
472+
tail_width : int or float, optional
473+
Width of arrow tail, sent to ArrowStyle.
474+
Defaults to 2.
475+
476+
text_props, arrow_props : dict
477+
Properties of the text and arrows, passed to
478+
:class:`matplotlib.text.TextPath` and
479+
`matplotlib.patches.FancyArrowPatch`
480+
481+
**kwargs :
482+
Keyworded arguments to pass to
483+
:class:`matplotlib.offsetbox.AnchoredOffsetbox`.
484+
485+
Attributes
486+
----------
487+
arrow_x, arrow_y : `matplotlib.patches.FancyArrowPatch`
488+
Arrow x and y
489+
490+
text_path_x, text_path_y : `matplotlib.text.TextPath`
491+
Path for arrow labels
492+
493+
p_x, p_y : `matplotlib.patches.PathPatch`
494+
Patch for arrow labels
495+
496+
box : `matplotlib.offsetbox.AuxTransformBox`
497+
Container for the arrows and labels.
498+
499+
Notes
500+
-----
501+
If *prop* is passed as a keyword argument, but *fontproperties* is
502+
not, then *prop* is be assumed to be the intended *fontproperties*.
503+
Using both *prop* and *fontproperties* is not supported.
504+
505+
Examples
506+
--------
507+
>>> import matplotlib.pyplot as plt
508+
>>> import numpy as np
509+
>>> from mpl_toolkits.axes_grid1.anchored_artists import \
510+
... AnchoredDirectionArrows
511+
>>> fig, ax = plt.subplots()
512+
>>> ax.imshow(np.random.random((10,10)))
513+
>>> arrows = AnchoredDirectionArrows(ax.transAxes, '111', '110')
514+
>>> ax.add_artist(arrows)
515+
>>> fig.show()
516+
517+
Using several of the optional parameters, creating downward pointing
518+
arrow and high contrast text labels.
519+
520+
>>> import matplotlib.font_manager as fm
521+
>>> fontprops = fm.FontProperties(family='monospace')
522+
>>> arrows = AnchoredDirectionArrows(ax.transAxes, 'East', 'South',
523+
... loc='lower left', color='k', aspect_ratio=-1, sep_x=0.02,
524+
... sep_y=-0.01, text_props={'ec':'w', 'fc':'k'},
525+
... fontproperties=fontprops)
526+
"""
527+
if arrow_props is None:
528+
arrow_props = {}
529+
530+
if text_props is None:
531+
text_props = {}
532+
533+
arrowstyle = ArrowStyle("Simple",
534+
head_width=head_width,
535+
head_length=head_length,
536+
tail_width=tail_width)
537+
538+
if fontproperties is None and 'prop' in kwargs:
539+
fontproperties = kwargs.pop('prop')
540+
541+
if 'color' not in arrow_props:
542+
arrow_props['color'] = color
543+
544+
if 'alpha' not in arrow_props:
545+
arrow_props['alpha'] = alpha
546+
547+
if 'color' not in text_props:
548+
text_props['color'] = color
549+
550+
if 'alpha' not in text_props:
551+
text_props['alpha'] = alpha
552+
553+
t_start = transform
554+
t_end = t_start + transforms.Affine2D().rotate_deg(angle)
555+
556+
self.box = AuxTransformBox(t_end)
557+
558+
length_x = length
559+
length_y = length*aspect_ratio
560+
561+
self.arrow_x = FancyArrowPatch(
562+
(0, back_length*length_y),
563+
(length_x, back_length*length_y),
564+
arrowstyle=arrowstyle,
565+
shrinkA=0.0,
566+
shrinkB=0.0,
567+
**arrow_props)
568+
569+
self.arrow_y = FancyArrowPatch(
570+
(back_length*length_x, 0),
571+
(back_length*length_x, length_y),
572+
arrowstyle=arrowstyle,
573+
shrinkA=0.0,
574+
shrinkB=0.0,
575+
**arrow_props)
576+
577+
self.box.add_artist(self.arrow_x)
578+
self.box.add_artist(self.arrow_y)
579+
580+
text_path_x = TextPath((
581+
length_x+sep_x, back_length*length_y+sep_y), label_x,
582+
size=fontsize, prop=fontproperties)
583+
self.p_x = PathPatch(text_path_x, transform=t_start, **text_props)
584+
self.box.add_artist(self.p_x)
585+
586+
text_path_y = TextPath((
587+
length_x*back_length+sep_x, length_y*(1-back_length)+sep_y),
588+
label_y, size=fontsize, prop=fontproperties)
589+
self.p_y = PathPatch(text_path_y, **text_props)
590+
self.box.add_artist(self.p_y)
591+
592+
AnchoredOffsetbox.__init__(self, loc, pad=pad, borderpad=borderpad,
593+
child=self.box,
594+
frameon=frameon, **kwargs)
Loading

‎lib/mpl_toolkits/tests/test_axes_grid1.py

Copy file name to clipboardExpand all lines: lib/mpl_toolkits/tests/test_axes_grid1.py
+27-1Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
inset_axes,
1414
BboxConnectorPatch
1515
)
16-
from mpl_toolkits.axes_grid1.anchored_artists import AnchoredSizeBar
16+
from mpl_toolkits.axes_grid1.anchored_artists import (
17+
AnchoredSizeBar,
18+
AnchoredDirectionArrows)
1719

1820
from matplotlib.colors import LogNorm
1921
from matplotlib.transforms import Bbox, TransformedBbox, \
@@ -322,3 +324,27 @@ def test_zooming_with_inverted_axes():
322324
ax.axis([3, 1, 3, 1])
323325
inset_ax = zoomed_inset_axes(ax, zoom=2.5, loc=4)
324326
inset_ax.axis([1.4, 1.1, 1.4, 1.1])
327+
328+
329+
@image_comparison(baseline_images=['anchored_direction_arrows'],
330+
extensions=['png'])
331+
def test_anchored_direction_arrows():
332+
fig, ax = plt.subplots()
333+
ax.imshow(np.zeros((10, 10)))
334+
335+
simple_arrow = AnchoredDirectionArrows(ax.transAxes, 'X', 'Y')
336+
ax.add_artist(simple_arrow)
337+
338+
339+
@image_comparison(baseline_images=['anchored_direction_arrows_many_args'],
340+
extensions=['png'])
341+
def test_anchored_direction_arrows_many_args():
342+
fig, ax = plt.subplots()
343+
ax.imshow(np.ones((10, 10)))
344+
345+
direction_arrows = AnchoredDirectionArrows(
346+
ax.transAxes, 'A', 'B', loc='upper right', color='red',
347+
aspect_ratio=-0.5, pad=0.6, borderpad=2, frameon=True, alpha=0.7,
348+
sep_x=-0.06, sep_y=-0.08, back_length=0.1, head_width=9,
349+
head_length=10, tail_width=5)
350+
ax.add_artist(direction_arrows)

0 commit comments

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