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:
Tim Schaub
2010-08-20 22:41:01 +00:00
parent 28558c171c
commit 25fac24436
6 changed files with 406 additions and 0 deletions

View 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>

View 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;
}
}

View File

@@ -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",

View 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
View 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>

View File

@@ -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>