PageSourceSearch

https://radzima.net/src/ol3-layerswitcher.js

js radzima.net collected 2026-10-02 11:55:18 UTC 10,063 bytes, 301 lines download raw bytes

1(function (root, factory) {
2  if(typeof define === "function" && define.amd) {
3    define(["openlayers"], factory);
4  } else if(typeof module === "object" && module.exports) {
5    module.exports = factory(require("openlayers"));
6  } else {
7    root.LayerSwitcher = factory(root.ol);
8  }
9}(this, function(ol) {
10    /**
11     * OpenLayers v3/v4 Layer Switcher Control.
12     * See [the examples](./examples) for usage.
13     * @constructor
14     * @extends {ol.control.Control}
15     * @param {Object} opt_options Control options, extends olx.control.ControlOptions adding:
16     *                              **`tipLabel`** `String` - the button tooltip.
17     */
18    ol.control.LayerSwitcher = function(opt_options) {
19
20        var options = opt_options || {};
21
22        var tipLabel = options.tipLabel ?
23          options.tipLabel : 'Legend';
24
25        this.mapListeners = [];
26
27        this.hiddenClassName = 'ol-unselectable ol-control layer-switcher';
28        if (ol.control.LayerSwitcher.isTouchDevice_()) {
29            this.hiddenClassName += ' touch';
30        }
31        this.shownClassName = 'shown';
32
33        var element = document.createElement('div');
34        element.className = this.hiddenClassName;
35
36        var button = document.createElement('button');
37        button.setAttribute('title', tipLabel);
38        element.appendChild(button);
39
40        this.panel = document.createElement('div');
41        this.panel.className = 'panel';
42        element.appendChild(this.panel);
43        ol.control.LayerSwitcher.enableTouchScroll_(this.panel);
44
45        var this_ = this;
46
47        button.onmouseover = function(e) {
48            this_.showPanel();
49        };
50
51        button.onclick = function(e) {
52            e = e || window.event;
53            this_.showPanel();
54            e.preventDefault();
55        };
56
57        this_.panel.onmouseout = function(e) {
58            e = e || window.event;
59            if (!this_.panel.contains(e.toElement || e.relatedTarget)) {
60                this_.hidePanel();
61            }
62        };
63
64        ol.control.Control.call(this, {
65            element: element,
66            target: options.target
67        });
68
69    };
70
71    ol.inherits(ol.control.LayerSwitcher, ol.control.Control);
72
73    /**
74     * Show the layer panel.
75     */
76    ol.control.LayerSwitcher.prototype.showPanel = function() {
77        if (!this.element.classList.contains(this.shownClassName)) {
78            this.element.classList.add(this.shownClassName);
79            this.renderPanel();
80        }
81    };
82
83    /**
84     * Hide the layer panel.
85     */
86    ol.control.LayerSwitcher.prototype.hidePanel = function() {
87        if (this.element.classList.contains(this.shownClassName)) {
88            this.element.classList.remove(this.shownClassName);
89        }
90    };
91
92    /**
93     * Re-draw the layer panel to represent the current state of the layers.
94     */
95    ol.control.LayerSwitcher.prototype.renderPanel = function() {
96
97        this.ensureTopVisibleBaseLayerShown_();
98
99        while(this.panel.firstChild) {
100            this.panel.removeChild(this.panel.firstChild);
101        }
102
103        var ul = document.createElement('ul');
104        this.panel.appendChild(ul);
105        this.renderLayers_(this.getMap(), ul);
106
107    };
108
109    /**
110     * Set the map instance the control is associated with.
111     * @param {ol.Map} map The map instance.
112     */
113    ol.control.LayerSwitcher.prototype.setMap = function(map) {
114        // Clean up listeners associated with the previous map
115        for (var i = 0, key; i < this.mapListeners.length; i++) {
116            ol.Observable.unByKey(this.mapListeners[i]);
117        }
118        this.mapListeners.length = 0;
119        // Wire up listeners etc. and store reference to new map
120        ol.control.Control.prototype.setMap.call(this, map);
121        if (map) {
122            var this_ = this;
123            this.mapListeners.push(map.on('pointerdown', function() {
124                this_.hidePanel();
125            }));
126            this.renderPanel();
127        }
128    };
129
130    /**
131     * Ensure only the top-most base layer is visible if more than one is visible.
132     * @private
133     */
134    ol.control.LayerSwitcher.prototype.ensureTopVisibleBaseLayerShown_ = function() {
135        var lastVisibleBaseLyr;
136        ol.control.LayerSwitcher.forEachRecursive(this.getMap(), function(l, idx, a) {
137            if (l.get('type') === 'base' && l.getVisible()) {
138                lastVisibleBaseLyr = l;
139            }
140        });
141        if (lastVisibleBaseLyr) this.setVisible_(lastVisibleBaseLyr, true);
142    };
143
144    /**
145     * Toggle the visible state of a layer.
146     * Takes care of hiding other layers in the same exclusive group if the layer
147     * is toggle to visible.
148     * @private
149     * @param {ol.layer.Base} The layer whos visibility will be toggled.
150     */
151    ol.control.LayerSwitcher.prototype.setVisible_ = function(lyr, visible) {
152        var map = this.getMap();
153        lyr.setVisible(visible);
154        if (visible && lyr.get('type') === 'base') {
155            // Hide all other base layers regardless of grouping
156            ol.control.LayerSwitcher.forEachRecursive(map, function(l, idx, a) {
157                if (l != lyr && l.get('type') === 'base') {
158                    l.setVisible(false);
159                }
160            });
161        }
162    };
163
164    /**
165     * Render all layers that are children of a group.
166     * @private
167     * @param {ol.layer.Base} lyr Layer to be rendered (should have a title property).
168     * @param {Number} idx Position in parent group list.
169     */
170    ol.control.LayerSwitcher.prototype.renderLayer_ = function(lyr, idx) {
171
172        var this_ = this;
173
174        var li = document.createElement('li');
175
176        var lyrTitle = lyr.get('title');
177        var lyrId = ol.control.LayerSwitcher.uuid();
178
179        var label = document.createElement('label');
180
181        if (lyr.getLayers && !lyr.get('combine')) {
182
183            li.className = 'group';
184            label.innerHTML = lyrTitle;
185            li.appendChild(label);
186            var ul = document.createElement('ul');
187            li.appendChild(ul);
188
189            this.renderLayers_(lyr, ul);
190
191        } else {
192
193            li.className = 'layer';
194            var input = document.createElement('input');
195            if (lyr.get('type') === 'base') {
196                input.type = 'radio';
197                input.name = 'base';
198            } else {
199                input.type = 'checkbox';
200            }
201            input.id = lyrId;
202            input.checked = lyr.get('visible');
203            input.onchange = function(e) {
204                this_.setVisible_(lyr, e.target.checked);
205            };
206            li.appendChild(input);
207
208            label.htmlFor = lyrId;
209            label.innerHTML = lyrTitle;
210
211            var rsl = this.getMap().getView().getResolution();
212            if (rsl > lyr.getMaxResolution() || rsl < lyr.getMinResolution()){
213                label.className += ' disabled';
214            }
215
216            li.appendChild(label);
217
218        }
219
220        return li;
221
222    };
223
224    /**
225     * Render all layers that are children of a group.
226     * @private
227     * @param {ol.layer.Group} lyr Group layer whos children will be rendered.
228     * @param {Element} elm DOM element that children will be appended to.
229     */
230    ol.control.LayerSwitcher.prototype.renderLayers_ = function(lyr, elm) {
231        var lyrs = lyr.getLayers().getArray().slice().reverse();
232        for (var i = 0, l; i < lyrs.length; i++) {
233            l = lyrs[i];
234            if (l.get('title')) {
235                elm.appendChild(this.renderLayer_(l, i));
236            }
237        }
238    };
239
240    /**
241     * **Static** Call the supplied function for each layer in the passed layer group
242     * recursing nested groups.
243     * @param {ol.layer.Group} lyr The layer group to start iterating from.
244     * @param {Function} fn Callback which will be called for each `ol.layer.Base`
245     * found under `lyr`. The signature for `fn` is the same as `ol.Collection#forEach`
246     */
247    ol.control.LayerSwitcher.forEachRecursive = function(lyr, fn) {
248        lyr.getLayers().forEach(function(lyr, idx, a) {
249            fn(lyr, idx, a);
250            if (lyr.getLayers) {
251                ol.control.LayerSwitcher.forEachRecursive(lyr, fn);
252            }
253        });
254    };
255
256    /**
257     * Generate a UUID
258     * @returns {String} UUID
259     *
260     * Adapted from http://stackoverflow.com/a/2117523/526860
261     */
262    ol.control.LayerSwitcher.uuid = function() {
263        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
264            var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
265            return v.toString(16);
266        });
267    }
268
269    /**
270    * @private
271    * @desc Apply workaround to enable scrolling of overflowing content within an
272    * element. Adapted from https://gist.github.com/chrismbarr/4107472
273    */
274    ol.control.LayerSwitcher.enableTouchScroll_ = function(elm) {
275       if(ol.control.LayerSwitcher.isTouchDevice_()){
276           var scrollStartPos = 0;
277           elm.addEventListener("touchstart", function(event) {
278               scrollStartPos = this.scrollTop + event.touches[0].pageY;
279           }, false);
280           elm.addEventListener("touchmove", function(event) {
281               this.scrollTop = scrollStartPos - event.touches[0].pageY;
282           }, false);
283       }
284    };
285
286    /**
287     * @private
288     * @desc Determine if the current browser supports touch events. Adapted from
289     * https://gist.github.com/chrismbarr/4107472
290     */
291    ol.control.LayerSwitcher.isTouchDevice_ = function() {
292        try {
293            document.createEvent("TouchEvent");
294            return true;
295        } catch(e) {
296            return false;
297        }
298    };
299    var LayerSwitcher = ol.control.LayerSwitcher;
300    return LayerSwitcher;
301}));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.