Interactive Multi-Filterable Dash Plotly GIS Map-- Figure Not Working

0

Hello Stack Exchange,

For the life of me I cannot figure out what I am doing wrong at this point. I'm a beginner programmer so i'm proud of getting here; however if there are other solutions that are more effective please feel free to suggest.

Data Files:

What I need the Dash app to do:

  • Work as a Multi-Filterable GIS Map in regards to fields listed (County, District Name, Age, RE, Quartiles, Housing Insecure)
  • Based on the filters chosen, update the px.choropleth_mapbox with the accurate count of 'points'.

Let's jump into the code. I'm also running this on a Google Colab Notebook.

filename_1 = root_path+"schoolDistrictBoundaries_Fixed.json"
file = open(filename_1)
schoolDistricts = gpd.read_file(file)

^^ being ran in a global cell

import dash
import dash.dependencies
import plotly.express as px
import pandas as pd
from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.graph_objects as go
import numpy as np

px.set_mapbox_access_token(mapbox_access_token)

###   Creating gdf_final_serial

geo_df = gpd.GeoDataFrame.from_features(geo_school["features"]).merge(joined, on="OBJECTID").set_index("OBJECTID") #GeoJson Join to Joined Dataframe
cleaned_geo_df = geo_df[["geometry_x", "CountyName_x", "RE", "QUARTILES", "HOUSING_INSECURE", "County", "Age", "DistrictName_x"]] #Picks the right columns
geo_df_final = cleaned_geo_df.rename(columns={"CountyName_x": "CountyName", "geometry_x": "geometry", 'DistrictName_x': 'DistrictName'}) #Changing Column Names
geo_df_final.to_pickle(root_path+"geo_df_final") #Write to drive as a pickle to serialize
df_final_serial = pd.read_pickle(root_path+"geo_df_final") #Read as DataFrame
gdf_final_serial = gpd.GeoDataFrame(data = df_final_serial, geometry= "geometry", crs = "epsg:4326") #Turn into GeoPandas DataFrame and set the geometry and crs


re_indicators = gdf_final_serial.RE.unique()
county_indicators = gdf_final_serial.CountyName.unique()
age_indicators = gdf_final_serial.Age.unique()
district_indicators = gdf_final_serial.DistrictName.unique()

external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]
app = JupyterDash(__name__, external_stylesheets=external_stylesheets)

app.layout = html.Div(
    [
        html.Div(
            children=[
                html.Label("County"),
                dcc.Checklist(
                    id="county",
                    options=[{"label": i, "value": i} for i in county_indicators],
                    value=[]
                ),
                html.Label("District Name"),
                dcc.Dropdown(
                    id="dname",
                    options=[],
                    value=[],
                    multi=True,
                ),
                html.Label("Age"),
                dcc.Dropdown(id="age", 
                             options=[], 
                             value=[],
                             multi = True),
                html.Label("RE"),
                dcc.Dropdown(id="re", 
                             options=[], 
                             value=[], 
                             multi = True),
                html.Label("Quartiles"),
                dcc.Dropdown(id="quartiles", 
                             options=[], 
                             value=[], 
                             multi=True),
                html.Label("Housing"),
                dcc.Dropdown(id="housing", 
                             options=[], 
                             value=[], 
                             multi=True),
                dcc.Graph(id="my_map", figure={})])
            ]
        )

@app.callback(
  dash.dependencies.Output("dname", "options"), 
  dash.dependencies.Input("county", "value")
)
def choose_county(county_pick): 
  if len(county_pick) > 0: 
      dff=gdf_final_serial[gdf_final_serial.CountyName.isin(county_pick)]
  else: 
      raise dash.exceptions.PreventUpdate
  return [{"label": i, "value": i} for i in (dff.DistrictName.unique())]

@app.callback(
  dash.dependencies.Output("dname", "value"),
  dash.dependencies.Input("dname", "options"),
)
def set_city_value(available_options_dname):
  return [x["value"] for x in available_options_dname]


@app.callback(
  dash.dependencies.Output("age", "options"), 
  dash.dependencies.Input("dname", "value")
)
def dname_age_picker(choose_dname):
  print(choose_dname)
  if len(choose_dname) > 0:
    dff = gdf_final_serial[gdf_final_serial.DistrictName.isin(choose_dname)]
  else:
    raise dash.exceptions.PreventUpdate
  return [{"label": i, "value": i} for i in (dff.Age.unique())]


@app.callback(
  dash.dependencies.Output("age", "value"),
  dash.dependencies.Input("age", "options"),
)
def set_age_value(available_options_age):
  return [x["value"] for x in available_options_age]


@app.callback(
  dash.dependencies.Output("re", "options"), 
  dash.dependencies.Input("age", "value")
)
def age_re_picker(choose_age):
  if len(choose_age) > 0:
    dff = gdf_final_serial[gdf_final_serial.Age.isin(choose_age)].dropna(axis = 0, how = 'any', subset = ["RE"])
  else:
    raise dash.exceptions.PreventUpdate
  return [{"label": i, "value": i} for i in (dff.RE.unique())]

@app.callback(
  dash.dependencies.Output("re", "value"),
  dash.dependencies.Input("re", "options")
)
def set_re_value(available_options_re):
  return [x["value"] for x in available_options_re]


@app.callback(
  dash.dependencies.Output("quartiles", "options"), 
  dash.dependencies.Input("re", "value")
)
def re_quartile_picker(choose_re_value):
  if len(choose_re_value) >= 0:
    dff = gdf_final_serial[gdf_final_serial.RE.isin(choose_re_value)].dropna(axis = 0, how = 'any', subset = ["QUARTILES"])
  else:
    raise dash.exceptions.PreventUpdate
  return [{"label": i, "value": i} for i in (dff.QUARTILES.unique())]

@app.callback(
  dash.dependencies.Output("quartiles", "value"),
  dash.dependencies.Input("quartiles", "options"),
)
def set_quart_value(available_options_quart):
  return [x["value"] for x in available_options_quart]

@app.callback(
  dash.dependencies.Output("housing", "options"), 
  dash.dependencies.Input("quartiles", "value")
)
def quart_picker(choose_quart_value):
  if len(choose_quart_value) >= 0:
      dff = gdf_final_serial[gdf_final_serial.QUARTILES.isin(choose_quart_value)]
  else:
      raise dash.exceptions.PreventUpdate
  return [{"label": i, "value": i} for i in (dff.HOUSING_INSECURE.unique())]

@app.callback(
  dash.dependencies.Output("housing", "value"),
  dash.dependencies.Input("housing", "options"),
)
def set_housing_value(available_options_housing):
  return [x["value"] for x in available_options_housing]

@app.callback(
  dash.dependencies.Output("my_map", "figure"),
  [dash.dependencies.Input("housing", "value"), 
  dash.dependencies.Input("quartiles", "value"), 
  dash.dependencies.Input("re", "value"),
  dash.dependencies.Input("age", "value"),
  dash.dependencies.Input("dname", "value"),
  dash.dependencies.Input("county", "value")]
)
def update_fig(selected_housing, selected_quartiles, selected_re, selected_age, selected_dname, selected_county):
  gdff_1 = gdf_final_serial[gdf_final_serial.CountyName.isin(selected_county) & 
                            gdf_final_serial.DistrictName.isin(selected_dname) &
                            gdf_final_serial.Age.isin(selected_age) &
                            gdf_final_serial.RE.isin(selected_re) &
                            gdf_final_serial.QUARTILES.isin(selected_quartiles) & 
                            gdf_final_serial.HOUSING_INSECURE.isin(selected_housing)]
  count_points = gdff_1.groupby("OBJECTID").size().rename("points")
  gdf_last = gdff_1.merge(count_points, on="OBJECTID", how="right", right_index = True)
  gdf_last.to_pickle(root_path+"gdf_last") #Write to drive as a pickle to serialize
  ddf_final_serial = pd.read_pickle(root_path+"gdf_last") #Read as DataFrame
  gddf_final_serial = gpd.GeoDataFrame(data = ddf_final_serial, geometry= "geometry", crs = "epsg:4326")
  gdff_2 = gddf_final_serial.head(50)
  px.set_mapbox_access_token(mapbox_access_token)
  fig = px.choropleth_mapbox(data_frame= gdff_2,
                             geojson= geo_school,
                             locations= 'DistrictName',
                             featureidkey = 'properties.DistrictName',
                             color="points",
                             center={"lat": 38.5941, "lon": -119.8815}, 
                             mapbox_style = 'dark').update_layout(mapbox_accesstoken =  mapbox_access_token)
  return fig
# Run app and display result inline in the notebook
if __name__ == "__main__":
  app.run_server(host="127.0.0.1", port="8888",debug=True, use_reloader=False)

I've been able to receive various conditions where the map/filter will produce a single county, sometimes two counties-- but never the whole shebang. Keep in mind my ultimate goal is to create this for the ENTIRE dataset (maybe using RapidsAI from NVIDIA; however i'm having trouble installing that as well within Google Colab).

What's more interesting is that when I run this code outside of dash (as a sample of the actual dataset) and pass it to fig.show(), it will show up; however, it won't work in the Dash app.

I suspect that the issue is either with my json handling or the final callback as the debugger indicates that while the inputs are there, my_map.figure isn't outputting the data. I appreciate the help.

bigdata gis json plotly-dash
2021-11-24 02:35:43
1

0
  1. It is hard to impossible currently do what you want in Colab because Plot.ly dash will run inline, but NOT as a Dash app dashboard due to the access restrictions Colab currently has for its instances. You should be using the RAPIDS install template for Colab, btw.
  2. We have something working similar to what you're working on, deployed in paperspace. We created a RAPIDS based tutorial. You can also take the code and deploy it elsewhere, if you'd like.
2021-11-24 20:30:46

In other languages

This page is in other languages

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................