-
-
Notifications
You must be signed in to change notification settings - Fork 568
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Leaflet: map flickers between centers after flyTo #3035
Comments
Thanks a lot for reporting this issue, @backbord! I just bisected it and identified commit a2544c1 (PR #2867), which was a collaboration with @afullerx and part of release 1.4.23. Somehow the way we process the outbox queue has an effect on the Leaflet element. I'll try to investigate further. But if anyone else has an idea, I'm all ears. |
I was able to consistently reproduce the bug using 1.4.21. (Windows 10, Python 3.12). While the changes made in #2867 may be causing it to be triggered more consistently for more users, it seems as though it is not the root cause. |
Ah, actually that kind of makes sense: When introducing asyncio events in PR #2867, we implicitly removed a short delay of 0.01s between loop cycles. Now the outbox reacts immediately when new messages are added to the queue, which could have changed the behavior of Line 61 in 614ea49
the flicker disappears. Why does the problem already exist on Windows before PR #2867? As we noticed in #2482, Windows tends to ignore delays shorter than 0.016s, so the implicit delay of 0.01s is ineffective. Now I want to find out the exact order of messages that leads to the endless loop. Maybe it's a Leaflet-specific problem. Otherwise we need to rethink the outbox. |
You nailed it there @falkoschindler. Here is the sequence of messages/updates after the button is clicked:
After the "flyTo" operation is completed, the following updates are issued (only the
An incorrect update is issued with the original Munich coordinates, quickly followed by the one with the correct Berlin coordinates. When a delay is present, the first update is overwritten before it is emitted, and everything functions properly. Without the delay, both updates are issued in quick succession, causing the leaflet to enter an endless loop navigating between them. |
Thanks, @afullerx! By the way, I thought I might be able to reproduce the problem with plain HTML/JavaScript, but this code works perfectly fine: <html>
<head>
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" />
</head>
<body>
<div id="map" style="height: 400px"></div>
<button id="flyToBerlin">Berlin</button>
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
<script>
const map = L.map("map").setView([48.1, 11.6], 10);
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png").addTo(map);
document.getElementById("flyToBerlin").onclick = () => map.flyTo([52.5, 13.4], 9, { duration: 1.0 });
map.on("moveend", console.log);
</script>
</body>
</html> |
I think outbox.py is ok and print(f'{time.time():.6f}', 'outbox enqueue_update', getattr(element, 'center', None)) and print(f'{time.time():.6f}', 'outbox enqueue_message', message_type, data) to outbox.py and run this slightly modified example: count = {'value': 0}
def tick():
count['value'] += 1
if count['value'] == 10:
sys.exit(0) # interrupt endless loop
m = ui.leaflet(center=(48.1, 11.6), zoom=10)
m.on('map-moveend', lambda e: (print(f'{time.time():.6f}', 'on map-moveend:', e.args['center']), tick()))
ui.button('Berlin', on_click=lambda: (print('click'), m.run_map_method('flyTo', [52.5, 13.4], 9, {'duration': 1.0}))) Output:
And the oscillation begins. |
Got it! Here is what happens:
|
I just noticed that the "Wait for Initialization" demo is flickering as well. It's probably the same problem. |
Thank you for looking into it and the fix! |
Hi again! While the patch fixes the behavior in most cases, sometimes the map still begins to flicker. For my use case, I've come up with this workaround, which is far from perfect. def avoid_flickering(leaflet: ui.leaflet) -> None:
# This is a workaround for https://github.com/zauberzeug/nicegui/issues/3035
# as it hasn't been solved completely.
last_centers: deque[tuple[float, float]] = deque(maxlen=2)
last_move_at = 0.0
def _avoid_flickering(event: events.GenericEventArguments) -> None:
nonlocal last_move_at
lat, lon = event.args["center"]
center = (lat, lon)
now = time.monotonic()
time_since_last_move = now - last_move_at
# print(f"{time_since_last_move:.4f} {last_centers} {center}")
if (
time_since_last_move < 0.05
and len(last_centers) > 1
and last_centers[1] != center == last_centers[0]
):
# try to stop flickering
leaflet.run_map_method("off", "moveend")
leaflet.set_center(last_centers[1])
leaflet.run_map_method("on", "moveend")
print(
f"Flickering detected. "
f"Attempting to stop by setting center to {last_centers[-1]}."
)
last_centers.append(center)
last_move_at = now
leaflet.on("map-moveend", _avoid_flickering) Please feel free to suggest improvements. :) Hope this helps! Thanks |
@backbord, you could also try increasing the sleep durations in nicegui/nicegui/elements/leaflet.py Lines 85 to 91 in 63c2024
It would be useful to know if there's a duration that works reliably for you. With the current sleep of only 20ms, there's a lot of room to play with. |
Description
Hi all,
thanks for the great project! Unfortunately, I came across a possible regression with an undesired effect:
I've updated from NiceGUI 1.4.21 to 1.4.24 and now this small example flickers between the old and the new center after the button was clicked.
There appears to be an endless loop issuing
moveend
events, alternating between the old and the new center, causing the map to flicker and not draw as desired. A pan or zoom with the mouse breaks the loop.Sometimes, it doesn't go into this bad loops and just works as desired.
Output:
I didn't see changes to leaflet in the diff from 1.4.21 to 1.4.24 and haven't looked deeper into the issue.
Do you have an idea what might cause this loop and how to fix ist?
Thanks!
The text was updated successfully, but these errors were encountered: