+
+
diff --git a/lib/OpenLayers.js b/lib/OpenLayers.js
index 7f87255f4b..0ff5f9e070 100644
--- a/lib/OpenLayers.js
+++ b/lib/OpenLayers.js
@@ -163,6 +163,7 @@
"OpenLayers/Control/SelectFeature.js",
"OpenLayers/Control/NavigationHistory.js",
"OpenLayers/Control/Measure.js",
+ "OpenLayers/Control/WMSGetFeatureInfo.js",
"OpenLayers/Geometry.js",
"OpenLayers/Geometry/Rectangle.js",
"OpenLayers/Geometry/Collection.js",
diff --git a/lib/OpenLayers/Control/WMSGetFeatureInfo.js b/lib/OpenLayers/Control/WMSGetFeatureInfo.js
new file mode 100644
index 0000000000..a35940af3b
--- /dev/null
+++ b/lib/OpenLayers/Control/WMSGetFeatureInfo.js
@@ -0,0 +1,363 @@
+/* Copyright (c) 2006-2008 MetaCarta, Inc., 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/Handler/Click.js
+ * @requires OpenLayers/Handler/Hover.js
+ * @requires OpenLayers/Request.js
+ */
+
+/**
+ * Class: OpenLayers.Control.WMSGetFeatureInfo
+ * The WMSGetFeatureInfo control uses a WMS query to get information about a point on the map. The
+ * information may be in a display-friendly format such as HTML, or a machine-friendly format such
+ * as GML, depending on the server's capabilities and the client's configuration. This control
+ * handles click or hover events, attempts to parse the results using an OpenLayers.Format, and
+ * fires a 'getfeatureinfo' event with the click position, the raw body of the response, and an
+ * array of features if it successfully read the response.
+ *
+ * Inherits from:
+ * -
+ */
+OpenLayers.Control.WMSGetFeatureInfo = OpenLayers.Class(OpenLayers.Control, {
+
+ /**
+ * APIProperty: hover
+ * {Boolean} Send WMS request on mouse moves. This will cause the
+ * "hoverfeature" and "outfeature" events to be triggered.
+ * Default is false.
+ */
+ hover: false,
+
+ /**
+ * APIProperty: maxFeatures
+ * {Integer} maximum number of features to return from a WMS query. This
+ * has the same effect as the feature_count parameter on WMS GetFeatureInfo
+ * requests. Will be ignored for box selections.
+ */
+ maxFeatures: 10,
+
+ /**
+ * Property: layers
+ * {} The Layer objects for which to find
+ * feature info. If omitted, search all WMS layers from the map whose url
+ * is the same as the one in this control's configuration. See the
+ * property
+ */
+ layers: null,
+
+ /**
+ * Property: queryVisible
+ * {Boolean} If true, filter out hidden layers when searching the map for
+ * layers to query. If an explicit layers parameter is set, then this does
+ * nothing.
+ */
+ queryVisible: false,
+
+ /**
+ * Property: url
+ * {String} The URL of the WMS service to use.
+ */
+ url: null,
+
+ /**
+ * Property: infoFormat
+ * {String} The mimetype to request from the server
+ */
+ infoFormat: 'text/html',
+
+ /**
+ * Property: vendorParams
+ * {Object} Additional parameters that will be added to the request, for
+ * WMS implementations that support them. This could e.g. look like
+ * (start code)
+ * {
+ * radius: 5
+ * }
+ * (end)
+ */
+
+ /**
+ * Property: formats
+ * An object mapping from mime-types to OL Format objects to use when
+ * parsing GFI responses. The default mapping is
+ * (start code)
+ * {
+ * 'application/vnd.ogc.gml': new OpenLayers.Format.WMSGetFeatureInfo()
+ * }
+ * (end)
+ * An object provided here will extend the default mapping.
+ */
+ formats: null,
+
+ /**
+ * APIProperty: handlerOptions
+ * {Object} Additional options for the handlers used by this control, e.g.
+ * (start code)
+ * {
+ * "click": {delay: 100},
+ * "hover": {delay: 300}
+ * }
+ * (end)
+ */
+ handlerOptions: null,
+
+ /**
+ * Property: handler
+ * {Object} Reference to the for this control
+ */
+ handler: null,
+
+ /**
+ * Property: hoverRequest
+ * {} contains the currently running hover request
+ * (if any).
+ */
+ hoverRequest: null,
+
+ /**
+ * Constant: EVENT_TYPES
+ *
+ * Supported event types:
+ * - *getfeatureinfo* Triggered when a GetFeatureInfo response is received.
+ * The event object has a text property with:
+ * text: a string containing the body of the response,
+ * features: an array of the parsed features, if parsing succeeded.
+ * xy: the position of the mouse click or hover event that
+ * triggered the request
+ */
+ EVENT_TYPES: ["getfeatureinfo"],
+
+ /**
+ * Constructor:
+ *
+ * Parameters:
+ * url - {String}
+ * options - {Object}
+ */
+ initialize: function(url, options) {
+ // concatenate events specific to vector with those from the base
+ this.EVENT_TYPES =
+ OpenLayers.Control.WMSGetFeatureInfo.prototype.EVENT_TYPES.concat(
+ OpenLayers.Control.prototype.EVENT_TYPES
+ );
+
+ options = options || {};
+ options.handlerOptions = options.handlerOptions || {};
+
+ OpenLayers.Control.prototype.initialize.apply(this, [options]);
+ this.url = url;
+ this.layers = options.layers;
+
+ this.formats = OpenLayers.Util.extend({
+ 'application/vnd.ogc.gml': new OpenLayers.Format.WMSGetFeatureInfo()
+ }, options.formats);
+
+
+ if (this.hover) {
+ this.handler = new OpenLayers.Handler.Hover(
+ this, {
+ 'move': this.cancelHover,
+ 'pause': this.getInfoForHover
+ },
+ OpenLayers.Util.extend(this.handlerOptions.hover || {}, {
+ 'delay': 250
+ }));
+ } else {
+ this.handler = new OpenLayers.Handler.Click(this,
+ {click: this.getInfoForClick}, this.handlerOptions.click || {});
+ }
+ },
+
+ /**
+ * Method: activate
+ * Activates the control.
+ *
+ * Returns:
+ * {Boolean} The control was effectively activated.
+ */
+ activate: function () {
+ if (!this.active) {
+ this.handler.activate();
+ }
+ return OpenLayers.Control.prototype.activate.apply(
+ this, arguments
+ );
+ },
+
+ /**
+ * Method: deactivate
+ * Deactivates the control.
+ *
+ * Returns:
+ * {Boolean} The control was effectively deactivated.
+ */
+ deactivate: function () {
+ return OpenLayers.Control.prototype.deactivate.apply(
+ this, arguments
+ );
+ },
+
+ /**
+ * Method: getInfoForClick
+ * Called on click
+ *
+ * Parameters:
+ * evt - {}
+ */
+ getInfoForClick: function(evt) {
+ // Set the cursor to "wait" to tell the user we're working on their
+ // click.
+ OpenLayers.Element.addClass(this.map.div, "olCursorWait");
+ this.request(evt.xy, {});
+ },
+
+ /**
+ * Method: getInfoForHover
+ * Pause callback for the hover handler
+ *
+ * Parameters:
+ * evt - {Object}
+ */
+ getInfoForHover: function(evt) {
+ this.request(evt.xy, {hover: true});
+ },
+
+ /**
+ * Method: cancelHover
+ * Cancel callback for the hover handler
+ */
+ cancelHover: function() {
+ if (this.hoverRequest) {
+ this.hoverRequest.abort();
+ this.hoverRequest = null;
+ }
+ },
+
+ /**
+ * Method: findLayers
+ * Internal method to get the layers, independent of whether we are
+ * inspecting the map or using a client-provided array
+ */
+ findLayers: function() {
+ if (this.layers) return this.layers;
+
+ var layers = [];
+
+ for (var i = 0, len = this.map.layers.length; i < len; i++) {
+ var mapLayer = this.map.layers[i];
+ if (mapLayer instanceof OpenLayers.Layer.WMS
+ && mapLayer.url === this.url
+ && (!this.queryVisible || mapLayer.getVisibility())) {
+
+ layers.push(mapLayer);
+ }
+ }
+
+ return layers;
+ },
+
+ /**
+ * Method: request
+ * Sends a GetFeatureInfo request to the WMS
+ *
+ * Parameters:
+ * clickPosition - The position on the map where the mouse event occurred
+ * options - {Object} additional options for this method.
+ *
+ * Valid options:
+ * - *hover* {Boolean} true if we do the request for the hover handler
+ */
+ request: function(clickPosition, options) {
+ options = options || {};
+ var layerNames = [];
+ var styleNames = [];
+
+ var layers = this.findLayers();
+
+ for (var i = 0, len = layers.length; i < len; i++) {
+ layerNames = layerNames.concat(layers[i].params.LAYERS);
+ // in the event of a WMS layer bundling multiple layers but not
+ // specifying styles,we need the same number of commas to specify
+ // the default style for each of the layers. We can't just leave it
+ // blank as we may be including other layers that do specify styles.
+ styleNames = styleNames.concat(layers[i].params.STYLES ||
+ layers[i].params.LAYERS.replace(/[^,]/g,""));
+ }
+
+ var wmsOptions = {
+ url: this.url,
+ params: OpenLayers.Util.applyDefaults({
+ service: "WMS",
+ version: "1.1.0",
+ request: 'getfeatureinfo',
+ layers: layerNames,
+ query_layers: layerNames,
+ styles: styleNames,
+ bbox: this.map.getExtent().toBBOX(),
+ srs: this.map.getProjection(),
+ feature_count: this.maxFeatures,
+ x: clickPosition.x,
+ y: clickPosition.y,
+ height: this.map.getSize().h,
+ width: this.map.getSize().w,
+ info_format: this.infoFormat
+ }, this.vendorParams),
+ callback: this.handleResponse,
+ scope: OpenLayers.Util.extend({
+ xy: clickPosition
+ }, this)
+ };
+
+ var response = OpenLayers.Request.GET(wmsOptions);
+
+ if (options.hover === true) {
+ this.hoverRequest = response.priv;
+ }
+ },
+
+ /**
+ * Method: handleResponse
+ * Handler for the GetFeatureInfo response. Will be called in an extended
+ * scope of this instance plus an xy property with the click position.
+ *
+ * Parameters:
+ * response - {Object}
+ */
+ handleResponse: function(response) {
+ var features;
+ var fmt = this.formats[
+ response.getResponseHeader("Content-type").split(";")[0]
+ ];
+ if (fmt) {
+ features = fmt.read(response.responseXml ||
+ response.responseText);
+ }
+
+ this.events.triggerEvent("getfeatureinfo", {
+ text: response.responseText,
+ features: features,
+ xy: this.xy
+ });
+
+ // Reset the cursor.
+ OpenLayers.Element.removeClass(this.map.div, "olCursorWait");
+ },
+
+ /**
+ * Method: setMap
+ * Set the map property for the control.
+ *
+ * Parameters:
+ * map - {}
+ */
+ setMap: function(map) {
+ this.handler.setMap(map);
+ OpenLayers.Control.prototype.setMap.apply(this, arguments);
+ },
+
+ CLASS_NAME: "OpenLayers.Control.WMSGetFeatureInfo"
+});
diff --git a/tests/Control/WMSGetFeatureInfo.html b/tests/Control/WMSGetFeatureInfo.html
new file mode 100644
index 0000000000..1fe5d359b3
--- /dev/null
+++ b/tests/Control/WMSGetFeatureInfo.html
@@ -0,0 +1,293 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/list-tests.html b/tests/list-tests.html
index 8693e0f0c3..eb5fa80ce6 100644
--- a/tests/list-tests.html
+++ b/tests/list-tests.html
@@ -33,6 +33,7 @@