Search code examples
rggplot2maps

Putting labels outside polygonswuth ggplot2? i.e. in the sea or in corners of map


I want to make a map with labels, but I don't want the labes in the polygons. Is there any way to get labels otside the polygons?

here reproductible example:

require(maps)
require(ggplot2)
require(ggrepel)

set.seed(1)
world <- map_data("world")
world <- world[world$region != "Antarctica",]
cities <- structure(list(city = c("Dallas", "Johannesburg", "Abu Dhabi", 
                                  "Dubai", "Jeddah", "Doha", "New York", "Newark", "Boston", "Houston", 
                                  "Los Angeles", "Toronto", "Mumbai", "Chicago", "Sydney", "Atlanta", 
                                  "San Francisco", "Hong Kong", "Guangzhou", "Taipei", "Melbourne"
), lon = c(-96.7969879, 28.0473051, 54.6972774, 55.2707828, 39.2375507, 
           51.5310398, -74.0059413, -74.1723667, -71.0588801, -95.3698028, 
           -118.2436849, -79.3831843, 72.8776559, -87.6297982, 151.2069902, 
           -84.3879824, -122.4194155, 114.109497, 113.264385, 121.5654177, 
           144.96328), lat = c(32.7766642, -26.2041028, 24.2991738, 25.2048493, 
                               21.2854067, 25.2854473, 40.7127837, 40.735657, 42.3600825, 29.7604267, 
                               34.0522342, 43.653226, 19.0759837, 41.8781136, -33.8674869, 33.7489954, 
                               37.7749295, 22.396428, 23.12911, 25.0329694, -37.814107)), 
class = "data.frame", .Names = c("city", "lon", "lat"), row.names = c(NA, -21L))



ggplot() + 
  geom_map(data=world, map=world, aes(x=long, y=lat, map_id=region), fill = 'grey70') +
  coord_map("mollweide") +
  geom_text_repel(data = cities, aes(lon, lat, label = city), size = 3,
                  box.padding = unit(0.1, 'lines'), force = 0.5)

enter image description here

But I want labels outside of land.


Solution

  • The issue is that geom_text_repel does not know about land mass, i.e. you pass only the information about the cities via the data argument.

    If you want geom_text_repel to make a a good job you have to make it aware of the land or more generally all data. To this end I bind both dataframes (world and cities) together. Not perfect but ...

    library(ggplot2)
    library(ggrepel)
    library(dplyr, warn=FALSE)
    
    set.seed(1)
    
    world <- map_data("world")
    world <- world[world$region != "Antarctica", ]
    cities <- structure(
      list(city = c(
        "Dallas", "Johannesburg", "Abu Dhabi",
        "Dubai", "Jeddah", "Doha", "New York", "Newark", "Boston", "Houston",
        "Los Angeles", "Toronto", "Mumbai", "Chicago", "Sydney", "Atlanta",
        "San Francisco", "Hong Kong", "Guangzhou", "Taipei", "Melbourne"
      ), lon = c(
        -96.7969879, 28.0473051, 54.6972774, 55.2707828, 39.2375507,
        51.5310398, -74.0059413, -74.1723667, -71.0588801, -95.3698028,
        -118.2436849, -79.3831843, 72.8776559, -87.6297982, 151.2069902,
        -84.3879824, -122.4194155, 114.109497, 113.264385, 121.5654177,
        144.96328
      ), lat = c(
        32.7766642, -26.2041028, 24.2991738, 25.2048493,
        21.2854067, 25.2854473, 40.7127837, 40.735657, 42.3600825, 29.7604267,
        34.0522342, 43.653226, 19.0759837, 41.8781136, -33.8674869, 33.7489954,
        37.7749295, 22.396428, 23.12911, 25.0329694, -37.814107
      )),
      class = "data.frame", .Names = c("city", "lon", "lat"), row.names = c(NA, -21L)
    )
    
    # Bind the dataframes for geom_text_repel.
    data_labels <- rename(world, lon = long) |> 
      mutate(city = "") |> 
      bind_rows(cities, .id = "id")
    
    ggplot() +
      geom_map(
        data = world,
        map = world,
        aes(map_id = region),
        fill = "grey70"
      ) +
      coord_map("mollweide") +
      geom_text_repel(
        data = data_labels, aes(lon, lat, label = city),
        size = 3, force = 1, seed = 123,
        max.overlaps = Inf
      )
    

    Created on 2023-11-21 with reprex v2.0.2