kms
kms

Reputation: 2024

apply custom color scale to pydeck h3hexagon layer

I am using pydeck to render a visualization using spatial data. I was hoping to use a custom color scale and apply the gradient to the hexagons based on counts.

here's some json data:

[{"count": "12", "hexIds": ["82beeffffffffff"]}, {"count": "35", "hexIds": ["82be77fffffffff"]}, {"count": "51", "hexIds": ["82b917fffffffff"]}, {"count": "32", "hexIds": ["82bf4ffffffffff"]}, {"count": "93", "hexIds": ["82be67fffffffff"]}, {"count": "51", "hexIds": ["82c997fffffffff"]}, {"count": "13", "hexIds": ["82be5ffffffffff"]}, {"count": "11", "hexIds": ["82bed7fffffffff"]}, {"count": "52", "hexIds": ["82be47fffffffff"]}, {"count": "9", "hexIds": ["82c987fffffffff"]}, {"count": "13", "hexIds": ["82b9a7fffffffff"]}, {"count": "26", "hexIds": ["82a737fffffffff"]}, {"count": "38", "hexIds": ["82be8ffffffffff"]}, {"count": "3", "hexIds": ["829d77fffffffff"]}, {"count": "85", "hexIds": ["82be0ffffffffff"]}, {"count": "12", "hexIds": ["82b9b7fffffffff"]}, {"count": "23", "hexIds": ["82be6ffffffffff"]}, {"count": "2", "hexIds": ["82b84ffffffffff"]}, {"count": "6", "hexIds": ["829d4ffffffffff"]}, {"count": "6", "hexIds": ["82b85ffffffffff"]}, {"count": "7", "hexIds": ["82bec7fffffffff"]}, {"count": "32", "hexIds": ["82be57fffffffff"]}, {"count": "2", "hexIds": ["82a7affffffffff"]}, {"count": "30", "hexIds": ["82a727fffffffff"]}, {"count": "6", "hexIds": ["82a787fffffffff"]}, {"count": "21", "hexIds": ["82bee7fffffffff"]}, {"count": "10", "hexIds": ["82b847fffffffff"]}, {"count": "5", "hexIds": ["82a617fffffffff"]}, {"count": "6", "hexIds": ["82a6a7fffffffff"]}, {"count": "7", "hexIds": ["8294effffffffff"]}, {"count": "17", "hexIds": ["82bef7fffffffff"]}, {"count": "1", "hexIds": ["8294e7fffffffff"]}, {"count": "6", "hexIds": ["82a78ffffffffff"]}, {"count": "13", "hexIds": ["82a79ffffffffff"]}, {"count": "3", "hexIds": ["82b877fffffffff"]}, {"count": "5", "hexIds": ["82a797fffffffff"]}, {"count": "28", "hexIds": ["82be4ffffffffff"]}, {"count": "7", "hexIds": ["829487fffffffff"]}, {"count": "4", "hexIds": ["82bedffffffffff"]}, {"count": "2", "hexIds": ["82945ffffffffff"]}, {"count": "10", "hexIds": ["82b997fffffffff"]}, {"count": "4", "hexIds": ["82b9affffffffff"]}, {"count": "9", "hexIds": ["829c27fffffffff"]}, {"count": "16", "hexIds": ["82a707fffffffff"]}, {"count": "3", "hexIds": ["829d07fffffffff"]}, {"count": "8", "hexIds": ["82c9b7fffffffff"]}, {"count": "2", "hexIds": ["8294affffffffff"]}, {"count": "5", "hexIds": ["829d5ffffffffff"]}, {"count": "5", "hexIds": ["829d57fffffffff"]}, {"count": "1", "hexIds": ["82b80ffffffffff"]}, {"count": "11", "hexIds": ["82beaffffffffff"]}, {"count": "2", "hexIds": ["82b8b7fffffffff"]}, {"count": "1", "hexIds": ["829497fffffffff"]}, {"count": "7", "hexIds": ["829d27fffffffff"]}, {"count": "2", "hexIds": ["82a7a7fffffffff"]}, {"count": "6", "hexIds": ["82b887fffffffff"]}, {"count": "7", "hexIds": ["829457fffffffff"]}, {"count": "4", "hexIds": ["82c99ffffffffff"]}, {"count": "2", "hexIds": ["8294cffffffffff"]}, {"count": "4", "hexIds": ["82b88ffffffffff"]}, {"count": "3", "hexIds": ["82b98ffffffffff"]}, {"count": "7", "hexIds": ["82b837fffffffff"]}, {"count": "9", "hexIds": ["829d0ffffffffff"]}, {"count": "2", "hexIds": ["8294c7fffffffff"]}, {"count": "6", "hexIds": ["829d2ffffffffff"]}, {"count": "2", "hexIds": ["829d47fffffffff"]}, {"count": "3", "hexIds": ["82b867fffffffff"]}, {"count": "1", "hexIds": ["82b807fffffffff"]}, {"count": "5", "hexIds": ["82b8a7fffffffff"]}, {"count": "2", "hexIds": ["829d67fffffffff"]}, {"count": "1", "hexIds": ["82a717fffffffff"]}, {"count": "2", "hexIds": ["82b82ffffffffff"]}, {"count": "1", "hexIds": ["829c6ffffffffff"]}, {"count": "2", "hexIds": ["829c2ffffffffff"]}, {"count": "1", "hexIds": ["8294dffffffffff"]}, {"count": "1", "hexIds": ["82d897fffffffff"]}, {"count": "8", "hexIds": ["82b86ffffffffff"]}, {"count": "1", "hexIds": ["82b91ffffffffff"]}, {"count": "3", "hexIds": ["82948ffffffffff"]}, {"count": "3", "hexIds": ["829c4ffffffffff"]}, {"count": "5", "hexIds": ["82b897fffffffff"]}, {"count": "1", "hexIds": ["82b89ffffffffff"]}, {"count": "1", "hexIds": ["829c07fffffffff"]}, {"count": "1", "hexIds": ["82b937fffffffff"]}, {"count": "1", "hexIds": ["82949ffffffffff"]}, {"count": "1", "hexIds": ["82b99ffffffffff"]}, {"count": "1", "hexIds": ["82b987fffffffff"]}, {"count": "1", "hexIds": ["8294d7fffffffff"]}, {"count": "1", "hexIds": ["82b8dffffffffff"]}, {"count": "1", "hexIds": ["829ce7fffffffff"]}, {"count": "15", "hexIds": ["82becffffffffff"]}, {"count": "13", "hexIds": ["82be1ffffffffff"]}, {"count": "1", "hexIds": ["82b827fffffffff"]}]
import pandas as pd
import pydeck
df = pd.read_json('aus_h3.duckgl.json')
h3_layer = pydeck.Layer(
    "H3ClusterLayer",
    df,
    pickable=True,
    stroked=True,
    filled=True,
    extruded=False,
    get_hexagons="hexIds",
    get_fill_color="[255, (1 - count / 500) * 255, 0]",
    get_line_color=[255, 255, 255],
    line_width_min_pixels=2,
)
view_state = pydeck.ViewState(latitude=-25.7773677126431,
                              longitude=135.084939479828,
                              zoom=4,
                              bearing=0,
                              pitch=45)
pydeck.Deck(
    layers=[h3_layer],
    initial_view_state=view_state,
    tooltip={"text": "Density: {count}"}
).to_html("aus_h3.duckgl.html")

How do I specify a custom color scale instead on [255, (1 - count / 500) * 255, 0] in get_fill_color ? For example, I'd like to use 6-class color scale: https://colorbrewer2.org/#type=sequential&scheme=YlOrRd&n=6

Upvotes: 0

Views: 1716

Answers (1)

limeman
limeman

Reputation: 96

According to the deck.gl documentation, get_fill_color needs to be an RGB(A) array, or a function returning such an array. The pydeck documentation says that general functions are not allowed, but expressions using your data as parameters are allowed to be given as strings, so that gives at least two options:

Option 1: Linear gradient

Using the color scheme you provided: the brightest color expressed in RGB is [255,255,178] and the darkest is [189,0,38]. A linear gradient that spans these colors can be constructed from your data such that the brightest color corresponds to lowest count and darkest color to highest count:

min_count = df['count'].min()
max_count = df['count'].max()
diff = max_count - min_count

color_scheme = f"""[
    255 - (255-189) * (count - {min_count})/{diff}, 
    255 - 255 * (count - {min_count})/{diff}, 
    178 - (178-38) * (count - {min_count})/{diff}
    ]"""

This can be passed to the pydeck.Layer as get_fill_color=color_scheme and gives approximately the same scale as you provided.

Option 2: Add the color scheme to the data

Add a new column to the DataFrame containing the exact color scheme you want to use. Then the value from that column can be passed as the get_fill_color-parameter. This can be done as such:

from math import floor

min_count = df['count'].min()
max_count = df['count'].max()
diff = max_count - min_count

color_scheme = [
        [255,255,178],
        [254,217,118],
        [254, 178, 76],
        [253, 141, 60],
        [240,59,32],
        [189,0,38]
    ]

def get_color(row):
    number_of_colors = len(color_scheme)
    index = floor(number_of_colors * (row['count'] - min_count) / diff)
    # the index might barely go out of bounds, so correct for that:
    if index == number_of_colors:
        index = number_of_colors - 1
    elif index == -1:
        index = 0
    return color_scheme[index]

df['color_column'] = df.apply(get_color, axis=1)

And pass this to the pydeck.Layer as get_fill_color="color_column".

Upvotes: 2

Related Questions