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))
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
)
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.
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)
@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.
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))
Does it look what you are looking for ?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With