diff --git a/examples/link.html b/examples/link.html index ee9680cc65..28b28bff89 100644 --- a/examples/link.html +++ b/examples/link.html @@ -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" ---
diff --git a/src/ol/interaction/Link.js b/src/ol/interaction/Link.js index 8f179ce100..af0dfeef75 100644 --- a/src/ol/interaction/Link.js +++ b/src/ol/interaction/Link.js @@ -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_) { diff --git a/test/browser/spec/ol/interaction/Link.test.js b/test/browser/spec/ol/interaction/Link.test.js index 6af4baaa8a..ba8d3b6337 100644 --- a/test/browser/spec/ol/interaction/Link.test.js +++ b/test/browser/spec/ol/interaction/Link.test.js @@ -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(); });