2

I'm using maps and ggplot 2 to create a world map plot that shows continents in a different colour based on some results (that I've added to the country coordinates). So far, so good. However, I'm struggling with adding the continent names to the map, i.e. I want to add "Asia" on the orange part in the plot, "LATAM" to the blue part and so on. Not sure how to do that.

I thought about calculating the geographical mid point for each continent (which will become problematic when including overseas regions like for UK or Greenland for Denmark) or trying to find a representative country for each continent and draw the name at that position, e.g. for Asia, I could draw the name "Asia" at China's position. But I also don't know how to do that.

Here's my code so far and the current plot:

library(maps)
library(ggplot2)
library(tidyverse)

world = map_data("world")

! I'm not sure how to show in a code example here, but I have a matching file that I'm merging with the world map that contains the continental region region of each country (e.g. Asia, or Africa)

# Here I create some dummy results data for each region    
results = data.frame(region = c("Africa", "Asia", "Europe", "NA", "Oceania", "SA"),
                     kpi    = c(20, 30, 40, 50, 60, 70),
                     stringsAsFactors = F)

# Joining the dummy results with the worldmap data    
results_world = inner_join(world, results, by = "region")


plain <- theme(
  axis.text = element_blank(),
  axis.line = element_blank(),
  axis.ticks = element_blank(),
  panel.border = element_blank(),
  panel.grid = element_blank(),
  axis.title = element_blank(),
  panel.background = element_rect(fill = "white"),
  plot.title = element_text(hjust = 0.5)
)

ggplot(data = results_world,
                    aes(x = long, y = lat, group = group)) +
  coord_fixed(1.3) +
  geom_polygon(aes(fill = kpi)) +
  geom_text(aes(label = region)) +
  scale_fill_distiller(palette = "Spectral", direction = 1) +
  plain

This gives me the following plot:

Raw World Map

whereas I want to have sth. like this (I know that some countries are missing, e.g. in Africa, I'm still searching for a comprehensive file in the www containing a) all countries, b) their geographical continents and c) their political affiliation):

Desired Output

deschen
  • 10,012
  • 3
  • 27
  • 50
  • Did you try [this](https://stackoverflow.com/questions/43801030/filling-countries-and-continents-with-maps-library-according-to-variable-value)? – David Klotz Aug 12 '19 at 16:25
  • @DavidKlotz that post doesn't involve placing labels, which is what this OP is missing – camille Aug 12 '19 at 16:56
  • 1
    If you can switch to an `sf` object for the shape, you can use `geom_sf` and `geom_sf_text` to do this without calculating midpoints or centroids. Otherwise, calculate the centroid of each polygon and add labels with `geom_text` / `geom_label`. Several related posts are [here](https://stackoverflow.com/questions/22038640/labeling-center-of-map-polygons-in-r-ggplot), [here](https://stackoverflow.com/q/9441436/5325862), [here](https://stackoverflow.com/q/28962453/5325862), [here](https://stackoverflow.com/q/50859765/5325862), and [here](https://stackoverflow.com/q/54964279/5325862) – camille Aug 12 '19 at 17:12
  • Thanks camille. I will check out these sources and let you know if I succeeded. – deschen Aug 12 '19 at 17:41
  • @camille Sorry, I'm still struggling, so with the help of your first link I managed to read in a shape file, the problem of the mentioned solution there is that it calculates the centroids based on the countries and I don't know a way to calculate the centroids based on the continents. The second link, however, at least shows a solution how to get the mean position of each continent, but it's too off. So I'm wondering (still looking at the first link) how to calculate the centroid per continent. – deschen Aug 13 '19 at 14:49
  • If you have 2 shapefiles, one where each polygon is a country and one where each polygon is a continent, calculating the centroids of each polygon shouldn't have any different procedures – camille Aug 13 '19 at 14:52
  • True, although it's difficult to find a nice continental shape file. It seems that shapefiles are mostly done for countries, so I was wondering how to aggregate these polygons into continental ones for which I then could calculate the centroid. – deschen Aug 13 '19 at 15:15
  • In that case, your first step would be to take a shapefile of countries and dissolve / union it into continents. There should be several SO posts on this already as well. When I googled "world continent shapefile" I found [this one](https://apps.gis.ucla.edu/geodata/dataset/continent_ln) – camille Aug 13 '19 at 19:54

1 Answers1

1

Ok, with the help of some links I found two potential solutions:

Labeling center of map polygons in R ggplot

https://gis.stackexchange.com/questions/63577/joining-polygons-in-r/273515

As @camille mentioned, it basically boils down to reshaping/dissolving the original country shape file into continents.

An alternative is to manually create a small data frame where I put in the coordinate positions of each continent and add is as a text layer to a plot.

Here are the two solutions for combining/dissolving the country shapefile to a continent one:

1. Solution: dissolving in the original shaepfile / polygons object

library(rgdal)
library(broom)
library(ggplot2)
library(svglite)
library(tidyverse)
library(maptools)
library(raster)
library(rgeos)


### First part is about downloading shapefiles

# load shape files
# download.file("http://naciscdn.org/naturalearth/packages/natural_earth_vector.zip",
#               "world maps.zip")
# 
# unzip("folder\\world maps.zip",
#       exdir = "folder\\Raw maps from zip")

### Next part is bringing the world data into the right shape and enrich with the my results
###

# read in the shape file
world = readOGR(dsn   = "folder\\Raw maps from zip\\110m_cultural",
                layer = "ne_110m_admin_0_countries")

# Reshape the world data so that polygons are continents not countries
world_id    = world@data$CONTINENT
world_union = unionSpatialPolygons(world, world_id)

# Bring it into tidy format
world_fortified = tidy(world_union, region = "CONTINENT")

# Here I create some dummy survey results
results = data.frame(id             = c("Africa", "Asia", "Europe", "North America", "Oceania", "South America"),
                     kpi            = c(20, 30, 50, 50, 60, 70),
                     continent_long = c(15, 80, 20, -100, 150, -60),
                     continent_lat  = c(15, 35, 50, 40, -25, -15),
                     stringsAsFactors = F)

# Combine world map with results and drop Antarctica and seaven Seas
world_for_plot = world_fortified %>%
  left_join(., results, by = "id") %>%
  filter(!is.na(kpi))

### plot the results.

# Let's create the plot first wit data and let's care about the labels later
plain <- theme(
  axis.text = element_blank(),
  axis.line = element_blank(),
  axis.ticks = element_blank(),
  panel.border = element_blank(),
  panel.grid = element_blank(),
  axis.title = element_blank(),
  panel.background = element_rect(fill = "transparent"),
  plot.background = element_rect(fill = "transparent"),
  plot.title = element_text(hjust = 0.5)
)

# This is the actual results plot with different colours based on the results
raw_plot = ggplot(data = world_for_plot,
                  aes(x = long,
                  y = lat,
                  group = group)) +
  geom_polygon(aes(fill = kpi)) +
  coord_equal(1.3) +
  scale_fill_distiller(palette = "RdYlGn", direction = 1) +
  labs(fill = "kpi") +
  plain

## Now automatically adding label positions form the shapefile

# We start with getting the centroid positions of each continent and delete the continents we don't have
position = coordinates(world_union)

position = data.frame(position, row.names(position))
names(position) = c("long", "lat", "id")

position = position %>%
  filter(id %in% world_for_plot$id)

# We can now refer to this new data in our previously created plot object
final_plot = raw_plot +
  geom_text(data = position,
            aes(label = id,
                x = long,
                y = lat,
                group = id))

# But we can also put in the continent coordinates manually. I already created some coordinates in the results object
# So we can easily use this data instead of the above calculated positions.
final_plot = raw_plot +
  geom_text(data = results,
            aes(label = id,
                x = continent_long,
                y = continent_lat,
                group = id))

2. Solution: Using sf objects in a more data frame like way

library(sf)
library(tidyverse)
library(ggplot2)

# I dropped the part of downloading the shapefile here. See solution 1 for that.

world = read_sf(dsn   = "folder\\Raw maps from zip\\110m_cultural",
                layer = "ne_110m_admin_0_countries")

# Next we just do some tidy magic and group the data by CONTINENT and get the respective coordinates in a long list
continents = world %>%
  group_by(CONTINENT) %>%
  summarise(.)

# Here I create some dummy survey results
results = data.frame(CONTINENT      = c("Africa", "Asia", "Europe", "North America", "Oceania", "South America"),
                     kpi         = c(20, 30, 50, 50, 60, 70),
                     continent_long = c(15, 80, 20, -100, 150, -60),
                     continent_lat  = c(15, 35, 50, 40, -25, -15),
                     stringsAsFactors = F)

# Now let's join the continent data with the results
world_for_plot = continents %>%
  left_join(., results, by = c("CONTINENT")) %>%
  filter(!is.na(kpi))

### Now we can plot the results.

# Let's create the plot first with data and let's care about the labels later
plain <- theme(
  axis.text = element_blank(),
  axis.line = element_blank(),
  axis.ticks = element_blank(),
  panel.border = element_blank(),
  panel.grid = element_blank(),
  axis.title = element_blank(),
  panel.background = element_rect(fill = "transparent"),
  plot.background = element_rect(fill = "transparent"),
  plot.title = element_text(hjust = 0.5)
)

# This is the actual results plot with different colours based on the results
raw_plot = ggplot(data = world_for_plot) +
  geom_sf(aes(fill = kpi),
          colour=NA) +
  coord_sf() +
  scale_fill_distiller(palette = "RdYlGn", direction = 1) +
  plain

# Now we can add the labels
final_plot = raw_plot +
  geom_sf_text(aes(label=CONTINENT))

# We could also use our own label positions
final_plot = raw_plot +
  geom_text(aes(label = CONTINENT,
                   x = continent_long,
                   y = continent_lat,
                   group = CONTINENT))

Happy to hear your thoughts about it.

Please note that the plot below is the one where I actually manually positioned the labels.

enter image description here

deschen
  • 10,012
  • 3
  • 27
  • 50