Search code examples
rggplot2plotphylogeny

Formatting phylogeny to map projection (`phylo.to.plot`, or alternate method) in R


I am hoping someone can help me with the formating from phylo.to.plot() or suggest another method that can produce a similar output.

I have followed tutorial(s) here to produce an output but it seems difficult to alter the resulting figures.

Briefly these are my questions. I will expand further below.

  1. How to plot a subregion of a "WorldHires" map, not entire region?
  2. Change the shape of the points on the map, but maintain the colour?
  3. Add gradient of continuous variable to map

Reproducible example:

Here is a very basic tree with some randomly assigned geographic locations

myTree <- ape::read.tree(text='((A, B), ((C, D), (E, F)));')
plot(myTree)

# It needs to be rooted for `phylo.to.map()` to work
myTree$branch.length = NULL
rooted_cladogram = ape::compute.brlen(myTree)

# Sample information
Sample <- c("A","B","C","D","E","F")
coords <- matrix(c(56.001966,57.069417,50.70228, 51.836213, 54.678997, 54.67831,-5.636926,-2.47805,-3.8975018, -2.235444,-3.4392211, -1.751833), nrow=6, ncol=2)

rownames(coords) <- Sample  
head(coords)

## Plot phylo.to.map
obj<-phylo.to.map(rooted_cladogram,coords,database="worldHires", regions="UK",plot=FALSE,xlim=c(-11,3), ylim=c(49,59),direction="rightwards")
plot(obj,direction="rightwards",fsize=0.5,cex.points=c(0,1), lwd=c(3,1),ftype="i")

Plot output here:

output

Question 1: How do I plot a subregion of a "WorldHires" map, not the entire region?

I would like to only have mainland Britain which is a subregion of the "UK" in the WorldHires database. To access it normally I would do:

map1 <- ggplot2::map_data(map = "worldHires", region = c("UK"),xlim=c(-11,3), ylim=c(49,59))
GB <- subset(map1, subregion=="Great Britain")
# Plot
GB_plot<- ggplot(GB )+
  geom_polygon(aes(x = long, y = lat, group = group), fill = "white", colour = "black")+
theme_classic()+
  theme(axis.line=element_blank(),
        axis.text=element_blank(),
        axis.ticks=element_blank(),
        axis.title=element_blank(),
        panel.border = element_blank())

Which looks like this: GB_wordHire_subRegion

I have tried but it ignore the subregion argument.

obj<-phylo.to.map(ttree,coords,database="worldHires", regions="UK", subregion="Great Britain",plot=FALSE,xlim=c(-11,3), ylim=c(49,59),direction="rightwards")

Is there a way to provide it directly with a map instead of using WorldHires?

Question 2: How do I change the shape of the points on the map but keep maintain the colour?

I want to use shapes on the map to indicate the 3 major clade on my tree geographically. However, when I add a pch argument in, it correctly changes the shapes but the points then become black instead of following the colour that they were before. The lines from the tree to the map maintain the colour, it is just the points themselves that seem to turn black.

This is how I have tried to change the shape of the points:

# Original code - points
cols <-setNames(colorRampPalette(RColorBrewer::brewer.pal(n=6, name="Dark2"))(Ntip(myTree)),myTree$tip.label)

obj<-phylo.to.map(rooted_cladogram,coords,database="worldHires", regions="UK",plot=FALSE,xlim=c(-11,3), ylim=c(49,59),direction="rightwards")
plot(obj,direction="rightwards",fsize=0.5,cex.points=c(0,1), colors=cols,lwd=c(3,1),ftype="i")

Point and lines are coloured. I would like to change the shape of points Coloured points

# Code to change points = but points are no longer coloured
shapes <- c(rep(2,2),rep(1,2),rep(0,2))

obj<-phylo.to.map(rooted_cladogram,coords,database="worldHires", regions="UK",plot=FALSE,xlim=c(-11,3), ylim=c(49,59),direction="rightwards")
plot(obj,direction="rightwards",fsize=0.5,cex.points=c(0,1), colors=cols,pch=shapes,lwd=c(3,1),ftype="i")

Output: The shapes are changed but they are no longer coloured in: shape_butNocolour

Question 3: How do I add a gradient to the map?

Given this fake dataset, how to I create a smoothed gradient of the value variable?

Any help and advice on this would be very much appreciated. It would also be useful to know how to change the size of points

Thank you very much in advance, Eve


Solution

  • I improved (somewhat) on my comments by using the map you made in your question. Here's the code:

    library(mapdata)
    library(phytools)
    library(ggplot2)
    
    myTree <- ape::read.tree(text='((A, B), ((C, D), (E, F)));')
    plot(myTree)
    
    # It needs to be rooted for `phylo.to.map()` to work
    myTree$branch.length = NULL
    rooted_cladogram = ape::compute.brlen(myTree)
    
    # Sample information
    Sample <- c("A","B","C","D","E","F")
    coords <- matrix(
      c(56.001966,
        57.069417,
        50.70228,
        51.836213,
        54.678997,
        54.67831,
        -5.636926,
        -2.47805,
        -3.8975018,
        -2.235444,
        -3.4392211,
        -1.751833),
      nrow=6,
      ncol=2)
    
    rownames(coords) <- Sample  
    head(coords)
    
    obj <- phylo.to.map(
      rooted_cladogram,
      coords,
      database="worldHires",
      regions="UK",
      plot=FALSE,
      xlim=c(-11,3),
      ylim=c(49,59),
      direction="rightwards")
    
    # Disable default map
    obj2 <- obj
    obj2$map$x <- obj$map$x[1]
    obj2$map$y <- obj$map$y[1]
    
    # Set plot parameters
    cols <- setNames(
      colorRampPalette(
        RColorBrewer::brewer.pal(n=6, name="Dark2"))(Ntip(myTree)),myTree$tip.label)
    shapes <- c(rep(2,2),rep(1,2),rep(0,2))
    sizes <- c(1, 2, 3, 4, 5, 6)
    
    # Plot phylomap
    plot(
      obj2,
      direction="rightwards",
      fsize=0.5,
      cex.points=0,
      colors=cols,
      pch=shapes,
      lwd=c(3,1),
      ftype="i")
    
    # Plot new map area that only includes GB
    uk <- map_data(
      map = "worldHires",
      region = "UK")
    gb <- uk[uk$subregion == "Great Britain",]
    points(x = gb$long,
           y = gb$lat,
           cex = 0.001)
    
    # Plot points on map
    points(
      x = coords[,2],
      y = coords[,1],
      pch = shapes,
      col = cols,
      cex = sizes)
    

    phlyo_gb

    e: Use sf object instead of points to illustrate GB. It is tough to provide more advice beyond this on how to add symbology for your spatially varying variable, but sf is popular and very well documented, e.g. https://r-spatial.github.io/sf/articles/sf5.html. Let me know if you have any other questions!

    ee: Added lines to plot name and symbol on tips.

    eee: Added gradient dataset to map.

    library(phytools)
    library(mapdata)
    library(ggplot2)
    library(sf)
    
    myTree <- ape::read.tree(text='((A, B), ((C, D), (E, F)));')
    plot(myTree)
    
    # It needs to be rooted for `phylo.to.map()` to work
    myTree$branch.length = NULL
    rooted_cladogram = ape::compute.brlen(myTree)
    
    # Sample information
    Sample <- c("A","B","C","D","E","F")
    coords <- matrix(c(56.001966,57.069417,50.70228, 51.836213, 54.678997, 54.67831,-5.636926,-2.47805,-3.8975018, -2.235444,-3.4392211, -1.751833), nrow=6, ncol=2)
    rownames(coords) <- Sample  
    head(coords)
    
    obj <- phylo.to.map(
      rooted_cladogram,
      coords,
      database="worldHires",
      regions="UK",
      plot=FALSE,
      xlim=c(-11,3),
      ylim=c(49,59),
      direction="rightwards")
    
    # Disable default map
    obj2 <- obj
    obj2$map$x <- obj$map$x[1]
    obj2$map$y <- obj$map$y[1]
    
    ## Plot tree portion of map
    
    # Set plot parameters
    cols <- setNames(
      colorRampPalette(
        RColorBrewer::brewer.pal(n=6, name="Dark2"))(Ntip(myTree)),myTree$tip.label)
    shapes <- c(rep(2,2),rep(1,2),rep(0,2))
    sizes <- c(1, 2, 3, 4, 5, 6)
    
    # Plot phylomap
    plot(
      obj2,
      direction="rightwards",
      fsize=0.5,
      cex.points=0,
      colors=cols,
      pch=shapes,
      lwd=c(3,1),
      ftype="i")
    
    tiplabels(pch=shapes, col=cols, cex=0.7, offset = 0.2)
    tiplabels(text=myTree$tip.label, col=cols, cex=0.7, bg = NA, frame = NA, offset = 0.2)
    
    ## Plot GB portion of map
    
    # Plot new map area that only includes GB
    uk <- map_data(map = "worldHires", region = "UK")
    gb <- uk[uk$subregion == "Great Britain",]
    
    # Convert GB to sf object
    gb_sf <- st_as_sf(gb, coords = c("long", "lat"))
    
    # Covert to polygon
    gb_poly <- st_sf(
      aggregate(
        x = gb_sf$geometry,
        by = list(gb_sf$region),
        FUN = function(x){st_cast(st_combine(x), "POLYGON")}))
    
    # Add polygon to map
    plot(gb_poly, col = NA, add = TRUE)
    
    ## Load and format gradient data as sf object
    
    # Load data
    g <- read.csv("gradient_data.txt", sep = " ", na.strings = c("NA", " "))
    # Check for, then remove NAs
    table(is.na(g))
    g2 <- g[!is.na(g$Lng),]
    # For demonstration purposes, make dataset easier to manage
    # Delete this sampling line to use the full dataset
    g2 <- g2[sample(1:nrow(g2), size = 1000),]
    # Create sf point object
    gpt <- st_as_sf(g2, coords = c("Lng", "Lat"))
    
    ## Set symbology and plot 
    
    # Cut data into 5 groups based on "value"
    groups <- cut(gpt$value,
                  breaks = seq(min(gpt$value), max(gpt$value), len = 5),
                  include.lowest = TRUE)
    # Set colors
    gpt$colors <- colorRampPalette(c("yellow", "red"))(5)[groups]
    # Plot
    plot(gpt$geometry, pch = 16, col = gpt$colors, add = TRUE)
    
    ## Optional legend for gradient data
    
    # Order labels and colors for the legend
    lev <- levels(groups)
    # Used rev() here to make colors in correct order
    fil <- rev(levels(as.factor(gpt$colors)))
    legend("topright", legend = lev, fill = fil, add = TRUE)
    
    ## Plot sample points on GB
    
    # Plot points on map
    points(
      x = coords[,2],
      y = coords[,1],
      pch = shapes,
      col = cols,
      cex = sizes)
    

    see here for more info on gradient symbology and legends: R: Gradient plot on a shapefile

    enter image description here

    gradient2