Skip to content

Commit 4ba7f54

Browse files
committed
Squashed commit of the following:
commit a05a486 Merge: 6ec4f8f e751286 Author: Ida Hjorth <idahjorth@gmail.com> Date: Sun Apr 29 19:07:32 2018 +0200 Merge pull request matplotlib#3 from magnunor/idahj_anchored_directionarrows Idahj anchored directionarrows commit e751286 Merge: 3ba79fb 6ec4f8f Author: Magnus Nord <magnunor@gmail.com> Date: Sun Apr 29 09:00:34 2018 +0200 Merge branch 'NEW_AnchoredDirectionArrows' of github.com:idahj/matplotlib into idahj_anchored_directionarrows commit 6ec4f8f Author: Ida <idahjorth@gmail.com> Date: Fri Aug 25 13:14:26 2017 +0100 AnchoredDirectionArrows: Docstring, what's new commit 19f4a38 Author: Ida <idahjorth@gmail.com> Date: Thu Aug 24 14:27:23 2017 +0100 AnchoredDirectionArrows: Demo docstring commit d024684 Author: Ida <idahjorth@gmail.com> Date: Thu Aug 24 14:09:52 2017 +0100 AnchoredDirectionArrows: Fix error commit 27b6bc5 Author: Ida <idahjorth@gmail.com> Date: Thu Aug 24 13:58:58 2017 +0100 AnchoredDirectionArrows: make variable not private commit c0762d2 Author: Ida <idahjorth@gmail.com> Date: Thu Aug 24 13:02:38 2017 +0100 AnchoredDirectionArrows: Add docstring in demo commit 88d04d0 Author: Ida <idahjorth@gmail.com> Date: Wed Aug 23 14:19:06 2017 +0100 AnchoredDirectionArrows: docstring, what's new commit efcca59 Author: Magnus Nord <Magnus.Nord@glasgow.ac.uk> Date: Wed Aug 23 13:31:32 2017 +0100 AnchoredDirectionArrows: add test which uses many of the arguments commit e200c44 Author: Magnus Nord <Magnus.Nord@glasgow.ac.uk> Date: Wed Aug 23 12:06:05 2017 +0100 AnchoredDirectionArrows: minor docstring changes commit b6b0ea2 Author: Ida <idahjorth@gmail.com> Date: Tue Aug 22 13:28:56 2017 +0100 AnchoredDirectionArrows: PEP8 commit 5fde65a Author: Ida <idahjorth@gmail.com> Date: Tue Aug 22 13:23:25 2017 +0100 AnchoredDirectionArrows: Add demo commit 23f7d5b Author: Ida <idahjorth@gmail.com> Date: Tue Aug 22 11:16:41 2017 +0100 AnchoredDirectionArrows: Add working unit test commit 459ae07 Author: Ida <idahjorth@gmail.com> Date: Tue Aug 22 10:37:44 2017 +0100 AnchoredDirectionArrow: first attempt unit test Currently not working commit f4ec0e2 Author: Ida <idahjorth@gmail.com> Date: Wed Aug 16 19:31:05 2017 +0100 Cleanup, bug fix, begin merge overlapping arrows commit 75e52b0 Author: Magnus Nord <magnunor@gmail.com> Date: Mon Aug 14 23:05:30 2017 +0100 AnchoredDirectionArrows: PEP8 fixes, and improve docstring commit 4fff775 Author: Ida <idahjorth@gmail.com> Date: Mon Aug 14 22:04:22 2017 +0100 Add AnchoredDirectionArrows class in axes_grid1
1 parent 7f0632d commit 4ba7f54

File tree

6 files changed

+344
-5
lines changed

6 files changed

+344
-5
lines changed
Lines 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.
Lines 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

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

lib/mpl_toolkits/tests/test_axes_grid1.py

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