fix: render choropleth as single image overlay — eliminates all tile seams
All checks were successful
Deploy Internet for Kids / Build & Push (push) Successful in 26s
Deploy Internet for Kids / Deploy (push) Successful in 6s
Deploy Internet for Kids / Health Check (push) Successful in 2s
Deploy Internet for Kids / Smoke Tests (push) Successful in 3s
Deploy Internet for Kids / IndexNow Ping (push) Successful in 7s
Deploy Internet for Kids / Promote to Latest (push) Successful in 2s
Deploy Internet for Kids / Rollback (push) Has been skipped
Deploy Internet for Kids / Audit (push) Successful in 2s

GeoJSON sources are internally tiled by geojson-vt, causing seams at
tile boundaries during panning. Solution: pre-render the choropleth
to a canvas, convert to PNG data URL, and add as a MapLibre image
source. Single image = zero tiles = zero seams.

GeoJSON source kept invisible for hover/click hit testing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Christian Gick
2026-04-04 17:43:29 +03:00
parent 302a02eebd
commit b1cc054df0

View File

@@ -24,7 +24,8 @@ body { margin: 0; overflow: hidden; }
var LANG = 'en';
try { var m = parent.location.pathname.match(/^\/(de|fr|en)\//); if (m) LANG = m[1]; } catch(e) {}
var STATUS_COLORS = { enforced: '#667eea', passed: '#764ba2', progress: '#a5b4fc', guidelines: '#e0e7ff' };
// Pre-blended with base map — fully opaque to prevent tile seam artifacts from transparency
var STATUS_COLORS = { enforced: '#9cabf1', passed: '#a68ac3', progress: '#c5cefd', guidelines: '#ebefff' };
var STATUS_LABELS = {
en: { enforced: 'Enforced', passed: 'Passed', progress: 'In Progress', guidelines: 'Guidelines' },
de: { enforced: 'In Kraft', passed: 'Verabschiedet', progress: 'In Bearbeitung', guidelines: 'Richtlinien' },
@@ -41,6 +42,7 @@ body { margin: 0; overflow: hidden; }
center: [20, 20],
zoom: 2,
scrollZoom: false,
fadeDuration: 0,
style: 'https://maps.clicksports.de/styles/klokantech-basic/style.json'
});
_ifkMap.addControl(new maplibregl.NavigationControl({ showCompass: false }), 'top-right');
@@ -65,28 +67,57 @@ body { margin: 0; overflow: hidden; }
geo.features.forEach(function(f) {
var id = String(f.id).padStart(3, '0');
var c = byIsoNum[id];
f.properties.fillColor = c ? STATUS_COLORS[c.status] : '#dce4ec';
f.properties.fillOpacity = c ? 0.65 : 0.1;
f.properties.fillColor = c ? STATUS_COLORS[c.status] : 'transparent';
f.properties.fillOpacity = c ? 1 : 0;
f.properties.isoNum = id;
});
_ifkMap.addSource('countries', { type: 'geojson', data: geo });
// Render choropleth to a canvas, then add as single image source (NOT tiled)
var W = 2048, H = 1024;
var offscreen = document.createElement('canvas');
offscreen.width = W; offscreen.height = H;
var ctx = offscreen.getContext('2d');
_ifkMap.addLayer({
id: 'choropleth-fill', type: 'fill', source: 'countries',
paint: {
'fill-color': ['get', 'fillColor'],
'fill-opacity': ['get', 'fillOpacity'],
'fill-antialias': false
}
// Draw country fills using Equirectangular projection
geo.features.forEach(function(f) {
var id = String(f.id).padStart(3, '0');
var c = byIsoNum[id];
if (!c) return;
ctx.fillStyle = STATUS_COLORS[c.status];
ctx.beginPath();
var coords = f.geometry.type === 'Polygon' ? [f.geometry.coordinates] : f.geometry.coordinates;
coords.forEach(function(poly) {
poly.forEach(function(ring) {
for (var i = 0; i < ring.length; i++) {
var x = (ring[i][0] + 180) / 360 * W;
var y = (90 - ring[i][1]) / 180 * H;
if (i === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
}
ctx.closePath();
});
});
ctx.fill();
});
// Add as image source — single image, NO tiling
_ifkMap.addSource('choropleth-img', {
type: 'image',
url: offscreen.toDataURL('image/png'),
coordinates: [[-180, 85.05], [180, 85.05], [180, -85.05], [-180, -85.05]]
});
_ifkMap.addLayer({
id: 'choropleth-border', type: 'line', source: 'countries',
paint: { 'line-color': '#94a3b8', 'line-width': 0.5 }
id: 'choropleth-fill',
type: 'raster',
source: 'choropleth-img',
paint: { 'raster-opacity': 0.85, 'raster-fade-duration': 0 }
});
// Keep GeoJSON source for hover/click interaction (invisible)
_ifkMap.addSource('countries', { type: 'geojson', data: geo });
_ifkMap.addLayer({
id: 'choropleth-hover', type: 'fill', source: 'countries',
paint: { 'fill-color': '#667eea', 'fill-opacity': 0.2 },
paint: { 'fill-color': '#667eea', 'fill-opacity': 0.2, 'fill-antialias': false },
filter: ['==', 'isoNum', '']
});