tmap_mode("view") not rendering polygons correctly with >500 features in an sfc
tmap 4.1: I'm trying to plot an sf object with >500 polygons through packcircles with tmap "view". After trying to debug why the sf object is generating incorrect interactive plots, it seems that 1:499 polygons works fine, but >=500 results in an interactive view issue. Apologies in advance if this is related to my use of packcircles, but I can't see an alternative explanation with the sf objects other than nrow. Reproducible example below:
Working with 499 polygons:
library(dplyr)
library(sf)
library(packcircles)
library(tibble)
library(tmap)
tmap_mode("view")
n_individuals <- 499
species_n <- sample(letters[1:11], n_individuals, replace = TRUE)
# Generate random radii
radii <- runif(n_individuals, min = 0.1, max = 0.4)
# Generate circle centers within bounding box
x <- runif(n_individuals, min = -0.25, max = 20.5)
y <- runif(n_individuals, min = -0.25, max = 10.5)
# Create circle layout data
layout <- tibble(x = x, y = y, radius = radii, id = seq_along(x))
# Create circle vertices
circle_vertices <- packcircles::circleLayoutVertices(layout)
# Convert to sf polygons
circles_sf <- circle_vertices |>
sf::st_as_sf(coords = c("x", "y"), crs = 3857) |>
group_by(id) |>
summarise(geometry = sf::st_combine(geometry), .groups = "drop") |>
sf::st_cast("POLYGON") |>
mutate(species = species_n)
tm_shape(circles_sf) +
tm_polygons(fill = "species",
col="black", fill_alpha = 0.7) +
tm_view(set_zoom_limits = c(22, 30))
not working with 502 polygons:
library(dplyr)
library(sf)
library(packcircles)
library(tibble)
n_individuals <- 251
generate_circles <- function(seed_offset = 0) {
set.seed(100 + seed_offset)
species_n <- sample(letters[1:11], n_individuals, replace = TRUE)
radii <- runif(n_individuals, min = 0.1, max = 0.4)
x <- runif(n_individuals, min = -0.25, max = 20.5)
y <- runif(n_individuals, min = -0.25, max = 10.5)
layout <- tibble(x = x, y = y, radius = radii, id = seq_along(x))
vertices <- packcircles::circleLayoutVertices(layout)
sf::st_as_sf(vertices, coords = c("x", "y"), crs = 3857) |>
group_by(id) |>
summarise(geometry = sf::st_combine(geometry), .groups = "drop") |>
sf::st_cast("POLYGON") |>
mutate(species = species_n, replicate = seed_offset + 1)
}
circles1 <- generate_circles(0)
circles2 <- generate_circles(1)
circles_all <- bind_rows(circles1, circles2)
tm_shape(circles_all) +
tm_polygons(fill = "species",
col="black", fill_alpha = 0.7) +
tm_view(set_zoom_limits = c(22, 30))
But... same sf object working fine with slice_sample = 499
tm_shape(circles_all |> slice_sample(n=499)) +
tm_polygons(fill = "species",
col="black", fill_alpha = 0.7) +
tm_view(set_zoom_limits = c(22, 30))
and no problems with >500 nrow and tmap_mode("plot")
tm_shape(circles_all) +
tm_polygons(fill = "species",
col="black", fill_alpha = 0.7) +
tm_view(set_zoom_limits = c(22, 30))
Same with circles and simpler sf shapes (squares), and doesn't seem to be linked to crs:
library(dplyr)
library(sf)
library(packcircles)
library(tibble)
n_individuals <- 1000
n_replicates <- 1
generate_random_circles <- function() {
species_n <- sample(letters[1:11], n_individuals, replace = TRUE)
radii <- runif(n_individuals, min = 0.1, max = 0.4)
x <- runif(n_individuals, min = -0.25, max = 20.5)
y <- runif(n_individuals, min = -0.25, max = 10.5)
layout <- tibble(x = x, y = y, radius = radii, id = seq_along(x))
vertices <- packcircles::circleLayoutVertices(layout)
sf::st_as_sf(vertices, coords = c("x", "y"), crs = 3857) |>
group_by(id) |>
summarise(geometry = sf::st_combine(geometry), .groups = "drop") |>
sf::st_cast("POLYGON") |>
mutate(species = species_n)
}
circles_all <- bind_rows(
replicate(n_replicates, generate_random_circles(), simplify = FALSE),
.id = "replicate"
)
n_individuals <- 1000
n_replicates <- 1
generate_random_squares <- function() {
species_n <- sample(letters[1:11], n_individuals, replace = TRUE)
side <- runif(n_individuals, min = 0.1, max = 0.4)
x <- runif(n_individuals, min = -0.25, max = 20.5)
y <- runif(n_individuals, min = -0.25, max = 10.5)
square_list <- lapply(seq_len(n_individuals), function(i) {
cx <- x[i]
cy <- y[i]
s <- side[i] / 2
coords <- matrix(c(
cx - s, cy - s,
cx - s, cy + s,
cx + s, cy + s,
cx + s, cy - s,
cx - s, cy - s
), ncol = 2, byrow = TRUE)
sf::st_polygon(list(coords))
})
sf::st_sf(
species = species_n,
geometry = sf::st_sfc(square_list, crs = 3857)
)
}
squares_all <- bind_rows(
replicate(n_replicates, generate_random_squares(), simplify = FALSE),
.id = "replicate"
)
tm_shape(squares_all |> slice_sample(n=499)) +
tm_polygons(fill = "species",
col="black", fill_alpha = 0.7) +
tm_view(set_zoom_limits = c(22, 30))
tm_shape(squares_all |> slice_sample(n=501)) +
tm_polygons(fill = "species",
col="black", fill_alpha = 0.7) +
tm_view(set_zoom_limits = c(22, 30))
tm_shape(squares_all |> st_transform(4326)) +
tm_polygons(fill = "species",
col="black", fill_alpha = 0.7) +
tm_view(set_zoom_limits = c(22, 30))
Thx @marine-ecologist
It's an issue with webgl layers. I'll look into it. For the time being, set use_WebGL = FALSE to disable this (it's disabled by default when n < 500):
tm_shape(squares_all |> slice_sample(n=501)) +
tm_polygons(fill = "species",
col="black", fill_alpha = 0.7) +
tm_view(set_zoom_limits = c(22, 30), use_WebGL = FALSE)
@marine-ecologist seems to have been solved upstream (strange polygon rendering r-spatial/leafgl#106).
I increased the threshold number of features for which webgl is enabled to 1000 (was 500).