This page was generated from notebooks/08_manual_coloring.ipynb. Interactive online version: Binder badge

[1]:
import geodatasets
import geopandas as gpd
import pandas as pd

import mapclassify
from mapclassify.util import get_color_array

%load_ext watermark
%watermark -a 'eli knaap' -iv
Author: eli knaap

pandas     : 2.3.1
geodatasets: 2024.8.0
geopandas  : 1.1.1
mapclassify: 2.9.1.dev9+gde74d6f.d20250614

[2]:
df = gpd.read_file(geodatasets.get_path("geoda cincinnati")).to_crs(4326)
[3]:
# use mapclassify under the hood
df.plot("DENSITY", scheme="quantiles", cmap="YlOrBr")
[3]:
<Axes: >
../_images/notebooks_08_manual_coloring_2_1.png
[4]:
# get colors directly and pass them to geopandas
colors = get_color_array(
    df.DENSITY.values, scheme="quantiles", cmap="YlOrBr", as_hex=True
)
[5]:
colors
[5]:
array(['#fee290', '#fd9828', '#fee290', '#662505', '#fee290', '#fd9828',
       '#662505', '#fd9828', '#fd9828', '#ca4b02', '#fd9828', '#662505',
       '#662505', '#fd9828', '#ffffe5', '#662505', '#662505', '#ca4b02',
       '#662505', '#662505', '#fee290', '#662505', '#ca4b02', '#662505',
       '#ffffe5', '#fd9828', '#fee290', '#ca4b02', '#fd9828', '#fd9828',
       '#ffffe5', '#ca4b02', '#fd9828', '#ca4b02', '#ffffe5', '#662505',
       '#662505', '#ca4b02', '#fd9828', '#ca4b02', '#fee290', '#ca4b02',
       '#662505', '#ffffe5', '#fd9828', '#fd9828', '#fd9828', '#fee290',
       '#fee290', '#fee290', '#662505', '#ffffe5', '#ffffe5', '#fee290',
       '#ffffe5', '#ca4b02', '#fd9828', '#fd9828', '#ffffe5', '#ca4b02',
       '#fd9828', '#fee290', '#ca4b02', '#ca4b02', '#ca4b02', '#ca4b02',
       '#662505', '#fd9828', '#fee290', '#fee290', '#fee290', '#fee290',
       '#fee290', '#ffffe5', '#fd9828', '#ffffe5', '#ffffe5', '#fee290',
       '#fd9828', '#ffffe5', '#ffffe5', '#ffffe5', '#ffffe5', '#ffffe5',
       '#ffffe5', '#fee290', '#ffffe5', '#fee290', '#ca4b02', '#ffffe5',
       '#ffffe5', '#ffffe5', '#ca4b02', '#fee290', '#ffffe5', '#662505',
       '#662505', '#fee290', '#fd9828', '#ffffe5', '#ca4b02', '#ffffe5',
       '#fd9828', '#fd9828', '#662505', '#662505', '#fd9828', '#fee290',
       '#fd9828', '#662505', '#fd9828', '#ffffe5', '#ca4b02', '#fd9828',
       '#662505', '#ca4b02', '#662505', '#fd9828', '#fee290', '#ca4b02',
       '#fee290', '#ffffe5', '#ffffe5', '#ffffe5', '#fd9828', '#662505',
       '#ca4b02', '#ffffe5', '#fd9828', '#fee290', '#ffffe5', '#ca4b02',
       '#ffffe5', '#fee290', '#fee290', '#ffffe5', '#662505', '#ffffe5',
       '#fd9828', '#662505', '#fd9828', '#662505', '#662505', '#ffffe5',
       '#662505', '#ffffe5', '#662505', '#ffffe5', '#fd9828', '#ffffe5',
       '#ffffe5', '#ca4b02', '#662505', '#662505', '#ca4b02', '#662505',
       '#ffffe5', '#662505', '#ca4b02', '#fee290', '#fd9828', '#ca4b02',
       '#662505', '#ffffe5', '#fd9828', '#fd9828', '#ca4b02', '#ca4b02',
       '#662505', '#ffffe5', '#fee290', '#fee290', '#662505', '#ca4b02',
       '#ffffe5', '#662505', '#fd9828', '#fd9828', '#ffffe5', '#ffffe5',
       '#fee290', '#fee290', '#fee290', '#ffffe5', '#ffffe5', '#fee290',
       '#ffffe5', '#ffffe5', '#ffffe5', '#ffffe5', '#ca4b02', '#ffffe5',
       '#fee290', '#ffffe5', '#662505', '#fee290', '#fee290', '#662505',
       '#fd9828', '#fd9828', '#ca4b02', '#ca4b02', '#ffffe5', '#fee290',
       '#fee290', '#662505', '#ffffe5', '#fd9828', '#ffffe5', '#ffffe5',
       '#fee290', '#fd9828', '#fd9828', '#fee290', '#ca4b02', '#ca4b02',
       '#fd9828', '#fee290', '#fee290', '#fee290', '#fee290', '#fee290',
       '#fee290', '#ca4b02', '#fee290', '#662505', '#fee290', '#662505',
       '#ca4b02', '#662505', '#662505', '#fd9828', '#ffffe5', '#ffffe5',
       '#ffffe5', '#ffffe5', '#ca4b02', '#662505', '#ca4b02', '#662505',
       '#662505', '#ffffe5', '#ffffe5', '#ca4b02', '#ca4b02', '#ca4b02',
       '#ca4b02', '#ca4b02', '#662505', '#ffffe5', '#fee290', '#fee290',
       '#fee290', '#ffffe5', '#fee290', '#ffffe5', '#ffffe5', '#fee290',
       '#fee290', '#fee290', '#ca4b02', '#662505', '#fd9828', '#fd9828',
       '#662505', '#fd9828', '#ca4b02', '#ffffe5', '#662505', '#fd9828',
       '#fee290', '#ffffe5', '#fee290', '#ca4b02', '#ca4b02', '#ca4b02',
       '#ca4b02', '#fd9828', '#fd9828', '#662505', '#ca4b02', '#ca4b02',
       '#ca4b02', '#ca4b02', '#ca4b02', '#fd9828', '#fd9828', '#ca4b02',
       '#662505', '#ca4b02', '#662505', '#ca4b02', '#ca4b02', '#ca4b02',
       '#fee290', '#ffffe5', '#ffffe5', '#662505', '#ca4b02', '#fee290',
       '#fee290', '#ffffe5', '#fd9828', '#ca4b02', '#fd9828', '#fd9828',
       '#fee290', '#fee290', '#662505', '#fd9828', '#ca4b02', '#ffffe5',
       '#fee290', '#fee290', '#662505', '#662505', '#ffffe5', '#662505',
       '#662505', '#ca4b02', '#fee290', '#fd9828', '#fee290', '#662505',
       '#ffffe5', '#662505', '#ffffe5', '#ffffe5', '#ffffe5', '#fee290',
       '#ffffe5', '#ffffe5', '#fee290', '#ffffe5', '#fd9828', '#fee290',
       '#662505', '#ffffe5', '#fee290', '#ffffe5', '#662505', '#ffffe5',
       '#ffffe5', '#fee290', '#ca4b02', '#fd9828', '#fee290', '#fd9828',
       '#fee290', '#662505', '#fee290', '#662505', '#fd9828', '#662505',
       '#662505', '#fd9828', '#fee290', '#fd9828', '#662505', '#662505',
       '#fee290', '#fd9828', '#ca4b02', '#fd9828', '#ffffe5', '#fee290',
       '#ca4b02', '#ca4b02', '#fee290', '#ffffe5', '#fd9828', '#fee290',
       '#fd9828', '#ffffe5', '#662505', '#fee290', '#fd9828', '#fee290',
       '#ca4b02', '#ffffe5', '#fd9828', '#662505', '#fd9828', '#ca4b02',
       '#662505', '#662505', '#fd9828', '#662505', '#662505', '#662505',
       '#ca4b02', '#fd9828', '#fee290', '#fee290', '#ca4b02', '#ffffe5',
       '#662505', '#fd9828', '#ca4b02', '#662505', '#ca4b02', '#ffffe5',
       '#ca4b02', '#662505', '#ca4b02', '#662505', '#662505', '#ca4b02',
       '#ca4b02', '#fd9828', '#662505', '#fd9828', '#fee290', '#662505',
       '#662505', '#662505', '#fee290', '#fd9828', '#ca4b02', '#fee290',
       '#ca4b02', '#662505', '#fd9828', '#fd9828', '#ca4b02', '#662505',
       '#fd9828', '#fd9828', '#fee290', '#ca4b02', '#fd9828', '#ca4b02',
       '#ca4b02', '#ca4b02', '#fd9828', '#ca4b02', '#fd9828', '#ca4b02',
       '#ca4b02', '#ca4b02', '#fd9828', '#fd9828', '#fd9828', '#662505',
       '#fee290', '#ca4b02', '#fd9828', '#fee290', '#662505', '#662505',
       '#fd9828', '#662505', '#fd9828', '#fd9828', '#ffffe5', '#ca4b02',
       '#fee290'], dtype=object)
[6]:
df.plot(color=colors)
[6]:
<Axes: >
../_images/notebooks_08_manual_coloring_5_1.png

geopandas explore method can also use mapclassify under the hood or take a list of colors

[7]:
# json doesnt like numpy arrays
df.explore(color=list(colors), tiles="CartoDB Positron")
[7]:
Make this Notebook Trusted to load map: File -> Trust Notebook

For some visualization libraries, you need to pass the colors explicitly. Their examples usually punt on classification schemes, but rather use linear or logarithmic scalers

[8]:
from lonboard import Map, PolygonLayer

lonboard requires a 2-dimensional array of integers

[9]:
colors = get_color_array(
    df.DENSITY.values, scheme="quantiles", cmap="YlOrBr", alpha=0.6
)
[10]:
colors
[10]:
array([[254, 226, 144, 153],
       [253, 152,  40, 153],
       [254, 226, 144, 153],
       ...,
       [255, 255, 229, 153],
       [202,  75,   2, 153],
       [254, 226, 144, 153]], shape=(457, 4), dtype=uint8)
[11]:
# get RGBA instead of hex
layer = PolygonLayer.from_geopandas(
    df,
    get_fill_color=colors,
)
m = Map(layers=[layer], _height=800)
m
[11]:
[12]:
import pydeck as pdk

pydeck requires a list of RGBA colors, but because JSON cant serialize uint8, it needs lists of floats

[13]:
df["fill"] = pd.Series(list(colors.astype(float))).apply(list).values
[14]:
df["fill"]
[14]:
0      [254.0, 226.0, 144.0, 153.0]
1       [253.0, 152.0, 40.0, 153.0]
2      [254.0, 226.0, 144.0, 153.0]
3         [102.0, 37.0, 5.0, 153.0]
4      [254.0, 226.0, 144.0, 153.0]
                   ...
452     [253.0, 152.0, 40.0, 153.0]
453     [253.0, 152.0, 40.0, 153.0]
454    [255.0, 255.0, 229.0, 153.0]
455       [202.0, 75.0, 2.0, 153.0]
456    [254.0, 226.0, 144.0, 153.0]
Name: fill, Length: 457, dtype: object
[15]:
layers = [
    pdk.Layer(
        "GeoJsonLayer",
        data=df.to_crs(4326)[["geometry", "fill"]],
        get_fill_color="fill",
        auto_highlight=True,
        pickable=True,
    ),
]
view_state = pdk.ViewState(
    **{
        "latitude": df.union_all().centroid.y,
        "longitude": df.union_all().centroid.x,
        "zoom": 12,
    }
)
D = pdk.Deck(
    layers,
    map_provider="carto",
    map_style=pdk.map_styles.LIGHT,
    initial_view_state=view_state,
)
D
[15]: