Skip to content

Commit eabea8d

Browse files
authored
Add support for Makie's new Annotation recipe (#663)
```julia smallest_largest = sort(penguins, :body_mass_g)[[1, end], :] smallest_largest.x_offset = [-80, 20] smallest_largest.y_offset = [-20, -80] base_mapping = mapping(:bill_length_mm, :bill_depth_mm, color = :species) penguin_layer = data(penguins) * base_mapping * visual(Scatter, alpha = 0.4) text_layer = data(smallest_largest) * mapping( # from :x_offset, :y_offset, # to :bill_length_mm, :bill_depth_mm, color = :species, text = :body_mass_g => x -> verbatim("$(x)g"), ) * visual(Annotation, style = Ann.Styles.LineArrow()) draw(penguin_layer + text_layer) ``` <img width="585" alt="image" src="https://github.com/user-attachments/assets/5118f309-2893-4b07-865a-db057547b861" />
1 parent db8b30a commit eabea8d

File tree

9 files changed

+102
-10
lines changed

9 files changed

+102
-10
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## Unreleased
44

5+
## v0.10.8 - 2025-06-06
6+
7+
- Added support for the Makie's new `Annotation` recipe [#663](https://github.com/MakieOrg/AlgebraOfGraphics.jl/pull/663).
8+
59
## v0.10.7 - 2025-05-27
610

711
- Fixed facet labels in paginated plots with a `Layout` scale in which categories were also relabeled using the `categories = pairs` mechanism [#661](https://github.com/MakieOrg/AlgebraOfGraphics.jl/pull/661).

Project.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "AlgebraOfGraphics"
22
uuid = "cbdf2221-f076-402e-a563-3d30da359d67"
33
authors = ["Pietro Vertechi", "Julius Krumbiegel"]
4-
version = "0.10.7"
4+
version = "0.10.8"
55

66
[deps]
77
Accessors = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697"
@@ -50,7 +50,7 @@ GridLayoutBase = "0.6, 0.7, 0.8, 0.9, 0.10, 0.11"
5050
Isoband = "0.1"
5151
KernelDensity = "0.6"
5252
Loess = "0.5.1, 0.6.1"
53-
Makie = "0.22.5"
53+
Makie = "0.22.10"
5454
NaturalSort = "1"
5555
PlotUtils = "1"
5656
PolygonOps = "0.1.1"

docs/src/tutorials/intro-iv.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,23 +160,31 @@ draw(spec_nonnumeric)
160160

161161
### `verbatim`
162162

163-
As you already know, string columns are treated as categorical by AlgebraOfGraphics. But sometimes we may want to just pass strings as they are to Makie's plotting functions. The main situation where this happens is when we are adding `Text` plots because the `Text` plot type expects a vector of string-like objects as its `text` attribute.
163+
As you already know, string columns are treated as categorical by AlgebraOfGraphics. But sometimes we may want to just pass strings as they are to Makie's plotting functions. The main situation where this happens is when we are adding `Annotation` or `Text` plots because they expect a vector of string-like objects as their `text` attribute.
164164

165165
For example, we can label a couple of our penguins with their body weight, by making a subset dataframe, and applying both a string formatting function and the `verbatim` function to the `body_mass_g` column:
166166

167167
```@example tut
168168
smallest_largest = sort(penguins, :body_mass_g)[[1, end], :]
169+
smallest_largest.x_offset = [-80, 20]
170+
smallest_largest.y_offset = [-20, -80]
169171
170172
base_mapping = mapping(:bill_length_mm, :bill_depth_mm, color = :species)
171173
172-
penguin_layer = data(penguins) * base_mapping * visual(Scatter, alpha = 0.2)
174+
penguin_layer = data(penguins) * base_mapping * visual(Scatter, alpha = 0.4)
173175
174176
text_layer = data(smallest_largest) *
175-
base_mapping *
176177
mapping(
178+
# from
179+
:x_offset,
180+
:y_offset,
181+
# to
182+
:bill_length_mm,
183+
:bill_depth_mm,
184+
color = :species,
177185
text = :body_mass_g => x -> verbatim("$(x)g"),
178186
) *
179-
visual(Makie.Text, fontsize = 20)
187+
visual(Annotation, style = Ann.Styles.LineArrow())
180188
181189
draw(penguin_layer + text_layer)
182190
```

src/aesthetics.jl

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -478,4 +478,39 @@ function aesthetic_mapping(::Type{Stairs}, N::Int)
478478
:color => AesColor,
479479
:linestyle => AesLineStyle,
480480
])
481-
end
481+
end
482+
483+
aesthetic_mapping(::Type{Annotation}, ::Normal, ::Normal) = aesthetic_mapping(Annotation, 2)
484+
aesthetic_mapping(::Type{Annotation}, ::Normal, ::Normal, ::Normal, ::Normal) = aesthetic_mapping(Annotation, 4)
485+
486+
487+
function aesthetic_mapping(::Type{Annotation}, N::Int)
488+
@assert N in (2, 4)
489+
positionals = if N == 4
490+
[
491+
1 => :labelspace => dictionary([
492+
:data => AesX,
493+
:relative_pixel => AesAnnotationOffsetX,
494+
]),
495+
2 => :labelspace => dictionary([
496+
:data => AesY,
497+
:relative_pixel => AesAnnotationOffsetY,
498+
]),
499+
3 => AesX,
500+
4 => AesY,
501+
]
502+
else
503+
[
504+
1 => AesX,
505+
2 => AesY,
506+
]
507+
end
508+
509+
dictionary([
510+
positionals...,
511+
:color => AesColor,
512+
:textcolor => AesColor,
513+
])
514+
end
515+
516+
mandatory_attributes(::Type{Annotation}) = dictionary([:labelspace => :relative_pixel])

src/algebra/layers.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ function hardcoded_or_mapped_aes(processedlayer, key::Union{Int,Symbol}, aes_map
290290
hardcoded = hardcoded_mapping(key)
291291
hardcoded !== nothing && return hardcoded
292292
if !haskey(aes_mapping, key)
293-
throw(ArgumentError("ProcessedLayer with plot type $(processedlayer.plottype) did not have $(repr(key)) in its AestheticMapping. The mapping was $aes_mapping"))
293+
throw(ArgumentError("ProcessedLayer with plot type $(processedlayer.plottype) did not have $(repr(key)) in its AestheticMapping. That means AlgebraOfGraphics does not know how to handle this attribute in its scale system. Note that you can use any array-valued attribute in a `mapping` if you bypass the scale system using the `=> verbatim` helper. The available mapping dictionary was:\n $(sprint(print, aes_mapping))"))
294294
end
295295
return aes_mapping[key]
296296
end
@@ -602,7 +602,7 @@ function values_to_markersizes(data, sizerange, extrema)
602602
end
603603
end
604604

605-
function full_rescale(data, aes::Type{<:Union{AesX,AesY,AesZ,AesDeltaX,AesDeltaY,AesDeltaZ}}, scale::ContinuousScale)
605+
function full_rescale(data, aes::Type{<:Union{AesX,AesY,AesZ,AesDeltaX,AesDeltaY,AesDeltaZ,AesAnnotationOffsetX,AesAnnotationOffsetY}}, scale::ContinuousScale)
606606
return data
607607
end
608608

src/guides/legend.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ function legend_elements(T::Type{LinesFill}, attributes, scale_args::MixedArgume
441441
]
442442
end
443443

444-
function legend_elements(T::Type{Makie.Text}, attributes, scale_args::MixedArguments)
444+
function legend_elements(T::Type{<:Union{Makie.Text,Annotation}}, attributes, scale_args::MixedArguments)
445445
[PolyElement(
446446
color = _get(T, scale_args, attributes, :color),
447447
)]

src/scales.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ struct AesContourColor <: Aesthetic end # this is just so the third contour argu
2828
struct AesPlaceholder <: Aesthetic end # choropleth for example takes as first arg only geometries which avoid scales, but for now we still have to give an aes to 1, so this can serve for that purpose
2929
struct AesABIntercept <: Aesthetic end
3030
struct AesABSlope <: Aesthetic end
31+
struct AesAnnotationOffsetX <: Aesthetic end
32+
struct AesAnnotationOffsetY <: Aesthetic end
3133

3234
# helper to dissociate scales belonging to the same Aesthetic type
3335
struct ScaleID

test/reference_tests.jl

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1443,3 +1443,46 @@ reftest("rainclouds") do
14431443
legend!(f[2, 2], fg2)
14441444
f
14451445
end
1446+
1447+
reftest("annotation", true) do
1448+
f = Figure(size = (600, 450))
1449+
text = string.(range('A', length = 5)) => verbatim
1450+
spec1 = mapping(1:5, 1:5) *
1451+
(
1452+
visual(Annotation) * mapping(; text) +
1453+
visual(Scatter)
1454+
)
1455+
draw!(f[1, 1], spec1)
1456+
spec2 = mapping(1:5, 1:5, color = 1:5) *
1457+
(
1458+
visual(Annotation) * mapping(; text) +
1459+
visual(Scatter)
1460+
)
1461+
draw!(f[1, 2], spec2)
1462+
spec3 = mapping(1:5, 1:5, color = 1:5 => nonnumeric) *
1463+
(
1464+
visual(Annotation) * mapping(; text) +
1465+
visual(Scatter)
1466+
)
1467+
fg = draw!(f[2, 1][1, 1], spec3)
1468+
legend!(f[2, 1][1, 2], fg)
1469+
spec4 = mapping(
1470+
[30, 15, 0],
1471+
[100, 50, 30],
1472+
1:3,
1473+
1:3;
1474+
text = ["A", "B", "C"] => verbatim,
1475+
) * visual(Annotation) +
1476+
mapping(1:5, 1:5) * visual(Scatter) +
1477+
mapping(50, -50, 3, 3, text = "point" => verbatim) *
1478+
visual(Annotation, style = Ann.Styles.LineArrow(), path = Ann.Paths.Arc(height = -0.3)) +
1479+
mapping(
1480+
[4, 4.5],
1481+
[3, 4],
1482+
4:5,
1483+
4:5;
1484+
text = ["D", "E"] => verbatim,
1485+
) * visual(Annotation; labelspace = :data)
1486+
draw!(f[2, 2], spec4)
1487+
f
1488+
end
28.9 KB
Loading

0 commit comments

Comments
 (0)