Adding Orientation Arrows and Annotations to Field Layout Plots

Introduction

When presenting field trial layouts, it’s often important to provide spatial context through orientation arrows (indicating north or other directions) and annotations that reference physical features of the field (such as boundaries, gates, or adjacent landmarks). This vignette demonstrates how to add these elements to field layout plots generated by the biometryassist package.

The biometryassist package generates field layout plots as ggplot2 objects, which means we can enhance them using standard ggplot2 functions and extensions. This vignette will show you how to:

  1. Add orientation arrows with arbitrary rotation
  2. Add text annotations outside the plotting area to label field features

Setup

First, let’s load the required packages and generate a basic experimental design:

library(biometryassist)
library(ggplot2)
library(ggspatial)

# Generate a completely randomized design (CRD)
des.out <- design(
  type = "crd", 
  treatments = c(1, 5, 10, 20),
  reps = 5, 
  nrows = 4, 
  ncols = 5, 
  seed = 42
)
Source of Variation                     df
=============================================
treatments                              3
Residual                                16
=============================================
Total                                   19

The design() function creates a field layout and stores it in des.out$plot.des as a ggplot2 object. We can now enhance this plot with additional annotations.

Adding an Orientation Arrow

Orientation arrows help readers understand the spatial layout of a field trial relative to cardinal directions or other reference points. This is particularly useful when field orientation affects environmental conditions like sun exposure or prevailing winds.

Using ggspatial for North Arrows

The ggspatial package provides the annotation_north_arrow() function, which is specifically designed for adding directional arrows to plots. While it’s primarily intended for spatial data with coordinate reference systems, it works well for field layout plots too.

des.out$plot.des + 
  annotation_north_arrow(
    location = "tr",
    rotation = 53,
    pad_x = unit(-1.5, "cm"),
    pad_y = unit(0.5, "cm"),
    style = north_arrow_fancy_orienteering(
      line_width = 1,
      text_size = 10
    )
  ) +
  coord_cartesian(clip = "off") +
  theme(plot.margin = margin(t = 10, r = 50, b = 10, l = 10, unit = "pt"))

Let’s break down each component of this code:

annotation_north_arrow() arguments:

Additional ggplot2 modifications:

Customizing Arrow Appearance

You can customize the arrow style parameters to match your needs:

des.out$plot.des + 
  annotation_north_arrow(
    location = "tr",
    rotation = 30,  # Different rotation angle
    pad_x = unit(-1, "cm"),
    pad_y = unit(0.3, "cm"),
    style = north_arrow_minimal(  # Different style
      line_width = 1.5,
      text_size = 12
    )
  ) +
  coord_cartesian(clip = "off") +
  theme(plot.margin = margin(t = 10, r = 50, b = 10, l = 10, unit = "pt"))

Adding Field Feature Annotations

In addition to orientation, it’s often helpful to label physical features of the field site, such as boundaries, access points, or adjacent landmarks. These annotations provide practical context for field operations and help with trial management.

Using annotate() for Text Labels

The annotate() function allows us to add text (and other geometric objects) to specific locations on the plot. Unlike the north arrow, these annotations use the plot’s coordinate system.

des.out$plot.des + 
  annotate(
    "text",
    x = 5.8,
    y = mean(des.out$design$row),
    label = "Boundary Road",
    angle = 270,
    hjust = 0.5,
    vjust = -0.5,
    size = 6
  ) +
  annotate(
    "text",
    x = 1.5,
    y = 5,
    label = "Gate",
    hjust = 0.5,
    vjust = -0.5,
    size = 6
  ) +
  coord_cartesian(xlim = c(0.5, 5.5), ylim = c(0.5, 4.5), clip = "off") +
  theme(plot.margin = margin(t = 10, r = 70, b = 20, l = 10, unit = "pt"),
        legend.position="none")

Let’s examine each annotation in detail:

First annotation - “Boundary Road” (vertical text on right):

Second annotation - “Gate” (horizontal text on top):

Critical settings for external annotations:

Understanding the Coordinate System

The field layout plot uses the row and column numbers as coordinates: - Columns are numbered 1 to ncols (here, 1 to 5) - Rows are numbered 1 to nrows (here, 1 to 4) - Each plot cell is centered on its integer coordinates

To position annotations: - Outside right edge: Use x > max column number (e.g., x = 5.8 when max column is 5) - Outside top edge: Use y > max row number (e.g., y = 5 when max row is 4) - Outside left edge: Use x < min column number (e.g., x = 0.2 when min column is 1) - Outside bottom edge: Use y < min row number (e.g., y = 0.2 when min row is 1)

Adding Multiple Features

You can add as many annotations as needed by chaining multiple annotate() calls:

des.out$plot.des + 
  annotate("text", x = 5.8, y = mean(des.out$design$row), 
           label = "Boundary Road", angle = 270, hjust = 0.5, vjust = -0.5, size = 6) +
  annotate("text", x = 1.5, y = 5, 
           label = "Gate", hjust = 0.5, vjust = -0.5, size = 6) +
  annotate("text", x = 0.2, y = mean(des.out$design$row), 
           label = "Irrigation Line", angle = 90, hjust = 0.5, vjust = -0.5, size = 5) +
  annotate("text", x = mean(des.out$design$col), y = 0.2, 
           label = "Drainage Ditch", hjust = 0.5, vjust = 1, size = 5) +
  coord_cartesian(xlim = c(0.5, 5.5), ylim = c(0.5, 4.5), clip = "off") +
  theme(plot.margin = margin(t = 20, r = 70, b = 30, l = 50, unit = "pt"),
        legend.position="none")

Combining Orientation Arrows and Annotations

You can combine both techniques to create a fully annotated field layout:

des.out$plot.des + 
  # Add north arrow
  annotation_north_arrow(
    location = "tr",
    rotation = 45,
    pad_x = unit(-1.5, "cm"),
    pad_y = unit(0.5, "cm"),
    style = north_arrow_fancy_orienteering(line_width = 1, text_size = 10)
  ) +
  # Add field feature annotations
  annotate("text", x = 5.8, y = mean(des.out$design$row), 
           label = "Boundary Road", angle = 270, hjust = 0.5, vjust = -0.5, size = 6) +
  annotate("text", x = 1.5, y = 5, 
           label = "Gate", hjust = 0.5, vjust = -0.5, size = 6) +
  # Apply coordinate system and theme adjustments
  coord_cartesian(xlim = c(0.5, 5.5), ylim = c(0.5, 4.5), clip = "off") +
  theme(plot.margin = margin(t = 20, r = 70, b = 20, l = 10, unit = "pt"),
        legend.position="none")

Tips and Best Practices

Determining Appropriate Coordinates

To find the right coordinates for your annotations:

  1. Check the design dimensions: Use str(des.out$design) to see the row and column ranges
  2. Use summary functions: mean(), min(), and max() help center or position annotations
  3. Experiment iteratively: Start with approximate values and adjust based on the output

Adjusting Margins

If your annotations are cut off or too far from the plot:

Text Rotation Guidelines

Justification Parameters

For external annotations, vjust values outside 0-1 (like vjust = -0.5) create spacing between the text and the plot edge.

Working with Different Design Types

These techniques work with any design type generated by biometryassist. Here’s an example with a randomized complete block design (RCBD):

# Generate an RCBD
des.rcbd <- design(
  type = "rcbd",
  treatments = LETTERS[1:6],
  reps = 4,
  nrows = 6,
  ncols = 4,
  brows = 6,
  bcols = 1,
  seed = 123
)
Source of Variation                     df
=============================================
Block stratum                           3
---------------------------------------------
treatments                              5
Residual                                15
=============================================
Total                                   23


# Add annotations
des.rcbd$plot.des +
  annotation_north_arrow(
    location = "tr",
    rotation = 0,
    pad_x = unit(-1, "cm"),
    pad_y = unit(0.5, "cm"),
    style = north_arrow_minimal(line_width = 1, text_size = 10)
  ) +
  annotate("text", x = 4.8, y = mean(des.rcbd$design$row),
           label = "Access Road", angle = 270, hjust = 0.5, vjust = -0.5, size = 5) +
  coord_cartesian(xlim = c(0.5, 4.5), ylim = c(0.5, 6.5), clip = "off") +
  theme(plot.margin = margin(t = 10, r = 60, b = 10, l = 10, unit = "pt"))

Note how the coordinate limits (xlim and ylim) are adjusted to match the dimensions of this design (4 columns, 6 rows).

Troubleshooting

Problem: Annotation is cut off

Solution: Increase the relevant plot margin using theme(plot.margin = margin(...)). Ensure margins are large enough to accommodate your text.

Problem: Plot is stretched or distorted

Solution: When using annotate() with coordinates outside the plot range, always specify explicit limits in coord_cartesian(xlim = ..., ylim = ...) to prevent automatic axis expansion.

Problem: North arrow is not visible

Solution: 1. Check that coord_cartesian(clip = "off") is included 2. Verify that plot margins are large enough 3. Adjust pad_x and pad_y values to position the arrow within the margin area

Problem: Text orientation is wrong

Solution: Adjust the angle parameter: - For right side labels reading upward: angle = 90 - For right side labels reading downward (conventional): angle = 270 - For left side labels: reverse these angles

Conclusion

Adding orientation arrows and field feature annotations to your experimental design plots enhances their utility for field management, reporting, and publication. The combination of ggspatial::annotation_north_arrow() for directional reference and ggplot2::annotate() for custom labels provides flexible tools for creating publication-ready field layout diagrams.

Remember the key principles: - Use coord_cartesian(clip = "off") to allow drawing outside the plot panel - Set appropriate plot.margin values to create space for external elements - For annotate(), explicitly set xlim and ylim to prevent axis stretching - Experiment with positioning and adjust iteratively

Further Reading