mapview icon indicating copy to clipboard operation
mapview copied to clipboard

Misalignment problem when curvilinear raster grid are ploted as regular ones

Open GillesSanMartin opened this issue 1 year ago • 0 comments

I came across an unexpected behavior of mapview (?) : I create a vector grid with sf:st_make_grid then I transform it into a terra raster object. When I plot both with mapview they are misaligned.

After my question on stackoverflow, Robert Hijman suggested that it was a problem with the Mercator projection used in mapview that should produce a curvilinear grid but is represented with a regular grid (NB: I paraphrase with my own words and understanding)

Here is a reproducible example :

library(sf)
library(terra)
library(stars)
library(ggplot2)
library(mapview)

my_cellsize <- 50000

# Load the 'nc' shapefile from the sf package
nc <- st_read(system.file("shape/nc.shp", package = "sf")) |> 
    st_transform(crs = 32617) # projection in meters

# Create a grid within the extent of the 'nc' dataset
my_grid <-  st_make_grid(nc, cellsize = my_cellsize)
my_grid <- st_sf(GridID = 1:length(my_grid), my_grid)
my_grid <- my_grid[nc,]

# add some random values
my_grid$value1 <- rnorm(nrow(my_grid))

# convert the vector grid into a raster stack
template = rast(vect(my_grid), res = my_cellsize)
value1_raster <- rasterize(vect(my_grid), template, field = "value1")

Various calls to mapview showing the misalignment

# With a simple call to mapview the vector grid and the raster grid are not aligned
mapview(st_as_stars(value1_raster) ) + mapview(my_grid)

# Transforming to the Mercator projection used by mapview does not help
# But the misalignment is different
mapview(st_as_stars(value1_raster) |> st_transform(crs = "+proj=webmerc +datum=WGS84")) + 
    mapview(my_grid |> st_transform(crs = "+proj=webmerc +datum=WGS84"))

# using st_warp instead of st_transform seems to simply reproduce what is done by mapview in the backgroud
mapview(st_as_stars(value1_raster) |> st_warp(crs = "+proj=webmerc +datum=WGS84")) + 
    mapview(my_grid |> st_transform(crs = "+proj=webmerc +datum=WGS84"))

Various calls to other plotting approaches that seem to work fine :

# The raster and the vector grid are correctly 
# plotted with a simple call to plot on the terra objects : 

plot(value1_raster, reset = TRUE)
lines(my_grid, col = "red")

# Using stars, we can also have a correct alignment of a curvilinear raster grid with the vector
# This is what I would expect to see in mapview

stars::st_as_stars(value1_raster) |> 
    st_transform(crs = "+proj=webmerc +datum=WGS84") |> 
    plot(reset = FALSE)
my_grid |> st_transform(crs = "+proj=webmerc +datum=WGS84") |> st_geometry() |> 
    plot(add = TRUE, reset = FALSE, border = "red", lwd = 2, color = NA)

# Same with ggplot : 
ggplot() +
    geom_stars(data = st_transform(st_as_stars(value1_raster), 
                                   crs = "+proj=webmerc +datum=WGS84"), 
               alpha = 0.8, downsample = c(0, 0, 0)) +
    geom_sf(data = st_transform(my_grid, crs = "+proj=webmerc +datum=WGS84"), 
            color = "red", fill = NA) +
    theme_void()

Again, as pointed out by Robert Hijmans, one way to get a correct plot in mapview would be to use the Mercator projection from the beginning. But that does not seem to be a usable solution in real life where you might need other projections adapted to your local conditions to compute areas, distances, etc.

my_cellsize <- 50000
nc <- st_read(system.file("shape/nc.shp", package = "sf")) |> 
    st_transform(crs = "+proj=webmerc +datum=WGS84") 
my_grid <-  st_make_grid(nc, cellsize = my_cellsize)
my_grid <- st_sf(GridID = 1:length(my_grid), my_grid)
my_grid <- my_grid[nc,]
my_grid$value1 <- rnorm(nrow(my_grid))
template = rast(vect(my_grid), res = my_cellsize)
value1_raster <- rasterize(vect(my_grid), template, field = "value1")

mapview(st_as_stars(value1_raster)) + mapview(st_as_sf(as.lines(vect(my_grid))))

GillesSanMartin avatar Oct 24 '23 20:10 GillesSanMartin