nakarte

Source code of https://map.sikmir.ru (fork)
git clone git://git.sikmir.ru/nakarte
Log | Files | Refs | LICENSE

commit 38587038d3416557bd7dd3e030fe042cdd69c45e
parent 7afc05a2bd3da5b14f9a3b5dbce8f3d281a4c664
Author: Sergej Orlov <wladimirych@gmail.com>
Date:   Tue,  8 Nov 2016 00:15:25 +0300

added hash state for map position and layers

Diffstat:
Msrc/App.js | 39++++++++++++++++++---------------------
Asrc/lib/hashState/Leaflet.Control.Layers.js | 39+++++++++++++++++++++++++++++++++++++++
Asrc/lib/hashState/Leaflet.Map.js | 39+++++++++++++++++++++++++++++++++++++++
Asrc/lib/hashState/hashState.js | 101+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/hashState/leaflet.hashState.js | 32++++++++++++++++++++++++++++++++
5 files changed, 229 insertions(+), 21 deletions(-)

diff --git a/src/App.js b/src/App.js @@ -7,38 +7,35 @@ import './lib/control.caption/caption' import config from './config' import './lib/control.coordinates/coordinates'; import './lib/control.layers.hotkeys/control.Layers-hotkeys'; +import './lib/hashState/Leaflet.Map'; +import './lib/hashState/Leaflet.Control.Layers'; function setUp() { const map = L.map('map', { - zoomControl: false, - fadeAnimation: false, - center:[55.75185, 37.61856], - zoom: 10 - }); + zoomControl: false, + fadeAnimation: false, + zoom: 10 + } + ); + map.enableHashState('m', [10, 55.75185, 37.61856]); + new L.Control.Caption(`<a href=mailto:${config.email}">nakarte@nakarte.tk</a>`, { - position: 'topleft' - }).addTo(map); + position: 'topleft' + } + ).addTo(map); L.control.zoom().addTo(map); - { - let baseLayers = layers.getBaseMaps(); - const layersControl = L.control.layers(baseLayers, layers.getOverlays(), {collapsed: false}).addTo(map); - map.addLayer(baseLayers['OpenStreetMap']); - } + + let baseLayers = layers.getBaseMaps(); + const layersControl = L.control.layers(baseLayers, layers.getOverlays(), {collapsed: false}).addTo(map); + map.addLayer(baseLayers['OpenStreetMap']); + layersControl.enableHashState('l'); + new L.Control.PrintPages().addTo(map); new L.Control.Coordinates().addTo(map); - // const hashState = L.HashState(); - // hashState.bind(map); - // hashState.bind(layersControl); - - // let p = L.polyline([[0, 0], [20, 20]]); - // p.addTo(map); - // map.on('contextmenu', () => { - // map.flyTo([10, 20], 13, {duration: 1}); - // }); } export default {setUp}; diff --git a/src/lib/hashState/Leaflet.Control.Layers.js b/src/lib/hashState/Leaflet.Control.Layers.js @@ -0,0 +1,39 @@ +import L from 'leaflet'; +import './leaflet.hashState'; + +L.Control.Layers.include(L.Mixin.HashState); + +L.Control.Layers.include({ + stateChangeEvents: ['baselayerchange', 'overlayadd', 'overlayremove'], + stateChangeEventsSource: '_map', + + serializeState: function(e) { + const state = []; + + this._map.eachLayer((layer) => { + let key = layer.options.code; + if (key && key.length === 1) { + state.push(key); + } + } + ); + return state; + }, + + unserializeState: function(values) { + if (!values || values.length === 0) { + return false; + } + + for (let layer of this._layers) { + if (values.includes(layer.layer.options.code)) { + this._map.addLayer(layer.layer); + } else { + this._map.removeLayer(layer.layer); + } + } + return true; + } + } +); + diff --git a/src/lib/hashState/Leaflet.Map.js b/src/lib/hashState/Leaflet.Map.js @@ -0,0 +1,39 @@ +import L from 'leaflet'; +import './leaflet.hashState' + +L.Map.include(L.Mixin.HashState); + +L.Map.include({ + stateChangeEvents: ['moveend'], + + serializeState: function() { + var center = this.getCenter(); + var zoom = this.getZoom(); + var precision = 5; + var state = [ + zoom, + center.lat.toFixed(precision), + center.lng.toFixed(precision), + ]; + return state; + }, + + unserializeState: function(values) { + if (!values || values.length !== 3) { + return false; + } + var zoom = parseInt(values[0], 10), + lat = parseFloat(values[1]), + lng = parseFloat(values[2]); + if (isNaN(zoom) || isNaN(lat) || isNaN(lng) || zoom < 0 || zoom > 32 || lat < -90 || lat > 90 || lng < -180 || + lng > 180) { + return false; + } + // this._updating_state = true; + this.setView([lat, lng], zoom); + // this._updating_state = false; + return true; + } + } +); + diff --git a/src/lib/hashState/hashState.js b/src/lib/hashState/hashState.js @@ -0,0 +1,101 @@ +function arrayItemsEqual(l1, l2) { + l1 = l1 || []; + l2 = l2 || []; + if (l1.length !== l2.length) + return false; + for (var i = 0; i < l1.length; i++) { + if (l1[i] !== l2[i]) { + return false; + } + } + return true; +} + +const hashState = { + _listeners: [], + _state: {}, + + addEventListener: function(key, callback) { + for (let [k, c] of this._listeners) { + if (k === key && c === callback) { + return; + } + } + this._listeners.push([key, callback]); + }, + + removeEventListener: function(key, callback) { + this._listeners.forEach(([k, c], i) => { + if (k === key && c === callback) { + this._listeners.splice(i, 1); + return; + } + } + ); + }, + + updateState: function(key, values) { + this._state[key] = values; + this._saveStateToHash(); + }, + + getState: function(key) { + return this._state[key]; + }, + + _parseHash: function() { + let hash = location.hash; + const args = {}, + i = hash.indexOf('#'); + if (i >= 0) { + hash = hash.substr(i + 1).trim(); + let m, key, value; + for (let pair of hash.split('&')) { + m = /(.+?)=(.+)/.exec(pair); + if (m) { + [, key, value] = m; + value = value.split('/'); + value = value.map(decodeURIComponent); + args[key] = value; + } + } + } + return args; + }, + + _saveStateToHash: function() { + var stateItems = []; + for (var key in this._state) { + if (this._state[key].length) { + var values = this._state[key].join('/'); + stateItems.push(key + '=' + values); + } + } + var hashString = '#' + stateItems.join('&'); + location.replace(hashString); + }, + + + onHashChanged: function() { + const newState = this._parseHash(); + const changedKeys = {}; + for (let key in newState) { + if (!arrayItemsEqual(newState[key], this._state[key])) { + changedKeys[key] = 1; + } + } + for (let [key, callback] of this._listeners) { + if (key in changedKeys) { + // setTimeout(callback.bind(null, state[key]), 0); + callback(newState[key]); + } + } + this._state = newState; + } +}; + +window.addEventListener('hashchange', hashState.onHashChanged.bind(hashState)); +hashState.onHashChanged(); + +export default hashState; + diff --git a/src/lib/hashState/leaflet.hashState.js b/src/lib/hashState/leaflet.hashState.js @@ -0,0 +1,31 @@ +import L from 'leaflet'; +import hashState from './hashState'; + +L.Mixin.HashState = { + enableHashState: function(key, defaultInitialState = null) { + this._hashStateKey = key; + const eventSource = this.stateChangeEventsSource ? this[this.stateChangeEventsSource] : this; + this.stateChangeEvents.forEach((event) => { + eventSource.on(event, this.updateHashState, this); + } + ); + + hashState.addEventListener(key, (state) => { + this.unserializeState(state) + } + ); + + const state = hashState.getState(key) || defaultInitialState; + if (state) { + if (!this.unserializeState(state)) { // state from hash is invalid, update hash from component state + hashState.updateState(key, this.serializeState()); + } + } + }, + + updateHashState: function(state) { + hashState.updateState(this._hashStateKey, this.serializeState()); + } + + // TODO: disableHashState +}; +\ No newline at end of file