From: Bill B. <wb...@gm...> - 2006-07-27 11:31:37
|
I want to draw some labels with plot.text() and have them appear a given number of pixels (or mm, or points) to above and to the right of the data points they are describing. Is there some way to specify a screen offset from a point in graph coordinates? Like a method of the axes that converts from screen to graph coords? Using graph coordinates means that when I zoom in really close to see some details, the text labels are way out in never-never land. Thanks, --bb |
From: Bill B. <wb...@gm...> - 2006-07-27 23:57:21
|
Howdy PGM, Thanks for the reply. On 7/28/06, PGM <pgm...@gm...> wrote: > Bill, > Could you post an example of the problem ? I must admit I'm slower than usual > to react today, and I'm not sure I understand what you mean. > I tried that: > > plot([3,],[3,],'o') > gca().text(3,3,'TEXT') I'm plotting a mesh. A graph of vertexes and edges. I want to put labels on the vertexes, but I want them off to the side of the vertex a little bit so they don't overlap the vertex marker and all the incident edges so much. With the commands you have above, the lower left corner of the text and the marker overlap a little bit. plot([3,],[3,],'o') gca().text(3+little_offset,3+little_offset,'TEXT') is what I want, except the mesh has some dense parts that I need to zoom in to see clearly. But when I zoom in, that little_offset is magnified just like everything else and the text ends up being a huge distance from the vertex on the screen. > and couldn't repdouce what you were mentioning. By default, the text method of > an axes uses data coords, not graph coords. You want an offset to data > coords, right ? No, I want the offset to move things by a fixed distance in screen space. I.e. 5 pixels on the final plot no matter the zoom level. I did find one workaround just now. plot([3,],[3,],'o') gca().text(3,3,' TEXT\n') That's good enough for me for now, but it seems like it would be generally useful to be able to have a way to specify screen space offsets from graph coords when drawing. Is there not such a thing? --bb |
From: Eric F. <ef...@ha...> - 2006-07-28 00:33:33
|
Bill, You can see an example of how to do something similar by looking at the QuiverKey class in quiver.py. It is all a matter of using the transforms module. Eric Bill Baxter wrote: > I want to draw some labels with plot.text() and have them appear a > given number of pixels (or mm, or points) to above and to the right of > the data points they are describing. Is there some way to specify a > screen offset from a point in graph coordinates? Like a method of the > axes that converts from screen to graph coords? Using graph > coordinates means that when I zoom in really close to see some > details, the text labels are way out in never-never land. > > Thanks, > --bb |
From: Bill B. <wb...@gm...> - 2006-07-28 02:12:27
|
Great. Thanks. I'll take a look at that file. Is "transFigure" the one that I was calling "screen space"? --bb On 7/28/06, Eric Firing <ef...@ha...> wrote: > Bill, > > You can see an example of how to do something similar by looking at the > QuiverKey class in quiver.py. It is all a matter of using the > transforms module. > > Eric > > Bill Baxter wrote: > > I want to draw some labels with plot.text() and have them appear a > > given number of pixels (or mm, or points) to above and to the right of > > the data points they are describing. Is there some way to specify a > > screen offset from a point in graph coordinates? Like a method of the > > axes that converts from screen to graph coords? Using graph > > coordinates means that when I zoom in really close to see some > > details, the text labels are way out in never-never land. > > > > Thanks, > > --bb > |
From: Eric F. <ef...@ha...> - 2006-07-28 03:00:42
|
Bill Baxter wrote: > Great. Thanks. I'll take a look at that file. Is "transFigure" the > one that I was calling "screen space"? All of the transforms go from some particular coordinate system to what one might call "screen coordinates": position in dots (pixels), with (0,0) in the lower left corner. transFigure and transAxes go from normalized coordinates to these screen coordinates; for transFigure, (0,0) and (1,1) are the lower left and upper right corners of the figure, and for transAxes they are the corners of the axes. I think that what you want to do requires something like the mechanism in QuiverKey: a derived artist with a draw method that figures out at draw time where to put the text; I don't think there is any other way to handle zooming while keeping the screen separation from a data point fixed. This may be a common enough task to warrant adding an "offset" tuple kwarg to the Text class, and maybe a transOffset as well. Eric > > --bb > > On 7/28/06, Eric Firing <ef...@ha...> wrote: > >> Bill, >> >> You can see an example of how to do something similar by looking at the >> QuiverKey class in quiver.py. It is all a matter of using the >> transforms module. >> >> Eric >> >> Bill Baxter wrote: >> > I want to draw some labels with plot.text() and have them appear a >> > given number of pixels (or mm, or points) to above and to the right of >> > the data points they are describing. Is there some way to specify a >> > screen offset from a point in graph coordinates? Like a method of the >> > axes that converts from screen to graph coords? Using graph >> > coordinates means that when I zoom in really close to see some >> > details, the text labels are way out in never-never land. >> > >> > Thanks, >> > --bb >> |
From: John H. <jdh...@ac...> - 2006-07-28 17:47:18
|
>>>>> "Eric" == Eric Firing <ef...@ha...> writes: Eric> I think that what you want to do requires something like the Eric> mechanism in QuiverKey: a derived artist with a draw method Eric> that figures out at draw time where to put the text; I don't Eric> think there is any other way to handle zooming while keeping Eric> the screen separation from a data point fixed. You can do this using offsets -- see matplotlib.axis.XTick._get_text1. This is how tick labeling is done (a point offset from an x location in data coords and a y location in axis coords). Here is an example -- you have to copy the default data transform so that the offset doesn't affect the figure data from matplotlib.transforms import Value, translation_transform,blend_xy_sep_transform from pylab import figure, show fig = figure() ax = fig.add_subplot(111) points = 7 pad = fig.dpi*Value(points/72.0) # poor man's copy trans = blend_xy_sep_transform(ax.transData, ax.transData) # to the left and above offset = translation_transform(Value(-1)*pad, pad) trans.set_offset( (0,0), offset) ax.plot([1,2,3]) t = ax.text(1,2, 'hi', transform=trans) show() |
From: Eric F. <ef...@ha...> - 2006-07-28 19:09:22
Attachments:
transoffset.py
|
Attached is a simple example that illustrates the method. What threw me off last night is that the copy_bbox_transform() function was not doing what I expected. I don't know yet whether this is because of a bug or a misunderstanding on my part, but in any case, the example provides an alternative. (It is not valid for any bbox transform, but I think it will be fine in normal use cases.) The basic method can be used on any artist by calling set_offset on that artist's transform. Once again, John's transform module works its magic! I will make a more complete example and include it in the examples subdirectory of mpl. This is really great functionality that needs to be made more readily accessible. Eric John Hunter wrote: >>>>>>"Eric" == Eric Firing <ef...@ha...> writes: > > > Eric> I think that what you want to do requires something like the > Eric> mechanism in QuiverKey: a derived artist with a draw method > Eric> that figures out at draw time where to put the text; I don't > Eric> think there is any other way to handle zooming while keeping > Eric> the screen separation from a data point fixed. > > You can do this using offsets -- see > matplotlib.axis.XTick._get_text1. This is how tick labeling is done > (a point offset from an x location in data coords and a y location in > axis coords). Here is an example -- you have to copy the default data > transform so that the offset doesn't affect the figure data > > from matplotlib.transforms import Value, translation_transform,blend_xy_sep_transform > from pylab import figure, show > > fig = figure() > ax = fig.add_subplot(111) > points = 7 > pad = fig.dpi*Value(points/72.0) > # poor man's copy > trans = blend_xy_sep_transform(ax.transData, ax.transData) > > # to the left and above > offset = translation_transform(Value(-1)*pad, pad) > trans.set_offset( (0,0), offset) > > ax.plot([1,2,3]) > > t = ax.text(1,2, 'hi', transform=trans) > > show() > > > |
From: John H. <jdh...@ac...> - 2006-07-28 19:19:22
|
>>>>> "Eric" == Eric Firing <ef...@ha...> writes: Eric> Attached is a simple example that illustrates the method. Eric> What threw me off last night is that the Eric> copy_bbox_transform() function was not doing what I Eric> expected. I don't know yet whether this is because of a bug Eric> or a misunderstanding on my part, but in any case, the Eric> example provides an alternative. (It is not valid for any Eric> bbox transform, but I think it will be fine in normal use What I just wrote to Eric offlist is that copy_bbox_transform is a deep copy, and so all the references of the matplotlib transforms are lost (see http://matplotlib.sf.net/matplotlib.transforms.html for details on the reference semantics of mpl transforms). What we want is a shallow copy -- this is like Eric's get_bbox_transform below but also handles nonlinear transformation like log I added to svn: def copy_bbox_transform_shallow(trans): """ return a shallow copy of the bbox transform -- the Values are retained by reference but the transform is copied. This allows you to copy a transform, set a new offset to it, but not lose the value reference semantics """ inbox = trans.get_bbox1() outbox = trans.get_bbox2() typex = trans.get_funcx().get_type() typey = trans.get_funcy().get_type() newtrans = get_bbox_transform(inbox, outbox) newtrans.get_funcx().set_type(typex) newtrans.get_funcy().set_type(typey) return newtrans Eric> cases.) The basic method can be used on any artist by Eric> calling set_offset on that artist's transform. More precisely, on a shallow copy of any artist's transform. You don't want to set the offset of the artist's transform itself, since that will move the artist itself. Eric, how about adding a helper method to matplotlib.transforms call "relative_to", something like trans = relative_to(artist, pointsx, pointsy) you can get dpi from artist.figure.dpi to construct the right points->pixels offset. Then you could do trans = relative_to(line, -5, 10) ax.text(x, y, 'hi', transform=trans) This may not be the perfect API, but it should give you the idea of providing a very simple interface to users to build these transforms for offsets. JDH |
From: Eric F. <ef...@ha...> - 2006-07-28 20:35:17
|
John, Very good--I will work on it this weekend. Eric > Eric, how about adding a helper method to matplotlib.transforms call > "relative_to", something like > > trans = relative_to(artist, pointsx, pointsy) > > you can get dpi from artist.figure.dpi to construct the right > points->pixels offset. Then you could do > > trans = relative_to(line, -5, 10) > ax.text(x, y, 'hi', transform=trans) > > This may not be the perfect API, but it should give you the idea of > providing a very simple interface to users to build these transforms > for offsets. > > JDH |
From: Eric F. <ef...@ha...> - 2006-07-31 17:51:42
|
Thanks to all who contributed to this thread concerning how to draw something such as text at an offset relative to a data point, with the offset in screen coordinates so that it stays constant with zooming etc. The result in svn is a new function in transforms: def offset_copy(trans, fig=None, x=0, y=0, units='inches'): ''' Return a shallow copy of a transform with an added offset. args: trans is any transform kwargs: fig is the current figure; it can be None if units are 'dots' x, y give the offset units is 'inches', 'points' or 'dots' ''' This works for all transformations including polar; an example is given in examples/transoffset.py, also in svn. All transformations now have shallowcopy and deepcopy methods; the shallowcopy method is used in offset_copy. The deepcopy methods were there all along in _transforms.cpp, with functionality apparently partly duplicated in the copy_bbox_transform function in transforms.py. John added copy_bbox_transform_shallow to transforms.py as part of this thread. I would like to remove it, together with copy_bbox_transform, on the grounds that these functions probably have not been used by anyone except during the last few days, and their functionality is available in a much more general way via the Transformation deepcopy and shallowcopy methods. Any objections? Thanks. Eric |
From: John H. <jdh...@ac...> - 2006-07-31 18:04:49
|
>>>>> "Eric" == Eric Firing <ef...@ha...> writes: Eric> thread. I would like to remove it, together with Eric> copy_bbox_transform, on the grounds that these functions Eric> probably have not been used by anyone except during the last Eric> few days, and their functionality is available in a much Eric> more general way via the Transformation deepcopy and Eric> shallowcopy methods. Eric> Any objections? +1 Just make sure you document it in API_CHANGES and the CHANGELOG Thanks, JDH |