summaryrefslogtreecommitdiff
path: root/misc/openlayers/lib/OpenLayers/Control/GetFeature.js
diff options
context:
space:
mode:
Diffstat (limited to 'misc/openlayers/lib/OpenLayers/Control/GetFeature.js')
-rw-r--r--misc/openlayers/lib/OpenLayers/Control/GetFeature.js597
1 files changed, 597 insertions, 0 deletions
diff --git a/misc/openlayers/lib/OpenLayers/Control/GetFeature.js b/misc/openlayers/lib/OpenLayers/Control/GetFeature.js
new file mode 100644
index 0000000..144e87f
--- /dev/null
+++ b/misc/openlayers/lib/OpenLayers/Control/GetFeature.js
@@ -0,0 +1,597 @@
+/* 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/Box.js
+ * @requires OpenLayers/Handler/Hover.js
+ * @requires OpenLayers/Filter/Spatial.js
+ */
+
+/**
+ * Class: OpenLayers.Control.GetFeature
+ * Gets vector features for locations underneath the mouse cursor. Can be
+ * configured to act on click, hover or dragged boxes. Uses an
+ * <OpenLayers.Protocol> that supports spatial filters to retrieve
+ * features from a server and fires events that notify applications of the
+ * selected features.
+ *
+ * Inherits from:
+ * - <OpenLayers.Control>
+ */
+OpenLayers.Control.GetFeature = OpenLayers.Class(OpenLayers.Control, {
+
+ /**
+ * APIProperty: protocol
+ * {<OpenLayers.Protocol>} Required. The protocol used for fetching
+ * features.
+ */
+ protocol: null,
+
+ /**
+ * APIProperty: multipleKey
+ * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets
+ * the <multiple> property to true. Default is null.
+ */
+ multipleKey: null,
+
+ /**
+ * APIProperty: toggleKey
+ * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets
+ * the <toggle> property to true. Default is null.
+ */
+ toggleKey: null,
+
+ /**
+ * Property: modifiers
+ * {Object} The event modifiers to use, according to the current event
+ * being handled by this control's handlers
+ */
+ modifiers: null,
+
+ /**
+ * APIProperty: multiple
+ * {Boolean} Allow selection of multiple geometries. Default is false.
+ */
+ multiple: false,
+
+ /**
+ * APIProperty: click
+ * {Boolean} Use a click handler for selecting/unselecting features. If
+ * both <click> and <box> are set to true, the click handler takes
+ * precedence over the box handler if a box with zero extent was
+ * selected. Default is true.
+ */
+ click: true,
+
+ /**
+ * APIProperty: single
+ * {Boolean} Tells whether select by click should select a single
+ * feature. If set to false, all matching features are selected.
+ * If set to true, only the best matching feature is selected.
+ * This option has an effect only of the <click> option is set
+ * to true. Default is true.
+ */
+ single: true,
+
+ /**
+ * APIProperty: clickout
+ * {Boolean} Unselect features when clicking outside any feature.
+ * Applies only if <click> is true. Default is true.
+ */
+ clickout: true,
+
+ /**
+ * APIProperty: toggle
+ * {Boolean} Unselect a selected feature on click. Applies only if
+ * <click> is true. Default is false.
+ */
+ toggle: false,
+
+ /**
+ * APIProperty: clickTolerance
+ * {Integer} Tolerance for the filter query in pixels. This has the
+ * same effect as the tolerance parameter on WMS GetFeatureInfo
+ * requests. Will be ignored for box selections. Applies only if
+ * <click> or <hover> is true. Default is 5. Note that this not
+ * only affects requests on click, but also on hover.
+ */
+ clickTolerance: 5,
+
+ /**
+ * APIProperty: hover
+ * {Boolean} Send feature requests on mouse moves. Default is false.
+ */
+ hover: false,
+
+ /**
+ * APIProperty: box
+ * {Boolean} Allow feature selection by drawing a box. If set to
+ * true set <click> to false to disable the click handler and
+ * rely on the box handler only, even for "zero extent" boxes.
+ * See the description of the <click> option for additional
+ * information. Default is false.
+ */
+ box: false,
+
+ /**
+ * APIProperty: maxFeatures
+ * {Integer} Maximum number of features to return from a query in single mode
+ * if supported by the <protocol>. This set of features is then used to
+ * determine the best match client-side. Default is 10.
+ */
+ maxFeatures: 10,
+
+ /**
+ * Property: features
+ * {Object} Hash of {<OpenLayers.Feature.Vector>}, keyed by fid, holding
+ * the currently selected features
+ */
+ features: null,
+
+ /**
+ * Proeprty: hoverFeature
+ * {<OpenLayers.Feature.Vector>} The feature currently selected by the
+ * hover handler
+ */
+ hoverFeature: null,
+
+ /**
+ * APIProperty: handlerOptions
+ * {Object} Additional options for the handlers used by this control. This
+ * is a hash with the keys "click", "box" and "hover".
+ */
+
+ /**
+ * Property: handlers
+ * {Object} Object with references to multiple <OpenLayers.Handler>
+ * instances.
+ */
+ handlers: null,
+
+ /**
+ * Property: hoverResponse
+ * {<OpenLayers.Protocol.Response>} The response object associated with
+ * the currently running hover request (if any).
+ */
+ hoverResponse: null,
+
+ /**
+ * Property: filterType
+ * {<String>} The type of filter to use when sending off a request.
+ * Possible values:
+ * OpenLayers.Filter.Spatial.<BBOX|INTERSECTS|WITHIN|CONTAINS>
+ * Defaults to: OpenLayers.Filter.Spatial.BBOX
+ */
+ filterType: OpenLayers.Filter.Spatial.BBOX,
+
+ /**
+ * 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>):
+ * beforefeatureselected - Triggered when <click> is true before a
+ * feature is selected. The event object has a feature property with
+ * the feature about to select
+ * featureselected - Triggered when <click> is true and a feature is
+ * selected. The event object has a feature property with the
+ * selected feature
+ * beforefeaturesselected - Triggered when <click> is true before a
+ * set of features is selected. The event object is an array of
+ * feature properties with the features about to be selected.
+ * Return false after receiving this event to discontinue processing
+ * of all featureselected events and the featuresselected event.
+ * featuresselected - Triggered when <click> is true and a set of
+ * features is selected. The event object is an array of feature
+ * properties of the selected features
+ * featureunselected - Triggered when <click> is true and a feature is
+ * unselected. The event object has a feature property with the
+ * unselected feature
+ * clickout - Triggered when when <click> is true and no feature was
+ * selected.
+ * hoverfeature - Triggered when <hover> is true and the mouse has
+ * stopped over a feature
+ * outfeature - Triggered when <hover> is true and the mouse moves
+ * moved away from a hover-selected feature
+ */
+
+ /**
+ * Constructor: OpenLayers.Control.GetFeature
+ * Create a new control for fetching remote features.
+ *
+ * Parameters:
+ * options - {Object} A configuration object which at least has to contain
+ * a <protocol> property (if not, it has to be set before a request is
+ * made)
+ */
+ initialize: function(options) {
+ options.handlerOptions = options.handlerOptions || {};
+
+ OpenLayers.Control.prototype.initialize.apply(this, [options]);
+
+ this.features = {};
+
+ this.handlers = {};
+
+ if(this.click) {
+ this.handlers.click = new OpenLayers.Handler.Click(this,
+ {click: this.selectClick}, this.handlerOptions.click || {});
+ }
+
+ if(this.box) {
+ this.handlers.box = new OpenLayers.Handler.Box(
+ this, {done: this.selectBox},
+ OpenLayers.Util.extend(this.handlerOptions.box, {
+ boxDivClassName: "olHandlerBoxSelectFeature"
+ })
+ );
+ }
+
+ if(this.hover) {
+ this.handlers.hover = new OpenLayers.Handler.Hover(
+ this, {'move': this.cancelHover, 'pause': this.selectHover},
+ OpenLayers.Util.extend(this.handlerOptions.hover, {
+ 'delay': 250,
+ 'pixelTolerance': 2
+ })
+ );
+ }
+ },
+
+ /**
+ * Method: activate
+ * Activates the control.
+ *
+ * Returns:
+ * {Boolean} The control was effectively activated.
+ */
+ activate: function () {
+ if (!this.active) {
+ for(var i in this.handlers) {
+ this.handlers[i].activate();
+ }
+ }
+ return OpenLayers.Control.prototype.activate.apply(
+ this, arguments
+ );
+ },
+
+ /**
+ * Method: deactivate
+ * Deactivates the control.
+ *
+ * Returns:
+ * {Boolean} The control was effectively deactivated.
+ */
+ deactivate: function () {
+ if (this.active) {
+ for(var i in this.handlers) {
+ this.handlers[i].deactivate();
+ }
+ }
+ return OpenLayers.Control.prototype.deactivate.apply(
+ this, arguments
+ );
+ },
+
+ /**
+ * Method: selectClick
+ * Called on click
+ *
+ * Parameters:
+ * evt - {<OpenLayers.Event>}
+ */
+ selectClick: function(evt) {
+ var bounds = this.pixelToBounds(evt.xy);
+
+ this.setModifiers(evt);
+ this.request(bounds, {single: this.single});
+ },
+
+ /**
+ * Method: selectBox
+ * Callback from the handlers.box set up when <box> selection is on
+ *
+ * Parameters:
+ * position - {<OpenLayers.Bounds>|Object} An OpenLayers.Bounds or
+ * an object with a 'left', 'bottom', 'right' and 'top' properties.
+ */
+ selectBox: function(position) {
+ var bounds;
+ if (position instanceof OpenLayers.Bounds) {
+ var minXY = this.map.getLonLatFromPixel({
+ x: position.left,
+ y: position.bottom
+ });
+ var maxXY = this.map.getLonLatFromPixel({
+ x: position.right,
+ y: position.top
+ });
+ bounds = new OpenLayers.Bounds(
+ minXY.lon, minXY.lat, maxXY.lon, maxXY.lat
+ );
+
+ } else {
+ if(this.click) {
+ // box without extent - let the click handler take care of it
+ return;
+ }
+ bounds = this.pixelToBounds(position);
+ }
+ this.setModifiers(this.handlers.box.dragHandler.evt);
+ this.request(bounds);
+ },
+
+ /**
+ * Method: selectHover
+ * Callback from the handlers.hover set up when <hover> selection is on
+ *
+ * Parameters:
+ * evt - {Object} event object with an xy property
+ */
+ selectHover: function(evt) {
+ var bounds = this.pixelToBounds(evt.xy);
+ this.request(bounds, {single: true, hover: true});
+ },
+
+ /**
+ * Method: cancelHover
+ * Callback from the handlers.hover set up when <hover> selection is on
+ */
+ cancelHover: function() {
+ if (this.hoverResponse) {
+ this.protocol.abort(this.hoverResponse);
+ this.hoverResponse = null;
+
+ OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait");
+ }
+ },
+
+ /**
+ * Method: request
+ * Sends a GetFeature request to the WFS
+ *
+ * Parameters:
+ * bounds - {<OpenLayers.Bounds>} bounds for the request's BBOX filter
+ * options - {Object} additional options for this method.
+ *
+ * Supported options include:
+ * single - {Boolean} A single feature should be returned.
+ * Note that this will be ignored if the protocol does not
+ * return the geometries of the features.
+ * hover - {Boolean} Do the request for the hover handler.
+ */
+ request: function(bounds, options) {
+ options = options || {};
+ var filter = new OpenLayers.Filter.Spatial({
+ type: this.filterType,
+ value: bounds
+ });
+
+ // Set the cursor to "wait" to tell the user we're working.
+ OpenLayers.Element.addClass(this.map.viewPortDiv, "olCursorWait");
+
+ var response = this.protocol.read({
+ maxFeatures: options.single == true ? this.maxFeatures : undefined,
+ filter: filter,
+ callback: function(result) {
+ if(result.success()) {
+ if(result.features.length) {
+ if(options.single == true) {
+ this.selectBestFeature(result.features,
+ bounds.getCenterLonLat(), options);
+ } else {
+ this.select(result.features);
+ }
+ } else if(options.hover) {
+ this.hoverSelect();
+ } else {
+ this.events.triggerEvent("clickout");
+ if(this.clickout) {
+ this.unselectAll();
+ }
+ }
+ }
+ // Reset the cursor.
+ OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait");
+ },
+ scope: this
+ });
+ if(options.hover == true) {
+ this.hoverResponse = response;
+ }
+ },
+
+ /**
+ * Method: selectBestFeature
+ * Selects the feature from an array of features that is the best match
+ * for the click position.
+ *
+ * Parameters:
+ * features - {Array(<OpenLayers.Feature.Vector>)}
+ * clickPosition - {<OpenLayers.LonLat>}
+ * options - {Object} additional options for this method
+ *
+ * Supported options include:
+ * hover - {Boolean} Do the selection for the hover handler.
+ */
+ selectBestFeature: function(features, clickPosition, options) {
+ options = options || {};
+ if(features.length) {
+ var point = new OpenLayers.Geometry.Point(clickPosition.lon,
+ clickPosition.lat);
+ var feature, resultFeature, dist;
+ var minDist = Number.MAX_VALUE;
+ for(var i=0; i<features.length; ++i) {
+ feature = features[i];
+ if(feature.geometry) {
+ dist = point.distanceTo(feature.geometry, {edge: false});
+ if(dist < minDist) {
+ minDist = dist;
+ resultFeature = feature;
+ if(minDist == 0) {
+ break;
+ }
+ }
+ }
+ }
+
+ if(options.hover == true) {
+ this.hoverSelect(resultFeature);
+ } else {
+ this.select(resultFeature || features);
+ }
+ }
+ },
+
+ /**
+ * Method: setModifiers
+ * Sets the multiple and toggle modifiers according to the current event
+ *
+ * Parameters:
+ * evt - {<OpenLayers.Event>}
+ */
+ setModifiers: function(evt) {
+ this.modifiers = {
+ multiple: this.multiple || (this.multipleKey && evt[this.multipleKey]),
+ toggle: this.toggle || (this.toggleKey && evt[this.toggleKey])
+ };
+ },
+
+ /**
+ * Method: select
+ * Add feature to the hash of selected features and trigger the
+ * featureselected and featuresselected events.
+ *
+ * Parameters:
+ * features - {<OpenLayers.Feature.Vector>} or an array of features
+ */
+ select: function(features) {
+ if(!this.modifiers.multiple && !this.modifiers.toggle) {
+ this.unselectAll();
+ }
+ if(!(OpenLayers.Util.isArray(features))) {
+ features = [features];
+ }
+
+ var cont = this.events.triggerEvent("beforefeaturesselected", {
+ features: features
+ });
+ if(cont !== false) {
+ var selectedFeatures = [];
+ var feature;
+ for(var i=0, len=features.length; i<len; ++i) {
+ feature = features[i];
+ if(this.features[feature.fid || feature.id]) {
+ if(this.modifiers.toggle) {
+ this.unselect(this.features[feature.fid || feature.id]);
+ }
+ } else {
+ cont = this.events.triggerEvent("beforefeatureselected", {
+ feature: feature
+ });
+ if(cont !== false) {
+ this.features[feature.fid || feature.id] = feature;
+ selectedFeatures.push(feature);
+
+ this.events.triggerEvent("featureselected",
+ {feature: feature});
+ }
+ }
+ }
+ this.events.triggerEvent("featuresselected", {
+ features: selectedFeatures
+ });
+ }
+ },
+
+ /**
+ * Method: hoverSelect
+ * Sets/unsets the <hoverFeature>
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>} the feature to hover-select.
+ * If none is provided, the current <hoverFeature> will be nulled and
+ * the outfeature event will be triggered.
+ */
+ hoverSelect: function(feature) {
+ var fid = feature ? feature.fid || feature.id : null;
+ var hfid = this.hoverFeature ?
+ this.hoverFeature.fid || this.hoverFeature.id : null;
+
+ if(hfid && hfid != fid) {
+ this.events.triggerEvent("outfeature",
+ {feature: this.hoverFeature});
+ this.hoverFeature = null;
+ }
+ if(fid && fid != hfid) {
+ this.events.triggerEvent("hoverfeature", {feature: feature});
+ this.hoverFeature = feature;
+ }
+ },
+
+ /**
+ * Method: unselect
+ * Remove feature from the hash of selected features and trigger the
+ * featureunselected event.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>}
+ */
+ unselect: function(feature) {
+ delete this.features[feature.fid || feature.id];
+ this.events.triggerEvent("featureunselected", {feature: feature});
+ },
+
+ /**
+ * Method: unselectAll
+ * Unselect all selected features.
+ */
+ unselectAll: function() {
+ // we'll want an option to supress notification here
+ for(var fid in this.features) {
+ this.unselect(this.features[fid]);
+ }
+ },
+
+ /**
+ * Method: setMap
+ * Set the map property for the control.
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>}
+ */
+ setMap: function(map) {
+ for(var i in this.handlers) {
+ this.handlers[i].setMap(map);
+ }
+ OpenLayers.Control.prototype.setMap.apply(this, arguments);
+ },
+
+ /**
+ * Method: pixelToBounds
+ * Takes a pixel as argument and creates bounds after adding the
+ * <clickTolerance>.
+ *
+ * Parameters:
+ * pixel - {<OpenLayers.Pixel>}
+ */
+ pixelToBounds: function(pixel) {
+ var llPx = pixel.add(-this.clickTolerance/2, this.clickTolerance/2);
+ var urPx = pixel.add(this.clickTolerance/2, -this.clickTolerance/2);
+ var ll = this.map.getLonLatFromPixel(llPx);
+ var ur = this.map.getLonLatFromPixel(urPx);
+ return new OpenLayers.Bounds(ll.lon, ll.lat, ur.lon, ur.lat);
+ },
+
+ CLASS_NAME: "OpenLayers.Control.GetFeature"
+});