Closed mtennekes closed 6 months ago
I think the core of the symbols are there in leaflegend. There are internals but modify triangle in makeSymbol
as an example
'inverseTriangle' = htmltools::tags$polygon(
id = 'triangle',
points = sprintf('%s,%s %s,%s %s,%s',
strokewidth,
height + strokewidth,
width + strokewidth,
height + strokewidth,
width / 2 + strokewidth,
strokewidth),
stroke = color,
fill = fillColor,
'stroke-opacity' = opacity,
'fill-opacity' = fillOpacity,
transform = sprintf('translate(%f, %f) rotate(45 0 0 )', height/2, width/2),
...
)
It's an svg so you can rotate it around the center point.
For pch, maybe a function generator to pick by both name an position
pch_symbol <- function(i, width, height, offset = 0) {
c('rect' = drawRect(width, height, offset = 0)
'circle' = drawCircle(width, height, offset = 0))[i](width, height, offset = 0)
}
I'm happy to add this into leaflegend if it helps. I'm not sure on the naming conventions for each symbol.
I like the symbols to have userfriendly names, like leaflegend: circle is much easer to understand than 21 (you don't wanna know how often I google 'R pch' :-).
Fully agree. Most important thing for users is ease-of-use and compatibility between plot and view modes for the most commonly needed shapes, and all of pch options are not needed IMO. Leaflegend looks amazing!
For pch, maybe a function generator to pick by both name an position
pch_symbol <- function(i, width, height, offset = 0) { c('rect' = drawRect(width, height, offset = 0) 'circle' = drawCircle(width, height, offset = 0))[i](width, height, offset = 0) }
I'm happy to add this into leaflegend if it helps. I'm not sure on the naming conventions for each symbol.
That would be awesome, @tomroh! This is especially great for backwards compatibility: probably each of the pch symbols is used in the wild.
My proposal (feel free to improve):
0 open-rect 1 open-circle 2 open-triangle 3 simple-plus 4 simple-cross 5 open-diamond 6 open-down-triangle 7 cross-rect 8 simple-star 9 plus-diamond 10 plus-circle 11 hexagram 12 plus-rect 13 cross-circle 14 triangle-rect 15 solid-rect 16 solid-circle 17 solid-triangle 18 solid-diamond 19 solid-circle 20 solid-dot 21 circle 22 rect 23 diamond 24 triangle 25 down-triangle
The normal (filled) symbols will the defaults in tmap. Probably I can mimic your plus, cross, and star in plot mode with layering two pch 3/4/8 symbols, one with a large lwd in black ("col"
) and on top a small lwd symbol in blue ("fill"
).
I still need to tweak some of the symbol aesthetics but the implementation will work like this:
pchNames <- stats::setNames(seq(0L, 25L, 1L),
c('open-rect', 'open-circle', 'open-triangle', 'simple-plus',
'simple-cross', 'open-diamond', 'open-down-triangle', 'cross-rect',
'simple-star', 'plus-diamond', 'plus-circle', 'hexagram', 'plus-rect',
'cross-circle', 'triangle-rect', 'solid-rect', 'solid-circle-md',
'solid-triangle', 'solid-diamond', 'solid-circle-bg', 'solid-circle-sm', 'circle',
'rect', 'diamond', 'triangle', 'down-triangle'
))
defaultSize <- 20
i <- 1:26
pchSvg <- lapply(names(pchNames)[i], makePch, width = defaultSize,
color = 'black', `stroke-width` = 2, fillOpacity = .5)
pchSvgI <- lapply(i-1, makePch, width = defaultSize,
color = 'black', `stroke-width` = 2, fillOpacity = .5)
leaflet::leaflet(options = leaflet::leafletOptions(zoomControl = FALSE)) |>
addLegendImage(images = pchSvg, labels =names(pchNames),
width = defaultSize, height = defaultSize, position = 'topright') |>
addLegendImage(images = pchSvgI, labels = i-1,
width = defaultSize, height = defaultSize, position = 'topleft')
tomroh/leaflegend@a75fd0f700991fa39f79e3614ea9e87b8680a250
It's in the development version on github now.
Wow, amazing! Thanks!
I've tried
pchSvg <- lapply(names(pchNames)[i], makePch, width = defaultSize,
color = 'black', `stroke-width` = 2, fillColor = "blue", fillOpacity = .5)
pchSvgI <- lapply(i-1, makePch, width = defaultSize,
color = 'black', `stroke-width` = 2, fillColor = "blue", fillOpacity = .5)
leaflet::leaflet(options = leaflet::leafletOptions(zoomControl = FALSE)) |>
addLegendImage(images = pchSvg, labels =names(pchNames),
width = defaultSize, height = defaultSize, position = 'topright') |>
addLegendImage(images = pchSvgI, labels = i-1,
width = defaultSize, height = defaultSize, position = 'topleft')
Almost what I expected. The only thing is that 21:25 don't have a black border color anymore. Is that correct, or do I miss something.
What browser? Although I don't think it should matter. Do you have the output of:
cat(URLdecode(pchSvg[[26]]))
I copied that same code you put up there and:
My mistake: I was sleeping :-)) My screenshot only showed 1-18, the last ones didn't fit my laptop screen. I've checked again and it looks great! Thanks again!
I'll leave this issue open until I've implemented this in tmap (as reminder).
Hi @tomroh I'm about to implement the new symbols. However, I get this:
leaflegend::makeSymbolIcons(shape = "open-rect", width = 20, height = 20, color = "#000000", opacity = 1)
#> Error in (function (shape, width, height = width, color, fillColor = color, : Invalid shape argument.
Created on 2023-04-25 with reprex v2.0.2
I have forgotten why, but currently tmap uses makeSymbolIcons
for the map and makeSymbols
for the legend.
Do you have any clue?
pch symbols are in a different function. Use makePch
for 'open-rect' and the like. makeSymbolIcons
is used for the map because the svg data uris (images) need to be wrapped in leaflet::icon
. I added makePchIcons
so that you have the same functionality.
> leaflegend::makePchIcons
function (shape, color, fillColor = color, opacity, fillOpacity = opacity,
strokeWidth = 1, width, height = width, ...)
{
symbols <- Map(makeSymbol, shape = shape, width = width,
height = height, color = color, fillColor = fillColor,
opacity = opacity, fillOpacity = fillOpacity, `stroke-width` = strokeWidth,
...)
leaflet::icons(iconUrl = unname(symbols), iconAnchorX = width/2,
iconAnchorY = height/2)
}
<bytecode: 0x10e0e5548>
<environment: namespace:leaflegend>
Shouldn't it be Map(makePch, ... )
instead of Map(makeSymbol, ...)
@tomroh ?
I rushed that... I refactored the code. You can now use makeSymbol
and makeSymbolIcons
. Usage:
pchNames <- stats::setNames(seq(0L, 25L, 1L),
c('open-rect', 'open-circle', 'open-triangle', 'simple-plus',
'simple-cross', 'open-diamond', 'open-down-triangle', 'cross-rect',
'simple-star', 'plus-diamond', 'plus-circle', 'hexagram', 'plus-rect',
'cross-circle', 'triangle-rect', 'solid-rect', 'solid-circle-md',
'solid-triangle', 'solid-diamond', 'solid-circle-bg', 'solid-circle-sm', 'circle',
'rect', 'diamond', 'triangle', 'down-triangle'
))
i <- 1:26
pchSvg <- lapply(names(pchNames)[i], makeSymbol, width = defaultSize,
color = 'black', `stroke-width` = 2, fillOpacity = .5)
leaflet::leaflet(options = leaflet::leafletOptions(zoomControl = FALSE)) |>
addSymbols(lng = i, lat = i, shape = availableShapes()[['pch']],
color = 'black', values = i, `strokeWidth` = 2, fillOpacity = .5) |>
addLegendImage(images = pchSvg, labels =names(pchNames),
width = defaultSize, height = defaultSize, position = 'topright')
Hopefully that makes it easier to work with. There is a name conflict with original symbols and pch names but they return the same svg specs for the symbol.
leaflegend version 1.1 is on its way to CRAN. In addition to the pch symbols, you can show NA encodings in the legend when NAs are present in the values, and you can also change the labels for addLegendNumeric
.
Excellent @tomroh thanks! Very handy that you standardised makeSymbol(Icons)
I just noticed one discrepancy with the base implementation of pch symbols: In base R the color of symbols 15 to (and including) 20 come from color
rather than fill
:
Conceptually, both interpretations are explainable. Using fill
for symbols 15-20 is more intuitive for most users I think, but on the other hand, there may be users who are familiar with using color
.
What do you think? Also glad to hear more opinions (eg @Nowosad @Robinlovelace @xiaofanliang @tim-salabim )
I think fill
may be slightly more intuitive. There are few people in the world of ggplot2 who use base R as a daily driver for graphics so I think whatever is most intuitive, without worrying about compatibility issues, is a reasonable approach here.
Personally, I like fill
better as it's more intuitive. Though, I tend to stick to the upstream leaflet terminology where it would be fillColor
I guess
I think fillColor
is more consistent with the use of leaflegend
and more intuitive (granted I'm not a base R plot user in general). You don't have to context switch and treat 6 symbols differently than the others. The only issue I could see is if someone is developing something that can switch between base R static and leaflet interactive graphics. Then it would be a lot smoother to have the color specs consistent. Although, fillColor
!= fill
and col
!= color
anyway, except with partial matching of arguments.
Thanks for your input!
tmap4 has a few upstream paths, e.g. leaflet for view mode and grid for plot mode (in the future perhaps also rayshader). So therefore the aim is to have a consistent set of visual variable and values, which is often a trade-off. At the start of tmap4 (already 2 years ago 🙈), we had a short discussion about the variable names: https://github.com/r-tmap/tmap/issues/579
The only issue I could see is if someone is developing something that can switch between base R static and leaflet interactive graphics.
Yes, that is exactly one of the main aims of tmap.
I will settle with the most intuitive, and that is using fill
. It could mean that backwards compatibility is not guaranteed, but perhaps I can catch those cases.
I could make it so you could specify fillColor
or the color
argument for pch 15-20 if that would help?
solid pch symbols now use color if fillColor is missing in >=v1.1.1
I've added this example to tm_symbols
:
# create grid of 25 points in the Athlantic
library(sf)
x = st_as_sf(cbind(expand.grid(x = -51:-47, y = 20:24), id = seq_len(25)),
coords = c("x", "y"), crs = 4326)
tm_shape(x, bbox = bb(x, ext = 1.2)) +
tm_symbols(shape = "id",
size = 2,
lwd = 2,
fill = "orange",
col = "black",
shape.scale = tm_scale_asis()) +
tm_text("id", ymod = -2)
Plot mode:
View mode:
Thx @tomroh for making this possible!
Symbols in view mode are working (see below) thanks to @tomroh , but we need to standardize the symbols between plotting modes:
Currently, tmap uses the
pch
numbering:Note that black corresponds to the visual variable
col
and blue tofill
. In order to makefill
usable, tmap (by default) uses symbols 21:25.leaflegend
which I use for leaflet legends, natively supports these symbols:What are your thoughts and ideas (@Nowosad @Robinlovelace @tim-salabim)?