You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The problem is that the best placement only takes axes.lines into account. To get to the actual code, look at legend.py, and in particular the _find_best_position and _auto_legend_data methods.
@pelson Thanks for that info. Given your comment, would it be too presumptuous to say we'd also see this behaviour for text and arrows under the legend? Perhaps _find_best_position and _auto_legend_data should look more broadly at the axes artists?
So I'm trying to fix this just for the scatter case, so I can see what's going on. I'm having problems getting the bounding box for each of the points. This is my loop:
There are 3 PathCollections, presumably one for each point in the scatter plot
The way this code has been written, yes, there should be 3 collections. In most cases though, one would plot the 3 points in one PathCollection i.e. plt.scatter(range(3), range(3)). It may be that the bounding box for a collection cannot be broken down for each of its components.
My knowledge of Collections is very limited, but you may be interested in the _offsets and _transOffset attributes on a Collection...
Aha! Thank you. So a PathCollection is actually just a single path, together with a bunch of offsets. Then that's enough information to define the whole collection. So it should be easy to get the bounding box for each component in the collection: Work out the bounding box of the path. Then, for each offset, transform that bounding box by the offset amount.
So it should be easy to get the bounding box for each component in the collection: Work out the bounding box of the path. Then, for each offset, transform that bounding box by the offset amount.
Hey, I took a crack at solving this bug along with some help from @abalmeida7 and @ShaiMitchell
I used the _offsets from the collections that @dmcdougall presented above, and just added one to the badness score if the point intersected with the legend box.
@@ -716,6 +716,7 @@ class Legend(Artist):
ax = self.parent
bboxes = []
lines = []
+ points = []
for handle in ax.lines:
assert isinstance(handle, Line2D)
@@ -734,12 +735,15 @@ class Legend(Artist):
transform = handle.get_transform()
bboxes.append(handle.get_path().get_extents(transform))
+ for handle in ax.collections:+ points.append(handle._offsets)+
try:
vertices = np.concatenate([l.vertices for l in lines])
except ValueError:
vertices = np.array([])
- return [vertices, bboxes, lines]+ return [vertices, bboxes, lines, points]
def draw_frame(self, b):
'b is a boolean. Set draw frame to b'
@@ -896,7 +900,7 @@ class Legend(Artist):
# should always hold because function is only called internally
assert self.isaxes
- verts, bboxes, lines = self._auto_legend_data()+ verts, bboxes, lines, points = self._auto_legend_data()
bbox = Bbox.from_bounds(0, 0, width, height)
if consider is None:
@@ -927,12 +927 @@ class Legend(Artist):
if line.intersects_bbox(legendBox):
badness += 1
+ for point in points:+ val = self.parent.transData.transform(np.vstack(point[0]).T)+ if legendBox.count_contains(val):+ badness += 1
ox, oy = l, b
if badness == 0:
return ox, oy
candidates.append((badness, (l, b)))
Activity
pelson commentedon Sep 13, 2012
Fantastic simple, reproducible case.
The problem is that the best placement only takes
axes.linesinto account. To get to the actual code, look atlegend.py, and in particular the_find_best_positionand_auto_legend_datamethods.dmcdougall commentedon Sep 13, 2012
@pelson Thanks for that info. Given your comment, would it be too presumptuous to say we'd also see this behaviour for text and arrows under the legend? Perhaps
_find_best_positionand_auto_legend_datashould look more broadly at the axes artists?Edit: Grammar.
pelson commentedon Sep 13, 2012
In my eyes, that would make best better...
dmcdougall commentedon Sep 18, 2012
So I'm trying to fix this just for the
scattercase, so I can see what's going on. I'm having problems getting the bounding box for each of the points. This is my loop:There are 3
PathCollections, presumably one for each point in thescatterplot, but I'm getting rubbish bounding boxes:Any idea what's going on? Did I do something silly?
pelson commentedon Sep 18, 2012
The way this code has been written, yes, there should be 3 collections. In most cases though, one would plot the 3 points in one PathCollection i.e.
plt.scatter(range(3), range(3)). It may be that the bounding box for a collection cannot be broken down for each of its components.My knowledge of Collections is very limited, but you may be interested in the
_offsetsand_transOffsetattributes on a Collection...dmcdougall commentedon Sep 18, 2012
Aha! Thank you. So a
PathCollectionis actually just a single path, together with a bunch of offsets. Then that's enough information to define the whole collection. So it should be easy to get the bounding box for each component in the collection: Work out the bounding box of the path. Then, for each offset, transform that bounding box by the offset amount.pelson commentedon Sep 19, 2012
Easy when you put it like that :-)
wmak commentedon Mar 1, 2014
Hey, I took a crack at solving this bug along with some help from @abalmeida7 and @ShaiMitchell
I used the _offsets from the collections that @dmcdougall presented above, and just added one to the badness score if the point intersected with the legend box.
tacaswell commentedon Mar 3, 2014
@wmak Could you put that in a PR? It make reviewing proposed changes much easier (and lets the CI server have at it).
fix for matplotlib#1235
Fixes matplotlib#1235
Merge pull request #6110 from nansonzheng/1235-fix-proposal
Merge pull request #6110 from nansonzheng/1235-fix-proposal