Giter Club home page Giter Club logo

geomtextpath's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

geomtextpath's Issues

Plotmath and expressions

One of the things users will presumably want to do is to have expressions as textpaths. This isn't currently handled. A textGrob can take expressions, hence geom_text can parse text into expressions and pass them on directly to textGrob. However, the rendering of expressions in textGrob happens via C code in plotmath.c, meaning that parsed expressions produce a single object that cannot be broken up and curved in the way that a text string can.

There are a few ways to handle this

  1. Just don't support expressions, and advise folks to use Unicode if they want subscripts, superscripts etc.
  2. Allow expressions, but accept that they need to be straight and have them sitting at the most appropriate angle on the path.
  3. Figure out a way of allowing curved expressions with a completely different mechanism to the one we use already. One method that comes to mind here is to convert the expression to svg (which is easily done), then convert the svg to a bunch of lines and symbols (difficult / complex), then curve them (moderately difficult).
    However, if we were going to go to the bother of doing this, the mechanism would also work for plain text, and we would be as well changing the entire implementation, which I really don't fancy doing.

Letter angles when modifying the aspect ratio

This is a very nice little package, and exactly what I was looking for in a particular project I am working on.

Very briefly, the angle of letters does not appear to handle varying aspect ratios. In the stackoverflow discussion, the angle in the example makes use of scaling with dev_size(), which helps when modifying the scaling of the output. Unless I have overlooked something, this appears not to be included in the package.

Consider these graphs; output in trellis.device() to control aspect ratios, as for e.g. ggsave()

gg1 <- ggplot(data.frame(y = sin(seq(0, pi, length.out = 100)),
                  x = seq(0, pi, length.out = 100),
                  z = 'Pirates vs. Ninjas'), aes(x, y, label = z)) + 
  geom_textpath(hjust = 0.2)`

trellis.device(device="windows", height = 8, width = 8, color=TRUE)
gg1
# Looks fine
trellis.device(device="windows", height = 2, width = 8, color=TRUE)
gg1
# Looks wrong

Adding scaling with dev.size() as in the stackoverflow example helps, but is not perfect:

.add_path_data <- function (.data) 
{
  grad <- diff(.data$y)/diff(.data$x)
  mult <- diff(range(.data$x))/diff(range(.data$y)) * dev.size()[2] / dev.size()[1]
  .data$grad <- approx(seq_along(grad), grad, seq(1, length(grad), 
                                                  length.out = length(grad) + 1))$y
  .data$angle <- atan(mult*.data$grad) * 180/pi
  .data$angle <- ifelse(sign(c(0, diff(.data$x))) < 0, .data$angle - 
                          180, .data$angle)
  .data$length <- c(0, cumsum(sqrt(diff(.data$x)^2 + diff(mult*.data$y/1)^2)))
  rad_diff <- diff(atan(mult*.data$grad))
  rad_diff <- rad_diff[abs(rad_diff) < pi/2]
  rad_diff <- approx(seq_along(rad_diff), rad_diff, seq(1, 
                                                        length(rad_diff), length.out = nrow(.data) - 1))$y
  curvature <- rad_diff/diff(.data$length)
  effective_length <- diff(.data$length) * (1 + (.data$vjust[1] - 
                                                   0.5) * 0.04 * curvature)
  .data$adj_length <- c(0, cumsum(effective_length))
  .data$string_length <- sapply(.data$label, strwidth, units = "figure")
  .data
}

assignInNamespace(x = '.add_path_data',.add_path_data,ns='geomtextpath')
environment(.add_path_data)<-asNamespace('geomtextpath')

However, this does not account for the difference between the device surface and the ggplot panel aspect ratios.
Ideally, the line
mult <- diff(range(.data$x))/diff(range(.data$y)) * dev.size()[2] / dev.size()[1]
should employ the relative aspect ratios of the ggplot panel within the device. Not sure how best to extract those parameters, though.

Rich text

This might be an overly ambitious idea, but I find that the gridtext package does a nice job of parsing rich text and displaying it in grid. It would be really neat, if we could also do this.

The upside is that we can have really rich text with various colour and fontfaces within the same string, which can be lovely in some cases.

The downside is that it is a lot of work, and most likely requires additional dependencies to deal with html / markdown / css. Furthermore, I'm not all that experienced with these languages, but I think it can be done anyway, especially as {gridtext} paved the way. If you think this is an idea worth pursuing, we might ask Claus Wilke whether it is OK if we adapt some of the parsers of {gridtext}.

A `geom` similar to `geom_text` but curves in polar co-ordinates

Consider the following example where we can't use geom_textpath because we only want labels at a single point rather than following a path:

df <- data.frame(region = c("North America", "South America", 
                            "Australasia", "Europe"), 
                 values = c(500, 200, 350, 600))

p <- ggplot(df, aes(region, values, fill = region)) + 
  geom_col(width = 0.5) + 
  lims(y = c(0, 750)) +
  labs(x = "") +
  theme_bw() +
  theme(legend.position = "none",
        axis.text.x = element_blank(),
        axis.ticks.length.x = unit(0, "mm"))

p1 <- p + geom_text(aes(label = region), size = 5, vjust = -1) 

p1

image

But if we switch to polar co-ordinates, the labels are of course straight:

p1 + coord_polar()

image

With what we have in place so far, it should be straightforward to define a new geom that acts as a drop-in for geom_text but bends in polar co-ordinates, so for example:

p2 <- p + geomtextpath:::geom_textpolar(aes(label = region), size = 5, vjust = -1) 

p2

image

and

p2 + coord_polar()

image

The implementation is something like this:

geom_textpolar <- function(
  mapping = NULL, data = NULL, stat = "identity",
  position = "identity", na.rm = FALSE, show.legend = NA,
  inherit.aes = TRUE,  ...
)
{
  layer(geom = GeomTextPolar, mapping = mapping, data = data, stat = stat,
        position = position, show.legend = show.legend,
        inherit.aes = inherit.aes,
        params = list(
          na.rm     = na.rm,
          ...
        ))
}

GeomTextPolar <- ggproto("GeomLine", GeomTextpath,
    extra_params = c("na.rm", "orientation"),

    setup_data = function(data, params) {
      data$linewidth <- 0
      data <- data[rep(seq(nrow(data)), each = 100),]
      if(!is.numeric(data$x)) data$x <- as.numeric(as.factor(data$x))

      text_width <- diff(range(data$x))/5
      range_seq <- seq(-text_width, text_width, length.out = 100)
      data$x <- data$x + range_seq

      data
    }
)

The questions are

  1. Is this something that people would use (I'm sure I have looked for something similar before)
  2. Do you immediately foresee any problems
  3. I'm not too happy with the name. Can you think of a better one?

geom_textsegment() with arrow() draws line to the top left of the plot

As in the title, geom_textsegment() with arrow() draws a line starting from where the text starts to the top left of the plot.

Using the example from the docs:

ggplot(mapping = aes(x, y)) +
    geom_col(
        data = data.frame(x = c(1, 2), y = c(1, 10))
    ) +
    annotate(
        "textsegment",
        x = 1, xend = 2, y = 1, yend = 10,
        label = "10x increase", arrow = arrow()
    )

The output:

image

sessionInfo():

R version 4.1.2 (2021-11-01)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 18363)

Matrix products: default

locale:
[1] LC_COLLATE=Dutch_Netherlands.1252  LC_CTYPE=Dutch_Netherlands.1252    LC_MONETARY=Dutch_Netherlands.1252 LC_NUMERIC=C                      
[5] LC_TIME=Dutch_Netherlands.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] geomtextpath_0.1.0 ggplot2_3.3.5     

loaded via a namespace (and not attached):
 [1] magrittr_2.0.1    tidyselect_1.1.1  munsell_0.5.0     colorspace_2.0-2  R6_2.5.1          rlang_0.4.12      fansi_0.5.0       dplyr_1.0.7      
 [9] tools_4.1.2       grid_4.1.2        gtable_0.3.0      utf8_1.2.2        DBI_1.1.2         withr_2.4.3       systemfonts_1.0.3 ellipsis_0.3.2   
[17] digest_0.6.29     assertthat_0.2.1  tibble_3.1.6      lifecycle_1.0.1   crayon_1.4.2      textshaping_0.3.6 farver_2.1.0      purrr_0.3.4      
[25] vctrs_0.3.8       glue_1.6.0        labeling_0.4.2    compiler_4.1.2    pillar_1.6.4      generics_0.1.1    scales_1.1.1      pkgconfig_2.0.3  

This happens regardless if I use geom_textsegment as an annotation.

Labeling simple features objects

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?

geom_textpath() always draw a empty box when label is ""

Hi, thank you so much for writing such a nice package.
But I noticed that ggplot2::geom_text () is OK when label is empty, but geomtextpath::geom_textpath() always draws a blank box. The following is an example:

library(ggplot2)
  
## draw empty text with geom_text(), it's OK
df <- data.frame(x = 1, y = 1, label = "")
  
  ggplot(df, aes(x, y, label = label)) +
    geom_text()
  
## draw empty text with geom_textpath(), it always draw a empty box 
tt <- seq(0, 2*pi, length.out = 300)
df2 <- data.frame(x = cos(tt), y = sin(tt), label = "")
  ggplot(df2, aes(x, y, label = label)) +
    geomtextpath::geom_textpath()

Roadmap

Hi Alan,

We've come really far with this package and since we finished some major issues such as text smoothing, sf-text and richtext, I thought it is a good time to discuss what the next steps would be. It would be nice for example if we'd submit the package to CRAN at some point.

First, do still have some issues on your wish list that you'd be keen to see implemented in a first version? Personally, on the shorter term, I'd like to complete the vignettes, for example.

Next, do we want to announce the package before submitting to CRAN? I know that because we've been collaborating on this package publicly, some people have already picked up on this package and shared it on the social internet. Nonetheless, it might be nice to register the package in the ggplot2 extension gallery and/or announce on twitter that we think it is ready, and maybe solicit some feedback.

Lastly, before submitting to CRAN, we should probably do most of the items in the checklist generated by usethis:::release_checklist(). In particular, the rhub::check_for_cran() step has helped me previously.

[Unicode] Unicode combining characters not kept with base character

Code

ggplot(iris, aes(x=Sepal.Length))+
geom_textpath(aes(label="Composed: \u00ea, DeC: e\u0302, \u05aa\u05d0\u05aa"), stat="density", vjust=-0.2, size=8, fontface=1)+
ylim(0.1,0.5)

Expected

Text should show "Composed: ê, DeC: ê, ֪א֪" on a line

Actual

combiningbug

Explanation

The first "exciting" character is the precomposed form U+00EA, which works just fine, and is how I'd expect it to appear. The next one, after DeC[omposed], is the standard "e" with the combining hat U+0302. The hat is not rotated, and not located in the correct position, instead appearing inside the e. For this specific case, this could be solved with NFC or NFKC, but that only sidesteps the problem, as there is no, for example p̂ that is one codepoint. The Hebrew Alef U+05D0 is combined with some Hebrew accents U+05AA, which should be below the letters, but instead are shown in "naked" form as they are rendered separately.

`vjust` is not respected on linux

Hi, thank you for this amazing package! I noticed that for some reason the geomtextpath doesn't respond to vjust value on my linux machine. This bug doesn't appear on my macOS machine tho

Here is the code I copyed from a previous issue

library(ggplot2)
library(geomtextpath)
library(patchwork)

df <- data.frame(
  x = seq(-0.5, 1.5, length.out = 10),                                       
  y = 1,
  label = "geom_textpath"
)

plot_vjust <- function(vjust) {
  ggplot(df, aes(x, y)) +
    geom_textpath(aes(label = label), vjust = vjust) +                       
    annotate(x = 2, y = 1, label = "geom_text", geom = "text", vjust = vjust) +
    ggtitle(paste0("vjust = ", vjust)) +                                     
    scale_x_continuous(limits = c(-1, 3))                                    
}

p = plot_vjust(-1) + plot_vjust(0) + plot_vjust(1) + plot_vjust(2) + plot_vjust(10) + plot_vjust(15)

And this is my output on Linux machine: you can see the geom_textpath does not respond to vjust value like geom_text

image

Here is my sessionInfo

R version 4.1.1 (2021-08-10)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 16.04.6 LTS

Matrix products: default
BLAS/LAPACK: /usr/lib/libopenblasp-r0.2.18.so

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=en_US.UTF-8       
  [4] LC_COLLATE=en_US.UTF-8     LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
   [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                  LC_ADDRESS=C              
   [10] LC_TELEPHONE=C             LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

   attached base packages:
   [1] stats     graphics  grDevices utils     datasets  methods   base     

   other attached packages:
   [1] patchwork_1.1.1    geomtextpath_0.1.0 ggplot2_3.3.5     

   loaded via a namespace (and not attached):
    [1] Rcpp_1.0.7             magrittr_2.0.1         tidyselect_1.1.1      
     [4] munsell_0.5.0          colorspace_2.0-2       R6_2.5.1              
      [7] ragg_1.2.1             rlang_0.4.12           fansi_0.5.0           
      [10] dplyr_1.0.7            tools_4.1.1            grid_4.1.1            
      [13] gtable_0.3.0           utf8_1.2.2             DBI_1.1.2             
      [16] withr_2.4.3            systemfonts_1.0.3.9000 ellipsis_0.3.2        
      [19] digest_0.6.28          assertthat_0.2.1       tibble_3.1.5          
      [22] lifecycle_1.0.1        crayon_1.4.2           textshaping_0.3.6     
      [25] farver_2.1.0           purrr_0.3.4            vctrs_0.3.8           
      [28] glue_1.4.2             labeling_0.4.2         compiler_4.1.1        
      [31] pillar_1.6.4           generics_0.1.0         scales_1.1.1          
      [34] pkgconfig_2.0.3    

Thank you!

While working with ragg, minuses aren't drawn

Thanks for the quick resolution of the previous issue and the nice package.

As the title says, when I use geomtextpath with ragg, minuses (-) aren't drawn. With grDevices::png on windows, minuses are drawn.

The following code:

library(ragg)
library(ggplot2)

library(geomtextpath)

data <- data.frame(x = 1:3, text = c("+", "-", "1 - 2 = -1"))

plot <- ggplot(data = data) + 
  geom_textpath(aes(x, 1, label = text), color = "red") +
  geom_text(aes(x, 2, label = text), color = "blue") +
  scale_x_continuous(limits = c(1, 3.5))

ggsave("plot_ragg.png", plot, device = ragg::agg_png(), width = 3, height = 3)
ggsave("plot_grdevices.png", plot, device = grDevices::png, width = 3, height = 3)

Produces 2 plots:

plot_ragg:

plot_grdevices:

As you can see, with ragg, geom_textpath fails to draw minuses/dashes, while geom_text can draw minuses with both grDevices and ragg.

Sessioninfo R version 4.1.2 (2021-11-01) Platform: x86_64-w64-mingw32/x64 (64-bit) Running under: Windows 10 x64 (build 18363)

Matrix products: default

locale:
[1] LC_COLLATE=Dutch_Netherlands.1252 LC_CTYPE=Dutch_Netherlands.1252 LC_MONETARY=Dutch_Netherlands.1252 LC_NUMERIC=C
[5] LC_TIME=Dutch_Netherlands.1252

attached base packages:
[1] stats graphics grDevices utils datasets methods base

other attached packages:
[1] geomtextpath_0.1.0.9000 ggplot2_3.3.5 ragg_1.2.1

loaded via a namespace (and not attached):
[1] httr_1.4.2 tidyr_1.2.0 jsonlite_1.7.3 splines_4.1.2 foreach_1.5.2 carData_3.0-5 prodlim_2019.11.13
[8] assertthat_0.2.1 yaml_2.2.2 pec_2021.10.11 globals_0.14.0 timereg_2.0.1 numDeriv_2016.8-1.1 pillar_1.7.0
[15] backports_1.4.1 lattice_0.20-45 glue_1.6.1 digest_0.6.29 ggsignif_0.6.3 colorspace_2.0-2 htmltools_0.5.2
[22] Matrix_1.4-0 pkgconfig_2.0.3 broom_0.7.12 listenv_0.8.0 haven_2.4.3 purrr_0.3.4 scales_1.1.1
[29] openxlsx_4.2.5 lava_1.6.10 tibble_3.1.6 farver_2.1.0 generics_0.1.2 car_3.0-12 xgboost_1.5.2.1
[36] ellipsis_0.3.2 ggpubr_0.4.0 gtsummary_1.5.2 withr_2.4.3 cli_3.1.1 survival_3.2-13 magrittr_2.0.2
[43] crayon_1.5.0 evaluate_0.15 future_1.23.0 fansi_1.0.2 parallelly_1.30.0 broom.helpers_1.6.0 rstatix_0.7.0
[50] forcats_0.5.1 textshaping_0.3.6 tools_4.1.2 data.table_1.14.2 hms_1.1.1 lifecycle_1.0.1 stringr_1.4.0
[57] munsell_0.5.0 zip_2.2.0 compiler_4.1.2 systemfonts_1.0.3 rlang_1.0.1 grid_4.1.2 gt_0.3.1
[64] iterators_1.0.13 rstudioapi_0.13 labeling_0.4.2 rmarkdown_2.11 gtable_0.3.0 codetools_0.2-18 abind_1.4-5
[71] DBI_1.1.2 R6_2.5.1 knitr_1.37 dplyr_1.0.7 fastmap_1.1.0 future.apply_1.8.1 utf8_1.2.2
[78] stringi_1.7.6 parallel_4.1.2 Rcpp_1.0.8 vctrs_0.3.8 tidyselect_1.1.2 xfun_0.29

geom_textlinerange

@teunbrand I came across a question on SO today where I thought "Aha! This is a perfect use case for geom_textlinerange, only to find that it doesn't exist! I am opening this as a feature request as a way of adding it to my to-do list.

Incidentally I updated the package to version 0.1.1 today on CRAN. They have switched on html5 validation on packages' html, and this flagged some of the roxygen2-built documents, causing a CMD NOTE

It was an easy fix - just updating roxygen2 and rebuilding.

[FR] Support shadowtext / text background

There are currently multiple implementations for the geom_text with border. For example, shadowtext. ggrepel also take a bg.color argument that can produce a similar effect.

https://guangchuangyu.github.io/2017/11/shadow-text-effect-in-grid-and-ggplot2-graphics/
https://stackoverflow.com/questions/56798482/how-to-combine-repelling-labels-and-shadow-or-halo-text-in-ggplot2

This could be helpful for geomtextpath, especially when gap = FALSE, or when plot with other geom layers.

Also could help my previous problem #55 , to better separate the text and line when vjust is not working.

Thank you so much!

gZCst

[Unicode] RTL scripts are rendered backwards

Code

ggplot(iris, aes(x=Sepal.Length))+
geom_textpath(aes(label="Sarah is \u05e9\u05e8\u05d4 with ש on R"), stat="density", vjust=-0.2, size=8, fontface=1)+
ylim(0.1,0.5) +
annotate("text", label="expect: Sarah is \u05e9\u05e8\u05d4 with ש on R", y=0.2, x=5.75)

Expected

Text should show "Sarah is שרה with ש on R" on a line, exactly as shown in the normal text geom at the bottom

Actual

RTL confusion

Notes

Hebrew used here because it's the example at the top of the BiDi Wikipedia page, but all RTL langauges are affected, including override marks

text_only in geom_labelsegment

Hey ho!

When setting text_only = TRUE in geom_labelsegment, then the label is actually shorter then defined by x, y, xend, yend (lines at the ends are ommitted). (It is shorter due to length of the text.)
Is it possible to force label size to the defined limits? If the labeltext is shorter, then ends should be white; if the labeltext is longer linebreaks my be forced.

Could such feature be added in case it does not exist yet?

Thank you.

Denisty fill

Hello! Thank you for this incredible package. I was wondering if it would be possible to be able to add the fill aesthetic to density plots? It doesn't appear to work presently.

library(geomtextpath)
#> Loading required package: ggplot2

ggplot(iris, aes(Sepal.Length)) +
  geom_textdensity(aes(fill = Species, label = Species),
                   hjust = "ymax", vjust = -0.2)

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

The `vjust` issue

I thought to separate this discussion from the discussion in #50 where we expanded the issue identified in #55 which hopefully makes it easier to distinguish roadmap things from vjust things. (Also, it would be very satisfying to close this issue instead of having to wait until we're done with all roadmap things)

What we know so far summarized:

  • How vjust behaves in {geomtextpath} differs somewhat from {ggplot2}
  • The difference appears multiplicative rather than additive.
  • The vjust displacement in {ggplot2} appears approximately 75%-79% of {geomtextpath}'s displacement. Hypothetical reasons for this number might be:
    • A pixel is 0.75 points.
    • The max_ascent / lineheight ratio is about 0.787 (for Arial I'll presume)
    • The output of textshaping::shape_text() might be in points rather than pixels?
  • There also appears to be a multiplicative difference in lineheight of about a factor ~1.1

Option for line width and/or font size control when using 'size = ___' for geom_textsmooth()

Hi all, thank you for the excellent package. Everything is documented well and its easy to drop these geoms into existing work. I did have a question or a feature that might be useful, and that is the option to set the line width and the text size when using geom_textsmooth() Currently size = ____ can be used within the function to adjust font size, which blocks the ability to change line thickness as one would with ggplot2::geom_smooth(). I can think of cases where one might want to have the ability to adjust both independently and just wanted to raise the idea. Thanks again for sharing this excellent package.

Smoothing bumpy text

Thought I'd better make a note somewhere about an attempt I made to add in a smoothing parameter. The idea was to "iron out" small bumps in the text by a simple loess of x co-ordinates and another of y co-ordinates. This produces a very nice effect in some cases. For example, with the trend lines. Compare without any smoothing:

library(geomtextpath)

ggplot(iris, aes(x = Sepal.Length, y = Petal.Length)) +
  geom_point(alpha = 0.1) +
  geom_textpath(aes(label = Species, colour = Species),
                stat = "smooth", method = "loess", formula = y ~ x,
                size = 7, linetype = 3, fontface = 2, linewidth = 1) +
  scale_colour_manual(values = c("forestgreen", "deepskyblue4", "tomato4")) +
  coord_fixed(ratio = 1) +
  theme_bw()

image

versus with smoothing:

ggplot(iris, aes(x = Sepal.Length, y = Petal.Length)) +
  geom_point(alpha = 0.1) +
  geom_textpath(aes(label = Species, colour = Species),
                stat = "smooth", method = "loess", formula = y ~ x,
                size = 7, linetype = 3, fontface = 2, linewidth = 1,
                smooth = 0.75) +
  scale_colour_manual(values = c("forestgreen", "deepskyblue4", "tomato4")) +
  coord_fixed(ratio = 1) +
  theme_bw()

image

To my eye this looks a lot nicer and is closer to publication standard. The problem is that it smooths the underlying path itself, so can't be used when the exact path is important (such as density curves), and often distorts the spacing of the text.

We can actually get the same result as the nice one above by simply calling geom_textpath with a span = 1, which, although we don't use as a parameter, gets passed to stat_smooth:

ggplot(iris, aes(x = Sepal.Length, y = Petal.Length)) +
  geom_point(alpha = 0.1) +
  geom_textpath(aes(label = Species, colour = Species),
                stat = "smooth", method = "loess", formula = y ~ x,
                span = 1,
                size = 7, linetype = 3, fontface = 2, linewidth = 1) +
  scale_colour_manual(values = c("forestgreen", "deepskyblue4", "tomato4")) +
  coord_fixed(ratio = 1) +
  theme_bw()

image

So I'm not convinced that a smoothing feature is required. Perhaps a stat_textspline will be the route to smoothing paths, and this is on the to-do list.

Enable hjust to place text proportionally along a line within the plot bounds

Greetings,

Awesome package! I just was introduced to it via my StackOverflow question here, and the answer-er ran into issues with reproducing my desired result given the hjust option. This bit from the manual is very enticing, though in the example it didn't seem entirely accurate?

Although such lines aren’t curved, there are some benefits to using the geomtextpath functions if a labelled reference line is required: only a single call is needed, co-ordinates are not required for the text label, ...

Mainly, while coordinates aren't required, it does seem that tailoring hjust to the specific line is still necessary. I'll re-create the example here.

base plot

library(ggplot2)

set.seed(123)
df <- data.frame(
  x = runif(100, 0, 1),
  y = runif(100, 0, 1))

lines <- data.frame(
  intercept = rep(0, 5),
  slope = c(0.1, 0.25, 0.5, 1, 2))

p <- ggplot(df, aes(x = x, y = y)) +
  geom_point() +
  geom_abline(aes(intercept = intercept, slope = slope),
              linetype = "dashed", data = lines)

grab_2022-01-18_083517

manual labeling (trial and error to construct the data frame of x, y, and labels

This would be the ideal output of some automated solution, placing the labels nicely off to the top/side.

labels <- data.frame(
  x = c(rep(1, 3), 0.95, 0.47),
  y = c(0.12, 0.28, 0.53, 1, 1),
  label = lines$slope)

p + geom_text(aes(label = label), color = "red", data = labels)

grab_2022-01-18_083626

solution from camille using geom_textabline

library(geomtextpath)
p + geom_textabline(aes(intercept = intercept, 
                        slope = slope,
                        label = as.character(slope)),
                    data = lines,
                    gap = FALSE,
                    offset = unit(0.2, "lines"),
                    text_only = TRUE)

grab_2022-01-18_083844

The hjust argument isn't doing exactly what I would expect given typical ggplot2 meaning (left, center, right justification). I understand from camille's answer that 0.5 is the default value, however from trial and error starting at 0 and incrementing by 0.1, I found that 0.4 is the first time they show up:

p + geom_textabline(aes(intercept = intercept, 
                        slope = slope,
                        label = as.character(slope)),
                    data = lines,
                    gap = FALSE,
                    offset = unit(0.2, "lines"),
                    text_only = TRUE,
                    hjust = 0.4)

grab_2022-01-18_084200

And by 0.6, the top label is already off the plot area, with the rest following at 0.7.

p + geom_textabline(aes(intercept = intercept, 
                        slope = slope,
                        label = as.character(slope)),
                    data = lines,
                    gap = FALSE,
                    offset = unit(0.2, "lines"),
                    text_only = TRUE,
                    hjust = 0.6)

grab_2022-01-18_084213

#27 and #34 seem related to this, but for contours. Basically giving a sort of "auto-placement", though in this case it seems a lot more straightforward, just figuring out the min/max range of the line vs. having to figure out where a contour is "flattest".

I'll include her suggestions for "manual" placement, either from the data itself, or by using the plot object internals:

# y = intercept + slope * x
xmax <- max(df$x) 
# or layer_scales(p)$x$get_limits()[2] for data range
# or ggplot_build(p)$layout$panel_params[[1]]$y.range[2] for panel range
ymax <- max(df$y)
lines_calc <- lines %>%
  mutate(xcalc = pmin((ymax - intercept) / slope, xmax),
         ycalc = pmin(intercept + slope * xmax, ymax))

p +
  geom_text(aes(x = xcalc, y = ycalc, label = as.character(slope)),
            data = lines_calc, vjust = 0, nudge_y = 0.02)

I've honestly never programmed "under the hood" in an R package, but would be willing to try if you think there is merit/feasibility to any of these approaches? Or if there was a suggested couple of files to look in for something like the $get_limits trick that might be used for this package, I could take those as a starting point?

Let me know what you think, and thanks for your consideration/thoughs!

Line breaks don't work

The current code doesn't allow for line breaks in text strings due to the string width calculations.

For example:

df <- data.frame(x = 1:1000, y = 1, z = "This is a perfectly\nflat label")

ggplot(df, aes(x, y, label = z)) +
  geom_textpath(size = 6, linewidth = 0)

image

Whereas, we probably want the equivalent of:

p <- ggplot(df1, aes(x, y, label = z)) +
  geom_textpath(size = 6, vjust = -0.2, linewidth = 0) +
  geom_textpath(size = 6, vjust = 1.2, data = df2, linewidth = 0)

Which gives us

p

image

and

p + coord_polar(start = pi)

image

This means that to implement line breaks, we probably need to find all instances of \n , \r and \r\n, split the text at those points and create multiple paths with individually calculated vjust values.

Tracking bugs and features

It's good that we have identified some issues and ideas that we want to keep track of, but these are currently mentioned all over the place. I thought it could be useful to have a checklist issue to keep track of bugs we're aware of. Plus, it is very satisfying to check off things from a checklist. Feel free to comment additional ones as you encounter them.

Bugs

  • Line breaks don't work (#4).
  • Sometimes text flips (#7 (comment)).
  • Spacing might be too wide when curvature is variable (#7 (comment)).
  • There are no tests for correctness yet (#1 (comment)).
  • Under extreme aspect ratios, all letters of a string could be discarded, leading to errors in .get_surrounding_lines().

Features

  • Alternatively parametrised versions of geom_textpath() (#5).
  • Expose the shape_string(tracking = ...) argument to user to finetune letter spacing (#4 (comment)).
  • A halign parameter for alignment of multi-line strings (see example in comments)

label border?

Hi, great work with this packge!

I just started playing around with it and was wondering if there is a way to remove the border from geom_labelpath ?

I want to have a background but no border. In geom_label you would achieve this with label.size=0. Alternatively, is there a way to specify border color?

geom_hline and geom_vline

First - this is FANTASTIC and absolutely BEAUTIFUL.
I was wondering if there's an geomtextpath equivariant for geom_hline() and geom_vline which I frequently use to denote some descriptive stat such as mean, max, minimum, median etc in a density, scatter or bar plot.

Feature request - avoid text overlapping

Dear Allan and Teun,

First of all, thank you so much for your package. I have just downloaded it and it looks very useful and easy to use for what I want!

I am using the geom_textline package which is working well, but my textline turns out to overlap for close line plots. Is there any way to avoid the overlapping of the text in the lines? Also, is there any option for changing the line and text size independently?

Thanks a lot!

Best
Julie

Preventing overplotting

Text-based layers always run the risk of overplotting, and there are whole packages (like ggrepel) that aim to prevent it. Some plots will not work well with geomtextpath because of overplotting, and it would be useful to include some methods to prevent it.

Since we are largely using vjust and hjust to control text positioning, it seems sensible to me that we should allow these to be mapped to variables, so that mapping a discrete variable will by default allow wide separation of the labels. I have added a scales.R file that allows a full range of hjust and vjust scale functions.

Current behaviour is:

library(geomtextpath)
#> Loading required package: ggplot2

df <- data.frame(x = rep(0:1, each = 3), 
                 y = c(0, 2, 2.1), 
                 z = paste("Example", 1:3))

p <- ggplot(df, aes(x, y, label = z, group = z)) + ylim(c(0, 2.5))

p + geom_textline(size = 8) 

p + geom_textline(aes(vjust = z), size = 8) +
  scale_vjust_manual(values = c(-0.5, 1.5, -0.5))

p + geom_textline(aes(vjust = z, hjust = z), size = 8) +
  scale_vjust_manual(values = c(-0.5, 1.5, -0.5))

It might be a useful exercise to consider whether any other aesthetics could be usefully mapped to scales.

There are other options for preventing overplotting, such as collision detection etc, but this might be overkill? I would be interested to get your thoughts on this.

Another problem that crops up a lot on geom_textsf is that labels can sometimes be much longer than the linestrings. This produces long, straight, criss-crossing and ugly labels. It is quite easy to automatically remove such labels, but it needs to be done at makeContent level.

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

geom_textsmooth() doesn't choose method if not explicitly set

geom_smooth() will choose a default smoothing method if one isn't specified, and ?geom_textpath says that it will also do this. But it actually issues a warning and returns a blank plot unless a method is explicitly set.

library(ggplot2)
library(geomtextpath)

## normal geom_smooth() behavior
ggplot(iris) +
  geom_smooth(
    aes(Sepal.Length, Sepal.Width, color = Species),
    se = FALSE
  )
# `geom_smooth()` using method = 'loess' and formula 'y ~ x'

iris1

## geom_textsmooth() yields warning and blank plot
ggplot(iris) +
  geom_textsmooth(
    aes(Sepal.Length, Sepal.Width, color = Species, label = Species),
    se = FALSE
  )
# `geom_smooth()` using formula 'y ~ x'
# Warning message:
# Computation failed in `stat_smooth()`:
# 'what' must be a function or character string 

iris2

## setting `method` = "loess" fixes it
ggplot(iris) +
  geom_textsmooth(
    aes(Sepal.Length, Sepal.Width, color = Species, label = Species),
    method = "loess",
    se = FALSE
  )
# `geom_smooth()` using formula 'y ~ x'

iris3

Software details:

OS Version: Windows 10 x64 (build 19044)
R Version: R version 4.1.2 (2021-11-01)
ggplot2 Version: 3.3.5
geomtextpath Version: 0.1.0

Text smoothing

Before attempting to introduce automatic text smoothing, we need some idea of when and how to smooth a path. There are two situations I can think of where adhering too closely to a path brings ugly results.

One is where the line is overly geometric, producing corners that makes the text appear to separate:

library(geomtextpath)
#> Loading required package: ggplot2

df <- data.frame(x = 1:5, y = c(0, 5, 5, 0, 5), 
                 z = "A long text label for demonstration purposes")

p <- ggplot(df, aes(x, y)) + geom_line() + lims(y = c(0, 6))

p + geom_textline(aes(label = z), size = 7, hjust = 0.25, 
                  vjust = -0.5, text_only = TRUE)

I have written an algorithm that attempts to smooth such corners using quadratic Bezier curves. It looks like this:

df2 <- geomtextpath:::smooth_corners(df$x, df$y, radius = 0.25)

df2 <- setNames(as.data.frame(df2), c("x", "y"))
df2$z <- "A long text label for demonstration purposes"

p + 
  geom_textline(aes(label = z), data = df2, size = 7, 
                hjust = 0.25, vjust = -0.5, linecolor = "red") 

You will notice that it needs a radius parameter. It is possible that setting it to a reasonable value internally could avoid extra parameter passing to the grob.

The other type of problematic path is the "noisy" path, as in the economics example. There are several ways to handle this (including the rolling mean), but I have again come up with another algorithm that smooths by finding the centre of mass of regularly spaced chunks of the path (say 50) then creating splines to join the dots:

library(geomtextpath)
#> Loading required package: ggplot2

plot(economics$unemploy, type = "l")
df <- geomtextpath:::smooth_noisy(seq(nrow(economics)), economics$unemploy)
df <- setNames(df, c("x", "y"))
lines(df, col = "red")

This function also takes a parameter, samples which is just the number of points on which the path is sampled. However, this seems to work pretty well with 50 - 100 samples for noisy paths, and again we might not need to expose this parameter.

The latest commit includes these functions, both of which return a two-column x, y matrix.

There may be problems that you see with these approaches, or other problematic paths I haven't considered, so let me know what you think before I look at applying these in the makeContent code.

Created on 2021-12-22 by the reprex package (v2.0.1)

Dumb textpath

I have a new feature idea for labelling more noisy curves.

Currently, our text placement is too fancy to conveniently label a noisy curve. For example, in the plot below I want to label the decline in unemployment between 1992-2000 with the text "Decline".

library(ggplot2)
library(geomtextpath)

p <- ggplot(economics, aes(date, unemploy)) +
  geom_path(colour = "grey")
p + geom_textpath(
    aes(label = "Decline", group = 1),
    hjust = 0.55, size = 5, include_line = FALSE, vjust = 0
  )
#> Warning: The text offset exceeds the curvature in one or more paths. This will result
#> in displaced letters. Consider reducing the vjust or text size, or use the
#> hjust parameter to move the string to a different point on the path.

The idea is to allow something like the following, without the need for the subsetting on the date and using stat_smooth().

p + geom_textpath(
    stat = "smooth", method = "lm", formula = y ~ x,
    aes(label = "Decline", group = 1),
    data = ~ subset(.x, date > as.Date("1992-01-01") &
                      date < as.Date("2000-01-01")),
    hjust = 0.5, size = 5, include_line = FALSE, vjust = 0
  )

Created on 2021-11-30 by the reprex package (v2.0.1)

Ideally, I would like to specify something like the following:

p + geom_dumb_textpath(aes(label = "Decline"), anchor_at = x == as.Date("1996-01-01"), vjust = 0)

Implementation-wise, we can skip some steps that we currently make. For instance, we can skip per-letter calculations, and calculate the total angle over the anchorpoint - text_width / 2 and anchorpoint + text_width / 2 arc-length interval instead of every line segment. I imagine this as a separate geom/grob pair from geom_textpath()/textpathGrob(), but conveniently making use of all the helpers we already have. Before I start anything, I just wanted to get your opinion on this.

Labelled contour lines

One of the things I had hoped this package would handle would be labelled contour lines, where there are breaks in the lines for the labels. This is something that comes up now and again on SO, and none of the existing solutions seem that great.

I was about to explore how we might implement this, when I realized that...we already have:

library(geomtextpath)
#> Loading required package: ggplot2

set.seed(1)

df  <- data.frame(x = rnorm(100), y = rnorm(100))

ggplot(df, aes(x, y)) + 
  stat_density2d(geom = "textpath", aes(label = after_stat(level))) +
  theme_classic()

I think we want to include an example like this in our readme and gallery, but the issue is: is this useful enough to warrant its own geom? It would be very little work to create since it's essentially already implemented.

Created on 2021-12-07 by the reprex package (v2.0.0)

Initial commit for geomtextpath

The initial version of geomtextpath with rudimentary docs and functionality is here! This version passes CRAN checks.

I have put copious comments in the code, including a couple of TODO sections that might be good places to start.

There are a few thoughts that occurred to me that we might wish to consider.

  1. There are no tests currently. How do we unit test when visual inspection of the output is required?
  2. What other geoms might usefully benefit from curved text?
  3. It would be nice to include a polar coord that has curved axis text & titles.
  4. It is going to be difficult to think of all the use cases and edge cases. How do we beta test?
  5. How will we know / decide when to submit to CRAN?

Allowing text from points in `geom_textpath`

At the moment, we need to specify a path for each label in geom_textpath, but it would make the geom easier to use if, instead of emitting a warning when each group only consists of a single observation, we interpret this as a request by the user to have a label at a single point. If we construct a little flat path for the label to sit on that is the same size as the text itself, it should behave much the same as geom_text, except curve in polar co-ordinates. This is much the same as what I was proposing in #5 except we don't need a separate geom to do it.

Since the calculation would only happen in the situation where otherwise the geom would fail, it doesn't add any overhead to the existing use cases.

Essentially , the behaviour would be like this:

library(ggplot2)
library(geomtextpath)

df <- data.frame(x = 1:10, y = c(2, 5, 8, 3, 1, 7, 9, 4, 6, 10) + 5, 
                 label = rownames(mtcars)[1:10])

p <- ggplot(df, aes(x, y, colour = label)) + 
       geom_point() +
       lims(y = c(0, 20)) +
       theme_bw() +
       theme(legend.position = "none")

p_text <- p + geom_text(aes(label = label), vjust = 1.5)

p_textpath <- p + geom_textpath(aes(label = label), vjust = 1.5)

p_text

p_textpath

p_text + coord_polar()

p_textpath + coord_polar()

Created on 2021-11-30 by the reprex package (v2.0.0)

Does this fit with our conception of how the geom should behave? It means that under the correct circumstances geom_textpath could act as either exactly like geom_text, or exactly like geom_path, but usually some handy mixture of the two. I like this, but wanted to find out if there were any objections or something I'm missing before committing.

UX: vjust confusing (wrong model?)

First of all, thanks for making a package out of the answer to my SO question!

I finally got around to installing and using what you have so far (impressive!), and it took me awhile to figure out how vjust works. Here are my observations:

  • vjust = -0.1 is sitting on the line, and far enough away to not break the line
  • vjust = 0 is sitting on the line, but not far enough away to not break the line
  • vjust = 0.5 is roughly in the middle (breaks the line)
  • vjust = 1 is below the line, but not far enough to not break the line
  • vjust = 1.1 is below the line, and far enough away to not break the line

I understand the positioning is basically where a geom_text(vjust=...) would put it, but I am not sure that's necessarily the right model for geom_textpath. Note that i'm calling "above" and "below" as on a straight, horziontal line.
My intuition going into this was the following:

  • vjust = 1 in on top of the line at "underline" level, and far enough away to not break the line
  • vjust = 0.99 is sitting on the line, but not far enough away to not break the line
  • vjust = 0 is roughly in the middle (breaks the line)
  • vjust = -0.99 is below the line, but not far enough to not break the line
  • vjust = -1 is below the line at "overline" level, and far enough away to not break the line

Let me explain why that was my intuition, and why I see 3 confusing things here, in order of surprise:

  1. The direction of the scale puts the text below (negative y axis) for positive values, and above (positive y axis) for negative values. This makes more sense when geom_text is specifying the anchor point relative to the text, but geom_textpath doesn't seem like specifying the anchor point of the text (relative to the text), so much as the location of the text on the line relative to the line.
  2. The point in the scale where the line break changes. To me at least, this seems like it should be round numbers, not odd ones like 1.1 or -0.1. As a user, I want obvious parameters for obvious results, and the 3 obvious results are: centered, nicely hanging below (line is in "overline" spot), and nicely on top (line is in "underline" spot). Right now, those 3 obvious results requires values of -0.3, 1.2, and 0.5.
  3. The range of the scales isn't 3 integers, but 2 integers right now. 2 integers is fine, but I again feel like this was copied from geom_text when it shouldn't have been. geom_text lets you anchor in one of the 4 corners (2 sides), but geom_textpath has 3 "sides". Given there are 3 obvious "sides", a range of 3 integers would seem to be better (-1,0,1) vs (0,1).

I don't mind doing some research to find the right metric for underline and overline distance. I may submit a PR for that if I have time over Thanksgiving. I love playing with font metrics :-)

(Another option would be by specifying some font-metric related thing, but those seem to get complicated quickly. I'm not in favor of that)

geom_textsmooth computation fails if method argument is not specified

I came across this issue after I'd added a solution to this popular problem with your awesome package and a user asked me to demonstrate how one can do this with geom_smooth.

This seems very related to https://stackoverflow.com/questions/42493048/computation-failed-for-stat-summary-what-must-be-a-character-string-or-a-func and tidyverse/ggplot2#1937.
However, as you can see below, I am using reprex, and do have a completely empty session. I strongly assume there must be some objects defined that have names such as "mean" or "lm" that are not disambiguated any longer.

library(geomtextpath)
#> Loading required package: ggplot2

ggplot(iris, aes(Sepal.Length, Sepal.Width, label = "a")) +
  geom_textsmooth()
#> `geom_smooth()` using formula 'y ~ x'
#> Warning: Computation failed in `stat_smooth()`:
#> 'what' must be a function or character string

## works when specifying method argument
ggplot(iris, aes(Sepal.Length, Sepal.Width, label = "a")) +
  geom_textsmooth(method = "loess")
#> `geom_smooth()` using formula 'y ~ x'

Created on 2022-05-30 by the reprex package (v2.0.1)

Session info
sessioninfo::session_info()
#> ─ Session info ───────────────────────────────────────────────────────────────
#>  setting  value
#>  version  R version 4.1.2 (2021-11-01)
#>  os       macOS Big Sur 10.16
#>  system   x86_64, darwin17.0
#>  ui       X11
#>  language (EN)
#>  collate  en_GB.UTF-8
#>  ctype    en_GB.UTF-8
#>  tz       Europe/London
#>  date     2022-05-30
#>  pandoc   2.14.0.3 @ /Applications/RStudio.app/Contents/MacOS/pandoc/ (via rmarkdown)
#> 
#> ─ Packages ───────────────────────────────────────────────────────────────────
#>  package      * version date (UTC) lib source

#>  geomtextpath * 0.1.0   2022-01-24 [1] CRAN (R 4.1.2)
#>  ggplot2      * 3.3.6   2022-05-03 [1] CRAN (R 4.1.2)

#> 
#>  [1] /Library/Frameworks/R.framework/Versions/4.1/Resources/library
#> 
#> ──────────────────────────────────────────────────────────────────────────────

geom_labelsf() not recognizing aesthetics

First of all, thank you for this fantastic package! The issue I'm having is that geom_labelsf() doesn't seem to be passing on aesthetic arguments on to geom_labelpath() as the documentation indicates.

For instance, here's an example of geom_labelsf() not recognizing the fill or text_only arguments.

library(tidyverse)
#> Warning: package 'tidyverse' was built under R version 4.1.2
#> Warning: package 'tidyr' was built under R version 4.1.2
library(sf)
#> Linking to GEOS 3.9.0, GDAL 3.2.1, PROJ 7.2.1
library(geomtextpath)
#> Warning: package 'geomtextpath' was built under R version 4.1.2
sample_coords <- tibble(
  x = c(43.00778, 42.99675),
  y = c(-87.94263, -87.90710)
) %>%
  st_as_sf(coords = c("y", "x"), crs = 4326) %>%
  summarise() %>%
  st_cast(., "LINESTRING")

sample_coords %>%
  ggplot() +
  geom_labelsf(label = "Label", fill = "red",
               text_only = TRUE)
#> Warning: Ignoring unknown parameters: text_only

Using the non-spatial geom_labelpath() with the same arguments, however, works as expected.

tibble(
  x = c(1, 2),
  y = c(0, 1)
) %>%
  ggplot(aes(x = x, y = y, group = 1)) +
  geom_labelpath(label = "Label", fill = "red",
                 text_only = TRUE)

I appreciate any guidance you can provide. Thanks again!

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

Labelled contour lines

One of the things I had hoped this package would handle would be labelled contour lines, where there are breaks in the lines for the labels. This is something that comes up now and again on SO, and none of the existing solutions seem that great.

I was about to explore how we might implement this, when I realized that...we already have:

library(geomtextpath)
#> Loading required package: ggplot2

set.seed(1)

df  <- data.frame(x = rnorm(100), y = rnorm(100))

ggplot(df, aes(x, y)) + 
  stat_density2d(geom = "textpath", aes(label = after_stat(level))) +
  theme_classic()

I think we want to include an example like this in our readme and gallery, but the issue is: is this useful enough to warrant its own geom? It would be very little work to create since it's essentially already implemented.

Created on 2021-12-07 by the reprex package (v2.0.0)

Negative values in geom_textcontour not appearing

Hi,

First of all, I love your package. I am trying to make a bathymetric map using geom_textcontours, but the strange thing is that the negative values are not drawn.

# packages
library(tidyverse)
library(elevatr)
library(geomtextpath)
library(mapSpain)
library(sf)
library(terra)

#
bal <- esp_get_ccaa(ccaa = "Baleares") # limits Balearic Island

# bbox
balbf <- st_bbox(bal) %>% 
          st_as_sfc() 

# elevation data
mdt <- get_elev_raster(balbf, z = 6) %>% rast()
mdt <- aggregate(mdt, 4)
mdt <- crop(mdt, bal)

mdt[mdt > 0] <- NA

# xyz 
mdtdf <- as.data.frame(mdt, xy = TRUE)
names(mdtdf)[3] <- "prof"

# contours
ggplot(mdtdf) +
geom_textcontour(aes(x, y, z = prof), bins = 6, 
                   hjust = "auto",
                   straight = TRUE,
                   remove_long = TRUE,
                   size = 2, 
                   padding = unit(0.05, "in")) +
  geom_sf(data = bal, fill = "black", colour = NA) +
  coord_sf(expand = FALSE) +
  theme_void() 

med_elev

Question on angle

Hi, Thanks for developing this nice package!
I didn't see a simple answer, and maybe this was not the point of this package, but isn't there a simple way to make the label angle simply follow the theta angle instead of the curvature in cases where axis label are too dense to be displayed along the circle (so a +90° shift). maybe a simple option could be added to coord_curvedpolar() to easily switch?

Hex logo

Too soon for a hex sticker? Maybe something like this?

library(geomtextpath)
#> Loading required package: ggplot2

p <- hexSticker::sticker(
       ggplot(data.frame(x = seq(0, 10, 0.01),
                         y = 10 - sqrt(100 - seq(0, 10, 0.01)^2),
                         label = "geomtextpath"), 
              aes(x, y, label = label)) +
          geom_textpath(vjust = -0.5, size = 12, color = "white") +
          labs(x = "", y = "") +
          scale_y_continuous(breaks = 0:2 * 5, labels = round) +
          scale_x_continuous(breaks = 0:2 * 5, labels = round) +
          hexSticker::theme_transparent() +
          theme(axis.text = element_text(color = "white", size = 20),
                panel.grid = element_blank(),
                axis.ticks = element_blank(),
                axis.line = element_line(color = "white")),
       package = "", s_height = 1.2, s_width = 1.2, 
       s_x = 0.9, s_y = 0.9, h_fill = "#2332A9", h_color = "orange")

p

Created on 2021-11-19 by the reprex package (v2.0.0)

Originally posted by @AllanCameron in #8 (comment)

Installing Package - geomtextpath

Hi,
I am having problems installing the 'geomtextpath' package.
I have the following progress output:

remotes::install_github("AllanCameron/geomtextpath")
Downloading GitHub repo AllanCameron/geomtextpath@HEAD
Skipping 4 packages not available: textshaping, rlang, sf, systemfonts
Skipping 13 packages ahead of CRAN: colorspace, Rcpp, labeling, munsell, plyr, magrittr, stringi, stringr, scales, reshape2, gtable, digest, ggplot2
✓ checking for file ‘/private/var/folders/kz/3dpjfv_53g5_jy2fg9fm05qw0000gp/T/RtmpegDQ4w/remotes90546c717c/AllanCameron-geomtextpath-1d8be74/DESCRIPTION’ ...
─ preparing ‘geomtextpath’:
✓ checking DESCRIPTION meta-information ...
─ checking for LF line-endings in source and make files and shell scripts
─ checking for empty or unneeded directories
─ building ‘geomtextpath_0.1.0.tar.gz’

Installing package into ‘/Users/michael/.checkpoint/2016-08-22/lib/x86_64-apple-darwin17.0/4.1.1’
(as ‘lib’ is unspecified)
ERROR: dependencies ‘sf’, ‘textshaping’ are not available for package ‘geomtextpath’

  • removing ‘/Users/michael/.checkpoint/2016-08-22/lib/x86_64-apple-darwin17.0/4.1.1/geomtextpath’
    Warning message:
    In i.p(...) :
    installation of package ‘/var/folders/kz/3dpjfv_53g5_jy2fg9fm05qw0000gp/T//RtmpegDQ4w/file90544f8e841b/geomtextpath_0.1.0.tar.gz’ had non-zero exit status

any ideas?
cheers
M

Keywords for placement of label

We were discussing in #27 that it might be convenient to have labels placed at some position. In particular, we were discussing hjust = "auto" for placing the label at the flattest part of the curve, but that got me thinking about other placement rules. I think the following keywords for the hjust parameter make sense:

  • "flattest", as we discussed before: at the flattest part of the curve. We can also do "steepest" to do the inverse, but that makes less sense to me.
  • "xmin"/"xmax"/"xmid" for placement at the leftmost/rightmost or middle horizontal position on the curve.
  • "ymin"/"ymax"/"ymid" for placement at the top, bottom or middle vertical position on the curve.
  • "before"/"after" for doing the equivalent of hjust = -1 or hjust = 2, where the text is anchored a textwidth away from the stated anchor point. This would translate to before the start of the curve or after the end of the curve (where the angles would be extrapolated based on the first/last angle). Consequently, this text would be flat and we can use the simplified per-string placement instead of the per-character placement.

This is not an exhaustive list, but these came to mind.

Chinese character support

I am considering this SO question as a feature request. I have no idea how feasible or practical this is, but it seems reasonable to explore it.

The following demonstrates the issue (it doesn't work via reprex due to limited unicode support)

library(tidyverse)
library(geomtextpath)
plot_data_2 <- data.frame(category=c('品类A','品类B','品类C','品类D'),
                          amount=c(1,6,4,7))

plot_data_2 %>% ggplot(aes(x=1,y=amount,fill=category))+
  geom_col()+geom_textpath(position = position_stack(vjust=0.5),
                           aes(label=category))+
  coord_polar()

image

Parameter names

Hi Alan,

I was thinking about these parameters we've introduced and whether there might be a more convenient way to pass them around. This made me think that now would be a good time to review the names of the parameters. The timing seems about right, because we've not written too many geoms yet such that this would be a huge burden, and we've no responsibility yet to users that these names remain stable. Some of them are quite long, so I thought of the following alternatives.

include_line

  • path
  • draw_path
  • text_only (need to flip TRUE/ FALSE then)

cut_path

  • trim (would have conflict with the density stat's trim parameter)
  • trim_path
  • bite, because we're taking a 'bite' from the path where the text should be
  • bite_path

flip_inverted

  • flip
  • autoflip
  • upright

halign

I think this is fine and have no better alternatives

offset

I think this is a fine name, but is confusing because we're using offset in two ways in our code, once as this parameter, which is just to pass a unit object to vjust, and once for actual path offsets.

  • vadjust for vertical adjustment
  • oadjust for an adjustment orthogonal to the direction of the text
  • ort_adj

keep_straight

  • straight
  • straight_text
  • straighten
  • curve_text (flip the meaning of TRUE/FALSE)
  • bend_text " "

I'm curious to hear your thoughts and wonder if you might have some suggestions of your own!

Manual textsmoothing?

Hi all,

Thanks for the fantastic package. I'm not sure if this is planning on being implemented but I think some kind of scale_textsmooth_manual or a vector of textsmooth inputs would be useful idea to keep in mind. It's been an issue that has been coming up for me while I've been using the package. Thank you so much!

Ignoring unknown parameters: text_smoothing

I am using geomtextpath_0.1.0.9000 in R and I get:

Ignoring unknown parameters: text_smoothing

My code:

ggplot(dt, aes(x = year, y = val)) + 
	scale_color_brewer(palette="Set1") +
	geom_textline(aes(label = Series, color = Series), vjust = -0.5, hjust = "ymax", linewidth = 2, text_smoothing = 30)

What seems to be the issue?
My session info below:

> sessionInfo() 
R version 4.1.0 (2021-05-18)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 19042)

Matrix products: default

locale:
[1] LC_COLLATE=English_United States.1252 
[2] LC_CTYPE=English_United States.1252   
[3] LC_MONETARY=English_United States.1252
[4] LC_NUMERIC=C                          
[5] LC_TIME=English_United States.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] remotes_2.4.2           ggstream_0.1.0          ggh4x_0.2.1            
 [4] ggrepel_0.9.1           ggforce_0.3.3           forcats_0.5.1          
 [7] geomtextpath_0.1.0.9000 RColorBrewer_1.1-3      scales_1.1.1           
[10] ggtext_0.1.1            ggplot2_3.3.5           fstcore_0.9.12         
[13] fst_0.9.8               dplyr_1.0.8             data.table_1.14.2

coord_curvedpolar

I have developed a new coord, though it still needs documented and tested before I commit the changes. Effectively, it uses textpath grobs for axis labels:

p <- ggplot(data.frame(x = paste("Category label", 1:5), y = runif(5)), 
            aes(x, y, fill = x)) +
       geom_col() +
       theme_bw() +
       theme(panel.border = element_blank(),
             legend.position = "none")

p

image

p + coord_polar()

image

p + coord_curvedpolar()

image

 p + coord_curvedpolar() + 
       theme(textpath.axis.text = element_textpath(color = "blue4", size = 15))

image

Is this something we're both happy to incorporate in the package? I think it improves the look of the native polar coordinate labels considerably. Not sure about what to call it though.

Feature request - multiple labels per line

Allan, first of all, you know that I am a big fan of this package. Incredible feat!

I just tried to create a slope graph with it (I wanted to add to my answer to this Stackoverflow thread, but it failed, because apparently it only allows one label per line (maybe I've overlooked something).

I am adding my ggh4x approach to show the current in my opinion most straight forward solution for a slope graph, but it would be cool to be able to do this with geom_textpath.

I can see that the use cases would be fairly limited and thus I'd understand if this should not have high priority for development. Anyways :)

library(ggh4x)
#> Loading required package: ggplot2

df <- data.frame(x = rep(1:5, each = 5), 
                 y = c(outer(seq(0, .8, .2), seq(0.02, 0.1, 0.02), `+`)),
                 cat = rep(paste0("a", 1:5)))

ggplot(df, aes(x, y)) +
  geom_text(aes(label = cat)) +
  geom_pointpath(aes(group = cat, shape = NA))

library(geomtextpath)
ggplot(df) +
  geom_textline(aes(x, y, label = cat, group = cat)) 

Created on 2022-05-29 by the reprex package (v2.0.1)

Request something like "dodge" along path

Thanks for this package!

Would it be possible to provide more control over where the text / label is placed along the curve? I think in general putting it in the middle is a good guess, but sometime the middle is where things are the most busy, as in the example below.

library(tidyverse)
library(geomtextpath)

set.seed(2022)
tibble(x = seq(0, 1, 0.01),
       a_fairly_long_label = rnorm(x, mean = 0, sd = 1),
       b_fairly_long_label = rnorm(x, mean = 0.5, sd = 1)
) %>%
  pivot_longer(cols = c(a_fairly_long_label, b_fairly_long_label), 
               names_to = "group", values_to = "y") %>%
  ggplot(aes(x, y, linetype = group)) +
  #geom_smooth(se = FALSE) +
  geom_labelsmooth(aes(label = group), method = "loess", formula = y ~ x,
                   se = FALSE) +
  theme_minimal() +
  theme(legend.position = "none")

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

Reconstruct offsetted path from curvature?

Hey Alan,

I think I made some progress on how to avoid some overhead of embedding all offset information in the vjust parameter, but I'm stuck on something that I think you might know more about.

Near the end of .add_path_data(), how would we go about reconstructing the x/y positions of the offset curve? So far I got this:

# From .add_path_data
curvature <- diff_rads / (diff(.data$length) * pi)
length_correction <-  1 +  offset * curvature
effective_length  <- c(0, diff(.data$length) * length_correction)

# Get start points of the offset curve at 90 degrees from original start
xstart <- .data$x[1] + cos(rads[1] + pi / 2) * offset 
ystart <- .data$y[1] + sin(rads[1] + pi / 2) * offset

# Reconstruct xy positions of offset curve
xout <- cumsum(cos(rads) * effective_length) + xstart
yout <- cumsum(sin(rads) * effective_length) + ystart

However, this fails badly at the polar coordinates example. Do you have any clue on how to reconstruct xy points from the curvature?

For reference full code here, it's essentially a variation on .add_path_data() that I want to integrate at some point:
https://github.com/teunbrand/geomtextpath/blob/851f5fc5eca3a25009ecb936c52160940980c97a/R/geometry_helpers.R#L96

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.