522 lines
16 KiB
JavaScript
522 lines
16 KiB
JavaScript
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
|
|
* full list of contributors). Published under the 2-clause BSD license.
|
|
* See license.txt in the OpenLayers distribution or repository for the
|
|
* full text of the license. */
|
|
|
|
/**
|
|
* @requires OpenLayers/Control.js
|
|
* @requires OpenLayers/Lang.js
|
|
* @requires OpenLayers/Util.js
|
|
* @requires OpenLayers/Events/buttonclick.js
|
|
*/
|
|
|
|
/**
|
|
* Class: OpenLayers.Control.LayerSwitcher
|
|
* The LayerSwitcher control displays a table of contents for the map. This
|
|
* allows the user interface to switch between BaseLasyers and to show or hide
|
|
* Overlays. By default the switcher is shown minimized on the right edge of
|
|
* the map, the user may expand it by clicking on the handle.
|
|
*
|
|
* To create the LayerSwitcher outside of the map, pass the Id of a html div
|
|
* as the first argument to the constructor.
|
|
*
|
|
* Inherits from:
|
|
* - <OpenLayers.Control>
|
|
*/
|
|
OpenLayers.Control.LayerSwitcher = OpenLayers.Class(OpenLayers.Control, {
|
|
|
|
/**
|
|
* Property: layerStates
|
|
* {Array(Object)} Basically a copy of the "state" of the map's layers
|
|
* the last time the control was drawn. We have this in order to avoid
|
|
* unnecessarily redrawing the control.
|
|
*/
|
|
layerStates: null,
|
|
|
|
// DOM Elements
|
|
|
|
/**
|
|
* Property: layersDiv
|
|
* {DOMElement}
|
|
*/
|
|
layersDiv: null,
|
|
|
|
/**
|
|
* Property: baseLayersDiv
|
|
* {DOMElement}
|
|
*/
|
|
baseLayersDiv: null,
|
|
|
|
/**
|
|
* Property: baseLayers
|
|
* {Array(Object)}
|
|
*/
|
|
baseLayers: null,
|
|
|
|
|
|
/**
|
|
* Property: dataLbl
|
|
* {DOMElement}
|
|
*/
|
|
dataLbl: null,
|
|
|
|
/**
|
|
* Property: dataLayersDiv
|
|
* {DOMElement}
|
|
*/
|
|
dataLayersDiv: null,
|
|
|
|
/**
|
|
* Property: dataLayers
|
|
* {Array(Object)}
|
|
*/
|
|
dataLayers: null,
|
|
|
|
|
|
/**
|
|
* Property: minimizeDiv
|
|
* {DOMElement}
|
|
*/
|
|
minimizeDiv: null,
|
|
|
|
/**
|
|
* Property: maximizeDiv
|
|
* {DOMElement}
|
|
*/
|
|
maximizeDiv: null,
|
|
|
|
/**
|
|
* APIProperty: ascending
|
|
* {Boolean}
|
|
*/
|
|
ascending: true,
|
|
|
|
/**
|
|
* Constructor: OpenLayers.Control.LayerSwitcher
|
|
*
|
|
* Parameters:
|
|
* options - {Object}
|
|
*/
|
|
initialize: function(options) {
|
|
OpenLayers.Control.prototype.initialize.apply(this, arguments);
|
|
this.layerStates = [];
|
|
},
|
|
|
|
/**
|
|
* APIMethod: destroy
|
|
*/
|
|
destroy: function() {
|
|
|
|
//clear out layers info and unregister their events
|
|
this.clearLayersArray("base");
|
|
this.clearLayersArray("data");
|
|
|
|
this.map.events.un({
|
|
buttonclick: this.onButtonClick,
|
|
addlayer: this.redraw,
|
|
changelayer: this.redraw,
|
|
removelayer: this.redraw,
|
|
changebaselayer: this.redraw,
|
|
scope: this
|
|
});
|
|
this.events.unregister("buttonclick", this, this.onButtonClick);
|
|
|
|
OpenLayers.Control.prototype.destroy.apply(this, arguments);
|
|
},
|
|
|
|
/**
|
|
* Method: setMap
|
|
*
|
|
* Properties:
|
|
* map - {<OpenLayers.Map>}
|
|
*/
|
|
setMap: function(map) {
|
|
OpenLayers.Control.prototype.setMap.apply(this, arguments);
|
|
|
|
this.map.events.on({
|
|
addlayer: this.redraw,
|
|
changelayer: this.redraw,
|
|
removelayer: this.redraw,
|
|
changebaselayer: this.redraw,
|
|
scope: this
|
|
});
|
|
if (this.outsideViewport) {
|
|
this.events.attachToElement(this.div);
|
|
this.events.register("buttonclick", this, this.onButtonClick);
|
|
} else {
|
|
this.map.events.register("buttonclick", this, this.onButtonClick);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Method: draw
|
|
*
|
|
* Returns:
|
|
* {DOMElement} A reference to the DIV DOMElement containing the
|
|
* switcher tabs.
|
|
*/
|
|
draw: function() {
|
|
OpenLayers.Control.prototype.draw.apply(this);
|
|
|
|
// create layout divs
|
|
this.loadContents();
|
|
|
|
// set mode to minimize
|
|
if(!this.outsideViewport) {
|
|
this.minimizeControl();
|
|
}
|
|
|
|
// populate div with current info
|
|
this.redraw();
|
|
|
|
return this.div;
|
|
},
|
|
|
|
/**
|
|
* Method: onButtonClick
|
|
*
|
|
* Parameters:
|
|
* evt - {Event}
|
|
*/
|
|
onButtonClick: function(evt) {
|
|
var button = evt.buttonElement;
|
|
if (button === this.minimizeDiv) {
|
|
this.minimizeControl();
|
|
} else if (button === this.maximizeDiv) {
|
|
this.maximizeControl();
|
|
} else if (button._layerSwitcher === this.id) {
|
|
if (button["for"]) {
|
|
button = document.getElementById(button["for"]);
|
|
}
|
|
if (!button.disabled) {
|
|
if (button.type == "radio") {
|
|
button.checked = true;
|
|
this.map.setBaseLayer(this.map.getLayer(button._layer));
|
|
} else {
|
|
button.checked = !button.checked;
|
|
this.updateMap();
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Method: clearLayersArray
|
|
* User specifies either "base" or "data". we then clear all the
|
|
* corresponding listeners, the div, and reinitialize a new array.
|
|
*
|
|
* Parameters:
|
|
* layersType - {String}
|
|
*/
|
|
clearLayersArray: function(layersType) {
|
|
this[layersType + "LayersDiv"].innerHTML = "";
|
|
this[layersType + "Layers"] = [];
|
|
},
|
|
|
|
|
|
/**
|
|
* Method: checkRedraw
|
|
* Checks if the layer state has changed since the last redraw() call.
|
|
*
|
|
* Returns:
|
|
* {Boolean} The layer state changed since the last redraw() call.
|
|
*/
|
|
checkRedraw: function() {
|
|
if ( !this.layerStates.length ||
|
|
(this.map.layers.length != this.layerStates.length) ) {
|
|
return true;
|
|
}
|
|
|
|
for (var i = 0, len = this.layerStates.length; i < len; i++) {
|
|
var layerState = this.layerStates[i];
|
|
var layer = this.map.layers[i];
|
|
if ( (layerState.name != layer.name) ||
|
|
(layerState.inRange != layer.inRange) ||
|
|
(layerState.id != layer.id) ||
|
|
(layerState.visibility != layer.visibility) ) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
},
|
|
|
|
/**
|
|
* Method: redraw
|
|
* Goes through and takes the current state of the Map and rebuilds the
|
|
* control to display that state. Groups base layers into a
|
|
* radio-button group and lists each data layer with a checkbox.
|
|
*
|
|
* Returns:
|
|
* {DOMElement} A reference to the DIV DOMElement containing the control
|
|
*/
|
|
redraw: function() {
|
|
//if the state hasn't changed since last redraw, no need
|
|
// to do anything. Just return the existing div.
|
|
if (!this.checkRedraw()) {
|
|
return this.div;
|
|
}
|
|
|
|
//clear out previous layers
|
|
this.clearLayersArray("base");
|
|
this.clearLayersArray("data");
|
|
|
|
var containsOverlays = false;
|
|
var containsBaseLayers = false;
|
|
|
|
// Save state -- for checking layer if the map state changed.
|
|
// We save this before redrawing, because in the process of redrawing
|
|
// we will trigger more visibility changes, and we want to not redraw
|
|
// and enter an infinite loop.
|
|
var len = this.map.layers.length;
|
|
this.layerStates = new Array(len);
|
|
for (var i=0; i <len; i++) {
|
|
var layer = this.map.layers[i];
|
|
this.layerStates[i] = {
|
|
'name': layer.name,
|
|
'visibility': layer.visibility,
|
|
'inRange': layer.inRange,
|
|
'id': layer.id
|
|
};
|
|
}
|
|
|
|
var layers = this.map.layers.slice();
|
|
if (!this.ascending) { layers.reverse(); }
|
|
for(var i=0, len=layers.length; i<len; i++) {
|
|
var layer = layers[i];
|
|
var baseLayer = layer.isBaseLayer;
|
|
|
|
if (layer.displayInLayerSwitcher) {
|
|
|
|
if (baseLayer) {
|
|
containsBaseLayers = true;
|
|
} else {
|
|
containsOverlays = true;
|
|
}
|
|
|
|
// only check a baselayer if it is *the* baselayer, check data
|
|
// layers if they are visible
|
|
var checked = (baseLayer) ? (layer == this.map.baseLayer)
|
|
: layer.getVisibility();
|
|
|
|
// create input element
|
|
var inputElem = document.createElement("input"),
|
|
// The input shall have an id attribute so we can use
|
|
// labels to interact with them.
|
|
inputId = OpenLayers.Util.createUniqueID(
|
|
this.id + "_input_"
|
|
);
|
|
|
|
inputElem.id = inputId;
|
|
inputElem.name = (baseLayer) ? this.id + "_baseLayers" : layer.name;
|
|
inputElem.type = (baseLayer) ? "radio" : "checkbox";
|
|
inputElem.value = layer.name;
|
|
inputElem.checked = checked;
|
|
inputElem.defaultChecked = checked;
|
|
inputElem.className = "olButton";
|
|
inputElem._layer = layer.id;
|
|
inputElem._layerSwitcher = this.id;
|
|
|
|
if (!baseLayer && !layer.inRange) {
|
|
inputElem.disabled = true;
|
|
}
|
|
|
|
// create span
|
|
var labelSpan = document.createElement("label");
|
|
// this isn't the DOM attribute 'for', but an arbitrary name we
|
|
// use to find the appropriate input element in <onButtonClick>
|
|
labelSpan["for"] = inputElem.id;
|
|
OpenLayers.Element.addClass(labelSpan, "labelSpan olButton");
|
|
labelSpan._layer = layer.id;
|
|
labelSpan._layerSwitcher = this.id;
|
|
if (!baseLayer && !layer.inRange) {
|
|
labelSpan.style.color = "gray";
|
|
}
|
|
labelSpan.innerHTML = layer.name;
|
|
labelSpan.style.verticalAlign = (baseLayer) ? "bottom"
|
|
: "baseline";
|
|
// create line break
|
|
var br = document.createElement("br");
|
|
|
|
|
|
var groupArray = (baseLayer) ? this.baseLayers
|
|
: this.dataLayers;
|
|
groupArray.push({
|
|
'layer': layer,
|
|
'inputElem': inputElem,
|
|
'labelSpan': labelSpan
|
|
});
|
|
|
|
|
|
var groupDiv = (baseLayer) ? this.baseLayersDiv
|
|
: this.dataLayersDiv;
|
|
groupDiv.appendChild(inputElem);
|
|
groupDiv.appendChild(labelSpan);
|
|
groupDiv.appendChild(br);
|
|
}
|
|
}
|
|
|
|
// if no overlays, dont display the overlay label
|
|
this.dataLbl.style.display = (containsOverlays) ? "" : "none";
|
|
|
|
// if no baselayers, dont display the baselayer label
|
|
this.baseLbl.style.display = (containsBaseLayers) ? "" : "none";
|
|
|
|
return this.div;
|
|
},
|
|
|
|
/**
|
|
* Method: updateMap
|
|
* Cycles through the loaded data and base layer input arrays and makes
|
|
* the necessary calls to the Map object such that that the map's
|
|
* visual state corresponds to what the user has selected in
|
|
* the control.
|
|
*/
|
|
updateMap: function() {
|
|
|
|
// set the newly selected base layer
|
|
for(var i=0, len=this.baseLayers.length; i<len; i++) {
|
|
var layerEntry = this.baseLayers[i];
|
|
if (layerEntry.inputElem.checked) {
|
|
this.map.setBaseLayer(layerEntry.layer, false);
|
|
}
|
|
}
|
|
|
|
// set the correct visibilities for the overlays
|
|
for(var i=0, len=this.dataLayers.length; i<len; i++) {
|
|
var layerEntry = this.dataLayers[i];
|
|
layerEntry.layer.setVisibility(layerEntry.inputElem.checked);
|
|
}
|
|
|
|
},
|
|
|
|
/**
|
|
* Method: maximizeControl
|
|
* Set up the labels and divs for the control
|
|
*
|
|
* Parameters:
|
|
* e - {Event}
|
|
*/
|
|
maximizeControl: function(e) {
|
|
|
|
// set the div's width and height to empty values, so
|
|
// the div dimensions can be controlled by CSS
|
|
this.div.style.width = "";
|
|
this.div.style.height = "";
|
|
|
|
this.showControls(false);
|
|
|
|
if (e != null) {
|
|
OpenLayers.Event.stop(e);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Method: minimizeControl
|
|
* Hide all the contents of the control, shrink the size,
|
|
* add the maximize icon
|
|
*
|
|
* Parameters:
|
|
* e - {Event}
|
|
*/
|
|
minimizeControl: function(e) {
|
|
|
|
// to minimize the control we set its div's width
|
|
// and height to 0px, we cannot just set "display"
|
|
// to "none" because it would hide the maximize
|
|
// div
|
|
this.div.style.width = "0px";
|
|
this.div.style.height = "0px";
|
|
|
|
this.showControls(true);
|
|
|
|
if (e != null) {
|
|
OpenLayers.Event.stop(e);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Method: showControls
|
|
* Hide/Show all LayerSwitcher controls depending on whether we are
|
|
* minimized or not
|
|
*
|
|
* Parameters:
|
|
* minimize - {Boolean}
|
|
*/
|
|
showControls: function(minimize) {
|
|
|
|
this.maximizeDiv.style.display = minimize ? "" : "none";
|
|
this.minimizeDiv.style.display = minimize ? "none" : "";
|
|
|
|
this.layersDiv.style.display = minimize ? "none" : "";
|
|
},
|
|
|
|
/**
|
|
* Method: loadContents
|
|
* Set up the labels and divs for the control
|
|
*/
|
|
loadContents: function() {
|
|
|
|
// layers list div
|
|
this.layersDiv = document.createElement("div");
|
|
this.layersDiv.id = this.id + "_layersDiv";
|
|
OpenLayers.Element.addClass(this.layersDiv, "layersDiv");
|
|
|
|
this.baseLbl = document.createElement("div");
|
|
this.baseLbl.innerHTML = OpenLayers.i18n("Base Layer");
|
|
OpenLayers.Element.addClass(this.baseLbl, "baseLbl");
|
|
|
|
this.baseLayersDiv = document.createElement("div");
|
|
OpenLayers.Element.addClass(this.baseLayersDiv, "baseLayersDiv");
|
|
|
|
this.dataLbl = document.createElement("div");
|
|
this.dataLbl.innerHTML = OpenLayers.i18n("Overlays");
|
|
OpenLayers.Element.addClass(this.dataLbl, "dataLbl");
|
|
|
|
this.dataLayersDiv = document.createElement("div");
|
|
OpenLayers.Element.addClass(this.dataLayersDiv, "dataLayersDiv");
|
|
|
|
if (this.ascending) {
|
|
this.layersDiv.appendChild(this.baseLbl);
|
|
this.layersDiv.appendChild(this.baseLayersDiv);
|
|
this.layersDiv.appendChild(this.dataLbl);
|
|
this.layersDiv.appendChild(this.dataLayersDiv);
|
|
} else {
|
|
this.layersDiv.appendChild(this.dataLbl);
|
|
this.layersDiv.appendChild(this.dataLayersDiv);
|
|
this.layersDiv.appendChild(this.baseLbl);
|
|
this.layersDiv.appendChild(this.baseLayersDiv);
|
|
}
|
|
|
|
this.div.appendChild(this.layersDiv);
|
|
|
|
// maximize button div
|
|
var img = OpenLayers.Util.getImageLocation('layer-switcher-maximize.png');
|
|
this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv(
|
|
"OpenLayers_Control_MaximizeDiv",
|
|
null,
|
|
null,
|
|
img,
|
|
"absolute");
|
|
OpenLayers.Element.addClass(this.maximizeDiv, "maximizeDiv olButton");
|
|
this.maximizeDiv.style.display = "none";
|
|
|
|
this.div.appendChild(this.maximizeDiv);
|
|
|
|
// minimize button div
|
|
var img = OpenLayers.Util.getImageLocation('layer-switcher-minimize.png');
|
|
this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv(
|
|
"OpenLayers_Control_MinimizeDiv",
|
|
null,
|
|
null,
|
|
img,
|
|
"absolute");
|
|
OpenLayers.Element.addClass(this.minimizeDiv, "minimizeDiv olButton");
|
|
this.minimizeDiv.style.display = "none";
|
|
|
|
this.div.appendChild(this.minimizeDiv);
|
|
},
|
|
|
|
CLASS_NAME: "OpenLayers.Control.LayerSwitcher"
|
|
});
|