-
Notifications
You must be signed in to change notification settings - Fork 1
/
app.R
369 lines (325 loc) · 18.7 KB
/
app.R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
#BM: it's nice for other users if we auto-install missing packages
#for them
load_libraries <- function(x){
for( i in x ){
# require returns TRUE invisibly if it was able to load package
if( ! require( i , character.only = TRUE ) ){
# If package was not able to be loaded then re-install
install.packages( i , dependencies = TRUE )
# Load package after installing
require( i , character.only = TRUE )
}
}
}
load_libraries( c("shiny" , "here" , "plotly", "leaflet",
"devtools", "maps", "sp", "maptools",
"tmap", "cartogram", "DT", "dplyr") )
# seems like we need to have library calls so that shinyapps.io can detect what pkgs to
# install
library("shiny")
library("here")
library("plotly")
library("leaflet")
#library("devtools")
library("maps")
library("sp")
library("maptools")
#library("tmap")
#library("cartogram")
library("DT")
library("dplyr")
#library("tidyverse")
#library("shinyWidgets")
# BM: docs say that "the directory that you save server.R in
# will become the working directory of your Shiny app.
# https://shiny.rstudio.com/tutorial/written-tutorial/lesson5/
# so we need to source from here
source("R/permillcalculation.R")
source("R/permilltablefunc.R")
source("R/permillgraphfunc.R")
source("R/choroplethmapfunc.R")
source("R/descstatfuncs.R")
source("R/choroplethmapfunc.R")
source("R/interactivemap.R")
#-------------------------------------------------------UI-----------------------------------------------------------------#
ui <- navbarPage(title = "FFSG", id = "navbar",
#About Page
tabPanel(title = "About", value = "tab1",
fluidPage(fluidRow(
column(10,
h1("Fatal Force Study Group")),
column(2,
icon('question-circle', class='fa-2x helper-btn'),
tags$div(class="helper-box", style="display:none",
p('Upload a file of observed network data (must be of a supported type).',
'Add custom attributes or symmetrize on the "Edit Network" tab.')),
actionLink('abtleft', class = 'larrow', icon=icon('arrow-left', class='fa-2x'), label=NULL),
actionLink('abtright', class = 'rarrow', icon=icon('arrow-right', class='fa-2x'), label=NULL)
)
)),
sidebarLayout(
sidebarPanel(
h3("Data Resources"),
uiOutput("felink"),
#h6("This data source has been collected since 2000 and is active until present day. As of the month of April there have been a total of 19,856 number of cases that are recorded. This database allows you to go in and download any data needed and also includes visualizations."),
uiOutput("kbplink"),
actionLink("moredata", "More"),
h3("Aditional Information")
),
mainPanel(
h2("About"),
h4("UW Fatal Force Research Group (FFRG) was brought together at the University of Washington by Professor Martina Morris and Ben Marwick. Morris' background in sociology and statistics led her to creating this research group to fight injustice in police using fatal force. Ben Marwick, an Archeology professor, with a background in statistics and social science joined Morris as a side project. This research group started about a year and half ago with two students of Morris. The group has now expanded to seven undergraduate students, two from Western Washington University, with the addition of the two UW Professors. UW FFRG's mission is to bring justice and peace to communities most impacted by police brutality through a comprehensive data analysis combined with the comparisons of respective laws and policies."),
h2("Washington Policies"),
h4("In the state of Washington, De-Escalate Washington Initiative 940 was introduced to initiate officer training and community safety. Because of the amount of deaths by police that happened in the state of Washington action was called. I-940 required training on mental illness, violence de-escalation, and first aid. It also required that the communities stakeholders be involved in any policy making. Community stakeholders include persons with disabilities; members of the lesbian, gay, bisexual, transgender, and queer community; persons of color; immigrants; non-citizens; native Americans; youth; and formerly incarcerated persons."),
h4("On March 8, 2018, Washington state legislature voted on I-940 with the inclusion of ESHB 3003 to come to an agreement on how to further build trust back into the communities. With ESHB 3003, both sides agreed that there needs to be a clearer meaning of good faith. Together with I-940 and ESHB 3003 resulted in requiring violence de-escalation and mental health training. Require first aid training for all officers and require that police render first aid at the earliest safe opportunity. Removes the de facto immunity and adopts a reasonable officer standard. Requires completely independent investigations of use of deadly force. Requires notification of Tribal governments where a tribal person was injured or killed. Brings diverse community stakeholders into the process for input on policy."),
h4("On April 20, 2018, Judge Christine Schaller of Thurston County, WA ordered state legislature to put I-940 back on the November 2018 ballot. Time Eyman argued that the passing of ESHB 3003 was rush and, disrespect[ed] initiative signers and prevent[ed] voters from exercising their right to vote. Since wording and phrases were changed from the original initiative it went against Washington state's constitution stated that it must be passed with such wording or it should be sent to the ballot.")
)
)
),
#Tab for Tables and Graphs
navbarMenu(
"Tables and Graphs",
#Page for counts and per capita values of Fatal Encounters
tabPanel(title = "Counts", value = "tab2",
fluidPage(
fluidRow(
column(10,
h1("Counting Fatal Encounters")),
column(2,
icon('question-circle', class='fa-2x helper-btn'),
tags$div(class="helper-box", style="display:none",
p("View trends by state over the years
2000 to 2017. Plot and table tabs
allow you to switch between viewing
the data in a line plot or in a table.")),
actionLink('cntleft', class = 'larrow', icon=icon('arrow-left', class='fa-2x'), label=NULL),
actionLink('cntright', class = 'rarrow', icon=icon('arrow-right', class='fa-2x'), label=NULL)
)
)
),
sidebarLayout(
sidebarPanel(
selectInput("state", "State", c(sort(c(state.name, "District of Columbia")), "United States"), selected = "Washington"),
checkboxInput("all", "Display with other states", FALSE),
icon('question-circle', class='fa-2x helper-btn-small'),
tags$div(class="helper-box-small", style="display:none",
p("Selected state is colored red
with the other states displayed
in gray and the US average in
black. (US average is only
shown for per capita values)")),
checkboxInput("capita", "Calculate per capita (in millions)", TRUE),
icon('question-circle', class='fa-2x helper-btn-small'),
tags$div(class="helper-box-small", style="display:none",
p("When selected values are
calculated as a number of
fatal events per million
people in the population.
Otherwise displays total
number of fatal events."))
),
#Creates plot and table tabs so user can view data in either form
mainPanel(tabsetPanel(
type = "tabs",
tabPanel("plot", plotOutput("permillplot")),
tabPanel("table", dataTableOutput("permillDT"))
))
)
),
#Page for stats based on demographics: Race, Gender, Age
tabPanel(title= "Descriptive Statistics", value="tab3",
fluidPage(
fluidRow(
column(10,
h1("Descriptive Statistics")),
column(2,
icon('question-circle', class='fa-2x helper-btn'),
tags$div(class="helper-box", style="display:none",
p("Displays total counts of fatal
encounters in the US by demographic
(race, age, or gender).")),
actionLink('dsleft', class = 'larrow', icon=icon('arrow-left', class='fa-2x'), label=NULL),
actionLink('dsright', class = 'rarrow', icon=icon('arrow-right', class='fa-2x'), label=NULL)
)
)
),
sidebarLayout(
sidebarPanel(
selectInput("dem", "Demographic", c("Race", "Gender", "Age")),
h6("Disclaimer: Please take note that the data we are currently using is still a work in progress so some of the data is missing. This means that there is a possibility that the trends displayed aren't the true trends for the data."),
icon('question-circle', class='fa-2x helper-btn-small'),
tags$div(class="helper-box-small", style="display:none",
p("Trends will differ based on
if the data is missing at
random (meaning that each group
is just as likely to have been
marked as unspecified) or not.
If not the trends will change."))
),
mainPanel(dataTableOutput("dstbl"), plotOutput("dsplt"))
)
)
),
#Tab for Maps
navbarMenu("Maps",
#Choropleth Page
tabPanel(title = "Choropleth", value = "tab4",
fluidPage(
fluidRow(
column(10,
h1("Choropleth Map of Deaths per Capita")),
column(2,
icon('question-circle', class='fa-2x helper-btn'),
tags$div(class="helper-box", style="display:none",
p("Map displays distribution of fatal
events by state. States that are
darker have more deaths per capita.
Hovering over a state displays
the state's name and number of
fatal events per capita.")),
actionLink('cpleft', class = 'larrow', icon=icon('arrow-left', class='fa-2x'), label=NULL),
actionLink('cpright', class = 'rarrow', icon=icon('arrow-right', class='fa-2x'), label=NULL)
)
)
),
sidebarLayout(
sidebarPanel(
#displays mean and when selected shows values by year
h5("Map is shown for mean values, to select a year choose Select Year"),
checkboxInput("yearselect", "Select Year", FALSE),
conditionalPanel("input.yearselect",
sliderInput("year", "Year", 2000, 2017,
value = 2000,
animate = animationOptions(1500, TRUE),
sep = "") # BM: make the numbers look like years
)
),
mainPanel(plotlyOutput("choropleth"))
)
),
#Cartogram Page
tabPanel(title = "Cartogram", value = "tab5",
fluidPage(
fluidRow(
column(10,
h1("Cartogram of Deaths per Capita")),
column(2,
icon('question-circle', class='fa-2x helper-btn'),
tags$div(class="helper-box", style="display:none",
p("Map displays distribution of fatal
events by state. States that are
darker and bigger have more deaths
per capita.")),
actionLink('crgleft', class = 'larrow', icon=icon('arrow-left', class='fa-2x'), label=NULL),
actionLink('crgright', class = 'rarrow', icon=icon('arrow-right', class='fa-2x'), label=NULL)
)
)
),
sidebarLayout(
sidebarPanel(
#displays by year and allows for animation when play button is hit
sliderInput("yearcart", "Year", 2000, 2017, value = 2010, animate = animationOptions(1500, TRUE), sep = "") # BM: make the numbers look like years
),
mainPanel(plotOutput("cartogram"))
)
),
#Interactive Map Page
tabPanel(title = "Interactive", value = "tab6",
fluidPage(
fluidRow(
column(10,
h1("Interactive Map")),
column(2,
icon('question-circle', class='fa-2x helper-btn'),
tags$div(class="helper-box", style="display:none",
p("Map displays counts based on region
clicking on bubbles or zooming in
breaks bubbles into smaller areas.
At lowest level individual cases
are showed and can be clicked on to
display more info in a pop-up.")),
actionLink('intleft', class = 'larrow', icon=icon('arrow-left', class='fa-2x'), label=NULL),
actionLink('intright', class = 'rarrow', icon=icon('arrow-right', class='fa-2x'), label=NULL)
)
),
leafletOutput("intmap")
)
)
),
#THIS IS TAB PANEL PURGATORY
#tabPanel("Data Analytics"),
#
#tabPanel("Data Compiling"),
inverse = TRUE,
tags$head(
tags$script(type="text/javascript", src="alert.js"),
tags$link(rel="stylesheet", type="text/css",href="style.css")
)
)
#-------------------------------------------------------Server----------------------------------------------------------#
server <- function(input, output, session) {
#update active tab in navbar when arrows are clicked
leftarrowclicks <- reactive({
input$abtleft+input$cntleft+input$dsleft+input$cpleft+input$crgleft+input$intleft
})
rightarrowclicks <- reactive({
input$abtright+input$cntright+input$dsright+input$cpright+input$crgright+input$intright
})
observe({
if(leftarrowclicks() == 0) {return()}
tabOptions <- c('tab1', 'tab2', 'tab3', 'tab4', 'tab5', 'tab6')
current <- isolate(which(input$navbar==tabOptions))
updateTabsetPanel(session, 'navbar', selected=tabOptions[current-1])
})
observe({
if(rightarrowclicks() == 0) {return()}
tabOptions <- c('tab1', 'tab2', 'tab3', 'tab4', 'tab5', 'tab6')
current <- isolate(which(input$navbar==tabOptions))
updateTabsetPanel(session, 'navbar', selected=tabOptions[current+1])
})
#Outputs for tables, plots, and maps
#Plot for fatal encounter total or capita values by state
output$permillplot <-
renderPlot({
permillgraph(input$state, input$all, input$capita) #From: "permillgraphfunc.R"
})
#Data table for fatal encounter total or capita values by state
output$permillDT <-
renderDataTable({
permilltable(input$state, input$all, input$capita) #From: "permilltablefunc.R"
})
#Choropleth Map
output$choropleth <-
renderPlotly({
if(input$yearselect){
choroplethmap(paste("p", input$year, sep="")) #From: "choroplethmapfunc.R"
}else{
choroplethmap()
}
})
#Interactive Leaflet Map
output$intmap <-
renderLeaflet({
interactivemap #From: "interactivemap.R"
})
#Cartogram Map
output$cartogram <- renderImage({
filename <- normalizePath(file.path(paste('data/cartogramplots/cart', input$yearcart, '.jpg', sep="")))
list(src = filename)
}, deleteFile = FALSE) #Static Jpegs from "data/cartogramplots" folder. Update using "updatecart.R" in folder
#Data Table for demographics (Race, Gender, Age)
output$dstbl <- renderDataTable({
dstable(input$dem) #From: "descstatfuncs.R"
})
#Plot for demographics (Race, Gender, Age)
output$dsplt <- renderPlot({
dsplot(input$dem) #From: "descstatfuncs.R"
})
output$felink <- renderUI({
tagList(a("Fatal Encounters", href="http://www.fatalencounters.org"), "- This data source has been collected since 2000 and is active until present day. As of the month of April there have been a total of 19,856 number of cases that are recorded. This database allows you to go in and download any data needed and also includes visualizations.")
})
output$kbplink <- renderUI({
tagList(a("Killed by Police", href="http://killedbypolice.net"), "- An open sourced data collection from an online anonymous source that dates back to May 1, 2013. The data set is still in continuation and the legitimacy of each data point is confirmed through actual online news articles of each fatality. Killed By Police has a number of 4,629 cases recorded.")
})
}
shinyApp(ui, server)