rggplot2labelcontourmetr

Labeling contour lines in R using metR with gratia or ggplot2


Some ways to add labels on contour plots

# load packages
library('mgcv')
library('gratia') # draw(); smooth_estimates()
library('metR') # geom_contour2(); geom_text_contour()
library('ggplot2')

Simulate data using the example from Gavin Simpson's website: https://fromthebottomoftheheap.net/2018/10/23/introducing-gratia/

set.seed(1)
dat <- gamSim(2, n = 4000, dist = "normal", scale = 1, verbose = FALSE)
mod <- gam(y ~ s(x, z, k = 30), data = dat$data, method = "REML")
sm <- smooth_estimates(mod); sm

Plot using gratia with the number of contour lines automatically adjusted:

draw(mod) +
  geom_text_contour( 
    aes(z = est), # 'est' from smooth_estimates(mod)
    colour = "black", size = 4.5, fontface = "bold",
    stroke = 0.3, stroke.colour = "white", # 'stroke' controls the width of stroke relative to the size of the text 
    skip = 0, # number of contours to skip
    rotate = FALSE, # horizontal labeling; if TRUE, rotate text following the contour
    label.placer = label_placer_fraction(frac = 0.5)) # 'frac = 0.5' places the label at equal distance from extremities. Try 'label.placer = label_placer_n(2)' to display two labels per contour line

enter image description here

However, contour lines and labeling do no longer match if we use e.g. 'n_contour = 10' within draw(). To allow this matching, use 'n_contour = 0' within draw(), define 'binwidth' within geom_contour2() and 'breaks' within geom_text_contour(), as follows.

Plot using gratia::draw with 'binwidth'-adjusted contour lines:

min(sm$est); max(sm$est) # find min() and max() for adjusting the 'est' z-scale
draw(mod, n_contour = 0) +
  geom_contour2(aes(z = est), binwidth = 0.2) +
  geom_text_contour(
    aes(z = est), # 'est' from smooth_estimates(mod)
    breaks = seq(-0.4, 0.4, by = 0.2), # 'breaks' must match with 'binwidth' above
    colour = "black", size = 4.5, fontface = "bold",
    stroke = 0.3, stroke.colour = "white", # 'stroke' controls the width of stroke relative to the size of the text
    skip = 0, # number of contours to skip
    rotate = FALSE, # horizontal labelling; if TRUE, rotate text following the contour
    label.placer = label_placer_fraction(frac = 0.5)) # 'frac = 0.5' places the label at equal distance from contour lines' extremities. Try 'label.placer = label_placer_n(2)' to display two labels per contour line

enter image description here

Also possible to customize the graph directly with ggplot2:

ggplot(data = sm, aes(x = x, y = z, z = est)) +
  geom_contour2(aes(z = est), binwidth = 0.1) +
  geom_text_contour(
    aes(z = est), # 'est' from smooth_estimates(mod)
    breaks = seq(-0.4, 0.4, by = 0.1), # 'breaks' instead of 'bins' to not have too many decimals
    colour = "black", size = 4.5, fontface = "bold",
    stroke = 0.3, stroke.colour = "white", # 'stroke' controls the width of stroke relative to the size of the text
    skip = 0, # number of contours to skip
    rotate = FALSE, # horizontal labelling; if TRUE, rotate text following the contour
    label.placer = label_placer_fraction(frac = 0.5)) # 'frac = 0.5' places the label at equal distance from contour lines' extremities. Try 'label.placer = label_placer_n(2)' to display two labels per contour line

enter image description here


Solution

  • You can use geom_textcontour from geomtextpath to obtain nicely placed labels without having to tweak lots of different parameters:

    library(geomtextpath)
    
    ggplot(sm, aes(x, z, z = est)) + geom_textcontour()
    

    enter image description here

    To use it within the gratia::draw framework, you can remove the existing contour from the plot first:

    p <- draw(mod)
    p$layers[[2]] <- NULL
    p + geom_textcontour(aes(z = est), fontface = 'bold')
    

    enter image description here


    EDIT

    To get a similar effect to the stroke parameter we can do:

    library(ggfx)
    
    p + with_outer_glow(geom_textcontour(aes(z = est), fontface = 'bold', 
                                         linetype = NA),
                        colour = 'white', expand = 3, sigma = 1) +
      geom_textcontour(aes(z = est), fontface = 'bold', textcolour = NA)
    

    enter image description here