commit d2135cb94b339935c5199198c383c2a5af1ed1a3
parent 4f9e61842fcf1fb4c4a340628da4d9a1460d872f
Author: Sergej Orlov <wladimirych@gmail.com>
Date: Thu, 8 Dec 2016 00:38:48 +0300
[layers] added dialog to select enabled layers
Diffstat:
4 files changed, 686 insertions(+), 60 deletions(-)
diff --git a/src/App.js b/src/App.js
@@ -2,7 +2,6 @@ import './App.css';
import './leaflet-fixes.css';
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
-import layers from './layers';
import 'lib/leaflet.control.printPages/control'
import 'lib/leaflet.control.caption/caption'
import config from './config'
@@ -19,8 +18,10 @@ import 'lib/leaflet.control.track-list/track-list.hash-state';
import 'lib/leaflet.control.track-list/track-list.localstorage';
import enableLayersControlAdaptiveHeight from 'lib/leaflet.control.layers.adaptive-height/adaptive-height';
import enableLayersMinimize from 'lib/leaflet.control.layers.minimize/minimize';
+import enableLayersConfig from 'lib/leaflet.control.layers.configure/layers-configure';
import hashState from 'lib/leaflet.hashState/hashState';
import raiseControlsOnFocus from 'lib/leaflet.controls.raise-on-focus/raise-on-focus';
+import getLayers from 'layers';
@@ -58,14 +59,13 @@ function setUp() {
/////////// controls top-right corner
- let baseLayers = layers.getBaseMaps();
- map.addLayer(baseLayers['OpenStreetMap']);
- const layersControl = L.control.layers(baseLayers, layers.getOverlays(), {collapsed: false})
- .addTo(map)
- .enableHashState('l');
+ const layersControl = L.control.layers(null, null, {collapsed: false})
+ .addTo(map);
enableLayersControlHotKeys (layersControl);
enableLayersControlAdaptiveHeight(layersControl);
enableLayersMinimize(layersControl);
+ enableLayersConfig(layersControl, getLayers());
+ layersControl.enableHashState('l');
/////////// controls bottom-left corner
diff --git a/src/layers.js b/src/layers.js
@@ -6,59 +6,388 @@ import config from './config';
import 'lib/leaflet.layer.soviet-topomaps-grid/soviet-topomaps-grid';
import 'lib/leaflet.layer.westraPasses/westraPasses';
-function getBaseMaps() {
- return {
- 'OpenStreetMap': L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
- {code: 'O', scaleDependent: true, print: true, jnx: true}
- ),
- 'ESRI Sat': L.tileLayer(
- 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
- {code: 'E', maxNativeZoom: 17, print: true, jnx: true}
- ),
- 'Yandex': new L.Layer.Yandex('map', {scaleDependent: true, code: 'Y', print: true, jnx: true}),
- 'Yandex Sat': new L.Layer.Yandex('sat', {scaleDependent: false, code: 'S', print: true, jnx: true}),
- 'Google': new L.Layer.Google('ROADMAP', {code: 'G', scaleDependent: true, print: true, jnx: true}),
- 'Google Sat': new L.Layer.Google('SATELLITE', {code: 'L', print: true, jnx: true}),
- 'Bing Sat': L.bingLayer(config.bingKey, {code: 'I', print: true, jnx: true}),
- 'marshruty.ru': L.tileLayer('http://maps.marshruty.ru/ml.ashx?x={x}&y={y}&z={z}&i=1&al=1',
- {code: 'M', maxNativeZoom: 18, noCors: true, scaleDependent: true, print: true, jnx: true}
- ),
- 'Topomapper 1km': L.tileLayer(
- 'http://144.76.234.107//cgi-bin/ta/tilecache.py/1.0.0/topomapper_v2/{z}/{x}/{y}.jpg',
- {code: 'T', maxNativeZoom: 13, noCors: true, print: true, jnx: true}
- )
- };
-}
+export default function getLayers() {
+ const layers = [
+ {
+ group: 'Default layers',
+ layers: [
+ {
+ title: 'OpenStreetMap',
+ description: 'OSM default style',
+ order: 10,
+ isOverlay: false,
+ isDefault: true,
+ layer: L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
+ {code: 'O', scaleDependent: true, print: true, jnx: true}
+ )
+ },
+ {
+ title: 'ESRI Sat',
+ order: 20,
+ isOverlay: false,
+ isDefault: true,
+ layer: L.tileLayer(
+ 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
+ {code: 'E', scaleDependent: false, maxNativeZoom: 17, print: true, jnx: true}
+ )
+ },
+ {
+ title: 'Yandex map',
+ order: 30,
+ isOverlay: false,
+ isDefault: true,
+ layer: new L.Layer.Yandex('map', {scaleDependent: true, code: 'Y', print: true, jnx: true})
+ },
+ {
+ title: 'Yandex Sat',
+ order: 40,
+ isOverlay: false,
+ isDefault: true,
+ layer: new L.Layer.Yandex('sat', {scaleDependent: false, code: 'S', print: true, jnx: true})
+ },
+ {
+ title: 'Google',
+ order: 50,
+ isOverlay: false,
+ isDefault: true,
+ layer: new L.Layer.Google('ROADMAP', {code: 'G', scaleDependent: true, print: true, jnx: true})
+ },
+ {
+ title: 'Google Sat',
+ order: 60,
+ isOverlay: false,
+ isDefault: true,
+ layer: new L.Layer.Google('SATELLITE', {code: 'L', scaleDependent: false, print: true, jnx: true})
+ },
+ {
+ title: 'Bing Sat',
+ order: 70,
+ isOverlay: false,
+ isDefault: true,
+ layer: L.bingLayer(config.bingKey, {code: 'I', scaleDependent: false, print: true, jnx: true})
+ },
+
+ {
+ title: 'marshruty.ru',
+ order: 80,
+ isOverlay: false,
+ isDefault: true,
+ layer: L.tileLayer('http://maps.marshruty.ru/ml.ashx?x={x}&y={y}&z={z}&i=1&al=1',
+ {code: 'M', maxNativeZoom: 18, noCors: true, scaleDependent: true, print: true, jnx: true}
+ )
+ },
+ {
+ title: 'Topomapper 1km',
+ order: 90,
+ isOverlay: false,
+ isDefault: true,
+ layer: L.tileLayer(
+ 'http://144.76.234.107//cgi-bin/ta/tilecache.py/1.0.0/topomapper_v2/{z}/{x}/{y}.jpg',
+ {code: 'T', scaleDependent: false, maxNativeZoom: 13, noCors: true, print: true, jnx: true}
+ )
+ },
+
+ {
+ title: 'Topo 10km',
+ order: 1010,
+ isOverlay: true,
+ isDefault: true,
+ layer: L.tileLayer("http://{s}.tiles.nakarte.tk/topo001m/{z}/{x}/{y}",
+ {code: 'D', tms: true, scaleDependent: false, maxNativeZoom: 9, print: true, jnx: true}
+ )
+ },
+ {
+ title: 'GGC 2 km',
+ order: 1020,
+ isOverlay: true,
+ isDefault: true,
+ layer: L.tileLayer("http://{s}.tiles.nakarte.tk/ggc2000/{z}/{x}/{y}",
+ {code: 'N', tms: true, scaleDependent: false, maxNativeZoom: 15, print: true, jnx: true}
+ )
+ },
+ {
+ title: 'ArbaletMO',
+ order: 1030,
+ isOverlay: true,
+ isDefault: true,
+ layer: L.tileLayer("http://{s}.tiles.nakarte.tk/ArbaletMO/{z}/{x}/{y}",
+ {code: 'A', tms: true, scaleDependent: false, maxNativeZoom: 13, print: true, jnx: true}
+ )
+ },
+ {
+ title: 'Slazav mountains',
+ order: 1040,
+ isOverlay: true,
+ isDefault: true,
+ layer: L.tileLayer("http://{s}.tiles.nakarte.tk/map_hr/{z}/{x}/{y}",
+ {code: 'Q', tms: true, scaleDependent: false, maxNativeZoom: 13, print: true, jnx: true}
+ )
+ },
+ {
+ title: 'GGC 1km',
+ order: 1050,
+ isOverlay: true,
+ isDefault: true,
+ layer: L.tileLayer("http://{s}.tiles.nakarte.tk/ggc1000/{z}/{x}/{y}",
+ {code: 'J', tms: true, scaleDependent: false, maxNativeZoom: 13, print: true, jnx: true}
+ )
+ },
+ {
+ title: 'Topo 1km',
+ order: 1060,
+ isOverlay: true,
+ isDefault: true,
+ layer: L.tileLayer("http://{s}.tiles.nakarte.tk/topo1000/{z}/{x}/{y}",
+ {code: 'C', tms: true, scaleDependent: false, maxNativeZoom: 13, print: true, jnx: true}
+ )
+ },
+ {
+ title: 'GGC 500m',
+ order: 1070,
+ isOverlay: true,
+ isDefault: true,
+ layer: L.tileLayer("http://{s}.tiles.nakarte.tk/ggc500/{z}/{x}/{y}",
+ {code: 'F', tms: true, scaleDependent: false, maxNativeZoom: 14, print: true, jnx: true}
+ )
+ },
+ {
+ title: 'Topo 500m',
+ order: 1080,
+ isOverlay: true,
+ isDefault: true,
+ layer: L.tileLayer("http://{s}.tiles.nakarte.tk/topo500/{z}/{x}/{y}",
+ {code: 'B', tms: true, scaleDependent: false, maxNativeZoom: 14, print: true, jnx: true}
+ )
+ },
+ {
+ title: 'GGC 250m',
+ order: 1090,
+ isOverlay: true,
+ isDefault: true,
+ layer: L.tileLayer("http://{s}.tiles.nakarte.tk/ggc250/{z}/{x}/{y}",
+ {code: 'K', tms: true, scaleDependent: false, maxNativeZoom: 15, print: true, jnx: true}
+ )
+ },
+ {
+ title: 'Slazav map',
+ order: 1100,
+ isOverlay: true,
+ isDefault: true,
+ layer: L.tileLayer("http://{s}.tiles.nakarte.tk/map_podm/{z}/{x}/{y}",
+ {code: 'Z', tms: true, scaleDependent: false, maxNativeZoom: 14, print: true, jnx: true}
+ )
+ },
+ {
+ title: 'O-sport',
+ order: 1110,
+ isOverlay: true,
+ isDefault: true,
+ layer: L.tileLayer("http://{s}.tiles.nakarte.tk/osport/{z}/{x}/{y}",
+ {code: 'R', tms: true, scaleDependent: false, maxNativeZoom: 17, print: true, jnx: true}
+ )
+ },
+ {
+ title: 'Soviet topo maps grid',
+ order: 1120,
+ isOverlay: true,
+ isDefault: true,
+ layer: new L.Layer.SovietTopoGrid({code: 'Ng'})
+ },
+ // {
+ // title: 'Wikimapia',
+ // order: 1130,
+ // isOverlay: true,
+ // isDefault: true,
+ // layer: new L.Wikimapia({code: 'W', zIndexOffset: 10000}),
+ // },
+ {
+ title: 'Mountain passes (Westra)',
+ order: 1140,
+ isOverlay: true,
+ isDefault: true,
+ layer: new L.Layer.WestraPasses(config.westraDataBaseUrl, {
+ code: 'Wp',
+ print: true,
+ scaleDependent: true
+ }
+ )
+ }]
+ },
+
+
+ {
+ group: 'OpenStreetMap alternatives',
+ layers: [
+ {
+ title: 'OpenTopoMap',
+ order: 11,
+ isOverlay: false,
+ isDefault: false,
+ layer: L.tileLayer('https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png',
+ {code: 'Otm', scaleDependent: true, print: true, jnx: true}
+ )
+ },
+ {
+ title: 'OpenCycleMap',
+ order: 12,
+ isOverlay: false,
+ isDefault: false,
+ layer: L.tileLayer('http://{s}.tile.thunderforest.com/cycle/{z}/{x}/{y}.png',
+ {code: 'Ocm', maxNativeZoom: 17, scaleDependent: true, print: true, jnx: true}
+ )
+ }]
+ },
+ {
+ group: 'Topo maps',
+ layers: [
+ {
+ title: 'Topo 250m',
+ order: 1095,
+ isOverlay: true,
+ isDefault: false,
+ layer: L.tileLayer("http://{s}.tiles.nakarte.tk/topo250/{z}/{x}/{y}",
+ {code: 'T25', tms: true, maxNativeZoom: 15, print: true, jnx: true, scaleDependent: false}
+ )
+ }]
+ },
+ {
+ group: 'Mountains maps',
+ layers: [
+ {
+ title: 'Mountains by Aleksey Tsvetkov',
+ description: 'Tian Shan, Dzungaria, <a href="http://pereval.g-utka.ru/">http://pereval.g-utka.ru/</a>',
+ order: 1039,
+ isOverlay: true,
+ isDefault: false,
+ layer: L.tileLayer("http://map.g-utka.ru/{z}/{x}/{y}.png",
+ // FIXME: сделать minZoom=5, когда перейдём на версию leaflet с поддержкой minNativeZoom
+ {
+ code: 'Mt',
+ tms: false,
+ minZoom: 7,
+ minNativeZoom: 7,
+ maxNativeZoom: 15,
+ print: true,
+ jnx: true,
+ scaleDependent: false
+ }
+ )
+ }]
+ },
+ {
+ group: 'Norway <a href="https://www.ut.no/kart/">https://www.ut.no/kart/</a>',
+ layers: [
+ {
+ // Вместо 404 отдают 500 для отсутствующих тайлов
+ title: 'UT map',
+ order: 500,
+ isOverlay: false,
+ isDefault: false,
+ layer: L.tileLayer("http://dntutnotilesprod.cloudapp.net/tilestache/ut_topo_light/{z}/{x}/{y}.jpg",
+ {code: 'Nu', tms: false, maxNativeZoom: 16, print: true, jnx: true, scaleDependent: true}
+ )
+ },
+ // {
+ // title: '',
+ // order: 0,
+ // isOverlay: true,
+ // isDefault: false,
+ // layer: null
+ // },
+ // {
+ // title: '',
+ // order: 0,
+ // isOverlay: true,
+ // isDefault: false,
+ // layer: null
+ // },
+ {
+ // Вместо 404 отдают 500 для отсутствующих тайлов
+ title: 'Norway roads',
+ order: 500,
+ isOverlay: false,
+ isDefault: false,
+ layer: L.tileLayer("http://maptiles1.finncdn.no/tileService/1.0.3/normap/{z}/{x}/{y}.png",
+ {code: 'Nr', tms: false, print: true, jnx: true, scaleDependent: true}
+ )
-function getOverlays() {
- return {
- "Topo 10km": new L.TileLayer("http://{s}.tiles.nakarte.tk/topo001m/{z}/{x}/{y}",
- {code: 'D', tms: true, maxNativeZoom: 9, print: true, jnx: true}),
- "GGC 2 km": new L.TileLayer("http://{s}.tiles.nakarte.tk/ggc2000/{z}/{x}/{y}",
- {code: 'N', tms: true, maxNativeZoom: 15, print: true, jnx: true}),
- "ArbaletMO": new L.TileLayer("http://{s}.tiles.nakarte.tk/ArbaletMO/{z}/{x}/{y}",
- {code: 'A', tms: true, maxNativeZoom: 13, print: true, jnx: true}),
- "Slazav mountains": new L.TileLayer("http://{s}.tiles.nakarte.tk/map_hr/{z}/{x}/{y}",
- {code: 'Q', tms: true, maxNativeZoom: 13, print: true, jnx: true}),
- "GGC 1km": new L.TileLayer("http://{s}.tiles.nakarte.tk/ggc1000/{z}/{x}/{y}",
- {code: 'J', tms: true, maxNativeZoom: 13, print: true, jnx: true}),
- "Topo 1km": new L.TileLayer("http://{s}.tiles.nakarte.tk/topo1000/{z}/{x}/{y}",
- {code: 'C', tms: true, maxNativeZoom: 13, print: true, jnx: true}),
- "GGC 500m": new L.TileLayer("http://{s}.tiles.nakarte.tk/ggc500/{z}/{x}/{y}",
- {code: 'F', tms: true, maxNativeZoom: 14, print: true, jnx: true}),
- "Topo 500m": new L.TileLayer("http://{s}.tiles.nakarte.tk/topo500/{z}/{x}/{y}",
- {code: 'B', tms: true, maxNativeZoom: 14, print: true, jnx: true}),
- "GGC 250m": new L.TileLayer("http://{s}.tiles.nakarte.tk/ggc250/{z}/{x}/{y}",
- {code: 'K', tms: true, maxNativeZoom: 15, print: true, jnx: true}),
- "Slazav map": new L.TileLayer("http://{s}.tiles.nakarte.tk/map_podm/{z}/{x}/{y}",
- {code: 'Z', tms: true, maxNativeZoom: 14, print: true, jnx: true}),
- "O-sport": new L.TileLayer("http://{s}.tiles.nakarte.tk/osport/{z}/{x}/{y}",
- {code: 'R', tms: true, maxNativeZoom: 17, print: true, jnx: true}),
- "Soviet topo maps grid": new L.Layer.SovietTopoGrid({code: 'Ng'}),
- // "Wikimapia": new L.Wikimapia({code: 'W', zIndexOffset: 10000}),
- // "Google Street View": new L.GoogleStreetView('street-view', {print: true, code: 'Gs', zIndexOffset: 10000}),
- "Mountain passes (Westra)": new L.Layer.WestraPasses(config.westraDataBaseUrl, {code: 'Wp', print: true})
- };
+ }]
+ },
+ {
+ group: 'Czech <a href="http://mapy.cz">http://mapy.cz</a>',
+ layers: [
+ {
+ title: 'Czech basic',
+ order: 501,
+ isOverlay: false,
+ isDefault: false,
+ layer: L.tileLayer("https://m{s}.mapserver.mapy.cz/base-m/{z}-{x}-{y}",
+ {code: 'Czb', tms: false, print: true, jnx: true, subdomains: '1234', scaleDependent: true}
+ )
+ },
+ {
+ title: 'Czhch tourist',
+ order: 502,
+ isOverlay: false,
+ isDefault: false,
+ layer: L.tileLayer("https://m{s}.mapserver.mapy.cz/wturist-m/{z}-{x}-{y}",
+ {code: 'Czt', tms: false, print: true, jnx: true, subdomains: '1234', scaleDependent: true}
+ )
+ },
+ {
+ title: 'Czech summer',
+ order: 503,
+ isOverlay: false,
+ isDefault: false,
+ layer: L.tileLayer("https://m{s}.mapserver.mapy.cz/turist_aquatic-m/{z}-{x}-{y}",
+ {code: 'Czs', tms: false, print: true, jnx: true, subdomains: '1234', scaleDependent: true}
+ )
+ },
+ {
+ title: 'Czech winter',
+ order: 504,
+ isOverlay: false,
+ isDefault: false,
+ layer: L.tileLayer("https://m{s}.mapserver.mapy.cz/wturist_winter-m/{z}-{x}-{y}",
+ {code: 'Czw', tms: false, print: true, jnx: true, subdomains: '1234', scaleDependent: true}
+ )
+ },
+ {
+ title: 'Czech geographical',
+ order: 505,
+ isOverlay: false,
+ isDefault: false,
+ layer: L.tileLayer("https://m{s}.mapserver.mapy.cz/zemepis-m/{z}-{x}-{y}",
+ {code: 'Czg', tms: false, print: true, jnx: true, subdomains: '1234', scaleDependent: true}
+ )
+ }]
+ },
+ // {
+ // title: '',
+ // order: 0,
+ // isOverlay: true,
+ // isDefault: false,
+ // layer: null
+ // },
+ ];
+ // TODO: move it to tests
+ const codes = {};
+ for (let group of layers) {
+ for (let layer of group.layers) {
+ layer = layer.layer;
+ if (!layer.options) {
+ throw new Error('Layer without options: ' + layer.title);
+ }
+ let code = layer.options.code;
+ if (!code) {
+ throw new Error('Layer without code: ' + layer.title);
+ }
+ if (code in codes) {
+ throw new Error(`Duplicate layer code "${code}"`);
+ }
+ codes[code] = 1;
+ }
+ }
+ return layers;
}
-export default {getBaseMaps, getOverlays};
-\ No newline at end of file
diff --git a/src/lib/leaflet.control.layers.configure/layers-configure.js b/src/lib/leaflet.control.layers.configure/layers-configure.js
@@ -0,0 +1,206 @@
+import L from 'leaflet';
+import './style.css';
+import enableTopRow from 'lib/leaflet.control.layers.top-row/top-row';
+import ko from 'knockout';
+
+
+function enableConfig(control, layers) {
+ const originalOnAdd = control.onAdd;
+ const originalUnserializeState = control.unserializeState;
+ if (control._configEnabled) {
+ return;
+ }
+ enableTopRow(control);
+
+ L.Util.extend(control, {
+ configEnabled: true,
+ _allLayersGroups: layers,
+ _allLayers: [].concat(...layers.map(group => group.layers)),
+
+ onAdd: function(map) {
+ const container = originalOnAdd.call(this, map);
+ this.__injectConfigButton();
+ return container;
+ },
+
+ __injectConfigButton: function() {
+ const configButton = L.DomUtil.create('div', 'button-config');
+ configButton.innerHTML = 'More layers';
+ this._topRow.appendChild(configButton);
+ L.DomEvent.on(configButton, 'click', this._onConfigButtonClick, this);
+ },
+
+ _initializeLayersState: function() {
+ let storedLayersEnabled = {};
+ if (window.localStorage) {
+ const serialized = window.localStorage.getItem('layersEnabled');
+ if (serialized) {
+ try {
+ storedLayersEnabled = JSON.parse(serialized);
+ } catch (e) {
+ }
+ }
+ }
+ for (let layer of this._allLayers) {
+ // TODO: check if state is stored in localStorage, else
+ let enabled = storedLayersEnabled[layer.layer.options.code];
+ // if storage is empty enable only default layers
+ // if new default layer appears it will be enabled
+ if (typeof enabled === 'undefined') {
+ enabled = layer.isDefault;
+ }
+ layer.enabled = enabled;
+ layer.checked = ko.observable(enabled);
+ }
+ this.storeEnabledLayers();
+ this.updateEnabledLayers();
+ },
+
+ _onConfigButtonClick: function() {
+ this.showLayersSelectWindow()
+ },
+
+ _initLayersSelectWindow: function() {
+ if (this._configWindow) {
+ return;
+ }
+
+ const container = this._configWindow =
+ L.DomUtil.create('div', 'leaflet-layers-select-window-wrapper');
+ L.DomEvent.disableClickPropagation(container);
+ if (!L.Browser.touch) {
+ L.DomEvent.disableScrollPropagation(container);
+ }
+ container.innerHTML = `
+<div class="leaflet-layers-select-window">
+ <form data-bind="foreach: _allLayersGroups">
+ <div class="section-header" data-bind="html: group"></div>
+ <!-- ko foreach: layers -->
+ <label>
+ <input type="checkbox" data-bind="checked: checked"/>
+ <span data-bind="text: title"></span>
+ </label>
+ <!-- /ko -->
+ </form>
+ <div class="buttons-row">
+ <div href="#" class="button" data-bind="click: onSelectWindowOkClicked">Ok</div>
+ <div href="#" class="button" data-bind="click: onSelectWindowCancelClicked">Cancel</div>
+ <div href="#" class="button" data-bind="click: onSelectWindowResetClicked">Reset</div>
+ </div>
+</div>
+ `;
+ ko.applyBindings(this, container);
+ },
+
+ showLayersSelectWindow: function() {
+ if (this._configWindowVisible) {
+ return;
+ }
+ this._allLayers.forEach(layer => layer.checked(layer.enabled));
+ this._initLayersSelectWindow();
+ this._map._controlContainer.appendChild(this._configWindow);
+ this._configWindowVisible = true;
+ },
+
+ hideSelectWindow: function() {
+ if (!this._configWindowVisible) {
+ return;
+ }
+ this._map._controlContainer.removeChild(this._configWindow);
+ this._configWindowVisible = false;
+ },
+
+ onSelectWindowCancelClicked: function() {
+ this.hideSelectWindow();
+ },
+
+ onSelectWindowResetClicked: function() {
+ if (!this._configWindow) {
+ return;
+ }
+ this._allLayers.forEach(layer => layer.checked(layer.isDefault))
+ },
+
+ onSelectWindowOkClicked: function() {
+ this._allLayers.forEach(layer => layer.enabled = layer.checked())
+ this.updateEnabledLayers();
+ this.hideSelectWindow();
+ this.storeEnabledLayers();
+ },
+
+ updateEnabledLayers: function() {
+ const layersOnMap = [];
+ while (this._layers.length) {
+ let layer = this._layers[0];
+ if (this._map.hasLayer(layer.layer)) {
+ layersOnMap.push(layer.layer);
+ }
+ this.removeLayer(layer.layer);
+ this._map.removeLayer(layer.layer);
+ }
+
+ let hasBaselayerOnMap = false;
+ const enabledLayers = [];
+ for (let layer of this._allLayers) {
+ if (layer.enabled) {
+ enabledLayers.push(layer);
+ }
+ }
+ enabledLayers.sort((l1, l2) => l1.order - l2.order);
+ enabledLayers.forEach((l) => {
+ l.isOverlay ? this.addOverlay(l.layer, l.title) : this.addBaseLayer(l.layer, l.title);
+ if (layersOnMap.includes(l.layer)) {
+ this._map.addLayer(l.layer);
+ hasBaselayerOnMap = hasBaselayerOnMap || !l.isOverlay;
+ }
+ }
+ );
+ // если нет активного базового слоя, включить первый, если он есть
+ if (!hasBaselayerOnMap) {
+ for (let layer of enabledLayers) {
+ if (!layer.isOverlay) {
+ this._map.addLayer(layer.layer);
+ break;
+ }
+ }
+ }
+
+ },
+
+ storeEnabledLayers: function() {
+ if (!window.localStorage) {
+ return;
+ }
+ const layersState = {};
+ for (let layer of this._allLayers) {
+ if (layer.isDefault || layer.enabled) {
+ layersState[layer.layer.options.code] = layer.enabled;
+ }
+ }
+ const serialized = JSON.stringify(layersState);
+ localStorage.setItem('layersEnabled', serialized);
+ },
+
+ unserializeState: function(values) {
+ if (values) {
+ for (let layer of this._allLayers) {
+ if (layer.layer.options && values.includes(layer.layer.options.code)) {
+ layer.enabled = true;
+ }
+ }
+ this.updateEnabledLayers();
+ }
+ this.storeEnabledLayers();
+ return originalUnserializeState.call(this, values);
+ }
+
+ }
+ );
+ if (control._map) {
+ control.__injectConfigButton()
+ }
+ control._initializeLayersState();
+}
+
+
+export default enableConfig;
+\ No newline at end of file
diff --git a/src/lib/leaflet.control.layers.configure/style.css b/src/lib/leaflet.control.layers.configure/style.css
@@ -0,0 +1,90 @@
+.leaflet-control-layers .button-config {
+ cursor: pointer;
+ width: auto;
+ margin-right: auto;
+
+ height: 18px;
+ border-radius: 4px;
+ border: 1px solid #ccc;
+ background-color: white;
+
+ padding: 0 4px;
+ font-weight: bold;
+ color: #777777;
+ float: left;
+}
+
+.leaflet-control-layers .button-config:hover {
+ background-color: #f4f4f4;
+}
+
+.leaflet-layers-select-window-wrapper {
+ position: absolute;
+ top: 25px;
+ bottom: 25px;
+ max-width: 100%;
+ padding-bottom: 50px;
+ left: 50%;
+}
+
+.leaflet-layers-select-window {
+ position: relative;
+ left: -50%;
+ background-color: white;
+ border-radius: 5px;
+ box-shadow: 0 1px 5px rgba(0, 0, 0, 0.4);
+ z-index: 3000;
+ max-width: 100%;
+ overflow: hidden;
+ height: 100%;
+ padding: 6px 0 40px 6px;
+}
+
+.leaflet-layers-select-window form {
+ overflow: auto;
+ overflow: overlay;
+ padding-right: 18px;
+ max-height: 100%;
+ margin-bottom: 50px;
+ width: auto ;
+}
+
+.leaflet-layers-select-window * {
+ white-space: nowrap;
+}
+
+.leaflet-layers-select-window .buttons-row {
+ position: absolute;
+ bottom: 0;
+ margin-bottom: 6px;
+}
+
+.leaflet-layers-select-window label {
+ display: block;
+}
+
+.leaflet-layers-select-window .button {
+ display: inline-block;
+ height: 26px;
+ border-radius: 4px;
+ border: 1px solid #ccc;
+ cursor: pointer;
+ margin-right: 6px;
+ padding: 0 1em;
+ line-height: 26px;
+ font-weight: bold;
+ color: #333;
+}
+
+.leaflet-layers-select-window .button:hover {
+ background-color: #f4f4f4;
+}
+
+.leaflet-layers-select-window .section-header{
+ font-size: 14px;
+ font-weight: bold;
+}
+
+.leaflet-layers-select-window a {
+ color: #666;
+}
+\ No newline at end of file