1129 lines
36 KiB
JavaScript
1129 lines
36 KiB
JavaScript
/* 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 {boolean} options.counter layer counter, 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')
|
|
if (options.counter) element.classList.add('ol-counter')
|
|
|
|
this.counter = ol_ext_element.create('SPAN', {
|
|
class: 'ol-counter',
|
|
text: 0,
|
|
parent: element
|
|
});
|
|
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
|
|
// Counter
|
|
this.counter.innerHTML = this.panel_.parentNode.querySelectorAll('ul.panel > li:not(.ol-header)').length;
|
|
}
|
|
/** Change layer visibility according to the baselayer option
|
|
* @param {ol.layer}
|
|
* @param {Array<ol.layer>} 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
|