Question

I am working with matplotlib in Python 3.3. I have an animated 2d and 3d window, on which I draw points. These points represent objects, but it is not certain that they are actually there. So I want to draw a circle around these points to display the uncertainty. This uncertainty is variating, so bottomline: I want to draw multiple (might be 0, might be 100) circkles in a 2D animation with a variable center and a variable radius.

I tried scatter. Scatter looks very promising, until I resize the screen: the size of the scatter is in pixels and not related to the size of the axes. Any idea how I can do this?

I have tried the following scatter code:

#In the init
self.circles = self.ax.scatter([],[],[],c = 'b',alpha=0.6)
#In the animate function
self.circles.set_offsets([[10],[15]])    
self.circles._sizes = [2000]

The centerpoint is correct, at x = 10 and y = 15. But the size is the size in pixels and not related to the axes. In this case I expected the circle to hit x = 10 and y = 20015, for example. But it does not.

This becomes a problem when I resize the window.

Was it helpful?

Solution

Drawing Collections is faster, so I modified the draw() function of the PathCollection object returned by scatter(). The new draw() uses transData to calculate the scale of every circle, it uses the size of every circle as diameter.

import pylab as pl
import numpy as np
from matplotlib.collections import Collection
from matplotlib import transforms
import matplotlib.animation as animation

fig = pl.figure()

N = 40
x, y = np.random.rand(2, N)
color = np.random.rand(N)
r = np.random.rand(N) * 0.05 + 0.05

c = pl.scatter(x, y, s=r, c=color, alpha=0.5, animated=True)
pl.gca().set_aspect("equal")

def draw(self, renderer):
    if self._sizes is not None:
        m = self.axes.transData.get_matrix()
        self._transforms = [
            transforms.Affine2D().scale(m[0, 0]*x, m[1, 1]*x)
            for x in self._sizes]
    return Collection.draw(self, renderer)

c.draw = lambda renderer:draw(c, renderer)

def update_loc(n):
    global x2, y2, x, y
    n = n % 50
    if n == 0:
        x2, y2 = np.random.rand(2, N)
    x += (x2 - x) * 0.1
    y += (y2 - y) * 0.1
    c.set_offsets(np.c_[x, y])
    return c,

ani = animation.FuncAnimation(fig, update_loc, 2500, interval=50, blit=True)

pl.show()

Here is a frame of the animation:

enter image description here

OTHER TIPS

Tcaswell wrote the answer in the comments above, so if you want to upvote this, upvote him too.

The fix was to use a circle: http://matplotlib.org/api/artist_api.html#matplotlib.patches.Circle. These work very different from the axis.plot and axis.scatter in the sens that to add them you have to do the following:

i = pyplot.Circle((item["x"],item["y"]), radius=item["inaccuracy"], fc='None'))
self.ax.add_patch(i)

And to remove them:

i.remove()

tcaswell also suggested that instead of removing every circle before drawing a new set of circles you can also just move and resize i. Which is indeed very possible: just change the radius and xy propertes of i. I have not tested this, but it is not hard to imagine that this will be faster then removing all existing circles and adding all new ones.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top