feat(karte): nur Deutschland anzeigen (Invers-Maske) + lokale pmtiles-Basemap verdrahten
- map.js: Invers-Maske aus Bundeslaender-GeoJSON blendet Nachbarlaender/Meer aus (Even-Odd, alle Ringe -> Enklaven Berlin/Bremen bleiben sichtbar) - map.js: Ausschnitt via setMaxBounds fest auf Deutschland begrenzt - map.js: Grenz-GeoJSON immer laden; gefuellter Umriss als Fallback ohne Tiles - scripts.html/karte.html/hugo.toml: protomaps-leaflet + pmtiles-Basemap - .gitignore: /static/tiles/ (6,7-GB-pmtiles liegt ausserhalb des Repos)
This commit is contained in:
@@ -21,41 +21,91 @@
|
||||
var defaultColor = payload.defaultColor || "#1e2758";
|
||||
var klein = !!payload.klein;
|
||||
var geojsonUrl = payload.geojsonUrl || "";
|
||||
var attribution = payload.attribution || "Geodaten: © GeoBasis-DE / BKG";
|
||||
var germanyBounds = null;
|
||||
var pmtilesUrl = payload.pmtilesUrl || "";
|
||||
var flavor = payload.flavor || "light";
|
||||
var attribution = payload.attribution || "© OpenStreetMap-Mitwirkende";
|
||||
// Statischer Deutschland-Ausschnitt (für Locator/leere Filterung ohne Marker)
|
||||
var germanyBounds = L.latLngBounds([[47.2, 5.8], [55.1, 15.1]]);
|
||||
|
||||
var map = L.map(el, {
|
||||
scrollWheelZoom: false,
|
||||
minZoom: 5, maxZoom: 12,
|
||||
minZoom: 5, maxZoom: 18,
|
||||
attributionControl: true
|
||||
});
|
||||
map.attributionControl.setPrefix(false).addAttribution(attribution);
|
||||
map.on("focus", function () { map.scrollWheelZoom.enable(); });
|
||||
map.on("blur", function () { map.scrollWheelZoom.disable(); });
|
||||
|
||||
// Lokale Deutschland-Karte (Bundesländer) als Vektor-Hintergrund laden.
|
||||
// Same-Origin-Fetch aus dem Repo – kein externer Request.
|
||||
// Ausschnitt fest auf Deutschland begrenzen: kein Wegscrollen ins Ausland.
|
||||
map.setMaxBounds(germanyBounds.pad(0.08));
|
||||
|
||||
// Basemap: selbst gehostete Vektor-Tiles (Protomaps/OSM, .pmtiles auf dem
|
||||
// eigenen Server – kein externes CDN). Der Browser rendert Strassen/Haeuser.
|
||||
var tilesActive = false;
|
||||
if (pmtilesUrl && typeof protomapsL !== "undefined" && protomapsL.leafletLayer) {
|
||||
try {
|
||||
protomapsL.leafletLayer({ url: pmtilesUrl, flavor: flavor, lang: "de" }).addTo(map);
|
||||
tilesActive = true;
|
||||
} catch (e) { tilesActive = false; }
|
||||
}
|
||||
|
||||
// Grenz-GeoJSON (Bundeslaender) immer laden:
|
||||
// - ohne Tiles: gefuellter Umriss als Ersatz-Hintergrund
|
||||
// - IMMER: Invers-Maske, die alles ausserhalb Deutschlands abdeckt, damit
|
||||
// nur Deutschland sichtbar ist (Nachbarlaender/Meer ausgeblendet)
|
||||
if (geojsonUrl) {
|
||||
fetch(geojsonUrl)
|
||||
.then(function (r) { return r.ok ? r.json() : null; })
|
||||
.then(function (geo) {
|
||||
if (!geo) return;
|
||||
var land = L.geoJSON(geo, {
|
||||
style: {
|
||||
color: "#1e2758", weight: 1, opacity: .85,
|
||||
fillColor: "#f4f1e6", fillOpacity: 1
|
||||
},
|
||||
interactive: false
|
||||
}).addTo(map);
|
||||
land.bringToBack();
|
||||
germanyBounds = land.getBounds();
|
||||
// Detail-Minikarte = Verortung: ganz Deutschland mit dem einen Pin zeigen.
|
||||
// Grosse Karte: nur wenn (noch) keine Marker den Ausschnitt bestimmen.
|
||||
if (klein || !records.length) {
|
||||
try { map.fitBounds(germanyBounds.pad(0.02)); } catch (e) {}
|
||||
if (!geo) { fitGermanyIfNeeded(); return; }
|
||||
if (!tilesActive) {
|
||||
L.geoJSON(geo, {
|
||||
style: { color: "#1e2758", weight: 1, opacity: .85, fillColor: "#f4f1e6", fillOpacity: 1 },
|
||||
interactive: false
|
||||
}).addTo(map).bringToBack();
|
||||
}
|
||||
addGermanyMask(geo);
|
||||
fitGermanyIfNeeded();
|
||||
})
|
||||
.catch(function () {});
|
||||
.catch(function () { fitGermanyIfNeeded(); });
|
||||
}
|
||||
|
||||
function fitGermanyIfNeeded() {
|
||||
if (klein || !records.length) {
|
||||
try { map.fitBounds(germanyBounds.pad(0.02)); } catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
// Invers-Maske: weltgrosses Rechteck mit Deutschland als "Loechern".
|
||||
// Fuellung (Papierfarbe) deckt alles ausserhalb der Landesgrenze ab.
|
||||
// Even-Odd-Fuellregel: es MUESSEN alle Ringe rein (auch innere), sonst
|
||||
// werden Enklaven wie Berlin/Bremen faelschlich ueberdeckt.
|
||||
// Liegt im overlayPane – ueber den Tiles, aber unter den Markern.
|
||||
function addGermanyMask(geo) {
|
||||
var world = [[-89, -180], [-89, 180], [89, 180], [89, -180]];
|
||||
var holes = [];
|
||||
function toLatLng(ring) {
|
||||
return ring.map(function (c) { return [c[1], c[0]]; });
|
||||
}
|
||||
function collect(geom) {
|
||||
if (!geom) return;
|
||||
if (geom.type === "Polygon") {
|
||||
geom.coordinates.forEach(function (ring) { holes.push(toLatLng(ring)); });
|
||||
} else if (geom.type === "MultiPolygon") {
|
||||
geom.coordinates.forEach(function (poly) {
|
||||
poly.forEach(function (ring) { holes.push(toLatLng(ring)); });
|
||||
});
|
||||
}
|
||||
}
|
||||
(geo.features || []).forEach(function (f) { collect(f.geometry); });
|
||||
if (geo.type === "Polygon" || geo.type === "MultiPolygon") collect(geo);
|
||||
if (!holes.length) return;
|
||||
L.polygon([world].concat(holes), {
|
||||
stroke: false,
|
||||
fill: true, fillColor: "#f6f6f1", fillOpacity: 1,
|
||||
fillRule: "evenodd",
|
||||
interactive: false
|
||||
}).addTo(map);
|
||||
}
|
||||
|
||||
var useCluster = places.length > 1 && typeof L.markerClusterGroup === "function";
|
||||
@@ -125,11 +175,11 @@
|
||||
var countCards = cards.filter(function (c) { return !c.classList.contains("is--hidden"); }).length;
|
||||
if (countEl) countEl.textContent = countCards;
|
||||
if (bounds.isValid()) {
|
||||
// Auf der Detail-Minikarte nicht ins Leere zoomen: Umkreis statt Punkt
|
||||
if (klein) map.fitBounds(bounds.pad(0.15), { maxZoom: 8 });
|
||||
// Detail-Minikarte: nah an Strasse/Haus heran (Zoom 17)
|
||||
if (klein) map.fitBounds(bounds.pad(0.15), { maxZoom: 17 });
|
||||
else map.fitBounds(bounds.pad(0.15));
|
||||
}
|
||||
else if (allBounds) map.fitBounds(allBounds.pad(0.15), klein ? { maxZoom: 8 } : undefined);
|
||||
else if (allBounds) map.fitBounds(allBounds.pad(0.15), klein ? { maxZoom: 17 } : undefined);
|
||||
else if (germanyBounds) map.fitBounds(germanyBounds.pad(0.02));
|
||||
}
|
||||
|
||||
|
||||
@@ -36,8 +36,10 @@
|
||||
"colors" $colorMap
|
||||
"defaultColor" $default
|
||||
"klein" ($klein | default false)
|
||||
"pmtilesUrl" (site.Params.map.pmtilesURL | default ("tiles/germany.pmtiles" | relURL))
|
||||
"flavor" (site.Params.map.flavor | default "light")
|
||||
"geojsonUrl" ("geo/deutschland-bundeslaender.geojson" | relURL)
|
||||
"attribution" (site.Params.map.attribution | default "Geodaten: © GeoBasis-DE / BKG (Bundesländergrenzen)")
|
||||
"attribution" (site.Params.map.attribution | default "© OpenStreetMap-Mitwirkende")
|
||||
}}
|
||||
|
||||
<div id="op-map" class="op-map{{ if $klein }} op-map--klein{{ end }}"
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
{{ if or .IsHome (eq .Type "schauplaetze") (eq .Type "kategorien") (eq .Type "erinnerungsorte") }}
|
||||
<script src="{{ "vendor/leaflet/leaflet.js" | relURL }}"></script>
|
||||
<script src="{{ "vendor/markercluster/leaflet.markercluster.js" | relURL }}"></script>
|
||||
{{/* Selbst gehostete Vektor-Basemap (Protomaps/OSM, .pmtiles) – kein externes CDN */}}
|
||||
<script src="{{ "vendor/protomaps/protomaps-leaflet.js" | relURL }}"></script>
|
||||
{{ $map := resources.Get "js/map.js" }}
|
||||
{{ if hugo.IsProduction }}{{ $map = $map | minify | fingerprint }}{{ end }}
|
||||
<script src="{{ $map.RelPermalink }}"{{ with $map.Data.Integrity }} integrity="{{ . }}"{{ end }}></script>
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user