How To Make Concentric Polar Axes with Custom Band Size in Makie?

Hi folks! :waving_hand:

On the part of my ongoing quest: Building a Julia-Powered E-Ink Dashboard: A Dev Log - #11 by TheCedarPrince

I would like to make multiple polar axes concentric with custom band widths. Here are two polar axes I have:

Temperature data:

Wind data:


As an example, what I would like to do is put the wind data polar axis within the temperature polar axis plots I have made. What I do not know is the following:

  1. How to link the same center for polar axes across multiple different polar axes
  2. How to control the width of bands formed between the distance of the outer and inner radius of the polar axis (say like 1.5cm or something like that)
  3. How to stack polar axes together in one plot.

In an ideal world for 2, I would like to have something like this:

ax = PolarAxis(
    f[1, 1], 
    bandwidth = .25
)

Where the .25 corresponds extending 25% of the band’s size from the outside radius to the center. And then you could say .5 for a bandwidth that is halfway to the center, etc.


Any thoughts on how to do this?

Thanks!

~ tcp :deciduous_tree:

I would’ve thought something like this should have worked:

fig = Figure()
ax = PolarAxis(fig[1,1]; rlimits = (5.0, 10.0))
lines!(ax, range(0, 2π; length = 101), fill(6.0, 101))

ax_inset = PolarAxis(fig[1,1];
    rlimits = (0.0, 3.0),
    width=Relative(0.3),
    height=Relative(0.3),
    halign=0.5,
    valign=0.5
)
lines!(ax_inset, range(0, 2π; length = 101), fill(2.0, 101))

translate!(ax_inset.blockscene, 0, 0, 150)
fig

but it doesn’t quite, it produces this:

It might be possible to fiddle with how individual elements are z-translated to get it to work, but perhaps polar insets are not quite supported.

Edit:

[e9467ef8] GLMakie v0.13.4
[ee78f7c6] Makie v0.24.4
Julia Version 1.11.6
OS: Windows (x86_64-w64-mingw32)
1 Like

Yea, that’s what I have also learned with tinkering… I am also just not quite sure how to make it work.

Thanks for trying a hand at this here @JonasWickman!

The problem is that Makie doesn’t have arbitrary clip paths, so the clipping of PolarAxis is done with an opaque polygon, which is the one covering your inset

Oh interesting. So, would there be any way to achieve what I am trying to do with Makie? Or what would it take to get this working within Makie?

as long as no data actually clips outside it might work to just hide the clip polygon

1 Like

Oh that’s great! So would I want to play around with these settings:

Or is it something more fundamental @jules?

For future reference (my own if nothing else), setting clip = false does indeed do the trick.

using GLMakie
fig = Figure()
ax = PolarAxis(fig[1,1]; rlimits = (5.0, 10.0), clip = false)
lines!(ax, range(0, 2π; length = 101), fill(6.0, 101))

ax_inset = PolarAxis(fig[1,1];
    rlimits = (0.0, 3.0), clip = false,
    width=Relative(0.3),
    height=Relative(0.3),
    halign=0.5,
    valign=0.5
)
lines!(ax_inset, range(0, 2π; length = 101), fill(2.0, 101))

fig

2 Likes

After much experimentation, I did indeed find that your suggestions worked wonders here @JonasWickman ! :smiley: What I ended up doing is making the rlimits of each PolarAxis break up between 0, 3.33, 6.66, 10 to create reasonably sized bandwidths. Here’s an example of a plot I made; still need to tweak the layout to make it cleaner, but it captures the vision:


For future reference, here is what the code looks like. Folks can adapt it however they see fit:

using CairoMakie
f = Figure(
    size = (240, 240)
);

# Scale data in-between band ranges
function scale_vals(data, r)
    r_min = minimum(data)
    r_max = maximum(data)
    t_min = r[1]
    t_max = r[2]

    [(m - r_min)/(r_max - r_min) * (t_max - t_min) + t_min for m in data]
end

# Outside ring
ax = PolarAxis(
    f[1, 1], 
    width = 200,
    height = 240,
    rminorgridvisible = false,
    rminorticksvisible = false,
    rgridvisible = false,
    rticks = LinearTicks(2),
    rticklabelsize = 10,
    rticklabelsvisible = false,
    rgridcolor = :blue,
    rlimits = (6.66, 10),
    thetaminorticksvisible = false,
    thetaticks = (collect(-3pi/2:15 * pi / 180:pi/2)[1:end-1], vcat("0", ["$x" for x in 23:-1:1])),
    thetaticklabelsize = 14,
    thetaminorticks = IntervalsBetween(3),
    thetagridcolor = :black,
    thetagridvisible = false,
    thetagridwidth = 1,
    clip = false
)

scatter!(ax, collect(-3pi/2:15 * pi / 180:pi/2)[1:end-1], scale_vals(reverse(hourly_vars["wind_speed_10m"]), ax.rlimits.val), color = :black, markersize = 12, marker = '↑', rotation = hourly_vars["wind_direction_10m"] .* (pi / 180))

# Middle ring
ax = PolarAxis(
    f[1, 1],
    width=120,
    height=120,
    rminorgridvisible = false,
    rminorticksvisible = false,
    rgridvisible = false,
    rticks = LinearTicks(2),
    rticklabelsize = 10,
    rticklabelsvisible = false,
    rgridcolor = :blue,
    rlimits = (3.33, 6.66),
    thetaminorticksvisible = false,
    thetaticks = (collect(-3pi/2:15 * pi / 180:pi/2)[1:end-1], vcat("0", ["$x" for x in 23:-1:1])),
    thetaticklabelsize = 10,
    thetaticklabelsvisible = false,
    thetaminorticks = IntervalsBetween(3),
    thetagridcolor = :black,
    thetagridvisible = false,
    thetagridwidth = 1,
    clip = false,
)

norm_val = ax.rlimits.val[1] / minimum(hourly_vars["temperature_2m"]) 

scatter!(ax, collect(-3pi/2:15 * pi / 180:pi/2)[1:end-1], scale_vals(reverse(hourly_vars["temperature_2m"]), ax.rlimits.val), color = :black, markersize = 8, marker = markers)

# Inside ring
ax = PolarAxis(
    f[1,1];
    width=70,
    height=70,
    rlimits = (0, 3.33),
    rminorgridvisible = false,
    rminorticksvisible = false,
    rgridvisible = false,
    rticks = LinearTicks(2),
    rticklabelsize = 10,
    rticklabelsvisible = false,
    rgridcolor = :blue,
    thetaminorticksvisible = false,
    thetaticks = (collect(-3pi/2:15 * pi / 180:pi/2)[1:end-1], vcat("0", ["$x" for x in 23:-1:1])),
    thetaticklabelsvisible = false,
    thetaminorticks = IntervalsBetween(3),
    thetagridcolor = :black,
    thetagridvisible = false,
    thetagridwidth = 1,
    clip = false,
);

p = barplot!(ax, collect(-3pi/2:15 * pi / 180:pi/2)[1:end-1], rand(0:.1:3.33, 24))
pp = popat!(p.plots, findfirst(x -> x isa Poly, p.plots))
polys = map(pp[1].value) do rects
    map(rects) do rect
        N_steps = 100
        mini = minimum(rect); maxi = maximum(rect)
        ps = Point2f[mini, Point2f(mini[1], maxi[2]), maxi, Point2f(maxi[1], mini[2]), mini]
        ps = map(range(0, 4, length = N_steps)) do f
            ps[1] * max(0, 1-f) + 
            ps[2] * max(0, 1 - abs(f-1)) + 
            ps[3] * max(0, 1 - abs(f-2)) + 
            ps[4] * max(0, 1 - abs(f-3)) + 
            ps[5] * max(0, 1 - abs(f-4))
        end
        Makie.Polygon(ps)
    end
end
poly!(p, attributes(pp), polys[1], strokecolor = :black, strokewidth = 2, color = :black)