# GLMakie: selecting points in log-log plot

**URL:** <https://discourse.julialang.org/t/glmakie-selecting-points-in-log-log-plot/130371>\
**Category:** Visualization\
**Tags:** makie, gui, glmakie\
**Created:** [July 1, 2025, 2:17pm UTC](https://discourse.julialang.org/t/glmakie-selecting-points-in-log-log-plot/130371 "2025-07-01T14:17:28Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![aris](https://avatars.discourse-cdn.com/v4/letter/a/258eb7/32.png) [@aris](https://discourse.julialang.org/u/aris)\
**Post date:** [July 1, 2025, 2:17pm UTC](https://discourse.julialang.org/t/glmakie-selecting-points-in-log-log-plot/130371/1 "2025-07-01T14:17:29Z")

</div>

Hello everyone,

I have a gui which involves a contour plot with log-log axes.  
The goal is to be able to select some points from within the plot by clicking on in.

Everything works fine in the case of linear axes:

> **MRE for linear axes**
>
> ```julia
> using GLMakie
> 
> f = Figure()
> ax = Axis(f[:,:],)
> 
> # make some data to plot 
> x = range(0.001, 1, 100)
> y = x
> N = 100
> σ = 15.0
> μ = N / 2
> z = [exp(-((x - μ)^2 + (y - μ)^2) / (2 * σ^2)) for y in 1:N, x in 1:N]
> 
> contour!(ax,x,y,z)
> 
> point = select_point(f.content[1].scene, marker=:circle)
> # Update selection Observable by pushing the selected point to it
> on(point) do _
> display(point)
> end
> 
> ```

but as soon as the axis get changed to log10:

> **MRE for log10 axes**
>
> ```julia
> using GLMakie
> 
> f = Figure()
> ax = Axis(f[:,:], xscale = log10, yscale = log10)
> 
> # make some data to plot 
> x = logrange(0.001, 1, 100)
> y = x
> N = 100
> σ = 15.0
> μ = N / 2
> z = [exp(-((x - μ)^2 + (y - μ)^2) / (2 * σ^2)) for y in 1:N, x in 1:N]
> 
> contour!(ax,x,y,z)
> 
> point = select_point(f.content[1].scene, marker=:circle)
> # Update selection Observable by pushing the selected point to it
> on(point) do _
> display(point)
> end
> 
> ```

there are errors:

```julia
 Error in callback:
DomainError with -0.594361:
log10 was called with a negative real argument but will only return a complex result if called with a complex argument. Try log10(Complex(x)).

```

It seems that the `select_point` function returns `log10.(point)` rather than `point`, or something like that.

When I was working on this, about a year ago, I ended up using two sets of axes, one for displaying the actual log-scale values, and a linear one from 0 to 1 to selecting points from it.  
This used to work, but it becomes a bit problematic now that I need to be able to zoom in and out, while keeping these axes synced.

Would there be any straightforward ways to deal with either of these issues?  
That is, either select the points directly from the log-log axes without pesky errors;  
or keep two axes with different values in sync.

---

<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:** [July 1, 2025, 3:11pm UTC](https://discourse.julialang.org/t/glmakie-selecting-points-in-log-log-plot/130371/2 "2025-07-01T15:11:48Z")

</div>

Sounds like a bug in Makie, should be easy enough to fix if the projection is just applied incorrectly somewhere?

---

<div class="post-metadata">

**Author:** ![raman\_kumar](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/raman_kumar/32/26782_2.png) [@raman\_kumar](https://discourse.julialang.org/u/raman_kumar)\
**Post date:** [July 1, 2025, 4:13pm UTC](https://discourse.julialang.org/t/glmakie-selecting-points-in-log-log-plot/130371/3 "2025-07-01T16:13:43Z")

</div>

> [@aris](#):
>
> ```julia-auto
> Error in callback:
> DomainError with -0.594361:
> log10 was called with a negative real argument but will only return a complex result if called with a complex argument. Try log10(Complex(x)).
> 
> ```

Why not you supply absolute values inside `log10()` function?

```julia-auto
z = abs.([exp(-((x - μ)^2 + (y - μ)^2) / (2 * σ^2)) for y in 1:N, x in 1:N])

```

- Earlier i was also having same issue of evaluating log of negative values. I overcame this issue by writing safe\_log() function for normalized input values.

- Or Makie.jl should define a function that evaluates `log10(-5) = -log10(5)`

---

<div class="post-metadata">

**Author:** ![aris](https://avatars.discourse-cdn.com/v4/letter/a/258eb7/32.png) [@aris](https://discourse.julialang.org/u/aris)\
**Post date:** [July 1, 2025, 7:23pm UTC](https://discourse.julialang.org/t/glmakie-selecting-points-in-log-log-plot/130371/4 "2025-07-01T19:23:57Z")

</div>

> [@raman\_kumar](#):
>
> `z = abs.([exp(-((x - μ)^2 + (y - μ)^2) / (2 * σ^2)) for y in 1:N, x in 1:N])`

But in this case z is already full of positive values, I don’t see how that changes things.

---

<div class="post-metadata">

**Author:** ![aris](https://avatars.discourse-cdn.com/v4/letter/a/258eb7/32.png) [@aris](https://discourse.julialang.org/u/aris)\
**Post date:** [July 1, 2025, 7:29pm UTC](https://discourse.julialang.org/t/glmakie-selecting-points-in-log-log-plot/130371/5 "2025-07-01T19:29:42Z")

</div>

> [@jules](#):
>
> the projection is just applied incorrectly somewhere?

Thought so as well, but looking at the code for; the `select_point` function, it doesn’t seem obvious to me.  
Perhaps it has to do with the line `point = Observable([Point2f(0,0)])`?

Would there be any alternatives in case that’s not straightforward to fix?

---

<div class="post-metadata">

**Author:** ![raman\_kumar](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/raman_kumar/32/26782_2.png) [@raman\_kumar](https://discourse.julialang.org/u/raman_kumar)\
**Post date:** [July 1, 2025, 7:36pm UTC](https://discourse.julialang.org/t/glmakie-selecting-points-in-log-log-plot/130371/6 "2025-07-01T19:36:11Z")

</div>

Sorry, Please find where and which variable is supplying negative value in`log10()` ? Please paste full error message that you are getting. In error message we can see where `log10()` is getting error.

---

<div class="post-metadata">

**Author:** ![aris](https://avatars.discourse-cdn.com/v4/letter/a/258eb7/32.png) [@aris](https://discourse.julialang.org/u/aris)\
**Post date:** [July 1, 2025, 7:40pm UTC](https://discourse.julialang.org/t/glmakie-selecting-points-in-log-log-plot/130371/7 "2025-07-01T19:40:10Z")

</div>

> [@raman\_kumar](#):
>
> where and which variable is supplying negative value in`log10()`

Yes, I’m trying to find that but it does not seem very obvious 🙂

> **Here's the error**
>
> ```julia
> DomainError with -2.646445:
> log10 was called with a negative real argument but will only return a complex result if called with a complex argument. Try log10(Complex(x)).
> Stacktrace:
> [1] throw_complex_domainerror(f::Symbol, x::Float32)
> @ Base.Math ./math.jl:33
> [2] _log
> @ ./special/log.jl:330 [inlined]
> [3] log10(x::Float32)
> @ Base.Math ./special/log.jl:259
> [4] apply_transform
> @ ~/.julia/packages/Makie/ux0Te/src/layouting/transformation.jl:373 [inlined]
> [5] #995
> @ ~/.julia/packages/Makie/ux0Te/src/layouting/transformation.jl:369 [inlined]
> [6] iterate
> @ ./generator.jl:48 [inlined]
> [7] _collect(c::Vector{Point{2, Float32}}, itr::Base.Generator{Vector{Point{2, Float32}}, Makie.var"#995#996"{Tuple{typeof(log10), typeof(log10)}}}, ::Base.EltypeUnknown, isz::Base.HasShape{1})
> @ Base ./array.jl:811
> [8] collect_similar
> @ ./array.jl:720 [inlined]
> [9] map
> @ ./abstractarray.jl:3371 [inlined]
> [10] apply_transform
> @ ~/.julia/packages/Makie/ux0Te/src/layouting/transformation.jl:369 [inlined]
> [11] apply_transform
> @ ~/.julia/packages/Makie/ux0Te/src/layouting/transformation.jl:312 [inlined]
> [12] apply_transform_and_f32_conversion(float32convert::Makie.LinearScaling, transform_func::Tuple{typeof(log10), typeof(log10)}, model::StaticArraysCore.SMatrix{4, 4, Float64, 16}, data::Vector{Point{2, Float32}}, space::Symbol)
> @ Makie ~/.julia/packages/Makie/ux0Te/src/float32-scaling.jl:331
> [13] invokelatest(::Any, ::Any, ::Vararg{Any}; kwargs::@Kwargs{})
> @ Base ./essentials.jl:1055
> [14] invokelatest(::Any, ::Any, ::Vararg{Any})
> @ Base ./essentials.jl:1052
> [15] (::Observables.MapCallback)(value::Any)
> @ Observables ~/.julia/packages/Observables/YdEbO/src/Observables.jl:436
> [16] #invokelatest#2
> @ ./essentials.jl:1055 [inlined]
> [17] invokelatest
> @ ./essentials.jl:1052 [inlined]
> [18] notify
> @ ~/.julia/packages/Observables/YdEbO/src/Observables.jl:206 [inlined]
> [19] #70
> @ ./tuple.jl:692 [inlined]
> [20] BottomRF
> @ ./reduce.jl:86 [inlined]
> [21] afoldl
> @ ./operators.jl:553 [inlined]
> [22] _foldl_impl
> @ ./reduce.jl:68 [inlined]
> [23] foldl_impl
> @ ./reduce.jl:48 [inlined]
> [24] mapfoldl_impl
> @ ./reduce.jl:44 [inlined]
> [25] mapfoldl
> @ ./reduce.jl:175 [inlined]
> [26] foldl
> @ ./reduce.jl:198 [inlined]
> [27] foreach
> @ ./tuple.jl:692 [inlined]
> [28] (::Makie.var"#306#307"{UnionAll, Tuple{Observable{Vector{Point{2, Float32}}}}})(kw::Vector{Pair{Symbol, Any}}, args::Vector{Point{2, Float32}})
> @ Makie ~/.julia/packages/Makie/ux0Te/src/interfaces.jl:185
> [29] invokelatest(::Any, ::Any, ::Vararg{Any}; kwargs::@Kwargs{})
> @ Base ./essentials.jl:1055
> [30] invokelatest(::Any, ::Any, ::Vararg{Any})
> @ Base ./essentials.jl:1052
> [31] (::Observables.OnAny)(value::Any)
> @ Observables ~/.julia/packages/Observables/YdEbO/src/Observables.jl:420
> [32] #invokelatest#2
> @ ./essentials.jl:1055 [inlined]
> [33] invokelatest
> @ ./essentials.jl:1052 [inlined]
> [34] notify
> @ ~/.julia/packages/Observables/YdEbO/src/Observables.jl:206 [inlined]
> [35] setindex!
> @ ~/.julia/packages/Observables/YdEbO/src/Observables.jl:123 [inlined]
> [36] (::Makie.var"#1306#1308"{Bool, Scene, Scatter{Tuple{Vector{Point{2, Float32}}}}, Observable{Point{2, Float32}}, Observable{Vector{Point{2, Float32}}}, Observable{Bool}, Makie.Mouse.Button})(event::Makie.MouseButtonEvent)
> @ Makie ~/.julia/packages/Makie/ux0Te/src/interaction/interactive_api.jl:399
> [37] #invokelatest#2
> @ ./essentials.jl:1055 [inlined]
> [38] invokelatest
> @ ./essentials.jl:1052 [inlined]
> [39] notify
> @ ~/.julia/packages/Observables/YdEbO/src/Observables.jl:206 [inlined]
> [40] setindex!
> @ ~/.julia/packages/Observables/YdEbO/src/Observables.jl:123 [inlined]
> [41] (::GLMakie.var"#mousebuttons#170"{Observable{Makie.MouseButtonEvent}})(window::GLFW.Window, button::GLFW.MouseButton, action::GLFW.Action, mods::Int32)
> @ GLMakie ~/.julia/packages/GLMakie/87u59/src/events.jl:104
> [42] _MouseButtonCallbackWrapper(window::GLFW.Window, button::GLFW.MouseButton, action::GLFW.Action, mods::Int32)
> @ GLFW ~/.julia/packages/GLFW/wmoTL/src/callback.jl:43
> [43] PollEvents
> @ ~/.julia/packages/GLFW/wmoTL/src/glfw3.jl:702 [inlined]
> [44] pollevents(screen::GLMakie.Screen{GLFW.Window}, frame_state::Makie.TickState)
> @ GLMakie ~/.julia/packages/GLMakie/87u59/src/screen.jl:546
> [45] on_demand_renderloop(screen::GLMakie.Screen{GLFW.Window})
> @ GLMakie ~/.julia/packages/GLMakie/87u59/src/screen.jl:1033
> [46] renderloop(screen::GLMakie.Screen{GLFW.Window})
> @ GLMakie ~/.julia/packages/GLMakie/87u59/src/screen.jl:1061
> [47] (::GLMakie.var"#79#80"{GLMakie.Screen{GLFW.Window}})()
> @ GLMakie ~/.julia/packages/GLMakie/87u59/src/screen.jl:922
> Observable(Float32[-2.646445, -1.0124221])
> 0 => (::var"#3#4")(::Any) @ Main REPL[18]:2
> 
> ```

---

<div class="post-metadata">

**Author:** ![aris](https://avatars.discourse-cdn.com/v4/letter/a/258eb7/32.png) [@aris](https://discourse.julialang.org/u/aris)\
**Post date:** [July 2, 2025, 8:03am UTC](https://discourse.julialang.org/t/glmakie-selecting-points-in-log-log-plot/130371/8 "2025-07-02T08:03:26Z")

</div>

I suppose a decent temporary fix would be to plot `log10.(x), log10.(y)` instead of `x, y` and then transform the values.

The following seems to work for the tick values.  
`ax.xtickformat = xs -> [L"10^{%$x}" for x in xs]`

---

<div class="post-metadata">

**Author:** ![asinghvi17](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/asinghvi17/32/8272_2.png) [@asinghvi17](https://discourse.julialang.org/u/asinghvi17)\
**Post date:** [July 2, 2025, 12:57pm UTC](https://discourse.julialang.org/t/glmakie-selecting-points-in-log-log-plot/130371/9 "2025-07-02T12:57:56Z")

</div>

[Fix select\_point to operate correctly in scenes with a transform\_func by asinghvi17 · Pull Request #5144 · MakieOrg/Makie.jl · GitHub](https://github.com/MakieOrg/Makie.jl/pull/5144) should fix it. Turns out we were not applying the inverse transform.

You can copy this statement and paste it into your REPL, then execute your logscale code again. Things should work after that.

> **Code to copy**
>
> ```julia
> @eval Makie begin
> 
> """
> select_point(scene; kwargs...) -> point
> 
> Interactively select a point on a 2D `scene` by clicking the left mouse button,
> dragging and then un-clicking. Return an **observable** whose value corresponds
> to the selected point on the scene. In addition the function
> _plots_ the point on the scene as the user clicks and moves the mouse
> around. When the button is not clicked any more, the plotted point disappears.
> 
> The value of the returned point is updated **only** when the user un-clicks.
> 
> The `kwargs...` are propagated into `scatter!` which plots the selected point.
> """
> function select_point(scene; blocking = false, priority=2, space = :data, kwargs...)
> key = Mouse.left
> waspressed = Observable(false)
> point = Observable([Point2f(0,0)])
> point_ret = Observable(Point2f(0,0))
> # Create an initially hidden arrow
> plotted_point = scatter!(
> scene, point; space = :data, transformation = Makie.Transformation(scene; transform_func = identity), visible = false, marker = Circle, markersize = 20px,
> color = RGBAf(0.1, 0.1, 0.8, 0.5), kwargs...,
> )
> 
> onany(events(scene).mousebutton, transform_func_obs(scene), priority=priority) do event, transform_func
> if event.button == key && is_mouseinside(scene)
> mp = mouseposition(scene)
> if event.action == Mouse.press
> waspressed[] = true
> plotted_point[:visible] = true # start displaying
> point[][1] = mp
> point[] = point[]
> return Consume(blocking)
> end
> end
> if !(event.button == key && event.action == Mouse.press)
> if waspressed[] # User has selected the rectangle
> waspressed[] = false
> final_point = project(scene, :data, space, point[][1])
> if space == :data
> final_point = apply_transform(inverse_transform(transform_func), final_point)
> end
> point_ret[] = final_point
> end
> plotted_point[:visible] = false
> return Consume(blocking)
> end
> return Consume(false)
> end
> on(events(scene).mouseposition, priority=priority) do event
> if waspressed[]
> mp = mouseposition(scene)
> point[][1] = mp
> point[] = point[] # actually update observable
> return Consume(blocking)
> end
> return Consume(false)
> end
> 
> return point_ret
> end
> 
> end
> 
> ```

---

<div class="post-metadata">

**Author:** ![aris](https://avatars.discourse-cdn.com/v4/letter/a/258eb7/32.png) [@aris](https://discourse.julialang.org/u/aris)\
**Post date:** [July 2, 2025, 1:12pm UTC](https://discourse.julialang.org/t/glmakie-selecting-points-in-log-log-plot/130371/10 "2025-07-02T13:12:37Z")

</div>

That does it, thanks @asinghvi17!

In case I’m using this in a package, would it be wise to just copy the snippet above into it, or should I wait for the next makie release?

---

<div class="post-metadata">

**Author:** ![asinghvi17](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/asinghvi17/32/8272_2.png) [@asinghvi17](https://discourse.julialang.org/u/asinghvi17)\
**Post date:** [July 2, 2025, 7:02pm UTC](https://discourse.julialang.org/t/glmakie-selecting-points-in-log-log-plot/130371/11 "2025-07-02T19:02:55Z")

</div>

If you need it immediately you could just copy it - otherwise I would advise waiting for a release
