Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Displaying data in a hexagonal grid using Python

What I am looking for is a method or class that allows me to display a list of hexagons in a grid. Ideally I would then be able to use some form of set-method to change the color/hatching/border of the individual hexagons.

The hexagons are stored using the axial coordinate system outlined in the guide by @amitp. But I can easily output their centers as xy-coordinates.

I feel like there might be a solution hidden somewhere in hexbin or RegularPolyCollection. But the former is a histogram method and the latter seems overly complex thanks to the DPI scaling.

So does anyone know of a library that provides a hexagonal grid? It doesn't have to be in matplotlib. I am also quite happy to use ASCII art or switch to R.

like image 230
John Avatar asked Oct 27 '14 08:10

John


2 Answers

Here comes an implementation that allow you to set hexagonal cell the color you want and also allow to create custom border colors.

This is all hand made, just took 1hour using your reference site. You may have to adapt it to your needs, but seems working.

from tkinter import *

class HexaCanvas(Canvas):
    """ A canvas that provides a create-hexagone method """
    def __init__(self, master, *args, **kwargs):
        Canvas.__init__(self, master, *args, **kwargs)

        self.hexaSize = 20

    def setHexaSize(self, number):
        self.hexaSize = number


    def create_hexagone(self, x, y, color = "black", fill="blue", color1=None, color2=None, color3=None, color4=None, color5=None, color6=None):
        """ 
        Compute coordinates of 6 points relative to a center position.
        Point are numbered following this schema :

        Points in euclidiean grid:  
                    6

                5       1
                    .
                4       2

                    3

        Each color is applied to the side that link the vertex with same number to its following.
        Ex : color 1 is applied on side (vertex1, vertex2)

        Take care that tkinter ordinate axes is inverted to the standard euclidian ones.
        Point on the screen will be horizontally mirrored.
        Displayed points:

                    3
              color3/      \color2      
                4       2
            color4|     |color1
                5       1
              color6\      /color6
                    6

        """
        size = self.hexaSize
        Δx = (size**2 - (size/2)**2)**0.5

        point1 = (x+Δx, y+size/2)
        point2 = (x+Δx, y-size/2)
        point3 = (x   , y-size  )
        point4 = (x-Δx, y-size/2)
        point5 = (x-Δx, y+size/2)
        point6 = (x   , y+size  )

        #this setting allow to specify a different color for each side.
        if color1 == None:
            color1 = color
        if color2 == None:
            color2 = color
        if color3 == None:
            color3 = color
        if color4 == None:
            color4 = color
        if color5 == None:
            color5 = color
        if color6 == None:
            color6 = color

        self.create_line(point1, point2, fill=color1, width=2)
        self.create_line(point2, point3, fill=color2, width=2)
        self.create_line(point3, point4, fill=color3, width=2)
        self.create_line(point4, point5, fill=color4, width=2)
        self.create_line(point5, point6, fill=color5, width=2)
        self.create_line(point6, point1, fill=color6, width=2)

        if fill != None:
            self.create_polygon(point1, point2, point3, point4, point5, point6, fill=fill)

class HexagonalGrid(HexaCanvas):
    """ A grid whose each cell is hexagonal """
    def __init__(self, master, scale, grid_width, grid_height, *args, **kwargs):

        Δx     = (scale**2 - (scale/2.0)**2)**0.5
        width  = 2 * Δx * grid_width + Δx
        height = 1.5 * scale * grid_height + 0.5 * scale

        HexaCanvas.__init__(self, master, background='white', width=width, height=height, *args, **kwargs)
        self.setHexaSize(scale)

    def setCell(self, xCell, yCell, *args, **kwargs ):
        """ Create a content in the cell of coordinates x and y. Could specify options throught keywords : color, fill, color1, color2, color3, color4; color5, color6"""

        #compute pixel coordinate of the center of the cell:
        size = self.hexaSize
        Δx = (size**2 - (size/2)**2)**0.5

        pix_x = Δx + 2*Δx*xCell
        if yCell%2 ==1 :
            pix_x += Δx

        pix_y = size + yCell*1.5*size

        self.create_hexagone(pix_x, pix_y, *args, **kwargs)



if __name__ == "__main__":
    tk = Tk()

    grid = HexagonalGrid(tk, scale = 50, grid_width=4, grid_height=4)
    grid.grid(row=0, column=0, padx=5, pady=5)

    def correct_quit(tk):
        tk.destroy()
        tk.quit()

    quit = Button(tk, text = "Quit", command = lambda :correct_quit(tk))
    quit.grid(row=1, column=0)

    grid.setCell(0,0, fill='blue')
    grid.setCell(1,0, fill='red')
    grid.setCell(0,1, fill='green')
    grid.setCell(1,1, fill='yellow')
    grid.setCell(2,0, fill='cyan')
    grid.setCell(0,2, fill='teal')
    grid.setCell(2,1, fill='silver')
    grid.setCell(1,2, fill='white')
    grid.setCell(2,2, fill='gray')

    tk.mainloop()

I tried to comment my code properly. If something seems unclear to you, please do not hesitate to ask for explanation.

Good luck Arthur Vaisse.

NB : script running on python 3. Drawing is a little jagged. Improve on Tk canvas can add anti alias as suggested in https://mail.python.org/pipermail/tkinter-discuss/2009-April/001904.html

like image 176
Arthur Vaïsse Avatar answered Nov 01 '22 08:11

Arthur Vaïsse


You can use my hexalattice python module exactly for that purpose - creating and plotting hexagonal grids:

from hexalattice.hexalattice import *
hex_centers, _ = create_hex_grid(nx=5,
                                 ny=5,
                                 do_plot=True)
                                 
plt.show()    # import matplotlib.pyplot as plt

Producing this plot: enter image description here

See this answer for more examples: this answer

like image 1
alexkaz Avatar answered Nov 01 '22 08:11

alexkaz