# Makie on(Observable) combo triggers multiple times

**URL:** <https://discourse.julialang.org/t/makie-on-observable-combo-triggers-multiple-times/115039>\
**Category:** Visualization\
**Tags:** makie, observables\
**Created:** [June 1, 2024, 2:09am UTC](https://discourse.julialang.org/t/makie-on-observable-combo-triggers-multiple-times/115039 "2024-06-01T02:09:39Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![oscarvdvelde](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscarvdvelde/32/202157_2.png) [@oscarvdvelde](https://discourse.julialang.org/u/oscarvdvelde)\
**Post date:** [June 1, 2024, 2:09am UTC](https://discourse.julialang.org/t/makie-on-observable-combo-triggers-multiple-times/115039/1 "2024-06-01T02:09:39Z")

</div>

I made a data visualization tool with Makie where observables watch my axis.finallimits selection to update the selected indices, which then triggers the plotting. This involves two `on()` blocks.

The problem is that it triggers multiple times, going back and forth between the `on()` blocks. I have so far mitigated the issue using a trick using `Dates.now()` to require more than a few hundred milliseconds since the last time it entered the block.

I now looked deeper into the cause of the issue. It happens only when a function is called in the on(finallimits) block AND a plot is made in the on(selection) block. Here is a MWE:

```julia
using GLMakie, Random, Dates

GLMakie.activate!(; focus_on_show=true, title="test")
fig = Figure(size = (900,900))
ax1 = Axis(fig[1,1])
lon = randn(100_000);
lat = randn(100_000).+41;
selection = Observable(trues(100_000))
s1 = scatter!(ax1, lon,lat)
display(fig)

function lonlatkm(reflat) # WGS84 ellipsoid, returns distance in km of 1 deg
    y_convfactor_km = (111132.954 - 559.822 * cos(2 * reflat * π/180) + 1.175 * cos(4 * reflat * π/180))/1000
    x_convfactor_km = (π*6378137 * cos(reflat * π/180) / (180 * sqrt(1 - 0.00669437999014 * sin(reflat * π/180)*sin(reflat * π/180))))/1000
    return x_convfactor_km, y_convfactor_km
end

# The o1 block, when used instead of o2, is not triggering multiple times.

# o1 = on(ax1.finallimits) do xylims
# println("inside on(ax1.finallimits) " * string(now()))
# lon1 = minimum(xylims)[1]
# lon2 = maximum(xylims)[1]
# lat1 = minimum(xylims)[2]
# lat2 = maximum(xylims)[2]
# ax1.limits.val = ((lon1, lon2), (lat1, lat2))
# selection[] = (lon1 .< lon .<= lon2) .&& (lat1 .< lat .<= lat2)
# end

# o2 uses a function to make sure any arbitrary lat-lon zoom results in a square geographic area.

o2 = on(ax1.finallimits) do xylims
    println("inside on(ax1.finallimits) " * string(now()))
    x1 = minimum(xylims)[1]
    x2 = maximum(xylims)[1]
    y1 = minimum(xylims)[2]
    y2 = maximum(xylims)[2]
    mx = 0.5 * (x1 + x2)
    my = 0.5 * (y1 + y2)
    dx = (x2 - x1) * lonlatkm(my)[1]
    dy = (y2 - y1) * lonlatkm(my)[2]
    dxy = 0.25 * (dx + dy)
    lon1 = mx - dxy / lonlatkm(my)[1]
    lon2 = mx + dxy / lonlatkm(my)[1]
    lat1 = my - dxy / lonlatkm(my)[2]
    lat2 = my + dxy / lonlatkm(my)[2]
    ax1.limits.val = ((lon1, lon2), (lat1, lat2))
    selection[] = (lon1 .< lon .<= lon2) .&& (lat1 .< lat .<= lat2)
end

s = on(selection) do sel
    println("inside on(selection) " * string(now()))
    empty!(ax1)
    scatter!(ax1, lon[sel], lat[sel])
end

# the s block is not triggering multiple times if it does not contain scatter, or with o1 block instead of o2.

```

Results:

```julia
julia> inside on(ax1.finallimits) 2024-06-01T03:54:20.256
inside on(selection) 2024-06-01T03:54:20.416
inside on(ax1.finallimits) 2024-06-01T03:54:20.432
inside on(selection) 2024-06-01T03:54:20.432
inside on(ax1.finallimits) 2024-06-01T03:54:20.432
inside on(selection) 2024-06-01T03:54:20.432
inside on(ax1.finallimits) 2024-06-01T03:54:20.432
inside on(selection) 2024-06-01T03:54:20.432
inside on(ax1.finallimits) 2024-06-01T03:54:20.448
inside on(selection) 2024-06-01T03:54:20.448
inside on(ax1.finallimits) 2024-06-01T03:54:20.450
inside on(selection) 2024-06-01T03:54:20.450

```

Perhaps it is happening because also without the `on(selection)` plotting routine, it will plot a zoomed map of the dots, so it may be an interference issue. It may not do that when using `o1` as that the axis limits are the same. Is there a way to disable the native plot update following a rectangle zoom in the axis? (without disabling the rectangle selection itself)

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [June 1, 2024, 4:07am UTC](https://discourse.julialang.org/t/makie-on-observable-combo-triggers-multiple-times/115039/2 "2024-06-01T04:07:03Z")

</div>

Perhaps not a direct fix, but perhaps adding a [`Observable.throttle`](https://juliagizmos.github.io/Observables.jl/stable/#Observables.throttle-Union%7BTuple%7BT%7D,%20Tuple%7BAny,%20AbstractObservable%7BT%7D%7D%7D%20where%20T) might help?

---

<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:** [June 1, 2024, 5:49am UTC](https://discourse.julialang.org/t/makie-on-observable-combo-triggers-multiple-times/115039/3 "2024-06-01T05:49:54Z")

</div>

`scatter!` causes a limit update so you have a loop in the logic. You could make the limits update function conditional on some boolean you set to true while you are adding the scatter. You currently cannot disable the update itself.

---

<div class="post-metadata">

**Author:** ![oscarvdvelde](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscarvdvelde/32/202157_2.png) [@oscarvdvelde](https://discourse.julialang.org/u/oscarvdvelde)\
**Post date:** [June 1, 2024, 1:50pm UTC](https://discourse.julialang.org/t/makie-on-observable-combo-triggers-multiple-times/115039/4 "2024-06-01T13:50:45Z")

</div>

That sounds like what I was doing, executing `on(ax1.finallimits)` block only when some minimum amount of time passed since the last time. I tried if I could throttle the observables after creating the first plot, which could be a more elegant solution:

```julia
using Observables
throttle(0.5,ax1.finallimits)
throttle(0.5,ax1.limits)

```

But, I see no effect on the multiple triggering.

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [June 1, 2024, 3:22pm UTC](https://discourse.julialang.org/t/makie-on-observable-combo-triggers-multiple-times/115039/5 "2024-06-01T15:22:47Z")

</div>

You are not using it correctly. `throttle` does not modify its argument. It returns a signal to is throttled.

```julia
on(throttle(0.5, selection)) do s
   # ...
end

```

---

<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:** [June 1, 2024, 3:30pm UTC](https://discourse.julialang.org/t/makie-on-observable-combo-triggers-multiple-times/115039/6 "2024-06-01T15:30:16Z")

</div>

In my opinion throttling is definitely less elegant than prohibiting the clear loop you have there from happening at the precise location where it happens 🙂 but your mileage may vary
