Skip to content

tessellation module

Module for setting up hexagon network.

  1. grabs the spatial bounds of fire shapefile.
  2. using user-defined area value to build hexagon network on the bounded area.
  3. projection of the hexagon network to be the same as that of the fire shapefile.
  4. generate the centroid of the hexes as nodes shapefile
  5. generate arcs connecting neighbouring nodes

create_arcs(hexagons, **kwargs)

Create geometry of arcs connecting each neighbouring hexagons.

Parameters:

Name Type Description Default
hexagons GeoDataFrame

geometry and ID of hexagonal patches

required

Returns:

Type Description
GeoDataFrame

return arcs geometry with attributes indicating IDs of the two hexagons it connects.

Source code in postbp/tessellation.py
def create_arcs(hexagons, **kwargs):
    """Create geometry of arcs connecting each neighbouring hexagons.

    Args:
        hexagons (GeoDataFrame): geometry and ID of hexagonal patches

    Returns:
        GeoDataFrame: return arcs geometry with attributes indicating IDs of the two hexagons it connects.
    """    
    hexagon = hexagons.copy()
    if 'Node_ID' in kwargs:
        hexagon = hexagon.rename(columns={kwargs["Node_ID"]: 'Node_ID'})

    nodes = nodes_from_hexagons(hexagon)
    nodes = nodes.set_index('Node_ID')
    SRID = hexagon.crs
    hexagon = hexagon.set_index('Node_ID')

    arcs = gpd.GeoDataFrame()
    df=gpd.GeoDataFrame()
    for index, _ in tqdm(hexagon.iterrows()):
        nnnn = hexagon[~hexagon.geometry.disjoint(hexagon.at[index,'geometry'])].index.tolist()
        nnnn = [i for i in nnnn if index != i]
        mmmm = [index] * len(nnnn)
        mmnn = [LineString(xy) for xy in zip(nodes.loc[mmmm].geometry, nodes.loc[nnnn].geometry)]
        mmnn = gpd.GeoDataFrame(df, crs = SRID, geometry = mmnn )
        mmnn['Node_1'] = mmmm
        mmnn['Node_2'] = nnnn
        arcs = pd.concat([arcs,mmnn])

    arcs['Node_1'] = arcs['Node_1'].astype(int)
    arcs['Node_2'] = arcs['Node_2'].astype(int)
    arcs.reset_index(drop=True, inplace=True)

    return arcs

create_hexagons(boundaryShp, offset_x=0, offset_y=0, **kwargs)

Creat geodataframe of hexagonal patches of defined size and range. Hexagon size can be defined in area, side length, or long diameter. Must input one parameter out of the three options.

Parameters:

Name Type Description Default
boundaryShp GeoDataFrame

the geodataframe defines the range covered by the hexagonal patches

required
area float, OPTIONAL

define the size of each hexagon by area in square meters

required
side float, OPTIONAL

define the size of each hexagon by side length in meter

required
diameter float, OPTIONAL

define the size of each hexagon by long diameter in meter

required
offset_x fraction, OPTIONAL

defines the horizontal offset for hexagons as a fraction of the length of the hexagon's long diagonal. Can be positive or negative.

0
offset_y fraction, OPTIONAL

defines the vertical offset for hexagons as a fraction of the length of the hexagon's long diagonal. Can be positive or negative.

0

Returns:

Type Description
GeoDataFrame

return a geodataframe of hexagonal network of the defined size and covering the defined range.

Source code in postbp/tessellation.py
def create_hexagons(boundaryShp, offset_x=0, offset_y=0, **kwargs):
    """Creat geodataframe of hexagonal patches of defined size and range.
       Hexagon size can be defined in area, side length, or long diameter. Must input one parameter out of the three options.
    Args:
        boundaryShp (GeoDataFrame): the geodataframe defines the range covered by the hexagonal patches
        area (float, OPTIONAL): define the size of each hexagon by area in square meters
        side (float, OPTIONAL): define the size of each hexagon by side length in meter
        diameter (float, OPTIONAL): define the size of each hexagon by long diameter in meter
        offset_x (fraction, OPTIONAL): defines the horizontal offset for hexagons as a fraction of the length of the hexagon's long diagonal. Can be positive or negative.
        offset_y (fraction, OPTIONAL): defines the vertical offset for hexagons as a fraction of the length of the hexagon's long diagonal. Can be positive or negative.

    Returns:
        GeoDataFrame: return a geodataframe of hexagonal network of the defined size and covering the defined range.
    """   
    if 'area' in kwargs:
        area = kwargs['area']

    elif "side" in kwargs:
        area = kwargs["side"]**2*3/2*math.sqrt(3)

    elif "diameter" in kwargs:
        area = kwargs["diameter"]**2*3/8*math.sqrt(3)

    else:
        print('Please define a value using one of the following parameters: area, side, or diameter of the intended hexagonal patches.')

    myCRS = boundaryShp.crs
    xmin,ymin,xmax,ymax =  boundaryShp.total_bounds
    nodes = _create_hexnodes(area, xmin, ymin, xmax, ymax, offset_x, offset_y)
    hexagons = [_create_hexgrids(area, node[0], node[1]) for node in nodes]

    nodes = [Point(node) for node in nodes]
    nodes = gpd.GeoDataFrame({'geometry': nodes})
    nodes['Node_ID'] = nodes.index + 1
    nodes.crs = myCRS
    hexagons = gpd.GeoDataFrame({'geometry':hexagons})
    hexagons['Node_ID'] = hexagons.index + 1
    hexagons.crs = myCRS

    return hexagons

create_hexagons_nodes(boundaryShp, offset_x=0, offset_y=0, **kwargs)

Creat geodataframe of hexagonal patches and the nodes (centroids) of the hexagons of defined size and range. Hexagon size can be defined in area, side length, or long diameter. Must input one parameter out of the three options.

Parameters:

Name Type Description Default
boundaryShp GeoDataFrame

the geodataframe defines the range covered by the hexagonal patches

required
area float, OPTIONAL

define the size of each hexagon by area in square meters

required
side float, OPTIONAL

define the size of each hexagon by side length in meter

required
diameter float, OPTIONAL

define the size of each hexagon by long diameter in meter

required
offset_x fraction, OPTIONAL

defines the horizontal offset for hexagons as a fraction of the length of the hexagon's long diagonal. Can be positive or negative.

0
offset_y fraction, OPTIONAL

defines the vertical offset for hexagons as a fraction of the length of the hexagon's long diagonal. Can be positive or negative.

0

Returns:

Type Description
GeoDataFrame

return geodataframes of hexagons and nodes of the defined size and covering the defined range. Note to give two variable names when using this function.

Source code in postbp/tessellation.py
def create_hexagons_nodes(boundaryShp, offset_x=0, offset_y=0, **kwargs):
    """Creat geodataframe of hexagonal patches and the nodes (centroids) of the hexagons of defined size and range.
       Hexagon size can be defined in area, side length, or long diameter. Must input one parameter out of the three options.
    Args:
        boundaryShp (GeoDataFrame): the geodataframe defines the range covered by the hexagonal patches
        area (float, OPTIONAL): define the size of each hexagon by area in square meters
        side (float, OPTIONAL): define the size of each hexagon by side length in meter
        diameter (float, OPTIONAL): define the size of each hexagon by long diameter in meter
        offset_x (fraction, OPTIONAL): defines the horizontal offset for hexagons as a fraction of the length of the hexagon's long diagonal. Can be positive or negative.
        offset_y (fraction, OPTIONAL): defines the vertical offset for hexagons as a fraction of the length of the hexagon's long diagonal. Can be positive or negative.
    Returns:
        GeoDataFrame: return geodataframes of hexagons and nodes of the defined size and covering the defined range.
                      Note to give two variable names when using this function.
    """    
    if 'area' in kwargs:
        area = kwargs['area']

    elif "side" in kwargs:
        area = kwargs["side"]**2*3/2*math.sqrt(3)

    elif "diameter" in kwargs:
        area = kwargs["diameter"]**2*3/8*math.sqrt(3)

    else:
        print('Please define a value using one of the following parameters: area, side, or diameter of the intended hexagonal patches.')

    myCRS = boundaryShp.crs
    xmin,ymin,xmax,ymax =  boundaryShp.total_bounds
    nodes = _create_hexnodes(area, xmin, ymin, xmax, ymax, offset_x, offset_y)
    hexagons = [_create_hexgrids(area, node[0], node[1]) for node in nodes]

    nodes = [Point(node) for node in nodes]
    nodes = gpd.GeoDataFrame({'geometry': nodes})
    nodes['Node_ID'] = nodes.index + 1
    nodes.crs = myCRS
    hexagons = gpd.GeoDataFrame({'geometry':hexagons})
    hexagons['Node_ID'] = hexagons.index + 1
    hexagons.crs = myCRS

    return hexagons, nodes

nodes_from_hexagons(hexagons)

Generate nodes geometry from hexagons

Parameters:

Name Type Description Default
hexagons GeoDataFrame

geometry and ID of hexagonal patches

required

Returns:

Type Description
GeoDataFrame

return nodes geometry with ID attributes same as the hexagons.

Source code in postbp/tessellation.py
def nodes_from_hexagons(hexagons):
    """Generate nodes geometry from hexagons

    Args:
        hexagons (GeoDataFrame): geometry and ID of hexagonal patches

    Returns:
        GeoDataFrame: return nodes geometry with ID attributes same as the hexagons.
    """    
    nodes = hexagons.copy()
    nodes['centroid'] = nodes.geometry.centroid
    nodes.drop(labels='geometry', axis=1, inplace=True)
    nodes.rename(columns={'centroid':'geometry'}, inplace=True)
    nodes = nodes.set_geometry('geometry')
    return nodes