feat: improve map zoom controls

This commit is contained in:
2026-07-21 12:28:07 +01:00
parent 5851517a6b
commit f76b553fc4
2 changed files with 97 additions and 0 deletions
+44
View File
@@ -72,6 +72,7 @@ const map = new ol.Map({
controls: ol.control controls: ol.control
.defaults.defaults({ rotate: false }) .defaults.defaults({ rotate: false })
.extend([ .extend([
createVisibleLayersExtentControl(),
new ol.control.Rotate({ new ol.control.Rotate({
autoHide: true, autoHide: true,
label: createNorthPointerIcon(), label: createNorthPointerIcon(),
@@ -118,6 +119,21 @@ function createNorthPointerIcon() {
return icon; return icon;
} }
function createVisibleLayersExtentControl() {
const element = document.createElement("div");
element.className = "ol-visible-layers-extent ol-unselectable ol-control";
const button = document.createElement("button");
button.type = "button";
button.title = "Zoom to visible layers";
button.setAttribute("aria-label", "Zoom to visible layers");
button.textContent = "⤢";
button.addEventListener("click", () => fitToVisibleLayers());
element.append(button);
return new ol.control.Control({ element });
}
async function searchParcela(event) { async function searchParcela(event) {
event.preventDefault(); event.preventDefault();
@@ -296,6 +312,34 @@ function fitToLayerExtent(layer) {
}); });
} }
function fitToVisibleLayers() {
const visibleLayers = availableLayers.filter((layer) => selectedLayers.includes(layer.name));
const projectedExtents = visibleLayers
.filter((layer) => layer.extent && layer.extent.every(Number.isFinite))
.map((layer) => ol.proj.transformExtent(layer.extent, "EPSG:4326", "EPSG:3857"));
if (!projectedExtents.length) {
updateStatus("Select a layer with a published extent to zoom to it.", "error");
return;
}
const combinedExtent = projectedExtents.reduce(
(combined, extent) => [
Math.min(combined[0], extent[0]),
Math.min(combined[1], extent[1]),
Math.max(combined[2], extent[2]),
Math.max(combined[3], extent[3]),
],
[Infinity, Infinity, -Infinity, -Infinity],
);
map.getView().fit(combinedExtent, {
padding: [64, 64, 64, 64],
duration: 500,
maxZoom: 18,
});
}
function childElementsByName(node, localName) { function childElementsByName(node, localName) {
return [...(node?.children || [])].filter((child) => child.localName === localName); return [...(node?.children || [])].filter((child) => child.localName === localName);
} }
+53
View File
@@ -312,6 +312,54 @@ button:disabled {
top: 16px; top: 16px;
right: 16px; right: 16px;
left: auto; left: auto;
overflow: hidden;
background: var(--panel);
border: 1px solid var(--panel-border);
border-radius: 14px;
backdrop-filter: blur(16px);
box-shadow: var(--shadow);
}
.map-wrap .ol-zoom button,
.map-wrap .ol-visible-layers-extent button {
width: 2.0625em;
height: 2.0625em;
margin: 0;
background: linear-gradient(135deg, var(--accent) 0%, #2b9a79 100%);
color: #fffefb;
font-size: 1rem;
line-height: 1;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.24);
}
.map-wrap .ol-zoom button + button {
border-top: 1px solid rgba(255, 255, 255, 0.35);
}
.map-wrap .ol-zoom button:hover,
.map-wrap .ol-zoom button:focus,
.map-wrap .ol-visible-layers-extent button:hover,
.map-wrap .ol-visible-layers-extent button:focus {
background: linear-gradient(135deg, var(--accent-strong) 0%, var(--accent) 100%);
color: #fff;
outline: 2px solid rgba(31, 122, 98, 0.35);
outline-offset: -2px;
}
.map-wrap .ol-visible-layers-extent {
top: 96px;
right: 16px;
left: auto;
overflow: hidden;
background: var(--panel);
border: 1px solid var(--panel-border);
border-radius: 14px;
backdrop-filter: blur(16px);
box-shadow: var(--shadow);
}
.map-wrap .ol-visible-layers-extent button {
font-size: 1.3rem;
} }
/* OpenLayers hides this control while the view is north-up. */ /* OpenLayers hides this control while the view is north-up. */
@@ -364,6 +412,11 @@ button:disabled {
right: 12px; right: 12px;
} }
.map-wrap .ol-visible-layers-extent {
top: 92px;
right: 12px;
}
.map-wrap .ol-rotate { .map-wrap .ol-rotate {
right: 12px; right: 12px;
bottom: 12px; bottom: 12px;