-
-
Notifications
You must be signed in to change notification settings - Fork 246
Expand file tree
/
Copy path_legend.py
More file actions
174 lines (151 loc) · 6.35 KB
/
Copy path_legend.py
File metadata and controls
174 lines (151 loc) · 6.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
import warnings
import numpy as np
from . import _color as mycol
def draw_legend(data, obj):
"""Adds legend code."""
texts = []
children_alignment = []
for text in obj.texts:
texts.append(f"{text.get_text()}")
children_alignment.append(f"{text.get_horizontalalignment()}")
# Get the location.
# http://matplotlib.org/api/legend_api.html
loc = obj._loc if obj._loc != 0 else _get_location_from_best(obj)
pad = 0.03
position, anchor = {
1: (None, None), # upper right
2: ([pad, 1.0 - pad], "north west"), # upper left
3: ([pad, pad], "south west"), # lower left
4: ([1.0 - pad, pad], "south east"), # lower right
5: ([1.0 - pad, 0.5], "east"), # right
6: ([3 * pad, 0.5], "west"), # center left
7: ([1.0 - 3 * pad, 0.5], "east"), # center right
8: ([0.5, 3 * pad], "south"), # lower center
9: ([0.5, 1.0 - 3 * pad], "north"), # upper center
10: ([0.5, 0.5], "center"), # center
}[loc]
# In case of given position via bbox_to_anchor parameter the center
# of legend is changed as follows:
if obj._bbox_to_anchor:
bbox_center = obj.get_bbox_to_anchor()._bbox._points[1]
position = [bbox_center[0], bbox_center[1]]
legend_style = [
# https://github.com/matplotlib/matplotlib/issues/15764#issuecomment-557823370
f"fill opacity={obj.get_frame().get_alpha()}",
"draw opacity=1",
"text opacity=1",
]
if position:
ff = data["float format"]
legend_style.append(f"at={{({position[0]:{ff}},{position[1]:{ff}})}}")
if anchor:
legend_style.append(f"anchor={anchor}")
# Get the edgecolor of the box
if obj.get_frame_on():
edgecolor = obj.get_frame().get_edgecolor()
data, frame_xcolor, _ = mycol.mpl_color2xcolor(data, edgecolor)
if frame_xcolor != "black": # black is default
legend_style.append(f"draw={frame_xcolor}")
else:
legend_style.append("draw=none")
# Get the facecolor of the box
facecolor = obj.get_frame().get_facecolor()
data, fill_xcolor, _ = mycol.mpl_color2xcolor(data, facecolor)
if fill_xcolor != "white": # white is default
legend_style.append(f"fill={fill_xcolor}")
# Get the horizontal alignment
try:
alignment = children_alignment[0]
except IndexError:
alignment = None
for child_alignment in children_alignment:
if alignment != child_alignment:
warnings.warn("Varying horizontal alignments in the legend. Using default.")
alignment = None
break
if alignment:
data["current axes"].axis_options.append(f"legend cell align={{{alignment}}}")
if obj._ncols != 1:
data["current axes"].axis_options.append(f"legend columns={obj._ncols}")
# Write styles to data
if legend_style:
j0, j1, j2 = (
("", ", ", "")
if sum(len(s) for s in legend_style) < 80
else ("\n ", ",\n ", "\n")
)
string = j1.join(legend_style)
style = f"legend style={{{j0}{string}{j2}}}"
data["current axes"].axis_options.append(style)
return data
def _get_location_from_best(obj):
# Create a renderer
from matplotlib.backends import backend_agg
renderer = backend_agg.RendererAgg(
width=obj.figure.get_figwidth(),
height=obj.figure.get_figheight(),
dpi=obj.figure.dpi,
)
# Rectangles of the legend and of the axes
# Lower left and upper right points
x0_legend, x1_legend = obj._legend_box.get_window_extent(renderer).get_points()
x0_axes, x1_axes = obj.axes.get_window_extent(renderer).get_points()
dimension_legend = x1_legend - x0_legend
dimension_axes = x1_axes - x0_axes
# To determine the actual position of the legend, check which corner
# (or center) of the legend is closest to the corresponding corner
# (or center) of the axes box.
# 1. Key points of the legend
lower_left_legend = x0_legend
lower_right_legend = np.array([x1_legend[0], x0_legend[1]], dtype=np.float_)
upper_left_legend = np.array([x0_legend[0], x1_legend[1]], dtype=np.float_)
upper_right_legend = x1_legend
center_legend = x0_legend + dimension_legend / 2.0
center_left_legend = np.array(
[x0_legend[0], x0_legend[1] + dimension_legend[1] / 2.0], dtype=np.float_
)
center_right_legend = np.array(
[x1_legend[0], x0_legend[1] + dimension_legend[1] / 2.0], dtype=np.float_
)
lower_center_legend = np.array(
[x0_legend[0] + dimension_legend[0] / 2.0, x0_legend[1]], dtype=np.float_
)
upper_center_legend = np.array(
[x0_legend[0] + dimension_legend[0] / 2.0, x1_legend[1]], dtype=np.float_
)
# 2. Key points of the axes
lower_left_axes = x0_axes
lower_right_axes = np.array([x1_axes[0], x0_axes[1]], dtype=np.float_)
upper_left_axes = np.array([x0_axes[0], x1_axes[1]], dtype=np.float_)
upper_right_axes = x1_axes
center_axes = x0_axes + dimension_axes / 2.0
center_left_axes = np.array(
[x0_axes[0], x0_axes[1] + dimension_axes[1] / 2.0], dtype=np.float_
)
center_right_axes = np.array(
[x1_axes[0], x0_axes[1] + dimension_axes[1] / 2.0], dtype=np.float_
)
lower_center_axes = np.array(
[x0_axes[0] + dimension_axes[0] / 2.0, x0_axes[1]], dtype=np.float_
)
upper_center_axes = np.array(
[x0_axes[0] + dimension_axes[0] / 2.0, x1_axes[1]], dtype=np.float_
)
# 3. Compute the distances between comparable points.
distances = {
1: upper_right_axes - upper_right_legend, # upper right
2: upper_left_axes - upper_left_legend, # upper left
3: lower_left_axes - lower_left_legend, # lower left
4: lower_right_axes - lower_right_legend, # lower right
# 5:, Not Implemented # right
6: center_left_axes - center_left_legend, # center left
7: center_right_axes - center_right_legend, # center right
8: lower_center_axes - lower_center_legend, # lower center
9: upper_center_axes - upper_center_legend, # upper center
10: center_axes - center_legend, # center
}
for k, v in distances.items():
distances[k] = np.linalg.norm(v, ord=2)
# 4. Take the shortest distance between key points as the final
# location
return min(distances, key=distances.get)