I posted a similar question here:
How do I create a Leaflet Proxy in observeEvent() for checkboxGroup in R Shiny .
But I'm a little desperate for answers, so I thought I'd rephrase my question and post it again. I've scoured the internet for answers and can't seem to find what I'm looking for. Apologies for the double posting.
Here's my issue.
I have a dataset here:
https://github.com/mallen011/Leaflet_and_Shiny/blob/master/Shiny%20Leaflet%20Map/csv/RE.csv
It's recycling centers in Kentucky. It's set up so each recyclable material is a column, and each row i.e. recycling center is listed as yes/no as to whether each center actually recycles said material.
Here's an example of what the data looks like, in case you can't access the csv. Top row is the header column. Sorry for the formatting:
Name___________________GL______AL_____PL
Bath Community Recycling___Yes_____No____Yes
Ted & Sons Scrap Yard______No______No____Yes
Now I have the csv visualized on a R shiny dashboard app like here using Leaflet:
https://github.com/mallen011/Leaflet_and_Shiny/blob/master/Shiny%20Leaflet%20Map/re_map.png
But I want to add a control in which users can filter through where they can recycle their goods, namely, I want to use checkboxGroupInput() in R shiny so users can check materials and have recycling centers populate the map. For example, if a person wants to know where to recycle their glass, they can check "glass" in the checkbox group, and all recycling centers that allow glass recycling pop up.
So in R Shiny, I've read my recycling data csv (RE.csv):
RE <- read.csv("C:/Users/username/Desktop/GIS/Shiny Leaflet Map/csv/RE.csv")
RE$y <- as.numeric(RE$y)
RE$x <- as.numeric(RE$x)
RE.SP <- SpatialPointsDataFrame(RE[,c(7,8)], RE[,-c(7,8)])
Here's my UI that puts the checkboxGroupInput() in the sidebar():
ui <- dashboardPage(
skin = "blue",
dashboardHeader(titleWidth = 400, title = "Controls"),
dashboardSidebar(width = 400
#here's the checkboxgroup, it calls the columns for glass, aluminum and plastic from the RE.csv, all of which have binary values of yes/no
checkboxGroupInput(inputId = "RE_check",
label = h3("Recycleables"),
choices = list("Glass" = RE$GL, "Aluminum" = RE$AL, "Plastic" = RE$PL),
selected = 0)
),
dashboardBody(
fluidRow(box(width = 12, leafletOutput(outputId = "map"))),
tags$style(type = "text/css", "#map {height: calc(100vh - 80px) !important;}"),
leafletOutput("map")
)
)
And now for the trouble I'm having: What do I put into my server so it observes each of these events?
This is what I have for the event in which a user checks "glass", and I have no idea how wrong or how right it is. I just know it's not working. I'm trying to use "if" statements, so only values that equal "yes" populate the map. But currently, the map in the dashboard is blank no matter what I do, although the checkbox group input seems to work.
server <- function(session, input, output) {
observeEvent({
RE_click <- input$map_marker_click
if (is.null(RE_click))
return()
if(input$RE$GL == "Yes"){
leafletProxy("map") %>%
clearMarkers() %>%
addMarkers(data = RE_click,
lat = RE$y,
lng = RE$x)
return("map")
}
})
Here's my output leaflet map too, in case that matters:
output$map <- renderLeaflet({
leaflet() %>%
setView(lng = -83.5, lat = 37.6, zoom = 8.5) %>%
addProviderTiles("Esri.WorldImagery") %>%
addProviderTiles(providers$Stamen.Toner, group = "Toner") %>%
addPolygons(data = counties,
color = "green",
weight = 1,
fillOpacity = .1,
highlight = highlightOptions(
weight = 3,
color = "green",
fillOpacity = .3)) %>%
addMarkers(data = RE,
lng = ~x, lat = ~y,
label = lapply(RE$popup, HTML),
group = "recycle",
clusterOptions = markerClusterOptions(showCoverageOnHover = FALSE)) %>%
addLayersControl(baseGroups = c("Esri.WorldImagery", "Toner"),
overlayGroups = c("recycle"),
options = layersControlOptions(collapsed = FALSE))
})
}
I'm new to R Shiny if that's not obvious. I'd really appreciate any and all help.
All my code is publicly available on my GitHub for download:
https://github.com/mallen011/Leaflet_and_Shiny
Thanks and stay safe!
Maybe this would work... You can add the different recycle types as layers, then add the checkboxes on the leaflet map instead of worrying about shiny integration. Obviously, you'd have to add the rest of your recycle types on here...
library(leaflet)
library(htmlTable)
RE <- read.csv("https://raw.githubusercontent.com/mallen011/Leaflet_and_Shiny/master/Shiny%20Leaflet%20Map/csv/RE.csv")
leaflet() %>%
setView(lng = -83.5, lat = 37.6, zoom = 8.5) %>%
addProviderTiles("Esri.WorldImagery") %>%
addProviderTiles(providers$Stamen.Toner, group = "Toner") %>%
# addPolygons(data = counties,
# color = "green",
# weight = 1,
# fillOpacity = .1,
# highlight = highlightOptions(
# weight = 3,
# color = "green",
# fillOpacity = .3)) %>%
addMarkers(data = RE[RE$AL=="Yes", ],
lng = ~x, lat = ~y,
#label = lapply(RE$popup, HTML),
group = "AL",
clusterOptions = markerClusterOptions(showCoverageOnHover = FALSE)) %>%
addMarkers(data = RE[RE$FE=="Yes", ],
lng = ~x, lat = ~y,
#label = lapply(RE$popup, HTML),
group = "FE",
clusterOptions = markerClusterOptions(showCoverageOnHover = FALSE)) %>%
addMarkers(data = RE[RE$NONFE=="Yes", ],
lng = ~x, lat = ~y,
#label = lapply(RE$popup, HTML),
group = "NONFE",
clusterOptions = markerClusterOptions(showCoverageOnHover = FALSE)) %>%
addLayersControl(baseGroups = c("Esri.WorldImagery", "Toner"),
overlayGroups = c("AL", "FE", "NONFE"),
options = layersControlOptions(collapsed = FALSE))
Related
I would like users to be able to change the choropleth map overlay by selecting different attributes from a dropdown. The map and different attributes render fine based on user selection, but the popup option of the addPolygons function does not interpret the input values the same as the rest of the app.
For below, assume we are using an object of class 'sf' named densitydata with columns for the polygon geography and the different densities to be mapped (dens1, dens2 and dens3). I want the popup to return the values of the selected density for the clicked feature. E.g. "Selected density: 1.23" but instead get the name of the column: "Selected density: dens1".
If I put the input in a mathematical function like round(), I get an error that I'm providing a non-numeric value to a mathematical function:
popup = ~paste0("<b>Selected density:</b> ", round(input$densities, 2))
Dropdown menu:
dashboardSidebar(
selectInput(inputId = "densities", label = "Select attribute",
choices = c("Density 1" = "dens1",
"Density 2" = "dens2",
"Density 3" = "dens3"), selected = NULL, multiple = F, selectize = TRUE, width = '500px', size = NULL),
Rendering the map in the server function:
output$DensMap <- renderLeaflet({
# Define palette for new selection
DensPal <- colorQuantile(palette = "YlOrRd",
domain = {densitydata %>%
pull(input$densities)},
n = 5)
# Render map
leaflet(data = densitydata) %>%
addTiles() %>% # Add default OpenStreetMap map tiles
addPolygons(fillColor = ~DensPal({densitydata %>% pull(input$densities)}),
fillOpacity = 0.5,
weight = 2,
popup = ~paste0("<b>Selected density:</b> ", input$densities) %>%
addLegend(pal = DensPal,
values = {densitydata %>% pull(input$densities)},
opacity=0.7,
title = "Legend title here",
position = "bottomleft" )
} )
You can not use input$densities directly, you can use it thru densitydata[[input$densities]]:
output$DensMap <- renderLeaflet({
# Define palette for new selection
DensPal <- colorQuantile(palette = "YlOrRd",
domain = densitydata[[input$densities]],
n = 5)
# Render map
leaflet(data = densitydata) %>%
addTiles() %>% # Add default OpenStreetMap map tiles
addPolygons(fillColor = ~DensPal(densitydata[[input$densities]]),
fillOpacity = 0.5,
weight = 2,
popup = ~paste0("<b>Selected density:</b> ",
densitydata[[input$densities]]) %>%
addLegend(pal = DensPal,
values = densitydata[[input$densities]],
opacity=0.7,
title = "Legend title here",
position = "bottomleft" )
} )
I am trying to create a leaflet map that adds and removes a polygon layer (SpatialDataFrame) based on changing user inputs into a flexdashboard Shiny App. The geometry of the polygons (4201 polygons) remains constant, but as user makes changes to inputs, the data set (2100500 records total) that merges with each polygon changes (to =4201 to merge with polygons).
I've been following the Leaflet R docs here https://rstudio.github.io/leaflet/shiny.html
And my sample code below seems to mimic the observe() event recommend to wrap around the addPolygons(). I've also looked at the source code from a number of similar Shiny apps from the shiny gallery page (particularly this one: https://walkerke.shinyapps.io/neighborhood_diversity/) but it doesn't seem to work
Here is a sample of the app, note the data are too large to load but see the comments. When I create the addPolygons() in the first leaflet() call, it works fine. The downside of this approach is that it causes the entire map to redraw when user input changes. Following leaflet documentation suggestion I then want to move this addPolygon into a separate observer. This is where it fails.
---
title: "Model Result Viewer"
output:
flexdashboard::flex_dashboard:
orientation: columns
vertical_layout: fill
runtime: shiny
---
```{r setup, include=FALSE}
library(flexdashboard)
library(...)
df <- fread("./data/rca_do_salt_1day.csv")
# get the unique parameters & layers
model_params <- unique(df$Parameter)
model_layers <- unique(df$Cell_K)
# read in the grid
grid <- spTransform(readOGR(dsn="./PVSC06_Grid", layer="PVSC06_WGS84"),
CRS("+proj=longlat +datum=WGS84 +no_defs"))
# Data Controls --------------------------------
<USER INPUTS SIMILAR TO:
# parameter selection
selectInput("param", "Parameter", model_params, selected = model_params[1])
#layer selection
selectInput("lyr", "Layer", model_layers, selected = model_layers[1])
# make the grid dataframe
df_subset <- reactive({
filter(df, Cell_K == input$lyr, Parameter == input$param, Time == input$timeslider)
})
# THIS MAKES THE POLYGONS FOR MAPPING
sp.grid <- reactive({
merge(grid, df_subset(), by.x = "Id", by.y = "Id")
})
# helpers for leaflet
pal <- reactive({
colorNumeric(
palette = input$colors, #"YlOrRd",
domain = df_parameter()$Value
)
})
#Set labels for grid hover
labels <- reactive({
sp.grid()$Value %>% lapply(htmltools::HTML)
})
output$map <- renderLeaflet({
leaflet() %>%
# Base Setup
addTiles(group = "Open Street Map") %>%
addProviderTiles('Esri.WorldImagery', group = "Satellite Imagery") %>%
addDrawToolbar(
targetGroup='Draw',
editOptions = editToolbarOptions(selectedPathOptions = selectedPathOptions())
) %>%
clearShapes() %>%
fitBounds(grid#bbox[1], grid#bbox[2], grid#bbox[3], grid#bbox[4]) %>%
# ================ THIS WORKS HERE, BUT NOT IN AN OBSERVER?!!! =================
# addPolygons(data = sp.grid(),
# layerId = ~Id,
# group= "Grid",
# weight = 0.1,
# opacity = 0,
# fillOpacity = 1,
# stroke = FALSE,
# fillColor = ~pal()(Value),
# highlightOptions = highlightOptions(color = "white",
# weight = 2,
# bringToFront = TRUE),
# label = labels(),
# labelOptions = labelOptions(style = list("font-weight" = "normal", padding = "3px 8px"),
# textsize = "15px",
# direction = "auto")) %>%
# # Legend
# addLegend(position = 'bottomright',
# pal = pal(), opacity = 1,
# values = sp.grid()$Value,
# title = input$param) %>%
#==================================================================================
# TOC Box
addLayersControl(
baseGroups = c("Satellite Imagery", "Open Street Map"),
overlayGroups = c("Grid", "Loads", "Draw"),
options = layersControlOptions(collapsed=TRUE)
)
})
# =============== THIS DOESN"T WORK ======================
observe({
req(sp.grid()) # this alone will cause the error
# why doesn't this work?
leafletProxy('map', data = sp.grid()) %>%
removeShape(~Id) %>%
addPolygons(
layerId = ~Id,
group= "Grid",
weight = 0.1,
opacity = 0,
fillOpacity = 1,
fill = TRUE,
stroke = FALSE,
fillColor = ~pal()(Value),
highlightOptions = highlightOptions(color = "white",
weight = 2,
bringToFront = TRUE),
label = labels(),
labelOptions = labelOptions(style = list("font-weight" = "normal", padding = "3px 8px"),
textsize = "15px",
direction = "auto")
)
})
# === THIS IS JUST EXAMPLE OF Observer that DOES work?!
# Click event for the map (will use to generate chart)
click_element <- eventReactive(input$map_shape_click, {
input$map_shape_click$id
})
# highlight the clicked element
observe({
req(click_element()) # do this if click_element() is not null
# Add the clicked element to the map in aqua, and remove when a new one is clicked
map <- leafletProxy('map') %>%
removeShape('element') %>%
addPolygons(data = sp.grid()[sp.grid()$Id == click_element(), ],
fill = TRUE,
color = '#00ffff', opacity = 1, layerId = 'element')
})
leafletOutput('map')
When I run this code, the Rmarkdown console gives error of something like and immediately crashes:
Error in : Result must have length 2100500, not 0
90 <Anonymous>
Note the actual number (90) varies but it is usually always >85 and it is the only line output in the Rmarkdown console.
Noteworthy: The 2100500 is the number of records in my non-spatial dataframe (that is filtered by user inputs and merged with spatial polygon (4201 polygons).
Therefore, it looks like the filtering isn't applying correctly, but then how come this works when i simply move it into the leaflet() call?
I am making an R leaflet map (not Shiny) and I have two control groups, and based on the selection I would like a different legend to become visible. Currently I only manage to have both legends visible at all time.
Below is the code for the leaflet map, and the output can be seen in the image.
leaflet() %>% addSearchOSM() %>%
addProviderTiles(providers$CartoDB.Positron,
options = providerTileOptions(noWrap = TRUE),
group = "kaart") %>%
# addFullscreenControl() %>%
addCircleMarkers(data = table#data,
lat = ~lng,
lng = ~lat,
color = ~palverbruikplaats(Verbruiksplaats),
label = bepaalPopup(),
group = "Verbruikplaatscircles"
)%>%
addCircleMarkers(data = table#data,
lat = ~lng,
lng = ~lat,
color = ~palstatus(`Status omschrijving`),
label = bepaalPopup(),
group = "statuscircles"
)%>%
leaflet::addLegend("bottomleft", pal = palverbruikplaats, values = verbruikplaatsuniek, title = "Legenda") %>%
leaflet::addLegend("bottomleft", pal = palstatus, values = statusuniek, title = "Legenda") %>%
addLayersControl(baseGroups = c("Verbruikplaatscircles", "statuscircles"),
options = layersControlOptions(collapsed = FALSE))
In your addLayersControl did you mean to set the overlayGroups argument instead of baseGroups?
library(leaflet)
leaflet() %>%
addTiles(group = "OpenStreetMap") %>%
addCircleMarkers(runif(20, -75, -74), runif(20, 41, 42), group = "Markers1", color ="red") %>%
addMarkers(runif(20, -75, -74), runif(20, 41, 42), group = "Markers2") %>%
addLegend(values = 1, group = "Markers1", position = "bottomleft", labels = "1", colors= "red") %>%
addLegend(values = 2, group = "Markers2", position = "bottomleft", labels = "2" ,colors= "blue") %>%
addLayersControl(overlayGroups = c("Markers1", "Markers2"),
options = layersControlOptions(collapsed = FALSE))
what you need to do is, you need to make your legends values reactive
addLegend("bottomright", pal = pal, values = maindata#data[,req_var1()],
you can declare the req_var1() in server before calling
req_var1<-reactive({if(input$`Comparison Metric`=="Current Territory Factors vs GeoProxy Smoothing"){
paste(input$Curr2,"Curr",sep="_")
} else if(input$`Comparison Metric`=="Current Written Premium Vs Indicated Written Premium"){
paste(input$Curr2,"CWP",sep="_")
}
})
and also the pal can be declared as
pal1 <- reactive({if(input$ColorType=="Percentile"){
colorQuantile(
palette = "Spectral",
domain = tempdata()#data[,req_var1()],
probs = if(input$`Comparison Metric`=="Current Territory Factors vs GeoProxy Smoothing"){seq(0,1,by=0.25)
} else if(input$`Comparison Metric`=="Current Written Premium Vs Indicated Written Premium"){
seq(0,1,by=0.5)
}
## In case of Current written premium the variation is very less so while executing color mapping code is throwing error.
## This is because the some of quantiles values are not differentiable.
## So in colorQuantile function we have given two different prob values depending on metric selection.
)
} else if(input$ColorType=="Absolute Value"){colorNumeric(
palette = "Spectral",
domain = tempdata()#data[,req_var1()])
}else{print("Plese select Any one color map")}
})
I added points on a map using shiny and leaflet.
Each point is a different type of transit option.
I want to distinguish the different types by color and couldn't figure this out.
Tried using "if" which doesn't work.
Thanks!
This is the basic code I have
library(leaflet)
ui <- fluidPage(
leafletOutput("map"),
headerPanel("Example"),
sidebarPanel(checkboxGroupInput(inputId = "Type", label = "Data
Layer",choices = c("Bike", "Muni", "Bus", "BART"), selected = "Bike")))
server <- function(input, output) {
output$map <- renderLeaflet({
rownumber = which(Stops_and_stations$Type == input$Type)
x <- Stops_and_stations[rownumber,]
leaflet(width = 1000, height = 500) %>%
addTiles() %>%
addCircleMarkers(lng = x$stop_lon,
lat = x$stop_lat,
radius= 3, color = '#ff6633') %>%
setView(lng = -122.4000,
lat = 37.79500,
zoom = 13)
})
}
shinyApp(ui, server)
And this is what I tried to add
.....
if(input$Type == "Bike"){
leaflet(width = 1000, height = 500) %>%
addTiles() %>%
addCircleMarkers(lng = x$stop_lon,
lat = x$stop_lat,
radius= 3, color = '#ff6633') %>%
setView(lng = -122.4000,
lat = 37.79500,
zoom = 13)
}
if(input$Type == "Muni"){
leaflet(width = 1000, height = 500) %>%
addTiles() %>%
addCircleMarkers(lng = x$stop_lon,
lat = x$stop_lat,
radius= 3, color = '#0033ff') %>%
setView(lng = -122.4000,
lat = 37.79500,
zoom = 13)
}
.....
It would be much easier to answer to your question if you provided Stops_and_stations and thus made it a reproducible example..
One approach to use distinct colors for different groups is to add a color column to your data.frame:
Since we don't know your data, I created some random dataset.
Stops_and_stations <- data.frame(
Type = rep(c("Bike", "Muni", "Bus", "BART"), each = 10),
stop_lon = -runif(40, 122.4200, 122.4500),
stop_lat = runif(40, 37.76800, 37.78900),
color = rep(c("Red", "Blue", "Green", "Yellow"), each = 10)
)
Then instead of specifying a concrete color such as #ff6633, you can use the color column.
addCircleMarkers(lng = x$stop_lon,
lat = x$stop_lat,
radius= 3, color = x$color)
I would also like to point out that your subsetting is not right: you are using checkboxGroupInput which can have more values, so you need to use the %in% operator to filter.
x <- Stops_and_stations[Stops_and_stations$Type %in% input$Type,]
AIM
Create a leaflet map using Shiny that represents one set of data with circle marker and add a markers for points using a second set of data.
ISSUE
The "circle" markers are working, but "markers" are not. The "addMarkers" code is not being read or is being ignored.
SERVER
library(shiny)
library(leaflet)
server <- function(input, output, session) {
points <- read.csv(textConnection("Loc,STZip,Lat,Long,Vol
Loc1,17699,40.0185,-76.297582,15
Loc2,76177,32.949819,-97.31406,20
Loc3,27801,35.935125,-77.77076,17
Loc4,52404,41.947335,-91.68819,12
Loc5,19380,39.983108,-75.59332,18
"))
newpoints <- read.csv(textConnection("Loc,STZip,Lat,Long,Vol
Loc6,18640,41.317242,-75.77942,12
Loc7,38133,35.208709,-89.80518,20
"))
output$mymap <- renderLeaflet({
leaflet() %>%
addProviderTiles("Stamen.TonerLite",
options = providerTileOptions(noWrap = TRUE)) %>%
addCircleMarkers(lng = ~Long, lat = ~Lat, radius = ~Vol, layerId = NULL,
group = "NGS_Facilities", stroke = TRUE, color = "#0000CC", weight = 5, opacity = 0.5,
fill = TRUE, fillColor = "#0000CC", fillOpacity = 0.2, dashArray = NULL,
popup = ~Loc, options = pathOptions(), clusterOptions = NULL, clusterId = NULL,
data = (newpoints)) %>%
#this code is not being read or is ignored...
addMarkers(lng = ~Long, lat = ~Lat, popup = ~Loc, data = (newpoints))
})
}
UI
library(shiny)
library(leaflet)
r_colors <- rgb(t(col2rgb(colors()) / 255))
names(r_colors) <- colors()
ui <- fluidPage(
title = "Map of Stuff",
leafletOutput("mymap", width = 1800, height = 800),
p()
)
This is a weird error... fought with it for a while, until I realized it was a problem with how you read your data.
> newpoints
Loc STZip Lat Long Vol
1 Loc6 18640 41.31724 -75.77942 12
2 Loc7 38133 35.20871 -89.80518 20
3 NA NA NA NA
Because your end quote is on a new line, it leaves a break. This causes the last line in your data to be NAs. When I was debugging, it seemed like anything I put before the data would display, but after would fail.
To fix this, read your data as:
points <- read.csv(textConnection("Loc,STZip,Lat,Long,Vol
Loc1,17699,40.0185,-76.297582,15
Loc2,76177,32.949819,-97.31406,20
Loc3,27801,35.935125,-77.77076,17
Loc4,52404,41.947335,-91.68819,12
Loc5,19380,39.983108,-75.59332,18"))
newpoints <- read.csv(textConnection("Loc,STZip,Lat,Long,Vol
Loc6,18640,40.0185,-76.297582,12
Loc7,38133,35.208709,-89.80518,20"))
For whatever reason, Leaflet bugs out if the last row is all NAs