# Automatic direct labels for Makie with Voronoi tessellation

**URL:** <https://discourse.julialang.org/t/automatic-direct-labels-for-makie-with-voronoi-tessellation/90247>\
**Category:** General Usage\
**Created:** [November 14, 2022, 4:37pm UTC](https://discourse.julialang.org/t/automatic-direct-labels-for-makie-with-voronoi-tessellation/90247 "2022-11-14T16:37:52Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![dgleich](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dgleich/32/1494_2.png) [@dgleich](https://discourse.julialang.org/u/dgleich)\
**Post date:** [November 14, 2022, 4:37pm UTC](https://discourse.julialang.org/t/automatic-direct-labels-for-makie-with-voronoi-tessellation/90247/1 "2022-11-14T16:37:52Z")

</div>

Best practice for making plots is to directly label lines and avoid indirect legends.  
(See discussions by Edward Tufte on this: [https://twitter.com/EdwardTufte/status/1274213603151880192/photo/1](https://twitter.com/EdwardTufte/status/1274213603151880192/photo/1))

I’ve been trying to adapt and improve the ideas from this post on directly labeling lines:

> **[Directly labelling lines](https://observablehq.com/@harrystevens/directly-labelling-lines)**
>
> A common goal of data visualization is to show how some values have changed over time. More often than not, you will use a line chart for this purpose. But when you want to show several different values in the same chart, you will need to come up...

[I got something cooked up pretty quickly]  
([MakieDirectLabels/label-explainer.md at main · dgleich/MakieDirectLabels · GitHub](https://github.com/dgleich/MakieDirectLabels/blob/main/label-explainer.md))  
This is a protopackage, a playground until I can figure out more stuff I’d need for a package.

This creates a single command: voronoi\_labels!(…) to directly annotate the graph.  
(This currently requires an edit to VoronoiCells, see [pull request here](https://github.com/JuliaGeometry/VoronoiCells.jl/pull/33). )

## Example

 ![label-explainer_2_1](https://global.discourse-cdn.com/julialang/original/3X/e/d/ed1c3cd17b4fe6507bd7f9c8bc0b4a612caafac8.png)

```julia
using Random
Random.seed!(1)
using CairoMakie
include("voronoi-labels.jl")

f = lines(cumsum(randn(100)), label="Are Great",
  color=Cycled(1), marker=:rect)
lines!(f.axis, cumsum(randn(100)), label="Direct",
  color=Cycled(2))
lines!(f.axis, cumsum(randn(100)), label="Labels",
  color=Cycled(3))

# make it look nicer
f.axis.topspinevisible = false
f.axis.leftspinevisible = false
f.axis.rightspinevisible = false
f.axis.xgridvisible = false

labels = voronoi_labels!(f; offset=1.0)
labels[1].offset[] = 1.5 # manual tweak of one label 

f

```

## What I need help with

I’m trying to gauge interest in this idea and ideally, solicit some help.

Interaction with Makie:

- working with scales – everything really should work in screen / pixel space;  
what’s the best way to get and plot things directly in screen space on top of  
an existing axis with lines / scatter?

- getting bounding boxes of labels/ checking intersections/etc. (`boundingbox(text!(...))` does something I can’t seem to figure out in terms of information about plots in the axis.

- thoughts on how else this might be done / made easier / etc.!

## Vision

The idea is to make adding even easier than a legend!

There are a few ways to add these automatic labels in other software.

R has a similar package: [https://tdhock.github.io/directlabels/docs/index.html](https://tdhock.github.io/directlabels/docs/index.html)

- `tail_labels!()` # automatically labels the “tail” of the plot like on our world in data.

- `direct_labels!()` # find a good place to label each line (what the current voronoi labels does)

- `inline_labels!()` # like the python example… [GitHub - cphyc/matplotlib-label-lines: Label line using matplotlib.](https://github.com/cphyc/matplotlib-label-lines)

Thanks! David

---

<div class="post-metadata">

**Author:** ![svilupp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/svilupp/32/34933_2.png) [@svilupp](https://discourse.julialang.org/u/svilupp)\
**Post date:** [November 15, 2022, 7:58am UTC](https://discourse.julialang.org/t/automatic-direct-labels-for-makie-with-voronoi-tessellation/90247/2 "2022-11-15T07:58:52Z")

</div>

What a great idea!

These are the types of things that would make me move from Plots.jl to Makie (smarter defaults that help me make impactful charts in a breeze)

---

<div class="post-metadata">

**Author:** ![joa-quim](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/joa-quim/32/227_2.png) [@joa-quim](https://discourse.julialang.org/u/joa-quim)\
**Post date:** [November 16, 2022, 1:08am UTC](https://discourse.julialang.org/t/automatic-direct-labels-for-makie-with-voronoi-tessellation/90247/3 "2022-11-16T01:08:26Z")

</div>

Already _almost_ possible with GMT (a random not repeatable plot)

```julia
using GMT

plot(1:40, GMT.fakedata(40), region=(0,40,-10,10), decorated=(quoted=true, const_label="Direct", curved=true, font=(8,:red)), lc=:red)
plot!(1:40, GMT.fakedata(40), decorated=(quoted=true, const_label="Labels", curved=true, font=(8,:green)), lc=:green)
plot!(1:40, GMT.fakedata(40), decorated=(quoted=true, const_label="Are Great", curved=true, font=(8,:blue)), lc=:blue, show=1)

```

The improvements here would be to find a _sufficiently smooth_ line chunk to put the labels, and be able to do the plot with a single call.

 ![GMTjl_tmp](https://global.discourse-cdn.com/julialang/original/3X/2/c/2c87841f8b098a45f17cef2602b3c77e135fd8d9.png)

---

<div class="post-metadata">

**Author:** ![dgleich](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dgleich/32/1494_2.png) [@dgleich](https://discourse.julialang.org/u/dgleich)\
**Post date:** [November 16, 2022, 3:04am UTC](https://discourse.julialang.org/t/automatic-direct-labels-for-makie-with-voronoi-tessellation/90247/4 "2022-11-16T03:04:16Z")

</div>

Neat! I was going to look at the problem of finding straight enough regions soon.

---

<div class="post-metadata">

**Author:** ![dgleich](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dgleich/32/1494_2.png) [@dgleich](https://discourse.julialang.org/u/dgleich)\
**Post date:** [November 16, 2022, 3:16am UTC](https://discourse.julialang.org/t/automatic-direct-labels-for-makie-with-voronoi-tessellation/90247/5 "2022-11-16T03:16:08Z")

</div>

Does anyone know how to show the bounding for a section of text in CairoMakie inside of a plot?

```julia
using CairoMakie, StableRNGs
x = cumsum(randn(StableRNG(1), 100).^2)
f = lines(x, axis=(yscale=log10,))
lbl = text!(f.axis.scene, text="Direct Labels are Great!", 50, 50, align=(:right,:bottom))
bb = boundingbox(lbl)
wireframe!(f.axis.scene, bb, color=:red, space=:pixel)
f

```

Expected output: The text is framed with a red box!

Actual output:

 ![download](https://global.discourse-cdn.com/julialang/original/3X/d/c/dc52d31dc064d758074072f448bdfb36da7f50ee.png)

(The bounding box info is clearly not “data” space …, but none of the other spaces work either.)

---

<div class="post-metadata">

**Author:** ![fatteneder](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fatteneder/32/33991_2.png) [@fatteneder](https://discourse.julialang.org/u/fatteneder)\
**Post date:** [November 16, 2022, 11:37am UTC](https://discourse.julialang.org/t/automatic-direct-labels-for-makie-with-voronoi-tessellation/90247/6 "2022-11-16T11:37:02Z")

</div>

This might be a bug in `Makie.jl`.

This here draws a red box around the text, but the text is then placed wrongly.

```julia
using CairoMakie, StableRNGs                                                                                                                                                                                                 
x = cumsum(randn(StableRNG(1), 100).^2)                                                             
f = lines(x)                                                                                        
scene = campixel(f.axis.scene)                                                                      
lbl = text!(scene, 50, 50, text="Direct Labels are Great!", align=(:right,:bottom))                 
bb = boundingbox(lbl)                                                                               
wireframe!(scene, bb, color=:red, space=:pixel)                                                     
save("mwe_makie.png", f)         

```

What I changed is

1. remove `axis=(yscale=log10,)`
2. Create a new `Scene` with `campixel` and use that to place the `text`.

I think that both points interfer somehow with each other…

Here is the output:

 ![image](https://global.discourse-cdn.com/julialang/original/3X/d/e/dea58468396e9e6ef1433fc090e8126b8e444414.png)

It was mentioned here [How to draw a textbox around a text in Makie.jl? - #2 by ffreyer](https://discourse.julialang.org/t/how-to-draw-a-textbox-around-a-text-in-makie-jl/75672/2) that `boundingbox` does not do the right thing. However, I could not get the workaround to work either (would need more time).

We should open an issue over at `Makie.jl`.

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://discourse.julialang.org/u/jules)\
**Post date:** [November 17, 2022, 5:50pm UTC](https://discourse.julialang.org/t/automatic-direct-labels-for-makie-with-voronoi-tessellation/90247/7 "2022-11-17T17:50:01Z")

</div>

I really like the idea of direct labelling. One reason why I’ve so far stayed away from implementing something like it is that I couldn’t quickly come up with clear semantics how labels should be placed given that a multitude of plot objects might be in a given Axis, and the Axis can change size due to layout recalculations. Effectively, the position of one label would depend on all other plot objects in the Axis, correct? (In order to minimize visual overlap). Right now, one plot object doesn’t even have access to the others in the same Axis, so that part is not quite clear to me. And if it’s supposed to work with interactive figures, the updating process would also have to be clarified.

Overall, great effort, and I’m very interested to see where this goes.

---

<div class="post-metadata">

**Author:** ![dgleich](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dgleich/32/1494_2.png) [@dgleich](https://discourse.julialang.org/u/dgleich)\
**Post date:** [November 19, 2022, 3:01am UTC](https://discourse.julialang.org/t/automatic-direct-labels-for-makie-with-voronoi-tessellation/90247/8 "2022-11-19T03:01:11Z")

</div>

The interaction with interactive plots will be interesting. Right now, I’m just trying to get things to make sense and be easy to use and give good results for a static plot. However, I would note that just the simple axis and ticks have all of the same challenges in terms of number of plots and datapoints, etc. I will try and take a quick look at how they solve similar issues.

---

<div class="post-metadata">

**Author:** ![dgleich](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dgleich/32/1494_2.png) [@dgleich](https://discourse.julialang.org/u/dgleich)\
**Post date:** [November 19, 2022, 3:20am UTC](https://discourse.julialang.org/t/automatic-direct-labels-for-makie-with-voronoi-tessellation/90247/9 "2022-11-19T03:20:23Z")

</div>

I did open an issue over at Makie.jl

> <https://github.com/MakieOrg/Makie.jl/issues/2424>
>
> There appear to be some issues with text bounding boxes when used within an axis…. Especially when the axis is scaled by a transform. 
> 
> Expected behavior
> \`\`\`
> using CairoMakie, StableRNGs
> x = cumsum(randn(StableRNG(1), 100).^2)
> f = lines(x, axis=(yscale=log10,))
> lbl = text!(f.axis, text="Direct Labels are Great!", 50, 50, align=(:right,:bottom))
> bb = boundingbox(lbl)
> wireframe!(f.axis, bb, color=:red, space=:data)
> f
> \`\`\`
> shows a bounding box around the text string.
> 
> Instead, this shows: 
> !\[download\](https://user-images.githubusercontent.com/33995/202203746-ebdb8f4b-d749-4ab6-965b-3e9e3625fe67.png)
> 
> Changing \`wireframe!(f.axis, bb, color=:red, space=:data)\` to \`wireframe!(f.axis.scene, bb, color=:red, space=:pixel)\` shows no bounding box. Adding \`campixel(f.axis.scene)\` does not work either. Plotting \`text!\` into \`f.axis.scene\` also made no difference. 
> 
> Where we do see somewhat expected behavior 
> \`\`\`
> using CairoMakie, StableRNGs                                                                                                                                                                                                 
> x = cumsum(randn(StableRNG(1), 100).^2)
> f = lines(x) # CHANGE, no log10 scale...
> scene = campixel(f.axis.scene) # CHANGE
> lbl = text!(scene, 200, 200, text="Direct Labels are Great!", align=(:right,:bottom),space=:pixel)                 
> bb = boundingbox(lbl)                                                                               
> wireframe!(scene, bb, color=:red, space=:pixel)         
> f
> \`\`\`
> !\[download\](https://user-images.githubusercontent.com/33995/202205852-be80bd67-9b04-4a7a-918b-cc85e47021b2.png)
> 
> This does work, but requires \_pixel coordinates\_ for the text rather than data coordinates. 
> 
> This also fails with specifying a logscale y axis...
> 
> \`\`\`
> using CairoMakie, StableRNGs                                                                                                                                                                                                 
> x = cumsum(randn(StableRNG(1), 100).^2)
> f = lines(x, axis=(yscale=log10,)) # CHANGE, log10 scale...
> scene = campixel(f.axis.scene) # CHANGE
> lbl = text!(scene, 200, 200, text="Direct Labels are Great!", align=(:right,:bottom),space=:pixel)                 
> bb = boundingbox(lbl)                                                                               
> wireframe!(scene, bb, color=:red, space=:pixel)         
> f
> \`\`\`
> 
> !\[download\](https://user-images.githubusercontent.com/33995/202206004-e601b23c-fc40-425f-a3ad-c0b07b6ca253.png)
> 
> 
> This was verified on the most recent versions I could update to of CairoMakie (CairoMakie v0.9.2) and Makie (Makie v0.18.2)
> 
> See origin of this discussion here:
> https://discourse.julialang.org/t/automatic-direct-labels-for-makie-with-voronoi-tessellation/90247/4
