Converting control to an interaction

To dispatch events, the interaction base class now inherits from
goog.events.EventTarget.
This commit is contained in:
ahocevar
2013-08-26 17:03:14 +02:00
parent c6e61e2d23
commit 100b85a7b0
10 changed files with 281 additions and 438 deletions

View File

@@ -201,16 +201,6 @@
* Default is '' (empty string).
*/
/**
* @typedef {Object} ol.control.SelectOptions
* @property {string|undefined} className CSS class name. Default is 'ol-select'.
* @property {Element|undefined} element Element.
* @property {undefined|function(ol.layer.Layer):boolean} layerFilter Filter
* function to restrict selection to a subset of layers.
* @property {ol.Map|undefined} map Map.
* @property {Element|undefined} target Target.
*/
/**
* @typedef {Object} ol.control.ScaleLineOptions
* @property {string|undefined} className CSS Class name. Default is 'ol-scale-line'.
@@ -305,6 +295,12 @@
* @property {number|undefined} delta Delta.
*/
/**
* @typedef {Object} ol.interaction.SelectOptions
* @property {undefined|function(ol.layer.Layer):boolean} layerFilter Filter
* function to restrict selection to a subset of layers.
*/
/**
* @typedef {Object} ol.interaction.TouchPanOptions
* @property {ol.Kinetic|undefined} kinetic Kinetic.

View File

@@ -1,3 +0,0 @@
@exportClass ol.control.Select ol.control.SelectOptions
@exportProperty ol.control.Select.prototype.activate
@exportProperty ol.control.Select.prototype.deactivate

View File

@@ -1,235 +0,0 @@
goog.provide('ol.control.Select');
goog.require('goog.array');
goog.require('goog.dom');
goog.require('goog.dom.TagName');
goog.require('goog.dom.classes');
goog.require('goog.events');
goog.require('goog.events.EventType');
goog.require('ol.MapBrowserEvent.EventType');
goog.require('ol.control.Control');
goog.require('ol.css');
goog.require('ol.interaction.condition');
goog.require('ol.layer.Vector');
goog.require('ol.layer.VectorLayerRenderIntent');
goog.require('ol.source.Vector');
/**
* @typedef {{layer: ol.layer.Layer,
* selected: (Array.<ol.Feature>|undefined),
* type: goog.events.EventType,
* unselected: (Array.<ol.Feature>|undefined)}}
*/
ol.control.SelectEventObject;
/**
* @constructor
* @extends {ol.control.Control}
* @param {ol.control.SelectOptions=} opt_options Options.
*/
ol.control.Select = function(opt_options) {
var options = goog.isDef(opt_options) ? opt_options : {};
/**
* @type {boolean}
* @private
*/
this.active_ = false;
/**
* Mapping between original features and cloned features on selection layers.
* @type {Object.<*,Object.<string,ol.Feature>>}
* @private
*/
this.featureMap_ = {};
/**
* Mapping between original layers and selection layers.
* @type {Object.<*,ol.layer.Vector>}
* @protected
*/
this.selectionLayers = {};
/**
* @type {null|function(ol.layer.Layer):boolean}
* @private
*/
this.layerFilter_ = goog.isDef(options.layerFilter) ?
options.layerFilter : null;
// TODO: css/button refactoring
var className = goog.isDef(options.className) ? options.className :
'ol-select';
var element = goog.dom.createDom(goog.dom.TagName.DIV, {
'class': className + ' ' + ol.css.CLASS_UNSELECTABLE
});
var button = goog.dom.createDom(goog.dom.TagName.A, {
'href': '#Select'
});
goog.dom.appendChild(element, button);
goog.events.listen(element, [
goog.events.EventType.TOUCHEND,
goog.events.EventType.CLICK
], this.toggleActive_, false, this);
goog.base(this, {
element: element,
map: options.map,
target: options.target
});
};
goog.inherits(ol.control.Select, ol.control.Control);
/**
* @param {goog.events.BrowserEvent} browserEvent Browser event.
* @private
*/
ol.control.Select.prototype.toggleActive_ = function(browserEvent) {
// prevent #Select anchor from getting appended to the url
browserEvent.preventDefault();
if (this.active_) {
this.deactivate();
} else {
this.activate();
}
};
/**
* Activate the control.
*/
ol.control.Select.prototype.activate = function() {
if (!this.active_) {
this.active_ = true;
goog.dom.classes.add(this.element, 'active');
var map = this.getMap();
for (var i in this.selectionLayers) {
map.addLayer(this.selectionLayers[i]);
}
// TODO: Implement box selection
this.listenerKeys.push(
goog.events.listen(map, ol.MapBrowserEvent.EventType.CLICK,
this.handleClick, true, this));
}
};
/**
* Dectivate the control.
*/
ol.control.Select.prototype.deactivate = function() {
if (this.active_) {
if (!goog.array.isEmpty(this.listenerKeys)) {
goog.array.forEach(this.listenerKeys, goog.events.unlistenByKey);
this.listenerKeys.length = 0;
}
var map = this.getMap();
for (var i in this.selectionLayers) {
map.removeLayer(this.selectionLayers[i]);
}
goog.dom.classes.remove(this.element, 'active');
this.active_ = false;
}
};
/**
* @param {ol.MapBrowserEvent} evt Event.
*/
ol.control.Select.prototype.handleClick = function(evt) {
var map = this.getMap();
var layers = map.getLayerGroup().getLayersArray();
if (!goog.isNull(this.layerFilter_)) {
layers = goog.array.filter(layers, this.layerFilter_);
}
var clear = !ol.interaction.condition.shiftKeyOnly(evt.browserEvent);
function select(featuresByLayer) {
this.select(featuresByLayer, layers, clear);
}
map.getFeatures({
layers: layers,
pixel: evt.getPixel(),
success: goog.bind(select, this)
});
};
/**
* @param {Array.<Array.<ol.Feature>>} featuresByLayer Features by layer.
* @param {Array.<ol.layer.Layer>} layers The queried layers.
* @param {boolean} clear Whether the current layer content should be cleared.
*/
ol.control.Select.prototype.select = function(featuresByLayer, layers, clear) {
for (var i = 0, ii = featuresByLayer.length; i < ii; ++i) {
var layer = layers[i];
var layerId = goog.getUid(layer);
var selectionLayer = this.selectionLayers[layerId];
if (!goog.isDef(selectionLayer)) {
selectionLayer = new ol.layer.Vector({
source: new ol.source.Vector({parser: null}),
style: layer.getStyle()
});
selectionLayer.setTemporary(true);
this.getMap().addLayer(selectionLayer);
this.selectionLayers[layerId] = selectionLayer;
this.featureMap_[layerId] = {};
}
var features = featuresByLayer[i];
var numFeatures = features.length;
var selectedFeatures = [];
var featuresToAdd = [];
var unselectedFeatures = [];
var featuresToRemove = [];
var featureMap = this.featureMap_[layerId];
for (var j = 0; j < numFeatures; ++j) {
var feature = features[j];
var featureId = goog.getUid(feature);
var clone = featureMap[featureId];
if (clone) {
// TODO: make toggle configurable
unselectedFeatures.push(feature);
featuresToRemove.push(clone);
delete featureMap[featureId];
}
if (clear) {
for (var f in featureMap) {
unselectedFeatures.push(layer.getFeatureWithUid(f));
featuresToRemove.push(featureMap[f]);
}
featureMap = {};
this.featureMap_[layerId] = featureMap;
}
if (!clone) {
clone = feature.clone();
featureMap[featureId] = clone;
clone.renderIntent = ol.layer.VectorLayerRenderIntent.SELECTED;
selectedFeatures.push(feature);
featuresToAdd.push(clone);
}
}
if (goog.isFunction(layer.setRenderIntent)) {
layer.setRenderIntent(ol.layer.VectorLayerRenderIntent.HIDDEN,
selectedFeatures);
layer.setRenderIntent(ol.layer.VectorLayerRenderIntent.DEFAULT,
unselectedFeatures);
}
selectionLayer.removeFeatures(featuresToRemove);
selectionLayer.addFeatures(featuresToAdd);
this.dispatchEvent(/** @type {ol.control.SelectEventObject} */ ({
layer: layer,
selected: selectedFeatures,
type: goog.events.EventType.CHANGE,
unselected: unselectedFeatures
}));
}
};

View File

@@ -2,6 +2,7 @@
goog.provide('ol.interaction.Interaction');
goog.require('goog.events.EventTarget');
goog.require('ol.MapBrowserEvent');
goog.require('ol.animation.pan');
goog.require('ol.animation.rotate');
@@ -12,9 +13,12 @@ goog.require('ol.easing');
/**
* @constructor
* @extends {goog.events.EventTarget}
*/
ol.interaction.Interaction = function() {
goog.base(this);
};
goog.inherits(ol.interaction.Interaction, goog.events.EventTarget);
/**

View File

@@ -0,0 +1,2 @@
@exportClass ol.interaction.Select ol.interaction.SelectOptions
@exportProperty ol.interaction.Select.prototype.dispose

View File

@@ -0,0 +1,180 @@
goog.provide('ol.interaction.Select');
goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.events');
goog.require('goog.events.EventType');
goog.require('ol.MapBrowserEvent.EventType');
goog.require('ol.interaction.Interaction');
goog.require('ol.interaction.condition');
goog.require('ol.layer.Vector');
goog.require('ol.layer.VectorLayerRenderIntent');
goog.require('ol.source.Vector');
/**
* @typedef {{layer: ol.layer.Layer,
* map: ol.Map,
* selected: (Array.<ol.Feature>|undefined),
* type: goog.events.EventType,
* unselected: (Array.<ol.Feature>|undefined)}}
*/
ol.interaction.SelectEventObject;
/**
* @constructor
* @extends {ol.interaction.Interaction}
* @param {ol.interaction.SelectOptions=} opt_options Options.
*/
ol.interaction.Select = function(opt_options) {
var options = goog.isDef(opt_options) ? opt_options : {};
/**
* Mapping between original features and cloned features on selection layers.
* @type {Object.<*,Object.<*,ol.Feature>>}
* @private
*/
this.featureMap_ = {};
/**
* Mapping between original layers and selection layers, by map.
* @type {Object.<*,{map:ol.Map,layers:Object.<*,ol.layer.Vector>}>}
* @protected
*/
this.selectionLayers = {};
/**
* @type {null|function(ol.layer.Layer):boolean}
* @private
*/
this.layerFilter_ = goog.isDef(options.layerFilter) ?
options.layerFilter : null;
goog.base(this);
};
goog.inherits(ol.interaction.Select, ol.interaction.Interaction);
/**
* @inheritDoc
*/
ol.interaction.Select.prototype.disposeInternal = function() {
for (var m in this.selectionLayers) {
var selectionLayers = this.selectionLayers[m].layers;
var map = this.selectionLayers[m].map;
for (var l in selectionLayers) {
map.removeLayer(selectionLayers[l]);
}
}
goog.base(this, 'disposeInternal');
};
/**
* @inheritDoc.
*/
ol.interaction.Select.prototype.handleMapBrowserEvent = function(evt) {
if (evt.type === ol.MapBrowserEvent.EventType.CLICK) {
var map = evt.map;
var layers = map.getLayerGroup().getLayersArray();
if (!goog.isNull(this.layerFilter_)) {
layers = goog.array.filter(layers, this.layerFilter_);
}
var clear = !ol.interaction.condition.shiftKeyOnly(evt.browserEvent);
var select = function(featuresByLayer) {
this.select(map, featuresByLayer, layers, clear);
};
map.getFeatures({
layers: layers,
pixel: evt.getPixel(),
success: goog.bind(select, this)
});
}
// TODO: Implement box selection
return true;
};
/**
* @param {ol.Map} map The map where the selction event originated.
* @param {Array.<Array.<ol.Feature>>} featuresByLayer Features by layer.
* @param {Array.<ol.layer.Layer>} layers The queried layers.
* @param {boolean} clear Whether the current layer content should be cleared.
*/
ol.interaction.Select.prototype.select =
function(map, featuresByLayer, layers, clear) {
var mapId = goog.getUid(map);
for (var i = 0, ii = featuresByLayer.length; i < ii; ++i) {
var layer = layers[i];
var layerId = goog.getUid(layer);
if (!(mapId in this.selectionLayers)) {
this.selectionLayers[mapId] = {map: map, layers: {}};
}
var selectionLayer = this.selectionLayers[mapId].layers[layerId];
if (!goog.isDef(selectionLayer)) {
goog.asserts.assertFunction(layer.getStyle,
'At least one of the layers has no "getStyle()" function.');
selectionLayer = new ol.layer.Vector({
source: new ol.source.Vector({parser: null}),
style: layer.getStyle()
});
selectionLayer.setTemporary(true);
map.addLayer(selectionLayer);
this.selectionLayers[mapId].layers[layerId] = selectionLayer;
this.featureMap_[layerId] = {};
}
var features = featuresByLayer[i];
var numFeatures = features.length;
var selectedFeatures = [];
var featuresToAdd = [];
var unselectedFeatures = [];
var featuresToRemove = [];
var featureMap = this.featureMap_[layerId];
var oldFeatureMap = featureMap;
if (clear) {
for (var f in featureMap) {
unselectedFeatures.push(layer.getFeatureWithUid(f));
featuresToRemove.push(featureMap[f]);
}
featureMap = {};
this.featureMap_[layerId] = featureMap;
}
for (var j = 0; j < numFeatures; ++j) {
var feature = features[j];
var featureId = goog.getUid(feature);
var clone = featureMap[featureId];
if (clone) {
// TODO: make toggle configurable
unselectedFeatures.push(feature);
featuresToRemove.push(clone);
delete featureMap[featureId];
} else if (!(featureId in oldFeatureMap)) {
clone = feature.clone();
featureMap[featureId] = clone;
clone.renderIntent = ol.layer.VectorLayerRenderIntent.SELECTED;
selectedFeatures.push(feature);
featuresToAdd.push(clone);
}
}
if (goog.isFunction(layer.setRenderIntent)) {
layer.setRenderIntent(ol.layer.VectorLayerRenderIntent.HIDDEN,
selectedFeatures);
layer.setRenderIntent(ol.layer.VectorLayerRenderIntent.DEFAULT,
unselectedFeatures);
}
selectionLayer.removeFeatures(featuresToRemove);
selectionLayer.addFeatures(featuresToAdd);
this.dispatchEvent(/** @type {ol.interaction.SelectEventObject} */ ({
layer: layer,
map: map,
selected: selectedFeatures,
type: goog.events.EventType.CHANGE,
unselected: unselectedFeatures
}));
}
};