I am making an R Shiny app where users will enter their city, state, and zip code, press a button, and then their location (Lat, Lon) will become the new center of the map. The inputs are collected via this section of code in the server.R:
output$ntextCounter <- eventReactive(input$goButton, {
citySelected <- as.character(input$city)
stateSelected <- as.character(input$state)
zipCodeSelected <- as.character(input$zipCode)
location2 <- stri_detect(paste(as.character(input$city),
as.character(input$state), as.character(input$zipCode), sep=", "), fixed = locationData$Location, opts_regex=stri_opts_regex(case_insensitive=TRUE))
counter <<- counter + 1
lat1 <- as.numeric(locationData[which(location2),]$latitude)
lon1 <- as.numeric(locationData[which(location2),]$longitude)
return(c(lat1, lon1))
})
I am able to easily view the new latitude/longitude values in the UI using:
verbatimTextOutput("ntextCounter")
But I need to be able to pass these values, "return(c(lat1, lon1))", to the center = c(Lat, Lon) in the leaflet map in the ui.r:
leafletMap("map", "100%", 365,
initialTileLayer = "http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
initialTileLayerAttribution = HTML('© OpenStreetMap contributors, CC-BY-SA'),
options=list(center = c(38.25, -93.85), zoom = 4, maxBounds = list(list(1, -180), list(78, 180))
)),
I have an initial map center at c(38.25, -93.85), but ultimately I want to be able to pass it changing values from ntextCounter. I'm not sure if this is a scoping issue or what but I need help getting the new lat/lon values into the leaflet map center.
Any assistance would be greatly appreciated. Thanks in advance.
It seems you are creating your leaflet on the ui-side. If you want it to be responsive to inputs you've got to do that on the server side with renderLeaflet.
Your coordinates could be stored in a reactiveValues and you'd update them with a observeEvent:
location <- reactiveValues(lat = 38.25, lon = -93.85)
observeEvent(input$goButton, {
city <- as.character(input$city)
state <- as.character(input$state)
zipCode <- as.character(input$zipCode)
newloc <- stri_detect(paste(city, state, zipCode, sep=", "),
fixed = locationData$Location,
opts_regex=stri_opts_regex(case_insensitive=TRUE))
location$lat <- as.numeric(locationData[which(newloc),]$latitude)
location$lon <- as.numeric(locationData[which(newloc),]$longitude)
})
Related
Some context: I want to use the layerId (as row index) from the marker a click on the leaflet map to extract another value from a data frame. The data frame is "def_veh" and has columns named "ID_Fahrzeug" and "Postleitzahl".
So I set the layerId of my markers equal to ID_Fahrzeug, so that when I observe a click on a marker the event$id returns me the value of "ID_Fahrzeug" for this marker as a character.
In the showPopup function, I would like to use this returned value to extract the value from the data frame "def_veh" that is located in the column "Postleitzahl" in the same row as the event$id value in the column "ID_Fahrzeug".
I want then to store this extracted value in the variable "codepost" to use it as input for a following function used to plot a chart.
Here is a code sample of what I tried to explain in words:
#map function
output$map <- renderLeaflet({
leaflet(def_veh) %>% #creates a map based on the coordinates of damages
addTiles() %>%
addMarkers(lng= ~Laengengrad, lat = ~Breitengrad, clusterOptions = markerClusterOptions(), layerId= ~ID_Fahrzeug) %>% #creates cluster markers to groups the points on the map where a damage occurred
setView(lng = 11.107353, lat = 50 , zoom = 7) #fits the map's boundaries on Germany (more or less)
})
# When map is clicked, show a popup with city info
showPopup <- function(id_veh, Breitengrad_OEM, Laengengrad_OEM) {
codeposte <- def_veh[def_veh$ID_Fahrzeug==id_veh,"Postleitzahl"]
Plot <- Plot_Bar_Cart(codeposte)
svg(filename= paste(folder,"plot.svg", sep = "/"),
width = 500 * 0.005, height = 300 * 0.005)
print(Plot)
dev.off()
content <- paste(popup_content,readLines(paste(folder,"plot.svg",sep="/")), collapse = "")
leafletProxy("map") %>% addPopups(Laengengrad_OEM, Breitengrad_OEM, content, layerId = ID_Fahrzeug)
}
observe({
leafletProxy("map") %>% clearPopups()
event <- input$map_marker_click
if (is.null(event))
return()
isolate({
showPopup(event$id, event$lat, event$lng)
})
})
The problem is now, that when I run my shinyApp and click on a marker on the map, the windows closes by itself... This is the error message that appears: "Error in base::try(showPopup, silent = TRUE) :
object 'showPopup' not found"
Can someone help me to locate the problem? :)
I'm using the leaftlet.extras R package to add Gps control inside a map.
I'm using the extension addControlGPS inside my code :
... %>%
addControlGPS(options = gpsOptions(position = "topleft", activate = TRUE,
autoCenter = TRUE, maxZoom = 60,
setView = TRUE)) %>%
...
The controller works ok.
I need to extract the Gps coordinates to re-use in my code as arguments for other functions. Is there any way to do that ?
Every time the gps location updates, the coordinates are written to map.id+'_gps_located'. You can find all leaflet.extras bindings in the htmlwidgets/bindings folder in their git.
Working example
library(leaflet)
library(leaflet.extras)
library(shiny)
ui <- fluidPage(
leafletOutput('map')
)
server <- function(input, output, session) {
output$map <- renderLeaflet({ leaflet()%>%addTiles() %>%
addControlGPS(options = gpsOptions(position = "topleft", activate = TRUE,
autoCenter = TRUE, maxZoom = 60,
setView = TRUE))})
observe(
print(input$map_gps_located)
)
}
shinyApp(ui, server)
I've recently had a similar problem with an app I was working on.
You can extract the gps coordinates from a leaflet map by using the _marker_click feature, where is the map label you specify as an output for the leaflet rendering statement.
In my case here's the chunk of code I used to retrieve the coords. In my case the output name of the map object was parksMap therefore the full input to consider in the event observation was parksMap_marker_click. This statement can be saved in a variable (in my case pin), that stores the coordinates data. Finally you need to wrap it all in a reactive expression to be able to save every coordinate when clicking on a point in the leaflet map.
# code to load the park card once the click event on a marker is intercepted
observeEvent(input$parksMap_marker_click, {
pin <- input$parksMap_marker_click
#print(Sys.time()) #uncomment to log coords
#print(pin) #uncomment to log coords
selectedPoint <- reactive(parks[parks$Latitude == pin$lat & parks$Longitude == pin$lng,])
leafletProxy("parksMap", data = selectedPoint()) %>% clearPopups() %>%
addPopups(~Longitude,
~Latitude,
popup = ~park_card(selectedPoint()$ParkName, selectedPoint()$ParkCode, selectedPoint()$State, selectedPoint()$Acres, selectedPoint()$Latitude, selectedPoint()$Longitude)
)
})
The full github repo of the app is available here.
I have a leaflet map which I created in R and I would like to add a dropdown filter based on fields in a column. The code looks straightforward in JS, see example here Leaflet dropdown filter, however I can't figure out how to adjust the code for R.
library(leaflet.extras)
n = c(2, 3, 5)
long = c(102.1,102.13,102.2)
lat = c(55,55.1,55.15)
select_cols= c("a","b","c")
df = data.frame(n, long, lat,select_cols)
pal <- colorFactor(c("navy", "red","green"), domain = c("a","b","c"))
leaflet(df)%>% addTiles()%>%
addCircleMarkers(lng = long,lat=lat,radius= ~n*2,
color=~pal(select_cols),stroke=F,fillOpacity = 1)
Here is the JS code that adds a dropdown filter.
var legend = L.control({position: 'topright'});
legend.onAdd = function (map) {
var div = L.DomUtil.create('div', 'info legend');
div.innerHTML = '<select><option>1</option><option>2</option>
<option>3</option></select>';
div.firstChild.onmousedown = div.firstChild.ondblclick =
L.DomEvent.stopPropagation;
return div;
};
legend.addTo(map);
I know how to add the "show hide layers" feature, however I have over 20 different fields and I think it would be easier if the user could select the associated field using a drop down box.
In case anyone is looking for something similar, I found a solution using crosstalk. See the code below for an example.
library(crosstalk)
library(tidyverse)
library(leaflet.extras)
quakes <- quakes %>%
dplyr::mutate(mag.level = cut(mag,c(3,4,5,6),
labels = c('3.01-4.00', '4.01-5.00', '5.01-6.00')))
quakes_sd<- SharedData$new(quakes)
map<- leaflet(quakes_sd)%>%addProviderTiles(providers$Esri.WorldTopoMap)%>% addCircles()
#add filter
bscols(
filter_select("Magnitude Level", "Magnitude Level", quakes_sd, ~mag.level)
)
bscols(map)
I have a map that I generate using Leaflet in an R/Shiny app, and am able to listen to event clicks on points on the map using code like this
observeEvent(input$map_marker_click, {
p = input$map_marker_click
p1 = filteredData()[filteredData()$Longitude == p$lng & filteredData()$Latitude == p$lat),]
if(p$id != 'Selected') {
//do stuff
}
output$ggplot = renderPlot({
//change plot based on selected point
})
}
But I'm wondering if it's possible to allow a user to select multiple points at once -- perhaps by shift-select or some other way. Would I need to somehow add a listener to see if they are holding down SHIFT, and then pass along a vector of clicked points?
EDIT:
I found an example that shows how to use MapEdit with selectFeatures to multi-select points on a leaflet map and then "do something" with the selection.
Here's the code sample along (taken from: https://gis.stackexchange.com/questions/253483/leaflet-tool-for-multiple-marker-selection-and-computation-of-summary)...:
# devtools::install_github("r-spatial/mapedit")
library(sf) # for spatial data type representation
library(mapview) # for the raster data and quick viewing
library(mapedit) # for the interaction (selection of the data)
# create the base map with a raster layer
m = mapview(poppendorf[[5]])
# create some mock data points in the vicinity of the raster layer
set.seed(42) # to be reproducible
dframe = data.frame(a = 1:50,
b = rnorm(50, 2, 1),
x = runif(50, 11.15, 11.25),
y = runif(50, 49.7, 49.75))
# convert data.frame to sf object as we need
# geo-spatial data type for this kind of objective
# epsg 4326 is geographic longlat 'projection'
dframe_sf = st_as_sf(dframe, coords = c("x", "y"), crs = 4326)
# inspect data on base map
mapview(dframe_sf, map = m)
# select features via polygon/rectangle/line/point or the like
# by using the draw tools of the draw toolbar on the left
# and press "Done" when finished.
# multiple selections are also possible.
# (line/point selection will not work as points have no dimension!)
# mode = "click" will enable selection via clicking on features.
selected = selectFeatures(dframe_sf, map = m, mode = "draw")
# check the selection (selected will be diplayed in blue)
mapview(dframe_sf, map = m, col.regions = "red") + selected
# given that selected is a sf object (and hence a data.frame)
# claculating summaries works just as expected with a normal data.frame
summary(selected)
mean(selected$a)
mean(selected$b)
sd(selected$b)
# we can also set other selection criteria. e.g. invert selection via st_disjoint
diff_selected = selectFeatures(dframe_sf, map = m, mode = "draw", op = st_disjoint)
# check the selection (selected will be diplayed in blue)
mapview(dframe_sf, map = m, col.regions = "red") + diff_selected
I understand that the following line is responsible for taking the SF dataframe and allowing us to select points on the map:
selected = selectFeatures(dframe_sf, map = m, mode = "draw")
My question is this -- how do I get it to render that same view but in Shiny, as opposed to my R console?
In Shiny, we have to wrap the leaflet map in the renderLeaflet function...
If I were doing it without Shiny, I could just do something like this:
m = leaflet(data = df) %>%
addCircleMarkers() %>%
addLegend...
selected = selectFeatures(dframe_benthic_sf, map = m, mode = "click")
Good afternoon everyone, I think a screenshot will tell better than words:
I would like a real marker instead of what is actually printed...
Here is the corresponding server.R part of the code (where fixed_points is a nrow(fixed_points) x 2 matrix with lat/lon coordinates
library(shiny); library(rCharts); library(leaflet)
output$baseMap <- renderMap({
baseMap <- Leaflet$new()
baseMap$setView(c(lng = 2.812500, lat = 46.732331), zoom = 6)
baseMap$tileLayer(provider = "Stamen.TonerLite")
for (i in 1:nrow(fixed_points)) {baseMap$marker(c(fixed_points[i, "lat"], fixed_points[i, "lon"]))}
baseMap$fullScreen(TRUE)
baseMap
})
and the ui.R
showOutput("baseMap", "leaflet") // or chartOuput(...), it works the same
Thanks in advance!