Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

whole earth polygon for world map in ggplot2 (and sf)

Tags:

r

ggplot2

gis

sf

When plotting world maps there is a problem with ggplot2: it colours the whole background with the same colour, including the corners of the plot which aren't actually part of the globe, see the snapshot below produced by the following code (it uses bleading edge sf abd ggplot2 versions but the issue is generic, see the blog post mentioned below):

    #install.packages("devtools")
    #devtools::install_github("tidyverse/ggplot2")
    #devtools::install_github("edzer/sfr")

    library(ggplot2)
    library(sf)
    library(rnaturalearth)
    library(dplyr)

    theme_map <- function(...) {
      theme_minimal() +
      theme(
        text = element_text(family = "Ubuntu Regular", color = "#22211d"),
        axis.line = element_blank(),
        axis.text.x = element_blank(),
        axis.text.y = element_blank(),
        axis.ticks = element_blank(),
        axis.title.x = element_blank(),
        axis.title.y = element_blank(),
        panel.grid.minor = element_line(color = "#ebebe5", size = 0.2),
        panel.grid.major = element_line(color = "#ebebe5", size = 0.2),
        plot.background = element_rect(fill = "#f5f5f2", color = NA),
        panel.background = element_rect(fill = "#f5f5f2", color = NA),
        legend.background = element_rect(fill = "#f5f5f2", color = NA),
        panel.border = element_blank(),
        ...
      )
    }

    crs <- "+proj=laea +lat_0=52 +lon_0=10 +x_0=4321000 +y_0=3210000 +datum=WGS84 +units=m +no_defs"
    ctrys50m <- ne_countries(scale = 50, type = "countries", returnclass = "sf") %>%
       select(iso_a3, iso_n3, admin)

    ggplot() + 
      geom_sf(data = ctrys50m, alpha = 0.15, fill="grey") +
      coord_map() +
      coord_sf(crs = crs) +
      theme_map()

enter image description here

In order to be able to nicely plot Earth contour, in D3.js a special GeoJSON type, {type: "Sphere"} has been added, see this thread which can be seen in action here: it is the exterior whole Earth black border in the following snapshot:

enter image description here

The only trick I found in R/ggplot2 is the one published by Matt Strimas-Mackey, in his blog entry Mapping the Longest Commericial Flights in R, see the Bounding box and graticules section and the make_bbox and project_recenter functions.

These are quite a lot of code and I was wondering whether some sf or geom_sf code would make to a cleaner/simpler code, so I tried:

    # whole world WSG84 bounding box
    sphere <- ne_download(category = "physical", type = "wgs84_bounding_box", returnclass = "sf")
    sphere_laea <- st_transform(sphere, crs)
    ggplot() + 
      geom_sf(data = sphere, fill = "#D8F4FF") +
      coord_sf(crs = crs) +
      geom_sf(data = ctrys50m, alpha = 0.15, fill="grey") +
      coord_map() +
      coord_sf(crs = crs) +
      theme_map()

What I get is just an extra "anti-meridian" (note the line from North pole...) and no oceans filled with #D8F4FF... And the polygon is quite irregular at the bottom (the D3.js gurus did some smart adaptive resampling to increase the accuracy of projected lines...)

enter image description here

Any ideas about what's wrong in my attempt to get a whole world polygon for ggplot2 world maps? (Thanks for reading this far!)

like image 220
espinielli Avatar asked Apr 04 '17 12:04

espinielli


2 Answers

Following Dave's suggestion, I created a new graticule small enough so that the convex hull operation will result in a good enough approximation of the border of the globe. The following code gives very good results (see image after):

library(ggplot2)
library(sf)
library(rnaturalearth)
library(dplyr)

crs <- "+proj=laea +lat_0=52 +lon_0=10 +x_0=4321000 +y_0=3210000 +datum=WGS84 +units=m +no_defs"

ctrys50m <- ne_countries(scale = 50, type = "countries", returnclass = "sf") %>%
  select(iso_a3, iso_n3, admin)

sphere <- st_graticule(ndiscr = 10000, margin = 10e-6) %>%
  st_transform(crs = crs) %>%
  st_convex_hull() %>%
  summarise(geometry = st_union(geometry))

ggplot()  +
  geom_sf(data = sphere, fill = "#D8F4FF", alpha = 0.7) +
  geom_sf(data = ctrys50m, fill="grey") +
  theme_bw()

enter image description here

like image 91
espinielli Avatar answered Oct 20 '22 12:10

espinielli


I am also still in the process of discovering the magic of sf, so there may be more straightforward solutions to your problem. Nevertheless you might find this approach useful:

In order to extract the polygon for the earth's sphere, simply use the graticule of your desired projection, create a convex hull and unite the resulting polygons.

library(ggplot2)
library(sf)
library(rnaturalearth)
library(dplyr)

crs <- "+proj=laea +lat_0=52 +lon_0=10 +x_0=4321000 +y_0=3210000
        +datum=WGS84 +units=m +no_defs"

ctrys50m <- ne_countries(scale = 50, type = "countries", returnclass = "sf") %>%
  select(iso_a3, iso_n3, admin)

sphere <- st_graticule(st_transform(ctrys50m, crs = crs)) %>%
  st_convex_hull() %>%
  summarise(geometry = st_union(geometry))

ggplot()  +
  geom_sf(data = sphere, fill = "#D8F4FF", alpha = 0.7) +
  geom_sf(data = ctrys50m, fill="grey") +
  #coord_sf(crs = crs) + # not necessary because the crs from the first layer is being used.
  theme_bw()

This gives you the following plot, which still has some visible corners but may be sufficient depending on your purpose.

enter image description here

like image 45
Dave Avatar answered Oct 20 '22 12:10

Dave