Hi All, I am trying to visualize some more text in an already rather crowded 2D plot. As this new information I want to display is optional (it depends on a use choice) but possibly very useful as information, I would like to add it as text inside my plot, and possibly to add it in a "smart" way (i.e., with minimum or no overlapping between this new text and the rest of the plot, like legend does with loc=0). Now, I have tried to steal the code from legend.py but I am not getting anywhere, also because I am mixing display coordinates, data coordinates and wx.DC text extents and I am getting crazy :-( This is the code I have so far: def PositionAnnotation(self, dc, label, dash, x, y): """ Position a matplotlib text instance without overlapping with the rest of the data. :param `dc`: an instance of wx.ClientDC; :param `label`: the label to display (2 lines of text); :param `dash`: the actual TextWithDash matplotlib class to position :param `x`: a list of possible x location (this is fixed) :param `y`: a list of possible y location (this is fixed) """ # Stolen from legend.py lines = [] # Here I work with display coordinates (!) for handle in self.leftaxis.lines: path = handle.get_path() trans = handle.get_transform() tpath = trans.transform_path(path) lines.append(tpath) # End of stolen from legend.py # Here I work with screen/character coordinates (!) width, height, dummy = dc.GetMultiLineTextExtent(label) candidates = [] for l, b in zip(x, y): # And here I work with data coordinates (!) dashBox = Bbox.from_bounds(l, b, width+5, height+5) badness = 0 for line in lines: if line.intersects_bbox(dashBox): badness += 1 ox, oy = l, b if badness == 0: return ox, oy candidates.append((badness, (l, b))) # Stolen from legend.py # rather than use min() or list.sort(), do this so that we are assured # that in the case of two equal badnesses, the one first considered is # returned. # NOTE: list.sort() is stable.But leave as it is for now. -JJL minCandidate = candidates[0] for candidate in candidates: if candidate[0] < minCandidate[0]: minCandidate = candidate ox, oy = minCandidate[1] return ox, oy This code is not doing anything useful as I always get a badness of 0, although I can see that the new text overlaps quite a lot of other artists. Does anyone have some suggestion on how to improve the code? Thank you in advance. Andrea. "Imagination Is The Only Weapon In The War Against Reality." http://xoomer.alice.it/infinity77/ http://thedoomedcity.blogspot.com/
On Sat, Feb 27, 2010 at 12:29 PM, Andrea Gavana <and...@gm...> wrote: > This code is not doing anything useful as I always get a badness of 0, > although I can see that the new text overlaps quite a lot of other > artists. > > Does anyone have some suggestion on how to improve the code? > A snippet of code seldom helps. Is your x,y input in display coordinates? Ideally, this must be checked in the drawing time (as in the legend). With your current approach, there could be lots of possible reason that this does not work. Regards, -JJ
On 28 February 2010 01:18, Jae-Joon Lee wrote: > On Sat, Feb 27, 2010 at 12:29 PM, Andrea Gavana <and...@gm...> wrote: >> This code is not doing anything useful as I always get a badness of 0, >> although I can see that the new text overlaps quite a lot of other >> artists. >> >> Does anyone have some suggestion on how to improve the code? >> > > A snippet of code seldom helps. > Is your x,y input in display coordinates? No, data values. > Ideally, this must be checked in the drawing time (as in the legend). This is what I am trying to do, but I messed up screen/data/canvas coordinates and I can't get my head around it :-( Andrea. "Imagination Is The Only Weapon In The War Against Reality." http://xoomer.alice.it/infinity77/ http://thedoomedcity.blogspot.com/
If I read your correctly, for l, b in zip(x, y): # And here I work with data coordinates (!) dashBox = Bbox.from_bounds(l, b, width+5, height+5) badness = 0 for line in lines: if line.intersects_bbox(dashBox): badness += 1 x, y (therefore l, b) in data coordinate. width, height?? this seems to be some wx specific coordinate, i have no idea. lines (therefore line) in display coordinate. converting x,y to display coordinate should straight forward. But I'm not sure what kind of coordinate width and height has. Is it a method of some class derived from matplotlib's Text?? If then, the extent of the text can be measured using the get_window_extent method. This requires a renderer instance, which should be known if the method is called during the drawing time. Again, post a complete but simple(!) code. -JJ On Sat, Feb 27, 2010 at 8:59 PM, Andrea Gavana <and...@gm...> wrote: > On 28 February 2010 01:18, Jae-Joon Lee wrote: >> On Sat, Feb 27, 2010 at 12:29 PM, Andrea Gavana <and...@gm...> wrote: >>> This code is not doing anything useful as I always get a badness of 0, >>> although I can see that the new text overlaps quite a lot of other >>> artists. >>> >>> Does anyone have some suggestion on how to improve the code? >>> >> >> A snippet of code seldom helps. >> Is your x,y input in display coordinates? > > No, data values. > >> Ideally, this must be checked in the drawing time (as in the legend). > > This is what I am trying to do, but I messed up screen/data/canvas > coordinates and I can't get my head around it :-( > > Andrea. > > "Imagination Is The Only Weapon In The War Against Reality." > http://xoomer.alice.it/infinity77/ > http://thedoomedcity.blogspot.com/ >
Hi Jae-Joon & All, On 28 February 2010 03:09, Jae-Joon Lee wrote: > If I read your correctly, > > for l, b in zip(x, y): > > # And here I work with data coordinates (!) > > dashBox = Bbox.from_bounds(l, b, width+5, height+5) > badness = 0 > for line in lines: > if line.intersects_bbox(dashBox): > badness += 1 > > x, y (therefore l, b) in data coordinate. > width, height?? this seems to be some wx specific coordinate, i have no idea. > lines (therefore line) in display coordinate. > > converting x,y to display coordinate should straight forward. But I'm > not sure what kind of coordinate width and height has. Is it a method > of some class derived from matplotlib's Text?? If then, the extent of > the text can be measured using the get_window_extent method. This > requires a renderer instance, which should be known if the method is > called during the drawing time. > > Again, post a complete but simple(!) code. OK, I think I got a complete code. Not super-simple, but simple enough I believe. After you run it you'll see a bunch of points and lines with some text. If you left-click inside the axis this will start the calculations for the "optimal positioning". There are a couple of problems: 1) The code I have looks only for optimal positioning with respect to lines in the plot, it doesn't include texts (I don't know how to do it); You'll see what I mean if you run the code, the "optimally" positioned texts overlap with other text in the plot, and with themselves too (i.e., one "optimally" positioned text overlap with another "optimally" positioned text); 2) The code as it stands it's veeeeery slow. On my (relatively fast) computer, it takes almost 6 seconds to "optimally" position 14 labels. In order to run the code, you'll also need the "lines.txt" file, which contains the main lines coordinates. Sorry about this extra file but I wanted it to be as close as possible to my use-case. Thank you in advance for your suggestions. Andrea. "Imagination Is The Only Weapon In The War Against Reality." http://xoomer.alice.it/infinity77/ http://thedoomedcity.blogspot.com/
I tried to put some code to check overlapping texts. The code runs, but I'm not sure if the code is correct. Texts still overlap but I haven't checked if this is due to the incorrect code or the input. But I believe it gives you enough idea how to fix things. You have several hundreds of candidate points for a few cases. No doubt it takes so long. Regards, -JJ On Mon, Mar 1, 2010 at 6:07 AM, Andrea Gavana <and...@gm...> wrote: > Hi Jae-Joon & All, > > On 28 February 2010 03:09, Jae-Joon Lee wrote: >> If I read your correctly, >> >> for l, b in zip(x, y): >> >> # And here I work with data coordinates (!) >> >> dashBox = Bbox.from_bounds(l, b, width+5, height+5) >> badness = 0 >> for line in lines: >> if line.intersects_bbox(dashBox): >> badness += 1 >> >> x, y (therefore l, b) in data coordinate. >> width, height?? this seems to be some wx specific coordinate, i have no idea. >> lines (therefore line) in display coordinate. >> >> converting x,y to display coordinate should straight forward. But I'm >> not sure what kind of coordinate width and height has. Is it a method >> of some class derived from matplotlib's Text?? If then, the extent of >> the text can be measured using the get_window_extent method. This >> requires a renderer instance, which should be known if the method is >> called during the drawing time. >> >> Again, post a complete but simple(!) code. > > OK, I think I got a complete code. Not super-simple, but simple enough > I believe. After you run it you'll see a bunch of points and lines > with some text. If you left-click inside the axis this will start the > calculations for the "optimal positioning". There are a couple of > problems: > > 1) The code I have looks only for optimal positioning with respect to > lines in the plot, it doesn't include texts (I don't know how to do > it); You'll see what I mean if you run the code, the "optimally" > positioned texts overlap with other text in the plot, and with > themselves too (i.e., one "optimally" positioned text overlap with > another "optimally" positioned text); > 2) The code as it stands it's veeeeery slow. On my (relatively fast) > computer, it takes almost 6 seconds to "optimally" position 14 labels. > > In order to run the code, you'll also need the "lines.txt" file, which > contains the main lines coordinates. Sorry about this extra file but I > wanted it to be as close as possible to my use-case. > > Thank you in advance for your suggestions. > > Andrea. > > "Imagination Is The Only Weapon In The War Against Reality." > http://xoomer.alice.it/infinity77/ > http://thedoomedcity.blogspot.com/ >