Related
I made the following map from a data frame that contains the number of parole of each state from the years 1995-2015 per 100,000 as well as the spatial information for each state. I want to incorporate it into r shiny app to have a slider to be able to choose the specific year and view it. I got the slider to work and change the data and when you first run it works and gives you the appropriate state and number. However, when you move around the slider the geospatial labels start moving around using the reactive and different states start getting different states labels. Like the following:
The slider starts at the year 2000 and as you can see the if I move it around it, in this case 2014, now we have florida being labeled as Montana.
All these was done within the R shiny app. This is the code I have below. I have my leaflet map fully created outside the server.
server <- function(input, output) {
#Set YEAR with Slider
state_parole_year <- reactive({
state_parole %>%
filter(year == year(input$year))
})
labels_year <- reactive({paste("Parole/100000 US Adults",
state_parole_year()$state, state_parole_year()$number_on_parole_per_100000_us_adult_residents)})
output$mymap <- renderLeaflet({
state_map %>%
addTiles()%>%
addPolygons(fillColor = ~ pal(state_parole_year()$number_on_parole_per_100000_us_adult_residents),
fillOpacity = 1,
color = "blue",
opacity = 0.1,
weight = 1,
highlight = highlightOptions(
weight = 3,
color = "blue",
fillOpacity = .2,
bringToFront = TRUE),
label = labels_year())
})
}
When I run the leaflet map outside of r shiny app and change the year manually by subsetting the csv it works perfectly. The problem occurs when I try to make the labels reactive to the slider. Does someone know how I can fix the problem? Thanks!
The problem is that you build the map on unfiltered data, then display it with filtered data. There is then a switch in factors.
A quick fix is to build your map on filtered data, directly in the server() function :
output$mymap <- renderLeaflet({
leaflet(data = state_parole_year()) %>%
addTiles() %>%
setView(lng = -80,
lat = 34.5,
zoom = 4) %>%
addPolygons(fillColor = ~ pal(state_parole$number_on_parole_per_100000_us_adult_residents),
fillOpacity = 1,
color = "blue",
opacity = 0.1,
weight = 1,
highlight = highlightOptions(
weight = 3,
color = "blue",
fillOpacity = .2,
bringToFront = TRUE),
label = labels) %>%
addLegend(
position = "topright",
pal = pal,
values = ~number_on_parole_per_100000_us_adult_residents,
title = "# of U.S. Adults on Parole/100000.",
opacity = 1) %>%
addTiles()%>%
addPolygons(fillColor = ~ pal(state_parole_year()$number_on_parole_per_100000_us_adult_residents),
fillOpacity = 1,
color = "blue",
opacity = 0.1,
weight = 1,
highlight = highlightOptions(
weight = 3,
color = "blue",
fillOpacity = .2,
bringToFront = TRUE),
label = ~labels_year())
})
I am trying to add layers to a leaflet map in which they layers can toggle the circle markers by ranges (0 - 100, 100 - 200, etc) but for some reason when I toggle the 100 - 200ft layer nothing changes on the map. The 'All' and 'AMA' layers will toggle.
Moreover the 100-200 layer is not displaying the data that is within the b dataframe. The b dataframe only contains values from 100 - 200 in the dtw column but the markers on the map are displaying below 100 and above 200 values.
However the x dataframe will display the entire dataframe from 0 -1000 and will toggle correctly.
All of the sf objects are in the same CRS and are of the same class.
I am new to leaflet and layer controls and I would really appreciate some help. Here are images of the leaflet map not toggling correctly. Thanks!
x = usgs_spatial %>% st_transform(4326)
ama3 = ama %>% st_transform(4326)
pal1 = RColorBrewer::brewer.pal(9,"Blues")
pal2 = RColorBrewer::brewer.pal(9,"YlOrRd")
pals1 = colorNumeric(pal1, domain = x$dtw)
pals2 = colorNumeric(pal2, domain = x$dtw)
pals3 = colorBin("magma", domain = 1:8)
a = dtw_range(x, 0, 100) %>% select(wellid, date, dtw, measurement_dist)
b = dtw_range(x, 100, 200) %>% select(wellid, date, dtw, measurement_dist)
c = dtw_range(x, 200, 300) %>% select(wellid, date, dtw, measurement_dist)
leaflet() %>%
addProviderTiles(providers$CartoDB.Positron,[enter image description here][1] group = 'Tiles') %>%
addCircleMarkers(data = b, #clusterOptions = markerClusterOptions(interactive()),
color = ~pals2(dtw), fillOpacity = .5,
stroke = FALSE,
popup = leafpop::popupTable(st_drop_geometry(x[,c(4, 7, 8, 13)]),
feature.id = FALSE,
row.numbers = FALSE), group = '100 - 200 ft') %>%
addCircleMarkers(data = x, #clusterOptions = markerClusterOptions(interactive()),
color = ~pals2(dtw), fillOpacity = .5,
stroke = FALSE,
popup = leafpop::popupTable(st_drop_geometry(x[,c(4, 7, 8, 13)]),
feature.id = FALSE,
row.numbers = FALSE), group = 'All') %>%
addPolygons(data = ama3,
fillColor = ~pals3(OBJECTID),
color = 'black',
label = ~MAP_LABEL, group = 'AMA') %>%
addLayersControl(overlayGroups = c('All', '100-200 ft', 'AMA'), baseGroups = c("Tiles"))
I figured it out, I had not changed the x argument in the popupTable() to the appropriate dataframe, and I also had spaces between the '-' in the addlayGroup() for '100-200 ft' which is why the toggle option was not being picked up.
In the tiny example shown below, I have two features associated with each country (polygons) in the map, namely: randomA, randomB. Each feature has its own legend, so I armed a group named "randomA" containing the polygons coloured with feature randomA and its corresponding legend. I did the same for group "randomB". When the map is depicted, leaflet correctly shows or hides polygons for features "randomA" and "randomB". However legends are always shown stacked on the bottom right corner.
This is the code:
library(rgdal)
library(leaflet)
# From http://data.okfn.org/data/datasets/geo-boundaries-world-110m
countries <- readOGR("json/countries.geojson")
n <- nrow(countries)
# Add two random fields
set.seed(15)
countries#data$randomA <- rnorm(n, 1000, 250)
countries#data$randomB <- rnorm(n, 10000, 3000)
map <- leaflet(countries) %>% addTiles()
pal <- colorNumeric(
palette = "YlGnBu",
domain = countries$randomA
)
map <- map %>%
addPolygons(stroke = FALSE, smoothFactor = 0.2, fillOpacity = 1,
color = ~pal(randomA), group = "randomA"
) %>%
addLegend("bottomright", pal = pal, values = ~randomA,
title = "random A",
labFormat = labelFormat(prefix = "$"),
opacity = 1, group = "randomA"
)
qpal <- colorQuantile("RdYlBu", countries$gdp_md_est, n = 5)
map <- map %>%
addPolygons(stroke = FALSE, smoothFactor = 0.2, fillOpacity = 1,
color = ~qpal(randomB), group = "randomB"
) %>%
addLegend(
"bottomright",
pal = qpal,
values = ~randomB,
opacity = 1, group = "randomB"
)
# Finally control layers:
map <- map %>%
addLayersControl(
baseGroups = c("randomA", "randomB"),
position = "bottomleft",
options = layersControlOptions(collapsed = F)
)
map
A snapshot of the result is shown in the image below:
Also, in the actual problem I have to represent nine of these groups, so I wish I had all the legends in the same place.
Do you have any suggestion?
Try using overlay groups instead of base groups:
addLayersControl(
overlayGroups = c("randomA", "randomB"),
position = "bottomleft",
options = layersControlOptions(collapsed = F)
)
I am trying to create a high-quality map of a small part of the UK, without any distortions caused by the use of projections, and with the addition of markers consisting of text and symbols. Ultimately the goal is to write out a png or pdf file. An earlier, related question can be found here.
Having not used R in anger for several years, I have been wading through a morass of packages trying to find something suitable. Leaflet for R is promising, but although I can create a decent-looking map, add markers, and vary the colour of markers and so on using columns from a data frame, I have not been able to vary the size, colour, and text offsets used in the labelOptions argument.
The following reproducible example shows what I can achieve, and also where I am not succeeding. I would like the size of text label to vary according to the df.data$textsizes column. Given that the style argument takes a list of value pairs, that would seem difficult, and nothing has worked so far.
If am hoping that somebody can either suggest either a way to bend the wily labelOptions to my will, or a completely different approach to try.
require(leaflet)
require(magrittr)
df.entrynames <- c("Entry 1: Some text","Entry 2: More text")
df.lat <- c(51.509898,51.510736)
df.lon <- c(-0.1345093,-0.135190)
df.colors <-c("Blue","Red")
df.sizes <-c(36,12)
df.data <- data.frame(entrynames=df.entrynames,lat=df.lat,lon=df.lon,colors=df.colors,textsizes=df.sizes)
df.data$entrynames <- as.character(df.data$entrynames)
df.data$colors <- as.character(df.data$colors)
df.data$textsizes <- paste(df.data$textsizes,"px",sep="")
leaflet() %>% setView(lng = -0.134509, lat = 51.509898, zoom = 17) %>% addTiles() %>%
addCircleMarkers(data = df.data,
lat = ~lat, lng = ~lon,
label = df.data$entrynames,
color = df.data$colors,
labelOptions = labelOptions(noHide = TRUE,
style = list(
"color" = "gray30",
"font-family" = "serif",
"font-style" = "italic",
"box-shadow" = "3px 3px rgba(0,0,0,0.25)",
"font-size" = "14px",
"border-color" = "rgba(0,0,0,0.5)"
),
textOnly = FALSE,
offset=c(0,0)))
df.entrynames <- c("Entry 1: Some text","Entry 2: More text")
df.lat <- c(51.509898,51.510736)
df.lon <- c(-0.1345093,-0.135190)
df.colors <-c("Blue","Red")
df.sizes <-c(36,2)
df.data <- data.frame(entrynames=df.entrynames,lat=df.lat,lon=df.lon,colors=df.colors,textsizes=df.sizes)
df.data$entrynames <- as.character(df.data$entrynames)
df.data$colors <- as.character(df.data$colors)
df.data$textsizes <- paste(df.data$textsizes,"px",sep="")
#Add a vector to split the data by
df.data$place<-seq(1:nrow(df.data))
library(purrr)
#split the data
ob_place <- df.data %>%
split(., .$place)
#make a map
m <- leaflet() %>%
addTiles()
#Add layers
names(ob_place) %>%
purrr::walk(function(df.data) {
m<<-m %>% #seems like there's supposed to be two carrots here, i had problems without one
addCircleMarkers(data=ob_place[[df.data]],fillColor=~colors,
fillOpacity = 0.6,
weight=1,
radius=13,
color="white",
opacity = .6,
lng=~lon, lat=~lat,
group = "Show All",
label = ~entrynames,
labelOptions = labelOptions(noHide = T,
#direction = ~MyDirection, #https://rstudio.github.io/leaflet/popups.html
textsize = ~textsizes,
#opacity=~opacity,
style = list(
"color"="black",
"font-family" ="sans-serif",
"box-shadow" = "3px 3px rgba(0,0,0,0.25)",
#"font-size" = "12px",
"border-color" = "rgba(0,0,0,0.5)"
)))
})
m
Similar to setting the direction of labels
I'm building a leaflet map on R having multiple layers that are controlled by addLayersControl. Every layer as the same spatial information, so only the data associated to each polylines changes. The idea is to have a basic map, where the user decide which data field is display. I succeeded at making the map, however I noticed that the size of the html file produced is huge.
In my actual context, making the map with only one layer leads to a ~20mb file. However, if I add one field it gets to ~40mb and three layer ~60mb. So it seems to me that the html produced is loading the same shapefile 3 times instead of simply using one shapefile and linking it a data frame of some sort.
Am I stock with this behavior of leaflet or is there a way to file size inflation in my context? I may not have programmed my leaflet the better way...
I've made a reproducible example to show the problem. It uses a small shapefile so the size problem is not dramatic, however the point is the same, which is constantly doubling file size. Also, the example is lengthy, sorry about that, I could'n find a way to simplify it further.
Preparation:
# loading the libraries
library(sf)
library(leaflet)
library(htmlwidgets)
# preparing the shapefile
nc <- st_read(system.file("gpkg/nc.gpkg", package="sf"), quiet = TRUE) %>%
st_transform(st_crs(4326))
# preparing the colors (not really important)
pal.area <- colorNumeric(palette = "inferno", domain = range(nc$AREA))
pal.perim <- colorNumeric(palette = "inferno", domain = range(nc$PERIMETER))
pal.cnty <- colorNumeric(palette = "inferno", domain = range(nc$CNTY_))
pal.sid74 <- colorNumeric(palette = "inferno", domain = range(nc$SID74))
Making the leaflet, this section is long, however it's simply 4 leaflet maps created one after another by adding one layer at a time. It's mostly copy-pasted work:
###
one_layer <- leaflet(data = nc) %>%
addTiles() %>%
addPolylines(fillColor = ~pal.area(AREA),
fill = TRUE,
opacity = 0.8,
group = "area") %>%
addLegend("bottomright",
pal = pal.area, values = ~AREA,
opacity = 1, group = "area"
)
###
###
two_layers <- leaflet(data = nc) %>%
addTiles() %>%
addPolylines(fillColor = ~pal.area(AREA),
fill = TRUE,
opacity = 0.8,
group = "area") %>%
addLegend("bottomright",
pal = pal.area, values = ~AREA,
opacity = 1, group = "area") %>%
addPolylines(fillColor = ~pal.perim(PERIMETER),
fill = TRUE,
opacity = 0.8,
group = "perim") %>%
addLegend("bottomright",
pal = pal.perim, values = ~PERIMETER,
opacity = 1, group = "perim"
) %>%
addLayersControl(
overlayGroups = c("area", "perim"), position = "bottomleft",
options = layersControlOptions(collapsed = FALSE)
)
###
###
three_layers <- leaflet(data = nc) %>%
addTiles() %>%
addPolylines(fillColor = ~pal.area(AREA),
fill = TRUE,
opacity = 0.8,
group = "area") %>%
addLegend("bottomright",
pal = pal.area, values = ~AREA,
opacity = 1, group = "area") %>%
addPolylines(fillColor = ~pal.perim(PERIMETER),
fill = TRUE,
opacity = 0.8,
group = "perim") %>%
addLegend("bottomright",
pal = pal.perim, values = ~PERIMETER,
opacity = 1, group = "perim"
) %>%
addPolylines(fillColor = ~pal.cnty(CNTY_),
fill = TRUE,
opacity = 0.8,
group = "cnty") %>%
addLegend("bottomright",
pal = pal.cnty, values = ~CNTY_,
opacity = 1, group = "cnty"
) %>%
addLayersControl(
overlayGroups = c("area", "perim", "cnty"), position = "bottomleft",
options = layersControlOptions(collapsed = FALSE)
) %>%
hideGroup(c("perim","cnty"))
###
###
four_layers <- leaflet(data = nc) %>%
addTiles() %>%
addPolylines(fillColor = ~pal.area(AREA),
fill = TRUE,
opacity = 0.8,
group = "area") %>%
addLegend("bottomright",
pal = pal.area, values = ~AREA,
opacity = 1, group = "area") %>%
addPolylines(fillColor = ~pal.perim(PERIMETER),
fill = TRUE,
opacity = 0.8,
group = "perim") %>%
addLegend("bottomright",
pal = pal.perim, values = ~PERIMETER,
opacity = 1, group = "perim"
) %>%
addPolylines(fillColor = ~pal.cnty(CNTY_),
fill = TRUE,
opacity = 0.8,
group = "cnty") %>%
addLegend("bottomright",
pal = pal.cnty, values = ~CNTY_,
opacity = 1, group = "cnty"
) %>%
addPolylines(fillColor = ~pal.sid74(SID74),
fill = TRUE,
opacity = 0.8,
group = "sid74") %>%
addLegend("bottomright",
pal = pal.sid74, values = ~SID74,
opacity = 1, group = "sid74"
) %>%
addLayersControl(
overlayGroups = c("area", "perim", "cnty", "sid74"), position = "bottomleft",
options = layersControlOptions(collapsed = FALSE)
) %>%
hideGroup(c("perim","cnty", "sid74"))
###
Then, you get 4 objects (maps) we can compare their size directly in R:
object.size(one_layer)
301864 bytes
object.size(two_layers)
531144 bytes
object.size(three_layers)
681872 bytes
object.size(four_layers)
828616 bytes
The size increase is constant and way higher that what we would expect if the only the data was added instead of all the spatial info. As a comparison, the initial shape which has 15 fields is of size:
object.size(nc)
135360 bytes
If we save the maps to HTML, the problem is even more visible:
saveWidget(one_layer, paste0(getwd(),"/temp_data/temp/one_layer.html"), selfcontained = F)
saveWidget(two_layers, paste0(getwd(),"/temp_data/temp/two_layers.html"), selfcontained = F)
saveWidget(three_layers, paste0(getwd(),"/temp_data/temp/three_layers.html"), selfcontained = F)
saveWidget(four_layers, paste0(getwd(),"/temp_data/temp/four_layers.html"), selfcontained = F)
file.info(list.files("temp_data/temp", pattern = ".html$", full.names = T))$size[c(2,4,3,1)] %>%
setNames(c("One Layer", "Two Layers", "Three Layers", "Four Layers")) %>%
barplot(ylab="size in Bytes")
It's clearly doubling in size.
So, to summarize, is there a way to get leaflet to not reproduced the spatial information when adding multiple fields of data to the same map?