Synchroniser l'animation de zoom du fond et du relief, dater les tuiles à l'horloge précise
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@ -389,8 +389,28 @@ const map = L.map('map', {
|
|||||||
map.attributionControl.setPrefix('');
|
map.attributionControl.setPrefix('');
|
||||||
L.control.scale({ metric: true, imperial: false, position: 'bottomleft' }).addTo(map);
|
L.control.scale({ metric: true, imperial: false, position: 'bottomleft' }).addTo(map);
|
||||||
|
|
||||||
const base = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
// Pendant l'animation de zoom, transformations SEULES (aucun niveau créé,
|
||||||
|
// tuiles chargées à la fin) : un niveau neuf n'est pas composé par Chrome
|
||||||
|
// (vide au départ, il est animé sur le thread principal, gelé ~90 ms par le
|
||||||
|
// chargement). Le fond sautait alors que le relief orienté, qui garde son
|
||||||
|
// niveau pair, glissait normalement — les deux couches se désynchronisaient.
|
||||||
|
// Leaflet 1.9 déclenche aussi « zoom » dès le DÉBUT de l'animation : même
|
||||||
|
// garde dans _resetView tant que la carte anime.
|
||||||
|
const ZoomAnimTileLayer = L.TileLayer.extend({
|
||||||
|
_animateZoom(e) { this._setView(e.center, e.zoom, true, true); },
|
||||||
|
_resetView(e) {
|
||||||
|
if (this._map && this._map._animatingZoom) {
|
||||||
|
this._setView(this._map.getCenter(), this._map.getZoom(), true, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
L.TileLayer.prototype._resetView.call(this, e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const base = new ZoomAnimTileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||||
maxZoom: 22, maxNativeZoom: 19, keepBuffer: 2, className: 'base-dark',
|
maxZoom: 22, maxNativeZoom: 19, keepBuffer: 2, className: 'base-dark',
|
||||||
|
// Sinon _setView recrée un niveau en cours d'animation malgré noUpdate.
|
||||||
|
updateWhenZooming: false,
|
||||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
||||||
}).addTo(map);
|
}).addTo(map);
|
||||||
|
|
||||||
@ -545,7 +565,7 @@ function tileUrl(key) {
|
|||||||
// réduites de moitié (nettes) ; au-delà du zoom natif, le niveau inférieur.
|
// réduites de moitié (nettes) ; au-delà du zoom natif, le niveau inférieur.
|
||||||
// Le niveau de tuile Leaflet est ici la numérotation standard (zoomOffset −1
|
// Le niveau de tuile Leaflet est ici la numérotation standard (zoomOffset −1
|
||||||
// avec des tuiles de 512 px).
|
// avec des tuiles de 512 px).
|
||||||
const EvenLevelTileLayer = L.TileLayer.extend({
|
const EvenLevelTileLayer = ZoomAnimTileLayer.extend({
|
||||||
_clampZoom(zoom) {
|
_clampZoom(zoom) {
|
||||||
let z = L.TileLayer.prototype._clampZoom.call(this, zoom);
|
let z = L.TileLayer.prototype._clampZoom.call(this, zoom);
|
||||||
if (this.options.evenLevels && z % 2) {
|
if (this.options.evenLevels && z % 2) {
|
||||||
|
|||||||
@ -405,7 +405,7 @@ def test_ui_reorder_shows_destination():
|
|||||||
def test_ui_uses_standard_tilelayer():
|
def test_ui_uses_standard_tilelayer():
|
||||||
"""L'interface s'appuie sur le LOD natif de Leaflet, pas sur un palier maison."""
|
"""L'interface s'appuie sur le LOD natif de Leaflet, pas sur un palier maison."""
|
||||||
from lidar_pipeline.mapui import _MAP_JS, render_html, ui_version
|
from lidar_pipeline.mapui import _MAP_JS, render_html, ui_version
|
||||||
assert "L.tileLayer(" in _MAP_JS
|
assert "L.TileLayer.extend(" in _MAP_JS # TileLayer Leaflet, LOD natif
|
||||||
assert "tileSize: META.tile_size" in _MAP_JS
|
assert "tileSize: META.tile_size" in _MAP_JS
|
||||||
assert "zoomOffset: META.zoom_offset" in _MAP_JS
|
assert "zoomOffset: META.zoom_offset" in _MAP_JS
|
||||||
assert "maxNativeZoom: META.max_native_zoom" in _MAP_JS
|
assert "maxNativeZoom: META.max_native_zoom" in _MAP_JS
|
||||||
@ -1169,3 +1169,21 @@ def test_background_tiles_before_sources(tmp_path, monkeypatch):
|
|||||||
assert fetched == [] and mapserve._bg["stats"]["telechargees"] == 1
|
assert fetched == [] and mapserve._bg["stats"]["telechargees"] == 1
|
||||||
assert mapserve._bg_process_one() is True
|
assert mapserve._bg_process_one() is True
|
||||||
assert fetched == [1]
|
assert fetched == [1]
|
||||||
|
|
||||||
|
|
||||||
|
def test_zoom_animation_transforms_only_on_all_tile_layers():
|
||||||
|
"""Pendant l'animation de zoom, AUCUNE couche ne crée de niveau : un
|
||||||
|
niveau neuf n'est pas composé par Chrome (animé sur le thread principal,
|
||||||
|
gelé ~90 ms par le chargement) — le fond sautait quand le relief orienté,
|
||||||
|
qui garde son niveau pair, glissait normalement : désynchronisés."""
|
||||||
|
import re
|
||||||
|
from lidar_pipeline.mapui import _MAP_JS
|
||||||
|
assert "_animateZoom(e) { this._setView(e.center, e.zoom, true, true); }" in _MAP_JS
|
||||||
|
# « zoom » part aussi au début de l'animation (Leaflet 1.9) : même garde
|
||||||
|
assert "if (this._map && this._map._animatingZoom) {" in _MAP_JS
|
||||||
|
# Sans quoi _setView recrée un niveau malgré noUpdate (tileZoomChanged)
|
||||||
|
assert _MAP_JS.count("updateWhenZooming: false") >= 2
|
||||||
|
# Le fond OSM ET les couches LiDAR héritent du comportement
|
||||||
|
assert re.search(r"const base = new ZoomAnimTileLayer\(", _MAP_JS)
|
||||||
|
assert "const EvenLevelTileLayer = ZoomAnimTileLayer.extend(" in _MAP_JS
|
||||||
|
assert "L.tileLayer(" not in _MAP_JS.replace("L.tileLayer` standard", "")
|
||||||
|
|||||||
@ -849,6 +849,12 @@ def _write_atomic(path, data):
|
|||||||
tmp = path.with_name(path.name + f".{os.getpid()}.part")
|
tmp = path.with_name(path.name + f".{os.getpid()}.part")
|
||||||
try:
|
try:
|
||||||
tmp.write_bytes(data)
|
tmp.write_bytes(data)
|
||||||
|
# Datée à l'horloge précise (time.time) : celle des dates de première
|
||||||
|
# apparition des dalles (_apply_seen). L'horloge grossière du noyau
|
||||||
|
# (mtime par défaut) retarde de quelques ms — une tuile écrite juste
|
||||||
|
# après l'arrivée d'une dalle paraîtrait sinon plus vieille qu'elle.
|
||||||
|
now = time.time()
|
||||||
|
os.utime(tmp, (now, now))
|
||||||
os.replace(tmp, path)
|
os.replace(tmp, path)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
logger.debug(f"Écriture de tuile impossible ({path}) : {e}")
|
logger.debug(f"Écriture de tuile impossible ({path}) : {e}")
|
||||||
|
|||||||
Reference in New Issue
Block a user