Adding a filter strategy for limiting features that are included in a layer's collection. The strategy takes a filter and caches features that don't pass the filter. Call setFilter on the strategy to update the cache and collection on the layer. r=ahocevar (closes #2790)
git-svn-id: http://svn.openlayers.org/trunk/openlayers@10655 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf
This commit is contained in:
48
examples/filter-strategy.html
Normal file
48
examples/filter-strategy.html
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>OpenLayers Filter Strategy Example</title>
|
||||||
|
<link rel="stylesheet" href="../theme/default/style.css" type="text/css">
|
||||||
|
<link rel="stylesheet" href="../theme/default/google.css" type="text/css">
|
||||||
|
<link rel="stylesheet" href="style.css" type="text/css">
|
||||||
|
<script src="../lib/OpenLayers.js"></script>
|
||||||
|
<script>OpenLayers.ImgPath = "../img/";</script>
|
||||||
|
<script src="filter-strategy.js"></script>
|
||||||
|
<style>
|
||||||
|
.olControlAttribution {
|
||||||
|
font-size: 9px;
|
||||||
|
bottom: 2px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body onload="init()">
|
||||||
|
<h1 id="title">Filter Strategy</h1>
|
||||||
|
<p id="shortdesc">
|
||||||
|
Demonstrates the filter strategy for limiting features passed to the layer.
|
||||||
|
</p>
|
||||||
|
<div id="map" class="smallmap"></div>
|
||||||
|
<label for="span">time span (seconds)</label>
|
||||||
|
<select id="span" name="span">
|
||||||
|
<option value="15">15</option>
|
||||||
|
<option value="30">30</option>
|
||||||
|
<option value="60" selected>60</option>
|
||||||
|
<option value="120">120</option>
|
||||||
|
<option value="240">240</option>
|
||||||
|
</select>
|
||||||
|
<input type="button" id="start" value="start">
|
||||||
|
<input type="button" id="stop" value="stop"><br><br>
|
||||||
|
<div id="docs">
|
||||||
|
<p>
|
||||||
|
This example uses a filter strategy to limit the features that are passed
|
||||||
|
to a layer. Features bound for this layer have a <code>when</code> attribute
|
||||||
|
with date values. A filter strategy is constructed with a between filter
|
||||||
|
that limits the span of dates shown. A simple animation cycles through
|
||||||
|
the domain of the <code>when</code> values, calling <code>setFilter</code>
|
||||||
|
on the strategy with an updated filter.
|
||||||
|
</p><p>
|
||||||
|
View the <a href="filter-strategy.js" target="_blank">filter-strategy.js</a>
|
||||||
|
source to see how this is done
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
89
examples/filter-strategy.js
Normal file
89
examples/filter-strategy.js
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
var map, filter, filterStrategy;
|
||||||
|
|
||||||
|
var startDate = new Date(1272736800000); // lower bound of when values
|
||||||
|
var endDate = new Date(1272737100000); // upper value of when values
|
||||||
|
var step = 8; // sencods to advance each interval
|
||||||
|
var interval = 0.125; // seconds between each step in the animation
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
|
||||||
|
// add behavior to elements
|
||||||
|
document.getElementById("start").onclick = startAnimation;
|
||||||
|
document.getElementById("stop").onclick = stopAnimation;
|
||||||
|
var spanEl = document.getElementById("span");
|
||||||
|
|
||||||
|
var mercator = new OpenLayers.Projection("EPSG:900913");
|
||||||
|
var geographic = new OpenLayers.Projection("EPSG:4326");
|
||||||
|
map = new OpenLayers.Map("map");
|
||||||
|
|
||||||
|
var osm = new OpenLayers.Layer.OSM();
|
||||||
|
|
||||||
|
filter = new OpenLayers.Filter.Comparison({
|
||||||
|
type: OpenLayers.Filter.Comparison.BETWEEN,
|
||||||
|
property: "when",
|
||||||
|
lowerBoundary: startDate,
|
||||||
|
upperBoundary: new Date(startDate.getTime() + (parseInt(spanEl.value, 10) * 1000))
|
||||||
|
});
|
||||||
|
|
||||||
|
filterStrategy = new OpenLayers.Strategy.Filter({filter: filter});
|
||||||
|
|
||||||
|
var flights = new OpenLayers.Layer.Vector("Aircraft Locations", {
|
||||||
|
projection: geographic,
|
||||||
|
strategies: [new OpenLayers.Strategy.Fixed(), filterStrategy],
|
||||||
|
protocol: new OpenLayers.Protocol.HTTP({
|
||||||
|
url: "kml-track.kml",
|
||||||
|
format: new OpenLayers.Format.KML({
|
||||||
|
extractTracks: true
|
||||||
|
//,extractStyles: true // use style from KML instead of styleMap below
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
styleMap: new OpenLayers.StyleMap({
|
||||||
|
"default": new OpenLayers.Style({
|
||||||
|
graphicName: "circle",
|
||||||
|
pointRadius: 3,
|
||||||
|
fillOpacity: 0.25,
|
||||||
|
fillColor: "#ffcc66",
|
||||||
|
strokeColor: "#ff9933",
|
||||||
|
strokeWidth: 1
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
renderers: ["Canvas", "SVG", "VML"]
|
||||||
|
});
|
||||||
|
|
||||||
|
map.addLayers([osm, flights]);
|
||||||
|
map.setCenter(new OpenLayers.LonLat(-93.2735, 44.8349).transform(geographic, mercator), 8);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
var animationTimer;
|
||||||
|
var currentDate;
|
||||||
|
function startAnimation() {
|
||||||
|
if (animationTimer) {
|
||||||
|
stopAnimation(true);
|
||||||
|
}
|
||||||
|
if (!currentDate) {
|
||||||
|
currentDate = startDate;
|
||||||
|
}
|
||||||
|
var spanEl = document.getElementById("span");
|
||||||
|
var next = function() {
|
||||||
|
var span = parseInt(spanEl.value, 10);
|
||||||
|
if (currentDate < endDate) {
|
||||||
|
filter.lowerBoundary = currentDate;
|
||||||
|
filter.upperBoundary = new Date(currentDate.getTime() + (span * 1000));
|
||||||
|
filterStrategy.setFilter(filter);
|
||||||
|
currentDate = new Date(currentDate.getTime() + (step * 1000))
|
||||||
|
} else {
|
||||||
|
stopAnimation(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
animationTimer = window.setInterval(next, interval * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopAnimation(reset) {
|
||||||
|
window.clearInterval(animationTimer);
|
||||||
|
animationTimer = null;
|
||||||
|
if (reset === true) {
|
||||||
|
currentDate = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -203,6 +203,7 @@
|
|||||||
"OpenLayers/Layer/Vector.js",
|
"OpenLayers/Layer/Vector.js",
|
||||||
"OpenLayers/Layer/Vector/RootContainer.js",
|
"OpenLayers/Layer/Vector/RootContainer.js",
|
||||||
"OpenLayers/Strategy.js",
|
"OpenLayers/Strategy.js",
|
||||||
|
"OpenLayers/Strategy/Filter.js",
|
||||||
"OpenLayers/Strategy/Fixed.js",
|
"OpenLayers/Strategy/Fixed.js",
|
||||||
"OpenLayers/Strategy/Cluster.js",
|
"OpenLayers/Strategy/Cluster.js",
|
||||||
"OpenLayers/Strategy/Paging.js",
|
"OpenLayers/Strategy/Paging.js",
|
||||||
|
|||||||
159
lib/OpenLayers/Strategy/Filter.js
Normal file
159
lib/OpenLayers/Strategy/Filter.js
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
/**
|
||||||
|
* @requires OpenLayers/Strategy.js
|
||||||
|
* @requires OpenLayers/Filter.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class: OpenLayers.Strategy.Filter
|
||||||
|
* Strategy for limiting features that get added to a layer by
|
||||||
|
* evaluating a filter. The strategy maintains a cache of
|
||||||
|
* all features until removeFeatures is called on the layer.
|
||||||
|
*
|
||||||
|
* Inherits from:
|
||||||
|
* - <OpenLayers.Strategy>
|
||||||
|
*/
|
||||||
|
OpenLayers.Strategy.Filter = OpenLayers.Class(OpenLayers.Strategy, {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIProperty: filter
|
||||||
|
* {<OpenLayers.Filter>} Filter for limiting features sent to the layer.
|
||||||
|
* Use the <setFilter> method to update this filter after construction.
|
||||||
|
*/
|
||||||
|
filter: null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Property: cache
|
||||||
|
* {Array(<OpenLayers.Feature.Vector>)} List of currently cached
|
||||||
|
* features.
|
||||||
|
*/
|
||||||
|
cache: null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Property: caching
|
||||||
|
* {Boolean} The filter is currently caching features.
|
||||||
|
*/
|
||||||
|
caching: false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor: OpenLayers.Strategy.Filter
|
||||||
|
* Create a new filter strategy.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* options - {Object} Optional object whose properties will be set on the
|
||||||
|
* instance. Strategy must be constructed with at least a <filter>
|
||||||
|
* property.
|
||||||
|
*/
|
||||||
|
initialize: function(options) {
|
||||||
|
OpenLayers.Strategy.prototype.initialize.apply(this, [options]);
|
||||||
|
if (!this.filter || !(this.filter instanceof OpenLayers.Filter)) {
|
||||||
|
throw new Error("Filter strategy must be constructed with a filter");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIMethod: activate
|
||||||
|
* Activate the strategy. Register any listeners, do appropriate setup.
|
||||||
|
* By default, this strategy automatically activates itself when a layer
|
||||||
|
* is added to a map.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* {Boolean} True if the strategy was successfully activated or false if
|
||||||
|
* the strategy was already active.
|
||||||
|
*/
|
||||||
|
activate: function() {
|
||||||
|
var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments);
|
||||||
|
if (activated) {
|
||||||
|
this.cache = [];
|
||||||
|
this.layer.events.on({
|
||||||
|
"beforefeaturesadded": this.handleAdd,
|
||||||
|
"beforefeaturesremoved": this.handleRemove,
|
||||||
|
scope: this
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return activated;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIMethod: deactivate
|
||||||
|
* Deactivate the strategy. Clear the feature cache.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* {Boolean} True if the strategy was successfully deactivated or false if
|
||||||
|
* the strategy was already inactive.
|
||||||
|
*/
|
||||||
|
deactivate: function() {
|
||||||
|
this.cache = null;
|
||||||
|
if (this.layer && this.layer.events) {
|
||||||
|
this.layer.events.un({
|
||||||
|
"beforefeaturesadded": this.handleAdd,
|
||||||
|
"beforefeaturesremoved": this.handleRemove,
|
||||||
|
scope: this
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return OpenLayers.Strategy.prototype.deactivate.apply(this, arguments);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method: handleAdd
|
||||||
|
*/
|
||||||
|
handleAdd: function(event) {
|
||||||
|
if (!this.caching) {
|
||||||
|
var features = event.features;
|
||||||
|
event.features = [];
|
||||||
|
var feature
|
||||||
|
for (var i=0, ii=features.length; i<ii; ++i) {
|
||||||
|
feature = features[i];
|
||||||
|
if (this.filter.evaluate(feature)) {
|
||||||
|
event.features.push(feature);
|
||||||
|
} else {
|
||||||
|
this.cache.push(feature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method: handleRemove
|
||||||
|
*/
|
||||||
|
handleRemove: function(event) {
|
||||||
|
if (!this.caching) {
|
||||||
|
this.cache = [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIMethod: setFilter
|
||||||
|
* Update the filter for this strategy. This will re-evaluate
|
||||||
|
* any features on the layer and in the cache. Only features
|
||||||
|
* for which filter.evalute(feature) returns true will be
|
||||||
|
* added to the layer. Others will be cached by the strategy.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* filter - <OpenLayers.Filter> A filter for evaluating features.
|
||||||
|
*/
|
||||||
|
setFilter: function(filter) {
|
||||||
|
this.filter = filter;
|
||||||
|
var previousCache = this.cache;
|
||||||
|
this.cache = [];
|
||||||
|
// look through layer for features to remove from layer
|
||||||
|
this.handleAdd({features: this.layer.features});
|
||||||
|
// cache now contains features to remove from layer
|
||||||
|
if (this.cache.length > 0) {
|
||||||
|
this.caching = true;
|
||||||
|
this.layer.removeFeatures(this.cache.slice(), {silent: true});
|
||||||
|
this.caching = false;
|
||||||
|
}
|
||||||
|
// now look through previous cache for features to add to layer
|
||||||
|
if (previousCache.length > 0) {
|
||||||
|
var event = {features: previousCache};
|
||||||
|
this.handleAdd(event);
|
||||||
|
// event has features to add to layer
|
||||||
|
this.caching = true;
|
||||||
|
this.layer.addFeatures(event.features, {silent: true});
|
||||||
|
this.caching = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
CLASS_NAME: "OpenLayers.Strategy.Filter"
|
||||||
|
|
||||||
|
});
|
||||||
108
tests/Strategy/Filter.html
Normal file
108
tests/Strategy/Filter.html
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src="../../lib/OpenLayers.js"></script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
var features = [];
|
||||||
|
for (var i=0; i<20; ++i) {
|
||||||
|
features.push(
|
||||||
|
new OpenLayers.Feature.Vector(
|
||||||
|
new OpenLayers.Geometry.Point(0, 0), {index: i}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var filter = new OpenLayers.Filter.Comparison({
|
||||||
|
type: OpenLayers.Filter.Comparison.LESS_THAN,
|
||||||
|
property: "index",
|
||||||
|
value: 10
|
||||||
|
});
|
||||||
|
|
||||||
|
function test_initialize(t) {
|
||||||
|
|
||||||
|
t.plan(3);
|
||||||
|
|
||||||
|
var strategy = new OpenLayers.Strategy.Filter({filter: filter});
|
||||||
|
|
||||||
|
t.ok(strategy instanceof OpenLayers.Strategy, "is strategy");
|
||||||
|
t.ok(strategy instanceof OpenLayers.Strategy.Filter, "is filter strategy");
|
||||||
|
|
||||||
|
t.ok(strategy.filter === filter, "has filter");
|
||||||
|
|
||||||
|
strategy.destroy();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_autoActivate(t) {
|
||||||
|
|
||||||
|
t.plan(2);
|
||||||
|
|
||||||
|
var strategy = new OpenLayers.Strategy.Filter({filter: filter});
|
||||||
|
|
||||||
|
var layer = new OpenLayers.Layer.Vector(null, {
|
||||||
|
strategies: [strategy]
|
||||||
|
});
|
||||||
|
|
||||||
|
t.ok(!strategy.active, "strategy not active before adding to map");
|
||||||
|
|
||||||
|
var map = new OpenLayers.Map({
|
||||||
|
div: "map",
|
||||||
|
allOverlays: true,
|
||||||
|
layers: [layer],
|
||||||
|
center: new OpenLayers.LonLat(0, 0),
|
||||||
|
zoom: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
t.ok(strategy.active, "strategy active after adding to map");
|
||||||
|
|
||||||
|
map.destroy();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_setFilter(t) {
|
||||||
|
|
||||||
|
t.plan(7);
|
||||||
|
|
||||||
|
var strategy = new OpenLayers.Strategy.Filter({filter: filter});
|
||||||
|
var layer = new OpenLayers.Layer.Vector(null, {
|
||||||
|
strategies: [strategy]
|
||||||
|
});
|
||||||
|
|
||||||
|
var map = new OpenLayers.Map({
|
||||||
|
div: "map",
|
||||||
|
allOverlays: true,
|
||||||
|
layers: [layer],
|
||||||
|
center: new OpenLayers.LonLat(0, 0),
|
||||||
|
zoom: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
// add all features
|
||||||
|
layer.addFeatures(features);
|
||||||
|
t.eq(features.length, 20, "collection of 20 features")
|
||||||
|
t.eq(layer.features.length, 10, "layer got 10 with filter 'index < 10'");
|
||||||
|
t.eq(strategy.cache.length, 10, "strategy cached 10 with filter 'index < 10'");
|
||||||
|
|
||||||
|
// update filter
|
||||||
|
filter.value = 5;
|
||||||
|
strategy.setFilter(filter);
|
||||||
|
t.eq(layer.features.length, 5, "layer got 5 with filter 'index < 5'");
|
||||||
|
t.eq(strategy.cache.length, 15, "strategy cached 15 with filter 'index < 5'");
|
||||||
|
|
||||||
|
// update filter
|
||||||
|
filter.value = 15;
|
||||||
|
strategy.setFilter(filter);
|
||||||
|
t.eq(layer.features.length, 15, "layer got 15 with filter 'index < 15'");
|
||||||
|
t.eq(strategy.cache.length, 5, "strategy cached 5 with filter 'index < 15'");
|
||||||
|
|
||||||
|
map.destroy();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</script></head>
|
||||||
|
<body>
|
||||||
|
<div id="map" style="width: 512px; height: 256px" />
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -177,6 +177,7 @@
|
|||||||
<li>Strategy.html</li>
|
<li>Strategy.html</li>
|
||||||
<li>Strategy/BBOX.html</li>
|
<li>Strategy/BBOX.html</li>
|
||||||
<li>Strategy/Cluster.html</li>
|
<li>Strategy/Cluster.html</li>
|
||||||
|
<li>Strategy/Filter.html</li>
|
||||||
<li>Strategy/Fixed.html</li>
|
<li>Strategy/Fixed.html</li>
|
||||||
<li>Strategy/Paging.html</li>
|
<li>Strategy/Paging.html</li>
|
||||||
<li>Strategy/Save.html</li>
|
<li>Strategy/Save.html</li>
|
||||||
|
|||||||
Reference in New Issue
Block a user