Raster output from rasterized sf points does not align with points - r

I am trying to rasterize some points and am getting a mismatch between the points and the rasters despite the crs being the same. If I convert the raster to polygons it lines up perfectly with the sf points data, but I can't figure out why the raster doesn't.
library(spData)
library(sf)
library(raster)
library(mapview)
## import some data
cycle_hire_osm = spData::cycle_hire_osm
## project to metres
cycle_hire_osm_projected = st_transform(cycle_hire_osm, crs = 27700)
## create raster template to rasterize to
raster_template <- raster(extent(cycle_hire_osm_projected), nrows = 10, ncols = 10, crs = 27700)
## rasterize the points
ch_raster1 = rasterize(cycle_hire_osm_projected, raster_template, field = 'capacity',
fun = sum, crs = 27700)
## convert raster to polygons
ch_poly <- rasterToPolygons(ch_raster1)
If these are plotted there are raster cells that have a value but have no points in.
## plot on a map
mapview(ch_poly)+cycle_hire_osm_projected+ch_raster1
Additional example based on reply to show the output as base, mapview and leaflet (note: I had to install the development versions of mapview and leaflet in order to plot SpatRasts)
library(spData)
library(sf)
library(terra)
library(dplyr)
# remove NAs so they are not considered
dat <- spData::cycle_hire_osm %>% filter(!is.na(capacity))
v <- vect(dat)
r <- rast(v, nrows=10, ncols=10)
chr <- rasterize(v, r, field="capacity", fun=sum, na.rm=TRUE)
## base plot
plot(chr)
points(v, cex=.5)
points(v[is.na(v$capacity)], cex=.5, col="red")
## mapview
library(mapview)
mapview(v)+chr
##leaflet
library(leaflet)
leaflet() |>
addProviderTiles(providers$CartoDB.Positron) |>
addCircles(data = v) |>
addRasterImage(chr)
All three plots are the same raster but the raster appears to have a different number of cells with values in each plot?
Adding example with project = FALSE as explain by #RobertHijmans
leaflet() |>
addProviderTiles(providers$CartoDB.Positron) |>
addCircles(data = v) |>
addRasterImage(chr, project = FALSE)

I do not see that issue with "raster" nor with its replacement, "terra"
when using base-plot
library(spData)
library(terra)
# using a SpatVector for easier plotting; results are the same
v <- vect(spData::cycle_hire_osm)
v <- project(v, "epsg:27700")
r <- rast(v, nrows=10, ncols=10)
chr <- rasterize(v, r, field="capacity", fun=sum, na.rm=TRUE)
plot(chr)
points(v, cex=.5)
points(v[is.na(v$capacity)], cex=.5, col="red")
But note that there are some cells with values of zero where all values of v$capacity are NA. That is because
sum(NA, na.rm=TRUE)
#[1] 0
To avoid that from happening you could do
vv <- v[!is.na(v$capacity)]
chr <- rasterize(vv, r, field="capacity", fun=sum, na.rm=TRUE)
The reason you see differences when using mapview/leaflet is that these use transform your data to the crs that they use. To avoid that use the
Pseudo-Mercator (EPSG:3857) crs, and, in leaflet, use project=FALSE when adding the raster data.
addRasterImage(chr, project=FALSE)

Related

How to replace values that correspond to polygons by NA?

I have a raster and a shapefile:
library(cartography)
library(sf)
library(raster)
r <- raster(matrix(rnorm(10*12), nrow=10), xmn = -180, xmx= 180, ymn = -90, ymx= 90)
mtq <- st_read(system.file("gpkg/mtq.gpkg", package="cartography"), quiet = TRUE)
I would like to intersect the raster r with the shapefile mtq and make the corresponding pixels to the all polygons as NA (replace the values of the pixels in the raster by NA) and return the raster.
You are likely looking for mask; it lives in both oldish {raster} and shiny new {terra}.
Note that I had to rewrite your r object a bit, as it was not quite compatible with the Martinique vector object from {cartography}.
Edit: if, as seems to be indicated in the comments, you are looking for replacing with NAs the values inside the polygon (and not outside) my answer is still raster::mask(), only with a little tweaking of the masking object (you need the inverse of the polygon over the extent of your raster).
library(cartography)
library(sf)
library(raster)
mtq <- st_read(system.file("gpkg/mtq.gpkg", package="cartography"), quiet = TRUE) %>%
dplyr::summarise() # dissolve internal boundaries
r <- raster(matrix(rnorm(10*12), nrow=10),
xmn = st_bbox(mtq)["xmin"],
xmx= st_bbox(mtq)["xmax"],
ymn = st_bbox(mtq)["ymin"],
ymx= st_bbox(mtq)["ymax"],
crs = st_crs(mtq))
plot(r) # original raster - full extent + range
# the masking object:
mask <- st_bbox(r) %>% # take extent of your raster...
st_as_sfc() %>% # make it a sf object
st_set_crs(st_crs(mtq)) %>% # in CRS of your polygon
st_difference(mtq) %>% # intersect with the polygon object
st_as_sf() # interpret as sf (and not sfc) object
result <- r %>%
mask(mask)
plot(result)
plot(st_geometry(mtq), add = T)

Plot global projected raster in R

I'm trying to plot a global (lon:-180- 180; lat -90- 90) raster in Equal Earth projection (doesn't matter - could be Winkel Tripel or Robinson) but the boundaries duplicate on both sides (see figure). How can I avoid this?
This SO question and this thread give an answer that only works for plotting in Mollweide, but no other projection.
Here's a reproducible example.
library(maptools)
library(raster)
data("wrld_simpl")
r <- raster(ncol=180, nrow=90)
r <- rasterize(wrld_simpl, r, field="UN")
world_ext = projectExtent(wrld_simpl, crs = '+proj=longlat +datum=WGS84 +no_defs ')
r <- crop(x = r, y = world_ext, snap= 'in')
r <- projectRaster(r, crs = crs("+proj=wintri"), over = T)
plot(r)
Many thanks!
You can use the mask=TRUE argument in terra::project for this
library(maptools)
library(terra)
data("wrld_simpl")
w <- vect(wrld_simpl)
r <- rast(ncol=180, nrow=90)
r <- rasterize(w, r, field="UN")
x <- project(r, "+proj=wintri", mask=TRUE)

Population density within polygons

So, I have some questions regarding the raster package in R. I have a raster with estimated population in each grid point. I also have a shapefile with polygons of regions. I want to find out the coordinates of the neighborhood with the highest population density within each regions. Supose that each neighborhood is a homogeneous square of 5 by 5 grid points.
The following toy example mimics my problem.
library(raster)
library(maptools)
set.seed(123)
data(wrld_simpl)
wrld_simpl <- st_as_sf(wrld_simpl)
contr_c_am <- wrld_simpl %>%
filter(SUBREGION ==13) %>%
filter(FIPS != "MX") %>%
select(NAME)
# Create a raster of population (sorry for the bad example spatial distribution)
r <- raster(xmn=-180, xmx=180, ymn=-90, ymx=90, res=0.1)
values(r) <- runif(ncell(r), 0, 100)
# keep only raster around the region of interest
r_small <- crop(r, extent(contr_c_am))
plot(r_small)
plot(st_geometry(contr_c_am), add = T)
raster_contr_c_am <- rasterize(contr_c_am, r)
raster_contr_c_am is the population grid and the name of the region is saved as an attribute.
Somehow I need to filter only grid points from one region, and probably use some funcion like focal() to find total nearby population.
focal(raster_contr_c_am, matrix(1,5,5),sum, pad = T, padValue = 0)
Then, I need to find which grid point has the highest value within each region, and save it's coordinates.
I hope my explanation is not too confusing,
Thanks for any help!
Here's an example that iterates over the shape defining the region, then uses the raster values within the region and the focal() function to find the maximum.
library(raster)
library(maptools)
library(sf)
library(dplyr)
set.seed(123)
data(wrld_simpl)
wrld_simpl <- st_as_sf(wrld_simpl)
contr_c_am <- wrld_simpl %>%
filter(SUBREGION ==13) %>%
filter(FIPS != "MX") %>%
select(NAME)
# Create a raster of population (sorry for the bad example spatial distribution)
r <- raster(xmn=-180, xmx=180, ymn=-90, ymx=90, res=0.1)
values(r) <- runif(ncell(r), 0, 100)
# keep only raster around the region of interest
r_small <- crop(r, extent(contr_c_am))
raster_contr_c_am <- rasterize(contr_c_am, r_small)
# function to find the max raster value using focal
# in a region
findMax <- function(region, raster) {
tt <- trim((mask(raster, region))) # focus on the region
ff <- focal(tt, w=matrix(1/25,nc=5,nr=5))
maximumCell <- which.max(ff) # find the maximum cell id
maximumvalue <- maxValue(ff) # find the maximum value
maximumx <- xFromCell(ff, maximumCell) # get the coordinates
maximumy <- yFromCell(ff, maximumCell)
# return a data frame
df <- data.frame(maximumx, maximumy, maximumvalue)
df
}
numberOfShapes <- nrow(contr_c_am)
ll <- lapply(1:numberOfShapes, function(s) findMax(region = contr_c_am[s,], raster = r_small))
merged <- do.call(rbind, ll)
maxpoints <- st_as_sf(merged, coords=c('maximumx', 'maximumy'), crs=crs(contr_c_am))
library(mapview) # optional but nice visualization - select layers to see if things look right
mapview(maxpoints) + mapview(r_small) + mapview(contr_c_am)
I've made an sf object so that it can be plotted with the other spatial objects. Using the mapview package, I get this.

Equivalent of `poly.counts` to count lat/long pairs falling inside of polygons with the sf package

The sf package provides a great approach to working with geographic features, but I can't figure out a simple equivalent to the poly.counts function from GISTools package which desires sp objects.
poly.counts computes the number of points from a SpatialPointsDataFrame fall within the polygons of a SpatialPolygonsDataFrame and can be used as follows:
Data
## Libraries
library("GISTools")
library("tidyverse")
library("sf")
library("sp")
library("rgdal")
## Obtain shapefiles
download.file(url = "https://www2.census.gov/geo/tiger/TIGER2016/STATE/tl_2016_us_state.zip", destfile = "data-raw/states.zip")
unzip(zipfile = "data-raw/states.zip", exdir = "data-raw/states")
sf_us_states <- read_sf("data-raw/states")
## Our observations:
observations_tibble <- tribble(
~lat, ~long,
31.968599, -99.901813,
35.263266, -80.854385,
35.149534, -90.04898,
41.897547, -84.037166,
34.596759, -86.965563,
42.652579, -73.756232,
43.670406, -93.575858
)
Calculate points per polygon
I generate both my sp objects:
sp_us_states <- as(sf_us_states, "Spatial")
observations_spdf <- observations_tibble %>%
select(long, lat) %>% # SPDF want long, lat pairs
SpatialPointsDataFrame(coords = .,
data = .,
proj4string = sp_us_states#proj4string)
Now I can use poly.counts
points_in_states <-
poly.counts(pts = observations_spdf, polys = sp_us_states)
Add this into the sp object:
sp_us_states$points.in.state <- points_in_states
Now I've finished I'd convert back to sf objects and could visualise as follows:
library("leaflet")
updated_sf <- st_as_sf(sp_us_states)
updated_sf %>%
filter(points.in.state > 0) %>%
leaflet() %>%
addPolygons() %>%
addCircleMarkers(
data = observations_tibble
)
Question
Can I perform this operation without tedious conversion between sf and sp objects?
Try the following:
sf_obs = st_as_sf(observations_tibble, coords = c("long", "lat"),
crs = st_crs(sf_us_states))
lengths(st_covers(sf_us_states, sf_obs))
# check:
summary(points_in_states - lengths(st_covers(sf_us_states, sf_obs)))
st_covers returns a list with the indexes of points covered by each state; lengths returns the vector of the lenghts of these vectors, or the point count. The warnings you'll see indicate that although you have geographic coordinates, the underlying software assumes they are cartesian (which, for this case, will be most likely not problematic; move to projected coordinates if you want to get rid of it the proper way)

Clipping raster using shapefile in R, but keeping the geometry of the shapefile

I am using {raster} to clip (or crop) a raster based on an irregular shapefile (the Amazon biome) but the output always has a rectangular extent. However, I need the output in the exact same geometry of the shapefile. Any tips? Cheers.
library(raster)
library(rgdal)
myshp <- readOGR("Amazon.shp", layer="Amazon")
e <- extent(myshp)
myraster <- raster("Temperature.tif")
myraster.crop <- crop(myraster, e, snap="out", filename="myoutput.tif")
One option is to use raster::mask()
library(maptools) ## For wrld_simpl
library(raster)
## Example SpatialPolygonsDataFrame
data(wrld_simpl)
SPDF <- subset(wrld_simpl, NAME=="Brazil")
## Example RasterLayer
r <- raster(nrow=1e3, ncol=1e3, crs=proj4string(SPDF))
r[] <- 1:length(r)
## crop and mask
r2 <- crop(r, extent(SPDF))
r3 <- mask(r2, SPDF)
## Check that it worked
plot(r3)
plot(SPDF, add=TRUE, lwd=2)
Package terra:: made it simpler, you can ::crop and mask in the same step.
# Load packages
library(maptools) # For geometry
library(terra) # Perform the crop and mask
###--- Preparing polygon and raster ---###
# Example SpatialPolygonsDataFrame
data(wrld_simpl)
polygon <- subset(wrld_simpl, NAME=="Luxembourg")
plot(polygon) # have a look
# Convert from SpatialPolygonsDataFrame to SpatVector (terra package format)
# And create a smaller polygon with buffer (negative to be "inside")
polygon_bf <- buffer(vect(polygon), width= -100000)
plot(polygon_bf, add= T) # have a look on both
# Create a SpatRaster from a file
f <- system.file("ex/elev.tif", package="terra")
r_lux <- rast(f)
plot(r_lux) # have a look on SpatRaster (terra package format)
# See the steps with plot
plot(polygon, add= T)
plot(polygon_bf, add= T)
Click to see intermediate steps (all files)
########################################################
### Crop and mask by any polygon ###
raster_cp <- crop(r_lux, polygon_bf, mask= T)
# Note: if mask= F, the crop will be by extent (box) ###
########################################################
### Check the results
plot(raster_cp)
plot(polygon_bf, lwd=1, add=T)
Click to see the final output
In addition, (with raster package) in case you want to perform with a simple geometry (i.g. box), the coordinates of the extent can be place directly:
e <- as(extent(c(xmin= -16, xmax= -7.25, ymin= 4, ymax= 12.75)), 'SpatialPolygons')
crs(e) <- "+proj=longlat +datum=WGS84 +no_defs"
r <- crop(my_raster, e)

Resources