rggplot2mapscoordinate-systemsgeom-sf

Closed boundaries on a map when using geom_sf to cross dateline


I am trying to make a Robinson-projection map that is not centered at longitude 0, and crosses both sides of the 180° line.

My code is below:

library("rnaturalearthdata")
world <- ne_countries(scale = "medium", returnclass = "sf")
robinson = "+proj=robin +lon_0=-90 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs"
transformed = st_transform(world, robinson)
ggplot(data=transformed) + geom_sf(fill = "gray", colour = "black")+theme_bw()

That generates this map, which is oriented correctly, except all the split boundaries are "inside out" and wrap across the whole map.

enter image description here

There is this related question from 9 years ago, but I am hoping that with recent package updates, there is a less complicated solution, which will allow us to plot our lat/lon data-points using geom_point() without additional transformations.

EDIT #2: The solution by L. Tyrone works great for me. To plot points using this method, you will have to transform them from a data frame to an sf object with the same Coord Ref System, or else the lat/lon is interpreted as meters, and ends up indistinguishable from 0,0:

# Transform with fake data...
fakedata=data.frame(lat=c(-10,-20,80),lon=c(45,-100,120),tag=c("indian","tropical","arctic"))
transpoint = st_as_sf(fakedata,coords=c("lon","lat"),crs=4326)
dtran = st_transform(transpoint,robinson)

Then you can plot them by adding the special geometry= and stat= declarations to geom_point or even geom_text_repel:

geom_point(data=dtran,aes(geometry=geometry,color=tag),stat="sf_coordinates") +
geom_text_repel(data=dtran,aes(geometry=geometry,label=tag),stat="sf_coordinates")

Final Result: successful plot of fake data onto map


Solution

  • You need to 'break' any polygons that cross 180 prior to projecting them. The st_break_antimeridian() function from the sf package is the easiest way to achieve this. It will throw a warning, which you can safely ignore.

    library(rnaturalearth)
    library(sf)
    library(dplyr)
    library(ggplot2)
    
    world <- ne_countries(scale = "medium", returnclass = "sf") %>%
      st_set_crs(4326)
    
    robinson <- "+proj=robin +lon_0=-90 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs"
    
    world_robinson_90w <- world %>%
      st_break_antimeridian(lon_0 = -90) %>%
      st_transform(crs = robinson)
    
    ggplot() +
      geom_sf(data = world_robinson_90w)
    

    result