Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In geom_sf_text, how to nudge x and y in aesthetics?

Tags:

r

ggplot2

sf

I would like to nudge the x and y position of text in ggplot2, using sf. The justification of text can be modified in aesthetics, which then requires nudging to be also modified similarly, but nudge_x and nudge_y can't be used in aesthetics.

Using sp and fortified data frames, I could modify x and y in aesthetics ( x=(x+hBump), y=(y+vBump) . I don't know if that is possible in sf.

Here are some data:

Cities <- read.table( text=
  "City long    lat hJust   vJust   hBump   vBump
  GrandJunction -108.550649 39.063871   1   1   -1100   -1000
  Gunnison  -106.925321 38.545825   0   1   0   -1000
  Tincup    -106.47836  38.754439   1   0   0   800",
  header=TRUE )

Convert to sf

Cities.sf <- st_as_sf(
  x = Cities, 
  coords = c("long", "lat"),
  crs = "+proj=longlat +datum=WGS84"
)

ggplot (the coord_sf is just so labels don't get off)

ggplot(Cities.sf) + 
  geom_sf() + 
  coord_sf(xlim = c(-109, -106.3), ylim = c(38.4, 39.2)) +
  geom_sf_text(aes(label = City, hjust = hJust, vjust = vJust))

enter image description here

Now if we nudge for one, the others may get messed up. Need aesthetic nudging!

ggplot(Cities.sf) + 
  geom_sf() + 
  coord_sf(xlim = c(-109, -106.3), ylim = c(38.4, 39.2)) +
  geom_sf_text(
    aes(label = City, hjust = hJust, vjust = vJust),
    nudge_x = 0.025, nudge_y = -0.025
  )

enter image description here Side question - I guess if I transform the object to another crs using meters, then the nudge units have to change from degrees to meters? That is the units that hBump and vBump are in.

like image 911
Jim Worrall Avatar asked Dec 29 '19 19:12

Jim Worrall


3 Answers

As far as I can tell, vectorized input for nudge_x and nudge_y is processed correctly.

library(ggplot2)
library(sf)
#> Linking to GEOS 3.7.2, GDAL 2.4.2, PROJ 5.2.0

Cities <- read.table(text=
  "City long    lat hJust   vJust   hBump   vBump
  GrandJunction -108.550649 39.063871   1   1   -1100   -1000
  Gunnison  -106.925321 38.545825   0   1   0   -1000
  Tincup    -106.47836  38.754439   1   0   0   800",
  header=TRUE)

Cities.sf <- st_as_sf(x = Cities, 
                      coords = c("long", "lat"),
                      crs = "+proj=longlat +datum=WGS84")

ggplot(Cities.sf) + geom_sf() + 
  coord_sf(xlim = c(-109, -106.3), ylim = c(38.4, 39.2)) +
  geom_sf_text(
    aes(label = City, hjust = hJust, vjust = vJust),
    nudge_x = c(-0.025, 0.025, -0.05),
    nudge_y = c(-0.025, -0.025, 0)
  )
#> Warning in st_point_on_surface.sfc(sf::st_zm(x)): st_point_on_surface may
#> not give correct results for longitude/latitude data

Created on 2020-01-01 by the reprex package (v0.3.0)

like image 139
Claus Wilke Avatar answered Nov 15 '22 00:11

Claus Wilke


@dc37 provided the key to doing what I want. For the added complexity of changing coordinate systems (since ggrepel can't use the geometry list column):

Transform to different CRS:

Cities.sf.t <- st_transform( Cities.sf, crs="+proj=laea +lon_0=-107.2 +lat_0=37.6 +x_0=0 +y_0=0 +ellps=WGS84 +units=m +no_defs" )

Then copy coordinates from the geometry list column and add them to data frame as columns X and Y, which can be used for text label:

Cities.sf.t <- cbind( Cities.sf.t, st_coordinates( Cities.sf.t ) )

And finally use that one data frame for the plot:

ggplot( Cities.sf.t ) + geom_sf() + 
  geom_text_repel( aes(x=X, y=Y, label=City) )

ggrepel allows use of h/vjust as aesthetics, but they don't behave as expected, and I can't make much sense of the documentation. I'll have to use trial and error or just accept where it wants to put the labels.

like image 26
Jim Worrall Avatar answered Nov 15 '22 00:11

Jim Worrall


I'm not really familiar with sf packages, but if you need to add labels on your plot and make them to not overlap your points, you can have the use of ggrepel package:

library(ggplot2)
library(sf)
library(ggrepel)
ggplot(Cities.sf) + geom_sf() + 
  coord_sf( xlim=c(-109, -106.3), ylim=c(38.4, 39.2 ) ) +
  geom_text_repel(inherit.aes = FALSE, data = Cities, aes(x = long, y = lat, label = City))

enter image description here

Does it look what you are looking for ?

like image 38
dc37 Avatar answered Nov 15 '22 01:11

dc37