Displaying Choropleth map

I am experiencing problems with this code not able to display the choropleth map of Kenyan Country.

Dataset
Geojson data

import streamlit as st
import pandas as pd
import altair as alt
import plotly.express as px
import json

# Page configuration
st.set_page_config(
    page_title="Kenyan Counties Population",
    page_icon="kenyan.png",
    layout="wide",
    initial_sidebar_state="expanded")

alt.themes.enable("dark")

# Load data
try:
    df_reshaped = pd.read_csv('Mydata.csv')
except FileNotFoundError:
    st.error("The file 'Mydata.csv' is not found.")
    raise

# Load GeoJSON data
try:
    with open('gadm41_KEN_1.geojson') as f:
        kenya_geojson = json.load(f)
except FileNotFoundError:
    st.error("The file 'gadm41_KEN_1.geojson' is not found.")
    raise

# Sidebar
with st.sidebar:
    st.title("πŸ‡°πŸ‡ͺKenyan Counties Population")

    year_list = list(df_reshaped.year.unique())[::-1]

    selected_year = st.selectbox('Select a year', year_list)
    df_selected_year = df_reshaped[df_reshaped.year == selected_year]
    df_selected_year_sorted = df_selected_year.sort_values(by="population", ascending=False)

    color_theme_list = ['blues', 'cividis', 'greens', 'inferno', 'magma', 'plasma', 'reds', 'rainbow', 'turbo',
                        'viridis']
    selected_color_theme = st.selectbox('Select a color theme', color_theme_list)


# Plots
def make_heatmap(input_df, input_y, input_x, input_color, input_color_theme):
    heatmap = alt.Chart(input_df).mark_rect().encode(
        y=alt.Y(f'{input_y}:O',
                axis=alt.Axis(title="Year", titleFontSize=18, titlePadding=15, titleFontWeight=900, labelAngle=0)),
        x=alt.X(f'{input_x}:O', axis=alt.Axis(title="", titleFontSize=18, titlePadding=15, titleFontWeight=900)),
        color=alt.Color(f'max({input_color}):Q', legend=None, scale=alt.Scale(scheme=input_color_theme)),
        stroke=alt.value('black'),
        strokeWidth=alt.value(0.25),
    ).properties(width=900).configure_axis(
        labelFontSize=12,
        titleFontSize=12
    )
    return heatmap


def make_choropleth(input_df, input_id, input_column, input_color_theme, geojson):
    choropleth = px.choropleth(input_df, geojson=geojson, locations=input_id, featureidkey="properties.id",
                               color=input_column,
                               color_continuous_scale=input_color_theme,
                               range_color=(0, max(df_selected_year.population)),
                               labels={'population': 'Population'},
                               scope="world")
    choropleth.update_geos(fitbounds="locations", visible=False)
    choropleth.update_layout(
        template='plotly_dark',
        plot_bgcolor='rgba(0, 0, 0, 0)',
        paper_bgcolor='rgba(0, 0, 0, 0)',
        margin=dict(l=0, r=0, t=0, b=0),
        height=350
    )
    return choropleth


def make_donut(input_response, input_text, input_color):
    chart_color = {
        'blue': ['#29b5e8', '#155F7A'],
        'green': ['#27AE60', '#12783D'],
        'orange': ['#F39C12', '#875A12'],
        'red': ['#E74C3C', '#781F16']
    }[input_color]

    source = pd.DataFrame({
        "Topic": ['', input_text],
        "% value": [100 - input_response, input_response]
    })
    source_bg = pd.DataFrame({
        "Topic": ['', input_text],
        "% value": [100, 0]
    })

    plot = alt.Chart(source).mark_arc(innerRadius=45, cornerRadius=25).encode(
        theta="% value",
        color=alt.Color("Topic:N", scale=alt.Scale(domain=[input_text, ''], range=chart_color), legend=None),
    ).properties(width=130, height=130)

    text = plot.mark_text(align='center', color="#29b5e8", font="Lato", fontSize=32, fontWeight=700,
                          fontStyle="italic").encode(text=alt.value(f'{input_response} %'))
    plot_bg = alt.Chart(source_bg).mark_arc(innerRadius=45, cornerRadius=20).encode(
        theta="% value",
        color=alt.Color("Topic:N", scale=alt.Scale(domain=[input_text, ''], range=chart_color), legend=None),
    ).properties(width=130, height=130)
    return plot_bg + plot + text


def format_number(num):
    if num > 1000000:
        if not num % 1000000:
            return f'{num // 1000000} M'
        return f'{round(num / 1000000, 1)} M'
    return f'{num // 1000} K'


def calculate_population_difference(input_df, input_year):
    selected_year_data = input_df[input_df['year'] == input_year].reset_index()
    previous_year_data = input_df[input_df['year'] == input_year - 1].reset_index()
    selected_year_data['population_difference'] = selected_year_data.population.sub(previous_year_data.population,
                                                                                    fill_value=0)
    return pd.concat([selected_year_data.counties, selected_year_data.id, selected_year_data.population,
                      selected_year_data.population_difference], axis=1).sort_values(by="population_difference",
                                                                                     ascending=False)


# Dashboard Main Panel
col = st.columns((1.5, 4.5, 2), gap='medium')

with col[0]:
    st.markdown('#### Gains/Losses')

    df_population_difference_sorted = calculate_population_difference(df_reshaped, selected_year)

    if selected_year > 2010:
        first_county_name = df_population_difference_sorted.counties.iloc[0]
        first_county_population = format_number(df_population_difference_sorted.population.iloc[0])
        first_county_delta = format_number(df_population_difference_sorted.population_difference.iloc[0])
    else:
        first_county_name = '-'
        first_county_population = '-'
        first_county_delta = ''
    st.metric(label=first_county_name, value=first_county_population, delta=first_county_delta)

    if selected_year > 2010:
        last_county_name = df_population_difference_sorted.counties.iloc[-1]
        last_county_population = format_number(df_population_difference_sorted.population.iloc[-1])
        last_county_delta = format_number(df_population_difference_sorted.population_difference.iloc[-1])
    else:
        last_county_name = '-'
        last_county_population = '-'
        last_county_delta = ''
    st.metric(label=last_county_name, value=last_county_population, delta=last_county_delta)

    st.markdown('#### Counties Migration')

    if selected_year > 2010:
        df_greater_50000 = df_population_difference_sorted[
            df_population_difference_sorted.population_difference > 50000]
        df_less_50000 = df_population_difference_sorted[df_population_difference_sorted.population_difference < -50000]

        countries_migration_greater = round(
            (len(df_greater_50000) / df_population_difference_sorted.counties.nunique()) * 100)
        countries_migration_less = round((len(df_less_50000) / df_population_difference_sorted.counties.nunique()) * 100)
        donut_chart_greater = make_donut(countries_migration_greater, 'Inbound Migration', 'green')
        donut_chart_less = make_donut(countries_migration_less, 'Outbound Migration', 'red')
    else:
        countries_migration_greater = 0
        countries_migration_less = 0
        donut_chart_greater = make_donut(countries_migration_greater, 'Inbound Migration', 'green')
        donut_chart_less = make_donut(countries_migration_less, 'Outbound Migration', 'red')

    migrations_col = st.columns((0.2, 1, 0.2))
    with migrations_col[1]:
        st.write('Inbound')
        st.altair_chart(donut_chart_greater)
        st.write('Outbound')
        st.altair_chart(donut_chart_less)

with col[1]:
    st.markdown('#### Total Population')

    choropleth = make_choropleth(df_selected_year, 'id', 'population',
                                 selected_color_theme, kenya_geojson)
    st.plotly_chart(choropleth, use_container_width=True)

    heatmap = make_heatmap(df_reshaped, 'year', 'counties', 'population',
                           selected_color_theme)
    st.altair_chart(heatmap, use_container_width=True)

with col[2]:
    st.markdown('#### Top Counties')

    st.dataframe(df_selected_year_sorted,
                 column_order=("counties", "population"),
                 hide_index=True,
                 width=None,
                 column_config={
                     "counties": st.column_config.TextColumn("Counties"),
                     "population": st.column_config.ProgressColumn("Population", format="%f", min_value=0,
                                                                   max_value=max(df_selected_year_sorted.population))}
                 )

    with st.expander('About', expanded=True):
        st.write('''
            - Data: [Kenyan National Bureau Of Statistics](https://www.knbs.or.ke/dataset.html).
            - :blue[**Gains/Losses**]: counties with high inbound/ outbound migration for selected year
            - :blue[**Counties Migration**]: percentage of counties with annual inbound/ outbound migration > 20,000
            ''')

Hey Stud,

Yes, I did have a chortle there.

I’m looking into this for you, but first, I noticed discrepancy in your mydata.csv.
I was getting the error that it could not find β€˜counties’ in the data frame, that’s because in the mydata.csv they are called states. Changing that got me the dashboard. And what I think must be the chloropleth map but it is displayed as a black square.

UPDATE: the display issue as such is down to the template being plotly_dark, changing this to plotly _white allows it to displays a map of Africa.

You will need to play around a bit to get exactly what your looking for but here is the updated make_choropleth function

def make_choropleth(input_df, input_id, input_column, input_color_theme, geojson):
    choropleth = px.choropleth(input_df, geojson=geojson, locations=input_id, featureidkey="properties.id",
                               color=input_column,
                               color_continuous_scale=input_color_theme,
                               range_color=(0, max(df_selected_year.population)),
                               labels={'population': 'Population'},
                               scope="africa",
                               #center={"lat": 1.2921, "lon": 36.8219}  # Adjust latitude and longitude for Kenya
                               )

    choropleth.update_geos(fitbounds="locations", visible=True)
    choropleth.update_layout(
        template='plotly_white',                # change to plotly_white as plotly _dark makes everything black/dark
        plot_bgcolor='rgba(255, 255, 255, 1)',  # Change plot background color
        paper_bgcolor='rgba(200, 200, 200, 1)',  # Change paper background color
        margin=dict(l=0, r=0, t=0, b=0),
        height=350
    )
    return choropleth

Which will display the following

Hopefully that gets you on your way.

1 Like

Thanks. It solved my problem.