slowkow / ggrepel

:round_pushpin: Repel overlapping text labels away from each other in your ggplot2 figures.
https://ggrepel.slowkow.com
GNU General Public License v3.0
1.21k stars 94 forks source link

Map labels: keep geom_text position exactly and only repel other geom_text #89

Closed tiernanmartin closed 6 years ago

tiernanmartin commented 6 years ago

As was pointed out in #18, geom_text_repel seems like an ideal tool for adding labels to maps but it could probably be improved.

One minor aesthetic critique I have of the currently implementation is that all geom_text_repel objects are positioned so that they don't cover the underlying XY coordinate - that feature makes sense for scatter plots but not for map labels:

Could you add a parameter that would center the geom_text_repel objects on their XY coordinates unless that causes an overlap with another geom_text_repel object?

Here's an example of the type of label positioning that I'd like to be able to achieve with geom_text_repel:

Thanks!

Reprex + Session info ``` r library(tidyverse) # devtools::install_github('tidyverse/ggplot2') library(sf) ## Linking to GEOS 3.6.1, GDAL 2.2.0, proj.4 4.9.3 library(albersusa) # devtools::install_github('hrbrmstr/albersusa') library(ggrepel) usa_sf <- st_as_sf(usa_composite("laea")) %>% mutate( CENTROID = map(geometry, st_centroid), COORDS = map(CENTROID, st_coordinates), COORDS_X = map_dbl(COORDS, 1), COORDS_Y = map_dbl(COORDS, 2) ) %>% as_tibble() %>% st_as_sf() ggplot(data = usa_sf) + geom_sf() + geom_text_repel(mapping = aes(COORDS_X, COORDS_Y, label = name), size = 3, min.segment.length = 0) + coord_sf(crs = st_crs(usa_sf), datum = NA) + theme_void() ``` ``` r devtools::session_info() ## Session info ------------------------------------------------------------- ## setting value ## version R version 3.4.2 (2017-09-28) ## system x86_64, mingw32 ## ui RTerm ## language (EN) ## collate English_United States.1252 ## tz America/Los_Angeles ## date 2017-12-01 ## Packages ----------------------------------------------------------------- ## package * version date source ## albersusa * 0.3.0 2017-11-30 Github (hrbrmstr/albersusa@82220d3) ## assertthat 0.2.0 2017-04-11 CRAN (R 3.4.1) ## backports 1.1.1 2017-09-25 CRAN (R 3.4.1) ## base * 3.4.2 2017-09-28 local ## bindr 0.1 2016-11-13 CRAN (R 3.4.1) ## bindrcpp * 0.2 2017-06-17 CRAN (R 3.4.1) ## bitops 1.0-6 2013-08-17 CRAN (R 3.4.1) ## broom 0.4.2 2017-02-13 CRAN (R 3.4.0) ## cellranger 1.1.0 2016-07-27 CRAN (R 3.4.1) ## class 7.3-14 2015-08-30 CRAN (R 3.4.2) ## classInt 0.1-24 2017-04-16 CRAN (R 3.4.2) ## cli 1.0.0 2017-11-05 CRAN (R 3.4.2) ## colorspace 1.3-2 2016-12-14 CRAN (R 3.4.1) ## compiler 3.4.2 2017-09-28 local ## crayon 1.3.4 2017-11-16 Github (r-lib/crayon@b5221ab) ## datasets * 3.4.2 2017-09-28 local ## DBI 0.7 2017-06-18 CRAN (R 3.4.1) ## devtools 1.13.4 2017-11-09 CRAN (R 3.4.2) ## digest 0.6.12 2017-01-27 CRAN (R 3.4.1) ## dplyr * 0.7.4 2017-09-28 CRAN (R 3.4.2) ## e1071 1.6-8 2017-02-02 CRAN (R 3.4.2) ## evaluate 0.10.1 2017-06-24 CRAN (R 3.4.2) ## forcats * 0.2.0 2017-01-23 CRAN (R 3.4.1) ## foreign 0.8-69 2017-06-22 CRAN (R 3.4.2) ## ggplot2 * 2.2.1.9000 2017-11-30 Github (tidyverse/ggplot2@505e4bf) ## ggrepel * 0.7.0 2017-11-16 Github (slowkow/ggrepel@680db17) ## glue 1.2.0 2017-10-29 CRAN (R 3.4.2) ## graphics * 3.4.2 2017-09-28 local ## grDevices * 3.4.2 2017-09-28 local ## grid 3.4.2 2017-09-28 local ## gtable 0.2.0 2016-02-26 CRAN (R 3.4.1) ## haven 1.1.0 2017-07-09 CRAN (R 3.4.1) ## hms 0.3 2016-11-22 CRAN (R 3.4.1) ## htmltools 0.3.6 2017-04-28 CRAN (R 3.4.1) ## httr 1.3.1 2017-08-20 CRAN (R 3.4.1) ## jsonlite 1.5 2017-06-01 CRAN (R 3.4.1) ## knitr 1.17 2017-08-10 CRAN (R 3.4.2) ## lattice 0.20-35 2017-03-25 CRAN (R 3.4.2) ## lazyeval 0.2.1 2017-10-29 CRAN (R 3.4.2) ## lubridate 1.7.1 2017-11-03 CRAN (R 3.4.2) ## magrittr 1.5 2014-11-22 CRAN (R 3.4.1) ## maptools 0.9-2 2017-03-25 CRAN (R 3.4.2) ## memoise 1.1.0 2017-04-21 CRAN (R 3.4.2) ## methods * 3.4.2 2017-09-28 local ## mnormt 1.5-5 2016-10-15 CRAN (R 3.4.1) ## modelr 0.1.1 2017-07-24 CRAN (R 3.4.1) ## munsell 0.4.3 2016-02-13 CRAN (R 3.4.1) ## nlme 3.1-131 2017-02-06 CRAN (R 3.4.2) ## parallel 3.4.2 2017-09-28 local ## pkgconfig 2.0.1 2017-03-21 CRAN (R 3.4.1) ## plyr 1.8.4 2016-06-08 CRAN (R 3.4.1) ## psych 1.7.8 2017-09-09 CRAN (R 3.4.2) ## purrr * 0.2.4 2017-10-18 CRAN (R 3.4.2) ## R6 2.2.2 2017-06-17 CRAN (R 3.4.1) ## Rcpp 0.12.14 2017-11-23 CRAN (R 3.4.2) ## RCurl 1.95-4.8 2016-03-01 CRAN (R 3.4.1) ## readr * 1.1.1 2017-05-16 CRAN (R 3.4.1) ## readxl 1.0.0 2017-04-18 CRAN (R 3.4.1) ## reshape2 1.4.2 2016-10-22 CRAN (R 3.4.1) ## rgdal 1.2-16 2017-11-21 CRAN (R 3.4.2) ## rgeos 0.3-26 2017-10-31 CRAN (R 3.4.2) ## rlang 0.1.4 2017-11-05 CRAN (R 3.4.2) ## rmarkdown 1.8 2017-11-17 CRAN (R 3.4.2) ## rprojroot 1.2 2017-01-16 CRAN (R 3.4.2) ## rvest 0.3.2 2016-06-17 CRAN (R 3.4.1) ## scales 0.5.0.9000 2017-11-30 Github (hadley/scales@d767915) ## sf * 0.5-5 2017-10-31 CRAN (R 3.4.2) ## sp 1.2-5 2017-06-29 CRAN (R 3.4.1) ## stats * 3.4.2 2017-09-28 local ## stringi 1.1.5 2017-04-07 CRAN (R 3.4.1) ## stringr * 1.2.0 2017-02-18 CRAN (R 3.4.1) ## tibble * 1.3.4 2017-08-22 CRAN (R 3.4.1) ## tidyr * 0.7.2 2017-10-16 CRAN (R 3.4.2) ## tidyverse * 1.2.1 2017-11-14 CRAN (R 3.4.2) ## tools 3.4.2 2017-09-28 local ## udunits2 0.13 2016-11-17 CRAN (R 3.4.1) ## units 0.4-6 2017-08-27 CRAN (R 3.4.1) ## utils * 3.4.2 2017-09-28 local ## withr 2.1.0.9000 2017-11-30 Github (jimhester/withr@fe81c00) ## XML 3.98-1.9 2017-06-19 CRAN (R 3.4.1) ## xml2 1.1.1 2017-01-24 CRAN (R 3.4.1) ## yaml 2.1.14 2016-11-12 CRAN (R 3.4.1) ```
slowkow commented 6 years ago

Thanks for opening this issue! Thanks also for the code and figures.

To avoid repulsion from the data points, set point.padding = NA.

To get geom_sf, I had to install the latest development version of ggplot2 instead of the one from CRAN.

I tried to modify your code to produce something similar to your desired output. I hope you can take it from here!

library(tidyverse) # devtools::install_github('tidyverse/ggplot2')
library(sf)
## Linking to GEOS 3.6.1, GDAL 2.2.0, proj.4 4.9.3
library(albersusa) # devtools::install_github('hrbrmstr/albersusa')
library(ggrepel) # devtools::install_github("tidyverse/ggplot2")
# geom_sf is in the dev version of ggplot2

usa_sf <-
  st_as_sf(usa_composite("laea")) %>%
  mutate(
    CENTROID = map(geometry, st_centroid),
    COORDS = map(CENTROID, st_coordinates),
    COORDS_X = map_dbl(COORDS, 1),
    COORDS_Y = map_dbl(COORDS, 2)
  ) %>%
  as_tibble() %>%
  st_as_sf()

usa_sf$nudge_x <- 0
usa_sf$nudge_y <- 0

x_range <- abs(Reduce("-", range(usa_sf$COORDS_X)))
y_range <- abs(Reduce("-", range(usa_sf$COORDS_Y)))

ix <- usa_sf$name %in% c("New Hampshire", "Vermont", "Massachusetts")
usa_sf$nudge_x[ix] <- -1 * 0.15 * x_range
usa_sf$nudge_y[ix] <- 1 * 0.15 * y_range

ix <- usa_sf$name %in% c(
  "Massachusetts",
  "Rhode Island", "Connecticut", "New Jersey", "West Virginia",
  "Maryland", "Delaware", "District of Columbia"
)
usa_sf$nudge_x[ix] <- 1 * 0.2 * x_range
usa_sf$nudge_y[ix] <- -1 * 0.15 * y_range

ggplot(data = usa_sf) +
  geom_sf() +
  geom_text_repel(
    mapping = aes(
      x = COORDS_X,
      y = COORDS_Y,
      label = name
    ),
    nudge_x = usa_sf$nudge_x,
    nudge_y = usa_sf$nudge_y,
    size = 3,
    min.segment.length = 0,
    point.padding = NA,
    segment.color = "grey50"
  ) +
  coord_sf(crs = st_crs(usa_sf), datum = NA) +
  theme_void() +
  xlim(min(usa_sf$COORDS_X) * 1.1, max(usa_sf$COORDS_X) * 1.15)

ggsave("issue89.png", width = 12, height = 9)

issue89

ifeomaozo commented 5 years ago

I have a similar problem as the previous OP. I too would like to repel text clustered at the middle of the map but I am unsure how to set the nudge values or select the specific values. I tried following your example but it didn't work well for me. nudge example_health district.pdf

See code. `DSshape_sf <- st_read("burkina_shapefiles/Health Districts Burkina")%>% mutate( CENTROID = map(geometry, st_centroid), COORDS = map(CENTROID, st_coordinates), COORDS_X = map_dbl(COORDS, 1), COORDS_Y = map_dbl(COORDS, 2) )

DSshape_sf$nudge_x <- 0 DSshape_sf$nudge_y <- 0

x_range <- abs(Reduce("-", range(DSshape_sf$COORDS_X))) y_range <- abs(Reduce("-", range(DSshape_sf$COORDS_Y)))

ix <- DSshape_sf$NOMDEP %in% c("NONGR-MASSOUM", "BASKUY") DSshape_sf$nudge_x[ix] <- 1 0.15 x_range DSshape_sf$nudge_y[ix] <- -1 0.15 y_range

ggplot(data = DSshape_sf) + geom_sf() + geom_text_repel( mapping = aes( x = COORDS_X, y = COORDS_Y, label = NOMDEP ), nudge_x = DSshape_sf$nudge_x, nudge_y = DSshape_sf$nudge_y, size = 3, min.segment.length = 0, point.padding = NA, segment.color = "blue" ) #+`

The health district shapefiles are in this github repo https://github.com/ifeomaozo/BF-shape-file

sem-22 commented 1 year ago

Thank you for posting this code and output! It took me forever just to know what to call these little lines to show the states. I now know they're called "leader lines". I'm having some issues with this code. I'm running the exact same code, but leader lines come up on ALL of the states.

I only superficially understand how this code is working so I can't figure out where I'm going wrong. Also when I open the map to see the full thing the labels disappear?

library(sf)
## Linking to GEOS 3.6.1, GDAL 2.2.0, proj.4 4.9.3
library(albersusa) # devtools::install_github('hrbrmstr/albersusa')
library(ggrepel) # devtools::install_github("tidyverse/ggplot2")
# geom_sf is in the dev version of ggplot2

usa_sf <-
  st_as_sf(usa_composite("laea")) %>%
  mutate(
    CENTROID = map(geometry, st_centroid),
    COORDS = map(CENTROID, st_coordinates),
    COORDS_X = map_dbl(COORDS, 1),
    COORDS_Y = map_dbl(COORDS, 2)
  ) %>%
  as_tibble() %>%
  st_as_sf()

usa_sf$nudge_x <- 0
usa_sf$nudge_y <- 0

x_range <- abs(Reduce("-", range(usa_sf$COORDS_X)))
y_range <- abs(Reduce("-", range(usa_sf$COORDS_Y)))

ix <- usa_sf$name %in% c("New Hampshire", "Vermont", "Massachusetts")
usa_sf$nudge_x[ix] <- -1 * 0.15 * x_range
usa_sf$nudge_y[ix] <- 1 * 0.15 * y_range

ix <- usa_sf$name %in% c(
  "Massachusetts",
  "Rhode Island", "Connecticut", "New Jersey", "West Virginia",
  "Maryland", "Delaware", "District of Columbia"
)
usa_sf$nudge_x[ix] <- 1 * 0.2 * x_range
usa_sf$nudge_y[ix] <- -1 * 0.15 * y_range

leaderlines <- ggplot(data = usa_sf) +
  geom_sf() +
  geom_text_repel(
    mapping = aes(
      x = COORDS_X,
      y = COORDS_Y,
      label = name
    ),
    nudge_x = usa_sf$nudge_x,
    nudge_y = usa_sf$nudge_y,
    size = 3,
    min.segment.length = 0,
    point.padding = NA,
    segment.color = "grey50"
  ) +
  coord_sf(crs = st_crs(usa_sf), datum = NA) +
  theme_void() +
  xlim(min(usa_sf$COORDS_X) * 1.1, max(usa_sf$COORDS_X) * 1.15)
leaderlines

My map:

Screen Shot 2022-09-14 at 3 40 05 PM
slowkow commented 1 year ago

Hi @sem-22

You might want to browse the Examples page to see some of the many ways we can customize our plots.

I tried running your code and modifying it slightly based on your comments. I only changed point.padding = NA to point.size = NA.

library(sf)
#> Linking to GEOS 3.8.1, GDAL 3.2.1, PROJ 7.2.1
## Linking to GEOS 3.6.1, GDAL 2.2.0, proj.4 4.9.3
library(albersusa) # devtools::install_github('hrbrmstr/albersusa')
library(ggrepel) # devtools::install_github("tidyverse/ggplot2")
#> Loading required package: ggplot2
# geom_sf is in the dev version of ggplot2
library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union
library(purrr)

usa_sf <-
  st_as_sf(usa_composite("laea")) %>%
  mutate(
    CENTROID = map(geometry, st_centroid),
    COORDS = map(CENTROID, st_coordinates),
    COORDS_X = map_dbl(COORDS, 1),
    COORDS_Y = map_dbl(COORDS, 2)
  ) %>%
  as_tibble() %>%
  st_as_sf()
#> Warning in showSRID(uprojargs, format = "PROJ", multiline = "NO", prefer_proj =
#> prefer_proj): Discarded datum unknown in Proj4 definition
#> Warning in spTransform(xSP, CRSobj, ...): NULL source CRS comment, falling back
#> to PROJ string
#> Warning in wkt(obj): CRS object has no comment

usa_sf$nudge_x <- 0
usa_sf$nudge_y <- 0

x_range <- abs(Reduce("-", range(usa_sf$COORDS_X)))
y_range <- abs(Reduce("-", range(usa_sf$COORDS_Y)))

ix <- usa_sf$name %in% c("New Hampshire", "Vermont", "Massachusetts")
usa_sf$nudge_x[ix] <- -1 * 0.15 * x_range
usa_sf$nudge_y[ix] <- 1 * 0.15 * y_range

ix <- usa_sf$name %in% c(
  "Massachusetts",
  "Rhode Island", "Connecticut", "New Jersey", "West Virginia",
  "Maryland", "Delaware", "District of Columbia"
)
usa_sf$nudge_x[ix] <- 1 * 0.2 * x_range
usa_sf$nudge_y[ix] <- -1 * 0.15 * y_range

ggplot(data = usa_sf) +
  geom_sf() +
  geom_text_repel(
    mapping = aes(
      x = COORDS_X,
      y = COORDS_Y,
      label = name
    ),
    nudge_x = usa_sf$nudge_x,
    nudge_y = usa_sf$nudge_y,
    size = 3,
    min.segment.length = 0,
    point.size = NA,
    segment.color = "grey50"
  ) +
  coord_sf(crs = st_crs(usa_sf), datum = NA) +
  theme_void() +
  xlim(min(usa_sf$COORDS_X) * 1.1, max(usa_sf$COORDS_X) * 1.15)

Created on 2022-09-14 by the reprex package (v2.0.1)

Session info ``` r sessioninfo::session_info() #> ─ Session info ─────────────────────────────────────────────────────────────── #> setting value #> version R version 4.0.3 (2020-10-10) #> os macOS Catalina 10.15.7 #> system x86_64, darwin17.0 #> ui X11 #> language (EN) #> collate en_US.UTF-8 #> ctype en_US.UTF-8 #> tz America/New_York #> date 2022-09-14 #> #> ─ Packages ─────────────────────────────────────────────────────────────────── #> package * version date lib source #> albersusa * 0.4.1 2022-09-14 [1] Github (hrbrmstr/albersusa@07aa87f) #> assertthat 0.2.1 2019-03-21 [2] CRAN (R 4.0.2) #> backports 1.2.1 2020-12-09 [1] CRAN (R 4.0.2) #> class 7.3-19 2021-05-03 [1] CRAN (R 4.0.2) #> classInt 0.4-3 2020-04-07 [2] CRAN (R 4.0.2) #> cli 3.0.1 2021-07-17 [1] CRAN (R 4.0.2) #> colorspace 2.0-2 2021-06-24 [1] CRAN (R 4.0.2) #> crayon 1.4.1 2021-02-08 [1] CRAN (R 4.0.2) #> curl 4.3.2 2021-06-23 [1] CRAN (R 4.0.2) #> DBI 1.1.1 2021-01-15 [1] CRAN (R 4.0.2) #> digest 0.6.28 2021-09-23 [1] CRAN (R 4.0.2) #> dplyr * 1.0.7 2021-06-18 [1] CRAN (R 4.0.2) #> e1071 1.7-9 2021-09-16 [1] CRAN (R 4.0.2) #> ellipsis 0.3.2 2021-04-29 [1] CRAN (R 4.0.2) #> evaluate 0.14 2019-05-28 [2] CRAN (R 4.0.1) #> fansi 0.5.0 2021-05-25 [1] CRAN (R 4.0.2) #> farver 2.1.0 2021-02-28 [1] CRAN (R 4.0.2) #> fastmap 1.1.0 2021-01-25 [1] CRAN (R 4.0.2) #> foreign 0.8-81 2020-12-22 [1] CRAN (R 4.0.2) #> fs 1.5.0 2020-07-31 [1] CRAN (R 4.0.2) #> generics 0.1.0 2020-10-31 [2] CRAN (R 4.0.2) #> ggplot2 * 3.3.6 2022-05-03 [1] CRAN (R 4.0.3) #> ggrepel * 0.9.1 2021-01-15 [1] CRAN (R 4.0.2) #> glue 1.4.2 2020-08-27 [2] CRAN (R 4.0.2) #> gtable 0.3.0 2019-03-25 [2] CRAN (R 4.0.2) #> highr 0.9 2021-04-16 [1] CRAN (R 4.0.2) #> htmltools 0.5.2 2021-08-25 [1] CRAN (R 4.0.2) #> httr 1.4.2 2020-07-20 [2] CRAN (R 4.0.2) #> KernSmooth 2.23-20 2021-05-03 [1] CRAN (R 4.0.2) #> knitr 1.36 2021-09-29 [1] CRAN (R 4.0.2) #> lattice 0.20-45 2021-09-22 [1] CRAN (R 4.0.2) #> lifecycle 1.0.1 2021-09-24 [1] CRAN (R 4.0.2) #> magrittr 2.0.1.9000 2020-12-15 [1] Github (tidyverse/magrittr@bb1c86a) #> maptools 1.1-2 2021-09-07 [1] CRAN (R 4.0.2) #> mime 0.12 2021-09-28 [1] CRAN (R 4.0.2) #> munsell 0.5.0 2018-06-12 [2] CRAN (R 4.0.2) #> pillar 1.6.3 2021-09-26 [1] CRAN (R 4.0.2) #> pkgconfig 2.0.3 2019-09-22 [2] CRAN (R 4.0.2) #> proxy 0.4-26 2021-06-07 [1] CRAN (R 4.0.2) #> purrr * 0.3.4 2020-04-17 [2] CRAN (R 4.0.2) #> R.cache 0.15.0 2021-04-30 [1] CRAN (R 4.0.2) #> R.methodsS3 1.8.1 2020-08-26 [1] CRAN (R 4.0.2) #> R.oo 1.24.0 2020-08-26 [1] CRAN (R 4.0.2) #> R.utils 2.11.0 2021-09-26 [1] CRAN (R 4.0.2) #> R6 2.5.1 2021-08-19 [1] CRAN (R 4.0.2) #> Rcpp 1.0.7 2021-07-07 [1] CRAN (R 4.0.2) #> reprex 2.0.1 2021-08-05 [1] CRAN (R 4.0.2) #> rgdal 1.5-27 2021-09-16 [1] CRAN (R 4.0.2) #> rgeos 0.5-8 2021-09-22 [1] CRAN (R 4.0.2) #> rlang 1.0.4 2022-07-12 [1] CRAN (R 4.0.3) #> rmarkdown 2.11 2021-09-14 [1] CRAN (R 4.0.2) #> rstudioapi 0.13 2020-11-12 [2] CRAN (R 4.0.2) #> scales 1.2.0 2022-04-13 [1] CRAN (R 4.0.3) #> sessioninfo 1.1.1 2018-11-05 [2] CRAN (R 4.0.2) #> sf * 1.0-3 2021-10-07 [1] CRAN (R 4.0.2) #> sp 1.4-5 2021-01-10 [1] CRAN (R 4.0.2) #> stringi 1.7.5 2021-10-04 [1] CRAN (R 4.0.2) #> stringr 1.4.0 2019-02-10 [2] CRAN (R 4.0.2) #> styler 1.6.2 2021-09-23 [1] CRAN (R 4.0.2) #> tibble 3.1.5 2021-09-30 [1] CRAN (R 4.0.2) #> tidyselect 1.1.1 2021-04-30 [1] CRAN (R 4.0.2) #> units 0.7-2 2021-06-08 [1] CRAN (R 4.0.2) #> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.0.2) #> vctrs 0.3.8 2021-04-29 [1] CRAN (R 4.0.2) #> withr 2.4.2 2021-04-18 [1] CRAN (R 4.0.2) #> xfun 0.26 2021-09-14 [1] CRAN (R 4.0.2) #> xml2 1.3.2 2020-04-23 [2] CRAN (R 4.0.2) #> yaml 2.2.1 2020-02-01 [2] CRAN (R 4.0.2) #> #> [1] /Users/kamil/Library/R/4.0/library #> [2] /Library/Frameworks/R.framework/Versions/4.0/Resources/library ```
slowkow commented 1 year ago

@sem-22 said:

Also when I open the map to see the full thing the labels disappear?

I'm not sure what you mean, but I guess you might be experiencing the issue that I reported to RStudio a few years ago: https://github.com/rstudio/rstudio/issues/4330