Open yutannihilation opened 6 years ago
Yes, I'd be interested to include those functions in ggrepel if you think it makes sense, and we're all in agreement across ggplot2, ggrepel, and ggforce. Looks like you have a great start already! Thanks for opening the issue!
I think I need to study the *_sf_*
functions a bit to figure out exactly what is going on. I would have assumed that geom_text_repel()
should be sufficient without an extra function for spatial data, but clearly this is not correct.
Thanks for the quick reply. Glad to hear that!
I would have assumed that geom_text_repel() should be sufficient without an extra function for spatial data
I think you are right in some sense, but, you need to extract the coordinates by yourself (c.f. https://github.com/tidyverse/ggplot2/issues/2111) so we naturally want some shortcut for this. That is, *_sf_*
functions, in my understanding :)
Now geom_sf_text_repel()
and geom_sf_label_repel()
are basically possible with stat_sf_coordinates()
. Example:
# github version of ggplot2
library(ggplot2)
nc <- sf::st_read(system.file("shape/nc.shp", package="sf"))
#> Reading layer `nc' from data source `/Library/Frameworks/R.framework/Versions/3.5/Resources/library/sf/shape/nc.shp' using driver `ESRI Shapefile'
#> Simple feature collection with 100 features and 14 fields
#> geometry type: MULTIPOLYGON
#> dimension: XY
#> bbox: xmin: -84.32385 ymin: 33.88199 xmax: -75.45698 ymax: 36.58965
#> epsg (SRID): 4267
#> proj4string: +proj=longlat +datum=NAD27 +no_defs
#> Reading layer `nc' from data source `/path/to/sf/shape/nc.shp' using driver `ESRI Shapefile'
#> Simple feature collection with 100 features and 14 fields
#> geometry type: MULTIPOLYGON
#> dimension: XY
#> bbox: xmin: -84.32385 ymin: 33.88199 xmax: -75.45698 ymax: 36.58965
#> epsg (SRID): 4267
#> proj4string: +proj=longlat +datum=NAD27 +no_defs
ggplot(nc) +
geom_sf() +
ggrepel::geom_label_repel(
data = head(nc),
aes(label = NAME, geometry = geometry),
stat = "sf_coordinates",
min.segment.length = 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 2018-09-04 by the reprex package (v0.2.0).
@yutannihilation Do you think we should create two new functions?
geom_sf_text_repel()
geom_sf_label_repel()
I suppose you could say they are not needed anymore, since you've shown that we can use stat = "sf_coordinates"
successfully for many types of figures. On the other hand, it would be convenient to have a wrapper.
What do you think? I think it makes sense to add them.
Whether or not we add two more functions, I think the vignette for ggrepel needs an example showing how to put repulsive labels on a sf plot.
If you're interested, I'd be very happy to review your pull request and merge it in. If you prefer, I can try to do it myself, but I'd appreciate your comments to make sure I'm writing the functions the way you'd expect.
As an aside, you're also making me wonder if I should think about a stat like stat_repel()
to enable repulsive positioning for other types of elements besides text boxes... I think I need to spend more time studying the ggplot2 code before I'm ready for this.
Yes, I think these functions are worth adding, of course! I want to implement them if you are ok (Sorry for not yet starting on this...).
While it seems easy to implement them as simple wrappers, I'm wondering if I can add an option to prevent labels to overwrap the geometry. Maybe is this the same thing as your concept of stat_repel()
? I too have to study the ggplot2 code. 🤔
Ok, feel free to get started whenever you have time. Thanks for your help!
I like the idea of avoiding collision with the shape of the geometry. This might be a challenge to implement, but I have an inkling that there might be library we can use to do that.
Thanks, I will try :)
I am facing problems trying to get geom_label_repel
with a geom_sf
. One neat feature would be to also have it repel from sf features themselves. In my case, I'm trying to map NZ regions with the labels falling in the ocean.
Working on the same problem as @kendonB .
Any news on this front? have geom_sf_label/text_repel()
been implemented?
Thanks :)
@CeresBarros Did you try the code example by @yutannihilation ?
Did it meet your needs?
I did not - I wasn't sure if that was the current "solution". I'll try it :)
Just tried it, works great, thanks!
Just come across this issue - getting "Error: geom_label_repel requires the following missing aesthetics: x and y
" message when using geom_label_repel
with a geom_sf
plot.
I was really confused because there's nothing in the ggrepel
docs saying anything about geom_label_repel
requiring x
and y
aesthetics!
Searching the web for that error message didn't get me any useful info, so I came here to search.
@yutannihilation 's solution works great, but I do think this should be covered in the ggrepel
examples vignette. So that future users don't have to hunt around for the fix.
Would you welcome a PR to your vignette to include an example of this?
@francisbarton Contributions to the documentation are welcome! Please feel free to make a PR with your suggested changes or additions.
Hi, just to let you know that I would be one more who would appreciate the wrapper functions. =) The code from @yutannihilation worked nicely, thanks!
I'd be happy to review and merge a pull request that resolves this issue.
Not sure if this is the right place to bring this up, but I'm wondering if it's possible to selectively apply geom_sf_label_repel() to certain features in an sf object and geom_sf_label to other features in the same object? I'm currently trying to do this but unable. Can share photos or a reprex if that would help.
Below is sample code, where "geom_repel_states" is a vector with a few states' FIPS codes. These are the states whose labels I'd like to repel, since their labels overlap when including in geom_sf_label().
Thank you!
example_plot = ggplot(example_sf) +
geom_sf(color = "black", aes(fill = example_variable_name)) +
geom_sf_label_repel(aes(label = ifelse(example_sf$state_fips %in% geom_repel_states, example_variable_name, ""))) +
geom_sf_label(aes(label=ifelse(!(example_sf$state_fips %in% geom_repel_states), example_variable_name, "")))
example_plot
@Steve-Koller This isn't really the best place to ask (better would be a new issue, best would be a new question on Stackoverflow), but since you asked...
Each geom_*()
function takes a data
argument. You can use geom_text_repel(data = ...)
to plot a subset of the original data. The ...
might be another dataframe like my_subdata
or it might also be a function like function(d) d %>% filter(my_condition)
.
We added an example to the ggrepel documentation. Feel free to comment below with your thoughts, and let me know if you believe we need to keep this issue open.
https://ggrepel.slowkow.com/articles/examples.html#label-sf-objects
# thanks to Hiroaki Yutani
# https://github.com/slowkow/ggrepel/issues/111#issuecomment-416853013
library(ggplot2)
library(sf)
nc <- sf::st_read(system.file("shape/nc.shp", package="sf"), quiet = TRUE)
ggplot(nc) +
geom_sf() +
ggrepel::geom_label_repel(
data = head(nc),
aes(label = NAME, geometry = geometry),
stat = "sf_coordinates",
min.segment.length = 0
)
Summary
ggplot2 will (hopefully) get
geom_sf_label()
andgeom_sf_text()
for labelingsf
objects. (c.f. https://github.com/tidyverse/ggplot2/issues/2742#issuecomment-403315573) Are you interested in implementing the_repel
version of these? Labelling sf objects is a very common task so I'm sure many people will findgeom_sf_label_repel()
useful.If you feel ggrepel is not the right place for them, they will probably be implemented in ggforce.
Minimal code example
Proof of Concept is here: https://yutannihilation.github.io/ggsflabel/#geom_label_repel-for-sf
Suggestions
Note that this can be addressed after
geom_sf_label()
andgeom_sf_text()
are implemented in ggplot2. So, please stay tuned on tidyverse/ggplot2#2742! (I will do my best to finish it as soon as possible)Version information
N/A