Files
internetforkids/layouts/shortcodes/world-map.html
Christian Gick 09a11520e8
All checks were successful
Deploy Internet for Kids / Build & Push (push) Successful in 13s
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 8s
Deploy Internet for Kids / Promote to Latest (push) Successful in 1s
Deploy Internet for Kids / Rollback (push) Has been skipped
Deploy Internet for Kids / Audit (push) Successful in 2s
fix: use OSM raster tiles for world map (clicksports vector tiles 503)
maps.clicksports.de vector tile style.json returns 503 on render.
Fall back to OSM raster tiles until tile server is fixed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 12:39:45 +03:00

143 lines
6.6 KiB
HTML

{{ $lang := .Page.Language.Lang }}
{{ $countries := hugo.Data.countries }}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/maplibre-gl@4.7.1/dist/maplibre-gl.min.css" />
<style>
.ifk-map-wrap { max-width: 100%; margin: 2rem 0; }
.ifk-map-wrap h3 { text-align: center; margin-bottom: 0.5rem; font-size: 1.25rem; }
.ifk-map-subtitle { text-align: center; color: #666; font-size: 0.85rem; margin-bottom: 1rem; }
#ifk-world-map { width: 100%; height: 420px; border-radius: 8px; }
.ifk-map-legend {
display: flex; flex-wrap: wrap; gap: 1rem; justify-content: center;
margin-top: 1rem; font-size: 0.8rem;
}
.ifk-map-legend-item { display: flex; align-items: center; gap: 0.35rem; }
.ifk-map-legend-swatch { width: 14px; height: 14px; border-radius: 3px; border: 1px solid rgba(0,0,0,0.1); }
.maplibregl-popup-content { font-size: 0.85rem; max-width: 260px; line-height: 1.4; }
.maplibregl-popup-content strong { font-size: 0.95rem; }
@media (max-width: 640px) { #ifk-world-map { height: 280px; } }
</style>
<div class="ifk-map-wrap">
<h3>{{ if eq $lang "de" }}Interaktive Weltkarte: Kinderschutzgesetze{{ else if eq $lang "fr" }}Carte interactive : lois de protection de l'enfance{{ else }}Interactive Map: Child Protection Laws{{ end }}</h3>
<p class="ifk-map-subtitle">{{ if eq $lang "de" }}Klicken Sie auf ein Land für Details{{ else if eq $lang "fr" }}Cliquez sur un pays pour plus de détails{{ else }}Click a country for details{{ end }}</p>
<div id="ifk-world-map"></div>
<div class="ifk-map-legend">
<div class="ifk-map-legend-item"><div class="ifk-map-legend-swatch" style="background:#1e40af"></div>{{ if eq $lang "de" }}In Kraft{{ else if eq $lang "fr" }}En vigueur{{ else }}Enforced{{ end }}</div>
<div class="ifk-map-legend-item"><div class="ifk-map-legend-swatch" style="background:#3b82f6"></div>{{ if eq $lang "de" }}Verabschiedet{{ else if eq $lang "fr" }}Adopté{{ else }}Passed{{ end }}</div>
<div class="ifk-map-legend-item"><div class="ifk-map-legend-swatch" style="background:#93c5fd"></div>{{ if eq $lang "de" }}In Bearbeitung{{ else if eq $lang "fr" }}En cours{{ else }}In Progress{{ end }}</div>
<div class="ifk-map-legend-item"><div class="ifk-map-legend-swatch" style="background:#dbeafe"></div>{{ if eq $lang "de" }}Richtlinien{{ else if eq $lang "fr" }}Directives{{ else }}Guidelines{{ end }}</div>
<div class="ifk-map-legend-item"><div class="ifk-map-legend-swatch" style="background:#e2e8f0"></div>{{ if eq $lang "de" }}Keine Daten{{ else if eq $lang "fr" }}Pas de données{{ else }}No Data{{ end }}</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/maplibre-gl@4.7.1/dist/maplibre-gl.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/topojson-client@3.1.0/dist/topojson-client.min.js"></script>
<script>
(function() {
var LANG = {{ $lang }};
var STATUS_COLORS = { enforced: '#1e40af', passed: '#3b82f6', progress: '#93c5fd', guidelines: '#dbeafe' };
var STATUS_LABELS = {
en: { enforced: 'Enforced', passed: 'Passed', progress: 'In Progress', guidelines: 'Guidelines' },
de: { enforced: 'In Kraft', passed: 'Verabschiedet', progress: 'In Bearbeitung', guidelines: 'Richtlinien' },
fr: { enforced: 'En vigueur', passed: 'Adopté', progress: 'En cours', guidelines: 'Directives' }
};
var countries = {{ $countries | jsonify }};
var byIsoNum = {};
countries.forEach(function(c) { byIsoNum[c.isoNum] = c; });
var map = new maplibregl.Map({
container: 'ifk-world-map',
style: { version: 8, sources: { osm: { type: 'raster', tiles: ['https://tile.openstreetmap.org/{z}/{x}/{y}.png'], tileSize: 256, attribution: '&copy; <a href="https://openstreetmap.org/copyright">OpenStreetMap</a>' } }, layers: [{ id: 'osm', type: 'raster', source: 'osm' }] },
center: [10, 25],
zoom: 1.5,
minZoom: 1,
maxZoom: 6,
scrollZoom: false,
attributionControl: true
});
map.addControl(new maplibregl.NavigationControl({ showCompass: false }), 'top-right');
map.on('load', function() {
fetch('https://cdn.jsdelivr.net/npm/world-atlas@2.0.2/countries-110m.json')
.then(function(r) { return r.json(); })
.then(function(topo) {
var geo = topojson.feature(topo, topo.objects.countries);
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] : '#e2e8f0';
f.properties.fillOpacity = c ? 0.65 : 0.15;
f.properties.isoNum = id;
});
map.addSource('countries-choropleth', { type: 'geojson', data: geo });
map.addLayer({
id: 'choropleth-fill',
type: 'fill',
source: 'countries-choropleth',
paint: {
'fill-color': ['get', 'fillColor'],
'fill-opacity': ['get', 'fillOpacity']
}
});
map.addLayer({
id: 'choropleth-outline',
type: 'line',
source: 'countries-choropleth',
paint: {
'line-color': '#94a3b8',
'line-width': 0.6
}
});
map.addLayer({
id: 'choropleth-hover',
type: 'fill',
source: 'countries-choropleth',
paint: {
'fill-color': '#1e293b',
'fill-opacity': 0
},
filter: ['==', 'isoNum', '']
});
var popup = new maplibregl.Popup({ closeButton: true, closeOnClick: false, maxWidth: '280px' });
map.on('mousemove', 'choropleth-fill', function(e) {
var id = e.features[0].properties.isoNum;
var c = byIsoNum[id];
map.getCanvas().style.cursor = c ? 'pointer' : '';
map.setFilter('choropleth-hover', ['==', 'isoNum', c ? id : '']);
map.setPaintProperty('choropleth-hover', 'fill-opacity', c ? 0.12 : 0);
});
map.on('mouseleave', 'choropleth-fill', function() {
map.getCanvas().style.cursor = '';
map.setFilter('choropleth-hover', ['==', 'isoNum', '']);
map.setPaintProperty('choropleth-hover', 'fill-opacity', 0);
});
map.on('click', 'choropleth-fill', function(e) {
var id = e.features[0].properties.isoNum;
var c = byIsoNum[id];
if (!c) return;
var loc = c[LANG] || c.en;
var labels = STATUS_LABELS[LANG] || STATUS_LABELS.en;
popup.setLngLat(e.lngLat).setHTML(
'<strong>' + c.flag + ' ' + loc.name + '</strong><br>' +
'<em>' + loc.law + '</em><br>' +
'<span style="color:' + STATUS_COLORS[c.status] + ';font-weight:600">' + labels[c.status] + '</span> (' + c.year + ')<br>' +
loc.detail
).addTo(map);
});
});
});
})();
</script>