grattan / grattan/grattantheme

Add in geoms for labelled first and final points

Open
#244 2 comments 0 reactions 1 assignee View on GitHub

@ashleighchangg is already working on this.

Since Dec 11, 2025.

Dominant language
R
Stars
24
Forks
6
Avg merge
30m
Merged PRs (30d)
1

Description

These are adaptations of geom_finallabel used by Liz in previous reports.

Initial version:

### geom_firstlabel()

#' Line with a dot on the first observation
#' @description geom_firstlabel() draws a ggplot2::geom_text()
#' at the observation with the maximum x value for each group.
#' @inheritParams ggplot2::geom_text
#' @param nudge_x_perc Amount to nudge label along the x-axis, expressed as a
#' percentage of the data range along the x-axis. The default is 1.5, which
#' will nudge the text 1.5% of the data range from the first point. This is applied
#' in addition to nudge_x, which nudges the text a specific number of data units.
#' @section Aesthetics:
#'   \code{geom_text()} understands the following aesthetics (required aesthetics are in bold):
#'   \itemize{
#'     \item \strong{\code{x}}
#'     \item \strong{\code{y}}
#'     \item \strong{\code{label}}
#'     \item \code{alpha}
#'     \item \code{angle}
#'     \item \code{colour}
#'     \item \code{family}
#'     \item \code{fontface}
#'     \item \code{group}
#'     \item \code{hjust}
#'     \item \code{lineheight}
#'     \item \code{size}
#'     \item \code{vjust}
#'   }
#'
#' Learn more about setting these aesthetics in \code{vignette("ggplot2-specs")}.
#' @seealso ggplot2::geom_text
#' @export
#' @rdname geom_firstlabel
#' @examples
#' library(ggplot2)
#'
#'ggplot(ggplot2::economics_long, aes(x = date, y = value)) +
#'   geom_linepoint(aes(col = variable)) +
#'   geom_firstlabel(aes(label = value)) +
#'   facet_wrap(~variable)
geom_firstlabel <-
  function(mapping = NULL,
           data = NULL,
           stat = "identity",
           position = "identity",
           nudge_x = 0,
           nudge_y = 0,
           nudge_x_perc = 1.5,
           na.rm = FALSE,
           show.legend = FALSE,
           inherit.aes = TRUE,
           ...) {
    if (!missing(nudge_x) || !missing(nudge_y)) {
      if (!missing(position)) {
        stop("You must specify either position or nudge_x/nudge_y.")
      }

      position <- position_nudge(nudge_x, nudge_y)
    }

    ggplot2::layer(
      data = data,
      mapping = mapping,
      stat = stat,
      geom = GeomfirstLabel,
      position = position,
      show.legend = show.legend,
      inherit.aes = inherit.aes,
      params = list(
        na.rm = na.rm,
        nudge_x_perc = nudge_x_perc,
        ...
      )
    )
  }

#' @export
#' @rdname geom_firstlabel
GeomfirstLabel <- ggplot2::ggproto(
  "GeomfirstLabel",
  ggplot2::Geom,
  extra_params = c("na.rm", "nudge_x_perc"),
  setup_data = function(data, params) {
    ggplot2::GeomText$setup_data(data, params)
  },
  draw_group = function(data,
                        panel_params,
                        coord,
                        nudge_x_perc,
                        flipped_aes = FALSE) {
    x_range <- range(data$x)
    x_min <- x_range[1]
    x_max <- x_range[2]
    data <- data[data$x == x_min, ]
    data$x <- data$x - ((x_max - x_min) * (nudge_x_perc / 100))


    ggplot2::GeomText$draw_panel(
      data,
      panel_params,
      coord
    )
  },
  draw_key = ggplot2::draw_key_text,
  required_aes = c("x", "y", "label"),
  default_aes = ggplot2::aes(
    colour = ggplot2::GeomText$default_aes$colour,
    size = ggplot2::GeomText$default_aes$size,
    angle = ggplot2::GeomText$default_aes$angle,
    hjust = 1,
    vjust = ggplot2::GeomText$default_aes$vjust,
    alpha = ggplot2::GeomText$default_aes$alpha,
    family = ggplot2::GeomText$default_aes$family,
    fontface = ggplot2::GeomText$default_aes$fontface,
    lineheight = 0.9
  )
)


#' Line with a dot on the final observation
#' @description geom_linepoint() draws a ggplot2::geom_line() and adds a
#' ggplot2::geom_point() at the observation with the maximum x value for each
#' group.
#' @inheritParams ggplot2::geom_line
#' @section Aesthetics:
#' \code{geom_linepoint()} understands the following aesthetics (required aesthetics are in bold):
#'   \itemize{
#'     \item \strong{\code{x}}
#'     \item \strong{\code{y}}
#'     \item \code{alpha}
#'     \item \code{colour}
#'     \item \code{group}
#'     \item \code{linetype}
#'     \item \code{pointfill}
#'     \item \code{pointshape}
#'     \item \code{pointsize}
#'     \item \code{pointstroke}
#'     \item \code{pointshape}
#'     \item \code{linewidth}
#'     \item \code{weight}
#'   }
#' The aesthetics that begin with 'point' (eg. pointfill) are passed to
#' geom_point() - for example pointfill is passed to the fill aesthetic
#' of geom_point().
#'
#' The x, y, alpha, colour, and group aesthetics are passed to both
#' geom_line() and geom_point().
#'
#' The linetype, linewidth, and weight aesthetics are passed to geom_line().
#'
#' Learn more about setting these aesthetics in \code{vignette("ggplot2-specs")}.
#' @seealso ggplot2::geom_line, ggplot2::geom_point
#' @rdname geom_linepoint
#' @export
#' @examples
#' library(ggplot2)
#'
#'ggplot(ggplot2::economics_long, aes(x = date, y = value)) +
#'   geom_linepoint(aes(col = variable)) +
#'   facet_wrap(~variable)
geom_linepoint <-
  function(mapping = NULL,
           data = NULL,
           stat = "identity",
           position = "identity",
           na.rm = FALSE,
           show.legend = NA,
           inherit.aes = TRUE,
           ...) {
    ggplot2::layer(
      data = data,
      mapping = mapping,
      stat = stat,
      geom = GeomLinePoint,
      position = position,
      show.legend = show.legend,
      inherit.aes = inherit.aes,
      params = list(
        na.rm = na.rm,
        ...
      )
    )
  }

#' @rdname geom_linepoint
#' @export
GeomLinePoint <- ggplot2::ggproto(
  "GeomLinePoint",
  ggplot2::Geom,
  extra_params = c("na.rm"),
  setup_data = function(data, params) {
    ggplot2::GeomLine$setup_data(data, params)
  },
  draw_group = function(data,
                        panel_params,
                        coord,
                        lineend = "butt",
                        linejoin = "round",
                        linemitre = 10,
                        size = 5,
                        flipped_aes = FALSE) {
    point <- data
    point <- point[point$x == point$x[length(point$x)], ]
    point$size <- point$pointsize
    point$fill <- point$pointfill
    point$shape <- point$pointshape
    point$stroke <- point$pointstroke
    point$shape <- point$pointshape

    if (utils::packageVersion("ggplot2") < "3.4.0") {
      names(data)[names(data) == "linewidth"] <- "size"
    }

    path <- transform(data, alpha = NA)

    grid::gList(
      ggplot2::GeomLine$draw_panel(
        path,
        panel_params,
        coord,
        lineend = lineend,
        linejoin = linejoin,
        linemitre = linemitre
      ),
      ggplot2::GeomPoint$draw_panel(point, panel_params, coord)
    )
  },
  draw_key = ggplot2::draw_key_smooth,
  required_aes = c("x", "y"),
  non_missing_aes = c(
    "linewidth", "shape", "colour", "pointsize",
    "pointstroke", "pointfill", "pointshape"
  ),
  default_aes = ggplot2::aes(
    pointsize = 2.5,
    pointfill = "white",
    pointshape = 21,
    shape = 19,
    colour = "black",
    linewidth = 1,
    alpha = 1,
    pointstroke = 1.5,
    linetype = 1,
    weight = 1
  )
)

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.