Skip to content

Commit

Permalink
fixed #888, #885, added glyph support (for tmap.glyphs)
Browse files Browse the repository at this point in the history
  • Loading branch information
mtennekes committed Jun 11, 2024
1 parent 0e3d893 commit 094dbe4
Show file tree
Hide file tree
Showing 14 changed files with 324 additions and 24 deletions.
4 changes: 0 additions & 4 deletions R/process_meta.R
Original file line number Diff line number Diff line change
Expand Up @@ -419,15 +419,11 @@ process_meta = function(o, d, cdt, aux) {

cdt2[is.na(by1__), by1__:=1]

po(meta.auto.margins)

meta.auto.margins = pmin(meta.auto.margins, do.call(pmax, lapply(unique(cdt2$by1__), function(b1) {
cdt2b = cdt2[by1__==b1, ]

cdt2b[stack_auto == TRUE, stack:= ifelse(n==1, ifelse(cell.h %in% c("left", "right"), o$legend.stack["all_row"], o$legend.stack["all_col"]), ifelse(orientation == "vertical", o$legend.stack["per_row"], o$legend.stack["per_col"]))]

print(cdt2b)

c(sum(sum(c(0,cdt2b[cell.v == "bottom" & stack == "vertical", legH,by = c("cell.h", "cell.v")]$legH)),
max(c(0,cdt2b[cell.v == "bottom" & stack == "horizontal", legH,by = c("cell.h", "cell.v")]$legH))) / o$devsize[2],
sum(sum(c(0,cdt2b[cell.h == "left" & stack == "horizontal", legW,by = c("cell.h", "cell.v")]$legW)),
Expand Down
90 changes: 79 additions & 11 deletions R/step2_helper_data.R
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
update_l = function(o, l, v, mfun) {
update_l = function(o, l, v, mfun, unm, active) {
# update legend options
oltype = o[c("legend.design", "legend.orientation")]
names(oltype) = c("design", "orientation")
Expand Down Expand Up @@ -26,13 +26,15 @@ update_l = function(o, l, v, mfun) {
l = complete_options(l, oleg)
l$call = call
l$mfun = mfun
l$unm = unm
l$active = active

# update legend class
class(l) = c(paste0("tm_legend_", l$design, ifelse(!is.null(l$orientation), paste0("_", l$orientation), "")), class(l))
l
}

update_crt = function(o, crt, v, mfun) {
update_crt = function(o, crt, v, mfun, unm, active) {

#crt_options
cls = class(crt)
Expand All @@ -46,14 +48,17 @@ update_crt = function(o, crt, v, mfun) {
if ("position" %in% names(crt) && is.character(crt$position)) crt$position = str2pos(crt$position)
if ("position" %in% names(crt) && is.numeric(crt$position)) crt$position = num2pos(crt$position)
if ("position" %in% names(crt) && inherits(crt$position, "tm_pos")) {
l$position = complete_options(crt$position, o$component.position[[crt$position$type]])
crt$position = complete_options(crt$position, o$component.position[[crt$position$type]])
if (crt$position$type %in% c("autoin", "autoout")) message_pos_auto(crt$position$type)
}

crt = complete_options(crt, ocrt)
crt$call = call
crt$mfun = mfun

crt$unm = unm
crt$active = active


# update legend class
#class(l) = c(paste0("tm_legend_", l$design, ifelse(!is.null(l$orientation), paste0("_", l$orientation), "")), class(l))
#l
Expand Down Expand Up @@ -105,12 +110,71 @@ getdts = function(aes, unm, p, q, o, dt, shpvars, layer, mfun, args, plot.order)
sfun = paste0("tmapValuesScale_", nm)
cfun = paste0("tmapValuesColorize_", nm)


if (inherits(val, "tmapUsrCls")) {
# not data driven variable: visual variable to support tm_mv of other visual variable (e.g. fill of donut maps)
dtl = data.table::data.table()
temp = local({
# not data driven variable: visual variable to support tm_mv of other visual variable (e.g. fill of donut maps)
k = length(aes$value[[1]])
vls = unname(aes$value[[1]])
dtl = data.table::data.table(tmapID__ = 1L:k, sel = TRUE, value = factor(vls, levels = vls))


varname = paste(unm, 1L, sep = "_")
legname = paste("legnr", 1L, sep = "_")
crtname = paste("crtnr", 1L, sep = "_")

l = update_l(o = o, l = aes$legend, v = "value", mfun = mfun, unm = unm, active = TRUE)
crt = update_crt(o = o, crt = aes$chart, v = "value", mfun = mfun, unm = unm, active = TRUE)

s = aes$scale
f = s$FUN
s$FUN = NULL
# update label.format
s$label.format = process_label_format(s$label.format, o$label.format)

if (all(is.ena(l$title))) l$title = ""

arglist = list(scale = s, legend = l, chart = crt,
o = o, aes = nm,
layer = layer,
layer_args = args,
sortRev = NA,
bypass_ord = TRUE,
submit_legend = TRUE)
dtl[, c(varname, legname, crtname) := do.call(f, c(unname(.SD), arglist)), .SDcols = "value"]


list(val = paste(dtl[[varname]], collapse = "__"),
legnr = dtl$legnr_1[1],
ctrnr = dtl$crtnr_1[1])
})
nvars = 1
nvarsi = 1
vars = temp$val
grp_bv = by123__[sort(b)]

nm = "skip"

sfun = paste0("tmapValuesScale_", nm)
cfun = paste0("tmapValuesColorize_", nm)
aes$data_vars = FALSE
}

} else if (!aes$data_vars && !aes$geo_vars) {
#
# List of 10
# $ scale :List of 1
# ..- attr(*, "class")= chr [1:3] "tm_scale_auto" "tm_scale" "list"
# $ legend :List of 65
# ..- attr(*, "class")= chr [1:2] "tm_legend_standard_portrait" "list"
# $ chart :List of 36
# ..- attr(*, "class")= chr [1:4] "tm_chart_none" "tm_chart" "tm_component" "list"
# $ o :List of 343
# $ aes : chr "fill"
# $ layer : chr "polygons"
# $ layer_args : list()
# $ sortRev : logi NA
# $ bypass_ord : logi FALSE
# $ submit_legend: logi TRUE
if (!aes$data_vars && !aes$geo_vars) {
#cat("step2_grp_lyr_aes_const", unm," \n")
# constant values (take first value (of possible tm_mv per facet)
if (any(nvari) > 1) warning("Aesthetic values considered as direct visual variables, which cannot be used with tm_mv", call. = FALSE)
Expand Down Expand Up @@ -177,8 +241,12 @@ getdts = function(aes, unm, p, q, o, dt, shpvars, layer, mfun, args, plot.order)
dtl[, legnr := vector("integer", length = nrow(dtl))]
dtl[, crtnr := vector("integer", length = nrow(dtl))]

if (exists("temp")) {
dtl_leg = data.table::data.table(sel = TRUE, legnr = temp$legnr, crtnr = temp$ctrnr)
} else {
dtl_leg = dtl[, .SD[1], by = c(grp_bv)][, tmapID__ := NULL][, legnr := (vapply(get(..unm), function(s) legend_save(list(mfun = mfun, unm = unm, active = FALSE, vneutral = s)), FUN.VALUE = integer(1)))][, crtnr := (vapply(get(..unm), function(s) chart_save(list()), FUN.VALUE = integer(1)))][, (unm) := NULL]
}

dtl_leg = dtl[, .SD[1], by = c(grp_bv)][, tmapID__ := NULL][, legnr := (vapply(get(..unm), function(s) legend_save(list(vneutral = s)), FUN.VALUE = integer(1)))][, crtnr := (vapply(get(..unm), function(s) chart_save(list()), FUN.VALUE = integer(1)))][, (unm) := NULL]
} else {
#cat("step2_grp_lyr_aes_var", nm," \n")

Expand Down Expand Up @@ -255,8 +323,8 @@ getdts = function(aes, unm, p, q, o, dt, shpvars, layer, mfun, args, plot.order)
if (length(v)) update_fl(k = v, lev = vars)

apply_scale = function(s, l, crt, v, varname, ordname, legname, crtname, sortRev, bypass_ord) {
l = update_l(o = o, l = l, v = v, mfun = mfun)
crt = update_crt(o = o, crt = crt, v = v, mfun = mfun)
l = update_l(o = o, l = l, v = v, mfun = mfun, unm = unm, active = TRUE)
crt = update_crt(o = o, crt = crt, v = v, mfun = mfun, unm = unm, active = TRUE)

if (length(s) == 0) stop("mapping not implemented for aesthetic ", unm, call. = FALSE)
f = s$FUN
Expand Down
2 changes: 1 addition & 1 deletion R/step4_helper_legends.R
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ step4_plot_collect_legends = function(tmx) {

if (!is.null(legs_aes)) {
for (k in 1:nrow(legs_aes)) {
if (length(legs_aes$legend[[k]]) == 1 || !legs_aes$legend[[k]]$show) {
if (!legs_aes$legend[[k]]$active || !legs_aes$legend[[k]]$show) {
legs_aes$legend[[k]] = list(NULL)
next
}
Expand Down
2 changes: 1 addition & 1 deletion R/tm_add_legend.R
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ toTitleCase = function(x) {

tmapAddedLegend = function(comp, o) {
#message("tm_mouse_coordinates ignored for 'plot' mode")
l = update_l(o = o, l = comp, v = "", mfun = toTitleCase(comp$type))
l = update_l(o = o, l = comp, v = "", mfun = toTitleCase(comp$type), unm = "", active = FALSE)

fun = paste0("tm_", comp$type)
if (!exists(fun)) {
Expand Down
33 changes: 33 additions & 0 deletions R/tm_scale_composition.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#' Scales: composition
#'
#' Scales in tmap are configured by the family of functions with prefix `tm_scale`.
#' Such function should be used for the input of the `.scale` arguments in the
#' layer functions (e.g. `fill.scale` in [tm_polygons()]).
#' The function `tm_scale_composition()` is used for the creation of composition glyphs, such as pie charts and donut charts.
#'
#' @param values (generic scale argument) The visual values. For colors (e.g. `fill` or `col` for `tm_polygons()`) this is a palette name from the `cols4all` package (see [cols4all::c4a()]) or vector of colors, for size (e.g. `size` for `tm_symbols()`) these are a set of sizes (if two values are specified they are interpret as range), for symbol shapes (e.g. `shape` for [tm_symbols()]) these are a set of symbols, etc. The tmap option `values.var` contains the default values per visual variable and in some cases also per data type.
#' @param values.repeat (generic scale argument) Should the values be repeated in case there are more categories?
#' @param values.range (generic scale argument) Range of the values. Vector of two numbers (both between 0 and 1) where the first determines the minimum and the second the maximum. Full range, which means that all values are used, is encoded as `c(0, 1)`. For instance, when a grey scale is used for color (from black to white), `c(0,1)` means that all colors are used, `0.25, 0.75` means that only colors from dark grey to light grey are used (more precisely `"grey25"` to `"grey75"`), and `0, 0.5` means that only colors are used from black to middle grey (`"grey50"`). When only one number is specified, this is interpreted as the second number (where the first is set to 0). Default values can be set via the tmap option `values.range`.
#' @param values.scale (generic scale argument) Scaling of the values. Only useful for size-related visual variables, such as `size` of [tm_symbols()] and `lwd` of [tm_lines()].
#' @param value.na (generic scale argument) Value used for missing values. See tmap option `"value.na"` for defaults per visual variable.
#' @param value.null (generic scale argument) Value used for NULL values. See tmap option `"value.null"` for defaults per visual variable. Null data values occur when out-of-scope features are shown (e.g. for a map of Europe showing a data variable per country, the null values are applied to countries outside Europe).
#' @param value.neutral (generic scale argument) Value that can be considered neutral. This is used for legends of other visual variables of the same map layer. E.g. when both `fill` and `size` are used for [tm_symbols()] (using filled circles), the size legend items are filled with the `value.neutral` color from the `fill.scale` scale, and fill legend items are bubbles of size `value.neutral` from the `size.scale` scale.
#' @param labels (generic scale argument) Labels
#' @param label.na (generic scale argument) Label for missing values
#' @param label.null (generic scale argument) Label for null (out-of-scope) values
#' @export
#' @name tm_scale_glyph_composition
#' @rdname tm_scale_glyph_composition
tm_scale_composition = function(
values = NA,
values.repeat = FALSE,
values.range = NA,
values.scale = 1,
value.na = NA,
value.null = NA,
value.neutral = NA,
labels = NULL,
label.na = NA,
label.null = NA) {
structure(c(list(FUN = "tmapScaleComposition"), as.list(environment())), class = c("tm_scale_composition", "tm_scale", "list"))
}
2 changes: 1 addition & 1 deletion R/tmapGridSymbols.R
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ tmapGridSymbols = function(shpTM, dt, gp, bbx, facet_row, facet_col, facet_page,

#gp = get_pch_1000p(gp)

gp = rescale_gp(gp, o$scale_down)
gp = rescale_gp(gp, o$scale_down * o$scale)

#gp = gp_to_gpar(gp, sel = "all")

Expand Down
4 changes: 3 additions & 1 deletion R/tmapLeaflet_layers.R
Original file line number Diff line number Diff line change
Expand Up @@ -251,8 +251,10 @@ tmapLeafletRaster = function(shpTM, dt, gp, pdt, popup.format, hdt, idt, bbx, fa

lf = get_lf(facet_row, facet_col, facet_page)

opts = leaflet::gridOptions(pane = pane)

lf %>%
leafem::addStarsImage(shp2, band = 1, colors = pal_col, opacity = pal_opacity, group = group) %>%
leafem::addStarsImage(shp2, band = 1, colors = pal_col, opacity = pal_opacity, group = group, options = opts) %>%
assign_lf(facet_row, facet_col, facet_page)
} else {
#shp2 = st_as_stars(list(values = tmapID), dimensions = shp)
Expand Down
91 changes: 91 additions & 0 deletions R/tmapScaleComposition.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
tmapScaleComposition = function(..., scale, legend, chart, o, aes, layer, layer_args, sortRev, bypass_ord, submit_legend = TRUE) {
args = list(...)

ct = NULL # what is it again?
cls = c("fact", "unord")

scale = get_scale_defaults(scale, o, aes, layer, cls, ct)

show.messages <- o$show.messages
show.warnings <- o$show.warnings

n = length(args)

with(scale, {
fun_getCVV = paste0("tmapValuesCVV_", aes)
VV = do.call(fun_getCVV, list(x = values, value.na = value.na, n = n, range = values.range, scale = values.scale, rep = values.repeat, o = o))

values = VV$vvalues
value.na = VV$value.na

sfun = paste0("tmapValuesScale_", aes)
cfun = paste0("tmapValuesColorize_", aes)
if (is.na(value.neutral)) value.neutral = VV$value.neutral else value.neutral = do.call(sfun, list(x = do.call(cfun, list(x = value.neutral, pc = o$pc)), scale = values.scale))

mfun = paste0("tmapValuesSubmit_", aes)
values = do.call(mfun, list(x = values, args = layer_args))
value.na = do.call(mfun, list(x = value.na, args = layer_args))
value.neutral = do.call(mfun, list(x = value.neutral, args = layer_args))

totals = Reduce("+", args)
mx = max(totals)
val_list = lapply(args, function(a) a / mx)

vals = do.call(encode_mv, c(val_list, list(digits = 4)))
labs = paste0("label", 1:n)

value.neutral = vals[1]

icon_scale = if (getOption("tmap.mode") == "plot") layer_args$icon.scale else 1

legend = within(legend, {
nitems = length(labs)
labels = labs
dvalues = values
vvalues = values
vneutral = value.neutral
icon_scale = icon_scale
na.show = FALSE
scale = "composition"
})


chartFun = paste0("tmapChart", toTitleCase(chart$summary))
chart = do.call(chartFun, list(chart))

if (submit_legend) {
if (bypass_ord) {
format_aes_results(vals, legend = legend, chart = chart)
} else {
format_aes_results(vals, ord = 1L, legend = legend, chart = chart)
}
} else {
list(vals = vals, ids = 1L, legend = legend, chart = chart, bypass_ord = bypass_ord)
}
})




}

encode_mv = function(..., digits = 4) {
args = list(...)

k = length(args)
m = seq(0, by = digits, length.out = k)

lst = mapply(function(v, mi) {
v * 10^(digits-1+mi)
}, args, m, SIMPLIFY = FALSE)
Reduce("+", lst)
}

decode_mv = function(x, digits = 4) {
m_temp = seq(0, by = digits, length.out = 20)
k = which(vapply(m_temp, function(mi) all(x < 10^mi), FUN.VALUE = logical(1)))[1] - 1L
m = seq(0, by = digits, length.out = k)
lst = lapply(m, function(mi) {
(floor(x / 10^mi) %% 10^digits) / 10^(digits-1L)
})
}
27 changes: 22 additions & 5 deletions R/tmapScale_.R
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ tmapVars = function(x) {
if (inherits(x, "tm_shape_vars")) return(structure(list(), class = "tmapShpVars"))
if (inherits(x, "tmapDimVars")) return(x)

cls = if (inherits(x, "AsIs")) "tmapAsIs" else "tmapVars"
cls = if (inherits(x, "AsIs")) "tmapAsIs" else if (inherits(x, "tmapUsrCls")) "tmapUsrCls" else "tmapVars"

isL = is.list(x)
if (!isL) {
Expand All @@ -35,6 +35,13 @@ tmapVars = function(x) {

structure(x, class = cls)
}

tmapUsrCls = function(x) {
structure(x, class = "tmapUsrCls")
}



format_aes_results = function(values, ord = NULL, legend, chart) {
legnr = vector(mode = "integer", length = length(values))
legnr[1] = legend_save(legend)
Expand Down Expand Up @@ -152,14 +159,22 @@ tmapScale = function(aes, value, scale, legend, chart, free) {
structure(list(aes = aes, value = tmapVars(value), scale = scale, legend = legend, chart = chart, free = free), class = c("tmapScale", "list"))
}

tmapScaleAuto = function(x1, scale, legend, chart, o, aes, layer, layer_args, sortRev, bypass_ord, submit_legend = TRUE, x2 = NULL) {
tmapScaleAuto = function(x1, scale, legend, chart, o, aes, layer, layer_args, sortRev, bypass_ord, submit_legend = TRUE, ...) {
args = list(...)
k = length(args) + 1L
if (length(args)) {
names(args) = paste0("x", 2L:(length(args)+1L))
}

cls = data_class(x1, check_for_color_class = aes %in% c("col", "fill"))

#if (cls[1] == "na")
sc_opt = getAesOption("scales.var", o, aes, layer, cls = cls)

if (!is.null(x2)) {
#if (aes == "fill") browser()
if (k == 2) {
sc = "bivariate"
} else if (k > 2) {
sc = "composition"
} else if (cls[1] == "asis") {
sc = "asis"
} else if (attr(cls, "unique") && !(sc_opt == "asis")) {
Expand Down Expand Up @@ -196,7 +211,9 @@ tmapScaleAuto = function(x1, scale, legend, chart, o, aes, layer, layer_args, so
scale_new$FUN = NULL

if (sc == "bivariate") {
do.call(FUN, list(x1 = x1, x2 = x2, scale = scale_new, legend = legend, chart = chart, o = o, aes = aes, layer = layer, layer_args = layer_args, sortRev, bypass_ord, submit_legend))
do.call(FUN, list(x1 = x1, x2 = args[[1]], scale = scale_new, legend = legend, chart = chart, o = o, aes = aes, layer = layer, layer_args = layer_args, sortRev = sortRev, bypass_ord = bypass_ord, submit_legend = submit_legend))
} else if (sc == "multi_continuous") {
do.call(FUN, c(list(x1 = x1), args, list(scale = scale_new, legend = legend, chart = chart, o = o, aes = aes, layer = layer, layer_args = layer_args, sortRev = sortRev, bypass_ord = bypass_ord, submit_legend = submit_legend)))
} else {
do.call(FUN, list(x1 = x1, scale = scale_new, legend = legend, chart = chart, o = o, aes = aes, layer = layer, layer_args = layer_args, sortRev, bypass_ord, submit_legend))
}
Expand Down
Loading

0 comments on commit 094dbe4

Please sign in to comment.