rggplot2r-maptools

R map - poor representation of land borders


I map the southern part of the South hemisphere. My issue is Australia which has poorly drawn borders.

My data :

library("maptools")
library("ggplot2")
library("tidyverse")

ylim_map <- c(-90, -30)
xlim_map <- c(-180, 180)
world <- maps::map("world", fill=TRUE, plot=FALSE, ylim = ylim_map)

Convert data in correct format for ggplot :

IDs <- sapply(strsplit(world$names, ":"), function(x) x[1])
world <- map2SpatialPolygons(world, IDs = IDs, 
                             proj4string = CRS("+proj=longlat +datum=WGS84"))
world_map <- fortify(world)
world_map <- world_map[which(between(world_map$lat, ylim_map[1], ylim_map[2]) &
                               between(world_map$lon, xlim_map[1], xlim_map[2])),]

And my plot :

ggplot() +

  coord_map("orthographic", orientation = c(-90, 0, 0), 
            xlim = xlim_map, ylim = c(ylim_map[1], ylim_map[2] + 10)) +

  geom_map(data = world_map, map = world_map,
           aes(x = long, y = lat, map_id = id), fill = "black") +

  geom_text(aes(x = 180, y = ylim_map[2]+5, label = "180°E"), color = "black") +
  geom_text(aes(x = 90, y = ylim_map[2]+5, label = "90°E"), angle = -90, color = "black") +
  geom_text(aes(x = 0, y = ylim_map[2]+5, label = "0°"), color = "black") +
  geom_text(aes(x = -90, y = ylim_map[2]+5, label = "90°W"), angle = 90, color = "black") +

  labs(y = "", x = "") +

  # Theme
  theme(text = element_text(size = 20),
        panel.background = element_blank(),
        axis.title = element_blank(),
        axis.text = element_blank(),
        axis.ticks = element_blank(),
        axis.line = element_blank(),
        aspect.ratio = 1) 

enter image description here


Solution

  • TLDR:

    You need to close your polygons.

    Explanation:

    Let's trim away extraneous code & zoom in onto Australia. (Though actually the problem exists for Africa & South America as well; it's just not as obvious there...)

    We can see that the top line is misbehaving. It's intersecting with the coastline further down south, rather than sticking to its correct latitude level:

    ggplot() +
      coord_map("orthographic", orientation = c(-40, 130, 0)) +
      geom_map(data = world_map, map = world_map,
               aes(x = long, y = lat, map_id=id), 
               fill = "darkgrey") +
      theme_bw()
    

    illustration 1

    Now a geom_map layer is essentially plotting polygons, and ?geom_polygon states:

    Polygons are very similar to paths (as drawn by geom_path()) except that the start and end points are connected and the inside is coloured by fill. The group aesthetic determines which cases are connected together into a polygon.

    If we replace the geom_map layer with its geom_polygon / geom_path equivalents, the situation becomes much more obvious: the polygon corresponding to Australia has no top line. Instead, the path starts at the one corner and ends at the opposite corner. geom_polygon connects them with a straight line, which may intersect other lines when the coordinate system isn't linear (and coord_map isn't):

    ggplot() +
      coord_map("orthographic", 
                orientation = c(-40, 130, 0)) +
      geom_polygon(data = world_map,
                   aes(x = long, y = lat, group = group), 
                   fill = "lightgrey") +
      geom_path(data = world_map,
                aes(x = long, y = lat, group = group)) +
      theme_bw()
    

    illustration 2

    Solution:

    We can manually close each polygon by repeating its first point at the end. (For polygons that are already closed, this has no additional effect.)

    library(dplyr)
    
    world_map2 <- world_map %>%
      group_by(group) %>%              # each group corresponds to a unique polygon
      arrange(order) %>%               # sort points in the appropriate sequence
      slice(c(1:n(), 1)) %>%           # repeat first row after last row
      mutate(order = seq(1, n())) %>%  # define new order for n+1 rows
      ungroup()
    

    Check that the polygons are now closed, & the top line for Australia now traces its latitude level nicely:

    ggplot() +
      coord_map("orthographic", 
                orientation = c(-40, 130, 0)) +
      geom_polygon(data = world_map2,
                   aes(x = long, y = lat, group = group), 
                   fill = "lightgrey") +
      geom_path(data = world_map2,
                aes(x = long, y = lat, group = group)) +
      theme_bw()
    

    illustration 3

    Applying this to the original use case:

    ggplot() +
    
      coord_map("orthographic", orientation = c(-90, 0, 0), 
                xlim = xlim_map, ylim = c(ylim_map[1], ylim_map[2] + 10)) +
    
      geom_map(data = world_map2, map = world_map2,
               aes(x = long, y = lat, map_id = id), fill = "black") +
    
      geom_text(aes(x = 180, y = ylim_map[2]+5, label = "180°E"), color = "black") +
      geom_text(aes(x = 90, y = ylim_map[2]+5, label = "90°E"), angle = -90, color = "black") +
      geom_text(aes(x = 0, y = ylim_map[2]+5, label = "0°"), color = "black") +
      geom_text(aes(x = -90, y = ylim_map[2]+5, label = "90°W"), angle = 90, color = "black") +
    
      labs(y = "", x = "") +
    
      # Theme
      theme(text = element_text(size = 20),
            panel.background = element_blank(),
            axis.title = element_blank(),
            axis.text = element_blank(),
            axis.ticks = element_blank(),
            axis.line = element_blank(),
            aspect.ratio = 1) 
    

    result