Search code examples
rplotshapesshapefiler-sf

Add labels on top of plot of shapefiles


Consider an example dataset where I have four polygons:

enter image description here

I want the colour of the polygon to be based on which group it is in, a legend showing what each colour means and a label over each polygon with it's house number.

The following line of code differentiates the colour by group:

plot(df$geometry, col=df$group) 

enter image description here

Reproducible data from example above:

structure(list(shape = c("polygon 1", "polygon 2", "polygon 3", 
"polygon 4"), geometry = structure(list(structure(list(structure(c(0, 
1, 1, 0, 0, 1, 1, 0, 0, 1), .Dim = c(5L, 2L))), class = c("XY", 
"POLYGON", "sfg"), precision = 0, bbox = structure(c(xmin = 0, 
ymin = 0, xmax = 1, ymax = 1), class = "bbox"), crs = structure(list(
input = NA_character_, wkt = NA_character_), class = "crs"), n_empty = 
0L), 
structure(list(structure(c(2, 3, 3, 2, 2, 3, 3, 2, 2, 3), .Dim = c(5L, 
2L))), class = c("XY", "POLYGON", "sfg"), precision = 0, bbox = 
structure(c(xmin = 2, 
ymin = 2, xmax = 3, ymax = 3), class = "bbox"), crs = structure(list(
    input = NA_character_, wkt = NA_character_), class = "crs"), 
n_empty = 0L), 
structure(list(structure(c(4, 5, 5, 4, 4, 4, 4, 5, 5, 4), .Dim = c(5L, 
2L))), class = c("XY", "POLYGON", "sfg"), precision = 0, bbox = 
structure(c(xmin = 4, 
ymin = 4, xmax = 5, ymax = 5), class = "bbox"), crs = structure(list(
    input = NA_character_, wkt = NA_character_), class = "crs"), 
n_empty = 0L), 
structure(list(structure(c(6, 7, 7, 6, 6, 7, 7, 6, 6, 7), .Dim = c(5L, 
2L))), class = c("XY", "POLYGON", "sfg"), precision = 0, bbox = 
structure(c(xmin = 6, 
ymin = 6, xmax = 7, ymax = 7), class = "bbox"), crs = structure(list(
    input = NA_character_, wkt = NA_character_), class = "crs"), 
n_empty = 0L)), precision = 0, bbox = structure(c(xmin = 0, 
ymin = 0, xmax = 7, ymax = 7), class = "bbox"), crs = structure(list(
input = NA_character_, wkt = NA_character_), class = "crs"), n_empty = 
0L, class = c("sfc_POLYGON", 
"sfc")), group = c(1, 1, 2, 2), house = c(1, 2, 3, 4)), row.names = 
c(NA, 
4L), class = c("sf", "tbl_df", "tbl", "data.frame"), sf_column = 
"geometry", agr = structure(c(shape = NA_integer_, 
group = NA_integer_, house = NA_integer_), class = "factor", .Label = 
c("constant", 
"aggregate", "identity")))

Solution

  • I couldn't plot it using the basic plot(). However, I think I achieved your goal using the ggplot package in a much more readable way, as follows:

    # setup environment
    library(ggplot2)
    # define a data frame with x and y coordinates and their groups
    df = data.frame(
      group = rep(1:2, each = 8),
      house = rep(1:4, each = 4),
      x = c(0, 1, 1, 0,
            2, 3, 3, 2,
            4, 5, 5, 4,
            6, 7, 7, 6),
      y = c(0, 0, 1, 1,
            2, 2, 3, 3,
            4, 4, 5, 5,
            6, 6, 7, 7),
    )
    # create a data frame with text labels and their coordinates
    lbl = df %>%
      group_by(house) %>%
      summarise(x = mean(x), y = max(y) + 0.5) %>%
      unique()
    # plot the houses using ggplot package
    ggplot(df, aes(x, y)) +
      geom_polygon(aes(fill = group, group = house), colour = 'black') +
      labs(title = 'House Geometries', x = 'X', y = 'Y') +
      scale_fill_manual(name = 'House', values = c('black', 'red')) +
      annotate('text', x = lbl$x, y = lbl$y, label = lbl$house, size = 6) +
      theme(plot.title = element_text(size = 19, face = 'bold', hjust = 0.5),
            legend.title = element_text(size = 15, hjust = 0.5),
            legend.text = element_text(size = 14, hjust = 0.5),
            axis.title.x = element_text(size = 15),
            axis.text.x = element_text(size = 14),
            axis.title.y = element_text(size = 15),
            axis.text.y = element_text(size = 14))
    

    Here is the final plot:

    enter image description here

    By the way, I work with civil engineering and your problem was quite interesting to me...

    Let me know if this is what you were planning to do.