|
/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for |
|
* full list of contributors). Published under the Clear BSD license. |
|
* See http://svn.openlayers.org/trunk/openlayers/license.txt for the |
|
* full text of the license. */ |
|
|
|
/** |
|
* @requires OpenLayers/Control.js |
|
* @requires OpenLayers/Layer/WMS/Post.js |
|
* @requires OpenLayers/Handler/RegularPolygon.js |
|
* @requires OpenLayers/Handler/Polygon.js |
|
* @requires OpenLayers/Handler/Path.js |
|
* @requires OpenLayers/Handler/Click.js |
|
* @requires OpenLayers/Filter/Spatial.js |
|
*/ |
|
|
|
/** |
|
* Class: OpenLayers.Control.SLDSelect |
|
* Perform selections on WMS layers using Styled Layer Descriptor (SLD) |
|
* |
|
* Inherits from: |
|
* - <OpenLayers.Control> |
|
*/ |
|
OpenLayers.Control.SLDSelect = OpenLayers.Class(OpenLayers.Control, { |
|
|
|
/** |
|
* Constant: EVENT_TYPES |
|
* {Array(String)} Supported application event types. Register a listener |
|
* for a particular event with the following syntax: |
|
* (code) |
|
* control.events.register(type, obj, listener); |
|
* (end) |
|
* |
|
* Listeners will be called with a reference to an event object. The |
|
* properties of this event depends on exactly what happened. |
|
* |
|
* Supported control event types (in addition to those from |
|
* <OpenLayers.Control>): |
|
* selected - Triggered when a selection occurs. Listeners receive an |
|
* event with *filters* and *layer* properties. Filters will be an |
|
* array of OpenLayers.Filter objects created in order to perform |
|
* the particular selection. |
|
*/ |
|
EVENT_TYPES: ["selected"], |
|
|
|
/** |
|
* APIProperty: clearOnDeactivate |
|
* {Boolean} Should the selection be cleared when the control is |
|
* deactivated. Default value is false. |
|
*/ |
|
clearOnDeactivate: false, |
|
|
|
/** |
|
* APIProperty: layers |
|
* {Array(<OpenLayers.Layer.WMS>)} The WMS layers this control will work |
|
* on. |
|
*/ |
|
layers: null, |
|
|
|
/** |
|
* Property: callbacks |
|
* {Object} The functions that are sent to the handler for callback |
|
*/ |
|
callbacks: null, |
|
|
|
/** |
|
* APIProperty: selectionSymbolizer |
|
* {Object} Determines the styling of the selected objects. Default is |
|
* a selection in red. |
|
*/ |
|
selectionSymbolizer: { |
|
'Polygon': {fillColor: '#FF0000', stroke: false}, |
|
'Line': {strokeColor: '#FF0000', strokeWidth: 2}, |
|
'Point': {graphicName: 'square', fillColor: '#FF0000', pointRadius: 5} |
|
}, |
|
|
|
/** |
|
* APIProperty: layerOptions |
|
* {Object} The options to apply to the selection layer, by default the |
|
* selection layer will be kept out of the layer switcher. |
|
*/ |
|
layerOptions: null, |
|
|
|
/** |
|
* APIProperty: handlerOptions |
|
* {Object} Used to set non-default properties on the control's handler |
|
*/ |
|
handlerOptions: null, |
|
|
|
/** |
|
* APIProperty: sketchStyle |
|
* {<OpenLayers.Style>|Object} Style or symbolizer to use for the sketch |
|
* handler. The recommended way of styling the sketch layer, however, is |
|
* to configure an <OpenLayers.StyleMap> in the layerOptions of the |
|
* <handlerOptions>: |
|
* |
|
* (code) |
|
* new OpenLayers.Control.SLDSelect(OpenLayers.Handler.Path, { |
|
* handlerOptions: { |
|
* layerOptions: { |
|
* styleMap: new OpenLayers.StyleMap({ |
|
* "default": {strokeColor: "yellow"} |
|
* }); |
|
* } |
|
* } |
|
* }); |
|
* (end) |
|
*/ |
|
sketchStyle: null, |
|
|
|
/** |
|
* APIProperty: wfsCache |
|
* {Object} Cache to use for storing parsed results from |
|
* <OpenLayers.Format.WFSDescribeFeatureType.read>. If not provided, |
|
* these will be cached on the prototype. |
|
*/ |
|
wfsCache: {}, |
|
|
|
/** |
|
* APIProperty: layerCache |
|
* {Object} Cache to use for storing references to the selection layers. |
|
* Normally each source layer will have exactly 1 selection layer of |
|
* type OpenLayers.Layer.WMS.Post. If not provided, layers will |
|
* be cached on the prototype. Note that if <clearOnDeactivate> is |
|
* true, the layer will no longer be cached after deactivating the |
|
* control. |
|
*/ |
|
layerCache: {}, |
|
|
|
/** |
|
* Constructor: OpenLayers.Control.SLDSelect |
|
* Create a new control for selecting features in WMS layers using |
|
* Styled Layer Descriptor (SLD). |
|
* |
|
* Parameters: |
|
* handler - {<OpenLayers.Class>} A sketch handler class. This determines |
|
* the type of selection, e.g. box (<OpenLayers.Handler.Box>), point |
|
* (<OpenLayers.Handler.Point>), path (<OpenLayers.Handler.Path>) or |
|
* polygon (<OpenLayers.Handler.Polygon>) selection. To use circle |
|
* type selection, use <OpenLayers.Handler.RegularPolygon> and pass |
|
* the number of desired sides (e.g. 40) as "sides" property to the |
|
* <handlerOptions>. |
|
* options - {Object} An object containing all configuration properties for |
|
* the control. |
|
* |
|
* Valid options: |
|
* layers - Array({<OpenLayers.Layer.WMS>}) The layers to perform the |
|
* selection on. |
|
*/ |
|
initialize: function(handler, options) { |
|
// concatenate events specific to this control with those from the base |
|
this.EVENT_TYPES = |
|
OpenLayers.Control.SLDSelect.prototype.EVENT_TYPES.concat( |
|
OpenLayers.Control.prototype.EVENT_TYPES |
|
); |
|
OpenLayers.Control.prototype.initialize.apply(this, [options]); |
|
|
|
this.callbacks = OpenLayers.Util.extend({done: this.select, |
|
click: this.select}, this.callbacks); |
|
this.handlerOptions = this.handlerOptions || {}; |
|
this.layerOptions = OpenLayers.Util.applyDefaults(this.layerOptions, { |
|
displayInLayerSwitcher: false |
|
}); |
|
if (this.sketchStyle) { |
|
this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults( |
|
this.handlerOptions.layerOptions, |
|
{styleMap: new OpenLayers.StyleMap({"default": this.sketchStyle})} |
|
); |
|
} |
|
this.handler = new handler(this, this.callbacks, this.handlerOptions); |
|
}, |
|
|
|
/** |
|
* APIMethod: destroy |
|
* Take care of things that are not handled in superclass. |
|
*/ |
|
destroy: function() { |
|
for (var key in this.layerCache) { |
|
delete this.layerCache[key]; |
|
} |
|
for (var key in this.wfsCache) { |
|
delete this.wfsCache[key]; |
|
} |
|
OpenLayers.Control.prototype.destroy.apply(this, arguments); |
|
}, |
|
|
|
/** |
|
* Method: coupleLayerVisiblity |
|
* Couple the selection layer and the source layer with respect to |
|
* layer visibility. So if the source layer is turned off, the |
|
* selection layer is also turned off. |
|
* |
|
* Parameters: |
|
* evt - {Object} |
|
*/ |
|
coupleLayerVisiblity: function(evt) { |
|
this.setVisibility(evt.object.getVisibility()); |
|
}, |
|
|
|
/** |
|
* Method: createSelectionLayer |
|
* Creates a "clone" from the source layer in which the selection can |
|
* be drawn. This ensures both the source layer and the selection are |
|
* visible and not only the selection. |
|
* |
|
* Parameters: |
|
* source - {<OpenLayers.Layer.WMS>} The source layer on which the selection |
|
* is performed. |
|
* |
|
* Returns: |
|
* {<OpenLayers.Layer.WMS.Post>} A WMS Post layer since SLD selections can |
|
* easily get quite long. |
|
*/ |
|
createSelectionLayer: function(source) { |
|
// check if we already have a selection layer for the source layer |
|
var selectionLayer; |
|
if (!this.layerCache[source.id]) { |
|
selectionLayer = new OpenLayers.Layer.WMS.Post(source.name, |
|
source.url, source.params, |
|
OpenLayers.Util.applyDefaults( |
|
this.layerOptions, |
|
source.getOptions()) |
|
); |
|
this.layerCache[source.id] = selectionLayer; |
|
// make sure the layers are coupled wrt visibility, but only |
|
// if they are not displayed in the layer switcher, because in |
|
// that case the user cannot control visibility. |
|
if (this.layerOptions.displayInLayerSwitcher === false) { |
|
source.events.on({ |
|
"visibilitychanged": this.coupleLayerVisiblity, |
|
scope: selectionLayer}); |
|
} |
|
this.map.addLayer(selectionLayer); |
|
} else { |
|
selectionLayer = this.layerCache[source.id]; |
|
} |
|
return selectionLayer; |
|
}, |
|
|
|
/** |
|
* Method: createSLD |
|
* Create the SLD document for the layer using the supplied filters. |
|
* |
|
* Parameters: |
|
* layer - {<OpenLayers.Layer.WMS>} |
|
* filters - Array({<OpenLayers.Filter>}) The filters to be applied. |
|
* geometryAttributes - Array({Object}) The geometry attributes of the |
|
* layer. |
|
* |
|
* Returns: |
|
* {String} The SLD document generated as a string. |
|
*/ |
|
createSLD: function(layer, filters, geometryAttributes) { |
|
var sld = {version: "1.0.0", namedLayers: {}}; |
|
var layerNames = [layer.params.LAYERS].join(",").split(","); |
|
for (var i=0, len=layerNames.length; i<len; i++) { |
|
var name = layerNames[i]; |
|
sld.namedLayers[name] = {name: name, userStyles: []}; |
|
var symbolizer = this.selectionSymbolizer; |
|
var geometryAttribute = geometryAttributes[i]; |
|
if (geometryAttribute.type.indexOf('Polygon') >= 0) { |
|
symbolizer = {Polygon: this.selectionSymbolizer['Polygon']}; |
|
} else if (geometryAttribute.type.indexOf('LineString') >= 0) { |
|
symbolizer = {Line: this.selectionSymbolizer['Line']}; |
|
} else if (geometryAttribute.type.indexOf('Point') >= 0) { |
|
symbolizer = {Point: this.selectionSymbolizer['Point']}; |
|
} |
|
var filter = filters[i]; |
|
sld.namedLayers[name].userStyles.push({name: 'default', rules: [ |
|
new OpenLayers.Rule({symbolizer: symbolizer, |
|
filter: filter, |
|
maxScaleDenominator: layer.options.minScale}) |
|
]}); |
|
} |
|
return new OpenLayers.Format.SLD().write(sld); |
|
}, |
|
|
|
/** |
|
* Method: parseDescribeLayer |
|
* Parse the SLD WMS DescribeLayer response and issue the corresponding |
|
* WFS DescribeFeatureType request |
|
* |
|
* request - {XMLHttpRequest} The request object. |
|
*/ |
|
parseDescribeLayer: function(request) { |
|
var format = new OpenLayers.Format.WMSDescribeLayer(); |
|
var doc = request.responseXML; |
|
if(!doc || !doc.documentElement) { |
|
doc = request.responseText; |
|
} |
|
var describeLayer = format.read(doc); |
|
var typeNames = []; |
|
var url = null; |
|
for (var i=0, len=describeLayer.length; i<len; i++) { |
|
// perform a WFS DescribeFeatureType request |
|
if (describeLayer[i].owsType == "WFS") { |
|
typeNames.push(describeLayer[i].typeName); |
|
url = describeLayer[i].owsURL; |
|
} |
|
} |
|
var options = { |
|
url: url, |
|
params: { |
|
SERVICE: "WFS", |
|
TYPENAME: typeNames.toString(), |
|
REQUEST: "DescribeFeatureType", |
|
VERSION: "1.0.0" |
|
}, |
|
callback: function(request) { |
|
var format = new OpenLayers.Format.WFSDescribeFeatureType(); |
|
var doc = request.responseXML; |
|
if(!doc || !doc.documentElement) { |
|
doc = request.responseText; |
|
} |
|
var describeFeatureType = format.read(doc); |
|
this.control.wfsCache[this.layer.id] = describeFeatureType; |
|
this.control._queue && this.control.applySelection(); |
|
}, |
|
scope: this |
|
}; |
|
OpenLayers.Request.GET(options); |
|
}, |
|
|
|
/** |
|
* Method: getGeometryAttributes |
|
* Look up the geometry attributes from the WFS DescribeFeatureType response |
|
* |
|
* Parameters: |
|
* layer - {<OpenLayers.Layer.WMS>} The layer for which to look up the |
|
* geometry attributes. |
|
* |
|
* Returns: |
|
* Array({Object}) Array of geometry attributes |
|
*/ |
|
getGeometryAttributes: function(layer) { |
|
var result = []; |
|
var cache = this.wfsCache[layer.id]; |
|
for (var i=0, len=cache.featureTypes.length; i<len; i++) { |
|
var typeName = cache.featureTypes[i]; |
|
var properties = typeName.properties; |
|
for (var j=0, lenj=properties.length; j < lenj; j++) { |
|
var property = properties[j]; |
|
var type = property.type; |
|
if ((type.indexOf('LineString') >= 0) || |
|
(type.indexOf('GeometryAssociationType') >=0) || |
|
(type.indexOf('GeometryPropertyType') >= 0) || |
|
(type.indexOf('Point') >= 0) || |
|
(type.indexOf('Polygon') >= 0) ) { |
|
result.push(property); |
|
} |
|
} |
|
} |
|
return result; |
|
}, |
|
|
|
/** |
|
* APIMethod: activate |
|
* Activate the control. Activating the control will perform a SLD WMS |
|
* DescribeLayer request followed by a WFS DescribeFeatureType request |
|
* so that the proper symbolizers can be chosen based on the geometry |
|
* type. |
|
*/ |
|
activate: function() { |
|
var activated = OpenLayers.Control.prototype.activate.call(this); |
|