/* Copyright (c) 2015 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 ol_layer_Tile from 'ol/layer/Tile.js'
import ol_layer_Vector from 'ol/layer/Vector.js'
import ol_layer_VectorTile from 'ol/layer/VectorTile.js'
// ol < 6 compatibility VectorImage is not defined
// import ol_layer_VectorImage from 'ol/layer/VectorImage.js'
import ol_layer_Image from 'ol/layer/Image.js'
import ol_layer_Heatmap from 'ol/layer/Heatmap.js'
import {intersects as ol_extent_intersects} from 'ol/extent.js'
import ol_ext_element from '../util/element.js'
/** Layer Switcher Control.
* @fires select
* @fires drawlist
* @fires toggle
* @fires reorder-start
* @fires reorder-end
* @fires layer:visible
* @fires layer:opacity
* @fires layer:keydown
*
* @constructor
* @extends {ol_control_Control}
* @param {Object=} options
* @param {boolean} options.selection enable layer selection when click on the title
* @param {function} options.displayInLayerSwitcher function that takes a layer and return a boolean if the layer is displayed in the switcher, default test the displayInLayerSwitcher layer attribute
* @param {boolean} options.show_progress show a progress bar on tile layers, default false
* @param {boolean} options.mouseover show the panel on mouseover, default false
* @param {boolean} options.reordering allow layer reordering, default true
* @param {boolean} options.trash add a trash button to delete the layer, default false
* @param {function} options.oninfo callback on click on info button, if none no info button is shown DEPRECATED: use on(info) instead
* @param {boolean} options.extent add an extent button to zoom to the extent of the layer
* @param {function} options.onextent callback when click on extent, default fits view to extent
* @param {number} options.drawDelay delay in ms to redraw the layer (usefull to prevent flickering when manipulating the layers)
* @param {boolean} options.collapsed collapse the layerswitcher at beginning, default true
* @param {ol.layer.Group} options.layerGroup a layer group to display in the switcher, default display all layers of the map
* @param {boolean} options.noScroll prevent handle scrolling, default false
* @param {function} options.onchangeCheck optional callback on click on checkbox, you can call this method for doing operations after check/uncheck a layer
*
* Layers attributes that control the switcher
* - allwaysOnTop {boolean} true to force layer stay on top of the others while reordering, default false
* - displayInLayerSwitcher {boolean} display the layer in switcher, default true
* - noSwitcherDelete {boolean} to prevent layer deletion (w. trash option = true), default false
*/
var ol_control_LayerSwitcher = class olcontrolLayerSwitcher extends ol_control_Control {
constructor(options) {
options = options || {}
var element = ol_ext_element.create('DIV', {
className: options.switcherClass || 'ol-layerswitcher'
})
super({
element: element,
target: options.target
})
var self = this
this.dcount = 0
this.show_progress = options.show_progress
this.oninfo = (typeof (options.oninfo) == 'function' ? options.oninfo : null)
this.onextent = (typeof (options.onextent) == 'function' ? options.onextent : null)
this.hasextent = options.extent || options.onextent
this.hastrash = options.trash
this.reordering = (options.reordering !== false)
this._layers = []
this._layerGroup = (options.layerGroup && options.layerGroup.getLayers) ? options.layerGroup : null
this.onchangeCheck = (typeof (options.onchangeCheck) == "function" ? options.onchangeCheck : null)
// displayInLayerSwitcher
if (typeof (options.displayInLayerSwitcher) === 'function') {
this.displayInLayerSwitcher = options.displayInLayerSwitcher
}
// Insert in the map
if (!options.target) {
element.classList.add('ol-unselectable')
element.classList.add('ol-control')
element.classList.add(options.collapsed !== false ? 'ol-collapsed' : 'ol-forceopen')
this.button = ol_ext_element.create('BUTTON', {
type: 'button',
parent: element
})
this.button.addEventListener('touchstart', function (e) {
element.classList.toggle('ol-forceopen')
element.classList.add('ol-collapsed')
self.dispatchEvent({ type: 'toggle', collapsed: element.classList.contains('ol-collapsed') })
e.preventDefault()
self.overflow()
})
this.button.addEventListener('click', function () {
element.classList.toggle('ol-forceopen')
element.classList.add('ol-collapsed')
self.dispatchEvent({ type: 'toggle', collapsed: !element.classList.contains('ol-forceopen') })
self.overflow()
})
if (options.mouseover) {
element.addEventListener('mouseleave', function () {
element.classList.add("ol-collapsed")
self.dispatchEvent({ type: 'toggle', collapsed: true })
})
element.addEventListener('mouseover', function () {
element.classList.remove("ol-collapsed")
self.dispatchEvent({ type: 'toggle', collapsed: false })
})
}
if (options.minibar)
options.noScroll = true
if (!options.noScroll) {
this.topv = ol_ext_element.create('DIV', {
className: 'ol-switchertopdiv',
parent: element,
click: function () {
self.overflow("+50%")
}
})
this.botv = ol_ext_element.create('DIV', {
className: 'ol-switcherbottomdiv',
parent: element,
click: function () {
self.overflow("-50%")
}
})
}
this._noScroll = options.noScroll
}
this.panel_ = ol_ext_element.create('UL', {
className: 'panel',
})
this.panelContainer_ = ol_ext_element.create('DIV', {
className: 'panel-container',
html: this.panel_,
parent: element
})
// Handle mousewheel
if (!options.target && !options.noScroll) {
ol_ext_element.addListener(this.panel_, 'mousewheel DOMMouseScroll onmousewheel', function (e) {
if (self.overflow(Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail))))) {
e.stopPropagation()
e.preventDefault()
}
})
}
this.header_ = ol_ext_element.create('LI', {
className: 'ol-header',
parent: this.panel_
})
this.set('drawDelay', options.drawDelay || 0)
this.set('selection', options.selection)
if (options.minibar) {
// Wait init complete (for child classes)
setTimeout(function () {
var mbar = ol_ext_element.scrollDiv(this.panelContainer_, {
mousewheel: true,
vertical: true,
minibar: true
})
this.on(['drawlist', 'toggle'], function () {
mbar.refresh()
})
}.bind(this))
}
}
/** Test if a layer should be displayed in the switcher
* @param {ol.layer} layer
* @return {boolean} true if the layer is displayed
*/
displayInLayerSwitcher(layer) {
return (layer.get('displayInLayerSwitcher') !== false)
}
/**
* Set the map instance the control is associated with.
* @param {_ol_Map_} map The map instance.
*/
setMap(map) {
super.setMap(map)
this.drawPanel()
if (this._listener) {
for (var i in this._listener){
ol_Observable_unByKey(this._listener[i])
}
}
this._listener = null
// Get change (new layer added or removed)
if (map) {
this._listener = {
moveend: map.on('moveend', this.viewChange.bind(this)),
size: map.on('change:size', this.overflow.bind(this))
}
// Listen to a layer group
if (this._layerGroup) {
this._listener.change = this._layerGroup.getLayers().on('change:length', this.drawPanel.bind(this))
} else {
//Listen to all layers
this._listener.change = map.getLayerGroup().getLayers().on('change:length', this.drawPanel.bind(this))
}
}
}
/** Show control
*/
show() {
this.element.classList.add('ol-forceopen')
this.overflow()
this.dispatchEvent({ type: 'toggle', collapsed: false })
}
/** Hide control
*/
hide() {
this.element.classList.remove('ol-forceopen')
this.overflow()
this.dispatchEvent({ type: 'toggle', collapsed: true })
}
/** Toggle control
*/
toggle() {
this.element.classList.toggle("ol-forceopen")
this.overflow()
this.dispatchEvent({ type: 'toggle', collapsed: !this.isOpen() })
}
/** Is control open
* @return {boolean}
*/
isOpen() {
return this.element.classList.contains("ol-forceopen")
}
/** Add a custom header
* @param {Element|string} html content html
*/
setHeader(html) {
ol_ext_element.setHTML(this.header_, html)
}
/** Calculate overflow and add scrolls
* @param {Number} dir scroll direction -1|0|1|'+50%'|'-50%'
* @private
*/
overflow(dir) {
if (this.button && !this._noScroll) {
// Nothing to show
if (ol_ext_element.hidden(this.panel_)) {
ol_ext_element.setStyle(this.element, { height: 'auto' })
return
}
// Calculate offset
var h = ol_ext_element.outerHeight(this.element)
var hp = ol_ext_element.outerHeight(this.panel_)
var dh = this.button.offsetTop + ol_ext_element.outerHeight(this.button)
//var dh = this.button.position().top + this.button.outerHeight(true);
var top = this.panel_.offsetTop - dh
if (hp > h - dh) {
// Bug IE: need to have an height defined
ol_ext_element.setStyle(this.element, { height: '100%' })
var li = this.panel_.querySelectorAll('li.ol-visible .li-content')[0]
var lh = li ? 2 * ol_ext_element.getStyle(li, 'height') : 0
switch (dir) {
case 1: top += lh; break
case -1: top -= lh; break
case "+50%": top += Math.round(h / 2); break
case "-50%": top -= Math.round(h / 2); break
default: break
}
// Scroll div
if (top + hp <= h - 3 * dh / 2) {
top = h - 3 * dh / 2 - hp
ol_ext_element.hide(this.botv)
} else {
ol_ext_element.show(this.botv)
}
if (top >= 0) {
top = 0
ol_ext_element.hide(this.topv)
} else {
ol_ext_element.show(this.topv)
}
// Scroll ?
ol_ext_element.setStyle(this.panel_, { top: top + "px" })
return true
} else {
ol_ext_element.setStyle(this.element, { height: "auto" })
ol_ext_element.setStyle(this.panel_, { top: 0 })
ol_ext_element.hide(this.botv)
ol_ext_element.hide(this.topv)
return false
}
}
else
return false
}
/** Set the layer associated with a li
* @param {Element} li
* @param {ol.layer} layer
* @private
*/
_setLayerForLI(li, layer) {
var listeners = []
if (layer.getLayers) {
listeners.push(layer.getLayers().on('change:length', this.drawPanel.bind(this)))
}
if (li) {
// Handle opacity change
listeners.push(layer.on('change:opacity', (function () {
this.setLayerOpacity(layer, li)
}).bind(this)))
// Handle visibility chage
listeners.push(layer.on('change:visible', (function () {
this.setLayerVisibility(layer, li)
}).bind(this)))
}
// Other properties
listeners.push(layer.on('propertychange', (function (e) {
if (e.key === 'displayInLayerSwitcher'
|| e.key === 'openInLayerSwitcher'
|| e.key === 'title'
|| e.key === 'name') {
this.drawPanel(e)
}
}).bind(this)))
this._layers.push({ li: li, layer: layer, listeners: listeners })
}
/** Set opacity for a layer
* @param {ol.layer.Layer} layer
* @param {Element} li the list element
* @private
*/
setLayerOpacity(layer, li) {
var i = li.querySelector('.layerswitcher-opacity-cursor')
if (i){
i.style.left = (layer.getOpacity() * 100) + "%"
}
this.dispatchEvent({ type: 'layer:opacity', layer: layer })
}
/** Set visibility for a layer
* @param {ol.layer.Layer} layer
* @param {Element} li the list element
* @api
*/
setLayerVisibility(layer, li) {
var i = li.querySelector('.ol-visibility')
if (i) {
i.checked = layer.getVisible()
}
if (layer.getVisible()){
li.classList.add('ol-visible')
} else{
li.classList.remove('ol-visible')
}
this.dispatchEvent({ type: 'layer:visible', layer: layer })
}
/** Clear layers associated with li
* @private
*/
_clearLayerForLI() {
this._layers.forEach(function (li) {
li.listeners.forEach(function (l) {
ol_Observable_unByKey(l)
})
})
this._layers = []
}
/** Get the layer associated with a li
* @param {Element} li
* @return {ol.layer}
* @private
*/
_getLayerForLI(li) {
for (var i = 0, l; l = this._layers[i]; i++) {
if (l.li === li) {
return l.layer
}
}
return null
}
/**
* On view change hide layer depending on resolution / extent
* @private
*/
viewChange() {
this.panel_.querySelectorAll('li').forEach(function (li) {
var l = this._getLayerForLI(li)
if (l) {
if (this.testLayerVisibility(l)) {
li.classList.remove('ol-layer-hidden')
} else {
li.classList.add('ol-layer-hidden')
}
}
}.bind(this))
}
/** Get control panel
* @api
*/
getPanel() {
return this.panelContainer_
}
/** Draw the panel control (prevent multiple draw due to layers manipulation on the map with a delay function)
* @api
*/
drawPanel() {
if (!this.getMap())
return
var self = this
// Multiple event simultaneously / draw once => put drawing in the event queue
this.dcount++
setTimeout(function () { self.drawPanel_() }, this.get('drawDelay') || 0)
}
/** Delayed draw panel control
* @private
*/
drawPanel_() {
if (--this.dcount || this.dragging_) {
return
}
var scrollTop = this.panelContainer_.scrollTop
// Remove existing layers
this._clearLayerForLI()
this.panel_.querySelectorAll('li').forEach(function (li) {
if (!li.classList.contains('ol-header')) li.remove()
}.bind(this))
// Draw list
if (this._layerGroup) {
this.drawList(this.panel_, this._layerGroup.getLayers())
} else if (this.getMap()) {
this.drawList(this.panel_, this.getMap().getLayers())
}
// Reset scrolltop
this.panelContainer_.scrollTop = scrollTop
}
/** Change layer visibility according to the baselayer option
* @param {ol.layer}
* @param {Array
} related layers
* @private
*/
switchLayerVisibility(l, layers) {
if (!l.get('baseLayer')) {
l.setVisible(!l.getVisible())
} else {
if (!l.getVisible()) {
l.setVisible(true)
}
layers.forEach(function (li) {
if (l !== li && li.get('baseLayer') && li.getVisible()) {
li.setVisible(false)
}
})
}
}
/** Check if layer is on the map (depending on resolution / zoom and extent)
* @param {ol.layer}
* @return {boolean}
* @private
*/
testLayerVisibility(layer) {
if (!this.getMap())
return true
var res = this.getMap().getView().getResolution()
var zoom = this.getMap().getView().getZoom()
// Calculate visibility on resolution or zoom
if (layer.getMaxResolution() <= res || layer.getMinResolution() >= res) {
return false
} else if (layer.getMinZoom && (layer.getMinZoom() >= zoom || layer.getMaxZoom() < zoom)) {
return false
} else {
// Check extent
var ex0 = layer.getExtent()
if (ex0) {
var ex = this.getMap().getView().calculateExtent(this.getMap().getSize())
return ol_extent_intersects(ex, ex0)
}
return true
}
}
/** Start ordering the list
* @param {event} e drag event
* @private
*/
dragOrdering_(e) {
e.stopPropagation()
e.preventDefault()
// Get params
var self = this
var elt = e.currentTarget.parentNode.parentNode
var start = true
var panel = this.panel_
var pageY
var pageY0 = e.pageY
|| (e.touches && e.touches.length && e.touches[0].pageY)
|| (e.changedTouches && e.changedTouches.length && e.changedTouches[0].pageY)
var target, dragElt
var layer, group
elt.parentNode.classList.add('drag')
// Stop ordering
function stop() {
if (target) {
// Get drag on parent
var drop = layer
var isSelected = self.getSelection() === drop
if (drop && target) {
var collection
if (group) {
collection = group.getLayers()
} else {
collection = self._layerGroup ? self._layerGroup.getLayers() : self.getMap().getLayers()
}
var layers = collection.getArray()
// Switch layers
for (var i = 0; i < layers.length; i++) {
if (layers[i] == drop) {
collection.removeAt(i)
break
}
}
for (var j = 0; j < layers.length; j++) {
if (layers[j] === target) {
if (i > j) {
collection.insertAt(j, drop)
} else {
collection.insertAt(j + 1, drop)
}
break
}
}
}
if (isSelected) self.selectLayer(drop)
self.dispatchEvent({ type: "reorder-end", layer: drop, group: group })
}
elt.parentNode.querySelectorAll('li').forEach(function (li) {
li.classList.remove('dropover')
li.classList.remove('dropover-after')
li.classList.remove('dropover-before')
})
elt.classList.remove("drag")
elt.parentNode.classList.remove("drag")
self.element.classList.remove('drag')
if (dragElt)
dragElt.remove()
ol_ext_element.removeListener(document, 'mousemove touchmove', move)
ol_ext_element.removeListener(document, 'mouseup touchend touchcancel', stop)
}
// Ordering
function move(e) {
// First drag (more than 2 px) => show drag element (ghost)
pageY = e.pageY
|| (e.touches && e.touches.length && e.touches[0].pageY)
|| (e.changedTouches && e.changedTouches.length && e.changedTouches[0].pageY)
if (start && Math.abs(pageY0 - pageY) > 2) {
start = false
elt.classList.add("drag")
layer = self._getLayerForLI(elt)
target = false
group = self._getLayerForLI(elt.parentNode.parentNode)
// Ghost div
dragElt = ol_ext_element.create('LI', {
className: 'ol-dragover',
html: elt.innerHTML,
style: {
position: "absolute",
"z-index": 10000,
left: elt.offsetLeft,
opacity: 0.5,
width: ol_ext_element.outerWidth(elt),
height: ol_ext_element.getStyle(elt, 'height'),
},
parent: panel
})
self.element.classList.add('drag')
self.dispatchEvent({ type: "reorder-start", layer: layer, group: group })
}
// Start a new drag sequence
if (!start) {
e.preventDefault()
e.stopPropagation()
// Ghost div
ol_ext_element.setStyle(dragElt, { top: pageY - ol_ext_element.offsetRect(panel).top + panel.scrollTop + 5 })
var li
if (!e.touches) {
li = e.target
// Get the HTML node within web component on click drag
if(e.target.shadowRoot){
li = e.composedPath()[0]
}
} else {
li = document.elementFromPoint(e.touches[0].clientX, e.touches[0].clientY);
//Get actual HTML node within web component on touch drag
while(li.shadowRoot){
li = li.shadowRoot.elementFromPoint(e.touches[0].clientX, e.touches[0].clientY)
}
}
if (li.classList.contains("ol-switcherbottomdiv")) {
self.overflow(-1)
} else if (li.classList.contains("ol-switchertopdiv")) {
self.overflow(1)
}
// Get associated li
while (li && li.tagName !== 'LI') {
li = li.parentNode
}
if (!li || !li.classList.contains('dropover')) {
elt.parentNode.querySelectorAll('li').forEach(function (li) {
li.classList.remove('dropover')
li.classList.remove('dropover-after')
li.classList.remove('dropover-before')
})
}
if (li && li.parentNode.classList.contains('drag') && li !== elt) {
target = self._getLayerForLI(li)
// Don't mix layer level
if (target && !target.get('allwaysOnTop') == !layer.get('allwaysOnTop')) {
li.classList.add("dropover")
li.classList.add((elt.offsetTop < li.offsetTop) ? 'dropover-after' : 'dropover-before')
} else {
target = false
}
ol_ext_element.show(dragElt)
} else {
target = false
if (li === elt)
ol_ext_element.hide(dragElt)
else
ol_ext_element.show(dragElt)
}
if (!target)
dragElt.classList.add("forbidden")
else
dragElt.classList.remove("forbidden")
}
}
// Start ordering
ol_ext_element.addListener(document, 'mousemove touchmove', move)
ol_ext_element.addListener(document, 'mouseup touchend touchcancel', stop)
}
/** Change opacity on drag
* @param {event} e drag event
* @private
*/
dragOpacity_(e) {
e.stopPropagation()
e.preventDefault()
var self = this
// Register start params
var elt = e.target
var layer = this._getLayerForLI(elt.parentNode.parentNode.parentNode)
if (!layer)
return
var x = e.pageX
|| (e.touches && e.touches.length && e.touches[0].pageX)
|| (e.changedTouches && e.changedTouches.length && e.changedTouches[0].pageX)
var start = ol_ext_element.getStyle(elt, 'left') - x
self.dragging_ = true
// stop dragging
function stop() {
ol_ext_element.removeListener(document, "mouseup touchend touchcancel", stop)
ol_ext_element.removeListener(document, "mousemove touchmove", move)
self.dragging_ = false
}
// On draggin
function move(e) {
var x = e.pageX
|| (e.touches && e.touches.length && e.touches[0].pageX)
|| (e.changedTouches && e.changedTouches.length && e.changedTouches[0].pageX)
var delta = (start + x) / ol_ext_element.getStyle(elt.parentNode, 'width')
var opacity = Math.max(0, Math.min(1, delta))
ol_ext_element.setStyle(elt, { left: (opacity * 100) + "%" })
elt.parentNode.nextElementSibling.innerHTML = Math.round(opacity * 100)
layer.setOpacity(opacity)
}
// Register move
ol_ext_element.addListener(document, "mouseup touchend touchcancel", stop)
ol_ext_element.addListener(document, "mousemove touchmove", move)
}
/** Render a list of layer
* @param {Elemen} element to render
* @layers {Array{ol.layer}} list of layer to show
* @api stable
* @private
*/
drawList(ul, collection) {
var self = this
var layers = collection.getArray()
// Change layer visibility
var setVisibility = function (e) {
e.stopPropagation()
e.preventDefault()
var l = self._getLayerForLI(this.parentNode.parentNode)
self.switchLayerVisibility(l, collection)
if (self.get('selection') && l.getVisible()) {
self.selectLayer(l)
}
if (self.onchangeCheck) {
self.onchangeCheck(l)
}
}
// Info button click
function onInfo(e) {
e.stopPropagation()
e.preventDefault()
var l = self._getLayerForLI(this.parentNode.parentNode)
self.oninfo(l)
self.dispatchEvent({ type: "info", layer: l })
}
// Zoom to extent button
function zoomExtent(e) {
e.stopPropagation()
e.preventDefault()
var l = self._getLayerForLI(this.parentNode.parentNode)
if (self.onextent) {
self.onextent(l)
} else {
self.getMap().getView().fit(l.getExtent(), self.getMap().getSize())
}
self.dispatchEvent({ type: "extent", layer: l })
}
// Remove a layer on trash click
function removeLayer(e) {
e.stopPropagation()
e.preventDefault()
var li = this.parentNode.parentNode.parentNode.parentNode
var layer, group = self._getLayerForLI(li)
// Remove the layer from a group or from a map
if (group) {
layer = self._getLayerForLI(this.parentNode.parentNode)
group.getLayers().remove(layer)
if (group.getLayers().getLength() == 0 && !group.get('noSwitcherDelete')) {
removeLayer.call(li.querySelectorAll('.layerTrash')[0], e)
}
} else {
li = this.parentNode.parentNode
self.getMap().removeLayer(self._getLayerForLI(li))
}
}
// Create a list for a layer
function createLi(layer) {
if (!this.displayInLayerSwitcher(layer)) {
this._setLayerForLI(null, layer)
return
}
var li = ol_ext_element.create('LI', {
className: (layer.getVisible() ? "ol-visible " : " ") + (layer.get('baseLayer') ? "baselayer" : ""),
parent: ul
})
this._setLayerForLI(li, layer)
if (this._selectedLayer === layer) {
li.classList.add('ol-layer-select')
}
var layer_buttons = ol_ext_element.create('DIV', {
className: 'ol-layerswitcher-buttons',
parent: li
})
// Content div
var d = ol_ext_element.create('DIV', {
className: 'li-content',
parent: li
})
// Visibility
var input = ol_ext_element.create('INPUT', {
type: layer.get('baseLayer') ? 'radio' : 'checkbox',
className: 'ol-visibility',
checked: layer.getVisible(),
click: function(e) {
setVisibility.bind(this)(e)
setTimeout(function() { e.target.checked = layer.getVisible(); });
},
on: {
// Set opacity on keydown
keydown: function(e) {
switch (e.key) {
// Change opacity on arrow
case 'ArrowLeft':
case 'ArrowRight': {
e.preventDefault();
e.stopPropagation();
var delta = (e.key==='ArrowLeft' ? -0.1 : 0.1);
var opacity = Math.min(1, Math.max(0, layer.getOpacity() + delta))
layer.setOpacity(opacity)
break;
}
// Select on enter
case 'Enter': {
if (self.get('selection')) {
e.preventDefault();
e.stopPropagation();
self.selectLayer(layer)
}
break;
}
// Expend
case '-':
case '+': {
if (layer.getLayers) {
this._focus = layer;
layer.set("openInLayerSwitcher", !layer.get("openInLayerSwitcher"))
}
}
// Move up dans down
// fallthrough
case 'ArrowUp':
case 'ArrowDown': {
if (e.ctrlKey && this.reordering) {
e.preventDefault();
e.stopPropagation();
var pos = collection.getArray().indexOf(layer);
if (pos > -1) {
if (e.key === 'ArrowDown') {
if (pos > 0) {
collection.remove(layer);
collection.insertAt(pos-1, layer)
self._focus = layer
self.dispatchEvent({ type: "reorder-end", layer: layer })
}
} else {
if (pos < collection.getLength()-1) {
collection.remove(layer);
collection.insertAt(pos+1, layer)
self._focus = layer
self.dispatchEvent({ type: "reorder-end", layer: layer })
}
}
}
}
break;
}
default: {
var group = this._getLayerForLI(ul.parentNode)
this.dispatchEvent({ type: 'layer:keydown', key: e.key, group: group, li: li, layer: layer, originalEvent: e })
}
}
}.bind(this)
},
parent: d
})
// Focus on element
if (layer === self._focus) {
input.focus();
self.overflow()
}
// Label
var label = ol_ext_element.create('LABEL', {
title: layer.get('title') || layer.get('name'),
click: setVisibility,
style: {
userSelect: 'none'
},
parent: d
})
label.addEventListener('selectstart', function () { return false })
ol_ext_element.create('SPAN', {
html: layer.get('title') || layer.get('name'),
click: function (e) {
if (this.get('selection')) {
e.stopPropagation()
this.selectLayer(layer)
}
}.bind(this),
parent: label
})
// up/down
if (this.reordering) {
if ((i < layers.length - 1 && (layer.get("allwaysOnTop") || !layers[i + 1].get("allwaysOnTop")))
|| (i > 0 && (!layer.get("allwaysOnTop") || layers[i - 1].get("allwaysOnTop")))) {
ol_ext_element.create('DIV', {
className: 'layerup ol-noscroll',
title: this.tip.up,
on: { 'mousedown touchstart': function (e) { self.dragOrdering_(e) } },
parent: layer_buttons
})
}
}
// Show/hide sub layers
if (layer.getLayers) {
var nb = 0
layer.getLayers().forEach(function (l) {
if (self.displayInLayerSwitcher(l))
nb++
})
if (nb) {
ol_ext_element.create('DIV', {
className: layer.get("openInLayerSwitcher") ? "collapse-layers" : "expend-layers",
title: this.tip.plus,
click: function () {
var l = self._getLayerForLI(this.parentNode.parentNode)
l.set("openInLayerSwitcher", !l.get("openInLayerSwitcher"))
},
parent: layer_buttons
})
}
}
// Info button
if (this.oninfo) {
ol_ext_element.create('DIV', {
className: 'layerInfo',
title: this.tip.info,
click: onInfo,
parent: layer_buttons
})
}
// Layer remove
if (this.hastrash && !layer.get("noSwitcherDelete")) {
ol_ext_element.create('DIV', {
className: 'layerTrash',
title: this.tip.trash,
click: removeLayer,
parent: layer_buttons
})
}
// Layer extent
if (this.hasextent && layers[i].getExtent()) {
var ex = layers[i].getExtent()
if (ex.length == 4 && ex[0] < ex[2] && ex[1] < ex[3]) {
ol_ext_element.create('DIV', {
className: 'layerExtent',
title: this.tip.extent,
click: zoomExtent,
parent: layer_buttons
})
}
}
// Progress
if (this.show_progress && layer instanceof ol_layer_Tile) {
var p = ol_ext_element.create('DIV', {
className: 'layerswitcher-progress',
parent: d
})
this.setprogress_(layer)
layer.layerswitcher_progress = ol_ext_element.create('DIV', { parent: p })
}
// Opacity
var opacity = ol_ext_element.create('DIV', {
className: 'layerswitcher-opacity',
// Click on the opacity line
click: function (e) {
if (e.target !== this)
return
e.stopPropagation()
e.preventDefault()
var op = Math.max(0, Math.min(1, e.offsetX / ol_ext_element.getStyle(this, 'width')))
self._getLayerForLI(this.parentNode.parentNode).setOpacity(op)
this.parentNode.querySelectorAll('.layerswitcher-opacity-label')[0].innerHTML = Math.round(op * 100)
},
parent: d
})
// Start dragging
ol_ext_element.create('DIV', {
className: 'layerswitcher-opacity-cursor ol-noscroll',
style: { left: (layer.getOpacity() * 100) + "%" },
on: {
'mousedown touchstart': function (e) { self.dragOpacity_(e) }
},
parent: opacity
})
// Percent
ol_ext_element.create('DIV', {
className: 'layerswitcher-opacity-label',
html: Math.round(layer.getOpacity() * 100),
parent: d
})
// Layer group
if (layer.getLayers) {
li.classList.add('ol-layer-group')
if (layer.get("openInLayerSwitcher") === true) {
var ul2 = ol_ext_element.create('UL', {
parent: li
})
this.drawList(ul2, layer.getLayers())
}
}
li.classList.add(this.getLayerClass(layer))
// Dispatch a dralist event to allow customisation
this.dispatchEvent({ type: 'drawlist', layer: layer, li: li })
}
// Add the layer list
for (var i = layers.length - 1; i >= 0; i--) {
createLi.call(this, layers[i])
}
this.viewChange()
if (ul === this.panel_) {
this.overflow('')
// Remove focus
this._focus = null;
}
}
/** Select a layer
* @param {ol.layer.Layer} layer
* @returns {string} the layer classname
* @api
*/
getLayerClass(layer) {
if (!layer)
return 'none'
if (layer.getLayers)
return 'ol-layer-group'
if (layer instanceof ol_layer_Vector)
return 'ol-layer-vector'
if (layer instanceof ol_layer_VectorTile)
return 'ol-layer-vectortile'
if (layer instanceof ol_layer_Tile)
return 'ol-layer-tile'
if (layer instanceof ol_layer_Image)
return 'ol-layer-image'
if (layer instanceof ol_layer_Heatmap)
return 'ol-layer-heatmap'
/* ol < 6 compatibility VectorImage is not defined */
// if (layer instanceof ol_layer_VectorImage) return 'ol-layer-vectorimage';
if (layer.getFeatures)
return 'ol-layer-vectorimage'
/* */
return 'unknown'
}
/** Select a layer
* @param {ol.layer.Layer} layer
* @api
*/
selectLayer(layer, silent) {
if (!layer) {
if (!this.getMap())
return
layer = this.getMap().getLayers().item(this.getMap().getLayers().getLength() - 1)
}
this._selectedLayer = layer
// Has focus ?
if (this.element.querySelector('input.ol-visibility:focus')) {
this._focus = layer;
}
// Draw
this.drawPanel()
if (!silent) {
this.dispatchEvent({ type: 'select', layer: layer })
}
}
/** Get selected layer
* @returns {ol.layer.Layer}
*/
getSelection() {
return this._selectedLayer
}
/** Handle progress bar for a layer
* @private
*/
setprogress_(layer) {
if (!layer.layerswitcher_progress) {
var loaded = 0
var loading = 0
var draw = function () {
if (loading === loaded) {
loading = loaded = 0
ol_ext_element.setStyle(layer.layerswitcher_progress, { width: 0 }) // layer.layerswitcher_progress.width(0);
} else {
ol_ext_element.setStyle(layer.layerswitcher_progress, { width: (loaded / loading * 100).toFixed(1) + '%' }) // layer.layerswitcher_progress.css('width', (loaded / loading * 100).toFixed(1) + '%');
}
}
layer.getSource().on('tileloadstart', function () {
loading++
draw()
})
layer.getSource().on('tileloadend', function () {
loaded++
draw()
})
layer.getSource().on('tileloaderror', function () {
loaded++
draw()
})
}
}
}
/** List of tips for internationalization purposes
*/
ol_control_LayerSwitcher.prototype.tip = {
up: "up/down",
down: "down",
info: "informations...",
extent: "zoom to extent",
trash: "remove layer",
plus: "expand/shrink"
};
export default ol_control_LayerSwitcher