1. Introduction

This vignette exemplifies how to use Spatial Annotations in SPATA2. It builds on the spatial annotations created in the vignette creating spatial annotations.

# load required packages
library(SPATA2)
library(tidyverse)

# load SPATA2 inbuilt data
object_t313 <- loadExampleObject("UKF313T", process = TRUE, meta = TRUE)

2. Image subsetting

Spatial annotations annotate space which can be directly translated into the image regardless of the annotation was drawn based on histomorphological features (Image Annotations) or numeric and grouping features (Numeric- and GroupAnnotations). The annotated space can be used to obtain image sections cropped to only include the annotated area. getSpatialAnnotation() extracts an object of class SpatialAnnotation.

# obtain the IDs of all spatial annotations
getSpatAnnIds(object_t313)
## [1] "necrotic_area"          "necrotic_center"        "necrotic_edge"         
## [4] "necrotic_edge2"         "necrotic_edge2_transgr"
necrotic_area <- 
  getSpatialAnnotation(
    object = object_t313,
    id = "necrotic_area", 
    add_image = T
    )

# print summary 
necrotic_area
## An object of class SpatialAnnotation
## ID:  necrotic_area 
## Sample:  UKF313T 
## Memory: 1.53 Mb
## Tags:  necrotic, compr 
## Area information:
##    outer :  286  vertices
##    inner1 :  235  vertices
## Cropped image dimensions (WxHxC):  238 x 250 x 3
# print slot names
slotNames(necrotic_area)
## [1] "parent_name" "area"        "id"          "image"       "image_info" 
## [6] "misc"        "sample"      "tags"        "version"

By default, the image is cropped in a way that only the annotation is included.

# visualize from the SPATA2 object, with expand = 0
plotSpatialAnnotations(object_t313, ids = "necrotic_area", expand = 0, fill = NA)

# expand = 0 is how the image is extracted when extracting the spatial annotation
# (in SPATA2, images are plotted upside down to fit into the cartesian coordinate system)
plot(EBImage::flip(necrotic_area@image))

The argument expand can be used with getSpatialAnnotation(), too, in order to manipulate with how much extra space the image is cropped. This works in pixel as well as with SPATA2’s SI unit system.

# visualize from the SPATA2 object, with expand = 0
plotSpatialAnnotations(
  object = object_t313,
  ids = "necrotic_area", 
  expand = "500um", 
  fill = NA, 
  sb_dist = "500um", 
  sb_pos = c("4.5mm", "3mm")
  )

necrotic_area_expanded <- 
  getSpatialAnnotation(
    object = object_t313,
    id = "necrotic_area", 
    add_image = T, 
    expand = "500um"
    )

expanded_image <- EBImage::flip(necrotic_area_expanded@image)

plot(expanded_image)

3. Inferring expression gradients

The borders of spatial annotations can be used as reference points to analyze expression of numeric features (e.g. gene expression) as a function of distance to the annotated areas. The figure below illustrates the concept.

necrotic_ids <- c("necrotic_area", "necrotic_edge", "necrotic_edge2")

# obtain distance data relative to spatial annotations (SA) with getCoordsDfSA()
coords_df <- 
  getCoordsDfSA(object_t313, ids = necrotic_ids, unit = "mm", binwidth = "200um", core0 = T) 

# add distance to necrosis as meta feature to the SPATA2 object
# to make them accessible for other functions
object_t313 <- 
  addFeatures(object_t313, feature_df = coords_df[,c("barcodes", "dist", "bins_dist")], overwrite = T)

# create ggproto layer for further surface plots
necrotic_outline <- 
  ggpLayerSpatAnnOutline(object_t313, ids = necrotic_ids, incl_edge = T, fill = "grey")

sas_screning <- 
  ggpLayerScreeningDirectionSAS(object_t313, ids = necrotic_ids, line_size = 0.75)

# show plots 
plotImage(object_t313) + 
  necrotic_outline + 
  sas_screning

plotSurface(object_t313, color_by = "dist", pt_clrsp = "plasma") + 
  necrotic_outline + 
  sas_screning + 
  labs(color = "Dist [mm]")

Inferring expression as function of distance is part of the Spatial Gradient Screening algorithm (SAS). Therefore, functions related to this concept feature the acronym Sas such as plotSasLineplot() or plotSasHeatmap().

plotSurface(object_t313, color_by = "HM_HYPOXIA", display_image = F, outline = T, pt_clrsp = "BuPu") + 
  necrotic_outline + 
  sas_screning

plotSasLineplot(object_t313, ids = necrotic_ids, variables = "HM_HYPOXIA", line_color = "blue") + 
  labs(x = "Distance to Necrotic Borders [mm]")

If the number of features displayed becomes too high for a lineplot switch to plotSasHeatmap().

# use spatialAnnotationScreening() to identify non random genes 
# and genes with specific expression pattern related to your spatial annotations
associated_with_necrosis <-
  c("SLC2A1", "ADM", "ERO1A", "CD44", "STC2", "CA12", "VEGFA", "NDRG1", "TMEM158", "LOX")

repelled_by_necrosis <- 
  c("CD74", "CLU", "IFITM3", "C1QA", "C1QB", "CD68", "SELENOP", "SEC61B", "HLA-DRB1", "EMC6")

plotSasHeatmap(object_t313, variables = associated_with_necrosis, ids = necrotic_ids, clrsp = "BuPu") + 
  labs(x = "Distance to Necrotic Borders [mm]")

plotSasHeatmap(object_t313, variables = repelled_by_necrosis, ids = necrotic_ids, clrsp = "Reds 3") + 
  labs(x = "Distance to Necrotic Borders [mm]")

3. Core, Environment and Periphery

The concept of Spatial Annotation Screening differentiates three types of areas when working with spatial annotations:

  • Core: The area annotated by the outer (and inner, see annotation necrotic_area) borders of a spatial annotation.
  • Environemnt: The area by which the annotation (or the annotations) is surrounded.
  • Periphery: The area that exceeds the distance up to which the expression pattern are inferred which is defined by the parameter distance.

By default distance is the maximum of distances between the objects observations (here, barcoded spots) to their respective closest annotation border. In the case of the three necrotic annotations the maximum distance is approximately 2mm.

## in mm, since unit = "mm"
round(max(coords_df$dist), digits = 2)
## [1] 1.99

This is referred to as distance to edge (dte) as observations up to the tissue’s edge are included. If your hypothesis requires a specific distance parameter up to which the expression gradient is inferred use the distance parameter. Fig.6 visualizes the differences in the set up.

sas_areas <- color_vector(clrp = "npg", names = c("core", "environment", "periphery"))

necrotic_outline_transp <- 
  ggpLayerSpatAnnOutline(object_t313, ids = necrotic_ids, incl_edge = T, fill = NA)

# rel_loc = location of each observation relative to the set up
plotSurface(coords_df, color_by = "rel_loc", clrp_adjust = sas_areas) + 
  necrotic_outline_transp +
  labs(subtitle = "`distance` = 'dte'")

# the scenario changes when distance is specified
coords_df2 <- 
  getCoordsDfSA(object_t313, ids = necrotic_ids, distance = "1.5mm")

plotSurface(coords_df2, color_by = "rel_loc", clrp_adjust = sas_areas) + 
  necrotic_outline_transp +
  labs(subtitle = "`distance` = 1.5mm")

Another aspect to consider is the core parameter. Depending on whether it is set to core = TRUE or core = FALSE the inside of the annotation is included in the gradient. It depends on the circumstances and your hypothesis whether it makes sense to include the core or not. In case of the necrotic areas we decided to set core = FALSE since we were particularly interested in the reaction of the surrounding of the necrotic areas.

4. Using external coordinate data.frames

You can relate spatial annotations to observations that do not live in the SPATA2 object but in a separate data.frame. Only requirements are that the data.frame contains x- and y- coordinates scaled to the resolution of the active image (if there is one) and a variable called barcodes identifying each observation uniquely.

# get sc deconvolution data
sc_input <- example_data$sc_input_lmu_mci

sc_input
## # A tibble: 4,356 × 5
##    barcodes     x     y cell_type             spot_id           
##    <chr>    <dbl> <dbl> <fct>                 <chr>             
##  1 cell1     442.  378. Neurons               AAACAAGTATCTCCCA-1
##  2 cell2     412.  140. Mural cells           AAACAGAGCGACTCCT-1
##  3 cell3     412.  137. Astrocytes            AAACAGAGCGACTCCT-1
##  4 cell4     413.  142. Astrocytes            AAACAGAGCGACTCCT-1
##  5 cell5     412.  138. Microglia             AAACAGAGCGACTCCT-1
##  6 cell6     424.  447. Monocytes/Macrophages AAACATTTCCCGGATT-1
##  7 cell7     425.  452. Microglia             AAACATTTCCCGGATT-1
##  8 cell8     426.  450. Microglia             AAACATTTCCCGGATT-1
##  9 cell9     492.  341. OPCs                  AAACCCGAACGAAATC-1
## 10 cell10    494.  346. OPCs                  AAACCCGAACGAAATC-1
## # ℹ 4,346 more rows
# load mouse brain sectionas as example data
object_mouse <- loadExampleObject("LMU_MCI", process = TRUE, meta = TRUE)

hemispheres <- ggpLayerTissueOutline(object_mouse)
injuries <- ggpLayerSpatAnnOutline(object_mouse, ids = c("inj1", "inj2"))

# left plot
plotSurface(object_mouse, color_by = "clusters") + 
  injuries

# right plot
plotSurface(sc_input, color_by = "cell_type", pt_clrp = "tab20b", pt_size = 0.5) + 
  injuries + 
  hemispheres  

Use sc_input as input for argument coords_df of getCoordsDfSA(). This way, not the coordinates stored in the SPATA2 objects are used but the ones you provide.

# relate to spatial annotations ...
sc_input_sa <- 
  getCoordsDfSA(
    object = object_mouse,
    ids = c("inj1", "inj2"),
    resolution = "300um", 
    coords_df = sc_input
    ) %>% 
  filter(rel_loc != "core")

# ... which adds the spatial information to the data.frame
sc_input_sa
## # A tibble: 4,092 × 15
##    barcodes     x     y tissue_section_id cell_type spot_id tissue_section  dist
##    <chr>    <dbl> <dbl> <chr>             <fct>     <chr>   <chr>          <dbl>
##  1 cell1     442.  378. tissue_section_2  Neurons   AAACAA… tissue_sectio… 0.502
##  2 cell10    494.  346. tissue_section_2  OPCs      AAACCC… tissue_sectio… 1.18 
##  3 cell100   408.  375. tissue_section_2  Oligos    AACGGA… tissue_sectio… 0.659
##  4 cell1000  370.  171. tissue_section_1  Monocyte… ATGCCG… tissue_sectio… 0.366
##  5 cell1001  371.  175. tissue_section_1  Oligos    ATGCCG… tissue_sectio… 0.415
##  6 cell1002  344.  442. tissue_section_2  Neurons   ATGCTC… tissue_sectio… 1.14 
##  7 cell1003  345.  441. tissue_section_2  Monocyte… ATGCTC… tissue_sectio… 1.13 
##  8 cell1004  346.  445. tissue_section_2  Astrocyt… ATGCTC… tissue_sectio… 1.11 
##  9 cell1005  347.  441. tissue_section_2  Astrocyt… ATGCTC… tissue_sectio… 1.10 
## 10 cell1006  481.  419. tissue_section_2  OPCs      ATGCTT… tissue_sectio… 0.567
## # ℹ 4,082 more rows
## # ℹ 7 more variables: border <chr>, dist_unit <chr>, bins_dist <fct>,
## #   rel_loc <fct>, angle <dbl>, bins_angle <fct>, id <fct>
ct <- c("Monocytes/Macrophages", "Microglia", "Neurons", "Astrocytes")

ggplot(sc_input_sa) + 
  geom_bar(mapping = aes(x = bins_dist, fill = cell_type), position = "stack") + 
  scale_color_add_on(aes = "fill", variable = sc_input_sa$cell_type, clrp = "tab20b") + 
  theme_classic()