summaryrefslogtreecommitdiff
path: root/misc/openlayers/lib/OpenLayers/Control/WMSGetFeatureInfo.js
diff options
context:
space:
mode:
Diffstat (limited to 'misc/openlayers/lib/OpenLayers/Control/WMSGetFeatureInfo.js')
-rw-r--r--misc/openlayers/lib/OpenLayers/Control/WMSGetFeatureInfo.js532
1 files changed, 532 insertions, 0 deletions
diff --git a/misc/openlayers/lib/OpenLayers/Control/WMSGetFeatureInfo.js b/misc/openlayers/lib/OpenLayers/Control/WMSGetFeatureInfo.js
new file mode 100644
index 0000000..c9242f6
--- /dev/null
+++ b/misc/openlayers/lib/OpenLayers/Control/WMSGetFeatureInfo.js
@@ -0,0 +1,532 @@
+/* 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/Handler/Click.js
+ * @requires OpenLayers/Handler/Hover.js
+ * @requires OpenLayers/Request.js
+ * @requires OpenLayers/Format/WMSGetFeatureInfo.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>
+ */
+OpenLayers.Control.WMSGetFeatureInfo = OpenLayers.Class(OpenLayers.Control, {
+
+ /**
+ * APIProperty: hover
+ * {Boolean} Send GetFeatureInfo requests when mouse stops moving.
+ * Default is false.
+ */
+ hover: false,
+
+ /**
+ * APIProperty: drillDown
+ * {Boolean} Drill down over all WMS layers in the map. When
+ * using drillDown mode, hover is not possible, and an infoFormat that
+ * returns parseable features is required. Default is false.
+ */
+ drillDown: false,
+
+ /**
+ * APIProperty: maxFeatures
+ * {Integer} Maximum number of features to return from a WMS query. This
+ * sets the feature_count parameter on WMS GetFeatureInfo
+ * requests.
+ */
+ maxFeatures: 10,
+
+ /**
+ * APIProperty: clickCallback
+ * {String} The click callback to register in the
+ * {<OpenLayers.Handler.Click>} object created when the hover
+ * option is set to false. Default is "click".
+ */
+ clickCallback: "click",
+
+ /**
+ * APIProperty: output
+ * {String} Either "features" or "object". When triggering a getfeatureinfo
+ * request should we pass on an array of features or an object with with
+ * a "features" property and other properties (such as the url of the
+ * WMS). Default is "features".
+ */
+ output: "features",
+
+ /**
+ * APIProperty: layers
+ * {Array(<OpenLayers.Layer.WMS>)} The layers to query for feature info.
+ * If omitted, all map WMS layers with a url that matches this <url> or
+ * <layerUrls> will be considered.
+ */
+ layers: null,
+
+ /**
+ * APIProperty: queryVisible
+ * {Boolean} If true, filter out hidden layers when searching the map for
+ * layers to query. Default is false.
+ */
+ queryVisible: false,
+
+ /**
+ * APIProperty: url
+ * {String} The URL of the WMS service to use. If not provided, the url
+ * of the first eligible layer will be used.
+ */
+ url: null,
+
+ /**
+ * APIProperty: layerUrls
+ * {Array(String)} Optional list of urls for layers that should be queried.
+ * This can be used when the layer url differs from the url used for
+ * making GetFeatureInfo requests (in the case of a layer using cached
+ * tiles).
+ */
+ layerUrls: null,
+
+ /**
+ * APIProperty: infoFormat
+ * {String} The mimetype to request from the server. If you are using
+ * drillDown mode and have multiple servers that do not share a common
+ * infoFormat, you can override the control's infoFormat by providing an
+ * INFO_FORMAT parameter in your <OpenLayers.Layer.WMS> instance(s).
+ */
+ infoFormat: 'text/html',
+
+ /**
+ * APIProperty: 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)
+ */
+ vendorParams: {},
+
+ /**
+ * APIProperty: format
+ * {<OpenLayers.Format>} A format for parsing GetFeatureInfo responses.
+ * Default is <OpenLayers.Format.WMSGetFeatureInfo>.
+ */
+ format: null,
+
+ /**
+ * APIProperty: formatOptions
+ * {Object} Optional properties to set on the format (if one is not provided
+ * in the <format> property.
+ */
+ formatOptions: null,
+
+ /**
+ * APIProperty: handlerOptions
+ * {Object} Additional options for the handlers used by this control, e.g.
+ * (start code)
+ * {
+ * "click": {delay: 100},
+ * "hover": {delay: 300}
+ * }
+ * (end)
+ */
+
+ /**
+ * Property: handler
+ * {Object} Reference to the <OpenLayers.Handler> for this control
+ */
+ handler: null,
+
+ /**
+ * Property: hoverRequest
+ * {<OpenLayers.Request>} contains the currently running hover request
+ * (if any).
+ */
+ hoverRequest: null,
+
+ /**
+ * APIProperty: events
+ * {<OpenLayers.Events>} Events instance for listeners and triggering
+ * control specific events.
+ *
+ * Register a listener for a particular event with the following syntax:
+ * (code)
+ * control.events.register(type, obj, listener);
+ * (end)
+ *
+ * Supported event types (in addition to those from <OpenLayers.Control.events>):
+ * beforegetfeatureinfo - Triggered before the request is sent.
+ * The event object has an *xy* property with the position of the
+ * mouse click or hover event that triggers the request.
+ * nogetfeatureinfo - no queryable layers were found.
+ * getfeatureinfo - Triggered when a GetFeatureInfo response is received.
+ * The event object has a *text* property with the body of the
+ * response (String), a *features* property with an array of the
+ * parsed features, an *xy* property with the position of the mouse
+ * click or hover event that triggered the request, and a *request*
+ * property with the request itself. If drillDown is set to true and
+ * multiple requests were issued to collect feature info from all
+ * layers, *text* and *request* will only contain the response body
+ * and request object of the last request.
+ */
+
+ /**
+ * Constructor: <OpenLayers.Control.WMSGetFeatureInfo>
+ *
+ * Parameters:
+ * options - {Object}
+ */
+ initialize: function(options) {
+ options = options || {};
+ options.handlerOptions = options.handlerOptions || {};
+
+ OpenLayers.Control.prototype.initialize.apply(this, [options]);
+
+ if(!this.format) {
+ this.format = new OpenLayers.Format.WMSGetFeatureInfo(
+ options.formatOptions
+ );
+ }
+
+ if(this.drillDown === true) {
+ this.hover = false;
+ }
+
+ 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 {
+ var callbacks = {};
+ callbacks[this.clickCallback] = this.getInfoForClick;
+ this.handler = new OpenLayers.Handler.Click(
+ this, callbacks, this.handlerOptions.click || {});
+ }
+ },
+
+ /**
+ * Method: getInfoForClick
+ * Called on click
+ *
+ * Parameters:
+ * evt - {<OpenLayers.Event>}
+ */
+ getInfoForClick: function(evt) {
+ this.events.triggerEvent("beforegetfeatureinfo", {xy: evt.xy});
+ // Set the cursor to "wait" to tell the user we're working on their
+ // click.
+ OpenLayers.Element.addClass(this.map.viewPortDiv, "olCursorWait");
+ this.request(evt.xy, {});
+ },
+
+ /**
+ * Method: getInfoForHover
+ * Pause callback for the hover handler
+ *
+ * Parameters:
+ * evt - {Object}
+ */
+ getInfoForHover: function(evt) {
+ this.events.triggerEvent("beforegetfeatureinfo", {xy: evt.xy});
+ 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() {
+
+ var candidates = this.layers || this.map.layers;
+ var layers = [];
+ var layer, url;
+ for(var i = candidates.length - 1; i >= 0; --i) {
+ layer = candidates[i];
+ if(layer instanceof OpenLayers.Layer.WMS &&
+ (!this.queryVisible || layer.getVisibility())) {
+ url = OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url;
+ // if the control was not configured with a url, set it
+ // to the first layer url
+ if(this.drillDown === false && !this.url) {
+ this.url = url;
+ }
+ if(this.drillDown === true || this.urlMatches(url)) {
+ layers.push(layer);
+ }
+ }
+ }
+ return layers;
+ },
+
+ /**
+ * Method: urlMatches
+ * Test to see if the provided url matches either the control <url> or one
+ * of the <layerUrls>.
+ *
+ * Parameters:
+ * url - {String} The url to test.
+ *
+ * Returns:
+ * {Boolean} The provided url matches the control <url> or one of the
+ * <layerUrls>.
+ */
+ urlMatches: function(url) {
+ var matches = OpenLayers.Util.isEquivalentUrl(this.url, url);
+ if(!matches && this.layerUrls) {
+ for(var i=0, len=this.layerUrls.length; i<len; ++i) {
+ if(OpenLayers.Util.isEquivalentUrl(this.layerUrls[i], url)) {
+ matches = true;
+ break;
+ }
+ }
+ }
+ return matches;
+ },
+
+ /**
+ * Method: buildWMSOptions
+ * Build an object with the relevant WMS options for the GetFeatureInfo request
+ *
+ * Parameters:
+ * url - {String} The url to be used for sending the request
+ * layers - {Array(<OpenLayers.Layer.WMS)} An array of layers
+ * clickPosition - {<OpenLayers.Pixel>} The position on the map where the mouse
+ * event occurred.
+ * format - {String} The format from the corresponding GetMap request
+ */
+ buildWMSOptions: function(url, layers, clickPosition, format) {
+ var layerNames = [], styleNames = [];
+ for (var i = 0, len = layers.length; i < len; i++) {
+ if (layers[i].params.LAYERS != null) {
+ layerNames = layerNames.concat(layers[i].params.LAYERS);
+ styleNames = styleNames.concat(this.getStyleNames(layers[i]));
+ }
+ }
+ var firstLayer = layers[0];
+ // use the firstLayer's projection if it matches the map projection -
+ // this assumes that all layers will be available in this projection
+ var projection = this.map.getProjection();
+ var layerProj = firstLayer.projection;
+ if (layerProj && layerProj.equals(this.map.getProjectionObject())) {
+ projection = layerProj.getCode();
+ }
+ var params = OpenLayers.Util.extend({
+ service: "WMS",
+ version: firstLayer.params.VERSION,
+ request: "GetFeatureInfo",
+ exceptions: firstLayer.params.EXCEPTIONS,
+ bbox: this.map.getExtent().toBBOX(null,
+ firstLayer.reverseAxisOrder()),
+ feature_count: this.maxFeatures,
+ height: this.map.getSize().h,
+ width: this.map.getSize().w,
+ format: format,
+ info_format: firstLayer.params.INFO_FORMAT || this.infoFormat
+ }, (parseFloat(firstLayer.params.VERSION) >= 1.3) ?
+ {
+ crs: projection,
+ i: parseInt(clickPosition.x),
+ j: parseInt(clickPosition.y)
+ } :
+ {
+ srs: projection,
+ x: parseInt(clickPosition.x),
+ y: parseInt(clickPosition.y)
+ }
+ );
+ if (layerNames.length != 0) {
+ params = OpenLayers.Util.extend({
+ layers: layerNames,
+ query_layers: layerNames,
+ styles: styleNames
+ }, params);
+ }
+ OpenLayers.Util.applyDefaults(params, this.vendorParams);
+ return {
+ url: url,
+ params: OpenLayers.Util.upperCaseObject(params),
+ callback: function(request) {
+ this.handleResponse(clickPosition, request, url);
+ },
+ scope: this
+ };
+ },
+
+ /**
+ * Method: getStyleNames
+ * Gets the STYLES parameter for the layer. Make sure the STYLES parameter
+ * matches the LAYERS parameter
+ *
+ * Parameters:
+ * layer - {<OpenLayers.Layer.WMS>}
+ *
+ * Returns:
+ * {Array(String)} The STYLES parameter
+ */
+ getStyleNames: function(layer) {
+ // 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.
+ var styleNames;
+ if (layer.params.STYLES) {
+ styleNames = layer.params.STYLES;
+ } else {
+ if (OpenLayers.Util.isArray(layer.params.LAYERS)) {
+ styleNames = new Array(layer.params.LAYERS.length);
+ } else { // Assume it's a String
+ styleNames = layer.params.LAYERS.replace(/[^,]/g, "");
+ }
+ }
+ return styleNames;
+ },
+
+ /**
+ * Method: request
+ * Sends a GetFeatureInfo request to the WMS
+ *
+ * Parameters:
+ * clickPosition - {<OpenLayers.Pixel>} 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) {
+ var layers = this.findLayers();
+ if(layers.length == 0) {
+ this.events.triggerEvent("nogetfeatureinfo");
+ // Reset the cursor.
+ OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait");
+ return;
+ }
+
+ options = options || {};
+ if(this.drillDown === false) {
+ var wmsOptions = this.buildWMSOptions(this.url, layers,
+ clickPosition, layers[0].params.FORMAT);
+ var request = OpenLayers.Request.GET(wmsOptions);
+
+ if (options.hover === true) {
+ this.hoverRequest = request;
+ }
+ } else {
+ this._requestCount = 0;
+ this._numRequests = 0;
+ this.features = [];
+ // group according to service url to combine requests
+ var services = {}, url;
+ for(var i=0, len=layers.length; i<len; i++) {
+ var layer = layers[i];
+ var service, found = false;
+ url = OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url;
+ if(url in services) {
+ services[url].push(layer);
+ } else {
+ this._numRequests++;
+ services[url] = [layer];
+ }
+ }
+ var layers;
+ for (var url in services) {
+ layers = services[url];
+ var wmsOptions = this.buildWMSOptions(url, layers,
+ clickPosition, layers[0].params.FORMAT);
+ OpenLayers.Request.GET(wmsOptions);
+ }
+ }
+ },
+
+ /**
+ * Method: triggerGetFeatureInfo
+ * Trigger the getfeatureinfo event when all is done
+ *
+ * Parameters:
+ * request - {XMLHttpRequest} The request object
+ * xy - {<OpenLayers.Pixel>} The position on the map where the
+ * mouse event occurred.
+ * features - {Array(<OpenLayers.Feature.Vector>)} or
+ * {Array({Object}) when output is "object". The object has a url and a
+ * features property which contains an array of features.
+ */
+ triggerGetFeatureInfo: function(request, xy, features) {
+ this.events.triggerEvent("getfeatureinfo", {
+ text: request.responseText,
+ features: features,
+ request: request,
+ xy: xy
+ });
+
+ // Reset the cursor.
+ OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait");
+ },
+
+ /**
+ * Method: handleResponse
+ * Handler for the GetFeatureInfo response.
+ *
+ * Parameters:
+ * xy - {<OpenLayers.Pixel>} The position on the map where the
+ * mouse event occurred.
+ * request - {XMLHttpRequest} The request object.
+ * url - {String} The url which was used for this request.
+ */
+ handleResponse: function(xy, request, url) {
+
+ var doc = request.responseXML;
+ if(!doc || !doc.documentElement) {
+ doc = request.responseText;
+ }
+ var features = this.format.read(doc);
+ if (this.drillDown === false) {
+ this.triggerGetFeatureInfo(request, xy, features);
+ } else {
+ this._requestCount++;
+ if (this.output === "object") {
+ this._features = (this._features || []).concat(
+ {url: url, features: features}
+ );
+ } else {
+ this._features = (this._features || []).concat(features);
+ }
+ if (this._requestCount === this._numRequests) {
+ this.triggerGetFeatureInfo(request, xy, this._features.concat());
+ delete this._features;
+ delete this._requestCount;
+ delete this._numRequests;
+ }
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Control.WMSGetFeatureInfo"
+});