Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Legend auto positioning takes text objects into account #16716

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 9 commits into from
21 changes: 14 additions & 7 deletions lib/matplotlib/legend.py
Original file line number Diff line number Diff line change
Expand Up @@ -824,6 +824,8 @@ def _auto_legend_data(self):
List of `.Path` corresponding to each line.
offsets
List of (x, y) offsets of all collection.
texts
List of the axes bounding boxes of all texts.
"""
assert self.isaxes # always holds, as this is only called internally
ax = self.parent
Expand All @@ -838,7 +840,11 @@ def _auto_legend_data(self):
_, transOffset, hoffsets, _ = handle._prepare_points()
for offset in transOffset.transform(hoffsets):
offsets.append(offset)
return bboxes, lines, offsets

texts = [text.get_window_extent()
for text in ax.texts]

return bboxes, lines, offsets, texts

def get_children(self):
# docstring inherited
Expand Down Expand Up @@ -1011,7 +1017,7 @@ def _find_best_position(self, width, height, renderer, consider=None):

start_time = time.perf_counter()

bboxes, lines, offsets = self._auto_legend_data()
bboxes, lines, offsets, texts = self._auto_legend_data()

bbox = Bbox.from_bounds(0, 0, width, height)
if consider is None:
Expand All @@ -1027,11 +1033,12 @@ def _find_best_position(self, width, height, renderer, consider=None):
# XXX TODO: If markers are present, it would be good to take them
# into account when checking vertex overlaps in the next line.
badness = (sum(legendBox.count_contains(line.vertices)
for line in lines)
+ legendBox.count_contains(offsets)
+ legendBox.count_overlaps(bboxes)
+ sum(line.intersects_bbox(legendBox, filled=False)
for line in lines))
for line in lines) +
legendBox.count_contains(offsets) +
legendBox.count_overlaps(bboxes) +
legendBox.count_overlaps(texts) +
sum(line.intersects_bbox(legendBox, filled=False)
for line in lines))
if badness == 0:
return l, b
# Include the index to favor lower codes in case of a tie.
Expand Down
35 changes: 35 additions & 0 deletions lib/matplotlib/tests/test_legend.py
Original file line number Diff line number Diff line change
Expand Up @@ -570,3 +570,38 @@ def test_no_warn_big_data_when_loc_specified():
ax.plot(np.arange(5000), label=idx)
legend = ax.legend('best')
fig.draw_artist(legend) # Check that no warning is emitted.


def test_overlap_no_texts():
# test there is no error without text
fig, ax = plt.subplots()
fig.canvas.draw()
legend = ax.legend()
fig.draw_artist(legend)


def test_overlap_one_text():
# test legend doesn't overlap with one text
fig, ax = plt.subplots()
fig.canvas.draw()
ax.text(1, 0, "Text")
legend = ax.legend()
fig.draw_artist(legend)
texts = [text.get_window_extent()
for text in ax.texts]

assert legend.get_window_extent().count_overlaps(texts) == 0


def test_overlap_two_texts():
# test legend doesn't overlap with two texts
fig, ax = plt.subplots()
fig.canvas.draw()
ax.text(1, 0, "Text")
ax.text(0, 1, "Text")
legend = ax.legend()
fig.draw_artist(legend)
texts = [text.get_window_extent()
for text in ax.texts]

assert legend.get_window_extent().count_overlaps(texts) == 0