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:
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> 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)