r-tmap / tmap

R package for thematic maps
https://r-tmap.github.io/tmap
GNU General Public License v3.0
865 stars 121 forks source link

tm_scale_continuous displaying with interval legend with linestrings #889

Open marine-ecologist opened 4 months ago

marine-ecologist commented 4 months ago

I'm converting v3 code to v4 and can't seem to get the new tm_scale_continuous to work with linestrings in that the continuous legend color scale reverts to intervals. Example below:

# generate 20 linestrings  with numeric groups:

library(sf)

set.seed(42)

lon_min <- -180
lon_max <- 180
lat_min <- -90
lat_max <- 90

create_random_line <- function() {
  lon_start <- runif(1, lon_min, lon_max)
  lat_start <- runif(1, lat_min, lat_max)
  lon_end <- runif(1, lon_min, lon_max)
  lat_end <- runif(1, lat_min, lat_max)

  st_linestring(matrix(c(lon_start, lat_start, lon_end, lat_end), ncol = 2, byrow = TRUE))
}

lines <- lapply(1:20, function(x) create_random_line())

lines_sf <- st_sfc(lines, crs = 4326)  # Use WGS84 (EPSG:4326)
values_df <- data.frame(value = 1:20)
lines_sf <- st_sf(values_df, geometry = lines_sf)

library(tmap)

tm_shape(lines_sf) +
  tm_lines("group", 
           lwd=5, 
           tm_scale_continuous(values="brewer.spectral")) +
tm_place_legends_right()

Screenshot 2024-06-17 at 6 20 58 am

When changing to points tm_scale_continuous works fine (presumably as it's fill and not col?):


tm_shape(points_sf) +
  tm_dots("value", 
           size=2, 
           tm_scale_continuous(values="brewer.spectral")) +
tm_place_legends_right()

Screenshot 2024-06-17 at 6 21 32 am

is there a way of getting a continuous colorscale legend with col?

mtennekes commented 4 months ago

This is by (current) design. The legend in the first map are not intervals, but a regular (pretty) sample of geometries (lines in this case) in the continuous scale. I thought that this may be useful in some applications (?) From that perspective, the second map is wrong, and should also include the same sample of bubbles (so a orange bubble with 5, yellow with 10 etc). Note that a sample is also used for other visual variables (e.g. size).

Apart from the fact that maps 1 and 2 are not consistent (which they should be), the question is what you prefer:

Second question: how should a user select the non-default option? What would be a good argument name in tm_legend_x?

Note-to-self: https://github.com/r-tmap/tmap/blob/master/R/tmapGridComp_leg_portrait.R#L15

marine-ecologist commented 4 months ago

I've corrected the maps below to make the example more consistent, where the tm_dots are the endpoints of the tm_lines:

points_sf <- lines_sf |> st_cast("POINT")

tm_shape(points_sf) +
  tm_dots("value", 
          size=0.5, 
          tm_scale_continuous(values="brewer.spectral"), shape=21) +
  tm_shape(lines_sf) +
  tm_lines("value", 
           tm_scale_continuous(values="brewer.spectral"), 
           lwd=1) +
  tm_place_legends_right()

Screenshot 2024-07-11 at 11 53 17 AM

With the above example, the colours are both set as tm_scale_continuous, but the tm_lines are displayed as intervals not as a colour gradient. As an asides, I don't think the map is "wrong" because the bubbles don't reflect size: often tm_bubbles is visually distracting where a fixed size and simple color gradient would suffice.

As a possible answer: why not allow tm_lines to function the same as tm_dots in terms of allowing both tm_scale_continuous and tm_scale_intervals (rather than defaulting continuous to intervals)?

Expanded example on above with tm_dots, same map, different color settings:


a <- tm_shape(points_sf) +
  tm_dots("value", 
          size=1, 
          tm_scale_continuous(values="brewer.spectral"), shape=21) +
  tm_labels("value",
            size=0.8) +
  tm_place_legends_right()

b <- tm_shape(points_sf) +
  tm_dots("value", 
          size=1, 
          tm_scale_intervals(values="brewer.spectral"), shape=21) +
  tm_labels("value",
            size=0.8) +
  tm_place_legends_right()

tmap_arrange(a,b, ncol=1)

Screenshot 2024-07-11 at 11 43 30 AM

marine-ecologist commented 4 months ago

As a clearer "real world" tmap example (hopefully!) that lead me to the question: a tm_scale_continuous legend would be more useful than an interval legend here as the linestring is a continuous (non-linear) gradient!

b

GuiAlDuS commented 3 months ago

I have a similar comment regarding tm_scale_continuous but in my case with tm_symbols(). I'm looking the have the continuous color scale legend but so far I haven't been able to create it:

tm_basemap("Esri.WorldImagery", alpha = 0.5) +
  tm_shape(SD_allsps) +
  tm_symbols(
    size = "radius_m", size.legend = tm_legend(show = F),
    col = "fcompleteness_2021", col_alpha = 0.9, 
    col.scale = tm_scale_continuous(values = viridis::mako(100)), 
    col.legend = tm_legend(title = "Mean Completeness 2021", 
                           position = tm_pos_in("left", "bottom"),
                           bg.color = "white",
                           text.size = 0.5, title.size = 0.6,
                           show = T),
    fill = "fcompleteness_2021", fill_alpha = 0.1, 
    fill.scale = tm_scale(values = "white"), fill.legend = tm_legend(show = F))

image