Skip to content

Instantly share code, notes, and snippets.

@patte
Last active August 26, 2026 23:30
Show Gist options
  • Select an option

  • Save patte/7480ff313bf95267b81cb6ad7da0af85 to your computer and use it in GitHub Desktop.

Select an option

Save patte/7480ff313bf95267b81cb6ad7da0af85 to your computer and use it in GitHub Desktop.
maplibre-gl 6.6.0: stale terrain drape textures after a zoom animation — press ① then ②
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>maplibre-gl: stale terrain drape textures after a zoom animation</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://unpkg.com/maplibre-gl@6.6.0/dist/maplibre-gl.css" rel="stylesheet">
<style>
html, body { margin: 0; height: 100%; }
#map { position: absolute; inset: 0; }
#panel {
position: absolute; top: 10px; left: 10px; z-index: 1;
background: rgba(20, 20, 24, 0.94); color: #eee; padding: 16px;
font: 16px/1.5 system-ui, sans-serif; border-radius: 10px; max-width: 400px;
}
#panel b { font-size: 18px; }
#panel button {
display: block; width: 100%; margin: 8px 0; padding: 12px;
font: inherit; cursor: pointer; border-radius: 8px;
background: #3a6df0; color: #fff; border: none;
}
#panel button:hover:enabled { background: #4c7cff; }
#panel button:disabled {
background: #2a2a30; color: #9a9aa4; border: 1px solid #44444c; cursor: default;
}
#status { color: #fc6; min-height: 4.5em; }
</style>
</head>
<body>
<div id="map"></div>
<div id="panel">
<b>Stale drape textures after a zoom animation</b>
<p>The rings are draped over the terrain and styled by zoom: <b style="color:#e8a33d">amber</b> below z16.5, <b style="color:#e8452e">red</b> above. The map rests at z16.05, so rings must be amber.</p>
<button id="ease" disabled>&#9312; animate: ease to z17.5 and back</button>
<button id="rebake" disabled>&#9313; force re-bake &mdash; must change nothing</button>
<div id="status">loading&hellip;</div>
</div>
<script type="module">
import * as maplibregl from 'https://unpkg.com/maplibre-gl@6.6.0/dist/maplibre-gl.mjs';
const status = (t) => { document.getElementById('status').textContent = t; };
const map = new maplibregl.Map({
container: 'map',
center: [10.190674, 45.93998],
zoom: 16.05,
style: 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json',
fadeDuration: 0,
});
window.map = map;
// concentric rings: the staleness shows in the bends, so give the texture bends in
// every direction; the zoom-interpolated width adds a second visible mismatch
const CLAT = 45.93998, CLNG = 10.190674;
const lines = [];
for (let r = 60; r <= 1500; r += 90) {
const ring = [];
for (let a = 0; a <= 96; a++) {
const t = (a / 96) * 2 * Math.PI;
ring.push([
CLNG + (r * Math.sin(t)) / (111320 * Math.cos((CLAT * Math.PI) / 180)),
CLAT + (r * Math.cos(t)) / 111320,
]);
}
lines.push(ring);
}
map.on('load', () => {
map.addSource('dem', {
type: 'raster-dem', encoding: 'terrarium',
tiles: ['https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png'],
maxzoom: 12, tileSize: 256,
});
map.setTerrain({source: 'dem'});
map.addSource('grid', {type: 'geojson', data: {
type: 'Feature',
geometry: {type: 'MultiLineString', coordinates: lines},
properties: {},
}});
map.addLayer({
id: 'grid', type: 'line', source: 'grid',
paint: {
// zoom-stepped color: any ring drawn red while the map rests below z16.5 is a
// texture that was baked at a zoom the map is no longer at
'line-color': ['step', ['zoom'], '#e8a33d', 16.5, '#e8452e'],
'line-width': ['interpolate', ['exponential', 2], ['zoom'], 15, 1.5, 18, 10],
},
});
map.once('idle', () => {
document.getElementById('ease').disabled = false;
status('ready — this is the correct rendering for z16.5');
});
});
document.getElementById('ease').addEventListener('click', () => {
status('animating…');
map.easeTo({zoom: 17.5, duration: 1200});
map.once('moveend', () => setTimeout(() => {
map.easeTo({zoom: 16.05, duration: 1200});
map.once('moveend', () => map.once('idle', () => {
document.getElementById('rebake').disabled = false;
status('settled back at z16.05 — every ring must be amber and thin again. Any red, fat ring ' +
'is a stale texture, baked mid-animation above z16.5. Now press ②.');
}));
}, 400));
});
document.getElementById('rebake').addEventListener('click', () => {
// a layout-property change reloads the grid source, which invalidates the cached
// drape textures — the camera is untouched, so a correct renderer changes nothing
map.setLayoutProperty('grid', 'visibility', 'none');
map.setLayoutProperty('grid', 'visibility', 'visible');
status('');
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment