diff --git a/newerhoods/components.R b/newerhoods/components.R new file mode 100644 index 0000000..faea941 --- /dev/null +++ b/newerhoods/components.R @@ -0,0 +1,155 @@ +header_nav <- withTags( + header(class ="header", + div(class="content col-xs-11", + div(class="navbar-wrapper", + a(href="https://www.twosigma.com/about/data-clinic/", target="_blank", + div(class="navbar-title", "NewerHoods"), + div(class="navbar-subtitle", "FROM TWO SIGMA DATA CLINIC") + ) + ) + ) + ) +) +### Modals +modal_features <- + bsModal( + id = "modal_features", + title="Getting started with NewerHoods", + body= includeMarkdown("markdowns/tutorial.md"), + size="medium", + trigger = "Help" + ) + +modal_plots <- + bs_modal( + id = "modal_plots", + title="Interpreting Plots", + body= includeMarkdown("markdowns/plots.md"), + size="medium" + ) + +modal_credits <- + bsModal( + id = "modal_credits", + title="NewerHoods", + body= includeMarkdown("markdowns/intro.md"), + size="medium", + trigger = "Credits" + ) + +modal_feedback <- + bsModal( + id = "modal_feedback", + title="Feedback", + body= includeMarkdown("markdowns/appendix.md"), + size="medium", + trigger = "Feedback" + ) + +### Info +info <- + div(class="text", + div("Choose characteristics to draw neighborhoods.") + ) + + +### Inputs +input_housing <- checkboxGroupInput( + inputId = 'housing',label="HOUSING", + choices=c("Age of buildings"="bldg_age","Median Sale Price"="sale_price"), + selected = "bldg_age" +) + +input_housing_sales <- conditionalPanel(condition="input.housing.includes('sale_price')", + radioButtons( + inputId = 'sales_features',label="", + choices=c("1y Average"="med_price_1y|sd_price_1y", + "3y Average"="med_price_3y|sd_price_3y", + "5y Average"="med_price_5y|sd_price_5y" + ),selected = NULL)) + + +input_crime <- + checkboxGroupInput( + inputId = 'crime_features', label="CRIME", + c("Violations"="violation_rate", + "Felonies"="felony_rate", + "Misdemeanors"="misdemeanor_rate" + ) + ) + +input_noise <- + checkboxGroupInput( + inputId = 'call_features',label="311 COMPLAINTS", + c("Ice Cream truck"="icecream_rate", + "Barking Dog"="animal_rate", + "Loud Music/party"="party_rate" + ) + ) + +input_clusters <- + sliderInput("num_clusters", + label="Number of neighborhoods", + ticks = FALSE, + min = 5, + max = 200, + value = 100) + +input_enable_heatmap <- + materialSwitch(inputId = "enable_heatmap", label = "Cluster map", status = "info") + +# info_plot_type <- shiny_iconlink() %>% +# bs_attach_modal(id_modal = "modal_plots") + +info_plot_type <- shiny_iconlink() %>% + bs_embed_tooltip(title="The cluster map shows the city divided into the selected neighborhoods. + The colors are only to differentiate clusters from one another. + The heatmap shows the relative value for clusters averaged over the + chosen characteristics.", + placement = "top") + +input_baseline <- + selectInput('baseline',label='Compare against', + choices=list("None"="none", + "Community Districts (59)"="cds", + "Public use Microdata Areas (55)"="pumas", + "Neighborhood Tabulation Areas (195)"="ntas", + "Police Precincts (77)"="precincts", + "School Districts (33)"="school_dists"), + selected = "none") + +map_control_panel <- div( + class="flex flex-between map-control", + div(class="xsflex", + input_clusters, + input_baseline + ), + div( + class="flex flex-end auto heatmap-group", + input_enable_heatmap, + div(class="heat-map-label", "Heat map"), + info_plot_type + ) +) + +help_link <- actionLink(inputId = "Help",label="Help") +feedback_link <- actionLink(inputId = 'Feedback',label="Feedback") +credits_link <- actionLink(inputId = 'Credits',label="About") + +intro_links <- + div(class="links flex", + div(class="mainlink",credits_link), + div(class="mainslink",help_link), + div(class="mainslink",feedback_link) + ) + +footer <- + div(class="footer", + div(class="content flex-between col-xs-11 xsflex", + div(class="links", a(href="https://www.twosigma.com/about/data-clinic/", "© 2019 Data Clinic. All rights reserved.")), + div(class="flex", + div(class="links", a(href="https://www.twosigma.com/legal-disclosure/", "Legal Disclosure", target="_blank")), + div(class="slink links", a(href="https://www.twosigma.com/legal-disclosure/privacy-policy/", "Privacy Policy", target="_blank")) + ) + ) + ) \ No newline at end of file diff --git a/newerhoods/markdowns/appendix.md b/newerhoods/markdowns/appendix.md index 53328d2..fcc34b6 100644 --- a/newerhoods/markdowns/appendix.md +++ b/newerhoods/markdowns/appendix.md @@ -1,17 +1,2 @@ -#### Data Sources - -1. [NYC Annualized Property Sales Data (2012-2017)](https://www1.nyc.gov/site/finance/taxes/property-annualized-sales-update.page) -2. [MapPLUTO (18v1)](https://www1.nyc.gov/site/planning/data-maps/open-data/dwn-pluto-mappluto.page) -3. [Geoclient API v1.1](https://developer.cityofnewyork.us/api/geoclient-api) -4. [Property Assessment Roll Archives](https://www1.nyc.gov/site/finance/taxes/property-assessment-roll-archives.page) -5. [NYPD Complaint Data Historic](https://data.cityofnewyork.us/Public-Safety/NYPD-Complaint-Data-Historic/qgea-i56i) -6. [311 Service Requests from 2010 to Present](https://data.cityofnewyork.us/Social-Services/311-Service-Requests-from-2010-to-Present/erm2-nwe9) - -#### References - -1. [ClustGeo: an R package for hierarchical clustering with spatial constraints](https://arxiv.org/abs/1707.03897) -2. [Making Neighborhoods - Understanding New York City Transitions 2000-2010](http://chpcny.org/assets/MakingNeighborhoodsPaper.pdf) - #### Have questions, feedback or wish to contribute? -![GitHub Logo](../images/GitHub-Mark-32px.png) [GitHub](https://github.com/tsdataclinic/new-neighborhoods) - +We invite [feedback](https://airtable.com/shr2sLGHHIiLY6BUC) on the tool and encourage users to contribute via NewerHoods’ [Github repository](https://github.com/tsdataclinic/newerhoods). To contact Data Clinic about NewerHoods, please email us at dataclinic@twosigma.com. diff --git a/newerhoods/markdowns/intro.html b/newerhoods/markdowns/intro.html index e7aa605..a66c5b4 100644 --- a/newerhoods/markdowns/intro.html +++ b/newerhoods/markdowns/intro.html @@ -372,17 +372,17 @@ -
-

NewerHoods

-
-

Redefining NYC neighborhood boundaries using open data and machine learning

+
+
Redefining NYC neighborhood boundaries using open data and machine learning

By Data Clinic ___

-

Purpose:

+
+
+

Purpose

New York City’s (NYC’s) neighborhoods are a driving force in the lives of New Yorkers—their identities are closely intertwined and a source of pride. However, the history and evolution of NYC’s neighborhoods don’t follow the rigid, cold lines of statistical and administrative boundaries. Instead, the neighborhoods we live and work in are the result of a more organic confluence of factors.

Data Clinic developed NewerHoods with the goal of helping individuals and organizations better advocate for their communities by enabling them to tailor insights to meet their specific needs. NewerHoods is an interactive web-app that uses open data to generate localized features at the census tract-level and machine learning to create homogeneous clusters. Users are able to select characteristics of interest (currently open data on housing, crime, and 311 complaints), visualize NewerHood clusters on an interactive map, find similar neighborhoods, and compare them against existing administrative boundaries. The tool is designed to enable users without in-depth data expertise to compare and incorporate these redefined neighborhoods into their work and life.

-

We invite feedback on the tool and encourage users to contribute via NewerHoods’ Github repository. To contact Data Clinic about NewerHoods, please email us at dataclinic@twosigma.com.

-
-
Data Sources
+
+ -

Have questions, feedback or wish to contribute?

-

GitHub Logo GitHub

-
+

We invite feedback on the tool and encourage users to contribute via NewerHoods’ Github repository. To contact Data Clinic about NewerHoods, please email us at dataclinic@twosigma.com.

diff --git a/newerhoods/markdowns/intro.md b/newerhoods/markdowns/intro.md index db32bde..0ba8c2e 100644 --- a/newerhoods/markdowns/intro.md +++ b/newerhoods/markdowns/intro.md @@ -1,15 +1,14 @@ -### NewerHoods -#### *Redefining NYC neighborhood boundaries using open data and machine learning* +##### *Redefining NYC neighborhood boundaries using open data and machine learning* By [Data Clinic](https://www.twosigma.com/about/data-clinic/) ___ -##### Purpose +#### Purpose New York City’s (NYC’s) neighborhoods are a driving force in the lives of New Yorkers—their identities are closely intertwined and a source of pride. However, the history and evolution of NYC’s neighborhoods don’t follow the rigid, cold lines of statistical and administrative boundaries. Instead, the neighborhoods we live and work in are the result of a more organic confluence of factors. Data Clinic developed NewerHoods with the goal of helping individuals and organizations better advocate for their communities by enabling them to tailor insights to meet their specific needs. NewerHoods is an interactive web-app that uses open data to generate localized features at the census tract-level and machine learning to create homogeneous clusters. Users are able to select characteristics of interest (currently open data on housing, crime, and 311 complaints), visualize NewerHood clusters on an interactive map, find similar neighborhoods, and compare them against existing administrative boundaries. The tool is designed to enable users without in-depth data expertise to compare and incorporate these redefined neighborhoods into their work and life. -##### Data Sources +#### Data Sources 1. [NYC Annualized Property Sales Data (2012-2017)](https://www1.nyc.gov/site/finance/taxes/property-annualized-sales-update.page) 2. [MapPLUTO (18v1)](https://www1.nyc.gov/site/planning/data-maps/open-data/dwn-pluto-mappluto.page) @@ -18,10 +17,8 @@ Data Clinic developed NewerHoods with the goal of helping individuals and organi 5. [NYPD Complaint Data Historic](https://data.cityofnewyork.us/Public-Safety/NYPD-Complaint-Data-Historic/qgea-i56i) 6. [311 Service Requests from 2010 to Present](https://data.cityofnewyork.us/Social-Services/311-Service-Requests-from-2010-to-Present/erm2-nwe9) -##### References +#### References 1. [ClustGeo: an R package for hierarchical clustering with spatial constraints](https://arxiv.org/abs/1707.03897) 2. [Making Neighborhoods - Understanding New York City Transitions 2000-2010](http://chpcny.org/assets/MakingNeighborhoodsPaper.pdf) -#### Have questions, feedback or wish to contribute? -We invite [feedback](https://airtable.com/shr2sLGHHIiLY6BUC) on the tool and encourage users to contribute via NewerHoods’ [Github repository](https://github.com/tsdataclinic/newerhoods). To contact Data Clinic about NewerHoods, please email us at dataclinic@twosigma.com. diff --git a/newerhoods/markdowns/tutorial.html b/newerhoods/markdowns/tutorial.html index 03e6bb4..31c08b6 100644 --- a/newerhoods/markdowns/tutorial.html +++ b/newerhoods/markdowns/tutorial.html @@ -372,36 +372,53 @@
-
-

How to use NewerHoods

-
-
-
#1 Select features of interest
-

You can start by browsing and selecting chararacteristics from the drop down menu and clicking the Select button. This selects the features which are clustered and an output map is produced. You can also use the same button to update your selection at any stage. To get more details on each of the features, use the information icon.

+

Follow these steps to begin creating NewerHoods of your own.

+
+

Step 1 Select features of interest

+

Select one or more chracteristics under Housing, Crime, and 311 Complaints from the sidebar and click Apply. The tool will return clusters based on your selection(s) and produce an output map. You can use the same button to update your selection at any stage. Details on each of the features are as below.

+
+
Housing
+
    +
  • Age of Building: The average age of residential real-estate from the time constructed.
  • +
  • Median Sale Price: The median price per square footage of real-estate sold in 2017, 3-year average for 2015-2017, and 5-year average for 2013-2017.
  • +
-
-
#2 Choose number of neighborhoods
-

Next, you can use the slider to choose the number of neighborhoods you want to see New York City divided up into. The map automatically updates everytime you change the number to neighborhoods to view. As a starting point, you can look at settings that correspond to existing administrative divisions of New York City.

+
+
Crime
+

Crimes are categorized into three levels of offense: felony, misdemeanor, and violation. The count of each of these types is calculated for each region and scaled by its respective population to obtain a rate.

    -
  • Community Districts: 59
  • -
  • Public Use Micordata Areas (PUMAs): 55
  • -
  • Neighborhood Tabulation Areas (NTAs): 195
  • -
  • Police Precincts: 77
  • -
  • School Districts: 33
  • +
  • Violations: Also known as infractions, these are the most minor of offenses. For example: a speeding ticket, public intoxication, or jaywalking.
  • +
  • Misdemeanors: More serious than violations but less severe than felonies, misdemeanors can carry up to a year in jail.
  • +
  • Felonies: These are the most serious type of criminal offense. Felonies often involve serious physical harm to victims, but they also include offenses like white collar crimes and fraud schemes.
-
-
#3 Dig into the numbers
-

Given a selection of features and number of neighborhoods, you can view the map of the NewerHoods. You can pan and zoom the map to your area of interest. Hovering over a cluster, you’ll find the NewerHood highlight and a pop-up window showing the average statistics of the characteristics chosen.

+
+
311 Calls
+

One of the largest categories of complaints received on 311 in NYC has to do with noise. In this case, we look at just a few sub-categories of noise complaints (ice cream trucks, barking dogs, and loud music/parties). As with crime statistics, these are aggregated for each region and scaled by the population.

+
-
-
#4 Check out different views
-

By choosing the Heatmap plot, you will find the NewerHoods coloured based on the magnitude of the values. When multiple features are chosen, the magnitude represent a combination of all the chosen features. These can be used to find broadly similar neighborhoods by simply looking at similarly coloured areas.

+
+

Step 2 Choose number of new neighborhoods

+

Next, you can use the slider to choose the number of NewerHoods into which you want to divide New York City. The map automatically updates every time you change the number to neighborhoods to view. As a starting point, consider settings that correspond to the city’s existing administrative divisions.

-
-
#5 Compare
-

Lastly, you can compare the NewerHoods to existing geographical boundaries. The chosen division appears on the map as a white dashed line.

+
+

Step 3 Dig into the numbers

+

Having generated a map of NewerHoods, you can pan and zoom the map to your area of interest. Hovering over a cluster, you’ll find the NewerHood highlight and a pop-up window showing the average statistics of the characteristics chosen.

+
+

Step 4 Check out different views

+

When you select the Heatmap plot, the NewerHoods will be colored according to the magnitude of the values. When multiple features are chosen, the magnitude represents a combination of all the chosen features. You can use them to find broadly similar neighborhoods by simply looking at similarly colored areas.

+
+
+

Step 5 Compare

+

Lastly, you can compare the NewerHoods to existing geographical boundaries. The chosen division appears on the map as a white dashed line. The available administrative divisions and their counts are as below:

+
    +
  • Community Districts: 59
  • +
  • Public Use Microdata Areas (PUMAs): 55
  • +
  • Neighborhood Tabulation Areas (NTAs): 195
  • +
  • Police Precincts: 77
  • +
  • School Districts: 33
  • +
diff --git a/newerhoods/markdowns/tutorial.md b/newerhoods/markdowns/tutorial.md index 0e12566..c67e5d7 100644 --- a/newerhoods/markdowns/tutorial.md +++ b/newerhoods/markdowns/tutorial.md @@ -1,12 +1,36 @@ -### How to use NewerHoods -___ -Follow these five simple steps to begin creating NewerHoods of your own. +Follow these steps to begin creating NewerHoods of your own. -##### Step 1 Select features of interest -To start building your own NewerHoods, browse and choose characteristics from the drop-down menu and click the Select button. The tool will return clusters based on your selection(s) and produce an output map. You can use the same button to update your selection at any stage. For more details on each of the features, click the information icon. +#### Step 1 Select features of interest +Select one or more chracteristics under Housing, Crime, and 311 Complaints from the sidebar and click Apply. The tool will return clusters based on your selection(s) and produce an output map. You can use the same button to update your selection at any stage. Details on each of the features are as below. -##### Step 2 Choose number of new neighborhoods -Next, you can use the slider to choose the number of NewerHoods into which you want to divide New York City. The map automatically updates every time you change the number to neighborhoods to view. As a starting point, consider settings that correspond to the city’s existing administrative divisions. +##### Housing + +- Age of Building: The average age of residential real-estate from the time constructed. +- Median Sale Price: The median price per square footage of real-estate sold in 2017, 3-year average for 2015-2017, and 5-year average for 2013-2017. + + +##### Crime +Crimes are categorized into three levels of offense: felony, misdemeanor, and violation. The count of each of these types is calculated for each region and scaled by its respective population to obtain a rate. + +- Violations: Also known as infractions, these are the most minor of offenses. For example: a speeding ticket, public intoxication, or jaywalking. +- Misdemeanors: More serious than violations but less severe than felonies, misdemeanors can carry up to a year in jail. +- Felonies: These are the most serious type of criminal offense. Felonies often involve serious physical harm to victims, but they also include offenses like white collar crimes and fraud schemes. + + +##### 311 Calls +One of the largest categories of complaints received on 311 in NYC has to do with noise. In this case, we look at just a few sub-categories of noise complaints (ice cream trucks, barking dogs, and loud music/parties). As with crime statistics, these are aggregated for each region and scaled by the population. + +#### Step 2 Choose number of new neighborhoods +Next, you can use the slider to choose the number of NewerHoods into which you want to divide New York City. The map automatically updates every time you change the number to neighborhoods to view. As a starting point, consider settings that correspond to the city’s existing administrative divisions. + +#### Step 3 Dig into the numbers +Having generated a map of NewerHoods, you can pan and zoom the map to your area of interest. Hovering over a cluster, you'll find the NewerHood highlight and a pop-up window showing the average statistics of the characteristics chosen. + +#### Step 4 Check out different views +When you select the Heatmap plot, the NewerHoods will be colored according to the magnitude of the values. When multiple features are chosen, the magnitude represents a combination of all the chosen features. You can use them to find broadly similar neighborhoods by simply looking at similarly colored areas. + +#### Step 5 Compare +Lastly, you can compare the NewerHoods to existing geographical boundaries. The chosen division appears on the map as a white dashed line. The available administrative divisions and their counts are as below: - Community Districts: 59 - Public Use Microdata Areas (PUMAs): 55 @@ -14,12 +38,3 @@ Next, you can use the slider to choose the number of NewerHoods into which you w - Police Precincts: 77 - School Districts: 33 -##### Step 3 Dig into the numbers -Having generated a map of NewerHoods, you can pan and zoom the map to your area of interest. Hovering over a cluster, you'll find the NewerHood highlight and a pop-up window showing the average statistics of the characteristics chosen. - -##### Step 4 Check out different views -When you select the Heatmap plot, the NewerHoods will be colored according to the magnitude of the values. When multiple features are chosen, the magnitude represents a combination of all the chosen features. You can use them to find broadly similar neighborhoods by simply looking at similarly colored areas. - -##### Step 5 Compare -Lastly, you can compare the NewerHoods to existing geographical boundaries. The chosen division appears on the map as a white dashed line. - diff --git a/newerhoods/server.R b/newerhoods/server.R index 61c1fac..d163e71 100644 --- a/newerhoods/server.R +++ b/newerhoods/server.R @@ -39,19 +39,6 @@ require(ClustGeo) source("settings_local.R") source("support_functions.R") -## function to add legend to plot -add_legend <- function(plot_type){ - proxy <- leafletProxy("map") - heatmap_palette <- c('#ffffb2','#fed976','#feb24c','#fd8d3c','#fc4e2a','#e31a1c','#b10026') - if(plot_type == "heat_map"){ - proxy %>% addLegend(position="bottomright", - colors = heatmap_palette[7:1], - labels = c("Low","","","","","","High")[7:1], - opacity = 1) - }else{ - proxy %>% clearControls()} -} - ## function to validate input validate_selection <- function(a,b,c){ if(a == "" & b == "" & c == ""){ @@ -62,6 +49,11 @@ validate_selection <- function(a,b,c){ ## loading pre-cleaned data load(file="clean_data/pre_compiled_data.RData") +load(file="clean_data/cds.RData") +load(file="clean_data/ntas.RData") +load(file="clean_data/precincts.RData") +load(file="clean_data/pumas.RData") +load(file="clean_data/school_dists.RData") ## Server function(input, output) { @@ -78,13 +70,18 @@ function(input, output) { # user_selection <- eventReactive(input$select,{ - selection <- paste0(c(input$crime_features,input$housing_features,input$call_features),collapse = "|") + selection <- c(input$crime_features,input$housing,input$call_features) + if("sale_price" %in% selection){ + selection <- selection[selection != "sale_price"] + selection <- c(selection,input$sales_features) + } + selection <- paste0(selection,collapse = "|") validate( - need(selection != "", "Please select atleast one feature") + need(selection != "", "Please select at least one feature") ) selection}, ignoreNULL = FALSE) # change to false for initial load - - + + tree <- eventReactive(user_selection(),{ features_to_use <- grepl(user_selection(),colnames(features)) @@ -180,11 +177,11 @@ function(input, output) { newerhoods <- reactive({ - plot_type <- input$plot_type + enable_heatmap <- input$enable_heatmap newerhoods <- clus_res() - if(plot_type=="heat_map"){ + if(enable_heatmap==TRUE){ heatmap_palette <- c('#ffffb2','#fed976','#feb24c', '#fd8d3c','#fc4e2a','#e31a1c','#b10026') pal_heatmap <- colorQuantile(heatmap_palette,newerhoods$dist, @@ -195,8 +192,8 @@ function(input, output) { map_cols <- c('#b7e882','#f4f062','#8fe2b4', '#237de0','#8dcd5c','#a327ad', '#d30000','#f9158d','#44b244', - '#2d5ead','#e8a0ea','#3ec8ed', - '#ea0a0a','#ef3fca','#efe8ab','#d87430') + '#0093ee','#136bb0','#1d4c7b', + '#1d293a','#017f7c','#015d5f','#003b3e') pal <- colorNumeric(map_cols, c(0:(length(map_cols)-1)), @@ -208,16 +205,6 @@ function(input, output) { }) - - ## To Do: Option to not have any baseline map - ### Baseline Map - - baseline_map <- eventReactive(input$baseline,{ - load(file=paste0("clean_data//",input$baseline,".RData")) - get(input$baseline) - }) - - ##### Interactive Map ##### output$map <- @@ -225,7 +212,7 @@ function(input, output) { leaflet() %>% setView(-73.885,40.71,11) %>% addProviderTiles("MapBox", options = providerTileOptions( - id= "mapbox.dark", + id= "mapbox.light", accessToken = Sys.getenv('MAPBOX_ACCESS_TOKEN')) ) %>% # addPolygons(data=census_tracts, @@ -237,14 +224,15 @@ function(input, output) { # ) %>% addPolygons(data=newerhoods(), fillColor = newerhoods()$colour, - weight = 0.5, + stroke = TRUE, + weight = 1, opacity = 0.5, - color = "white", + color = "lightgrey", fillOpacity = 0.6, ## highights highlight = highlightOptions( weight = 4, - color = ifelse(input$plot_type == "cluster_map","orange","green"), + color = ifelse(input$enable_heatmap == FALSE,"orange","green"), opacity = 0.8, fillOpacity = 0.6, bringToFront = F), @@ -255,31 +243,65 @@ function(input, output) { padding = "3px 8px"), textsize = "15px", direction = "auto") - ) %>% - addPolylines(data=baseline_map(), - stroke=TRUE, - weight=2.75, - opacity=0.75, - color="white", - dashArray="4") + ) }) - + ## To do: Simplify this. Feels like too many observes - observeEvent(input$select,{ - add_legend(input$plot_type) - }) + ## Adding Heatmap Legend + ## function to add legend to plot + ## function to add legend to plot + add_legend <- function(enable_heatmap){ + proxy <- leafletProxy("map") + # heatmap_palette <- c('#0093ee','#136bb0','#1d4c7b','#017f7c','#015d5f','#003b3e', '#1d293a') + heatmap_palette <- c('#ffffb2','#fed976','#feb24c', + '#fd8d3c','#fc4e2a','#e31a1c','#b10026') + if(enable_heatmap == TRUE){ + proxy %>% addLegend(position="bottomright", + colors = heatmap_palette[7:1], + labels = c("Low","","","","","","High")[7:1], + opacity = 1) + }else{ + proxy %>% clearControls()} + } - observe({ - add_legend(input$plot_type) - }) + observe({add_legend(input$enable_heatmap)}) + observeEvent(clus_res(),{add_legend(input$enable_heatmap)}) - observeEvent(clus_res(),{ - add_legend(input$plot_type) - }) - observeEvent(baseline_map(),{ - add_legend(input$plot_type) - }) + ### Baseline Map + baselines <- c("cds","ntas","pumas","precincts","school_dists") + add_baselines <- function(){ + for(i in c(1:length(baselines))){ + baseline_map <- get(baselines[i]) + proxy <- leafletProxy("map") + proxy %>% + addPolylines(data=baseline_map, + stroke=TRUE, + weight=2, + opacity=0.75, + color="white", + dashArray="5", + group = baselines[i]) + if(baselines[i] != input$baseline){ + proxy %>% hideGroup(baselines[i]) + } + } + } + + observeEvent({input$enable_heatmap},{add_baselines()}) + observeEvent({clus_res()},{add_baselines()}) + observeEvent(input$baseline,{ + if(input$baseline != "none"){ + baseline_map <- get(input$baseline) + + proxy <- leafletProxy("map") + proxy %>% hideGroup(baselines) + proxy %>% showGroup(input$baseline) + }else{ + proxy <- leafletProxy("map") + proxy %>% hideGroup(baselines) + } + }) } diff --git a/newerhoods/ui.R b/newerhoods/ui.R index 6a85a52..17b107e 100644 --- a/newerhoods/ui.R +++ b/newerhoods/ui.R @@ -19,121 +19,54 @@ require(markdown) require(shinycssloaders) ### Definitions - -### Modals -modal_features <- - bs_modal( - id = "modal_features", - title="Understanding Features", - body= includeMarkdown("markdowns/features.md"), - size="medium" - ) - -modal_plots <- - bs_modal( - id = "modal_plots", - title="Interpreting Plots", - body= includeMarkdown("markdowns/plots.md"), - size="medium" - ) - -### Inputs -input_housing <- - pickerInput(inputId = 'housing_features',label="Features to use", - choices=list("2017 Median Sale Price"="med_price_1y|sd_price_1y", - "2015-17 Median Sale Price"="med_price_3y|sd_price_3y", - "2013-17 Median Sale Price"="med_price_5y|sd_price_5y", - #"No. of Residential units"="res_units", - "Age of buildings"="bldg_age"), - options=list(`actions-box`=TRUE,title="Housing Characteristics"), - multiple=TRUE, selected = "med_price_1y|sd_price_1y" - ) %>% - shinyInput_label_embed( - shiny_iconlink() %>% - bs_attach_modal(id_modal = "modal_features") - ) - -input_crime <- - pickerInput(inputId = 'crime_features',#label=h6("Crime"), - choices=list("Violations"="violation_rate", - "Felonies"="felony_rate", - "Misdemeanors"="misdemeanor_rate"), - options=list(`actions-box`=TRUE,title="Crime Characteristics"), - multiple=TRUE) - -input_noise <- - pickerInput(inputId = 'call_features',#label=h6("311 Complaints"), - choices=list("Ice Cream truck"="icecream_rate", - "Barking Dog"="animal_rate", - "Loud Music/party"="party_rate"), - options=list(`actions-box`=TRUE,title="311 Noise Complaints"), - multiple=TRUE) - -input_clusters <- - sliderInput("num_clusters", - label="Number of NewerHoods", - min = 5, - max = 200, - value = 100) - -input_plot_type <- - radioGroupButtons(inputId = "plot_type",label="Plot type", - choices = list("Cluster Map"="cluster_map","Heatmap"="heat_map"), - justified = TRUE,status="primary" - ) %>% - shinyInput_label_embed( - shiny_iconlink() %>% - bs_attach_modal(id_modal = "modal_plots") - ) - -input_baseline <- - selectInput('baseline',label='Compare against', - choices=list("Community Districts (59)"="cds", - "Public use Microdata Areas (55)"="pumas", - "Neighborhood Tabulation Areas (195)"="ntas", - "Police Precincts (77)"="precincts", - "School Districts (33)"="school_dists")) +source("components.R") # UI -tagList( - navbarPage( - - theme = shinytheme("cerulean"), - - "NewerHoods", - - tabPanel("Map", - - ## add modals - modal_features, - modal_plots, - - ## Sidebar - sidebarPanel( - input_housing, - input_crime, - input_noise, - actionButton("select","Apply",class="btn-primary"), - bsTooltip("select", "Click to select or update features to be used for clustering", - "right", options = list(container = "body")), - br(), - br(), - input_clusters, - input_plot_type, - input_baseline - ), - mainPanel( - withSpinner(leafletOutput("map", height = "535"),type=5) - # leafletOutput("map", height = "535") - )), - tabPanel("Help", includeMarkdown("markdowns/tutorial.md")), - tabPanel("About",includeMarkdown("markdowns/intro.md"),width=4), - +bootstrapPage( + theme = "custom.css", + title = "NewerHoods", + tags$head( + tags$link(rel = "stylesheet", type = "text/css", href = "https://fonts.googleapis.com/css?family=Lato:300,400,400italic,500,500italic,700,700italic,900") + ), + header_nav, + div( + class="main col-xs-11", + div( + class="sidebar", + div(class="orange-border", ""), + div( + class="well", + ## add modals + modal_features, + modal_credits, + modal_plots, + modal_feedback, + info, + input_housing, + input_housing_sales, + input_crime, + input_noise, + actionButton("select","Apply",class="btn-custom") + ), + intro_links + ), + div( + class="map-content", + div(class="blue-border", ""), + div( + class="map custom", + withSpinner(leafletOutput("map", height = "535"),type=3, + color.background = "white",color="#0099a6"), + map_control_panel + ), + bsTooltip("select", "Click to select or update features to be used for clustering", + "right", options = list(container = "body")) + ) + ), + footer, # activate tooltips, popovers use_bs_tooltip(), use_bs_popover() - - ) ) diff --git a/newerhoods/www/assets/border-blue.svg b/newerhoods/www/assets/border-blue.svg new file mode 100644 index 0000000..b08b360 --- /dev/null +++ b/newerhoods/www/assets/border-blue.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/newerhoods/www/assets/border-orange.svg b/newerhoods/www/assets/border-orange.svg new file mode 100644 index 0000000..07c7f98 --- /dev/null +++ b/newerhoods/www/assets/border-orange.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/newerhoods/www/custom.css b/newerhoods/www/custom.css new file mode 100644 index 0000000..05a970f --- /dev/null +++ b/newerhoods/www/custom.css @@ -0,0 +1,430 @@ +html { + min-height: 100%; +} + +body { + background-color: #fff7e3; + color: #1d1e1f; + font-family: Lato; + display: grid; + grid-template-rows: 80px 1fr 130px; + grid-template-columns: 1fr; + min-height: 100vh; + display: -ms-grid; + -ms-grid-rows: 80px 1fr 130px; + -ms-grid-columns: 1fr; +} + +a +{ + color: #0099a6; +} + +.hint { + color: #929292; +} + +.mainlink { + color: #0099a6; + padding-right: 15px; + font-weight: bold; +} + +.mainlink:hint { + color: #0099a6; +} + +.link { + color: #0099a6; + padding-right: 15px; +} + +.link:focus, +.link:active, +.link:hover, +a:focus, +a:active, +a:hover { + color: #1d1e1f; + text-decoration: none; + cursor: pointer; +} + +/* custom checkbox */ +.checkbox input[type="checkbox"] { + opacity: 0; +} + +.checkbox span { + position: relative; + padding-left: 5px; +} + +.checkbox span:before { + content: ""; + position: absolute; + top: 0; + left: -19px; + width: 16px; + height: 16px; + z-index: 0; + border: 1px solid #c8c8c8; + background-color: #ffffff; + border-radius: 2px; + -webkit-transition: border 0.25s, background-color 0.25s, width 0.2s 0.1s, + height 0.2s 0.1s, top 0.2s 0.1s, left 0.2s 0.1s; + transition: border 0.25s, background-color 0.25s, width 0.2s 0.1s, + height 0.2s 0.1s, top 0.2s 0.1s, left 0.2s 0.1s; +} + +.checkbox [type="checkbox"]:checked + span:before { + border: 2px solid #e27327; + background-color: #e27327; +} + +.checkbox [type="checkbox"]:checked + span:after { + content: ""; + position: absolute; + top: 0; + left: -19px; + width: 8px; + height: 13px; + border-top: 2px solid transparent; + border-left: 2px solid transparent; + border-right: 2px solid #fff; + border-bottom: 2px solid #fff; + -webkit-transform: rotateZ(37deg); + transform: rotateZ(37deg); + -webkit-transform-origin: 100% 100%; + transform-origin: 100% 100%; +} + +/* custom radio */ +.radio input[type="radio"] { + opacity: 0; +} + +.radio [type="radio"] + span:before, +.radio [type="radio"] + span:after { + border: 2px solid #c8c8c8; + border-radius: 50%; + content: ""; + position: absolute; + top: 2px; + left: 0px; + width: 16px; + height: 16px; + transition: 0.28s ease; +} + +.radio [type="radio"]:checked + span:before, +.radio [type="radio"]:checked + span:after { + border: 2px solid #e27327; + background-color: #e27327; +} + +.radio [type="radio"]:checked + span:after { + transform: scale(0.7); + border: 2px solid #fff; +} + +/* layout */ +.flex, +.xsflex { + display: flex; +} + +.flex-between { + justify-content: space-between; + align-items: center; +} + +.flex-end { + justify-content: flex-end; + align-items: center; +} + +.mainslink { + color: #929292; + margin-right: 15px; + font-weight: bold; +} + +.mainslink::before { + content: "\2022"; + font-size: 8px; + position: relative; + top: -3px; + right: 5px; +} + +.slink { + color: #929292; + margin-right: 15px; +} + +.slink::before { + content: "\2022"; + font-size: 8px; + position: relative; + top: -3px; + right: 5px; +} + +.links { + padding-left: 15px; + list-style-type: none; + font-size: 14px; +} + +.header { + background-color: #eae2d0; + box-shadow: inset 0 -1px 0 0 rgba(0, 0, 0, 0.07); + padding: 10px 0 20px; + grid-row: 1; + grid-column: 1; + -ms-grid-column: 1; + -ms-grid-row: 1; +} + +.content { + float: none; + padding-left: 0; + padding-right: 0; + margin: 0 auto; +} + +.navbar-wrapper { + width: 200px; +} + +.navbar-title { + opacity: 0.7; + font-size: 24px; + font-weight: 300; + color: #1d1e1f; +} + +.heat-map-label { + position: relative; + left: -10px; +} + +.navbar-subtitle { + opacity: 0.7; + font-size: 11px; + font-weight: 900; + color: #1d1e1f; + letter-spacing: 1.1px; + padding-top: 4px; +} + +.main { + padding: 4rem 0; + margin: 0 auto; + float: none; + display: flex; + grid-row: 2; + grid-column: 1; + -ms-grid-column: 1; + -ms-grid-row: 2; +} + +.sidebar { + width: 320px; + margin-right: 20px; +} + +.well { + background-color: white; + box-shadow: 1px 1px 1px 0 rgba(0, 0, 0, 0.13); + border-radius: 3px; + border: none; + padding: 24px 16px; + font-size: 14px; + margin-bottom: 10px; +} + +.map-content { + flex: 1; +} + +.map { + padding: 24px 12px 8px; + background-color: white; + box-shadow: 1px 1px 1px 0 rgba(0, 0, 0, 0.13); +} + +.control-label { + opacity: 0.7; + font-size: 13px; + font-weight: 900; + margin-top: 12px; +} + +.btn-custom { + border-radius: 2px; + border: solid 2px #e27327; + color: #e27327; + text-transform: uppercase; + font-weight: bold; + font-size: 13px; + line-height: 1.54; + letter-spacing: 0.6px; +} + +.btn-custom:hover, +.btn-custom:focus { + background-color: #e27327; + color: white; + border: solid 2px #e27327; +} + +.orange-border { + height: 4px; + background-image: url("assets/border-orange.svg"); +} + +.blue-border { + height: 4px; + background-image: url("assets/border-blue.svg"); +} + +.leaflet-control-attribution { + display: none; +} + +.label-info { + background-color: #0099a6; +} + +.material-switch > label::after { + background-color: #0099a6; + box-shadow: none; +} + +#sales_features > label { + display: none; +} + +/* slider style override */ + +.irs { + width: 200px; + margin-right: 30px; +} + +.irs-bar { + border-top: 1px solid #0099a6; + border-bottom: 1px solid #0099a6; + background: #0099a6; +} + +.irs-min, +.irs-max { + background: none; + color: #929292; +} + +.irs-single { + background: none; + color: #0099a6; +} + +.irs-line, +.irs-bar, +.irs-bar-edge { + top: 20px; +} + +.irs-slider { + top: 13px; + box-shadow: none; + background: white; +} + +.irs-slider:hover, +.irs-slider:active, +.irs-slider:focus { + background: #0099a6; +} + +/* custom input override */ +.custom .shiny-input-container:not(.shiny-input-container-inline) { + width: auto; + max-width: none; +} + +.custom .form-group { + margin-bottom: 16px; + margin-top: 16px; + display: flex; + align-items: center; +} + +.custom label { + min-width: 60px; + opacity: 1; + margin-right: 10px; +} + +.custom .shiny-input-container > div:not(.material-switch) { + width: 230px; +} + +.selectize-control { + margin-bottom: 0; +} + +.auto .form-group { + width: auto; +} + +.footer { + background-color: #e7e0d0; + padding: 40px 0 70px; + grid-row: 3; + grid-column: 1; + -ms-grid-column: 1; + -ms-grid-row: 3; +} + +.heatmap-group { + width: 233px; + min-width: 233px; +} + +.tooltip { + min-width: 240px; +} + +.tooltip-inner { + text-align: left; +} + +.form-group.shiny-input-radiogroup.shiny-input-container.shiny-bound-input { + padding-left: 25px; +} + +/* responsive */ +@media (max-width: 1280px) { + .xsflex { + display: block; + } +} + +@media (max-width: 1024px) { + .sidebar { + width: 260px; + margin-right: 10px; + } +} + +@media (max-width: 767px) { + .sidebar { + width: 100%; + } + .main { + display: block; + } +} diff --git a/newerhoods/www/custom.js b/newerhoods/www/custom.js new file mode 100644 index 0000000..9491d66 --- /dev/null +++ b/newerhoods/www/custom.js @@ -0,0 +1,18 @@ +$(document).ready(function() { + setTimeout(function() { + shinyBS.addTooltip("select", "tooltip", { + container: "body", + placement: "right", + trigger: "hover", + title: "Click to select or update features to be used for clustering" + }); + }, 500); +}); + +$(function() { + $('[data-toggle="tooltip"]').tooltip(); +}); + +$(function() { + $('[data-toggle="popover"]').popover(); +}); diff --git a/newerhoods/www/logo.svg b/newerhoods/www/logo.svg new file mode 100644 index 0000000..84bdf87 --- /dev/null +++ b/newerhoods/www/logo.svg @@ -0,0 +1,7 @@ + + Logo + + + + + \ No newline at end of file