Reputation: 2623
I am trying to replicate this line chart from highcharts. The annotations are wrapped in speech/text bubbles. I have tried to do this in ggplot with geom_label. To get the points, I've added geom_point with a filled triangle shape up or down. Whilst this works for the dark and opaque fills and colours, it does not work for white semi transparent fill with black colour outline, as you can clearly see the triangle's base.
Does anybody know a way around this or a better approach?
Code:
library(ggplot)
ggplot(france, aes(Distance, Elevation))+
geom_area(fill = "#90ed7d", color = "#434348",
size = 0.7, alpha = 0.5)+ # area chart
# location labels
geom_label(aes(x, y-200, label = text), data = plot_labels[-3,],
label.padding = unit(.4, "lines"), label.size = unit(.4, "mm"),
label.r = unit(.05, "lines"), alpha = 0.5,
vjust = 0.5, size = 3, family = "Lucida Grande")+
# location labels points
geom_point(aes(x, y-105, fill = "white"), data = plot_labels[-3,],
shape = 24, fill = "#FFFFFF80", size = 3, color = "black")+
# elevation labels
geom_label(aes(x, y+200, label = text), data = text_labels[-3,],
label.padding = unit(.4, "lines"), label.size = unit(.4, "mm"),
label.r = unit(.05, "lines"), color = "#404040",
fill = "#404040",
vjust = 0.5, size = 3, family = "Lucida Grande")+
# text in white color for elevation labels
geom_text(aes(x, y+200, label = text), data = text_labels,
vjust = 0.5, size = 3, family = "Lucida Grande",
colour = "#ffffff")+
# elevation labels points
geom_point(aes(x, y+105), data = text_labels[-3,],
shape = 25, fill = "#404040", size = 3, color = "#404040")+
scale_y_continuous(labels = function (x) paste(x, "m"),
limits = c(0, 1600),
expand = c(0,0),
breaks = seq(0, 1500, 500))+
scale_x_continuous(labels = function(x) paste(x, "km"),
expand = c(0,1.5), breaks = seq(0, 200, 25),
limits = c(0, max(france$Distance)))+
ggtitle("2017 Tour de France Stage 8: Dole - Station des Rousses")+
theme_minimal()+
theme(panel.grid.minor = element_blank(),
panel.grid.major.x = element_blank(),
axis.ticks.x = element_line("#d6d7e6"),
axis.ticks.length.x = unit(.25, "cm"),
text = element_text("Lucida Grande", size = 11))
Data:
france <- read.csv(file = "https://raw.githubusercontent.com/mrRlover/data/main/Stackoverflow/tour_de_france_stage_8.csv")
plot_labels <- read.csv("https://raw.githubusercontent.com/mrRlover/data/main/Stackoverflow/labels.csv")
text_labels <- read.csv("https://raw.githubusercontent.com/mrRlover/data/main/Stackoverflow/text_labels.csv", encoding = "UTF-8", sep = ",")
colnames(france) <- c("Distance", "Elevation")
colnames(plot_labels) <- gsub("point__", "", colnames(plot_labels))
colnames(text_labels) <- gsub("point__", "", colnames(text_labels))
text_labels$text <- gsub("<br>", "\n", text_labels$text)
Current output:
Upvotes: 3
Views: 798
Reputation: 66870
Here's a semi-manual approach that takes a specifications
table with your label info and converts each coordinate into a series of 8 points, defining the outline of the bubble. Then those can be fed into geom_poly
to draw the bubbles.
Fake data:
set.seed(42)
fake_data <- data.frame(x = 1:100,
y = cumsum(rnorm(100, 0.1)))
Define labels:
library(tidyverse)
tri_width = 3
tri_height = 1
specifications <- data.frame(
id = ids,
x = c(12, 47, 94),
y = c(11, 1, 17),
y_dir = c(1, -1, 1),
label = c("label 1", "label 2 is very wide", "label 3\nhas a\nfew lines"),
width = c(15, 40, 19),
height = c(3, 3, 7)
)
Convert each label row to 8 rows, defining the outline of each bubble:
bubbles <- specifications %>%
uncount(8, .id = "pos") %>% # bubble will have 7 coords, last one repeated
mutate(x = x + recode(pos,
0,
tri_width/2,
width/2,
width/2,
-width/2,
-width/2,
-tri_width/2,
0),
y = y + y_dir * recode(pos,
0,
tri_height,
tri_height,
tri_height + height,
tri_height + height,
tri_height,
tri_height,
0)
)
Plot:
ggplot(fake_data, aes(x, y)) +
geom_line() +
geom_polygon(data = bubbles,
aes(x, y, group = id),
fill = "white", color = "gray70", alpha = 0.85) +
geom_text(data = specifications,
aes(label = label, y = y + y_dir*(tri_height + height/2)))
Upvotes: 5