@russss/maplibregl-layer-switcher 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +5 -0
- package/README.md +73 -0
- package/lib/index.js +152 -0
- package/lib/layerswitcher.css +47 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/lib/urlhash.js +93 -0
- package/package.json +25 -0
- package/tsconfig.tsbuildinfo +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
Copyright 2020 Russ Garrett
|
|
2
|
+
|
|
3
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
|
4
|
+
|
|
5
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# MapLibre GL Layer Switcher
|
|
2
|
+
|
|
3
|
+
This is a layer switcher component for MapLibre GL JS, which I use on [OpenInfraMap](https://openinframap.org).
|
|
4
|
+
Layers are selected by the string prefix of the layer name.
|
|
5
|
+
|
|
6
|
+
This package also includes a modified URL hash component which allows a compact representation of the enabled
|
|
7
|
+
layers to be added to the URL hash.
|
|
8
|
+
|
|
9
|
+
This package was formerly called `mapboxgl-layer-switcher` but after renaming it does not support Mapbox GL.
|
|
10
|
+
|
|
11
|
+
## Use
|
|
12
|
+
|
|
13
|
+
These steps assume you're using Webpack with css-loader. First, import the layer switcher component:
|
|
14
|
+
```javascript
|
|
15
|
+
import LayerSwitcher from '@russss/maplibregl-layer-switcher';
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Assuming you are using a build system which supports CSS imports, the CSS should be imported automatically.
|
|
19
|
+
|
|
20
|
+
Now define your layers as an object mapping the layer name to the prefix of the Mapbox GL layers you
|
|
21
|
+
want to switch. We also define a list of layers which will be initially enabled:
|
|
22
|
+
```javascript
|
|
23
|
+
const layers = {
|
|
24
|
+
Power: 'power_',
|
|
25
|
+
'Solar Generation': 'heatmap_',
|
|
26
|
+
Labels: 'place_',
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const layers_enabled = ['Power', 'Labels'];
|
|
30
|
+
const layer_switcher = new LayerSwitcher(layers, layers_enabled);
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Adding the layer switcher to the map after it's been initialised will result in the hidden layers being briefly
|
|
34
|
+
shown. To avoid this, we need to load the style into JS first (I usually bundle it with the rest of my JS), and
|
|
35
|
+
call the `setInitialVisibility` method on the loaded style object.
|
|
36
|
+
|
|
37
|
+
```javascript
|
|
38
|
+
const style = //...load map style JSON into an object
|
|
39
|
+
layer_switcher.setInitialVisibility(style);
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Now we can instantiate our map and add the layer switcher to it:
|
|
43
|
+
```javascript
|
|
44
|
+
|
|
45
|
+
const map = new maplibregl.Map({style: style, container: 'map'});
|
|
46
|
+
map.addControl(layer_switcher, 'top-right');
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## URL Hash
|
|
50
|
+
|
|
51
|
+
To include layers in the URL hash, first import and create the `URLHash` object, passing in your `LayerSwitcher`:
|
|
52
|
+
|
|
53
|
+
```javascript
|
|
54
|
+
import URLHash from '@russss/maplibregl-layer-switcher/urlhash';
|
|
55
|
+
// ...
|
|
56
|
+
const url_hash = new URLHash(layer_switcher);
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
When creating the map, you need to call `url_hash.init`, passing in your default map options. The URLHash class will
|
|
60
|
+
update the `zoom` and `center` options if they're provided in the URL hash. Then call `url_hash.enable` on the map:
|
|
61
|
+
```javascript
|
|
62
|
+
var map = new maplibregl.Map(url_hash.init({
|
|
63
|
+
container: 'map',
|
|
64
|
+
style: style,
|
|
65
|
+
minZoom: 2,
|
|
66
|
+
maxZoom: 17.9,
|
|
67
|
+
center: [12, 26],
|
|
68
|
+
}));
|
|
69
|
+
|
|
70
|
+
url_hash.enable(map);
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The URLHash object doesn't currently support tilt and rotation parameters.
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { el } from 'redom';
|
|
2
|
+
import invert from 'lodash.invert';
|
|
3
|
+
import isEqual from 'lodash.isequal';
|
|
4
|
+
import './layerswitcher.css';
|
|
5
|
+
class LayerSwitcher {
|
|
6
|
+
constructor(layers, default_visible = []) {
|
|
7
|
+
this._layers = layers;
|
|
8
|
+
this._identifiers = this._initLayerIdentifiers();
|
|
9
|
+
this._default_visible = default_visible;
|
|
10
|
+
this._container = el('div', { class: 'maplibregl-ctrl layer-switcher-list' });
|
|
11
|
+
this._container.appendChild(el('h3', 'Layers'));
|
|
12
|
+
this._visible = [...default_visible];
|
|
13
|
+
}
|
|
14
|
+
_initLayerIdentifiers() {
|
|
15
|
+
let identifiers = {};
|
|
16
|
+
Object.keys(this._layers)
|
|
17
|
+
.sort()
|
|
18
|
+
.forEach(layer_name => {
|
|
19
|
+
let size = 1;
|
|
20
|
+
let ident = null;
|
|
21
|
+
do {
|
|
22
|
+
ident = layer_name.slice(0, size);
|
|
23
|
+
size++;
|
|
24
|
+
} while (ident in identifiers);
|
|
25
|
+
identifiers[ident] = layer_name;
|
|
26
|
+
});
|
|
27
|
+
return identifiers;
|
|
28
|
+
}
|
|
29
|
+
_getLayerIdentifiers() {
|
|
30
|
+
let identifiers = [];
|
|
31
|
+
let id_map = invert(this._identifiers);
|
|
32
|
+
this._visible.sort().forEach(layer_name => {
|
|
33
|
+
identifiers.push(id_map[layer_name]);
|
|
34
|
+
});
|
|
35
|
+
return identifiers;
|
|
36
|
+
}
|
|
37
|
+
_updateVisibility() {
|
|
38
|
+
if (!this._map) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
var layers = this._map.getStyle().layers;
|
|
42
|
+
for (let layer of layers) {
|
|
43
|
+
let name = layer['id'];
|
|
44
|
+
for (let layer_name in this._layers) {
|
|
45
|
+
let pref = this._layers[layer_name];
|
|
46
|
+
if (name.startsWith(pref)) {
|
|
47
|
+
if (this._visible.includes(layer_name)) {
|
|
48
|
+
this._map.setLayoutProperty(name, 'visibility', 'visible');
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
this._map.setLayoutProperty(name, 'visibility', 'none');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (this.urlhash) {
|
|
57
|
+
this.urlhash._updateHash();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Modify a MapLibre GL style object before creating the map to set initial visibility states.
|
|
62
|
+
* This prevents flash-of-invisible-layers.
|
|
63
|
+
*
|
|
64
|
+
* @param style the MapLibre GL style object to modify
|
|
65
|
+
*/
|
|
66
|
+
setInitialVisibility(style) {
|
|
67
|
+
for (let layer of style['layers']) {
|
|
68
|
+
for (let layer_name in this._layers) {
|
|
69
|
+
let pref = this._layers[layer_name];
|
|
70
|
+
if (layer['id'].startsWith(pref) &&
|
|
71
|
+
!this._visible.includes(layer['id'])) {
|
|
72
|
+
if (!layer['layout']) {
|
|
73
|
+
layer['layout'] = {};
|
|
74
|
+
}
|
|
75
|
+
layer['layout']['visibility'] = 'none';
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
getURLString() {
|
|
81
|
+
if (!isEqual(this._visible.sort(), this._default_visible.sort())) {
|
|
82
|
+
return this._getLayerIdentifiers().join(',');
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
setURLString(string) {
|
|
87
|
+
if (string) {
|
|
88
|
+
const ids = string.split(',');
|
|
89
|
+
if (ids.length == 0) {
|
|
90
|
+
this._visible = [...this._default_visible];
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
this._visible = ids.map(id => this._identifiers[id]).filter(id => id);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
this._visible = [...this._default_visible];
|
|
98
|
+
}
|
|
99
|
+
if (this._map) {
|
|
100
|
+
this._updateVisibility();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
onAdd(map) {
|
|
104
|
+
this._map = map;
|
|
105
|
+
if (map.isStyleLoaded()) {
|
|
106
|
+
this._updateVisibility();
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
map.on('load', () => {
|
|
110
|
+
this._updateVisibility();
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
this._createList();
|
|
114
|
+
const wrapper = el('div', {
|
|
115
|
+
class: 'maplibregl-ctrl maplibregl-ctrl-group layer-switcher',
|
|
116
|
+
});
|
|
117
|
+
wrapper.appendChild(this._container);
|
|
118
|
+
wrapper.onmouseover = e => {
|
|
119
|
+
this._container.style.display = 'block';
|
|
120
|
+
};
|
|
121
|
+
wrapper.onmouseout = e => {
|
|
122
|
+
this._container.style.display = 'none';
|
|
123
|
+
};
|
|
124
|
+
return wrapper;
|
|
125
|
+
}
|
|
126
|
+
_createList() {
|
|
127
|
+
var list = el('ul');
|
|
128
|
+
var i = 0;
|
|
129
|
+
for (let name in this._layers) {
|
|
130
|
+
let checkbox = el('input', {
|
|
131
|
+
type: 'checkbox',
|
|
132
|
+
id: 'layer' + i,
|
|
133
|
+
checked: this._visible.includes(name),
|
|
134
|
+
});
|
|
135
|
+
let label = el('label', name, { for: 'layer' + i });
|
|
136
|
+
checkbox.onchange = e => {
|
|
137
|
+
if (e.target.checked) {
|
|
138
|
+
this._visible.push(name);
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
this._visible = this._visible.filter(item => item !== name);
|
|
142
|
+
}
|
|
143
|
+
this._updateVisibility();
|
|
144
|
+
};
|
|
145
|
+
let li = el('li', [label, checkbox]);
|
|
146
|
+
list.appendChild(li);
|
|
147
|
+
i++;
|
|
148
|
+
}
|
|
149
|
+
this._container.appendChild(list);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
export default LayerSwitcher;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
.layer-switcher {
|
|
2
|
+
min-height: 30px;
|
|
3
|
+
min-width: 30px;
|
|
4
|
+
background: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%228%22%20height%3D%228%22%20viewBox%3D%220%200%208%208%22%3E%0A%20%20%3Cpath%20d%3D%22M0%200v4h4v-4h-4zm5%202v3h-3v1h4v-4h-1zm2%202v3h-3v1h4v-4h-1z%22%20/%3E%0A%3C/svg%3E')
|
|
5
|
+
white;
|
|
6
|
+
background-repeat: no-repeat;
|
|
7
|
+
background-size: 15px 15px;
|
|
8
|
+
background-position: center;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.layer-switcher-list {
|
|
12
|
+
background: white;
|
|
13
|
+
padding-left: 8px;
|
|
14
|
+
padding-right: 8px;
|
|
15
|
+
position: relative;
|
|
16
|
+
display: none;
|
|
17
|
+
margin: 0 !important;
|
|
18
|
+
font-size: 15px;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.layer-switcher-list h3 {
|
|
22
|
+
margin: 0px;
|
|
23
|
+
margin-top: 6px;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.layer-switcher-list ul {
|
|
27
|
+
list-style: none;
|
|
28
|
+
padding: 0;
|
|
29
|
+
user-select: none;
|
|
30
|
+
margin-bottom: 4px;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.layer-switcher-list li {
|
|
34
|
+
display: block;
|
|
35
|
+
width: 100%;
|
|
36
|
+
height: 25px;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.layer-switcher-list input {
|
|
40
|
+
margin-left: 8px;
|
|
41
|
+
margin-top: 4px;
|
|
42
|
+
float: right;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.layer-switcher-list label {
|
|
46
|
+
float: left;
|
|
47
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"program":{"fileNames":["../node_modules/typescript/lib/lib.es5.d.ts","../node_modules/typescript/lib/lib.es2015.d.ts","../node_modules/typescript/lib/lib.es2016.d.ts","../node_modules/typescript/lib/lib.dom.d.ts","../node_modules/typescript/lib/lib.dom.iterable.d.ts","../node_modules/typescript/lib/lib.webworker.importscripts.d.ts","../node_modules/typescript/lib/lib.scripthost.d.ts","../node_modules/typescript/lib/lib.es2015.core.d.ts","../node_modules/typescript/lib/lib.es2015.collection.d.ts","../node_modules/typescript/lib/lib.es2015.generator.d.ts","../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../node_modules/typescript/lib/lib.es2015.promise.d.ts","../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../node_modules/typescript/lib/lib.decorators.d.ts","../node_modules/typescript/lib/lib.decorators.legacy.d.ts","../node_modules/typescript/lib/lib.es2016.full.d.ts","../node_modules/redom/index.d.ts","../node_modules/@types/lodash/common/common.d.ts","../node_modules/@types/lodash/common/array.d.ts","../node_modules/@types/lodash/common/collection.d.ts","../node_modules/@types/lodash/common/date.d.ts","../node_modules/@types/lodash/common/function.d.ts","../node_modules/@types/lodash/common/lang.d.ts","../node_modules/@types/lodash/common/math.d.ts","../node_modules/@types/lodash/common/number.d.ts","../node_modules/@types/lodash/common/object.d.ts","../node_modules/@types/lodash/common/seq.d.ts","../node_modules/@types/lodash/common/string.d.ts","../node_modules/@types/lodash/common/util.d.ts","../node_modules/@types/lodash/index.d.ts","../node_modules/@types/lodash.invert/index.d.ts","../node_modules/@types/lodash.isequal/index.d.ts","../node_modules/@types/mapbox__point-geometry/index.d.ts","../node_modules/@mapbox/tiny-sdf/index.d.ts","../node_modules/@types/pbf/index.d.ts","../node_modules/@types/geojson/index.d.ts","../node_modules/@types/mapbox__vector-tile/index.d.ts","../node_modules/@maplibre/maplibre-gl-style-spec/dist/index.d.ts","../node_modules/gl-matrix/index.d.ts","../node_modules/kdbush/index.d.ts","../node_modules/potpack/index.d.ts","../node_modules/maplibre-gl/dist/maplibre-gl.d.ts","../src/urlhash.ts","../src/index.ts"],"fileInfos":[{"version":"f59215c5f1d886b05395ee7aca73e0ac69ddfad2843aa88530e797879d511bad","affectsGlobalScope":true},"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","dc48272d7c333ccf58034c0026162576b7d50ea0e69c3b9292f803fc20720fd5",{"version":"3dda5344576193a4ae48b8d03f105c86f20b2f2aff0a1d1fd7935f5d68649654","affectsGlobalScope":true},{"version":"35299ae4a62086698444a5aaee27fc7aa377c68cbb90b441c9ace246ffd05c97","affectsGlobalScope":true},{"version":"c5c5565225fce2ede835725a92a28ece149f83542aa4866cfb10290bff7b8996","affectsGlobalScope":true},{"version":"7d2dbc2a0250400af0809b0ad5f84686e84c73526de931f84560e483eb16b03c","affectsGlobalScope":true},{"version":"9d9885c728913c1d16e0d2831b40341d6ad9a0ceecaabc55209b306ad9c736a5","affectsGlobalScope":true},{"version":"17bea081b9c0541f39dd1ae9bc8c78bdd561879a682e60e2f25f688c0ecab248","affectsGlobalScope":true},{"version":"4443e68b35f3332f753eacc66a04ac1d2053b8b035a0e0ac1d455392b5e243b3","affectsGlobalScope":true},{"version":"ab22100fdd0d24cfc2cc59d0a00fc8cf449830d9c4030dc54390a46bd562e929","affectsGlobalScope":true},{"version":"f7bd636ae3a4623c503359ada74510c4005df5b36de7f23e1db8a5c543fd176b","affectsGlobalScope":true},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true},{"version":"0c20f4d2358eb679e4ae8a4432bdd96c857a2960fd6800b21ec4008ec59d60ea","affectsGlobalScope":true},{"version":"36ae84ccc0633f7c0787bc6108386c8b773e95d3b052d9464a99cd9b8795fbec","affectsGlobalScope":true},{"version":"82d0d8e269b9eeac02c3bd1c9e884e85d483fcb2cd168bccd6bc54df663da031","affectsGlobalScope":true},{"version":"189c0703923150aa30673fa3de411346d727cc44a11c75d05d7cf9ef095daa22","affectsGlobalScope":true},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true},"11d072996eb4179a2697bb0cbaaa6372bd0ec2890d7ea25bc8b17454e2bce322","332f8e56efa13b367cb696e2036918d80887b13177c12acda6d49ee145c333ca","675e702f2032766a91eeadee64f51014c64688525da99dccd8178f0c599f13a8","458111fc89d11d2151277c822dfdc1a28fa5b6b2493cf942e37d4cd0a6ee5f22","19c816167e076e7c24f074389c6cf3ed87bdbb917d1ea439ca281f9d26db2439","187119ff4f9553676a884e296089e131e8cc01691c546273b1d0089c3533ce42","febf0b2de54781102b00f61653b21377390a048fbf5262718c91860d11ff34a6","98f9d826db9cd99d27a01a59ee5f22863df00ccf1aaf43e1d7db80ebf716f7c3","0aaef8cded245bf5036a7a40b65622dd6c4da71f7a35343112edbe112b348a1e","00baffbe8a2f2e4875367479489b5d43b5fc1429ecb4a4cc98cfc3009095f52a","dcd91d3b697cb650b95db5471189b99815af5db2a1cd28760f91e0b12ede8ed5","3c92b6dfd43cc1c2485d9eba5ff0b74a19bb8725b692773ef1d66dac48cda4bd","3cf0d343c2276842a5b617f22ba82af6322c7cfe8bb52238ffc0c491a3c21019","df996e25faa505f85aeb294d15ebe61b399cf1d1e49959cdfaf2cc0815c203f9",{"version":"f2eff8704452659641164876c1ef0df4174659ce7311b0665798ea3f556fa9ad","affectsGlobalScope":true},"b578be2890bf15a1a9b3b8177ff0975c99bae05f5422241452fe810eec3bff1c","6bb8d7433a29fbd33df8e9693f1788a273a9eb90b96c8f99c745678c7db623f1","e86cd9c6c99834f8591ed72f08e4ca31570296f081206d0168f58f032cec5b58","bc8b2489bf29fa17cf4e7d5a4447daa456d7caf61455f50aafc34f1f3b915727","2a856615ecd0af49997dfbb38cf94fd787d529062a2e96ce26b84432e61d785d","a3b6c93a9838b8c94c6998e85646d6f2d07c20ecfe1e235dba62158b29451391","ab438f22fa7748112b6b9744b992ab6049d2fa6f46f011c145ff9c90e533856a","e92ff9eccdb74e9bf34e9dcbd3525252b021ff7fd763e63c95d80c9e0eeb8406","6b729c3274f6d4738fe470109bcb08b09ef98a6b356c01da3ca8dc0996b4458b","4dbf094f9d643b74e516779a802d9c5debd2929fb875ccfbc608d61afba064e4","828ea2cf5792eba68ebd41d6dd17ac67d31622b7889965210f8fa50a6eb29eba","41d979b791363582ff485524e0db7d2312207a811d5f08e92566733ab90dde9f",{"version":"f33c46469c28f89f45e647fc0755cba1d4418ac3b8ac574d114a1b04c5103d3b","signature":"5ae269adda301545c45591e630c59df52e125cadc456f485e1d8b3c577fbbe9c"},{"version":"08aa3ed586993e892107dcc47902f90eb087d2616e1719c1dc608a6506d85ac5","signature":"d645d65f83ca9d4a8c9c1fa8566c6c3a2ecb5b965a09bb3157a1dbeb9c21ac14"}],"root":[47,48],"options":{"esModuleInterop":true,"module":5,"outDir":"./","strict":true,"target":3},"fileIdsList":[[37],[34],[22,24,25,26,27,28,29,30,31,32,33,34],[22,23,25,26,27,28,29,30,31,32,33,34],[23,24,25,26,27,28,29,30,31,32,33,34],[22,23,24,26,27,28,29,30,31,32,33,34],[22,23,24,25,27,28,29,30,31,32,33,34],[22,23,24,25,26,28,29,30,31,32,33,34],[22,23,24,25,26,27,29,30,31,32,33,34],[22,23,24,25,26,27,28,30,31,32,33,34],[22,23,24,25,26,27,28,29,31,32,33,34],[22,23,24,25,26,27,28,29,30,32,33,34],[22,23,24,25,26,27,28,29,30,31,33,34],[22,23,24,25,26,27,28,29,30,31,32,34],[22,23,24,25,26,27,28,29,30,31,32,33],[37,39,40],[37,38,41,42,43,44,45],[21,35,36,46,47],[46,48],[47],[48]],"referencedMap":[[42,1],[35,2],[36,2],[23,3],[24,4],[22,5],[25,6],[26,7],[27,8],[28,9],[29,10],[30,11],[31,12],[32,13],[33,14],[34,15],[41,16],[46,17],[48,18],[47,19]],"exportedModulesMap":[[42,1],[35,2],[36,2],[23,3],[24,4],[22,5],[25,6],[26,7],[27,8],[28,9],[29,10],[30,11],[31,12],[32,13],[33,14],[34,15],[41,16],[46,17],[48,20],[47,21]],"semanticDiagnosticsPerFile":[38,42,40,35,36,23,24,22,25,26,27,28,29,30,31,32,33,34,37,41,39,43,44,46,45,21,18,19,4,5,9,8,2,10,11,12,13,14,15,16,17,3,20,1,7,6,48,47]},"version":"5.1.6"}
|
package/lib/urlhash.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
class URLHash {
|
|
2
|
+
constructor(layerSwitcher) {
|
|
3
|
+
this.layerSwitcher = layerSwitcher;
|
|
4
|
+
this._onHashChange();
|
|
5
|
+
}
|
|
6
|
+
enable(map) {
|
|
7
|
+
this._map = map;
|
|
8
|
+
map.on('moveend', () => {
|
|
9
|
+
this._updateHash();
|
|
10
|
+
});
|
|
11
|
+
window.addEventListener('hashchange', () => {
|
|
12
|
+
this._onHashChange();
|
|
13
|
+
}, false);
|
|
14
|
+
}
|
|
15
|
+
_onHashChange() {
|
|
16
|
+
const loc = window.location.hash.replace('#', '').split('/');
|
|
17
|
+
if (loc.length >= 3) {
|
|
18
|
+
let layerString = "";
|
|
19
|
+
if (this._map) {
|
|
20
|
+
this._map.jumpTo({
|
|
21
|
+
center: [+loc[2], +loc[1]],
|
|
22
|
+
zoom: +loc[0],
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
for (let i = 3; i < loc.length; i++) {
|
|
26
|
+
let component = loc[i];
|
|
27
|
+
let matches = component.match(/([a-z])=(.*)/);
|
|
28
|
+
if (matches) {
|
|
29
|
+
if (matches[1] == 'm') {
|
|
30
|
+
//markerString = matches[2];
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
layerString = component;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (this.layerSwitcher) {
|
|
41
|
+
this.layerSwitcher.setURLString(layerString);
|
|
42
|
+
}
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
if (this.layerSwitcher) {
|
|
46
|
+
this.layerSwitcher.setURLString("");
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
_updateHash() {
|
|
51
|
+
try {
|
|
52
|
+
window.history.replaceState(window.history.state, '', this.getHashString());
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
console.log(e);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
getHashString() {
|
|
59
|
+
if (!this._map) {
|
|
60
|
+
throw new Error('getHashString called before map initialised');
|
|
61
|
+
}
|
|
62
|
+
const center = this._map.getCenter(), zoom = Math.round(this._map.getZoom() * 100) / 100,
|
|
63
|
+
// derived from equation: 512px * 2^z / 360 / 10^d < 0.5px
|
|
64
|
+
precision = Math.ceil((zoom * Math.LN2 + Math.log(512 / 360 / 0.5)) / Math.LN10), m = Math.pow(10, precision), lng = Math.round(center.lng * m) / m, lat = Math.round(center.lat * m) / m, bearing = this._map.getBearing(), pitch = this._map.getPitch();
|
|
65
|
+
let hash = `#${zoom}/${lat}/${lng}`;
|
|
66
|
+
let layers = null;
|
|
67
|
+
if (this.layerSwitcher) {
|
|
68
|
+
layers = this.layerSwitcher.getURLString();
|
|
69
|
+
}
|
|
70
|
+
if (layers) {
|
|
71
|
+
hash += '/' + layers;
|
|
72
|
+
}
|
|
73
|
+
return hash;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Modify MapLibre GL map constructor options to include values from the URL hash.
|
|
77
|
+
*
|
|
78
|
+
* @param options the original options passed to the MapLibre GL `Map` constructor.
|
|
79
|
+
* You should include defaults for the center and zoom parameters.
|
|
80
|
+
* @returns an options object to be passed to the `Map` constructor.
|
|
81
|
+
*/
|
|
82
|
+
init(options) {
|
|
83
|
+
options['hash'] = false;
|
|
84
|
+
const loc = window.location.hash.replace('#', '').split('/');
|
|
85
|
+
if (loc.length >= 3) {
|
|
86
|
+
options['center'] = [+loc[2], +loc[1]];
|
|
87
|
+
options['zoom'] = +loc[0];
|
|
88
|
+
}
|
|
89
|
+
return options;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
;
|
|
93
|
+
export default URLHash;
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@russss/maplibregl-layer-switcher",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Layer switcher for Maplibre GL JS based on layer prefixes",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": "lib/index.js",
|
|
7
|
+
"./urlhash": "lib/urlhash.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "npx tsc && cp src/layerswitcher.css lib/"
|
|
11
|
+
},
|
|
12
|
+
"author": "Russ Garrett",
|
|
13
|
+
"license": "ISC",
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"lodash.invert": "^4.3.0",
|
|
16
|
+
"lodash.isequal": "^4.5.0",
|
|
17
|
+
"redom": "^3.24.2"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/lodash.invert": "^4.3.7",
|
|
21
|
+
"@types/lodash.isequal": "^4.5.6",
|
|
22
|
+
"maplibre-gl": "^3.1.0",
|
|
23
|
+
"typescript": "^5.1.6"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"program":{"fileNames":[],"fileInfos":[],"root":[],"options":{"esModuleInterop":true,"module":1,"outDir":"./","strict":false,"target":3},"referencedMap":[],"exportedModulesMap":[]},"version":"5.1.6"}
|