rspatialtmap

tmap: jitter multiple points equally


I am trying to shift points on a map so that they do not overlap, but simultaneously shift them in pairs so that the pairs of points remain together. Any help appreciated.

I am using tmap and trying to simultaneously map the focal species for a study (pie$Species) and the number of studies on that species (pie$Number of effects). I can achieve this below by simultaneously mapping two points on-top of each other, such that for any one location the blue colour depicts the focal species and the red colour depicts the number of studies. However, these pairs of points (species and number of studies) overlap each other - i would like to avoid this.

This is my original and problematic map

library(sf); library(tmap); library(dplyr)

# This is my data
pie <- structure(list(Species = structure(c(1L, 1L, 1L, 1L, 1L, 2L, 
2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L), levels = c("Dingo (Canis familiaris)", 
"Feral cat (Felis catus)", "Red fox (Vulpes vulpes)"), class = "factor"), 
    latitude = c(-36.50086, -30.57784, -28.04301, -26.138, -21.47545, 
    -43.23844, -42.94019, -39.44835, -38.41747, -38.34843, -36.82553, 
    -34.91901, -33.88935, -33.68501, -31.5887, -31.37366, -30.38102, 
    -26.20224, -26.138, -25.84633, -25.81489, -25.75497, -23.52104, 
    -22.38505, -21.75822, -20.46208, -0.61152, -0.45597, -37.99066, 
    -36.76044, -36.00215, -35.04479, -32.13399, -31.98168, -30.38102, 
    -29.7344, -25.17532), longitude = c(148.26707, 152.37911, 
    117.89112, 121.352, 116.32237, 148.00311, 172.71215, 176.15995, 
    176.56565, 145.33848, 148.56044, 118.20129, 119.88379, 123.44054, 
    138.63257, 138.66283, 136.89996, 113.39237, 121.352, 113.89008, 
    113.05337, 113.54578, 149.21906, 118.79203, 116.01823, 115.52568, 
    -90.36483, -90.27174, 144.61613, 144.28746, 145.10796, 150.8078, 
    148.78329, 148.95018, 136.89996, 151.7338, 114.16974), `Number of effects` = c(1L, 
    10L, 4L, 1L, 3L, 1L, 1L, 1L, 2L, 1L, 1L, 1L, 2L, 5L, 3L, 
    1L, 5L, 7L, 2L, 1L, 5L, 44L, 2L, 3L, 2L, 1L, 1L, 1L, 2L, 
    4L, 1L, 1L, 1L, 1L, 1L, 2L, 1L)), row.names = c(NA, -37L), class = "data.frame")

# Format new dataframe
colnames(pie) <- c("Species", "latitude", "longitude", "Number of effects")
pie$latitude <- as.numeric(paste(pie$latitude))
pie$longitude <- as.numeric(paste(pie$longitude))

# Convert dataframe to spatial dataframe
pie.sf <- st_as_sf(pie, coords = c('longitude', 'latitude'), crs = 4326)

# Create bounding box for plotting
bbox <- st_bbox(pie.sf) 

# Alter bounding box slightly
xrange <- bbox$xmax - bbox$xmin # range of x values
yrange <- bbox$ymax - bbox$ymin # range of y values
bbox[1] <- bbox[1] - (0.05 * xrange) # xmin - left
bbox[3] <- bbox[3] + (0.2 * xrange) # xmax - right
bbox[2] <- bbox[2] - (0.1 * yrange) # ymin - bottom
bbox[4] <- bbox[4] + (0.1 * yrange) # ymax - top

# Original map
data("World")
tm_shape(World, bbox = bbox) + tm_borders() +
qtm(pie.sf, dots.col = 'Species', dots.palette = 'Blues', dots.size = 0.15) +
qtm(pie.sf, dots.col = 'Number of effects', dots.palette = 'Reds', dots.size = 0.03)

You can see in the above map that the pairs of blue and red points overlap Map.

My solution was to try and jitter each mapped point by the same amount and in the same direction. However, I could not achieve this

# Attempted solution to set.seed() prior to each call to symbol.jitter()
p <- tm_shape(World, bbox = bbox) + tm_borders()

set.seed(123)
p <- p + qtm(pie.sf, dots.col = 'Species', dots.palette = 'Blues', dots.size = 0.15, symbols.jitter = 0.2)

set.seed(123)
p <- p + qtm(pie.sf, dots.col = 'Number of effects', dots.palette = 'Reds', dots.size = 0.03, symbols.jitter = 0.2)

Now the pairs of species and number of studies for each location are separated, meaning that for any single location you cannot see both the focal species and the number of studies. Map

Help appreciated, thanks.


Solution

  • One option would be to jitter the "geometry" column, i.e. convert the geometry column to coordinates, then use jitter to jitter the coordinates. Then convert back to an sf object which could be used to plot your symbols.

    library(sf)
    library(tmap)
    library(dplyr)
    
    set.seed(123)
    
    pie.sf_jittered <- apply(sf::st_coordinates(pie.sf), 2, jitter, amount = 10) |>
      as_tibble() |>
      bind_cols(pie.sf |> sf::st_drop_geometry()) |>
      st_as_sf(coords = c("X", "Y"), crs = st_crs(pie.sf))
    
    tm_shape(World, bbox = bbox) + tm_borders() +
      qtm(pie.sf_jittered, dots.col = "Species", dots.palette = "Blues", dots.size = 0.15) +
      qtm(pie.sf_jittered, dots.col = "Number of effects", dots.palette = "Reds", dots.size = 0.03)