Oklahoma recently legalized medical marijuana, and I'm making a map of where dispensaries can set up. That depends on two things: it has to be in the right zoning area, and can't be too close to a school, church, or playground. I have two maps that show those things, but can't figure out how to layer them together. What I'm trying to achieve is showing how much of the correct zoning area is off-limits because it's too close to a school, church, etc.
The zoning code:
zoning_shapes <- "Primary_Zoning.shp"
zoning <- st_read(zoning_shapes)
library(dplyr)
zoning_1 <- filter(zoning, P_ZONE!="R-1")
zoning_2 <- filter(zoning_1, P_ZONE!="SPUD")
zoning_3 <- filter(zoning_2, P_ZONE!="AA")
zoning_4 <- filter(zoning_3, P_ZONE!="R-2")
zoning_5 <- filter(zoning_4, P_ZONE!="R-4")
zoning_6 <- filter(zoning_5, P_ZONE!="PUD")
zoning_7 <- filter(zoning_6, P_ZONE!="I-3")
zoning_8 <- filter(zoning_7, P_ZONE!="R-A")
zoning_9 <- filter(zoning_8, P_ZONE!="O-1")
zoning_10 <- filter(zoning_9, P_ZONE!="R-3")
zoning_11 <- filter(zoning_10, P_ZONE!="R-A2")
zoning_12 <- filter(zoning_11, P_ZONE!="R-1ZL")
zoning_13 <- filter(zoning_12, P_ZONE!="R-3M")
zoning_14 <- filter(zoning_13, P_ZONE!="R-4M")
zoning_15 <- filter(zoning_14, P_ZONE!="R-MH-1")
zoning_16 <- filter(zoning_15, P_ZONE!="R-MH-2")
zoning_17 <- filter(zoning_16, P_ZONE!="C-HC")
zoning_18 <- filter(zoning_17, P_ZONE!="HP")
zoning_19 <- filter(zoning_18, P_ZONE!="NC")
zoning_20 <- filter(zoning_19, P_ZONE!="AE-1")
zoning_21 <- filter(zoning_20, P_ZONE!="AE-2")
library(ggplot2)
library(sf)
ggplot(zoning_21) + geom_sf() +
theme_void() +
theme(panel.grid.major =
element_line(colour = 'transparent'))
The prohibited-location code:
library(dplyr)
library(tigris)
library(sf)
library(ggplot2)
library(leaflet)
library(readr)
locations <- read_csv("Marijuana_map_CSV.csv")
View(locations)
mew <- colorFactor(c("red", "blue", "purple"), domain=c("School", "Church", "Playground"))
okc_locations <- leaflet(locations) %>%
addTiles() %>%
setView(-97.5164, 35.4676, zoom = 7) %>%
addCircles(~Longitude, ~Latitude, popup=locations$Name,
weight = 3, radius=304.8,
color=~mew(locations$Type), stroke = T,
fillOpacity = 0.8) %>%
addPolygons(data=zoning_21, fillColor = "limegreen",
fillOpacity = 0.5, weight = 0.2,
smoothFactor = 0.2)
okc_locations
The problem I'm running into is that when I try to add the okc_locations code to the zoning_21 code, I get one red dot that's far away and a very compressed version of the city's zoning. When I try adding the zoning polygons to the to the prohibited-points map, they don't show up.
Any ideas of how to get these two maps to play together? Thank you!
Based on our conversation in the comments, it seems that you are having an issue with different projections, in which case you will want to use st_transform (documented here)
First, I made up some fake data:
locations <-
data.frame(Name = c("St. Josephs", "St. Anthony", "Edwards Elementary"),
type = c("Church", "Playground", "School"),
long = c(35.4722725, 35.4751038, 35.4797194),
lat = c(-97.5202865,-97.5239513,-97.4691759))
I downloaded tiger shapefiles for all counties, then narrowed to Oklahoma County:
us_counties <- read_sf("cb_2017_us_county_500k.shp")
ok_county <- subset(us_counties, STATEFP == "40" & NAME == "Oklahoma")
> print(st_crs(ok_county))
Coordinate Reference System:
EPSG: 4269
proj4string: "+proj=longlat +datum=NAD83 +no_defs"
So I then used st_transform:
t2 <- st_transform(ok_county, "+proj=longlat +datum=WGS84")
> print(st_crs(t2))
Coordinate Reference System:
EPSG: 4326
proj4string: "+proj=longlat +datum=WGS84 +no_defs"
And loading it into leaflet as so:
leaflet(locations) %>%
addTiles() %>%
setView(-97.5164, 35.4676, zoom = 11) %>%
addMarkers(~lat, ~long, popup=locations$Name) %>%
addPolygons(data=t2, fillColor = "limegreen",
fillOpacity = 0.5, weight = 0.2,
smoothFactor = 0.2)
Yields this map:
Related
How can I remove the crossing line while using the buffer code below. I tried to create buffers round some point locations and to have a union but ended up getting a crossline.
please see the codes below
train_data
library(raster)
library(dismo)
library(sf)
bioc1 <- getData('worldclim', var='bio', res=5) #
bio1 <- bioc1[[1]]
plot(bio1)
train <- read.csv("forexample_training.csv") # the points locations to be buffered
head(train)
train.sf <- sf::st_as_sf(train, coords=c("longitude", "latitude"), crs=raster::crs(bio1))
plot(train.sf, add=TRUE)
eckertIV <- "+proj=eck4 +lon_0=0 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs"
train.sf <- sf::st_transform(train.sf, crs = eckertIV)
train.buf <- sf::st_buffer(train.sf, dist = 500000) %>%
sf::st_union() %>%
sf::st_sf() %>%
sf::st_transform(crs = raster::crs(bio1))
plot(bio1, main = names(bio1))
train.sf <- sf::st_as_sf(train, coords=c("longitude", "latitude"), crs=raster::crs(bio1))
plot(train.sf, add=TRUE)
# To add sf objects to a plot, use add = TRUE
plot(train.buf, border = "red", lwd = 3, add = TRUE)
library(tidyverse)
library(tidycensus)
library(sf)
library(sp)
#install.packages('geosphere')
library('geosphere')
library(rgeos)
library(sfheaders)
#install.packages('reshape')
library('reshape')
#> Linking to GEOS 3.6.1, GDAL 2.1.3, PROJ 4.9.3
census_tract <- get_acs(geography = "tract",
variables = "B19013_001",
state = "CA",
county = c("San Joaquin","Merced","stanislaus"),
geometry = TRUE,
year = 2020)
plot(st_geometry(census_tract), axes = T)
plot(st_centroid(st_geometry(census_tract)), pch = "+", col = "red", add = T)
library(ggplot2)
ggplot(census_tract) + geom_sf() +
geom_sf(aes(geometry = st_centroid(st_geometry(census_tract))), colour = "red")
census_tract$centroid <- st_centroid(st_geometry(census_tract))
schoolloc <- read.csv("C:/Users/rlnu/Desktop/EXAMPLE/pubschls.csv")
schoolloc <- schoolloc%>% filter(County == c("San Joaquin","Merced","Stanislaus"))
census_tract <- census_tract %>%
mutate(long = unlist(map(census_tract$centroid,1)),
lat = unlist(map(census_tract$centroid,2)))
shortest_distance$min_distance <- expand.grid.df(census_tract,schoolloc) %>%
mutate(distance = distHaversine(p1 = cbind(long,lat),
p2 = cbind(Longitude,Latitude))
`
I am trying to find distance between the each census tract's centroid to three nearest schools. please help me out with it. I have written some code . The logic is wrong and the code is not working
Can achieve this using the sf package.
I could not access you schools data so made a dummy set of 4 schools.
library(sf)
schools <- data.frame(School_Name=c("School_1", "School_2", "School_3", "School_4"), Lat=c(37.83405, 38.10867, 37.97743, 37.51615), Long=c(-121.2810, -121.2312, -121.2575, -120.8772)) %>% st_as_sf(coords=c("Long", "Lat"), crs=4326)
Convert tracts to centroids and make the crs the same as the school set then calculate the distance matrix
census_centroid <- st_centroid(census_tract) %>% st_transform(4326)
DISTS<- st_distance(census_centroid, schools)
Rename the columns to be the school IDs
colnames(DISTS) <- schools$School_Name
link it back to centoids
cent_dists <- cbind(census_centroid, DISTS) %>% #bind ditances to centroids
pivot_longer(cols = -names(census_centroid), names_to = "School Name", values_to = "Distance") %>% #make long for ordering
group_by(NAME) %>% #group by centroid
slice_min(Distance,n= 3) %>% # take three closest
mutate(Near_No=paste0("Near_School_",rep(1:3))) #School distance ranking
Make wide if one row per census centroid desired, might want to play with column order though
cent_dists_wide <- cent_dists %>%
pivot_wider(names_from = c("Near_No"), values_from = c("Distance", "School Name"), names_sort = FALSE) #make wid if wyou want one row per centoid
Is it possible to display a projected raster on a projected basemap in R/leaflet? The addRasterImage() functions says that the projection needs to be in EPSG:3857. Can this not be changed by setting project = false? I am able to display projected vector data on a projected basemap, but not raster ...
My attempt:
library(leaflet)
library(raster)
library(sf)
# Find location in northern Canada
ca_df <- data.frame(long = -114.3717401, lat = 62.4525548, name="Yellowknife", stringsAsFactors = F )
ca_pt <- st_as_sf(ca_df,coords = c("long", "lat"), crs = 4326)
# Project to Alaska Polar Stereographic
ca_pt_5936 <- as_Spatial(st_transform(ca_pt, 5936))#coords
# Create raster around point
r_5936 <- raster(
matrix(round(runif(100)), ncol = 10),
xmn = ca_pt_5936[[1]] - 50000, xmx = ca_pt_5936[[1]] + 50000,
ymn = ca_pt_5936[[2]] - 50000, ymx = ca_pt_5936[[2]] + 50000,
crs = "EPSG:5936"
)
# Project raster to Web Mercator (needed to get the extent in lat/long)
r_3857 <- projectRaster(r_5936, crs="EPSG:3857", method = "ngb")
# Prep for leaflet: https://github.com/rstudio/leaflet/issues/550
tile_url <- 'https://services.arcgisonline.com/arcgis/rest/services/Polar/Arctic_Ocean_Base/MapServer/tile/{z}/{y}/{x}.png'
origin <- c(-2.8567784109255e+07, 3.2567784109255e+07)
resolutions <- c(
238810.813354,119405.406677, 59702.7033384999, 29851.3516692501,14925.675834625,
7462.83791731252,3731.41895865639, 1865.70947932806,932.854739664032,
466.427369832148, 233.213684916074, 116.60684245803701, 58.30342122888621,
29.151710614575396, 14.5758553072877, 7.28792765351156, 3.64396382688807,
1.82198191331174, 0.910990956788164, 0.45549547826179, 0.227747739130895,
0.113873869697739, 0.05693693484887, 0.028468467424435)
epsg5936 <- leafletCRS(
crsClass = 'L.Proj.CRS',
code = 'EPSG:5936',
proj4def = '+proj=stere +lat_0=90 +lat_ts=90 +lon_0=-150 +k=0.994 +x_0=2000000 +y_0=2000000 +datum=WGS84 +units=m +no_defs',
origin = origin,
resolutions = resolutions
)
# Map
leaflet(r_3857,
options= leafletOptions(
crs=epsg5936)) %>%
addTiles(urlTemplate = tile_url,
attribution = "Esri, DeLorme, GEBCO, NOAA NGDC, and other contributors",
options = tileOptions(minZoom = 0, maxZoom = 4)) %>%
addRasterImage(r_5936, project = F)
The output doesn't display the raster.
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.
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)