/* Copyright (c) 2015-2018 Jean-Marc VIGLINO, released under the CeCILL-B license (French BSD license) (http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt). */ import {unByKey as ol_Observable_unByKey} from 'ol/Observable.js' import ol_control_Control from 'ol/control/Control.js' import {transform as ol_proj_transform} from 'ol/proj.js' import ol_ext_element from '../util/element.js' import { toLonLat as ol_geohash_toLonLat } from '../geom/geohash.js' import { fromLonLat as ol_geohash_fromLonLat } from '../geom/geohash.js' /** * Set an hyperlink that will return the user to the current map view. * Just add a `permalink`property to layers to be handled by the control (and added in the url). * The layer's permalink property is used to name the layer in the url. * The control must be added after all layer are inserted in the map to take them into acount. * * @constructor * @extends {ol_control_Control} * @param {Object=} options * @param {boolean} [options.urlReplace=true] replace url or not, default true * @param {boolean|string} [options.localStorage=false] save current map view in localStorage, if 'position' only store map position * @param {boolean} [options.geohash=false] use geohash instead of lonlat, default false * @param {integer} [options.fixed=6] number of digit in coords, default 6 * @param {boolean} [options.anchor] use "#" instead of "?" in href * @param {boolean} [options.visible=true] hide the button on the map, default true * @param {boolean} [options.hidden] hide the button on the map, default false DEPRECATED: use visible instead * @param {function} [options.onclick] a function called when control is clicked * @param {number} [options.refreshDelay=500] */ var ol_control_Permalink = class olcontrolPermalink extends ol_control_Control { constructor(opt_options) { var options = opt_options || {} var element = document.createElement('div') super({ element: element, target: options.target }) var self = this var button = document.createElement('button') ol_ext_element.create('I', { parent: button }) this.replaceState_ = (options.urlReplace !== false) this.fixed_ = options.fixed || 6 this.hash_ = options.anchor ? "#" : "?" this._localStorage = options.localStorage if (!this._localStorage) { try { localStorage.removeItem('ol@permalink') } catch (e) { console.warn('Failed to access localStorage...')} } function linkto() { if (typeof (options.onclick) == 'function'){ options.onclick(self.getLink()) } else { self.setUrlReplace(!self.replaceState_) } } button.addEventListener('click', linkto, false) button.addEventListener('touchstart', linkto, false) element.className = (options.className || "ol-permalink") + " ol-unselectable ol-control" element.appendChild(button) if (options.hidden || options.visible === false) ol_ext_element.hide(element) this.set('geohash', options.geohash) this.set('initial', false) this.on('change', this.viewChange_.bind(this)) // Save search params this.search_ = {} var init = {} var hash = document.location.hash || document.location.search || '' // console.log('hash', hash) if (this.replaceState_ && !hash && this._localStorage) { try { hash = localStorage['ol@permalink'] } catch (e) { console.warn('Failed to access localStorage...')} } if (hash) { hash = hash.replace(/(^#|^\?)/, "").split("&") for (var i = 0; i < hash.length; i++) { var t = hash[i].split("=") switch (t[0]) { case 'lon': case 'lat': case 'z': case 'r': { init[t[0]] = t[1] break } case 'gh': { const ghash = t[1].split('-') const lonlat = ol_geohash_toLonLat(ghash[0]) init.lon = lonlat[0] init.lat = lonlat[1] init.z = ghash[1] break } case 'l': break default: this.search_[t[0]] = t[1] } } } if (init.hasOwnProperty('lon')) { this.set('initial', init) } this.set('refreshDelay', options.refreshDelay || 500) // Decode permalink if (this.replaceState_) this.setPosition() } /** * Get the initial position passed by the url */ getInitialPosition() { return this.get('initial') } /** * Set the map instance the control associated with. * @param {ol.Map} map The map instance. */ setMap(map) { if (this._listener) { ol_Observable_unByKey(this._listener.change) ol_Observable_unByKey(this._listener.moveend) } this._listener = null super.setMap.call(this, map) // Get change if (map) { this._listener = { change: map.getLayerGroup().on('change', this.layerChange_.bind(this)), moveend: map.on('moveend', this.viewChange_.bind(this)) } this.setPosition() } } /** Get layer given a permalink name (permalink propertie in the layer) * @param {string} the permalink to search for * @param {Array|undefined} an array of layer to search in * @return {ol.layer|false} */ getLayerByLink(id, layers) { if (!layers && this.getMap()) layers = this.getMap().getLayers().getArray() for (var i = 0; i < layers.length; i++) { if (layers[i].get('permalink') == id) return layers[i] // Layer Group if (layers[i].getLayers) { var li = this.getLayerByLink(id, layers[i].getLayers().getArray()) if (li) return li } } return false } /** Set coordinates as geohash * @param {boolean} */ setGeohash(b) { this.set('geohash', b) this.setUrlParam() } /** Set map position according to the current link * @param {boolean} [force=false] if true set the position even if urlReplace is disabled */ setPosition(force) { var map = this.getMap() if (!map) return var hash = (this.replaceState_ || force) ? document.location.hash || document.location.search : '' if (!hash && this._localStorage) { try { hash = localStorage['ol@permalink'] } catch (e) { console.warn('Failed to access localStorage...')} } if (!hash) return var i, t, param = {} hash = hash.replace(/(^#|^\?)/, "").split("&") for (i = 0; i < hash.length; i++) { t = hash[i].split("=") param[t[0]] = t[1] } if (param.gh) { var ghash = param.gh.split('-') var lonlat = ol_geohash_toLonLat(ghash[0]) param.lon = lonlat[0] param.lat = lonlat[1] param.z = ghash[1] } var c = ol_proj_transform([Number(param.lon), Number(param.lat)], 'EPSG:4326', map.getView().getProjection()) if (c[0] && c[1]) map.getView().setCenter(c) if (param.z) map.getView().setZoom(Number(param.z)) if (param.r) map.getView().setRotation(Number(param.r)) // Reset layers visibility function resetLayers(layers) { if (!layers) layers = map.getLayers().getArray() for (var i = 0; i < layers.length; i++) { if (layers[i].get('permalink')) { layers[i].setVisible(false) // console.log("hide "+layers[i].get('permalink')); } if (layers[i].getLayers) { resetLayers(layers[i].getLayers().getArray()) } } } if (param.l) { resetLayers() var l = param.l.split("|") for (i = 0; i < l.length; i++) { t = l[i].split(":") var li = this.getLayerByLink(t[0]) var op = Number(t[1]) if (li) { li.setOpacity(op) li.setVisible(true) } } } } /** * Get the parameters added to the url. The object can be changed to add new values. * @return {Object} a key value object added to the url as &key=value * @api stable */ getUrlParams() { return this.search_ } /** * Set a parameter to the url. * @param {string} key the key parameter * @param {string|undefined} value the parameter's value, if undefined or empty string remove the parameter * @api stable */ setUrlParam(key, value) { if (key) { if (value === undefined || value === '') delete (this.search_[encodeURIComponent(key)]) else this.search_[encodeURIComponent(key)] = encodeURIComponent(value) } this.viewChange_() } /** * Get a parameter url. * @param {string} key the key parameter * @return {string} the parameter's value or empty string if not set * @api stable */ getUrlParam(key) { return decodeURIComponent(this.search_[encodeURIComponent(key)] || '') } /** * Has a parameter url. * @param {string} key the key parameter * @return {boolean} * @api stable */ hasUrlParam(key) { return this.search_.hasOwnProperty(encodeURIComponent(key)) } /** Get the permalink * @param {boolean|string} [search=false] false: return full link | true: return the search string only | 'position': return position string * @return {permalink} */ getLink(search) { var map = this.getMap() var c = ol_proj_transform(map.getView().getCenter(), map.getView().getProjection(), 'EPSG:4326') var z = Math.round(map.getView().getZoom() * 10) / 10 var r = map.getView().getRotation() var l = this.layerStr_ // Change anchor var anchor = (r ? "&r=" + (Math.round(r * 10000) / 10000) : "") + (l ? "&l=" + l : "") if (this.get('geohash')) { var ghash = ol_geohash_fromLonLat(c, 8) anchor = "gh=" + ghash + '-' + z + anchor } else { anchor = "lon=" + c[0].toFixed(this.fixed_) + "&lat=" + c[1].toFixed(this.fixed_) + "&z=" + z + anchor } if (search === 'position') return anchor // Add other params for (var i in this.search_) { anchor += "&" + i + (typeof (this.search_[i]) !== 'undefined' ? "=" + this.search_[i] : '') } if (search) return anchor //return document.location.origin+document.location.pathname+this.hash_+anchor; return document.location.protocol + "//" + document.location.host + document.location.pathname + this.hash_ + anchor } /** Check if urlreplace is on * @return {boolean} */ getUrlReplace() { return this.replaceState_ } /** Enable / disable url replacement (replaceSate) * @param {bool} */ setUrlReplace(replace) { try { this.replaceState_ = replace if (!replace) { var s = "" for (var i in this.search_) { s += (s == "" ? "?" : "&") + i + (typeof (this.search_[i]) !== 'undefined' ? "=" + this.search_[i] : '') } this.replaceUrl_(document.location.origin + document.location.pathname + s, true) } else { this.replaceUrl_(this.getLink(), true) } } catch (e) { /* ok */ } /* if (this._localStorage) { localStorage['ol@permalink'] = this.getLink(true); } */ } /** Refresh the url * @private */ replaceUrl_(url, force) { clearTimeout(this.refreshTout_) if (force) { window.history.replaceState(null, null, url) } else { this.refreshTout_ = setTimeout(function() { window.history.replaceState(null, null, url) }, this.get('refreshDelay')) } } /** * On view change refresh link * @param {ol.event} The map instance. * @private */ viewChange_() { try { if (this.replaceState_) { this.replaceUrl_(this.getLink()) } } catch (e) { /* ok */ } if (this._localStorage) { try { localStorage['ol@permalink'] = this.getLink(this._localStorage) } catch (e) { console.warn('Failed to access localStorage...')} } } /** * Layer change refresh link * @private */ layerChange_() { // Prevent multiple change at the same time if (this._tout) { clearTimeout(this._tout) this._tout = null } this._tout = setTimeout(function () { this._tout = null // Get layers var l = "" function getLayers(layers) { for (var i = 0; i < layers.length; i++) { if (layers[i].getVisible() && layers[i].get("permalink")) { if (l) l += "|" l += layers[i].get("permalink") + ":" + layers[i].get("opacity") } // Layer Group if (layers[i].getLayers) getLayers(layers[i].getLayers().getArray()) } } getLayers(this.getMap().getLayers().getArray()) this.layerStr_ = l this.viewChange_() }.bind(this), 200) } } export default ol_control_Permalink