Actually, looking more closely, there is one thing that's still
bothering me: as it is now, it's impossible to have, say, 2 points
for plotted values, and 3 points for scatter plots on the same legend
(you have to give a numpoints=# command that's shared by everything in
the legend, if I'm understanding it). It'd be nice to have a
property, say, "scatterpoints" (and presumably then an associated
rcParam "legend.scatterpoints" ) that sets the number of points to use
for scatter plots. That way, I can make plots just like in the
original form, but it can also be the same number for both if so
desired. I've attached a patch based on the last one that does this,
although it probably needs to be changed to allow for an rcParam
'legend.scatterplot' (I don't really know the procedure for adding a
new rcParam).
On Mon, Oct 20, 2008 at 3:22 AM, Erik Tollerud <[EMAIL PROTECTED]> wrote:
> The current patch looks good to me... it satisfies all the use cases I
> had in mind, and I can't think of much else that would be wanted.
> Thanks!
>
> I also very much like the idea of the "sizebar," although that's
> probably a substantially larger job to implement. I may look into it
> though, time permitting...
>
> On Sat, Oct 18, 2008 at 7:04 PM, Jae-Joon Lee <[EMAIL PROTECTED]> wrote:
>>> To help clarify the original purpose of "update_from": I wrote this
>>> method when writing the original legend implementation so the legend
>>> proxy objects could easily copy their style attributes from the
>>> underlying objects they were a proxy for (so not every property is
>>> copied, eg the xdata for line objects is not copied). So the
>>> operating question should be: what properties do I need to copy to
>>> make the legend representation of the object. While you are in
>>> there, perhaps you could clarify this in the docstrings of the
>>> update_from method.
>>
>> Thanks for clarifying this, John.
>>
>> Manuel,
>> The patch looks good to me. We may submit the patch (I hope Erik is
>> okay with the current patch) and it would be great if you handle the
>> submission.
>>
>> -JJ
>>
>>
>>
>>
>>
>> On Fri, Oct 17, 2008 at 9:45 PM, Manuel Metz <[EMAIL PROTECTED]> wrote:
>>> Jae-Joon Lee wrote:
>>>> Thanks Manuel.
>>>>
>>>> Yes, we need rotation value and etc, but my point is, do we need to
>>>> update it within the update_from() method? Although my preference is
>>>> not to do it, it may not matter much as far as we state what this
>>>> method does clearly in the doc.
>>>
>>> Okay, it's probably better to create the object correctly (numsides ...)
>>> instead of copying the properties (see also JDHs mail !)
>>>
>>>> And, in your patch, I don't think updating the numsides value has any
>>>> effect as it does not recreate the paths.
>>>>
>>>> I'm attaching the revised patch. In this patch, update_from() only
>>>> update gc-related properties. And numsides, size, and rotations are
>>>> given during the object creation time.
>>>
>>> Yes, this looks better. But creating handle_sizes is a little bit too
>>> much effort. This is done internally. It will do passing a sizes list,
>>> that may or may not be shorter/longer than numpoints (see revised patch).
>>>
>>> I also changed the way the yoffsets are updated in _update_positions().
>>>
>>> One additional thing I have in mind (for a later time) is a "sizesbar"
>>> similar to a colorbar where you can read off values corresponding to
>>> marker sizes...
>>>
>>> Cheers,
>>> Manuel
>>>
>>>> Erik,
>>>> I see your points. My main concern is that the yoffsets makes the
>>>> results a bit funny when numpoints is 2. The attached patch has a
>>>> varying sizes of [0.5*(max+min), max, min]. The yoffsets are only
>>>> introduced when numpints > 2 and you can also provide it as an
>>>> optional argument.
>>>>
>>>> Regards,
>>>>
>>>> -JJ
>>>>
>>>>
>>>> On Thu, Oct 16, 2008 at 8:43 PM, Manuel Metz <[EMAIL PROTECTED]> wrote:
>>>>> Manuel Metz wrote:
>>>>>> Jae-Joon Lee wrote:
>>>>>>> Hi Manuel,
>>>>>>>
>>>>>>> I think it is a good to introduce the update_from method in Collections.
>>>>>>> But, I'm not sure if it is a good idea to also update sizes, paths and
>>>>>>> rotation (in RegularPolyCoolection). My impression is that update_from
>>>>>>> method is to update gc related attributes. For comparison,
>>>>>>> Patch.update_from() does not update the path.
>>>>>> That's exactly the point why I wasn't fully happy with the patch. The
>>>>>> path is generated by the _path_generator, so instead of copying the path
>>>>>> it seems to be better to create an instance of the corresponding class
>>>>>> (e.g. the StarPolygonCollection class, as suggested before).
>>>>>>
>>>>>> One should update the rotation attribute (!!); it's only one number. A
>>>>>> '+' marker, for example, has rotation = 0, whereas a 'x' marker has
>>>>>> rotation=pi/4. That's the only difference between those two !
>>>>>>
>>>>>>> Also, is it okay to update properties without checking its length?. It
>>>>>>> does not seem to cause any problems though.
>>>>>> It's in principal not a problem to copy the sizes attribute without
>>>>>> checking the length. If it's shorter the the number of items the sizes
>>>>>> are repeated; if it's longer it gets truncated.
>>>>>>
>>>>>> mm
>>>>>>
>>>>>>> I guess It would better to use xdata_markers than xdata in the
>>>>>>> get_handle() method. The difference is when numpoints==1. Using xdata
>>>>>>> gives two marker points.
>>>>>>>
>>>>>>> I was actually about to to commit my patch. I'll try to account your
>>>>>>> changes and post my version of patch later today.
>>>>>>>
>>>>>>> Regards,
>>>>>>>
>>>>>>> -JJ
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On Wed, Oct 15, 2008 at 4:07 PM, Manuel Metz <[EMAIL PROTECTED]> wrote:
>>>>>>>> hmm
>>>>>>>>
>>>>>>>> -------- Original Message --------
>>>>>>>> Jae-Joon Lee wrote:
>>>>>>>>>> - the parameter numpoints should be used (it's ignored right now)
>>>>>>>>>>
>>>>>>>>> Thanks Manuel. I guess we can simply reuse xdata_marker for this
>>>>>>>>> purpose.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>> - Some private variables are accessed and a new
>>>>>>>>>> RegularPolycollection is
>>>>>>>>>> created (does this work eg. with a StarPolygonCollection? I haven't
>>>>>>>>>> checked, but I don't think so !). Instead of creating a new
>>>>>>>>>> RegularPolyCollection it might be more useful to make a copy of the
>>>>>>>>>> existing object... I was thinking about a update_from() method for
>>>>>>>>>> the
>>>>>>>>>> Collection class(es) similar to update_from() for lines.
>>>>>>>>>>
>>>>>>>>> By changing "RegularPolyCoolection" to "type(handles)", it works for
>>>>>>>>> StarPolygonCollection.
>>>>>>>>>
>>>>>>>>> In Erik's current implementation, the markers in the legend have
>>>>>>>>> varying colors, sizes, and y offsets.
>>>>>>>>> The color variation seems fine. But do we need to vary the sizes and
>>>>>>>>> y-offsets? My inclination is to use a fixed size (median?) and a fixed
>>>>>>>>> y offset. How does Erik and others think?
>>>>>>>>>
>>>>>>>>> Regards,
>>>>>>>>>
>>>>>>>>> -JJ
>>>>>>>> Attached is my current version of the patch. I've moved all of the
>>>>>>>> properties-copying stuff to collections, which makes the changes
>>>>>>>> legend.py more clearer (but I'm not fully happy with the patch and
>>>>>>>> haven't commit anything yet)
>>>>>>>>
>>>>>>>> mm
>>>>>>>>
>>>>> Hi Jae-Joon,
>>>>> so here is my revised version of the patch. What do you think ?
>>>>>
>>>>> Manuel
>>>>>
>>>>>
>>>
>>>
>>> -------------------------------------------------------------------------
>>> This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
>>> Build the coolest Linux based applications with Moblin SDK & win great
>>> prizes
>>> Grand prize is a trip for two to an Open Source event anywhere in the world
>>> http://moblin-contest.org/redirect.php?banner_id=100&url=/
>>> _______________________________________________
>>> Matplotlib-devel mailing list
>>> Matplotlib-devel@lists.sourceforge.net
>>> https://lists.sourceforge.net/lists/listinfo/matplotlib-devel
>>>
>>>
>>
>> -------------------------------------------------------------------------
>> This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
>> Build the coolest Linux based applications with Moblin SDK & win great prizes
>> Grand prize is a trip for two to an Open Source event anywhere in the world
>> http://moblin-contest.org/redirect.php?banner_id=100&url=/
>> _______________________________________________
>> Matplotlib-devel mailing list
>> Matplotlib-devel@lists.sourceforge.net
>> https://lists.sourceforge.net/lists/listinfo/matplotlib-devel
>>
>
--
Erik Tollerud
Graduate Student
Center For Cosmology
Department of Physics and Astronomy
2142 Frederick Reines Hall
University of California, Irvine
Office Phone: (949)824-2587
Cell: (651)307-9409
[EMAIL PROTECTED]
Index: lib/matplotlib/legend.py
===================================================================
--- lib/matplotlib/legend.py (revision 6232)
+++ lib/matplotlib/legend.py (working copy)
@@ -83,6 +83,7 @@
def __init__(self, parent, handles, labels,
loc = None,
numpoints = None, # the number of points in the legend line
+ scatterpoints = 3,
prop = None,
pad = None, # the fractional whitespace inside the legend border
borderpad = None,
@@ -92,19 +93,21 @@
handlelen = None, # the length of the legend lines
handletextsep = None, # the space between the legend line and legend text
axespad = None, # the border between the axes and legend edge
-
- shadow = None
+ shadow = None,
+ scatter_handle_yoffsets=None,
):
"""
parent # the artist that contains the legend
handles # a list of artists (lines, patches) to add to the legend
labels # a list of strings to label the legend
loc # a location code
- numpoints = 4 # the number of points in the legend line
+ numpoints # the number of points in the legend line - legend.numpoints
+ scatterpoints # the number of points in scatter plot/Collection legend symbols
prop = FontProperties(size='smaller') # the font property
pad = 0.2 # the fractional whitespace inside the legend border
markerscale = 0.6 # the relative size of legend markers vs. original
shadow # if True, draw a shadow behind legend
+ scatter_handle_yoffsets # a list of yoffsets for scatter symbols
The following dimensions are in axes coords
labelsep = 0.005 # the vertical space between the legend entries
@@ -117,8 +120,8 @@
Artist.__init__(self)
- proplist=[numpoints, pad, borderpad, markerscale, labelsep, handlelen, handletextsep, axespad, shadow]
- propnames=['numpoints', 'pad', 'borderpad', 'markerscale', 'labelsep', 'handlelen', 'handletextsep', 'axespad', 'shadow']
+ proplist=[numpoints, scatterpoints, pad, borderpad, markerscale, labelsep, handlelen, handletextsep, axespad, shadow]
+ propnames=['numpoints','scatterpoints', 'pad', 'borderpad', 'markerscale', 'labelsep', 'handlelen', 'handletextsep', 'axespad', 'shadow']
for name, value in safezip(propnames,proplist):
if value is None:
value=rcParams["legend."+name]
@@ -127,13 +130,23 @@
warnings.warn("Use 'borderpad' instead of 'pad'.", DeprecationWarning)
# 2008/10/04
if self.numpoints <= 0:
- raise ValueError("numpoints must be >= 0; it was %d"% numpoints)
+ raise ValueError("numpoints must be > 0; it was %d"% numpoints)
+ if self.scatterpoints <= 0:
+ raise ValueError("scatterpoints must be > 0; it was %d"% scatterpoints)
if prop is None:
self.prop=FontProperties(size=rcParams["legend.fontsize"])
else:
self.prop=prop
self.fontsize = self.prop.get_size_in_points()
+ # introduce y-offset for handles of the scatter plot
+ if scatter_handle_yoffsets is None:
+ self._scatter_handle_yoffsets = np.array([4./8., 5./8., 3./8.])
+ else:
+ self._scatter_handle_yoffsets = np.asarray(scatter_handle_yoffsets)
+ reps = int(self.scatterpoints / len(self._scatter_handle_yoffsets)) + 1
+ self._scatter_handle_yoffsets = np.tile(self._scatter_handle_yoffsets, reps)[:self.scatterpoints]
+
if isinstance(parent,Axes):
self.isaxes = True
self.set_figure(parent.figure)
@@ -250,10 +263,11 @@
# centered marker proxy
for handle, label in safezip(handles, texts):
- if self.numpoints > 1:
- xdata = np.linspace(left, left + self.handlelen, self.numpoints)
+ npoints = self.scatterpoints if isinstance(handle, RegularPolyCollection) else self.numpoints
+ if npoints > 1:
+ xdata = np.linspace(left, left + self.handlelen, npoints)
xdata_marker = xdata
- elif self.numpoints == 1:
+ elif npoints == 1:
xdata = np.linspace(left, left + self.handlelen, 2)
xdata_marker = [left + 0.5*self.handlelen]
@@ -306,15 +320,26 @@
ret.append(legline)
elif isinstance(handle, RegularPolyCollection):
- if self.numpoints == 1:
- xdata = np.array([left])
- p = Rectangle(xy=(min(xdata), y-3/4*HEIGHT),
- width = self.handlelen, height=HEIGHT/2,
- )
- p.set_facecolor(handle._facecolors[0])
- if handle._edgecolors != 'none' and len(handle._edgecolors):
- p.set_edgecolor(handle._edgecolors[0])
- self._set_artist_props(p)
+ # the ydata values set here have no effects as it will
+ # be updated in the _update_positions() method.
+ ydata = (y-HEIGHT/2)*np.ones(np.asarray(xdata_marker).shape, float)
+
+ size_max, size_min = max(handle.get_sizes()),\
+ min(handle.get_sizes())
+ # we may need to scale these sizes by "markerscale"
+ # attribute. But other handle types does not seem
+ # to care about this atribute and it is currently ignored.
+ sizes = [.5*(size_max+size_min), size_max,
+ size_min]
+
+ p = type(handle)(handle.get_numsides(),
+ rotation=handle.get_rotation(),
+ sizes=sizes,
+ offsets=zip(xdata_marker,ydata),
+ transOffset=self.get_transform())
+
+ p.update_from(handle)
+ p.set_figure(self.figure)
p.set_clip_box(None)
p.set_clip_path(None)
ret.append(p)
@@ -532,6 +557,11 @@
elif isinstance(handle, Rectangle):
handle.set_y(y+1/4*h)
handle.set_height(h/2)
+ elif isinstance(handle,RegularPolyCollection):
+ offsets = handle.get_offsets()
+ offsets[:,1] = y+h*self._scatter_handle_yoffsets
+ handle.set_offsets(offsets)
+
# Set the data for the legend patch
bbox = self._get_handle_text_bbox(renderer)
Index: lib/matplotlib/collections.py
===================================================================
--- lib/matplotlib/collections.py (revision 6232)
+++ lib/matplotlib/collections.py (working copy)
@@ -148,6 +148,12 @@
result = result.inverse_transformed(transData)
return result
+ def get_window_extent(self, renderer):
+ bbox = self.get_datalim(transforms.IdentityTransform())
+ #TODO:check to ensure that this does not fail for
+ #cases other than scatter plot legend
+ return bbox
+
def _prepare_points(self):
"""Point prep for drawing and hit testing"""
@@ -417,7 +423,19 @@
else:
self._edgecolors = self.to_rgba(self._A, self._alpha)
+ def update_from(self, other):
+ 'copy properties from other to self'
+ artist.Artist.update_from(self, other)
+ self._antialiaseds = other._antialiaseds
+ self._edgecolors_original = other._edgecolors_original
+ self._edgecolors = other._edgecolors
+ self._facecolors_original = other._facecolors_original
+ self._facecolors = other._facecolors
+ self._linewidths = other._linewidths
+ self._linestyles = other._linestyles
+ self._pickradius = other._pickradius
+
# these are not available for the object inspector until after the
# class is built so we define an initial set here for the init
# function and they will be overridden after object defn
@@ -690,6 +708,7 @@
"""
Collection.__init__(self,**kwargs)
self._sizes = sizes
+ self._numsides = numsides
self._paths = [self._path_generator(numsides)]
self._rotation = rotation
self.set_transform(transforms.IdentityTransform())
@@ -706,7 +725,16 @@
def get_paths(self):
return self._paths
+ def get_numsides(self):
+ return self._numsides
+ def get_rotation(self):
+ return self._rotation
+
+ def get_sizes(self):
+ return self._sizes
+
+
class StarPolygonCollection(RegularPolyCollection):
"""
Draw a collection of regular stars with *numsides* points."""
-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
Matplotlib-devel mailing list
Matplotlib-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-devel