Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: scale.values argument to tm_scale_* support flexible resizing. #877

Closed
SebKrantz opened this issue May 13, 2024 · 3 comments

Comments

@SebKrantz
Copy link

SebKrantz commented May 13, 2024

I have an interval scale of the form

tm_dots(size = "population", 
        size.scale = tm_scale_intervals(breaks = c(0, 1, 2e5, 1e6, Inf), values.scale = 0.3),
        size.legend = tm_legend("City Category", frame = FALSE))

The result is
Screenshot at May 13 22-43-05

Now I want the circle sizes to roughly reflect the city sizes. This does not seem possible using values.scale, which only controls the overall size. Passing a function such as scale.values = exp or a vector of values of the same length as breaks would facilitate flexible resizing. Right now, the only way seems to define a new method for the multiplication operator:

# Define the constructor for the mult_power class
mult_power <- function(x) {
  # Ensure that x is numeric
  if (!is.numeric(x)) {
    stop("Input must be numeric.")
  }
  structure(x, class = "mult_power")
}

# Define the Ops method for the mult_power class
Ops.mult_power <- function(e1, e2) {
  # Detect the operation being performed
  if (.Generic == "*") {
    # Perform exponentiation if the operation is multiplication
    return(mult_power(e1^e2/sqrt(e2)))
  } else {
    # Default to standard operations for other types of operations
    # This calls the standard Ops method after dropping the class
    return(NextMethod(.Generic))
  }
}

such that

tm_dots(size = "population", 
        size.scale = tm_scale_intervals(breaks = c(0, 1, 2e5, 1e6, Inf), values.scale = mult_power(3)),
        size.legend = tm_legend("City Category", frame = FALSE))

yields

Screenshot at May 13 22-46-48

However, this is not elegant.

@mtennekes
Copy link
Member

Thanks for this question. Your solution may not be intuitive for the user, but surely helpful for me:-)

The classes of the intervals scale are in principle independent of the breaks. (Because there is not one standard way to make a relation.)

That explains the first legend: the sizes of the dots ( (in tmap jargon "values" of the scale) are from a squared scale from 0 to 1 (more specifically sqrt(seq(0,1,length.out=4))). The 0 and 1 can be changed with values.range.

What you are probably looking for (please correct me if I'm wrong) is simply "values". See first example below. However, note that these values are also in the map. Not sure if that is what you want? Alternatively, you can use a continuous scale (example 2), and use ticks and labels to change the legend one with intervals.

# Example 1
tm_shape(metro) +
  tm_dots(size = "pop1950", 
    size.scale = tm_scale_intervals(breaks = c(0, 1, 2e5, 1e6, Inf), values = c(.5,1,2,3), values.scale = 0.3),
    size.legend = tm_legend("City Category", frame = FALSE))

# Example 2
tm_shape(metro) +
  tm_dots(size = "pop1950", 
    size.scale = tm_scale_continuous(values.scale = 1, limits = c(0, 1e6), outliers.trunc = c(TRUE, TRUE)),
    size.legend = tm_legend("City Category", frame = FALSE))

# Example 3
tm_shape(metro) +
  tm_dots(size = "pop1950", 
    size.scale = tm_scale_continuous(
      values.scale = 1, 
      limits = c(0, 1e6), 
      ticks = c(5e4, 2e5, 5e5, 1e6), 
      labels = c("0 - 1", "1 - 200,000", "200,000 - 1,000,000", "1,000,000 or more"), 
      outliers.trunc = c(TRUE, TRUE)),
      size.legend = tm_legend("City Category", frame = FALSE))

Created on 2024-05-14 with reprex v2.1.0

Let me know if this is any helpful and if you have further questions or suggestions.

@SebKrantz
Copy link
Author

Thanks! Appreciate those examples, and would encourage you to include them in the documentation, e.g. of tm_scale_continuous().

@mtennekes
Copy link
Member

You're welcome. Good idea to add them to the examples!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants