More trip planner testing with colors
[busui.git] / labs / openlayers / lib / OpenLayers / Control / SLDSelect.js
blob:a/labs/openlayers/lib/OpenLayers/Control/SLDSelect.js -> blob:b/labs/openlayers/lib/OpenLayers/Control/SLDSelect.js
  /* 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);