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

Multi-variable / multivariate specification functions #819

Open
marine-ecologist opened this issue Jan 2, 2024 · 14 comments
Open

Multi-variable / multivariate specification functions #819

marine-ecologist opened this issue Jan 2, 2024 · 14 comments
Labels

Comments

@marine-ecologist
Copy link

Example using the stars raster:

L7file = system.file("tif/L7_ETMs.tif", package = "stars")
L7 = read_stars(L7file)

### working but gives warning
tm_shape(L7) + 
   tm_raster(3, 2, 1)

# Warning message:
# In value[[3L]](cond) : could not rename the data.table

### not working gives error
tm_shape(L7) + 
   tm_rgb(3, 2, 1)

# Error: palette should be a character value
# In addition: Warning message:
# In value[[3L]](cond) : could not rename the data.table
@Nowosad Nowosad added the bug label Jan 3, 2024
@mtennekes
Copy link
Member

Thx. It's already some time ago that I've worked on stars objects with bands and attributes. So I have to recall which function calls should be supported and that the 'correct' approach is.

At least, tmap4 does the 'multivariate' stuff via tm_mv and tm_mv_dim.

Since L7 is a stars object with a dimension called band this is the correct (but tedious looking) code:

tm_shape(L7) + 
	tm_raster(col = tm_mv_dim("band", c(3, 2, 1)), col.scale = tm_scale_rgb())

Or, shorter:

tm_shape(L7) + 
	tm_rgb(tm_mv_dim("band", c(3, 2, 1)))

image

Have to make sure your code works as well (backwards compatibility) or at least throws some useful errors.

Do you know if this code worked in earlier tmap4 development versions?

@mtennekes
Copy link
Member

FYI: This works as well

L7split = split(L7)
tm_shape(L7split) + 
	tm_rgb(tm_mv("X3", "X2", "X1"))

but tm_rgb() does not because the default is tm_mv(3, 2, 1). So I need to make sure numbers are translated to attribute indices...

@mtennekes
Copy link
Member

mtennekes commented Jul 21, 2024

See the newly added examples of tm_rgb. Now there are three functions to specify multivariate visual variables (such as rgb color channels:

  • tm_mv that uses attribute/layer names
  • tm_mv_shape_vars that uses attribute/layer indices
  • tm_mv_dim that uses a (stars) dimension

Let me now how useful this is.

mtennekes added a commit that referenced this issue Jul 21, 2024
@Nowosad
Copy link
Member

Nowosad commented Jul 22, 2024

@mtennekes would it be possible to merge tm_mv and tm_mv_shape_vars into just one function that would react to either provided names or indices?

mtennekes added a commit that referenced this issue Jul 23, 2024
@mtennekes
Copy link
Member

That's not so easy, because in general, the value specified for a visual variable (say col) is either a data variable name (e.g. "life_exp") or a visual value (e.g. "#ABCDEF").

tm_mv also follows this procedure, but for multivariate data. Therefore, tm_mv(1, 2, 3), could be processed as a multivariate visual value, so rgb(1,2,3, maxColorValue = 255). (Note: this does not work yet, because col requires a color name/hex code; to make this work, I need to make use of the tm_scale_rgb in order to obtain maxColorValue).

For tm_raster, I needed a default specification to show all attributes, for example for tm_shape(land) + tm_raster().
That lead to the function tm_shape_vars. For tm_rgb is was relatively easy to extend this function for multivariante data. Hence tm_mv_shape_vars.

That is the history behind these functions and their names. Suggestion to make this more intuitive (while still possible to implement) are welcome.

@Nowosad
Copy link
Member

Nowosad commented Aug 5, 2024

@mtennekes, what do you think about updating the names:

  • tm_mv_dim that uses a (stars) dimension
  • tm_mv_lyr that uses attribute/layer indices

?

@mtennekes
Copy link
Member

I like those names @Nowosad before we need to make sure to cover all use cases. Not just raster objects, but also vector objects. For instance, the function tm_mv_shape_vars also applies to polygons, so tm_mv_lyr would be a less intuitive name here.

Currently this family of functions is:

1 tm_mv(...)
2 tm_mv_dim(x, values)
3 tm_shape_vars(ids, n)
4 tm_mv_shape_vars(ids, n)

Examples of vector shapes:

tm_shape(World) +
    tm_polygons(tm_mv("HPI", "well_being"))

tm_shape(World) +
    tm_polygons(tm_shape_vars(n = 3))

tm_shape(World) +
    tm_polygons(tm_mv_shape_vars(ids = c(4, 9)))

So what would be the most intuitive names? We could also combine tm_mv and tm_mv_shape_vars and call it tm_mv_vars. For the multiple-variables (but not multivariate) case, so fill = c("HPI", "well_being") and fill = tm_shape_vars(n = 3), we could introduce tm_vars. So then we have:

1 tm_vars(..., ids, n)
2 tm_mv_vars(..., ids, n)
3 tm_mv_dim(x, values)

Then, tm_mv_vars is the same as your suggestion tm_mv_lyr (variable = layer = attribute) => not sure which of these names is the most intuitive.

@mtennekes mtennekes changed the title tm_rgb() returns "Error: palette should be a character value" with v3.99.9000 Multi-variable / multivariate specification functions Aug 12, 2024
@mtennekes mtennekes reopened this Aug 12, 2024
@mtennekes mtennekes removed the bug label Aug 13, 2024
@mtennekes
Copy link
Member

mtennekes commented Aug 16, 2024

Ignore my previous post. I've managed to make it into one function: tm_vars 4e7f35d

@Nowosad @marine-ecologist Please check this script https://github.com/r-tmap/tmap/blob/master/sandbox/vv2.R, which we should convert to vignette(s).

tm_vars will have the arguments:

  • x which can be either a vector variable names (previously the ...), or variable indices (previously ids), or a single dimension name
  • values only used in case x is a dimension
  • n the first n variables in case x is not specified (and in case x is a dimension and values are not specified, the first n unique (sorted) values of this dimension.
  • multivariate. FALSE means multiple variables (facets), TRUE means multivariate (e.g. bivariate choropleth)

This has breaking changes w.r.t. the earlier v4 implementation of multivariate visual variables. E.g.

tm_shape(World) +
	tm_polygons(tm_mv("HPI", "well_being"))

will become

tm_shape(World) +
	tm_polygons(tm_vars(c("HPI", "well_being")))

@Nowosad
Copy link
Member

Nowosad commented Aug 19, 2024

Awesome! I think that this is much more intuitive.

I checked the script, and have a few comments (reprex attached below):

  1. The "complex stars" section returns a few identical outputs based on different tmap codes. I think this is not as expected (?).
  2. The terra section gives a warning.
  3. The glyphs section returns an error.
library(tmap)
file = system.file("tif/L7_ETMs.tif", package = "stars")

# 1 complex stars ???

# 2 -- spurious warning
L7_terra = terra::rast(file)

tm_shape(L7_terra) +
tm_rgb(tm_vars(dimvalues = 1:3, multivariate = TRUE))
#> Warning in apply_scale(s, l, crt, val, unm, nm__ord, "legnr", "crtnr", sortRev,
#> : Too many variables defined

    
# 3 error

library(tmap.glyphs)

tm_shape(NLD_prov) + 
tm_polygons() +
tm_donuts(parts = tm_vars(c("origin_native", "origin_west", "origin_non_west"), multivariate = TRUE),
  size = "population",
  size.scale = tm_scale_continuous(values.scale = 1),
  fill.scale = tm_scale_categorical(values = "brewer.dark2"))
#> Error in get(fun, mode = "function", envir = envir): object 'tmapScaleComposition' of mode 'function' was not found

mtennekes added a commit to r-tmap/tmap.glyphs that referenced this issue Aug 19, 2024
mtennekes added a commit that referenced this issue Aug 19, 2024
@mtennekes
Copy link
Member

Thx @Nowosad

  • The "complex stars" section returns a few identical outputs based on different tmap codes. I think this is not as expected (?).

Actually, it is as expected, just to show the multiple ways to Rome. Maybe it would be better to ignore the output, and show multiple tmap code chunks that produce the same map.

  • The terra section gives a warning.

Fixed. The problem was in the tmap code: terra doesn't use dimensions, so dimvalues are ignored. By default all (in this case 6) variables are shown. Perhaps the warning could be a little more helpful.

  • The glyphs section returns an error.

Fixed! (please reinstall tmap.glyphs with the latest gh version)

@Nowosad
Copy link
Member

Nowosad commented Aug 21, 2024

Hi @mtennekes -- just to let you know, I am still getting warnings/errors after updating the packages from GitHub, see:

   # remotes::install_github("r-tmap/tmap")
# remotes::install_github("r-tmap/tmap.glyphs")
library(tmap)
file = system.file("tif/L7_ETMs.tif", package = "stars")

# 1 -- spurious warning
L7_terra = terra::rast(file)

tm_shape(L7_terra) +
tm_rgb(tm_vars(dimvalues = 1:3, multivariate = TRUE))
#> Warning in apply_scale(s, l, crt, val, unm, nm__ord, "legnr", "crtnr", sortRev,
#> : Too many variables defined

# 2 -- glyphs
library(tmap.glyphs)
tm_shape(NLD_prov) + 
tm_polygons() +
tm_donuts(parts = tm_vars(c("origin_native", "origin_west", "origin_non_west"), multivariate = TRUE),
  size = "population",
  size.scale = tm_scale_continuous(values.scale = 1),
  fill.scale = tm_scale_categorical(values = "brewer.dark2"))
#> Error in get_scale_defaults(scale, o, aes, layer, cls, ct): could not find function "get_scale_defaults"

@mtennekes
Copy link
Member

Should be fixed. Please check if the error message for (1) is clear and if (2) works @Nowosad

@Nowosad
Copy link
Member

Nowosad commented Aug 23, 2024

Thanks, @mtennekes -- the first example is fixed, but the second one still have some issues:

# remotes::install_github("r-tmap/tmap")
# remotes::install_github("r-tmap/tmap.glyphs")
library(tmap)
file = system.file("tif/L7_ETMs.tif", package = "stars")

# glyphs
library(tmap.glyphs)
tm_shape(NLD_prov) + 
  tm_polygons() +
  tm_donuts(parts = tm_vars(c("origin_native", "origin_west", "origin_non_west"), multivariate = TRUE),
            size = "population",
            size.scale = tm_scale_continuous(values.scale = 1),
            fill.scale = tm_scale_categorical(values = "brewer.dark2"))
#> Error in tmapValuesCVV_num(x = c(0, 1), value.na = NA, n = 3L, range = c(0, : could not find function "tmapValuesCVV_num"

mtennekes added a commit that referenced this issue Aug 28, 2024
@mtennekes
Copy link
Member

Thanks, @mtennekes -- the first example is fixed, but the second one still have some issues:

# remotes::install_github("r-tmap/tmap")
# remotes::install_github("r-tmap/tmap.glyphs")
library(tmap)
file = system.file("tif/L7_ETMs.tif", package = "stars")

# glyphs
library(tmap.glyphs)
tm_shape(NLD_prov) + 
  tm_polygons() +
  tm_donuts(parts = tm_vars(c("origin_native", "origin_west", "origin_non_west"), multivariate = TRUE),
            size = "population",
            size.scale = tm_scale_continuous(values.scale = 1),
            fill.scale = tm_scale_categorical(values = "brewer.dark2"))
#> Error in tmapValuesCVV_num(x = c(0, 1), value.na = NA, n = 3L, range = c(0, : could not find function "tmapValuesCVV_num"

Ok, the second one should be working. I had to export a s***tload of internal functions to make it work. They are hidden from the documentation index. However, they mess up the autocompletion when typing tmap::. Perhaps we should rename them with the prefix ".", so .tmapGridLines instead of tmapGridLines etc.

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

No branches or pull requests

3 participants