Comments (12)
I think I'll just write a grob that can tile an arbitrary rectangular area with an image. This will also address the longstanding request of being able to draw bars with textured fill. https://stackoverflow.com/questions/2895319/how-to-add-texture-to-fill-colors-in-ggplot2
from dataviz.
This would also be the right place to discuss the various research literature mentioned in issue #49.
from dataviz.
Some of the Otto Neurath graphs may make for useful examples. E.g. this one
I've never found a reasonably simple way to implement them with ggplot, but you might have a better understanding of R's graphics.
from dataviz.
I've been thinking along those lines today, e.g. visualizing the msleep
dataset using the animal silhouettes available here: http://phylopic.org/
Just not sure how much effort I want to put towards writing a geom that can do this. It's likely at least a week of work.
from dataviz.
To implement this, we'd have to take geom_col()
and replace the drawing subroutine with a function that covers the area of each bar with tiles of images. I see no technical hurdle to doing this. Just requires a bit of calculation to get this right without distorting the aspect ratio of the images and to make it responsive if the image is resized.
from dataviz.
I wonder if you could modify geom_dotplot
to allow setting the shape by category. Here's a dumb implementation with geom_point
:
data = tibble(value = c(1:5, 1:3), category = c(rep('a', 5), rep('b', 3)))
ggplot(data) +
aes(x=value, y=category, color=category, fill=category, shape=category) +
geom_point(size=40) +
scale_shape_manual(values=c("\u26C4", "\u26C5")) +
expand_limits(x=c(0.5, 5.5)) +
theme_classic()
Unfortunately, this approach is limited to symbols in fonts, which even with packages like font awesome is not very extensible.
from dataviz.
Something like this is going to do the job. The rest is just filling in the blanks and adding a nice API.
library(grid)
library(magick)
#> Linking to ImageMagick 6.9.9.39
#> Enabled features: cairo, fontconfig, freetype, lcms, pango, rsvg, webp
#> Disabled features: fftw, ghostscript, x11
get_asp <- function(img) {
info <- image_info(img)
info$height / info$width
}
texture_grob <- function(x = unit(0.5, "npc"), y = unit(0.5, "npc"),
width = unit(1, "npc"), height = unit(1, "npc"),
img = NULL) {
vp <- viewport(x, y, width, height, just = c(0, 0), clip = "on")
g <- gTree(img = img, cl = "texture_grob", vp = vp)
}
makeContent.texture_grob <- function(x) {
grob_width <- convertWidth(unit(1, "npc"), "in", valueOnly = TRUE)
grob_height <- convertHeight(unit(1, "npc"), "in", valueOnly = TRUE)
grob_asp <- grob_height / grob_width
asp <- get_asp(x$img)
n <- ceiling(grob_asp/asp) # number of image copies we need
bg <- rectGrob(gp = gpar(fill = "#E8E8E8"))
children <- lapply(
1:n,
function(i) {
vp = viewport(
x = unit(0, "in"), y = unit(i*asp*grob_width, "in"),
width = unit(grob_width, "in"), height = unit(asp*grob_width, "in"),
just = c(0, 1)
)
rasterGrob(x$img, vp = vp)
}
)
# add background to children
children <- c(list(bg), children)
# convert to gList and set
children <- do.call(gList, children)
setChildren(x, children)
}
img <- magick::image_read("https://jeroen.github.io/images/Rlogo.png")
grid.newpage()
tg1 <- texture_grob(unit(.2, "npc"), unit(.05, "npc"), unit(.1, "npc"), unit(.9, "npc"), img)
tg2 <- texture_grob(unit(.5, "npc"), unit(.05, "npc"), unit(.3, "npc"), unit(.6, "npc"), img)
grid.draw(tg1)
grid.draw(tg2)
Created on 2018-09-23 by the reprex package (v0.2.0).
from dataviz.
Nice! But because it's in npc units, changing the size or aspect ratio of the plot also changes the number of repetitions of the tiled image changes.
Running the same code with a different image size:
Maybe render each unit as a rectangle with the image's aspect ratio. Use the same texture approach only to allow for partial images. Separating each unit has the added bonus of allowing you to adjust spacing.
I modified your example to account for the image aspect ratio, but it still doesn't compensate for the plot's aspect ratio.
library(grid)
library(magick)
#> Linking to ImageMagick 6.9.9.39
#> Enabled features: cairo, fontconfig, freetype, lcms, pango, rsvg, webp
#> Disabled features: fftw, ghostscript, x11
get_asp <- function(img) {
info <- image_info(img)
info$height / info$width
}
texture_grob <- function(x = unit(0.5, "npc"), y = unit(0.5, "npc"),
width = unit(1, "npc"), height = unit(1, "npc"),
img = NULL) {
vp <- viewport(x, y, width, height, just = c(0, 0), clip = "on")
g <- gTree(img = img, cl = "texture_grob", vp = vp)
}
makeContent.texture_grob <- function(x) {
grob_width <- convertWidth(unit(1, "npc"), "in", valueOnly = TRUE)
grob_height <- convertHeight(unit(1, "npc"), "in", valueOnly = TRUE)
grob_asp <- grob_height / grob_width
asp <- get_asp(x$img)
# background square is useful for debugging
bg <- rectGrob(gp = gpar(fill = "#E8E8E8", lty = "blank"))
# draw one instance of the image
vp <- viewport(
x = unit(0, "in"), y = unit(asp*grob_width, "in"),
width = unit(grob_width, "in"), height = unit(asp*grob_width, "in"),
just = c(0, 1)
)
children <- list(rasterGrob(x$img, vp = vp))
# add background to children
children <- c(list(bg), children)
# convert to gList and set
children <- do.call(gList, children)
setChildren(x, children)
}
draw_vertical_stack_of_images <- function (
x = unit(0.5, "npc"),
y = unit(0.5, "npc"),
width = unit(.1, "npc"),
img = NULL,
unit_count = 1,
max_unit_count = 10,
spacing = 0.1) {
aspect_ratio <- get_asp(img)
width <- 1 / aspect_ratio / max_unit_count
height <- 1 / max_unit_count
for (i in 1:ceiling(unit_count)) {
unit_y <- y + (i - 1) * height
unit_height <- height * (1 - spacing) * min(1, unit_count - i + 1)
tg <- texture_grob(
unit(x, "npc"),
unit(unit_y, "npc"),
unit(width, "npc"),
unit(unit_height, "npc"),
img)
grid.draw(tg)
}
}
img1 <- magick::image_read("http://steveharoz.com/research/isotype/icons/giraffe.svg")
img2 <- magick::image_read("http://steveharoz.com/research/isotype/icons/elephant.svg")
grid.newpage()
draw_vertical_stack_of_images(x=.2, y=.05, width=.1, img=img1, unit_count=5.5, max_unit_count=10)
draw_vertical_stack_of_images(x=.5, y=.05, width=.1, img=img2, unit_count=2.2, max_unit_count=10)
from dataviz.
I think dependency on the plot aspect ratio is unavoidable. Similar issues arise with other packages, e.g. ggrepel. The label location changes every time you change the plot size.
For the problem of different tiling images with different aspect ratios, my perspective is that the underlying drawing infrastructure should do general-purpose tiling. Then, any desired effect can be obtained with that infrastructure by providing appropriately formatted and/or padded images. The padding could even be done in R with the magick package.
from dataviz.
Do you know how to get the plot aspect ratio? Is there a get_panel_size_in_pixels()
function in grid? If so, you could compensate for the image aspect ratio. The reason why I bring it up is that in most unit charts, a unit has a specific scale, like 1 elephant icon = 10,000 elephants. If the number of items changed with aspect ratio, it could become misleading.
from dataviz.
I think the right way to solve this problem is to make it possible to use the tiling images at a specified height or width and then ideally tie that to the scale in ggplot, so that e.g. one image height corresponds to x units on the scale regardless of the plot size or aspect ratio. I think that's possible, but I won't be able to look into this further until later this fall.
from dataviz.
I've got it figured out: https://github.com/clauswilke/ggtextures
(Legends aren't implemented yet.)
library(ggplot2)
library(tibble)
library(ggtextures)
data <- tibble(
count = c(5, 3, 6),
animal = c("giraffe", "elephant", "horse"),
image = list(
"http://steveharoz.com/research/isotype/icons/giraffe.svg",
"http://steveharoz.com/research/isotype/icons/elephant.svg",
"http://steveharoz.com/research/isotype/icons/horse.svg"
)
)
ggplot(data, aes(animal, count, image = image)) +
geom_isotype_col() +
theme_minimal()
ggplot(data, aes(animal, count, image = image)) +
geom_isotype_col(
img_width = grid::unit(1, "native"), img_height = NULL,
ncol = NA, nrow = 1,
hjust = 0, vjust = 0.5
) +
coord_flip() +
theme_minimal()
Created on 2018-09-27 by the reprex package (v0.2.0).
from dataviz.
Related Issues (20)
- Clarification on "No Derivatives" HOT 1
- Straggling warning HOT 4
- A small typo for misspelling "inadvertently" HOT 1
- Accidental warning message
- Small typo: "As longs as the lighter colors fall..."
- Include estimation plots?
- Visualización
- emmeans() and mutate() in "visualizing_uncertainty.Rmd" (lines 398 ff.)
- Error in grid.Call[...]: polygon edge not found
- Figure 6.7 is "bad"
- Naming of "Sina plots" might be contentious HOT 1
- Small typo in Chapter 4 - "stiched" HOT 2
- Inconsistent founding year for bioRxiv in Fig. 13.1 (dataviz/time_series.Rmd, line 19)
- Waffle charts
- typo
- a bit unclear?
- Math Processing Error
- Statistical significance language HOT 4
- Recommendations for ternary graphics HOT 1
- Color for skewed data HOT 5
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from dataviz.