diff --git a/climate_emotions_map/app.py b/climate_emotions_map/app.py index 2f305c8..4d949c0 100644 --- a/climate_emotions_map/app.py +++ b/climate_emotions_map/app.py @@ -16,7 +16,7 @@ from . import utility as utils from .data_loader import NATIONAL_SAMPLE_SIZE, SURVEY_DATA -from .layout import SINGLE_SUBQUESTION_FIG_KW, construct_layout +from .layout import MAP_LAYOUT, SINGLE_SUBQUESTION_FIG_KW, construct_layout from .make_descriptive_plots import make_descriptive_plots from .make_map import make_map from .make_stacked_bar_plots import make_stacked_bar @@ -62,10 +62,22 @@ def update_state_and_disable_state_select_and_party_switch_interaction( and disable the party stratify switch when a specific state is selected (i.e., not None). """ if ctx.triggered_id == "us-map": - map_selected_state = figure["points"][0]["customdata"][0] - - if is_party_stratify_checked or map_selected_state == selected_state: + if is_party_stratify_checked: raise PreventUpdate + + point = figure["points"][0] + + # TODO: This is a temporary fix to handle the edge case where the exact same point (coords) + # on the map is selected twice, in which case the customdata key is for some reason missing + # from the clickData of the second click. + # This workaround assumes that this can only happen in cases where the clicked state is + # the same as the currently selected state, and thus will deselect the state in this case. + if "customdata" not in point: + return None, no_update, False, False + + map_selected_state = point["customdata"][0] + if map_selected_state == selected_state: + return None, no_update, False, False return ( map_selected_state, no_update, @@ -130,6 +142,7 @@ def update_sample_descriptive_plot(state): @callback( Output("impact-select", "value"), Input("question-select", "value"), + prevent_initial_call=True, ) def reset_impact_select(question_value): """Reset the impact select dropdown when a new question is selected.""" @@ -157,6 +170,7 @@ def update_map_title(impact, options): Input("question-select", "value"), Input("impact-select", "value"), ], + prevent_initial_call=True, ) def update_map_subtitle(question_value, impact): """Update the map subtitle based on the selected question and whether an impact is selected.""" @@ -191,8 +205,9 @@ def update_map(question_value, state, impact): outcome=DEFAULT_QUESTION["outcome"], clicked_state=state, impact=impact, + colormap_range_padding=MAP_LAYOUT["colormap_range_padding"], + margins=MAP_LAYOUT["margin"], # opinion_colormap=OPINION_COLORMAP, - show_impact_as_gradient=True, # impact_colormap=IMPACT_COLORMAP, ) @@ -259,6 +274,7 @@ def toggle_selected_question_bar_plot_visibility(impact): @callback( Output("all-questions-title", "children"), Input("state-select", "value"), + prevent_initial_call=True, ) def update_all_questions_title(state): """Update the title for the section for all questions based on the selected state.""" diff --git a/climate_emotions_map/layout.py b/climate_emotions_map/layout.py index 86d2285..229d12b 100644 --- a/climate_emotions_map/layout.py +++ b/climate_emotions_map/layout.py @@ -21,12 +21,15 @@ "weighted_descriptives": "*N are unweighted while proportions are weighted according to census estimates for age, sex, race, and ethnicity", } +MAP_LAYOUT = { + "margin": {"l": 15, "r": 15, "t": 15, "b": 15}, + "colormap_range_padding": 0, +} + SINGLE_SUBQUESTION_FIG_KW = { "fontsize": 10, - # NOTE: Can calculate same actual height as create_bar_plots_for_question with: - # ( default height - (default-set margin top) - (default-set margin bottom) ) - "height": 105, - "margin": {"l": 30, "r": 30, "t": 5, "b": 20}, + "height": 120, + "margin": {"l": 30, "r": 30, "t": 10, "b": 20}, } @@ -173,7 +176,7 @@ def create_design_credit(): long_credit = dmc.Text( children=[ dmc.Text( - "Alyssa Dai, Nikhil Bhagwat, Rémi Gau, Arman Jahanpour, Kendra Oudyk, Sebastian Urchs, Michelle Wang" + "Alyssa Dai, Michelle Wang, Nikhil Bhagwat, Arman Jahanpour, Kendra Oudyk, Rémi Gau, Sebastian Urchs" ), dmc.Anchor( "ORIGAMI Lab, PI: Jean-Baptiste Poline", @@ -326,22 +329,25 @@ def create_impact_dropdown(): def create_map_plot(): """Create the component holding the cloropleth map plot of US states.""" us_map = dmc.Container( - # TODO: Make map margins smaller (or create a param for this, maybe), and make figure height larger (?) dcc.Graph( id="us-map", figure=make_map( question=DEFAULT_QUESTION["question"], sub_question=DEFAULT_QUESTION["sub_question"], outcome=DEFAULT_QUESTION["outcome"], + colormap_range_padding=MAP_LAYOUT["colormap_range_padding"], + margins=MAP_LAYOUT["margin"], # opinion_colormap=OPINION_COLORMAP, ), # vh = % of viewport height # TODO: Revisit once plot margins are adjusted + config={"scrollZoom": False}, style={"height": "65vh"}, ), - # sets max width + # set max width # TODO: Revisit once plot margins are adjusted - size="lg", + # style={"maxWidth": "70vw"}, + size="xl", ) return us_map diff --git a/climate_emotions_map/make_map.py b/climate_emotions_map/make_map.py index fcc8664..56d587d 100644 --- a/climate_emotions_map/make_map.py +++ b/climate_emotions_map/make_map.py @@ -108,8 +108,8 @@ def make_map( # constants col_location = "state" col_color = "percentage" - col_color_opinion = f"{col_color} (opinion)" - col_color_impact = f"{col_color} (impact)" + col_color_opinion = f"{col_color}
(endorsement)" + col_color_impact = f"{col_color}
(weather event)" # default values if opinion_colormap is None: @@ -194,6 +194,9 @@ def make_map( colorbar_title=col_gradient.capitalize(), name="main_map", hoverinfo="none", # no hoverbox but click events are still emitted (?) + # TODO: revisit + # position colorbar closer to plot area (map) + colorbar={"x": 1}, ) # add outline for clicked state @@ -241,7 +244,7 @@ def make_map( hovertemplate=( "%{customdata[0]}" "
Sample size: %{customdata[1]}" - f"
{col_gradient.capitalize()}: %{{z:.1f}}%" + f"
{col_gradient.replace('
', ' ').capitalize()}: %{{z:.1f}}%" f"{hovertemplate_extra}" "" ), diff --git a/climate_emotions_map/make_stacked_bar_plots.py b/climate_emotions_map/make_stacked_bar_plots.py index d549c54..797c056 100644 --- a/climate_emotions_map/make_stacked_bar_plots.py +++ b/climate_emotions_map/make_stacked_bar_plots.py @@ -204,7 +204,6 @@ def plot_bars( category_orders = {facet_var: facet_order} # Define custom hover data - # TODO: Do we need the raw outcomes in the hover data? "outcome" / color column custom_data = ["full_text"] # hides the secondary box that appears when hovering over the bars hovertemplate = "
".join( diff --git a/climate_emotions_map/utility.py b/climate_emotions_map/utility.py index d6f354d..ff4694e 100644 --- a/climate_emotions_map/utility.py +++ b/climate_emotions_map/utility.py @@ -25,7 +25,6 @@ def get_state_options(): """Get the options for the state dropdown.""" - # TODO: The state values include cluster labels in parentheses, e.g. (Cluster F). Can remove from label if desired. return [ {"label": state, "value": state} for state in SURVEY_DATA["samplesizes_state.tsv"]["state"]