I have two shapefiles that I read into R with sf.
The first shp file covers regions.
The second shp file covers administrative districts.
The electoral districts are nested into regions.
I would like to overlay the two maps, then coloring each electoral district in a shade of the same color, having one color for each region.
I can plot the two and play around with colors but cannot overlay and coloring.
Files can be accessed here from the Italian National Institute of Statistics :
Reg1991_WGS84.shp:
http://www.istat.it/storage/cartografia/confini_amministrativi/non_generalizzati/Limiti1991.zip
CAMERA_PLURI_2017.shp: https://www.istat.it/storage/COLLEGI_ELETTORALI_2017.zip
library(sf)
italia_regions_1991<- read_sf("Limiti1991/Reg1991/Reg1991_WGS84.shp") %>% select(geometry)
italia_camera_pluri <- read_sf("COLLEGI_ELETTORALI_2017/CAMERA_PLURI_2017.shp") %>% select(geometry)
This will get you started....
I used the leafgl library, since you are plotting alot of polylines/plygons... This performs (pretty)fast...
library(sf)
library(devtools)
library(leaflet)
#install leaflet with gl-suport
devtools::install_github("r-spatial/leafgl")
library(leafgl)
library(colourvalues)
#read shapefile regions and cast to polygons
sf1 <- st_read( "e:/two_shapes/Limiti1991/Reg1991/Reg1991_WGS84.shp" ) %>% st_cast( "POLYGON", warn = FALSE )
#read shapefile and cast to POLYGON and then to LINESTRING
sf2 <- st_read( "e:/two_shapes/COLLEGI_ELETTORALI_2017/COLLEGI_ELETTORALI_2017.shp") %>%
st_cast( "POLYGON", warn = FALSE ) %>%
st_cast( "LINESTRING", warn = FALSE )
#creaae color matrix for the regions( depending om DEN_REG), and for the polylines (=black)
col_region <- colour_values_rgb(sf1$DEN_REG, include_alpha = FALSE) / 255
col_lines <- matrix(data = c(0,0,0), nrow = 1 )
#plot leaflet (takes some time)
leaflet() %>% addTiles() %>%
addGlPolygons(data = sf1, color = col_region) %>%
addGlPolylines( data = sf2, color = col_lines)
result
Consider intersecting the regions & districts via sf::st_intersection - note however that there seems to be some overlap, as the regions and districts do not align perfectly (they mostly do, but not quite...)
I have also transformed the CRS to WGS84; perhaps not necessary, but works better with leaflet and the like...
library(sf)
library(dplyr)
library(ggplot2)
italia_regions_1991<- read_sf("Reg1991_WGS84.shp") %>%
select(region = DEN_REG) %>% # this, and geometry by default
st_transform(4326)
italia_camera_pluri <- read_sf("CAMERA_PLURI_2017.shp") %>%
select(geometry) %>% # only geometry...
st_transform(4326)
result <- italia_camera_pluri %>%
st_intersection(italia_regions_1991)
ggplot(data = result, aes(fill = region)) +
geom_sf()
Related
My final aim is to create a map of the Vermeille Coast in order to calculate the distance between two sampling points with the condition that the path between the two points is not crossing the land.
To do so:
1/ I took two shapefiles (you can download them here: https://www.dropbox.com/sh/hzsdklnmvjg4hsz/AAATHLV0pkJXDvSqyRIBlVl_a?dl=0)
2/ I sew them according to : R cran: sf Sew two MULTILINESTRING/LINESTRING
3/ try to create the associated polygons according to that: Sf package: Close a polygon fom complex shape
The script is the following:
frenchCoast_CoteBanyuls <- st_read("coasts_subnational/coasts_subnational.shp")
spainCoast_CoteBanyuls <- st_read("coasts_subnational SPAIN/coasts_subnational.shp")
combined_coast <- rbind(spainCoast_CoteBanyuls, frenchCoast_CoteBanyuls)
plot(combined_coast$geometry)
I get the following plot:
which is correct so far. I just need now to create the associated polygon:
bbox_combined_coast <- st_bbox(combined_coast) %>%
st_as_sfc()
polygon_combined_coast <- bbox_combined_coast %>%
lwgeom::st_split(combined_coast) %>%
st_collection_extract("POLYGON")
par(mfrow = c(2,4), mar = c(0,0,0,0))
for(i in 1:8){
plot(polygon_combined_coast[i], col = 'steelblue')
}
which gave:
None of these polygons represents the shape of combined maps above.
Nevertheless, when I plot the polygon from every single shapefile:
par(mfrow = c(1,2), mar = c(0,0,0,0))
bbox_frenchCoast_CoteBanyuls <- st_bbox(frenchCoast_CoteBanyuls) %>%
st_as_sfc()
polygon_frenchCoast_CoteBanyuls <- bbox_frenchCoast_CoteBanyuls %>%
lwgeom::st_split(frenchCoast_CoteBanyuls) %>%
st_collection_extract("POLYGON")
plot(polygon_frenchCoast_CoteBanyuls[1], col = 'steelblue')
bbox_spainCoast_CoteBanyuls <- st_bbox(spainCoast_CoteBanyuls) %>%
st_as_sfc()
polygon_spainCoast_CoteBanyuls <- bbox_spainCoast_CoteBanyuls %>%
lwgeom::st_split(spainCoast_CoteBanyuls) %>%
st_collection_extract("POLYGON")
plot(polygon_spainCoast_CoteBanyuls[3], col = 'steelblue')
It gave:
these two polygons are correctly shaping the polygon associated to each shapefile.
It seems that rbinding the two shapefiles makes something not expected.
Do you have an idea of the mistake ?
Thanks in advance,
Charlotte
To combine the two coast lines summarize with no grouping variable
combined_coast <- rbind(spainCoast_CoteBanyuls, frenchCoast_CoteBanyuls) %>% summarise()
Note that it will always be a MULTILINESTRING because there are several small islands off the coast so cannot be a single LINESTRING
can turn that into polygons with
combined_coast_poly<- combined_coast %>% st_cast("POLYGON")
The problem is that both coastlines does not touch, that's why the polygon approach doesn't work.
See here a solution, where I merge the two main coastlines, add the small islands in Spain and try the approach you provided:
# Download files
spainurl <- "https://geo.vliz.be/geoserver/wfs?request=getfeature&service=wfs&version=1.0.0&typename=MarineRegions:coasts_subnational&outputformat=SHAPE-ZIP&filter=%3CPropertyIsEqualTo%3E%3CPropertyName%3Emrgid_1%3C%2FPropertyName%3E%3CLiteral%3E3417%3C%2FLiteral%3E%3C%2FPropertyIsEqualTo%3E"
download.file(spainurl, "spain.zip", mode = "wb")
unzip("spain.zip", exdir = "spain", junkpaths = TRUE)
franceurl <- "https://geo.vliz.be/geoserver/wfs?request=getfeature&service=wfs&version=1.0.0&typename=MarineRegions:coasts_subnational&outputformat=SHAPE-ZIP&filter=%3CPropertyIsEqualTo%3E%3CPropertyName%3Emrgid_1%3C%2FPropertyName%3E%3CLiteral%3E19888%3C%2FLiteral%3E%3C%2FPropertyIsEqualTo%3E"
download.file(franceurl, "france.zip", mode = "wb")
unzip("france.zip", exdir = "france", junkpaths = TRUE)
library(sf)
library(tidyverse)
spainCoast_CoteBanyuls <- list.files("spain",
pattern = "shp$",
full.names = TRUE) %>% st_read()
frenchCoast_CoteBanyuls <- list.files("france",
pattern = "shp$",
full.names = TRUE) %>% st_read()
ggplot(spainCoast_CoteBanyuls) +
geom_sf() +
geom_sf(data = frenchCoast_CoteBanyuls)
Ok, now extract every single LINESTRING of the object. France only has 1.
# A. Decompose in linestrings
lines_spain <- st_geometry(spainCoast_CoteBanyuls) %>% st_cast("LINESTRING")
spainCoast_l <- st_sf(n = as.character(seq_len(length(lines_spain))), lines_spain)
ggplot(spainCoast_l) +
geom_sf(aes(color = n), size = 3)
lines_france <- st_geometry(frenchCoast_CoteBanyuls) %>% st_cast("LINESTRING")
franceCoast_l <- st_sf(n = as.character(seq_len(length(lines_france))), lines_france)
ggplot(franceCoast_l) +
geom_sf(aes(color = n), size = 3)
See if France and Spain touches (spoiler: no)
st_touches(lines_france, lines_spain, sparse = FALSE)
#> [,1] [,2] [,3] [,4] [,5] [,6]
#> [1,] FALSE FALSE FALSE FALSE FALSE FALSE
# Lines doesn't touch. Merge both main coastlines
spainmax <- spainCoast_l[which.max(st_length(spainCoast_l)), ]
spainrest <- spainCoast_l[-which.max(st_length(spainCoast_l)), ]
ggplot(spainmax) +
geom_sf() +
geom_sf(data = franceCoast_l)
ggplot(spainrest) +
geom_sf()
Here I merge both LINESTRINGs with st_union():
# Merge
joined <- c(st_geometry(spainmax), st_geometry(franceCoast_l)) %>%
st_union()
ggplot(joined) +
geom_sf()
And now the task is to reassamble the small parts of Spain and apply the lwgeom::st_split() approach.
# Ok, we are ready
# Get the rest of pieces of Spain
join_end <- st_union(joined, st_geometry(spainrest))
ggplot(join_end) +
geom_sf()
bbox_all <- st_bbox(joined) %>%
st_as_sfc()
polygon_joined <- bbox_all %>%
lwgeom::st_split(join_end) %>%
st_collection_extract("POLYGON")
#Polygons on position 2 and 3 need to be removed (visual inspection)
polygon_end <- polygon_joined[-c(2:3)]
ggplot(polygon_end) +
geom_sf()
Created on 2022-06-15 by the reprex package (v2.0.1)
I have a data set of whale sightings with some coordinate gaps but associated with areas of reference, of which I have coordinate limits. I've used these limits to create a polygon restricted to the marine environment (using library ‘sf’) for each of the areas. Now I would like to fill the coordinate gaps by randomly selecting latitudes and longitudes from the polygons.
My piece of code (example for the area 'Angola'):
#Creating a ocean-only polygon for the Southern Hemisphere (my study area)
x_coord = c(180, 180, -180, -180)
y_coord = c(0, -90, -90, 0)
polygonSH = cbind(x_coord, y_coord) %>%
st_linestring() %>%
st_cast("POLYGON") %>%
st_sfc(crs = 4326, check_ring_dir = TRUE) %>%
st_sf()
land = rnaturalearth::ne_countries(returnclass = "sf") %>%
st_union()
ocean = st_difference(polygonSH, land)
plot(st_geometry(land))
plot(st_geometry(polygonSH), add = TRUE)
plot(st_geometry(ocean), add = TRUE, col = "blue")
#Creating ocean-only polygons for each of the different areas to then use them in the arguments to run ramdon coords
#Angola
x_angola = c(11.72,11.72,13.58,13.58) #longitude limits of Angola area
y_angola = c(-12.34,-16.6,-16.6,-12.34) #latitude limits of Angola area
polygon_angola = cbind(x_angola, y_angola) %>%
st_linestring() %>%
st_cast("POLYGON") %>%
st_sfc(crs = 4326, check_ring_dir = TRUE) %>%
st_sf()
plot(st_geometry(land))
plot(st_geometry(polygon_angola), add = TRUE)
angola_ocean = st_difference (polygon_angola, land)
plot(st_geometry(angola_ocean), add = TRUE, col = "pink")
...
Before having the polygons restricted to the marine environment, I've used the code below to randonmly generate the coordinates, and ideally I would like to use something similar, but adjusted to working with spatial data:
for(i in 1:dim(x)[1]) {
x[i,"lat"] <- ifelse(is.na(x[i,"lat"]) && x[i,"area"]=="Angola", runif(1,-16.6,-12.34), x[i,"lat"])
x[i,"long"] <- ifelse(is.na(x[i,"long"]) && x[i,"area"]=="Angola", runif(1, 11.72,13.58), x[i,"long"])
}
I would really appreciate having folk's input on this issue.
I can't get your code to work due to issues (invalid spherical geometry) not directly related to the subject of the question.
So please allow me to illustrate a possible approach using the well known & much loved North Carolina shapefile that ships with the {sf}.
library(sf)
library(dplyr)
# included with sf package
shape <- st_read(system.file("shape/nc.shp", package="sf")) %>%
summarise() # a single polygon
# now the action! 50 random points over North Carolina
random_points <- shape %>%
st_sample(50)
# check results...
plot(shape)
plot(random_points, col = "red", pch = 4, add = T)
I have the following map boundaries in this .gdb folder
and here I have a csv which contains the variables that I want to plot and the coordinates of the points that need to be displayed on the map. My final goal is to create a map with polygons and inside every polygon there should be points according to the coordinates. Every polygon should be colored according to the count of studentid (students) for the year 2019. Any alternative is accepted
I believe that the 1st code chunk below is correct:
library(sf)
library(tidyverse)
library(data.table)
library(tigris)
library(leaflet)
library(mapview)
options(tigris_use_cache = TRUE)
# To keep enough digits on coords
options(digits = 11)
#coordinate reference system (long-lat system)
cr_sys = 4326
# Shp file for hs boundaries (constitutes overall district bounds)
hs_bounds <- st_read("C:/Users/makis/Documents/school/TPS_schools.shp")
# Read the feature class
#fc <- readOGR(dsn=fgdb )
#fc <- spTransform(fc, CRS("+proj=longlat +datum=WGS84 +no_defs"))
# Convert hs_bounds into longlat coord system
hs_bounds <- hs_bounds %>%
st_transform(4326)
tmp <- list.files(pattern = "school_report_data_fake.csv")
raw_master <- lapply(tmp,
function(x) read_csv(x,guess_max = 5000)) %>%
rbindlist(., fill = TRUE)
# r blocks in tps
tps_blocks <- blocks(state = "OK") %>%
st_as_sf() %>%
st_transform(crs = 4326) %>%
st_intersection(hs_bounds)
tps_bgs <- block_groups(state = "OK") %>%
st_as_sf() %>%
st_transform(crs = 4326) %>%
st_intersection(hs_bounds)
mapview(hs_bounds)
# Display all tps block groups on interactive map
tps_blocks_map <- mapview(tps_bgs) %>%
addFeatures(., hs_bounds)
# convert to df and remove geometry bc its a list col
tps_blocks_df <- tps_blocks %>%
as.data.frame() %>%
select(-geometry)
# Export blocks in tps. GEOID10 is the unique identifier for the block
write_csv(tps_blocks_df, path = "C:/Users/makis/Documents/school/tps_blocks.csv")
Here Im trying to include the student data as well but Im concluding in adataframe with zero data
#r students by geography
student_geos <- raw_master %>%
#filter for students active in a given year
filter(year == 2019) %>%
# filter(row_number() %in% sample(length(year), 20000)) %>%
# Parse lat/long. I believe that I should do something here with the lat and long
#and some variable of the csv like the geocode variable that is used here
#a similar should be present in my csv file as well
#mutate(lat = as.numeric(str_extract(geocode, "[0-9]+.[0-9]+"))) %>%
#mutate(lon = as.numeric(str_extract(geocode, "-[0-9]+.[0-9]+"))) %>%
# Please don't ask me why this rowwise is necessary
rowwise() %>%
# Create sf point for each set of coords
mutate(pt = st_sfc(st_point(x = c(lon, lat)), crs = 4326)) %>%
# Turn df into sfc then take intersection of pts and blocks
st_as_sf() %>%
st_intersection(tps_blocks)
# convert to df and remove geometry bc its a list col
student_geos_df <- student_geos %>%
as.data.frame() %>%
select(-pt)
If everything above is correct i should do something like:
# enrollment by tract
tract_enrol <- student_geos %>%
as.data.frame() %>%
group_by(year, TRACTCE10) %>%
summarize(enrollment = n())
# convert list of tracts into sfc
tracts <- tracts(state = "OK",
county = c("Tulsa", "Osage", "Wagoner", "Creek"),
year = 2010) %>%
st_as_sf() %>%
as.data.frame() %>%
#I guess student id instead of TRACTE10 here
inner_join(tract_enrol, by = "TRACTCE10") %>%
st_as_sf()
mapview(tracts, zcol = "enrollment", legend = TRUE)
Your file still doesn't download.
I can give you a generic guide to use ggplot2 to make a map. This will draw the polygons and the points.
You need to modify the Spatial_DataFrames with fortify() to get them into a format ggplot2 can use.
library(ggplot2)
hs_b2 <- fortify(hs_bounds) #or instead of "hs_bounds", "tracts" or whatever your polygon
#is called. If that doesn't work you need "<-as.data.frame()".
#Make sure the output has a separate column for x and y.
#repeat for the points (student) object.
student_2 <- fortify(<studentpointsobject>)
ggplot(data=<student_2>, aes(x=x,y=y)) +
geom_polygon(data=hs_b2, aes(x=long, y=lat, group=group) , #this will create the polygon
colour="grey90", alpha=1, #one of your color options for polygons
fill="grey40") + #one of your color options for polygons
theme(axis.ticks.y = element_blank(),axis.text.y = element_blank(), # get rid of x ticks/text
axis.ticks.x = element_blank(),axis.text.x = element_blank()) + # get rid of y ticks/text
geom_point(aes(color="grey20")) #to get the points drawn. mess with "fill" and "color"
You can customize the plot with 'color' or 'fill' in the aes().
I'm looking to create some proximity maps using R, which show how far areas are from certain points. I can't find any examples in R code, but I've found an output which is the sort of thing I want:
It doesn't necessarily have to have all the labelling/internal boundaries wizardry, but I'd like it to stop at the sea border (thinking of using the rgeos function gintersection - see here).
I've tried doing a density plot as 'heatmaps' (this would be a pretty good solution/alternative) and putting a shapefile over the top (following this suggestion, but they're not lining up and I can't do a gintersection, probably because there's not a coordinate system attached to the density plot.
I used your question to play a little with new libraries...
Get a UK map and define random points
library(raster)
library(sf)
library(ggplot2)
library(dplyr)
library(tidyr)
library(forcats)
library(purrr)
# Get UK map
GBR <- getData(name = "GADM", country = "GBR", level = 1)
GBR_sf <- st_as_sf(GBR)
# Define 3 points on the UK map
pts <- matrix(c(-0.4966766, -2.0772529, -3.8437793,
51.91829, 52.86147, 56.73899), ncol = 2)
# Project in mercator to allow buffer with distances
pts_sf <- st_sfc(st_multipoint(pts), crs = 4326) %>%
st_sf() %>%
st_transform(27700)
ggplot() +
geom_sf(data = GBR_sf) +
geom_sf(data = pts_sf, colour = "red")
Calculate buffer areas
We create a list of multipolygons for each buffer distance. The point dataset must be in projected coordinates (here mercator) as buffer distance is in the scale of the coordinates system.
# Define distances to buffer
dists <- seq(5000, 150000, length.out = 5)
# Create buffer areas with each distances
pts_buf <- purrr::map(dists, ~st_buffer(pts_sf, .)) %>%
do.call("rbind", .) %>%
st_cast() %>%
mutate(
distmax = dists,
dist = glue::glue("<{dists/1000} km"))
# Plot: alpha allows to see overlapping polygons
ggplot() +
geom_sf(data = GBR_sf) +
geom_sf(data = pts_buf, fill = "red",
colour = NA, alpha = 0.1)
Remove overlapping
Buffer areas are overlapping. On the figure above, the more intense red color is due to multiple overlapping layers of transparent red. Let's remove the overlapping. We need to remove from larger areas, the buffer with the lower size. I then need to add again the smallest area to the list.
# Remove part of polygons overlapping smaller buffer
pts_holes <- purrr::map2(tail(1:nrow(pts_buf),-1),
head(1:nrow(pts_buf),-1),
~st_difference(pts_buf[.x,], pts_buf[.y,])) %>%
do.call("rbind", .) %>%
st_cast() %>%
select(-distmax.1, -dist.1)
# Add smallest polygon
pts_holes_tot <- pts_holes %>%
rbind(filter(pts_buf, distmax == min(dists))) %>%
arrange(distmax) %>%
mutate(dist = forcats::fct_reorder(dist, distmax))
# Plot and define color according to dist
ggplot() +
geom_sf(data = GBR_sf) +
geom_sf(data = pts_holes_tot,
aes(fill = dist),
colour = NA) +
scale_fill_brewer(direction = 2)
Remove areas in the sea
If you want to find proximity area on terrestrial parts only, we need to remove buffer areas that are in the sea. Intersection is computed between multipolygons with the same projection. I previously realize an union of the UK map.
# Remove part of polygons in the sea
# Union and projection of UK map
GBR_sf_merc <- st_transform(st_union(GBR_sf), 27700)
pts_holes_uk <- st_intersection(pts_holes_tot,
GBR_sf_merc)
ggplot() +
geom_sf(data = GBR_sf) +
geom_sf(data = pts_holes_uk,
aes(fill = dist),
colour = NA) +
scale_fill_brewer(direction = 2)
And here is the final proximity map using sf, ggplot2 and a few other libraries...
Based on Sébastien's example, a more old-fashioned approach:
library(raster)
GBR <- getData(name = "GADM", country = "GBR", level = 1)
pts <- matrix(c(-0.4966766, -2.0772529, -3.8437793, 51.91829, 52.86147, 56.73899), ncol = 2)
r <- raster(GBR, res=1/12)
d <- distanceFromPoints(r, pts)
m <- mask(d, GBR)
plot(m)
In his article Kyle Walker showed a method to make Voronoi Polygons in Leaflet. He drew Voronoi polygons around each starbucks coffeehouse in Fort Worth by means of the following code:
library(leaflet); library(rgeos)
library(rgdal); library(spatstat)
library(maptools)
starbucks <- read.csv('starbucks.csv')
fw <- subset(starbucks, City == 'Fort Worth')
coords <- cbind(fw$Longitude, fw$Latitude)
## Spatial points w/the WGS84 datum
sp_fw <- SpatialPointsDataFrame(coords = coords, data = fw,
proj4string = CRS("+proj=longlat +datum=WGS84"))
sp_fw_proj <- spTransform(sp_fw, CRS("+init=epsg:26914"))
fw_coords <- sp_fw_proj#coords
## Create the window for the polygons
window <- owin(range(fw_coords[,1]), range(fw_coords[,2]))
## Create the polygons
d <- dirichlet(as.ppp(fw_coords, window))
## Convert to a SpatialPolygonsDataFrame and calculate an "area" field.
dsp <- as(d, "SpatialPolygons")
dsp_df <- SpatialPolygonsDataFrame(dsp,
data = data.frame(id = 1:length(dsp#polygons)))
proj4string(dsp_df) <- CRS("+init=epsg:26914")
dsp_df$area <- round((gArea(dsp_df, byid = TRUE) / 1000000), 1)
dsp_xy <- spTransform(dsp_df, CRS("+proj=longlat +datum=WGS84"))
## Map it!
leaflet() %>%
addMarkers(data = fw,
lat = ~ Latitude,
lng = ~ Longitude,
popup = fw$Name) %>%
addPolygons(data = dsp_xy,
color = "green",
fill = "green",
popup = paste0("Area: ",
as.character(dsp_xy$area),
" square km")) %>%
addTiles()
I want to add an extra feature to his map: I want to assign a specific color to a polygon. This color depends on the characteristics of the nearest marker (the centroid).
for example, color every polygon with a starbucks centroid "green", and with a Dunkin' Donuts centroid "purple". (assuming that the starbucks.csv also includes coordinates of Dunkin' Donuts)
In other words, I want to merge the data of a centroid ("fw") with that of the polygon it belongs to ("dsp_xy").
Can someone help me out in this one?
The voronoi function from the dismo package is what you need. I'll also use this post to demo the new sf package for R.
Let's generate a reproducible fake dataset of Starbucks and Dunkin Donuts locations:
library(leaflet)
library(sf)
library(dismo)
library(sp)
set.seed(1983)
# Get some sample data
long <- sample(seq(-118.4, -118.2, 0.001), 50, replace = TRUE)
lat <- sample(seq(33.9, 34.1, 0.001), 50, replace = TRUE)
type <- sample(c("Starbucks", "Dunkin"), 50, replace = TRUE)
Next, let's create an sf data frame from our data, and take a look:
points <- data.frame(long = long, lat = lat, type = type) %>%
st_as_sf(crs = 4326, coords = c("long", "lat"))
plot(points)
Next, we create the Voronoi polygons with the voronoi function from the dismo package, which is very straightforward, then give it the same coordinate system as our points. In a real-world workflow, you should use a projected coordinate system, but I'll just use WGS84 (which the operations will assume to be planar) for illustration. Also notice I'm going back and forth between sf and sp classes; the R world will fully support sf in time, but coercion is straightforward in the interim.
polys <- points %>%
as("Spatial") %>%
voronoi() %>%
st_as_sf() %>%
st_set_crs(., 4326)
plot(polys)
Now, visualize it with Leaflet using your desired colors:
pal <- colorFactor(c("purple", "green"), polys$type)
polys %>%
leaflet() %>%
addProviderTiles(providers$CartoDB.Positron) %>%
addPolygons(fillColor = ~pal(type), weight = 0.5, color = "grey") %>%
addCircleMarkers(data = points, label = ~type, color = ~pal(type))
We didn't need it here, but a function in sf that you'll want to know about as well is st_join, which handles spatial joins seamlessly and would work for the type of overlay you originally proposed.