The color of each bubble represents whether there is a high or low maximum number of users or devices relative to the other buildings on campus.
+ The diameter of each bubble represents the maximum number of users or devices during the specified time period.
+ The user can click on each bubble to view the building name, the maximum number of users or devices, and the average number of users or devices.
+ If you would like to differentiate the bubbles for each building only by color, select the same size option.
")),
+ wellPanel(inputPanel(radioButtons("datatype", "Choose to view Wi-Fi access by users or devices",
+ c("Users" = "user", "Devices" = "mac"), selected="user"),
+ dateInput('day', label = 'Choose a day', value = sort(unique(rpi_wap_raw$Date))[length(unique(rpi_wap_raw$Date))-1],
+ min = min(rpi_wap_raw$Date), max = max(rpi_wap_raw$Date)),
+ checkboxGroupInput("buildingtype", "Select a building type",
+ choices = c("Academic"="academic", "Housing" = "housing","Greek" = "greek",
+ "Other Off Campus" = "otherOffCampus",
+ "Other On Campus" = "otherOnCampus"),
+ selected = list("academic", "housing", "greek", "otherOnCampus")),
+ sliderInput("time", "Select a time period",
+ min = as_datetime(hms::parse_hm("0:00")),
+ max = as_datetime(hms::parse_hm("23:30")),
+ value = c(as_datetime(hms::parse_hm("14:00:00")),
+ as_datetime(hms::parse_hm("16:00:00"))),
+ timeFormat = "%H:%M",
+ timezone = "GMT",
+ step = 30*60),
+ radioButtons("size", "Select how you would like to view the size of the bubbles",
+ choices = c("Actual size" = "actual", "Same size" = "same"),
+ selected = "actual")),
+ leafletOutput("bubblemap", width = "100%", height = 700))
+ ),
+ #continuous tab
+ tabItem(tabName="continuous",
+ tags$style("#title_panel_cont {font-size:30px;color:black;display:block;}"),
+ fluidRow(
+ column(12,br(), br(), br(), div(style="text-align:center", box(width=18, title = uiOutput("title_panel_cont"))))
+ ),
+ helpText(HTML("",
+ "Return to campus map view")),
+ helpText( HTML("",
+ "Go to Campus WiFi Dashboard")),
+ wellPanel(inputPanel(dateInput('date',
+ label = 'Choose a date',
+ value = sort(unique(rpi_wap_raw$Date))[length(unique(rpi_wap_raw$Date))-1],
+ min = min(rpi_wap_raw$Date),
+ max = max(rpi_wap_raw$Date)
+ ),
+ radioButtons("data_type_continous", "Choose to view Wi-Fi access by USERS or by DEVICES",
+ c("Users" = "user", "Devices" = "mac"),
+ selected = "user"),
+ checkboxGroupInput(
+ "buildingType",
+ "Select a builiding type",
+ choices = c("Academic"="academic",
+ "Housing" = "housing",
+ "Greek" = "greek",
+ "Other Off Campus" = "otherOffCampus",
+ "Other On Campus" = "otherOnCampus"),
+ selected = list("academic")
+ ),
+ selectInput(
+ "buildingStr",
+ "Select a building from above building type",
+ choices = list("Building from above building type"="all",
+ "Academic" = academic,
+ "Housing" = housing,
+ "Greek" = greek,
+ "Other Off Campus" = otherOffCampus,
+ "Other On Campus" = otherOnCampus),
+ selected = "all",
+ multiple = FALSE,
+ selectize = TRUE,
+ width = NULL,
+ size = NULL
+ ),
+ selectInput(
+ "threshold",
+ "Ignore the Max User(or Device) counts during a day below",
+ choices = list("0" = 0,
+ "1" = 1,
+ "2" = 2,
+ "3" = 3,
+ "4" = 4,
+ "5" = 5,
+ "6" = 6,
+ "7" = 7,
+ "8" = 8,
+ "9" = 9,
+ "10" = 10),
+ selected = 5
+ )
+ )),
+ fluidRow(
+ tags$style("#continous_table {margin-top:10px; margin-right:10px}"),
+ tags$style("#continuous_ui {margin-top:10px}"),
+ column(8,div(uiOutput("continuous_ui"))),
+ column(3, div(DT::dataTableOutput("continous_table"))))
+ )
+ ),
+ tags$script(HTML('
+ $(document).ready(function() {
+ $(\'head\').append(\'\');
+ $(\'head\').append(\'\');
+ //all related pages should be linked below
+
+ $("header").find("nav").append(\'
COVID Safe Campus is an app that reveals the usage of Wi-Fi access points and aggregations of wireless users on the campus network at Rensselaer Polytechnic Institute, Troy, New York.
+
+
+
Campus WiFi Overview
+
This tab reveals the usage of Wi-Fi access points on the campus network at RPI.
+
Users can customize their searches using the following 4 selections:
+
+
View the WiFi access via user count or device count.
+
Select a specific date within the last 7 days.
+
Specify the 30 minutes interval they wish to view.
+
Refine their searches by only selecting specific building types.
+
Based on the user's selection, the main panel will display a bubble map.
+
+
The color of each bubble represents whether there is a high or low maximum number of users at that specific building during the given time interval.
+
The diameter of each bubble represents the maximum number of users at that specific building during the given time interval.
+
The user can click on each bubble to view the building name, maximum number of users, and average number of users during the given time interval.
+
+
Continuous Data Overview
+
This tab provides an overview of the WiFi access on campus via WAP devices.
+
Users can customize their searches using the following 3 selections:
+
+
Select a specific date within the last 7 days.
+
Select a specific or multiple buildings.
+
Refine their searches by only selecting from specific building types.
+
Based on the user's selection, the main panel will display a heatmap showcasing the WiFi access per WAP device.
+
+
Campus WiFi Dashboard
+
This tab provides an overview of the WiFi access on campus via WAP devices in 30 minute intervals.
+
Users can customize their searches using the following 5 selections:
+
+
View the WiFi access via user count or MAC count.
+
Select a specific date within the last 7 days.
+
Select a specific or multiple buildings.
+
Refine their searches by only selecting from specific building types.
+
Specify the 30 minutes interval they wish to view the information.
+
Based on user's selection, the main panel will display 4 plots showcasing a main heatmap displaying the WiFi access per WAP device at every 30 minutes interval, 2 aggregate bar plots showing the WiFi access per WAP device and every 30 minutes, and a plot of the WiFi access at each 30 minutes interval modeled by a polynomial function.
+
+
+
DISCLAIMERS
+
This is a prototype app. Some of the information displayed may not be 100% accurate. Improvements to this app will continue to be made.
+
+
+ "),
+
+ )
+ })
+
+ #Map tab
+ output$geographic_ctrl <- renderUI({
+ incoming <- reactive({
+ combined_wap_data$Building <- forcats::fct_explicit_na(combined_wap_data$Building)
+ wap_map_data_time1 <- combined_wap_data %>% filter(time==(hms::as_hms(input$time[1])), Date==input$day, buildingType %in% input$buildingtype)
+ wap_map_data_time2 <- combined_wap_data %>% filter(time==(hms::as_hms(input$time[2])), Date==input$day, buildingType %in% input$buildingtype)
+ combined_wap_data_with_times <- left_join(wap_map_data_time1, wap_map_data_time2, by=c("Building","Date", "latitude","longitude"))
+ if (input$datatype == "user"){
+ users <- combined_wap_data_with_times %>% select(Building, users.x, users.y)
+ users$max <- apply(users[,2:3],1,max)
+ combined_wap_data_final <- combined_wap_data_with_times %>% mutate(average_users = (users.x+users.y)/2, time = time.y-time.x)
+ combined_wap_data_final$max_users <- users$max
+ combined_wap_data_final <- combined_wap_data_final %>% select(-time.x, -time.y, -users.x, -users.y)
+ }
+ else{
+ macs <- combined_wap_data_with_times %>% select(Building, macs.x, macs.y)
+ macs$max <- apply(macs[,2:3],1,max)
+ combined_wap_data_final <- combined_wap_data_with_times %>% mutate(average_macs = (macs.x+macs.y)/2, time = time.y-time.x)
+ combined_wap_data_final$max_macs <- macs$max
+ combined_wap_data_final <- combined_wap_data_final %>% select(-time.x, -time.y, -macs.x, -macs.y)
+ }
+ })
+
+ # default box
+ minLng = min(rpi_wap_raw$longitude)
+ minLat = min(rpi_wap_raw$latitude)
+ maxLng = max(rpi_wap_raw$longitude)
+ maxLat = max(rpi_wap_raw$latitude)
+
+ #plot the bubble map
+ #observe({
+ if (input$size == "actual"){
+ output$bubblemap <- renderLeaflet({
+ if (input$datatype == "user"){
+ pal <- colorNumeric(palette, incoming()$max_users, reverse=TRUE)
+ m <- leaflet(incoming()) %>% addTiles() %>%
+ addLegend(pal = pal, values = ~max_users, group = "circles",
+ position = "topleft", title="Maximum Number of Users")
+ # only add the data if there is data to be added
+ if (nrow(incoming()) > 0) {
+ pal <- colorNumeric(palette = palette, domain = incoming()$max_users, reverse = TRUE)
+ m <- addCircles(m, lat = ~latitude, lng = ~longitude, radius = ~max_users/2,
+ color = ~pal(max_users), stroke = FALSE, fillOpacity = .7, options = list(padding = c(50,50)))
+ m <- addCircleMarkers(m, lat = ~latitude, lng = ~longitude, popup = ~paste0("Building: ", Building, " ", "Max: ", max_users," users", " ", "Average: ", average_users, " users", " ",
+ "Learn more about the ", "", Building,""),
+ options = popupOptions(closeButton = TRUE), radius = ~max_users/2, color = ~pal(max_users))
+ m
+ } else {
+ m <- fitBounds(m, lng1 = minLng, lat1 = minLat, lng2 = maxLng, lat2 = maxLat)
+ m
+ }
+ }
+ else{
+ pal <- colorNumeric(palette, incoming()$max_macs, reverse=TRUE)
+ m <- leaflet(incoming()) %>% addTiles() %>%
+ addLegend(pal = pal, values = ~max_macs, group = "circles",
+ position = "topleft", title="Maximum Number of Users")
+ # only add the data if there is data to be added
+ if (nrow(incoming()) > 0) {
+ pal <- colorNumeric(palette = palette, domain = incoming()$max_macs, reverse = TRUE)
+ m <- addCircles(m, lat = ~latitude, lng = ~longitude, radius = ~max_macs/2,
+ color = ~pal(max_macs), stroke = FALSE, fillOpacity = .7, options = list(padding = c(50,50)))
+ m <- addCircleMarkers(m, lat = ~latitude, lng = ~longitude, popup = ~paste0("Building: ", Building, " ", "Max: ", max_macs," devices", " ", "Average: ", average_macs, " devices", " ",
+ "Learn more about the ", "", Building,""),
+ options = popupOptions(closeButton = TRUE), radius = ~max_macs/2, color = ~pal(max_macs))
+ m
+ } else {
+ m <- fitBounds(m, lng1 = minLng, lat1 = minLat, lng2 = maxLng, lat2 = maxLat)
+ m
+ }
+ }
+ m
+ })
+ }
+ else{
+ output$bubblemap <- renderLeaflet({
+ if (input$datatype == "user"){
+ pal <- colorNumeric(palette, incoming()$max_users, reverse=TRUE)
+ m <- leaflet(incoming()) %>% addTiles() %>%
+ addLegend(pal = pal, values = ~max_users, group = "circles",
+ position = "topleft", title="Maximum Number of Users")
+ # only add the data if there is data to be added
+ if (nrow(incoming()) > 0) {
+ pal <- colorNumeric(palette = palette, domain = incoming()$max_users, reverse = TRUE)
+ m <- addCircles(m, lat = ~latitude, lng = ~longitude, radius = 15,
+ color = ~pal(max_users), stroke = FALSE, fillOpacity = .7, options = list(padding = c(50,50)))
+ m <- addCircleMarkers(m, lat = ~latitude, lng = ~longitude, popup = ~paste0("Building: ", Building, " ", "Max: ", max_users," users", " ", "Average: ", average_users, " users", " ",
+ "Learn more about the ", "", Building,""),
+ options = popupOptions(closeButton = TRUE), radius = 15, color = ~pal(max_users))
+ m
+ } else {
+ m <- fitBounds(m, lng1 = minLng, lat1 = minLat, lng2 = maxLng, lat2 = maxLat)
+ m
+ }
+ }
+ else{
+ pal <- colorNumeric(palette, incoming()$max_macs, reverse=TRUE)
+ m <- leaflet(incoming()) %>% addTiles() %>%
+ addLegend(pal = pal, values = ~max_macs, group = "circles",
+ position = "topleft", title="Maximum Number of Users")
+ # only add the data if there is data to be added
+ if (nrow(incoming()) > 0) {
+ pal <- colorNumeric(palette = palette, domain = incoming()$max_macs, reverse = TRUE)
+ m <- addCircles(m, lat = ~latitude, lng = ~longitude, radius = 15,
+ color = ~pal(max_macs), stroke = FALSE, fillOpacity = .7, options = list(padding = c(50,50)))
+ m <- addCircleMarkers(m, lat = ~latitude, lng = ~longitude, popup = ~paste0("Building: ", Building, " ", "Max: ", max_macs," devices", " ", "Average: ", average_macs, " devices", " ",
+ "Learn more about the ", "", Building,""),
+ options = popupOptions(closeButton = TRUE), radius = 15, color = ~pal(max_macs))
+ m
+ } else {
+ m <- fitBounds(m, lng1 = minLng, lat1 = minLat, lng2 = maxLng, lat2 = maxLat)
+ m
+ }
+ }
+
+ })
+ }
+
+ #})
+ })
+
+ # keep input selector in all the tabs consistently, allow user to change the input in any tab.
+ observe({
+ # when the users change input at the Campus Overview tab
+ if(input$tabs=='geographic'){
+ marker_info <- input$bubblemap_marker_click
+ abbreviation <- buildinginfo$abbrev[buildinginfo$latitude == marker_info$lat & buildinginfo$longitude == marker_info$lng]
+ # If user was jumping to Detailed WiFi overview tab, then input date, time should be same as map's, and marker should be clicked
+ if((!(input$buildingStr == abbreviation)||!(input$date == input$day)||!(input$buildingType == input$buildingtype)||!(input$data_type_continous == input$datatype)) &&
+ !is.null(input$bubblemap_marker_click)){
+ updateRadioButtons(session, "data_type", selected=input$datatype)
+ updateDateInput(session, "date", value = input$day)
+ updateCheckboxGroupInput(session, "buildingType", selected = input$buildingtype)
+ # If users do not select any building type, plot should show all the data;
+ # The dropdown should include all the buildings.
+ if(!is.null(input$buildingType)){
+ buildingtype<-input$buildingType
+ buildingname<-alltype[buildingtype]
+ buildingname<-c("Buildings from selected building type(s)"="all",buildingname)
+ }
+ else{
+ buildingname<-c("Buildings from selected building type(s)"="all",alltype)
+ }
+ updateSelectInput(session, "buildingStr",choices = buildingname,selected=abbreviation)
+ }
+ # If user was jumping to WiFi dashboard, then input date, time should be same as map's, and marker should be clicked
+ if((!(input$buildingStr_discrete == abbreviation)||!(input$date_discrete == input$day)||!(input$timeRange == input$time)||!
+ (identical(input$buildingType_discrete, input$buildingtype))||!(identical(input$data_type_discrete,input$datatype))) && !is.null(input$bubblemap_marker_click)){
+ updateRadioButtons(session, "data_type_discrete", selected=input$datatype)
+ updateDateInput(session, "date_discrete", value = input$day)
+ updateCheckboxGroupInput(session, "buildingType_discrete", selected = input$buildingtype)
+ updateSliderInput(session, "timeRange", value=input$time, timeFormat = "%H:%M")
+ # If users do not select any building type, plot should show all the data;
+ # The dropdown should include all the buildings.
+ if(!is.null(input$buildingType_discrete)){
+ buildingtype<-input$buildingType_discrete
+ buildingname<-alltype[buildingtype]
+ buildingname<-c("Buildings from selected building type(s)"="all",buildingname)
+ }
+ else{
+ buildingname<-c("Buildings from selected building type(s)"="all",alltype)
+ }
+ updateSelectInput(session, "buildingStr_discrete",choices = buildingname,selected=abbreviation)
+ }
+ }
+ # when the users change input at the Detailed Wifi Overview tab
+ else if(input$tabs=='continuous'){
+ data_type_info <- input$data_type_continous
+ date_info<-input$date
+ building_type_info<-input$buildingType
+ building_info<-input$buildingStr
+ # update the input in the Campus Wifi Overview tab consistently with the Detailed Wifi Overview tab
+ if(!(input$datatype == data_type_info)||!(input$day == date_info)||!(identical(input$buildingtype,building_type_info))){
+ updateRadioButtons(session, "datatype", selected=data_type_info)
+ updateDateInput(session, "day", value = date_info)
+ updateCheckboxGroupInput(session, "buildingtype", selected = building_type_info)
+ }
+ # update the input in the Campus Wifi Dashboard tab consistently with the Detailed Wifi Overview tab
+ if(!(input$data_type_discrete == data_type_info)||!(input$date_discrete == date_info)||!(identical(input$buildingType_discrete,building_type_info))||!(input$buildingStr_discrete==building_info)){
+ updateRadioButtons(session, "data_type__discrete", selected=data_type_info)
+ updateDateInput(session, "date_discrete", value = date_info)
+ updateCheckboxGroupInput(session, "buildingType_discrete", selected = building_type_info)
+ # If users do not select any building type, plot should show all the data;
+ # The dropdown should include all the buildings.
+ if(!is.null(input$buildingType_discrete)){
+ buildingtype<-input$buildingType_discrete
+ buildingname<-alltype[buildingtype]
+ buildingname<-c("Buildings from selected building type(s)"="all",buildingname)
+ }
+ else{
+ buildingname<-c("Buildings from selected building type(s)"="all",alltype)
+ }
+ updateSelectInput(session, "buildingStr_discrete",choices = buildingname,selected=building_info)
+ }
+
+ }
+ # when the users change input at the Campus Wifi Dashboard tab
+ else if(input$tabs=='discrete'){
+ data_type_info <- input$data_type_discrete
+ date_info<-input$date_discrete
+ building_type_info<-input$buildingType_discrete
+ building_info<-input$buildingStr_discrete
+ time_info<-input$timeRange
+ # update the input in the Campus Wifi Overview tab consistently with the Campus Wifi Dashboard tab
+ if(!(input$datatype == data_type_info)||!(input$day == date_info)||!(identical(input$buildingtype,building_type_info))||!(identical(input$time, time_info))){
+ updateRadioButtons(session, "datatype", selected=data_type_info)
+ updateDateInput(session, "day", value = date_info)
+ updateCheckboxGroupInput(session, "buildingtype", selected = building_type_info)
+ updateSliderInput(session, "time", value=time_info, timeFormat = "%H:%M")
+ }
+ # update the input in the Detailed Wifi Overview tab consistently with the Campus Wifi Dashboard tab
+ if(!(input$data_type_continous == data_type_info)||!(input$date == date_info)||!(identical(input$buildingType,building_type_info))||!(input$buildingStr==building_info)){
+ updateRadioButtons(session, "data_type_continous", selected=data_type_info)
+ updateDateInput(session, "date", value = date_info)
+ updateCheckboxGroupInput(session, "buildingType", selected = building_type_info)
+ if(!is.null(input$buildingType)){
+ buildingtype<-input$buildingType
+ buildingname<-alltype[buildingtype]
+ buildingname<-c("Buildings from selected building type(s)"="all",buildingname)
+ }
+ else{
+ buildingname<-c("Buildings from selected building type(s)"="all",alltype)
+ }
+ updateSelectInput(session, "buildingStr",choices = buildingname,selected=building_info)
+ }
+ }
+ })
+
+
+ #campus dashboard tab
+ output$interval_ctrl <- renderUI({
+ discrete_dat <- reactive({
+ if(input$buildingStr_discrete=="all"){
+ building=""
+ # if user does not select any building type, the plot should show all the Wap data.
+ if(is.null(input$buildingType_discrete)){
+ rpi_wap_raw<-rpi_wap_raw %>%
+ # Filter by selected date
+ filter(Date==as_date(input$date_discrete))
+ }
+ # if user select one or more building types, the plot should show the data of the buildings in those types.
+ else{
+ rpi_wap_raw<-rpi_wap_raw %>%
+ # Filter by selected date and building
+ filter(Date==as_date(input$date_discrete))%>%
+ filter(buildingType%in%input$buildingType_discrete) %>%
+ filter(time>=(hms::as_hms(input$timeRange[1]))) %>%
+ filter(time<=(hms::as_hms(input$timeRange[2])))
+ }
+ }
+ # when the user selects a specified building.
+ else{
+ rpi_wap_raw<-rpi_wap_raw %>%
+ filter(Date==as_date(input$date_discrete)) %>%
+ filter(abbrev == input$buildingStr_discrete)%>%
+ filter(time>=(hms::as_hms(input$timeRange[1]))) %>%
+ filter(time<=(hms::as_hms(input$timeRange[2])))
+ }
+
+ })
+
+ # Sum the number of Wi-Fi users by device
+ by_device <- reactive({
+
+ if (input$data_type_discrete == "user") {
+ trimmedHitsData <- discrete_dat() %>%
+ dplyr::group_by(devname, Date) %>%
+ dplyr::summarize(total = sum(usercount))
+ } else {
+ trimmedHitsData <- discrete_dat() %>%
+ dplyr::group_by(devname, Date) %>%
+ dplyr::summarize(total = sum(maccount))
+ }
+
+ })
+
+ # Sum the number of Wi-Fi users by hour
+ by_time <- reactive({
+ if (input$data_type_discrete == "user") {
+ trimmedHitsData <- discrete_dat() %>%
+ dplyr::group_by(Date, time) %>%
+ dplyr::summarize(total = sum(usercount))
+ } else {
+ trimmedHitsData <- discrete_dat() %>%
+ dplyr::group_by(Date, time) %>%
+ dplyr::summarize(total = sum(maccount))
+ }
+
+ })
+
+ discrete_dat_fin <- reactive({
+ if (input$data_type_discrete == "user") {
+ all_discrete_dat <- discrete_dat()
+ names(all_discrete_dat)[names(all_discrete_dat) == "usercount"] <- "Count"
+ all_discrete_dat
+ } else {
+ all_discrete_dat <- discrete_dat()
+ names(all_discrete_dat)[names(all_discrete_dat) == "maccount"] <- "Count"
+ all_discrete_dat
+ }
+ })
+
+ ht <- reactiveVal(400)
+ ht_main <- reactiveVal(400)
+
+ set_breaks = function(max_y) {
+ if (max_y <= 4){
+ seq(0, max_y, by = 1)
+ } else if (max_y > 4 & max_y < 16){
+ seq(0, max_y, by = 2)
+ } else {
+ seq(0, max_y, by = max_y %/% 4)
+ }
+ }
+
+ # Plot the graphs for the interval view
+ observe({
+ # Main plot for interval data
+ output$p1 <- renderPlot({
+ max_y <- max(discrete_dat_fin()$Count)
+ ggplot(discrete_dat_fin(), aes(x = time, y = devname, fill = Count)) +
+ geom_tile() +
+ xlab(label = "Time of day (24-hour clock)") +
+ ylab(label = "WAP Device Name") +
+ ggtitle(paste("RPI WiFi access per WAP devices in 30 minutes interval on", input$date_discrete,
+ "from", hms::as_hms(input$timeRange[1]), "to", hms::as_hms(input$timeRange[2]))) +
+ scale_fill_distiller(palette = "Purples", direction = "horizontal", name = "Count",
+ breaks = set_breaks(max_y)) + theme_ipsum() +
+ theme(plot.title = element_text(size = 16)) +
+ theme(axis.title.x = element_text(size = 12)) +
+ theme(axis.title.y = element_text(size = 12)) +
+ theme(plot.margin = unit(c(1,0,1,0), "cm")) +
+ theme(panel.grid.major.x = element_line(colour = "black"),
+ panel.ontop = TRUE)
+ }, height=ht_main)
+
+ output$discrete_plot1 <- renderUI({
+ if(nrow(discrete_dat())==0){
+ h1("There is no building under selected condition")
+ }
+ else{
+ plotOutput("p1",height = "100%")
+ }
+ })
+
+ # Plot for Wi-Fi connection by device
+ output$p2 <- renderPlot({
+ max_y <- max(by_device()$total)
+ ggplot(by_device(), aes(x=devname, y=total, fill=total)) +
+ geom_bar(stat="identity") +
+ xlab(label = "WAP Device Name") +
+ ylab(label = "Nunber of access per WAP device") +
+ ggtitle("RPI WiFi access per WAP devices") +
+ scale_fill_distiller(palette = "Purples", direction = "horizontal", name = "Count", breaks = set_breaks(max_y)) + theme_ipsum() +
+ theme_bw() + coord_flip() +
+ theme(plot.title = element_text(size = 16, face = "bold")) +
+ theme(axis.title.x = element_text(size = 12)) +
+ theme(axis.title.y = element_text(size = 12)) +
+ theme(plot.margin = unit(c(1,0,1,0), "cm"))
+ }, height=ht_main)
+
+ output$discrete_plot2 <- renderUI({
+ if(nrow(discrete_dat())==0){
+ h1(" ")
+ }
+ else{
+ plotOutput("p2",height = "100%")
+ }
+ })
+
+ # Plot for Wi-Fi connection by hour
+ output$p3 <- renderPlot({
+ max_y <- max(by_time()$total)
+ ggplot(by_time(), aes(x=time, y=total, fill=total)) +
+ geom_bar(stat="identity") +
+ xlab(label = "Time of day (24-hour clock)") +
+ ylab(label = "Number of WAP device access by hour") +
+ ggtitle(paste("RPI WiFi access in 30 minutes interval on", input$date_discrete,
+ "from", hms::as_hms(input$timeRange[1]), "to", hms::as_hms(input$timeRange[2]))) +
+ theme_bw() +
+ scale_fill_distiller(palette = "Purples", direction = "horizontal", name = "Count",
+ breaks = set_breaks(max_y)) +
+ theme_ipsum() +
+ theme(plot.title = element_text(size = 16)) +
+ theme(axis.title.x = element_text(size = 12)) +
+ theme(axis.title.y = element_text(size = 12)) +
+ theme(plot.margin = unit(c(1,0,1,2.5), "cm")) +
+ theme(panel.grid.major.x = element_line(colour = "black"),
+ panel.ontop = TRUE)
+ }, height=ht)
+
+ output$discrete_plot3 <- renderUI({
+ if(nrow(discrete_dat())==0){
+ h1(" ")
+ }
+ else{
+ plotOutput("p3",height = "100%")
+ }
+ })
+
+ # Plot for Wi-Fi data in continuous time
+ output$p4 <- renderPlot({
+ max_y <- max(by_time()$total)
+ ggplot(by_time(), aes(x=time, y = total)) +
+ geom_line(data = by_time(), aes(x=time, y=total), color = "#999999", size = 2) +
+ geom_point(data = by_time(), aes(fill=total), colour="black",pch=21, size=4) +
+ xlab(label = "Time of day (24-hour clock)") +
+ ylab(label = "Number of WAP device access by hour") +
+ ggtitle(paste("RPI WiFi access in 30 minutes interval \n (continuous representation)")) +
+ theme_bw() +
+ theme(plot.title = element_text(size = 16)) +
+ theme(axis.title.x = element_text(size = 12)) +
+ theme(axis.title.y = element_text(size = 12)) +
+ scale_colour_distiller(palette = "Purples", trans = "reverse") +
+ scale_fill_distiller(palette = "Purples", direction = "horizontal", name = "Count",
+ breaks = set_breaks(max_y)) + theme_ipsum() +
+ theme(plot.margin = unit(c(1,0,1,0), "cm"))
+ }, height=ht)
+
+ output$cont_plot <- renderUI({
+ if(nrow(discrete_dat())==0){
+ h1(" ")
+ }
+ else{
+ plotOutput("p4",height = "100%")
+ }
+ })
+
+ output$title_panel <- renderUI({
+ if(input$buildingStr_discrete=="all"){
+ paste("All building of", tools::toTitleCase(input$buildingType_discrete), "type on", input$date_discrete, "from",
+ hms::as_hms(input$timeRange[1]), "to", hms::as_hms(input$timeRange[2]), "EST")
+ } else{
+ row<- which(buildinginfo$abbrev == input$buildingStr_discrete)
+ buildingname<-as.character(buildinginfo[row, 1])
+ paste(buildingname, "on", input$date_discrete, "from", hms::as_hms(input$timeRange[1]), "to", hms::as_hms(input$timeRange[2]), "EST")
+ }
+ })
+
+ })
+
+ observeEvent({
+ input$buildingStr_discrete
+ input$date_discrete
+ input$buildingType_discrete
+ input$timeRange}, {
+ if(nrow(dat())!=0){
+ row_number<-length(unique(discrete_dat_fin()$devname))
+ if (row_number > 10000) {
+ ht_main(10000)
+ } else if(row_number==1){
+ ht_main(row_number*75)
+ } else if(row_number>1 & row_number<=3){
+ ht_main(row_number*65)
+ }else if(row_number>3 & row_number<=10){
+ ht_main(row_number*40)
+ } else if (row_number>10 & row_number<=20){
+ # browser()
+ ht_main(row_number*30)
+ } else {
+ ht_main(row_number*25)
+ }
+ }
+ })
+
+ })
+
+
+ # update input choices with only showing the buildings for the selected building types
+ observe({
+ # If users do not select any building type, plot should show all the data;
+ # The dropdown should include all the buildings.
+ if(!is.null(input$buildingType)){
+ buildingtype<-input$buildingType
+ buildingname<-alltype[buildingtype]
+ buildingname<-c("Buildings from above building type(s)"="all",buildingname)
+ }
+ else{
+ buildingname<-c("Buildings from above building type(s)"="all",alltype)
+ }
+ updateSelectInput(session, "buildingStr",
+ choices = buildingname
+ )
+ })
+
+ output$title_panel_cont = renderUI({
+ if(input$buildingStr=="all"){
+ if(length(input$buildingType)==1){
+ paste("All building of", tools::toTitleCase(input$buildingType), "type on", input$date)
+ }
+ # if the user selects multiple building types
+ else{
+ bulding_type=toString(input$buildingType)
+ paste("All building of", tools::toTitleCase(bulding_type), "type on", input$date)
+ }
+
+ } else{
+ row<- which(buildinginfo$abbrev == input$buildingStr)
+ buildingname<-buildinginfo[row, 1]
+ paste(buildingname, "on", input$date)
+ }
+ })
+
+ dat <- reactive({
+ if(input$data_type_continous=="user"){
+ # when the user selects the 'entire campus'
+ if(input$buildingStr=="all"){
+ building=""
+ # if user does not select any building type, the plot should show all the Wap data.
+ if(is.null(input$buildingType)){
+ rpi_wap_raw<-rpi_wap_raw %>%
+ # Filter by selected date and building
+ filter(Date==as_date(input$date))%>%
+ group_by(devname) %>%
+ mutate(max_count = max(usercount)) %>%
+ ungroup() %>%
+ filter(max_count>=input$threshold)
+ }
+ # if user select one or more building types, the plot should show the data of the buildings in those types.
+ else{
+ rpi_wap_raw<-rpi_wap_raw %>%
+ # Filter by selected date and building
+ filter(Date==as_date(input$date))%>%
+ filter(buildingType%in% input$buildingType)%>%
+ group_by(devname) %>%
+ mutate(max_count = max(usercount)) %>%
+ ungroup() %>%
+ filter(max_count>=input$threshold)
+ }
+ }
+ # when the user selects a specified building.
+ else{
+ rpi_wap_raw<-rpi_wap_raw %>%
+ # Filter by selected date, building types, and building
+ filter(Date==as_date(input$date)) %>%
+ filter(abbrev == input$buildingStr_discrete)%>%
+ group_by(devname) %>%
+ mutate(max_count = max(usercount)) %>%
+ ungroup() %>%
+ filter(max_count>=input$threshold)
+ }
+ }
+
+ else {
+ # when the user selects the 'entire campus'
+ if(input$buildingStr=="all"){
+ building=""
+ # if user does not select any building type, the plot should show all the Wap data.
+ if(is.null(input$buildingType)){
+ rpi_wap_raw<-rpi_wap_raw %>%
+ # Filter by selected date and building
+ filter(Date==as_date(input$date))%>%
+ group_by(devname) %>%
+ mutate(max_count = max(maccount)) %>%
+ ungroup() %>%
+ filter(max_count>=input$threshold)
+ }
+ # if user select one or more building types, the plot should show the data of the buildings in those types.
+ else{
+ rpi_wap_raw<-rpi_wap_raw %>%
+ # Filter by selected date and building
+ filter(Date==as_date(input$date))%>%
+ filter(buildingType %in% input$buildingType)%>%
+ group_by(devname) %>%
+ mutate(max_count = max(maccount)) %>%
+ ungroup() %>%
+ filter(max_count>=input$threshold)
+ }
+ }
+ # when the user selects a specified building.
+ else{
+ rpi_wap_raw<-rpi_wap_raw %>%
+ # Filter by selected date, building types, and building
+ filter(Date==as_date(input$date)) %>%
+ filter(abbrev == input$buildingStr_discrete)%>%
+ group_by(devname) %>%
+ mutate(max_count = max(maccount)) %>%
+ ungroup() %>%
+ filter(max_count>=input$threshold)
+ }
+ }
+ # if(nrow(rpi_wap_raw)==0){
+ # shinyalert("Oops!", "Something went wrong.", type = "error");
+ # }
+ })
+
+
+ plotHeight <- reactiveVal(1000)
+
+ observe({
+ output$plot2 <- renderPlot({
+ #if there is data under selected condition.
+ if(nrow(dat())!=0){
+ rpi_wap_raw<-dat()
+ if(input$data_type_continous=="user"){
+ rpi_wap_raw %>%
+ # Now plot!
+ ggplot( aes(x=date_time, y=0.5)) +
+ geom_line(aes(color = usercount, size=2)) +
+ scale_color_gradient(low = "white", high = "darkblue") +
+ ylim(0,1) +
+ theme_bw() +
+ theme(axis.text.y=element_blank(),
+ axis.ticks.y=element_blank(),
+ panel.grid.major.y = element_blank(),
+ panel.grid.minor.y = element_blank()) +
+ facet_grid(rows = vars(devname), switch = "both") +
+ theme(panel.border = element_rect(color = "white", fill = NA, size = 1))+
+ theme(strip.text.y.left = element_text(angle = 0)) +
+ theme(panel.grid.minor.x = element_line( size=0.5, color="black" ),
+ panel.grid.major.x = element_line( size=1, color="black" )) +
+ scale_x_datetime(date_breaks = "3 hours",date_minor_breaks="1 hour",position="top", date_labels="%H") +
+ theme(legend.position="right") +
+ theme(
+ panel.background = element_rect(fill = NA),
+ panel.ontop = TRUE)+
+ labs(y="Access Point ID", x = "Time of Day(24hr clock)")
+ }
+ else {
+ rpi_wap_raw %>%
+ # Now plot!
+ ggplot( aes(x=date_time, y=0.5)) +
+ geom_line(aes(color = maccount, size=2)) +
+ scale_color_gradient(low = "white", high = "darkblue") +
+ ylim(0,1) +
+ theme_bw() +
+ theme(axis.text.y=element_blank(),
+ axis.ticks.y=element_blank(),
+ panel.grid.major.y = element_blank(),
+ panel.grid.minor.y = element_blank()) +
+ facet_grid(rows = vars(devname), switch = "both") +
+ theme(panel.border = element_rect(color = "white", fill = NA, size = 1))+
+ theme(strip.text.y.left = element_text(angle = 0)) +
+ theme(panel.grid.minor.x = element_line( size=0.5, color="black" ),
+ panel.grid.major.x = element_line( size=1, color="black" )) +
+ scale_x_datetime(date_breaks = "3 hours",date_minor_breaks="1 hour",position="top", date_labels="%H") +
+ theme(legend.position="right") +
+ theme(
+ panel.background = element_rect(fill = NA),
+ panel.ontop = TRUE)+
+ labs(y="Access Point ID", x = "Time of Day(24hr clock)")
+ }
+
+ }
+
+ },height = plotHeight)
+ output$continuous_ui<- renderUI({
+ # if there is no data under selected condition.
+ if(nrow(dat())==0){
+ h1("There is no building under selected condition")
+ }
+ else{
+ plotOutput("plot2",height = "100%")
+ }
+ })
+ })
+
+ output$continous_table <- DT::renderDataTable({
+ if(input$buildingStr!="all"&input$data_type_continous=="user"){
+ building_user<-paste("Total number of users in", tools::toTitleCase(input$buildingStr))
+ type_user<-paste("Total number of users in", tools::toTitleCase(input$buildingType), "type")
+ max_building_user<-paste("Max number of users in", tools::toTitleCase(input$buildingStr))
+ max_type_user<-paste("Max number of users in", tools::toTitleCase(input$buildingType), "type")
+ max_count<-dat()[!duplicated(dat()$devname),]
+ hottest_time<-dat()[dat()$usercount==dat()$max_count,]
+ hottest_time<-hottest_time[!duplicated(hottest_time$devname),]$date_time
+ print(hottest_time)
+ cont_table<-data.frame("Devname"=(max_count$devname),"max_building_user"=(max_count$max_count),"Hottest Time"=hottest_time)
+ names(cont_table)[names(cont_table) == "max_building_user"]=max_building_user
+ rownames(cont_table) <- NULL
+ print(cont_table)
+ cont_table
+ }
+ },options = list(searching = FALSE))
+
+ observeEvent({
+ input$buildingStr
+ input$threshold
+ input$date
+ input$buildingType}, {
+ if(nrow(dat())!=0){
+ row_number<-length(unique(dat()$devname))
+ if (row_number > 10000) {
+ plotHeight(10000)
+ } else if(row_number==1){
+ plotHeight(row_number*75)
+ } else if(row_number>1 & row_number<=3){
+ plotHeight(row_number*65)
+ }else if(row_number>3 & row_number<=10){
+ plotHeight(row_number*40)
+ } else if (row_number>10 & row_number<=20){
+ # browser()
+ plotHeight(row_number*30)
+ } else {
+ plotHeight(row_number*25)
+ }
+ }
+ })
+}
+
+# Run the application
+shinyApp(ui = ui, server = server)
\ No newline at end of file
diff --git a/building_info.R b/building_info.R
new file mode 100644
index 0000000..5a8b373
--- /dev/null
+++ b/building_info.R
@@ -0,0 +1,210 @@
+### continuous and dashboard view ###
+
+# Building selector sub-lists
+academic <- list(
+ "Amos Eaton"="amos",
+ "Carnegie Building"="carn",
+ "CBIS"="biot",
+ "CII"="cii",
+ "Cogswell Lab"="cogs",
+ "DCC"="dcc",
+ "Folsom Library"="lib",
+ "Greene Building"="gree",
+ "JEC"="jec",
+ "Lally Building"="lall",
+ "Materials Research Center"="mrc",
+ "Pittsburgh Building"="pitt",
+ "Polymer Center"="poly",
+ "Rensselaer Union"="unio",
+ "Ricketts Building"="rick",
+ "Sage Lab"="sage",
+ "Science Center"="scic",
+ "Troy Building"="troy",
+ "Voorhees Computing Center"="vcc",
+ "Walker Lab"="walk",
+ "West Hall"="west",
+ "Winslow Building"="wins")
+
+greek <- list(
+ "Alpha Epsilon Pi, 284 Pawling Ave"="aepi",
+ "Delta Phi, 311 Congress St"="cong",
+ "Delta Tau Delta, 133 Sunset Terrace"="delt",
+ "Lambda Chi Alpha, 200 Sunset Terrace" ="lxa",
+ "Phi Kappa Theta, 107 Sunset Terrace" ="phik",
+ "Phi Mu Delta, 224 Pawling Ave"="phim",
+ "Phi Sigma Kappa, 316/320 Congress St"="phis",
+ "Theta Chi, 2100 Burdett Ave"="thetc",
+ "Theta Xi, 1490 Sage Ave"="thetx",
+ "Zeta Psi, 25 Belle Ave"="zeta")
+
+housing <- list(
+ "Albright Court #71"="albr71",
+ "Albright Court #73"="albr73",
+ "Albright Court #76"="albr76",
+ "Albright Court #82"="albr82",
+ "Albright Court #84"="albr84",
+ "Albright Court #85"="albr85",
+ "Albright Court #88"="albr88",
+ "Albright Court #91"="albr91",
+ "Albright Court #94"="albr94",
+ "Albright Court #95"="albr95",
+ "Albright Court #97"="albr97",
+ "BARH A"="barha",
+ "BARH B" = "barhb",
+ "BARH C" = "barhc",
+ "BARH D" = "barhd",
+ "Barton Hall"="bart",
+ "Beman Lane #11"="bema11",
+ "Beman Lane #13"="bema13",
+ "Beman Lane #15"="bema15",
+ "Beman Lane #16"="bema16",
+ "Beman Lane #18"="bema18",
+ "Beman Lane #21"="bema21",
+ "Beman Lane #23"="bema23",
+ "Beman Lane #25"="bema25",
+ "Beman Lane #26"="bema26",
+ "Beman Lane #28"="bema28",
+ "Blitman Commons"="blit",
+ "Bray Hall"="bray",
+ "Brinsmade Terrace #11"="brin11",
+ "Brinsmade Terrace #13"="brin13",
+ "Brinsmade Terrace #16"="brin16",
+ "Brinsmade Terrace #21"="brin21",
+ "Brinsmade Terrace #23"="brin23",
+ "Brinsmade Terrace #26"="brin26",
+ "Brinsmade Terrace #32"="brin32",
+ "Brinsmade Terrace #34"="brin34",
+ "Brinsmade Terrace #35"="brin35",
+ "Brinsmade Terrace #41"="brin41",
+ "Brinsmade Terrace #43"="brin43",
+ "Brinsmade Terrace #46"="brin46",
+ "Bryckwyck Apts, Sunset Terrace"="bryc",
+ "Burdett Ave #2144" ="burd",
+ "Cary Hall"="cary",
+ "Chasen Building, downtown"="chas",
+ "City Station South"="ctys",
+ "City Station West"="ctyw",
+ "Colonie Apts Bldg A"="coloa",
+ "Colonie Apts Bldg B"="colob",
+ "Colonie Apts Bldg C"="coloc",
+ "Colonie Apts Bldg D"="colod",
+ "Colvin Circle #12"="colv12",
+ "Colvin Circle #14"="colv14",
+ "Colvin Circle #16"="colv16",
+ "Colvin Circle #22"="colv22",
+ "Colvin Circle #24"="colv24",
+ "Colvin Circle #26"="colv26",
+ "Colvin Circle #28"="colv28",
+ "Colvin Circle #30"="colv30",
+ "Colvin Circle #31"="colv31",
+ "Colvin Circle #34"="colv34",
+ "Colvin Circle #36"="colv36",
+ "Colvin Circle #42"="colv42",
+ "Colvin Circle #43"="colv43",
+ "Colvin Circle #44"="colv44",
+ "Colvin Circle #52"="colv52",
+ "Colvin Circle #54"="colv54",
+ "Colvin Circle #56"="colv56",
+ "Colvin Circle #62"="colv62",
+ "Colvin Circle #64"="colv64",
+ "Colvin Circle #66"="colv66",
+ "Crockett Hall"="croc",
+ "Davison Hall"="davi",
+ "E-Housings (Cass)"="cass",
+ "E-Housings (Clem)"="clem",
+ "E-Housings (Hear)"="hear",
+ "E-Housings (Hira)"="hira",
+ "E-Housings (Voor)"="voor",
+ "E-Housings (Wait)"="wait",
+ "Gurley Building, downtown"="gurl",
+ "Hall Hall"="hall",
+ "McGiffert, Stacwyck Apartments" ="mcgi",
+ "Nason Hall"="naso",
+ "North Hall"="nort",
+ "Nugent Hall"="nuge",
+ "Polytech Apartments"="polyt",
+ "Quad (Cald)"="cald",
+ "Quad (Buck)"="buck",
+ "Quad (Chur)"="chur",
+ "Quad (Coop)"="coop",
+ "Quad (Hunt)"="hunt",
+ "Quad (IPAC)"="ipac",
+ "Quad (Macd)"="macd",
+ "Quad (Pard)"="pard",
+ "Quad (Roeb)"="roeb",
+ "Quad (Whit)"="whit",
+ "Rousseau, Stacwyck Apartments"="rous",
+ "Sharp Hall"="shar",
+ "Tech Terrace Apartments"="tech",
+ "Thompson, Stacwyck Apartments"="thom",
+ "Williams, Stacwyck Apartments"="will",
+ "Wiltsie, Stacwyck Apartments"="wilt",
+ "Warren Hall"="warr")
+
+otherOffCampus <- list(
+ "90 College Ave"="coll",
+ "CCI, North Greenbush"="cci",
+ "College Ave #92, RPI Ambulance"="ambu",
+ "DFWI, Lake George"="dfwi",
+ "Jordan Road #405, Tech Park, CCI"="cci",
+ "Jordan Road #250, Tech Park"="techp250",
+ "Jordan Road #350, Tech Park"="techp350",
+ "Jordan Road #465, Tech Park"="techp465",
+ "LINAC/NES, Tibbits Ave"="linac",
+ "Moes, College Ave"="moes",
+ "Off Campus Commons, 1525 15th St"="offc",
+ "Old Bumstead Garage, behind Colonie Apts"="bums",
+ "Pahl House, corner of 15th and Peoples"="pahl",
+ "Peoples Ave #1002"="peop1002",
+ "Peoples Ave #1516"="peop1516",
+ "Peoples Ave #901"="peop901",
+ "Peoples Ave #907"="peop907",
+ "President's House"="pres",
+ "Proctors Building, downtown"="proc",
+ "Rensselaer at Hartford"="cap0",
+ "SAE, 12 Myrtle Ave off Pawling Ave"="sael",
+ "Service Building, Peoples Ave"="servb",
+ "Walthousen Lab, Schenectady"="sche",
+ "Watervliet Facility, 805 25th St, Watervliet"="wate")
+
+otherOnCampus <- list(
+ "87 Gym"="gym8",
+ "Academy Hall"="acad",
+ "Admissions"="admi",
+ "ASRC"="armo",
+ "ASRC auto shop"="auto",
+ "ASRC garage"="garg",
+ "College Ave #90, EMPAC resident artists" = "empar",
+ "Commons Dining Hall"="comm",
+ "East Campus Community Center"="eccc",
+ "ECAV arena"="ecav",
+ "ECAV stadium"="ecavs",
+ "EMPAC"="empa",
+ "Fitness Center"="fitn",
+ "H Building"="hbui",
+ "Heffner Alumni House"="heff",
+ "Houston Field House"="fiel",
+ "J Building, Peoples Ave"="jbui",
+ "RPI Playhouse, 15th St"="play",
+ "Robison Pool"="robi",
+ "Russell Sage Dining Hall"="rsdh",
+ "Visitor Information Center, Public Safety"="vic0",
+ "WRPI tower, North Greenbush" ="wrpi")
+
+alltype<-list("academic"=academic,"greek"=greek,"housing"=housing,"otherOffCampus"=otherOffCampus,"otherOnCampus"=otherOnCampus)
+
+
+### campus view ###
+#apinfo <- read_csv("~/IDEA-COVID-ContactTracing/wap_data/apinfo.csv", col_types = cols())
+#apinfo <- read_csv("/academics/MATP-4910-F20/IDEA-COVID-ContactTracing/wap_data/apinfo.csv", col_types = cols())
+#colnames(apinfo) <- c("devname", "Building", "Floor", "Room","latitude", "longitude")
+
+#buildinginfo <- as.data.frame(read_csv("~/IDEA-COVID-ContactTracing/wap_data/allBuildingInfo.csv", col_types=cols()))
+
+#allinfo <- left_join(apinfo, buildinginfo, by="Building")
+#allinfo <- allinfo %>% select(-latitude.x,-longitude.x)
+#colnames(allinfo) <- c("devname","Building","Floor","Room","longitude","latitude","buildingType","abbrev")
+
+#custom color palette
+palette <- c("darkred", "red4", "red3", "indianred3", "salmon", "lightskyblue", "steelblue1", "royalblue1", "royalblue4", "navyblue", "midnightblue")
+