Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Labeling simple features objects #41

Closed
samueldodson opened this issue Dec 17, 2021 · 6 comments
Closed

Labeling simple features objects #41

samueldodson opened this issue Dec 17, 2021 · 6 comments
Assignees
Labels
enhancement New feature or request

Comments

@samueldodson
Copy link

Can this package label simple features objects? ggplot2 has the geom_sf_label and geom_sf_text functions for this; however, I would like to add labels along curvy lines / polylines (e.g., labeling a meandering river).

Here is an example of geom_sf_text:

library(ggplot2)
library(rnaturalearth)
library(rnaturalearthdata)
library(sf)

crop <- st_bbox(c(xmin = -84.74674,
                  xmax = -84.34674,
                  ymin = 42.5335,
                  ymax = 42.93353))

land <- ne_countries(scale = "medium",
                      returnclass = "sf") %>% st_crop(crop)

river <- ne_download(scale = 10,
                      type = "rivers_lake_centerlines",
                      category = "physical",
                      returnclass = "sf") %>% st_crop(crop)

ggplot() +
  geom_sf(data = land, fill = "cornsilk") +
  geom_sf(data = river, color = "blue") +
  geom_sf_text(data = river, aes(label = paste0(name, " River")), color = "blue")

...which outputs:

Untitled1

Instead, I would like a curvy label:

Untitled2

Can I do this with geomtextpath?

@teunbrand
Copy link
Collaborator

teunbrand commented Dec 17, 2021

I'd say not yet, as the package is still young, but we've toyed with the idea and agreed that this would be a nice addition. Adjacent discussion happened here, but this issue might suit as a more relevant place to discuss progress.

@AllanCameron
Copy link
Owner

Thanks for writing Samuel. Funnily enough, we have been discussing this recently and it's something we do have in the pipeline, hopefully in the next few days. I will keep this issue open as a feature request and keep you posted.

@AllanCameron AllanCameron added the enhancement New feature or request label Dec 18, 2021
@AllanCameron AllanCameron self-assigned this Dec 19, 2021
@AllanCameron
Copy link
Owner

@samueldodson The latest commit brings rudimentary support for labels on rivers / linestring objects. When I attempted your example, I was unable to apply st_crop to the object land for some reason, so I have had to modify it to show what we have so far.

library(geomtextpath)
library(rnaturalearth)
library(rnaturalearthdata)
library(sf)

limits <- c(xmin = -84.74674,
            xmax = -84.34674,
            ymin = 42.5335,
            ymax = 42.93353)

crop <- st_bbox(limits)

land <- ne_countries(scale = "medium",
                      returnclass = "sf")

river <- ne_download(scale = 10,
                      type = "rivers_lake_centerlines",
                      category = "physical",
                      returnclass = "sf")

river <- st_crop(river, crop)

ggplot() + 
  geom_sf(data = land, fill = "cornsilk") +
  geom_textsf(data = river, label = "Grand River",
              color = "blue3", vjust = -0.4) +
  lims(x = limits[1:2], y = limits[3:4])

image

@teunbrand the main problem I'm having is the inability to smooth text at grob level. One cannot use geom_smooth in this context, so I may look at implementing this at some point. There are many problems to iron out, though by chance the example here works reasonably well.

@teunbrand
Copy link
Collaborator

teunbrand commented Dec 20, 2021

Seems to work pretty well already, which is exciting!

smooth text at grob level

Yes I was afraid we'd have to do this at the grob level at some point. Looking at this blogpost, they also seem to indicate a preference to have the option to smooth text.

I've been giving this some thought and the problem with our current code is that we cannot, at the same time, do a path, draw that as-is and then also take the same path and smooth it and then draw the text over the smoothed path.

However, what we might do in >R4.1 is to use a clipping mask as detailed in Paul Murrell's report from last year. We already have the code to draw a textbox along a curve, we can use that same code to generate a polygon that can act as a clipping mask for the path. I haven't experimented with this (yet), but we could use it to clip the original path while we place the text on a different path.

As a side-note:
Do you think we could leave {sf} as a suggested package rather than imported package? I can't install {sf} at work for example due to not having admin right to install the right system libraries, and having it as a dependency would disallow me to install this.

@AllanCameron
Copy link
Owner

AllanCameron commented Dec 21, 2021

I'm happy with the "suggests" rather than "imports".

I think we should be able to feed a smoothed version of the path to get_path_points (I forget it's new name already...) and keep the main path for the line.

The blog post shows some of the problems I was worried we might see in real - world examples, but at least the author isn't too critical.

The sf support is indeed quite a lot of work. The sf ecosystem has a few quirks that mean you have to do some things the long way. I have also had to reproduce a few non-exported ggplot functions in utils to get it working, but I wanted to get something that I knew worked in principle before writing all the unit tests.

@AllanCameron
Copy link
Owner

AllanCameron commented Jan 2, 2022

The latest commit adds the ability to remove text that is too long for its path. Along with the grob level text-smoothing, this was the main impediment to properly functioning labelled linestrings. The reason for this is that when zoomed out of a map, having every little river and road labelled causes massive overplotting. The new mechanism removes text from features that are too small to support them. Unfortunately, this also has to be done at grob level, but it is quite a cheap calculation, and is only done when specifically asked for (it's only turned on by default in geom_textsf), so it should not affect the performance of the rest of the package.

As shown in the updated readme, the resulting appearance of geom_textsf is now much more generally acceptable, and doesn't need careful "cherry-picking" of areas to produce an aesthetic result.

library(geomtextpath)
#> Loading required package: ggplot2

df <- data.frame(x = c(-4.2518, -3.1883), 
                 y = c(55.8642, 55.9533),
                 label = c("Glasgow", "Edinburgh"))

ggplot(data = df) +
  geom_textsf(data = waterways,
              aes(label = name), text_smoothing = 99.5, linecolour = "#8888B3", 
              color = "gray30", hjust = 0.25, vjust = -0.5, fill = "#E6F0B3", 
              alpha = 0.8, fontface = 3, size = 3) + 
  geom_point(aes(x, y), data = df, color = "gray50", size = 3) + 
  geom_textpath(aes(x, y, label = label), color = "gray50",
                hjust = c(-0.2, 1.2)) +
  theme(panel.grid = element_line()) + 
  lims(x = c(-4.7, -3), y = c(55.62, 56.25))

Created on 2022-01-02 by the reprex package (v2.0.1)

While still not perfect, I think this could produce some very nice map-based plots that are not otherwise available at present in the ggplot ecosystem, and I am tempted to close this issue for now - unless there are any concerns @teunbrand ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants