Search code examples
snowflake-cloud-data-platformstreamlitaltairvegachoropleth

Altair choropleth not rendering in streamlit app on Snowflake and color coding problem


I am trying to make a choropleth map in a streamlit app on Snowflake.

Outside of snowflake, I can use the following code to render a map where just one county in Alabama is color coded.

import streamlit as st
import altair as alt
import pandas as pd
from vega_datasets import data

us_counties = alt.topo_feature(data.us_10m.url, "counties")

dist_background = alt.Chart(us_counties).mark_geoshape(fill="lightgray", stroke="white").project("albersUsa")

df = {"FIPS_CD": 1001,
      "FIPS_COLOR": 1}

df = pd.DataFrame(df, index=[0])

dist_foreground = (
    alt.Chart(us_counties)
    .mark_geoshape()
    .transform_lookup(lookup="id", from_=alt.LookupData(df, "FIPS_CD", ["FIPS_COLOR"]))
    .encode(color=alt.Color("FIPS_COLOR:Q"))
    .project(type="albersUsa")
)

dist_chart = alt.layer(dist_background, dist_foreground).properties(width=800, height=800)
st.altair_chart(dist_chart, use_container_width=True)

However, if I try to run the same code in a Streamlit app in Snowflake it does not render at all. I get a blank space with just the legend showing, but the legend shows a 'NaN' in Snowflake, where it shows a 1 when I run the code outside of the Snowflake environment. I can see that the data were loaded by clicking on the ellipsis next to the plot and going to the vega editor. When I do this I see the whole country plotted, but the color coding is not present.

What is Snowflake doing behind the scenes that is messing this up? On Snowflake the package options I am using are:

  • altair 5.0.1
  • vega-datasets 0.9.0
  • python 3.8
  • and the 'latest' streamlit and snowpark packages.

Solution

  • The issue is that Altair is trying to load the topojson file from an external CDN in the frontend, which is blocked by the Content Security Policy set by Snowflake (which prevents Streamlit in Snowflake apps from accessing arbitrary external domains for security reasons).

    If you open the browser developer console while running the app, you should see a log similar to this, which points out the issue:

    Refused to connect to 'https://cdn.jsdelivr.net/npm/[email protected]/data/us-10m.json' because it violates the document's Content Security Policy.

    A workaround available today is to use a local copy of the topojson file from the app stage - I'm not an expert at Altair topo features but I know for example that plotly.express.choropleth_mapbox supports this.

    As an aside, to repro the example code provided you need to add from vega_datasets import data.