-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #104 from Project-Resilience/filter-app
Filter app
- Loading branch information
Showing
134 changed files
with
1,177 additions
and
907 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,8 +5,6 @@ recursive=y | |
|
||
fail-under=9.7 | ||
|
||
jobs=0 | ||
|
||
max-line-length=120 | ||
|
||
suggestion-mode=yes | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,118 +1,49 @@ | ||
""" | ||
Main app file for ELUC demo. | ||
Uses many 'components' to separate divs and their related callbacks. | ||
They aren't necessarily truly reusable components, but they help to organize the code. | ||
Main entrypoint to run the app. Contains the layout of the app and registers all the callbacks of each component. | ||
""" | ||
import pandas as pd | ||
from dash import Dash | ||
from dash import dcc | ||
from dash import html | ||
from dash import Dash, html | ||
import dash_bootstrap_components as dbc | ||
import pandas as pd | ||
|
||
import app.constants as app_constants | ||
from app.components.chart import ChartComponent | ||
from app.components.legend import LegendComponent | ||
from app.components.lock import LockComponent | ||
from app.components.map import MapComponent | ||
from app.components.prediction import PredictionComponent | ||
from app.components.prescription import PrescriptionComponent | ||
from app.components.intro import IntroComponent | ||
from app.components.context.context import ContextComponent | ||
from app.components.filter import FilterComponent | ||
from app.components.dms.dms import DMSComponent | ||
from app.components.references import ReferencesComponent | ||
from app.components.sliders import SlidersComponent | ||
from app.components.trivia import TriviaComponent | ||
from app.utils import EvolutionHandler | ||
|
||
|
||
app = Dash(__name__, | ||
external_stylesheets=[dbc.themes.BOOTSTRAP, dbc.icons.BOOTSTRAP], | ||
prevent_initial_callbacks="initial_duplicate") | ||
server = app.server | ||
|
||
df = pd.read_csv(app_constants.DATA_FILE_PATH, index_col=app_constants.INDEX_COLS) | ||
|
||
legend_component = LegendComponent() | ||
|
||
map_component = MapComponent(df) | ||
map_component.register_update_map_callback(app) | ||
map_component.register_click_map_callback(app) | ||
map_component.register_select_country_callback(app) | ||
|
||
prescription_component = PrescriptionComponent(df) | ||
prescription_component.register_select_prescriptor_callback(app) | ||
prescription_component.register_toggle_modal_callback(app) | ||
|
||
sliders_component = SlidersComponent(df) | ||
sliders_component.register_set_frozen_reset_sliders_callback(app) | ||
sliders_component.register_show_slider_value_callback(app) | ||
sliders_component.register_sum_to_one_callback(app) | ||
|
||
lock_component = LockComponent() | ||
|
||
chart_component = ChartComponent(df) | ||
chart_component.register_update_context_chart_callback(app) | ||
chart_component.register_update_presc_chart_callback(app) | ||
app.title = 'Land Use Optimization' | ||
|
||
prediction_component = PredictionComponent(df) | ||
prediction_component.register_predictor_callback(app) | ||
prediction_component.register_land_use_callback(app) | ||
app_df = pd.read_csv("app/data/app_data.csv", index_col=app_constants.INDEX_COLS) | ||
|
||
trivia_component = TriviaComponent(df) | ||
trivia_component.register_update_trivia_callback(app) | ||
evolution_handler = EvolutionHandler() | ||
|
||
intro_component = IntroComponent() | ||
context_component = ContextComponent(app_df, evolution_handler) | ||
filter_component = FilterComponent(evolution_handler) | ||
dms_component = DMSComponent(app_df, evolution_handler) | ||
references_component = ReferencesComponent() | ||
|
||
app.title = 'Land Use Optimization' | ||
app.css.config.serve_locally = False | ||
# Don't be afraid of the 3rd party URLs: chriddyp is the author of Dash! | ||
# These two allow us to dim the screen while loading. | ||
# See discussion with Dash devs here: https://community.plotly.com/t/dash-loading-states/5687 | ||
app.css.append_css({'external_url': 'https://codepen.io/chriddyp/pen/bWLwgP.css'}) | ||
app.css.append_css({'external_url': 'https://codepen.io/chriddyp/pen/brPBPO.css'}) | ||
|
||
app.layout = html.Div([ | ||
dcc.Markdown(''' | ||
# Land Use Optimization | ||
This site is for demonstration purposes only. | ||
For a given context cell representing a portion of the earth, | ||
identified by its latitude and longitude coordinates, and a given year: | ||
* What changes can we make to the land usage | ||
* In order to minimize the resulting estimated CO2 emissions? (Emissions from Land Use Change, ELUC, | ||
in tons of carbon per hectare) | ||
context_component.register_callbacks(app) | ||
filter_component.register_callbacks(app) | ||
dms_component.register_callbacks(app) | ||
|
||
'''), | ||
dcc.Markdown('''## Context'''), | ||
html.Div([ | ||
dcc.Graph(id="map", figure=map_component.get_map_fig(), style={"grid-column": "1"}), | ||
html.Div([map_component.get_context_div()], style={"grid-column": "2"}), | ||
html.Div([legend_component.get_legend_div()], style={"grid-column": "3"}) | ||
], style={"display": "grid", "grid-template-columns": "auto 1fr auto", 'position': 'relative'}), | ||
dcc.Markdown('''## Actions'''), | ||
html.Div([ | ||
html.Div([prescription_component.get_presc_select_div()], style={"grid-column": "1"}), | ||
html.Div([chart_component.get_chart_select_div()], | ||
style={"grid-column": "2", "margin-top": "-10px", "margin-left": "10px"}), | ||
], style={"display": "grid", "grid-template-columns": "45% 15%"}), | ||
html.Div([ | ||
html.Div(lock_component.get_checklist_div(), style={"grid-column": "1", "height": "100%"}), | ||
html.Div(sliders_component.get_sliders_div(), style={'grid-column': '2'}), | ||
dcc.Graph(id='context-fig', | ||
figure=chart_component.create_treemap(type_context=True), | ||
style={'grid-column': '3'}), | ||
dcc.Graph(id='presc-fig', | ||
figure=chart_component.create_treemap(type_context=False), | ||
style={'grid-clumn': '4'}) | ||
], style={'display': 'grid', 'grid-template-columns': '4.5% 40% 1fr 1fr', "width": "100%"}), | ||
# The above line can't be set to auto because the lines will overflow! | ||
html.Div([ | ||
sliders_component.get_frozen_div(), | ||
html.Button("Sum to 100%", id='sum-button', n_clicks=0), | ||
html.Div(id='sum-warning') | ||
]), | ||
dcc.Markdown('''## Outcomes'''), | ||
prediction_component.get_predict_div(), | ||
dcc.Markdown('''## Trivia'''), | ||
trivia_component.get_trivia_div(), | ||
dcc.Markdown('''## References'''), | ||
references_component.get_references_div() | ||
], style={'padding-left': '10px'},) | ||
app.layout = html.Div( | ||
children=[ | ||
intro_component.get_div(), | ||
context_component.get_div(), | ||
filter_component.get_div(), | ||
dms_component.get_div(), | ||
references_component.get_references_div() | ||
] | ||
) | ||
|
||
if __name__ == '__main__': | ||
app.run_server(host='0.0.0.0', debug=False, port=4057, use_reloader=False, threaded=False) | ||
app.run_server(host='0.0.0.0', debug=False, port=4057, use_reloader=False, threaded=True) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
/* body { | ||
background-image: url("https://upload.wikimedia.org/wikipedia/commons/thumb/1/1f/Icelandic_Landscape_near_Neskaupsta%C3%B0ur_July_2014.JPG/640px-Icelandic_Landscape_near_Neskaupsta%C3%B0ur_July_2014.JPG"); | ||
background-size: cover; | ||
background-position: center; | ||
background-attachment: fixed; | ||
} */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
window.dccFunctions = window.dccFunctions || {}; | ||
window.dccFunctions.percentSlider = function(value) { | ||
return Math.round(value * 100); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
""" | ||
Context component class for selecting context. | ||
""" | ||
from dash import html, dcc, Output, Input | ||
import dash_bootstrap_components as dbc | ||
import pandas as pd | ||
import regionmask | ||
|
||
from app.components.context.map import MapComponent | ||
from app.utils import EvolutionHandler | ||
from data import constants | ||
|
||
|
||
class ContextComponent(): | ||
""" | ||
Component containing map as well as dropdowns and input fields for picking a more specific context. | ||
""" | ||
def __init__(self, app_df: pd.DataFrame, handler: EvolutionHandler): | ||
self.map_component = MapComponent(app_df) | ||
self.app_df = app_df | ||
self.handler = handler | ||
self.countries_df = regionmask.defined_regions.natural_earth_v5_0_0.countries_110.to_dataframe() | ||
|
||
def create_label_and_value(self, label: str, value: html.Div) -> html.Div: | ||
""" | ||
Standard dash function that pairs a label with any arbitrary value Div. | ||
""" | ||
div = html.Div( | ||
className="d-flex flex-row", | ||
children=[ | ||
html.Label(label, className="w-25"), | ||
html.Div( | ||
value, | ||
className="flex-grow-1" | ||
) | ||
] | ||
) | ||
return div | ||
|
||
def get_div(self): | ||
""" | ||
Returns the entire context div to display in the app. | ||
""" | ||
div = html.Div( | ||
className="mb-5 mx-5", | ||
children=[ | ||
dbc.Row( | ||
children=[ | ||
dbc.Col( | ||
width={"offset": 3, "size": 3}, | ||
children=[ | ||
dcc.Graph(id="map", figure=self.map_component.get_map_fig()), | ||
dcc.Dropdown( | ||
id="loc-dropdown", | ||
options=list(self.map_component.countries_df["names"]), | ||
value=list(self.map_component.countries_df["names"])[143] | ||
) | ||
] | ||
), | ||
dbc.Col( | ||
width=3, | ||
children=[ | ||
html.B("1. Select a land area on the map to optimize or manually enter coordinates."), | ||
self.create_label_and_value( | ||
"Latitude", | ||
dcc.Dropdown( | ||
id="lat-dropdown", | ||
options=[{"label": lat, | ||
"value": lat} for lat in self.map_component.lat_list], | ||
value=51.625, | ||
) | ||
), | ||
self.create_label_and_value( | ||
"Longitude", | ||
dcc.Dropdown( | ||
id="lon-dropdown", | ||
options=[{"label": lon, | ||
"value": lon} for lon in self.map_component.lon_list], | ||
value=-3.375, | ||
) | ||
), | ||
self.create_label_and_value( | ||
"Year", | ||
html.Div([ | ||
dcc.Input( | ||
id="year-input", | ||
type="number", | ||
value=2021, | ||
debounce=True | ||
), | ||
dcc.Tooltip(f"Year must be between \ | ||
{self.map_component.min_time} and \ | ||
{self.map_component.max_time}.") | ||
]) | ||
) | ||
] | ||
) | ||
] | ||
) | ||
] | ||
) | ||
return div | ||
|
||
def register_callbacks(self, app): | ||
""" | ||
Registers callbacks to make app interactive. Registers old map callbacks as well as new one to run prescription. | ||
""" | ||
self.map_component.register_click_map_callback(app) | ||
self.map_component.register_select_country_callback(app) | ||
self.map_component.register_update_map_callback(app) | ||
|
||
@app.callback( | ||
Output("results-store", "data"), | ||
Input("year-input", "value"), | ||
Input("lat-dropdown", "value"), | ||
Input("lon-dropdown", "value") | ||
) | ||
def run_prescription(year: int, lat: float, lon: float) -> dict[str: list]: | ||
""" | ||
Runs prescription for the selected context on all prescriptors. Returns the results as a json to a store. | ||
""" | ||
condition = (self.app_df["time"] == year) & (self.app_df["lat"] == lat) & (self.app_df["lon"] == lon) | ||
context_df = self.app_df[condition] | ||
context_df = context_df[constants.CAO_MAPPING["context"]].iloc[0:1] | ||
results_df = self.handler.prescribe_all(context_df) | ||
results_json = results_df.to_dict(orient="records") | ||
return results_json |
Oops, something went wrong.