I recently found this shape file of NYC bus routes shape file of NYC bus routes (zip file) that I am interested in plotting with the leaflet package in R.
When I attempt to do so, some routes do not show up on the map. I can tell they're missing because I overlay the bus stop data and some do not line up with the routes.
When I read in the shape file, I notice that the spatial lines data frame that is created has nested lists, which I think leaflet is not mapping.
What do I need to do so that leaflet reads coordinates of these missing routes? Below is the code I used to produce the map with missing routes:
bus <- readOGR(dsn = path.expand("bus_route_shapefile"), layer = "bus_route_shapefile")
bus.pj <- spTransform(bus, CRS("+proj=longlat +datum=WGS84"))
bus.st <- readOGR(dsn = path.expand("bus_stop_shapefile"), layer = "bus_stop_shapefile")
bus.st.pj <- spTransform(bus.st, CRS("+proj=longlat +datum=WGS84"))
bus_map <- leaflet() %>%
setView(lng = -73.932667, lat = 40.717266, zoom = 11) %>%
addPolylines(data = bus.pj, color = "black", opacity = 1) %>%
addCircles(data=bus.st.pj#data,~stop_lon, ~stop_lat, color = "red") %>%
addTiles()
bus_map
It would be easier to help you if you provided not only bus_routes but also bus_stop (zip file). You can solve it by converting bus.pj into new SpatialLinesxxx obj where each class Lines has only one class Line. SLDF below code makes doesn't have bus.pj#data$trip_heads because of unknown.
library(dplyr); library(sp); library(leaflet)
## resolve bus.pj#lines into list(Line.objs) (Don't worry about warnings)
Line_list <- lapply(bus.pj#lines, getLinesLinesSlot) %>% unlist()
## If you want just Lines infromation, finish with this line.
SL <- sapply(1:length(Line_list), function(x) Lines(Line_list[[x]], ID = x)) %>%
SpatialLines()
## make new ids (originalID_nth)
ori_id <- getSLLinesIDSlots(bus.pj) # get original ids
LinLS <- sapply(bus.pj#lines, function(x) length(x#Lines)) # how many Line.obj does each Lines.obj has
new_id <- sapply(1:length(LinLS), function(x) paste0(x, "_", seq.int(LinLS[[x]]))) %>%
unlist()
## make a new data.frame (only route_id)
df <- data.frame(route_id = rep(bus.pj#data$route_id, times = LinLS))
rownames(df) <- new_id
## integrate Line.objs, ids and a data.frame into SpatialLinesDataFrame.obj
SLDF <- mapply(function(x, y) Lines(x, ID = y), x = Line_list, y = new_id) %>%
SpatialLines() %>% SpatialLinesDataFrame(data = df)
leaflet() %>%
setView(lng = -73.932667, lat = 40.717266, zoom = 11) %>%
addPolylines(data = SLDF, color = "black", opacity = 1, weight = 1) %>%
addCircles(data=bus.st.pj#data,~stop_lon, ~stop_lat, color = "red", weight = 0.3)
Related
I want to loop through a dataframe that contains different monitoring sites and produce an individual leaflet map for each site.
See the minimal example below. I realize the loop, as written, will just overwrite each map. In my actual file, each site will have its own individual .html file with some ancillary information specific to each site produced at the end of the loop.
Is it possible to create leaflet maps in this way while retaining the for-loop? Do I need to add in a step that filters the larger dataframe based on the current site in the loop, and use that? Thanks in advance!
library(leaflet)
library(dataRetrieval)
# Create dataframe with 3 example sites
site1 <- readNWISsite(372322081241501)
site2 <- readNWISsite(380252079472801)
site3 <- readNWISsite(372223080234801)
df <- rbind(site1, site2)
df <- rbind(df, site3)
# Create a location map for each individual site in df
map <- leaflet()
q <- length(df$site_no)
for (j in 1:q) {
map %>%
addCircleMarkers(
lng = df[j]$dec_long_va,
lat = df[j]$dec_lat_va,
radius = 9.0,
color = "black",
weight = 1,
fillColor = "gray",
stroke = T,
fillOpacity = 1.0
)
}
# Error in validateCoords(lng, lat, funcName) :
# addCircleMarkers requires non-NULL longitude/latitude values
First, the issue with your code is that you have use df[j, ] instead of df[j] to get the first row of your data. But IMHO you could achieve your result much easier by using a list and to use lapply to read your data and to create your maps. Not sure what's the next step in your workflow. As you say that you want to add the maps to a HTML document per site you could for example loop over the list of maps to add them to create your documents.
library(leaflet)
library(dataRetrieval)
siteNumbers <- c(372322081241501, 380252079472801, 372223080234801)
names(siteNumbers) <- paste0("site", seq_along(siteNumbers))
sites <- lapply(siteNumbers, readNWISsite)
lapply(sites, function(x) {
leaflet() %>%
addCircleMarkers(
data = x,
lng = ~dec_long_va,
lat = ~dec_lat_va,
radius = 9.0,
color = "black",
weight = 1,
fillColor = "gray",
stroke = T,
fillOpacity = 1.0
)
})
#> $site1
#>
#> $site2
#>
#> $site3
But just in case. If you want to stick with the for loop you could achieve the same result like so:
library(leaflet)
library(dataRetrieval)
# Create dataframe with 3 example sites
site1 <- readNWISsite(372322081241501)
site2 <- readNWISsite(380252079472801)
site3 <- readNWISsite(372223080234801)
df <- rbind(site1, site2)
df <- rbind(df, site3)
map <- leaflet()
maps <- list()
for (j in seq(nrow(df))) {
maps[[j]] <- map %>%
addCircleMarkers(
lng = df[j, "dec_long_va"],
lat = df[j, "dec_lat_va"],
radius = 9.0,
color = "black",
weight = 1,
fillColor = "gray",
stroke = T,
fillOpacity = 1.0
)
}
maps
#> [[1]]
#>
#> [[2]]
#>
#> [[3]]
I would like to be able to find the centre point between two markers on a map (example below). Is there a function in leaflet or in another package that allows this? Thank you in advance
coor_zone6 <- c(3.16680, 3.16176, 42.46667, 42.46997)
matrice_coord_zone6 <- matrix(coor_zone6, nrow=2, ncol = 2)
colnames(matrice_coord_zone6) <- c("long", "lat")
matrice_coord_zone6 <- data.frame(matrice_coord_zone6)
matrice_coord_zone6$name <- c("M_1","M_3")
leaflet(matrice_coord_zone6) %>%
addMouseCoordinates(epsg = NULL, proj4string = NULL, native.crs = FALSE) %>%
addProviderTiles("Esri.WorldImagery") %>%
addMarkers(lng = ~long, lat = ~lat) %>%
addPolylines(~long, ~lat, popup = ~name)
I have not found any leaflet function that can perform this calculation, but it is not difficult to find the intermediate point between both coordinates.
You must add both longitudes and divide them by 2, you will have to do the same with both latitudes.
In your case, if I have not misunderstood, your first coordinate is (3.16680, 42.46667) and your second coordinate is (3.16176, 42.46997) so the calculation would be as follows:
(3,16680 + 3,16176) / 2 = 3,16428
(42,46667 + 42,46997) / 2 = 42,46832
So the intermediate point would be the following: (3.16428, 42.46832)
I have a shapefile with polylines of routes in different years. Here is an example data shapefile with routes in the year 2000 and year 2013. I would like the map to show the older routes at the top and more recent routes at the bottom. I've had a look at the addMapPane function but not sure how to apply it for a vector in the same file. Here is my code so far:
sample_palette <- leaflet::colorFactor(palette = rainbow(2),
domain = data_sample$Year)
sample_plot <- leaflet(data_sample) %>%
addProviderTiles("CartoDB.Positron") %>%
addPolylines(color = ~sample_palette(Year),
opacity = 1) %>%
leaflet::addLegend(values = ~Year,
opacity = 1,
pal = sample_palette,
title = "Routes")
sample_plot
I am using leaflet and R.
Please find one possible solution to get the older routes on top of recent routes: just need to change the order of rows in data_sample
Code
library(sf)
library(leaflet)
data_sample <- st_read("ADD YOUR PATH HERE")
# Order 'data_sample' rows in decreasing order of 'Year'
data_sample <- data_sample %>%
arrange(., desc(Year))
# Choose colors
sample_palette <- leaflet::colorFactor(palette = rainbow(2),
domain = data_sample$Year)
# Build the map
sample_plot <- leaflet(data_sample) %>%
addProviderTiles("CartoDB.Positron") %>%
addPolylines(color = ~sample_palette(Year),
opacity = 1) %>%
leaflet::addLegend(values = ~Year,
opacity = 1,
pal = sample_palette,
title = "Routes")
Visualization
sample_plot
I have a raster layer that contains 24-hour snow accumulations across the United States. The data can be pulled from here:
https://www.nohrsc.noaa.gov/snowfall_v2/data/202105/sfav2_CONUS_24h_2021052412.tif
I only want to plot the cells in the raster with values greater than or equal to 4 (inches) on a leaflet map. This is what my current map looks like:
https://i.stack.imgur.com/2Mi4r.png
I changed all values less than 4 to NA thinking that the raster cells wouldn't show up on the map. I want to remove all cells on the map that are greyed-out. The functions subset() and filter() do not work on raster layers. Any ideas? My code below for reference:
library(dplyr)
library(rgdal)
library(raster)
library(ncdf4)
library(leaflet)
library(leaflet.extras)
download.file(obsvSnow_Link, destfile = file.path(folderpath, 'observedSnow.tif'))
obsvSnow <- raster(file.path(folderpath, 'observedSnow.tif'))
names(obsvSnow) <- 'snowfall'
obsvSnow[obsvSnow < 4,] <- NA
colores <- c("transparent","#99CCFF","#3399FF","#0000FF","#FFE066", "#FF9900", "#E06666","#CC0000","#990033")
at <- c(4,8,seq(12,42,6),100)
cb <- colorBin(palette = colores, bins = at, domain = at)
mp <- leaflet(width = "100%",options = leafletOptions(zoomControl = FALSE)) %>%
addTiles() %>%
addRasterImage(x=obsvSnow$snowfall,
colors = cb,
opacity = 0.6) %>%
addLegend(title = 'Inches',
position='bottomright',
pal = cb, values = at) %>%
leaflet.extras::addSearchUSCensusBureau(options = searchOptions(autoCollapse=TRUE, minLength=10)) %>%
addScaleBar(position='bottomleft') %>%
addFullscreenControl()
mp
I need to label several overlapping polygons, but only the label of the biggest one is shown. However when I tested with some simulated data the labels were shown correctly. I compared the data in two cases carefully but cannot find the difference caused the problem.
Here is a minimal example of simulated overlapping polygons:
library(leaflet)
library(sp)
poly_a <- data.frame(lng = c(0, 0.5, 2, 3),
lat = c(0, 4, 4, 0))
poly_b <- data.frame(lng = c(1, 1.5, 1.8),
lat = c(2, 3, 2))
pgons = list(
Polygons(list(Polygon(poly_a)), ID="1"),
Polygons(list(Polygon(poly_b)), ID="2")
)
poly_dat <- data.frame(name = as.factor(c("a", "b")))
rownames(poly_dat) <- c("1", "2")
spgons = SpatialPolygons(pgons)
spgonsdf = SpatialPolygonsDataFrame(spgons, poly_dat, TRUE)
leaflet() %>% addPolygons(data = spgonsdf, label = ~name
# ,
# highlightOptions = highlightOptions(
# color = "red", weight = 2,bringToFront = TRUE)
)
It's working properly:
However it didn't work with my data.
https://github.com/rstudio/leaflet/files/1430888/Gabs.zip
You can drag the zip into this site and use the i button to see it's correctly labeled
library(rgdal)
# download Gabs.zip and extract files to Gabs folder
hr_shape_gabs <- readOGR(dsn = 'Gabs', layer = 'Gabs - OU anisotropic')
hr_shape_gabs_pro <- spTransform(hr_shape_gabs,
CRS("+proj=longlat +datum=WGS84 +no_defs"))
leaflet(hr_shape_gabs_pro) %>%
addTiles() %>%
addPolygons(weight = 1, label = ~name)
Only the biggest polygon label is shown:
The data in both case are SpatialPolygonsDataFrame, the data slot have proper polygon names.
Change the order of polygons in hr_shape_gabs: polygon in position 3 should be the smaller one.
library(leaflet)
library(sp)
library(rgdal)
hr_shape_gabs <- readOGR(dsn = 'Gabs - OU anisotropic.shp',
layer = 'Gabs - OU anisotropic')
# Change the position of the smaller and wider polygons
# Position 1 = wider polygon, position 3 = smaller polygon
pol3 <- hr_shape_gabs#polygons[[3]]
hr_shape_gabs#polygons[[3]] <- hr_shape_gabs#polygons[[1]]
hr_shape_gabs#polygons[[1]] <- pol3
hr_shape_gabs$name <- rev(hr_shape_gabs$name)
hr_shape_gabs_pro <- spTransform(hr_shape_gabs,
CRS("+proj=longlat +datum=WGS84 +no_defs"))
leaflet() %>%
addTiles() %>%
addPolygons(data= hr_shape_gabs_pro, weight = 1, label = ~name)
Here's a scalable solution in sf for many layers, based on this answer.
The idea is to order the polygons by decreasing size, such that the smallest polygons plot last.
library(sf)
library(dplyr)
# calculate area of spatial polygons sf object
poly_df$area <- st_area(poly_df)
poly_df <- arrange(poly_df, -area)
# view with labels in leaflet to see that small polygons plot on top
leaflet(poly_df) %>% addTiles() %>% addPolygons(label = ~id)
Apologies for the lack of reproducibility. This is more of a concept answer.