/* 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.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 * ): * 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()} 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 * {|Object} Style or symbolizer to use for the sketch * handler. The recommended way of styling the sketch layer, however, is * to configure an in the layerOptions of the * : * * (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 * . 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 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 - {} A sketch handler class. This determines * the type of selection, e.g. box (), point * (), path () or * polygon () selection. To use circle * type selection, use and pass * the number of desired sides (e.g. 40) as "sides" property to the * . * options - {Object} An object containing all configuration properties for * the control. * * Valid options: * layers - Array({}) 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 - {} The source layer on which the selection * is performed. * * Returns: * {} 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 - {} * filters - Array({}) 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= 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} 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= 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); if(activated) { for (var i=0, len=this.layers.length; i)} The new set of layers on which * the selection should be performed. */ setLayers: function(layers) { if(this.active) { this.deactivate(); this.layers = layers; this.activate(); } else { this.layers = layers; } }, /** * Function: createFilter * Create the filter to be used in the SLD. * * Parameters: * geometryAttribute - {Object} Used to get the name of the geometry * attribute which is needed for constructing the spatial filter. * geometry - {} The geometry to use. * * Returns: * {} The spatial filter created. */ createFilter: function(geometryAttribute, geometry) { var filter = null; if (this.handler instanceof OpenLayers.Handler.RegularPolygon) { // box if (this.handler.irregular === true) { filter = new OpenLayers.Filter.Spatial({ type: OpenLayers.Filter.Spatial.BBOX, property: geometryAttribute.name, value: geometry.getBounds()} ); } else { filter = new OpenLayers.Filter.Spatial({ type: OpenLayers.Filter.Spatial.INTERSECTS, property: geometryAttribute.name, value: geometry} ); } } else if (this.handler instanceof OpenLayers.Handler.Polygon) { filter = new OpenLayers.Filter.Spatial({ type: OpenLayers.Filter.Spatial.INTERSECTS, property: geometryAttribute.name, value: geometry} ); } else if (this.handler instanceof OpenLayers.Handler.Path) { // if source layer is point based, use DWITHIN instead if (geometryAttribute.type.indexOf('Point') >= 0) { filter = new OpenLayers.Filter.Spatial({ type: OpenLayers.Filter.Spatial.DWITHIN, property: geometryAttribute.name, distance: this.map.getExtent().getWidth()*0.01 , distanceUnits: this.map.getUnits(), value: geometry} ); } else { filter = new OpenLayers.Filter.Spatial({ type: OpenLayers.Filter.Spatial.INTERSECTS, property: geometryAttribute.name, value: geometry} ); } } else if (this.handler instanceof OpenLayers.Handler.Click) { if (geometryAttribute.type.indexOf('Polygon') >= 0) { filter = new OpenLayers.Filter.Spatial({ type: OpenLayers.Filter.Spatial.INTERSECTS, property: geometryAttribute.name, value: geometry} ); } else { filter = new OpenLayers.Filter.Spatial({ type: OpenLayers.Filter.Spatial.DWITHIN, property: geometryAttribute.name, distance: this.map.getExtent().getWidth()*0.01 , distanceUnits: this.map.getUnits(), value: geometry} ); } } return filter; }, /** * Method: select * When the handler is done, use SLD_BODY on the selection layer to * display the selection in the map. * * Parameters: * geometry - {Object} or {} */ select: function(geometry) { this._queue = function() { for (var i=0, len=this.layers.length; i