I'm new to Shiny and coding. I found an example that uses a choropleth map (ichorophlet function) to show crime rates across years and US states. I'd like to replicate this map in Shiny using annual poverty rates in the US. My questions are: 1) How do get the map to load on Shiny? 2) How do I get the animation button to work? Below are the R codes I used. Any ideas how to fix this issue?
ui.R
shinyUI(fluidPage(
titlePanel("U.S. Poverty Rates"),
# Sidebar with slider that demonstrates various years
sidebarLayout(
sidebarPanel(
helpText("Create a poverty map."),
# Animation with custom interval (in ms) to control speed, plus looping
sliderInput("animation", "Press Play:", 1980, 2015, 1, step=1,
animate=animationOptions(interval=800, loop=TRUE))),
# Show map summarizing the values entered
mainPanel(
plotOutput("map")
)
)
))
server.R
# Load libraries
library(lattice)
library(plyr)
library(dplyr)
library(readxl)
library(RColorBrewer)
library(rMaps)
library(rjson)
library(rCharts)
library(shiny)
# Load data and helper files
data <- read_excel("data/hstpov21.xls", sheet = "Sheet1")
source("toJASON.R")
source("ichoropleth.R")
# Remove DC
datm <- subset(na.omit(data),
!(State %in% c("D.C.", "District of Columbia")))
# Discreticize poverty rates
datm2 <- transform(datm,
State = state.abb[match(as.character(State), state.name)],
fillKey = cut(Poverty,
quantile(Poverty, seq(0, 1, 1/5)),
labels = LETTERS[1:5]),
Year = as.numeric(substr(Year, 1, 4))
)
# Fill colors
fills = setNames(
c(RColorBrewer::brewer.pal(5, 'YlOrRd'), 'white'),
c(LETTERS[1:5], 'defaultFill')
)
# Create Payload for DataMaps
dat2 <- dlply(na.omit(datm2), "Year", function(x){
y = toJSONArray2(x, json = F)
names(y) = lapply(y, '[[', 'State')
return(y)
})
# Define server logic for slider
shinyServer(
function(input, output) {
# Reactive expression to compose a data frame containing all of the values
sliderValues <- reactive({
# Compose data frame
data.frame(
Name = c("Animation"),
Value = as.character(c(input$animation)),
stringsAsFactors=FALSE)
})
# Show the values using a chorophlet map
output$map <- renderPlot({
sliderValues()
ichoropleth(Poverty ~ State,
data = datm2[,1:3],
pal = 'PuRd',
ncuts = 5,
animate = 'Year',
play = TRUE)
})
})
Related
this is my first time asking on stack overflow so sorry for mistakes.
I am making a project where I should create a Shiny app in R. The app should download the data from a certain domain and after that there are several things it should allow the user to do:
download the latest data from the EUROSTAT website;
select the set of countries whose data will be presented;
select the years of data to be presented;
selection of genders for which data will be presented;
presentation of selected data in tabular form in the format:
COUNTRY; TRIBE; WEEK; NUMBER;
aggregation of data on the map of EUROPE;
total for the indicated period, for the indicated genders, within the country;
visualization of selected data in the form of time series;
one time series for selected genders, for each country separately;
Right now I am focusing on the first 5 points. I know how to get the data downloaded and how to filter and prepare it properly but I think I am doing something wrong when it comes to the Shiny environment, because when I run the app it opens up but the table created has no data inside of it. I am having problems understading shiny since this is my first time doing anything in it. Any help is welcome.
I did 2 codes and tried to run them but as I said the outcome was and empty table no matter what year or week I chose.
This is how my server.R file looks:
library(ggplot2)
library(shiny)
library(dplyr)
library(data.table)
library(googleVis)
shinyServer(function(input, output) {
outVar <- reactiveValues(
selectYearVar = "2021"
)
outVar1 <- reactiveValues(
selectWeekVar = "1"
)
outVar2 <- reactiveValues(
selectSexVar = "f"
)
outVar3 <- reactiveValues(
selectCountryVar = "PL"
)
observeEvent(input$selectCountry,{
outVar3$selectCountryVar <- input$selectCountry
})
observeEvent(input$selectSex,{
outVar2$selectSexVar <- input$selectSex
})
observeEvent(input$selectYear,{
outVar$selectYearVar <- input$selectYear
})
observeEvent(input$selectWeek,{
outVar1$selectWeekVar <- input$selectWeek
})
dataIn <- reactive({
try({
options(width=250)
rm(list=ls())
dataDir <- getwd()#file.path(getwd(),"data")
download.file(url="https://ec.europa.eu/eurostat/estat-navtree-portlet-prod/BulkDownloadListing?file=data/demo_r_mwk_ts.tsv.gz",
destfile=file.path(dataDir,"demo_r_mwk_ts.tsv.gz"),method="curl")
d <- read.table(file=file.path(dataDir,"demo_r_mwk_ts.tsv.gz"),sep="\t",dec=".",header=T)
x <- as.data.frame(rbindlist(lapply(c("AD","AL","AM","AT","BE","BG","CH","CY","CZ","DE","DK","EE","EL","ES","FI","FR","GE","HR","HU","IE","IS","IT","LI","LT",
"LU","LV","ME","MT","NL","NO","PL","PT","RO","RS","SE","SI","SK","UK"),function(country){
x <- t(d[grep(country,d[,1]),])
x <- x[-1,]
options(warn=-1)
x <- data.frame(
week = gsub("X","",rownames(x)),
f = as.integer(gsub(" p","",x[,1])),
m = as.integer(gsub(" p","",x[,2])),
t = as.integer(gsub(" p","",x[,3])),
c = country
)
options(warn=0)
rownames(x) <- NULL
x <- x[order(x$week),]
return(x)
})))
rownames(x) <- NULL
x[, "year"] <- as.integer(substr(x[, "week"], 0, 4))
x[, "week"] <- as.integer(substr(x[, "week"], 6, 7))
x <- x[week, year, c, outVar2$selectSexVar]
x <- x[(as.character(x$year)==as.character(outVar$selectYearVar)) & (as.character(x$week)==as.character(outVar1$selectWeekVar)) & (as.character(x$c)==as.character(outVar3$selectCountryVar)),]
return(x)
},silent=T)
return(data.frame())
})
output$dataSample <- DT::renderDataTable({
DT::datatable(
dataIn(),
rownames = FALSE,
options = list(
scrollX = TRUE,
pageLength = 16,
lengthMenu = seq(from=2,by=2,to=16)
)
)
})
})
and here is the ui.R file
library(shiny)
library(data.table)
library(googleVis)
shinyUI(fluidPage(
titlePanel("PiWD/shiny/sgh/umieralnosc"),
sidebarLayout(
sidebarPanel(
selectInput("selectYear",
label = "Rok danych",
choices = as.vector(as.character(2023:2000),mode="list")
),
selectInput("selectWeek",
label = "Tydzień danych",
choices = as.vector(as.character(53:1),mode="list")
),
selectInput("selectSex",
label = "Płeć",
choices = as.vector(as.character(c("f","m","t")),mode="list")
),
selectInput("selectCountry",
label = "Rok danych",
choices = as.vector(as.character(c("AD","AL","AM","AT","BE","BG","CH","CY","CZ","DE","DK","EE","EL","ES","FI","FR","GE","HR","HU","IE","IS","IT","LI","LT",
"LU","LV","ME","MT","NL","NO","PL","PT","RO","RS","SE","SI","SK","UK")),mode="list")
)
),
mainPanel(
tabsetPanel(type = "tabs",
tabPanel("Moja tabela", DT::dataTableOutput("dataSample")),
)
)
)
))
I have a sample data set with 4 columns
Year_of_connection
country
Method_of_connection
User_Counts
I am trying to create a dashboard using Shiny where the user is requested to input the year drop down list and click the submit button. Upon clicking submit the output should be a leaflet map only for the year selected and reflecting User_counts from various countries
I followed the steps of getting the shapefile , ShapepolygonDataFrame using the maps package and then am trying to merge the shapefile with the dataframe - the dataframe is generated when user selects a set of specific dates only.
However, when I plot the map, it displays incorrect country names and values and this is very random. For an example from the data set if you select 2018 then all values are displayed correctly, but the moment you select 2019 or 2020 then incorrect country names and values get assigned to the poloygon.
What am I doing wrong or missed ?
I tried referencing Reactive Shiny Map Spatial Data Frame , Leaflet incorrect values for country , also value assigned to wrong country , followed the instructions, however I was not successful. I have included the image of the output, you can observe that its plotting the correct country polygons for which data exists but the country name and value from dataset is incorrect.
I will really appreciate any help
here is the code:
library(shiny)
library(shinydashboard)
library(lubridate)
library(sp)
library(sf)
library(leaflet)
library(stringr)
library(dplyr)
library(maps)
library(maptools)
#Read the file
my_data <- read.csv(file.choose(),header = T)
my_data$YEAR_OF_CONNECTION <- strptime(my_data$YEAR_OF_CONNECTION,"%Y")
my_data$YEAR_OF_CONNECTION <- as.numeric(format(my_data$YEAR_OF_CONNECTION,"%Y"))
print(my_data)
print(class(my_data$YEAR_OF_CONNECTION))
# Define UI
ui <- navbarPage("Cell Connectivity",
tabPanel("Country based Cellular Connections",
sidebarLayout(
sidebarPanel(
selectInput(
inputId = "val",
label = "Select time period:",
choices = 2014:2021
),
br(),
br(),
submitButton("Submit")
),
#
mainPanel(
leafletOutput("map")
)
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
selected <- reactive({
fildata <- filter(my_data,my_data$YEAR_OF_CONNECTION == input$val)
fildata
# my_data[my_data$YEAR_OF_CONNECTION == input$val,]
#
# print(class((input$val)))
# print(input$val)
})
output$map <- renderLeaflet({
# Use leaflet() here, and only include aspects of the map that
# won't need to change dynamically (at least, not unless the
# entire map is being torn down and recreated).
leaflet() %>%
addProviderTiles("CartoDB.Positron")
})
observe({
if(!is.null(input$val)){
#t2#data <- left_join(t2#data, selected (), by="country")
worldmap <- map("world", fill=TRUE, plot=FALSE)
IDs <- sapply(strsplit(worldmap$names, ":"), function(x) x[1])
worldmap_poly <- map2SpatialPolygons(worldmap, IDs=IDs, proj4string=CRS("+proj=longlat +datum=WGS84"))
worldmap_poly_sp <- SpatialPolygonsDataFrame(worldmap_poly,
data.frame(IDs=names(worldmap_poly),
stringsAsFactors=FALSE), FALSE)
##subset the SpatialpolygonDataFrame only for country of interest
t2 <- subset(worldmap_poly_sp, IDs %in% selected()$country)
t2#data <- inner_join(t2#data, selected(), by=character(),copy = TRUE)
# t3 <- merge(t2, selected(),by.x="country",by.y="country",all.y= TRUE)
leafletProxy("map", data = t2 ) %>%
#addTiles() %>%
clearShapes() %>%
addPolygons(data = t2,
#fillColor = "Green",
fillOpacity = 0.7,
#color = "Blue", weight = 2,
popup = ~ paste0("USER_COUNTS: ", as.character(selected()$USER_COUNTS),
"<br>","<b>",
"COUNTRY:",as.character(selected()$country)))
}
})
}
# Run the application
shinyApp(ui = ui, server = server)
Problem solved!
I was able to reference post on stackoverflow Incorrect Labels Leaflet Map Polygons in R Shiny
I made the changes to my code, rather than merging the entire subset of the SpatialpolygonDataFrame containing countries of interest, I merged them with shapefile#data attribute (where shapefile is your shapefile and #data is part of the large shapefile). Then I assigned the merged component (Shapefile#data and user's selected data frame) back to the shapefile#data attribute of the original subseted SpatialpolygonDataFrame. Thus the originally subseted data frame now contains the correct label values .
Here are the code changes
BEFORE
t2#data <- inner_join(t2#data, selected(), by=character(),copy = TRUE)
AFTER
t3 <- merge(t2#data, selected(),by.x="IDs",by.y="country",all.y= TRUE)
t2#data <- t3
leafletProxy("map", data = t2 ) %>%
addTiles() %>%
clearShapes() %>%
addPolygons(data = t2,
#fillColor = "Green",
fillOpacity = 0.7,
#color = "Blue", weight = 2,
popup = ~ paste0("USER_COUNTS: ", as.character(t2#data$USER_COUNTS),
"<br>","<b>",
"COUNTRY:",as.character(t2#data$IDs)))
Here is the correct output with country labels and values.
Full credit to the idea present in the post Incorrect Labels Leaflet Map Polygons in R Shiny
Using Shiny in R, I am attempting to create a Leaflet map which allows the user to click on any markers to generate a corresponding plot that represents the information (temperature) at that specific site.
I incorporated codes from this question (Click on points in a leaflet map as input for a plot in shiny) and the second trick on this blog (https://www.r-bloggers.com/4-tricks-for-working-with-r-leaflet-and-shiny/) but still cannot seem to successfully register the clicked marker point in Shiny.
i.e. Nothing plots when I click on any site.
I could not find any solutions based on further research, any help is appreciated.
library(leaflet)
library(shiny)
library(ggplot2)
# example data frame
wxstn_df <- data.frame(Site = c("a", "a", "b"), Latitude = c(44.1, 44.1, 37), Longitude = c(-110.2, -110.2, -112.7), Month = c(1,2,1), Temp_avg = c(10, 18, 12))
ui <- fluidPage(column(7, leafletOutput("wsmap", height = "600px")),
column(5, plotOutput("plot", height = "600px"))
)
server <- function(input, output) {
# create a reactive value to store the clicked site
stn <- reactiveValues(clickedMarker = NULL)
## leaflet map
output$wsmap <- renderLeaflet({
leaflet() %>%
addTiles() %>%
addCircleMarkers(data = wxstn_df, ~unique(Longitude), ~unique(Latitude), layerId = ~unique(Site), popup = ~unique(Site))
})
# store the click
observeEvent(input$map_marker_click, {
stn$clickedMarker <- input$map_marker_click
})
output$plot <- renderPlot({
ggplot(wxstn_df[wxstn_df$Site %in% stn$clickedmarker$Site,], aes(Month, Temp_avg)) +
geom_line()
})
}
shinyApp(ui, server)
Here's a solution:
library(leaflet)
library(shiny)
library(ggplot2)
# example data frame
wxstn_df <- data.frame(Site = c("a", "a", "b"), Latitude = c(44.1, 44.1, 37), Longitude = c(-110.2, -110.2, -112.7), Month = c(1,2,1), Temp_avg = c(10, 18, 12))
ui <- fluidPage(column(7, leafletOutput("wsmap", height = "600px")),
column(5, plotOutput("plot", height = "600px"))
)
server <- function(input, output) {
## leaflet map
output$wsmap <- renderLeaflet({
leaflet() %>%
addTiles() %>%
addCircleMarkers(data = wxstn_df, ~unique(Longitude), ~unique(Latitude), layerId = ~unique(Site), popup = ~unique(Site))
})
# generate data in reactive
ggplot_data <- reactive({
site <- input$wsmap_marker_click$id
wxstn_df[wxstn_df$Site %in% site,]
})
output$plot <- renderPlot({
ggplot(data = ggplot_data(), aes(Month, Temp_avg)) +
geom_line()
})
}
shinyApp(ui, server)
The main problem is that you were not changing the object names from the example that you were using, e.g. input$wsmap_marker_click because wsmap is the name of you leaflet ID. Similarly, to access Site info, use input$wsmap_marker_click$id not input$wsmap_marker_click$Site. It is often useful to print the objects within the reactive function to explore what the input object looks like and how to access parts of it.
e.g.
# generate data in reactive
ggplot_data <- reactive({
print(input$wsmap_marker_click)
site <- input$wsmap_marker_click$id
print(site)
data <- wxstn_df[wxstn_df$Site %in% site,]
print(data)
data})
Personally in this situation I would prefer to use a reactive expression generate ggplot data (ggplot_data()) from marker click rather than creating a reactiveValues object. Every time the marker is clicked the plot will update with new ggplot_data().
And proof it works:
How does one dynamically aggregate a raster in Shiny?
i.e. using an example .flt file:
https://www.ngdc.noaa.gov/mgg/global/relief/ETOPO2/ETOPO2v2-2006/ETOPO2v2c/raw_binary/
library("dplyr")
library("ggplot2")
library("shiny")
library("raster")
ui <- fluidPage(
mainPanel(
plotOutput("canvasHere")
),
sliderInput("sliderRes", label = h5("Resolution reduction"),
min = 1, max = 100, value = 5)
) ## UI end
shinyServer <- function(input, output) {
BMgradient <- raster("/home/berg/Downloads/ETOPO2v2c_f4_LSB/ETOPO2v2c_f4_LSB.flt",crs=NA,template=NULL)
##resolutionFactor <- input$sliderRes
resolutionFactor <- 5
BMgradient <- aggregate(BMgradient, fact=resolutionFactor, fun=max)
p <- rasterToPoints(BMgradient)
bmdf <- data.frame(p)
colnames(bmdf) <- c("bbb", "ccc", "varFillBBB")
output$canvasHere <- renderPlot({
ggplot()+
geom_tile(data=bmdf,aes(bbb,ccc,fill=varFillBBB))
})
}
print("Processed code")
runApp(list(ui = ui, server = shinyServer))
Now, usually I'd just adjust a variable in the server by using:
resolutionFactor <- input$sliderRes
However, this doesn't seem to work for raster aggregation, and I just have to use a static resolution factor such as: resolutionFactor <- 5
How can I do this dynamically via a slider in the UI?
After a while, I managed to find the solution.
The raster aggregation itself needs to be placed in the reactive environment.
i.e.
output$yourOutput <- renderPlot({
BMgradient <- raster(...)
resolutionFactor <- input$sliderRes
## Rest of raster code
ggplot()+
geom_tile(data=bmdf,aes(bbb,ccc,fill=varFillBBB))
})
Then, you can manually adjust the slider and the raster will dynamically aggregate.
I was able to create an interactive geoChart using the scripts below though, the problem is that the scale to distinguish map color changes each day. My data set is a year worth of daily stats by every state in US.
For instance, for day 1, the scale takes min and max value of that particular day. But I'm trying to change the scripts so that the scale becomes constant for any given day (and shows min and max of the whole year).
Can anyone please advice how to do this? Thank you!
global.R
library(shiny)
states <- read.csv("queries_geo.csv")
states$StartDate <- as.Date(states$StartDate, "%m/%d/%Y")
ui.R
library(shiny)
shinyUI(fluidPage(
titlePanel("PlayStation4 Search Volume Trend by States"),
sidebarLayout(
sidebarPanel(
sliderInput("StartDate", "Quarter",
min = min(states$StartDate),
max = max(states$StartDate),
value = min(states$StartDate),
step = 1,
animate = TRUE)),
mainPanel(
htmlOutput ("GeoStates")
))))
server.R
library(shiny)
library(dplyr)
library(googleVis)
shinyServer(function(input,output,session){
querydate <- reactive({
states_new <- states %>%
filter(StartDate == input$StartDate) %>%
select(Geo,Queries) %>%
arrange(Geo)})
output$GeoStates <- renderGvis ({
GeoStates <- gvisGeoChart(querydate(),
"Geo", #locationvar
"Queries", # colorvar
options = list(region = "US",
displayMode = "regions",
resolution = "provinces",
sizeAxis.maxValue = max(states$Queries),
sizeAxis.minValue = min(states$Queries),
width = 600,
height = 400)
)})})
I was able to figure this out. I'm posting my solution so that hopefully it saves someone else time in the future! Thank you for reading my question!
global.R => no change in this part
library(shiny)
states <- read.csv ("queries_geo.csv")
states$StartDate <- as.Date(states$StartDate, "%m/%d/%Y")
ui.R => significant changes happened in this part. Instead of creating a map chart in server.R and htmlOutput in ui.R, this code creates a map in ui.R. This way, the map scale stays constant and the animation works smoothly. server.R just feeds reactive data to the map chart in ui.R.
library(shiny)
library(googleVis)
library(googleCharts)
min_query <- min(states$Queries)
max_query <- max(states$Queries)
shinyUI(fluidPage(
# This line loads the Google Charts JS library
googleChartsInit(),
# https://developers.google.com/chart/interactive/docs/gallery/geochart
googleGeoChart("GeoStates",
width = 1000,
height = 600,
options = list(
fontSize = 13,
region = "US",
displayMode = "regions",
resolution = "provinces",
colorAxis = list(
maxValue = round(max_query, -3),
minValue = round(min_query, -1)
))),
fluidRow(
shiny::column(4, offset = 4,
sliderInput("StartDate",
"StartDate",
min = min(states$StartDate),
max = max(states$StartDate),
value = min(states$StartDate),
step = 60,
animate = TRUE)
))))
server.R
library(shiny)
library(dplyr)
library(googleVis)
shinyServer(function(input,output,session){
querydate <- reactive({
states_new <- states %>%
filter(StartDate == input$StartDate) %>%
select(Geo,Queries) %>%
arrange(Geo)
})
output$GeoStates <- reactive({
list(
data = googleDataTable(querydate())
)})})