Include layer visibility in the URL

This commit is contained in:
Tim Schaub
2022-05-21 10:38:31 -06:00
parent 6690dc99dd
commit cf7daf36d6
3 changed files with 71 additions and 17 deletions

View File

@@ -4,6 +4,9 @@ title: Map Link
shortdesc: Synchronizing map state with the URL.
docs: >
The `Link` interaction allows you to synchronize the map state with the URL.
The view center, zoom level, and rotation will be reflected in the URL as you
navigate around the map. Layer visibility is also reflected in the URL.
Reloading the page restores the map view state.
tags: "link, permalink, url, query, search, params"
---
<div id="map" class="map"></div>

View File

@@ -1,6 +1,7 @@
/**
* @module ol/interaction/Link
*/
import EventType from '../events/EventType.js';
import Interaction from './Interaction.js';
import MapEventType from '../MapEventType.js';
import {assign} from '../obj.js';
@@ -188,8 +189,11 @@ class Link extends Interaction {
*/
registerListeners_(map) {
this.listenerKeys_.push(
listen(map, MapEventType.MOVEEND, this.updateUrl_, this)
listen(map, MapEventType.MOVEEND, this.updateUrl_, this),
listen(map.getLayerGroup(), EventType.CHANGE, this.updateUrl_, this),
listen(map, 'change:layergroup', this.handleChangeLayerGroup_, this)
);
if (!this.replace_) {
addEventListener('popstate', this.updateState_);
}
@@ -208,15 +212,31 @@ class Link extends Interaction {
if (!this.replace_) {
removeEventListener('popstate', this.updateState_);
}
const url = new URL(window.location.href);
const params = url.searchParams;
this.delete_(params, 'x');
this.delete_(params, 'y');
this.delete_(params, 'z');
this.delete_(params, 'r');
this.delete_(params, 'l');
window.history.replaceState(null, '', url);
}
/**
* @private
*/
handleChangeLayerGroup_() {
const map = this.getMap();
if (!map) {
return;
}
this.unregisterListeners_(map);
this.registerListeners_(map);
this.initial_ = true;
this.updateUrl_();
}
/**
* @private
*/
@@ -232,23 +252,23 @@ class Link extends Interaction {
const url = new URL(window.location.href);
const params = url.searchParams;
let update = false;
let updateView = false;
/**
* @type {import('../View.js').AnimationOptions}
*/
const properties = {};
const viewProperties = {};
const zoom = readNumber(this.get_(params, 'z'));
if (differentNumber(zoom, view.getZoom())) {
update = true;
properties.zoom = zoom;
updateView = true;
viewProperties.zoom = zoom;
}
const rotation = readNumber(this.get_(params, 'r'));
if (differentNumber(rotation, view.getRotation())) {
update = true;
properties.rotation = rotation;
updateView = true;
viewProperties.rotation = rotation;
}
const center = [
@@ -256,22 +276,37 @@ class Link extends Interaction {
readNumber(this.get_(params, 'y')),
];
if (differentArray(center, view.getCenter())) {
update = true;
properties.center = center;
updateView = true;
viewProperties.center = center;
}
if (update) {
if (updateView) {
if (!this.initial_ && this.animationOptions_) {
view.animate(assign(properties, this.animationOptions_));
view.animate(assign(viewProperties, this.animationOptions_));
} else {
if (properties.center) {
view.setCenter(properties.center);
if (viewProperties.center) {
view.setCenter(viewProperties.center);
}
if ('zoom' in properties) {
view.setZoom(properties.zoom);
if ('zoom' in viewProperties) {
view.setZoom(viewProperties.zoom);
}
if ('rotation' in properties) {
view.setRotation(properties.rotation);
if ('rotation' in viewProperties) {
view.setRotation(viewProperties.rotation);
}
}
}
const layers = map.getAllLayers();
const layersParam = this.get_(params, 'l');
if (layersParam && layersParam.length === layers.length) {
for (let i = 0, ii = layers.length; i < ii; ++i) {
const value = parseInt(layersParam[i]);
if (!isNaN(value)) {
const visible = Boolean(value);
const layer = layers[i];
if (layer.getVisible() !== visible) {
layer.setVisible(visible);
}
}
}
}
@@ -296,12 +331,20 @@ class Link extends Interaction {
const zoom = view.getZoom();
const rotation = view.getRotation();
const layers = map.getAllLayers();
const visibilities = new Array(layers.length);
for (let i = 0, ii = layers.length; i < ii; ++i) {
visibilities[i] = layers[i].getVisible() ? '1' : '0';
}
const url = new URL(window.location.href);
const params = url.searchParams;
this.set_(params, 'x', writeNumber(center[0]));
this.set_(params, 'y', writeNumber(center[1]));
this.set_(params, 'z', writeNumber(zoom));
this.set_(params, 'r', writeNumber(rotation));
this.set_(params, 'l', visibilities.join(''));
if (url.href !== window.location.href) {
if (initial || this.replace_) {

View File

@@ -1,3 +1,4 @@
import Layer from '../../../../../src/ol/layer/Tile.js';
import Link from '../../../../../src/ol/interaction/Link.js';
import Map from '../../../../../src/ol/Map.js';
import View from '../../../../../src/ol/View.js';
@@ -13,6 +14,11 @@ describe('ol/interaction/Link', () => {
resolutions: [4, 2, 1],
zoom: 1,
}),
layers: [
new Layer({visible: true}),
new Layer({visible: false}),
new Layer({visible: true}),
],
});
map.renderSync();
});
@@ -32,6 +38,7 @@ describe('ol/interaction/Link', () => {
expect(params.get('x')).to.be('3');
expect(params.get('y')).to.be('4');
expect(params.get('r')).to.be('0.5');
expect(params.get('l')).to.be('101');
done();
});
@@ -51,6 +58,7 @@ describe('ol/interaction/Link', () => {
expect(params.get('ol:x')).to.be('3');
expect(params.get('ol:y')).to.be('4');
expect(params.get('ol:r')).to.be('0.5');
expect(params.get('ol:l')).to.be('101');
done();
});