rggplot2geom-text

ggplot2 geom_textpath spiral with datetime axis


I'm trying to replicate the spiral example for geom_textpath() shown at the top here: https://allancameron.github.io/geomtextpath/

My y-axis is numeric, so that part works out fine, but my x-axis is a scale_x_datetime(), and I can't multiply datetimes by sin() values for the x variable.

Any ideas?

# install.packages("remotes")
remotes::install_github("AllanCameron/geomtextpath")

library(ggplot2)
library(geomtextpath)

t <- seq(5, -1, length.out = 1000) * pi

spiral <- data.frame(x    = sin(t) * 1:1000, 
                     y    = cos(t) * 1:1000,
                     text = paste("Like a circle in a spiral,",
                                  "like a wheel within a wheel,",
                                  "never ending or beginning,",
                                  "on an ever spinning reel")
                     )

ggplot(spiral, aes(x, y, label = text)) +
  geom_textpath(size = 7, vjust = 2, text_only = TRUE) +
  coord_equal(xlim = c(-1500, 1500), ylim = c(-1500, 1500))

Solution

  • Internally, POSIXct is just numeric with a class:

    
    dput(Sys.time())
    # structure(1745060382.54419, class = c("POSIXct", "POSIXt"))
    

    So while sin(Sys.time()) isn't going to work, we can rescale the numeric x axis to the appropriate range of numeric times, and then convert the class to POSIXt.

    We can use scales::rescale but frankly the arithmetic is simple enough it can be done manually if desired. Since rescale doesn't know about POSIXt values, we can convert to numeric, rescale, and then re-class back.

    otherdat <- data.frame(y = seq(-1000, 1000, length.out = 11)) |>
      transform(tm = Sys.time() + y)
    spiral <- spiral |>
      transform(tm = scales::rescale(x, from = range(x), to = as.numeric(range(otherdat$tm)))) |>
      transform(tm = `class<-`(tm, class(otherdat$tm)))
    ggplot(otherdat, aes(tm, y)) +
      geom_point() +
      geom_textpath(
        data = spiral, aes(tm, y, label = text),
        size = 7, vjust = 2, text_only = TRUE
      ) +
      coord_equal(xlim = range(otherdat$tm) + c(-500, 500),
                  ylim = c(-1500, 1500))
    

    ggplot with spiral text overlaid on dots plotted on a datetime x-axis

    Note that the xlim= provides a time range instead of a numeric range. In this case, we're managing the POSIXt values (seconds) to have the same range as the non-time y-axis, over to you for your real use-case.