Hi all,
I will explain what I’m trying to achieve first, then the approaches I’ve attempted so far, with results.
I have data on a 2D grid which I want to present as an image — a la pyplot.imshow() — except that the grid is hexagonal, not rectangular. The grid can be represented in multiple ways, I use a regular 2D array with the convention that the lower left hex is (0,0), x increases to the right (crossing vertical hex boundaries) and y increases upwards slanting to the right.
Ideally, I’d also be able to have a colorbar.
I’m giving my routines three input vectors: x, y and c. X and y are initially integers, then transformed to centerpoint coordinates using
x, y = x+.5*y, y*np.cos(np.pi/6)
while c is used to look up values in a colormap.
I first tried to adapt ‘scatter_demo2.py’ to my needs. Unfortunately, the pyplot.scatter routine fails when given ‘c’ or ’s’ keywords, as has been reported elsewhere by somebody else:
http://stackoverflow.com/questions/20524888/attributeerror-numpy-ndarray-object-has-no-attribute-get-matrix
I’ve dug around in the code for a bit without finding out how this arises. It seems to me it has to do with how transforms are being handed around and at what point their representation is changed from objects to pure matrices. This backend appears to expect to see objects only, but is handed matrices instead. I’ve hacked my way around that one in backends/backend_macosx.py by changing lines around 79-80 from
master_transform = master_transform.get_matrix()
all_transforms = [t.get_matrix() for t in all_transforms]
to
try:
master_transform = master_transform.get_matrix()
except AttributeError: pass
try:
all_transforms = [t.get_matrix() for t in all_transforms]
except AttributeError: pass
(which is a dirty hack, but I don’t know how to do it right)
Now I can run the scatter_demo2 script, and I can obviously have it produce hexes at uniform size, but the size of the hexagons are set independently of the axes. Good for symbols used to mark arbitrary coordinates, not so good when I try to cover the plane without gaps.
Next, I’ve tried creating the patches one by one, essentially this:
r = 1./np.sqrt(3)
for xy, cc in zip(zip(x, y), c):
hexp = mpl.patches.RegularPolygon(xy, 6, radius=r, facecolor=cc, edgecolor=’none')
ax.add_patch(hexp)
ax.autoscale_view()
ax.figure.canvas.draw()
This works as I want it to, but becomes unbearably slow when the number of hexes grows beyond a few thousand.
Given that RegularPolygon can do the trick, it seems likely that RegularPolyCollection should also be able to?
This is what I tried:
ax = gca()
collection = RegularPolyCollection(
6, # a pentagon
rotation=(np.pi/7,),
sizes=(.5/np.sqrt(3),),
facecolors = cc,
edgecolors = None,
linewidths = (1,),
offsets = zip(xx,yy),
transOffset = ax.transData,
)
#collection.set_transform(ax.transData)
ax.add_collection(collection, autolim=True)
ax.autoscale_view()
ax.figure.canvas.draw()
This produces dots of minute sizes at the desired coordinates. I can tweak the size to make them bigger, but they don’t scale with the axes, as for the scatter_demo script used initially. Digging in the docs, I found a reference to the set_transform() method of Artists, so I tried setting that to ax.transData (the line commented out in the above snippet), and voila! I have hexagons covering the plane again. Strangely enough, they’re not in the right place anymore (and the locations change when zooming in or out), the sizes aren’t _quite_ right, the autoscaling of axes appear to believe the patches are where I wanted them to be, not where they appear on the plot, results are very different if saving to a figure instead of drawing to a figure window, etc.
Is there a way I can make RegularPolyCollection patches transform with axes coordinates in the same way that RegularPolygon patches do?
Am I barking up the wrong tree, is there another, blindingly obvious, way I should be doing this?
The observant reader will also notice the strange rotation keyword given to RegularPolyCollection. This keyword is ignored in the macosx backend, and I have been unable to find out why.
Thank you for all tips and pointers
--Tom Grydeland