diff --git a/examples/strategy-bbox.html b/examples/strategy-bbox.html
new file mode 100644
index 0000000000..173d33f1f7
--- /dev/null
+++ b/examples/strategy-bbox.html
@@ -0,0 +1,64 @@
+
+
+ OpenLayers BBOX Strategy Example
+
+
+
+
+
+
+
BBOX Strategy Example
+
+ Uses a BBOX strategy to request features within a bounding box.
+
+
+
+
The BBOX strategy requests data within a bounding box. When the
+ previously requested data bounds are invalidated (by browsing to
+ some area not covered by those bounds), another request for data
+ is issued.
+
+
+
diff --git a/lib/OpenLayers.js b/lib/OpenLayers.js
index a1c04e0526..cae000e7b7 100644
--- a/lib/OpenLayers.js
+++ b/lib/OpenLayers.js
@@ -185,6 +185,7 @@
"OpenLayers/Layer/Vector.js",
"OpenLayers/Strategy.js",
"OpenLayers/Strategy/Fixed.js",
+ "OpenLayers/Strategy/BBOX.js",
"OpenLayers/Protocol.js",
"OpenLayers/Protocol/HTTP.js",
"OpenLayers/Layer/PointTrack.js",
diff --git a/lib/OpenLayers/Protocol/HTTP.js b/lib/OpenLayers/Protocol/HTTP.js
index 866c8e318e..76c5fbc83d 100644
--- a/lib/OpenLayers/Protocol/HTTP.js
+++ b/lib/OpenLayers/Protocol/HTTP.js
@@ -112,6 +112,15 @@ OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, {
* options - {Object} Optional object for configuring the request.
* This object is modified and should not be reused.
*
+ * Valid options:
+ * url - {String} Url for the request.
+ * params - {Object} Parameters to get serialized as a query string.
+ * headers - {Object} Headers to be set on the request.
+ * filter - {} If a bbox filter is sent, it will be
+ * serialized according to the OpenSearch Geo extension
+ * (bbox=minx,miny,maxx,maxy). Note that a BBOX filter as the child
+ * of a logical filter will not be serialized.
+ *
* Returns:
* {} A response object, whose "priv" property
* references the HTTP request, this object is also passed to the
@@ -121,6 +130,14 @@ OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, {
read: function(options) {
options = OpenLayers.Util.applyDefaults(options, this.options);
var resp = new OpenLayers.Protocol.Response({requestType: "read"});
+
+ if(options.filter && options.filter instanceof OpenLayers.Filter.Spatial) {
+ if(options.filter.type == OpenLayers.Filter.Spatial.BBOX) {
+ options.params = OpenLayers.Util.extend(options.params, {
+ bbox: options.filter.value.toArray()
+ });
+ }
+ }
resp.priv = OpenLayers.Request.GET({
url: options.url,
diff --git a/lib/OpenLayers/Strategy/BBOX.js b/lib/OpenLayers/Strategy/BBOX.js
new file mode 100644
index 0000000000..f37fd0154d
--- /dev/null
+++ b/lib/OpenLayers/Strategy/BBOX.js
@@ -0,0 +1,210 @@
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license. See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Strategy.js
+ * @requires OpenLayers/Filter/Spatial.js
+ */
+
+/**
+ * Class: OpenLayers.Strategy.BBOX
+ * A simple strategy that reads new features when the viewport invalidates
+ * some bounds.
+ *
+ * Inherits from:
+ * -
+ */
+OpenLayers.Strategy.BBOX = OpenLayers.Class(OpenLayers.Strategy, {
+
+ /**
+ * Property: bounds
+ * {} The current data bounds.
+ */
+ bounds: null,
+
+ /**
+ * Property: ratio
+ * {Float} The ratio of the data bounds to the viewport bounds (in each
+ * dimension).
+ */
+ ratio: 2,
+
+ /**
+ * Property: response
+ * {} The protocol response object returned
+ * by the layer protocol.
+ */
+ response: null,
+
+ /**
+ * Constructor: OpenLayers.Strategy.BBOX
+ * Create a new BBOX strategy.
+ *
+ * Parameters:
+ * options - {Object} Optional object whose properties will be set on the
+ * instance.
+ */
+ initialize: function(options) {
+ OpenLayers.Strategy.prototype.initialize.apply(this, [options]);
+ },
+
+ /**
+ * Method: activate
+ * Set up strategy with regard to reading new batches of remote data.
+ *
+ * Returns:
+ * {Boolean} The strategy was successfully activated.
+ */
+ activate: function() {
+ var activated = OpenLayers.Strategy.prototype.activate.call(this);
+ if(activated) {
+ this.layer.events.on({
+ "moveend": this.update,
+ scope: this
+ });
+ this.layer.events.on({
+ "refresh": this.update,
+ scope: this
+ });
+ }
+ return activated;
+ },
+
+ /**
+ * Method: deactivate
+ * Tear down strategy with regard to reading new batches of remote data.
+ *
+ * Returns:
+ * {Boolean} The strategy was successfully deactivated.
+ */
+ deactivate: function() {
+ var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);
+ if(deactivated) {
+ this.layer.events.un({
+ "moveend": this.update,
+ scope: this
+ });
+ this.layer.events.un({
+ "refresh": this.update,
+ scope: this
+ });
+ }
+ return deactivated;
+ },
+
+ /**
+ * Method: update
+ * Callback function called on "moveend" or "refresh" layer events.
+ *
+ * Parameters:
+ * options - {Object} An object with a property named "force", this
+ * property references a boolean value indicating if new data
+ * must be incondtionally read.
+ */
+ update: function(options) {
+ var mapBounds = this.layer.map.getExtent();
+ if ((options && options.force) || this.invalidBounds(mapBounds)) {
+ this.calculateBounds(mapBounds);
+ this.triggerRead();
+ }
+ },
+
+ /**
+ * Method: invalidBounds
+ *
+ * Parameters:
+ * mapBounds - {} the current map extent, will be
+ * retrieved from the map object if not provided
+ *
+ * Returns:
+ * {Boolean}
+ */
+ invalidBounds: function(mapBounds) {
+ if(!mapBounds) {
+ mapBounds = this.layer.map.getExtent();
+ }
+ return !this.bounds || !this.bounds.containsBounds(mapBounds);
+ },
+
+ /**
+ * Method: calculateBounds
+ *
+ * Parameters:
+ * mapBounds - {} the current map extent, will be
+ * retrieved from the map object if not provided
+ */
+ calculateBounds: function(mapBounds) {
+ if(!mapBounds) {
+ mapBounds = this.layer.map.getExtent();
+ }
+ var center = mapBounds.getCenterLonLat();
+ var dataWidth = mapBounds.getWidth() * this.ratio;
+ var dataHeight = mapBounds.getHeight() * this.ratio;
+ this.bounds = new OpenLayers.Bounds(
+ center.lon - (dataWidth / 2),
+ center.lat - (dataHeight / 2),
+ center.lon + (dataWidth / 2),
+ center.lat + (dataHeight / 2)
+ );
+ },
+
+ /**
+ * Method: triggerRead
+ *
+ * Returns:
+ * {} The protocol response object
+ * returned by the layer protocol.
+ */
+ triggerRead: function() {
+ var filter = this.createFilter();
+ if (this.response && this.response.priv &&
+ typeof this.response.priv.abort == "function") {
+ this.response.priv.abort();
+ }
+ this.response = this.layer.protocol.read({
+ filter: filter,
+ callback: this.merge,
+ scope: this
+ });
+ },
+
+ /**
+ * Method: createFilter
+ *
+ * Returns
+ * {} The filter object.
+ */
+ createFilter: function() {
+ var filter = new OpenLayers.Filter.Spatial({
+ type: OpenLayers.Filter.Spatial.BBOX,
+ value: this.bounds,
+ projection: this.layer.projection
+ });
+ if (this.layer.filter) {
+ filter = new OpenLayers.Filter.Logical({
+ type: OpenLayers.Filter.Logical.AND,
+ filters: [this.layer.filter, filter]
+ });
+ }
+ return filter;
+ },
+
+ /**
+ * Method: merge
+ * Given a list of features, determine which ones to add to the layer.
+ *
+ * Parameters:
+ * resp - {} The response object passed
+ * by the protocol.
+ */
+ merge: function(resp) {
+ this.layer.destroyFeatures();
+ var features = resp.features;
+ if(features && features.length > 0) {
+ this.layer.addFeatures(features);
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Strategy.BBOX"
+});
diff --git a/tests/Protocol/HTTP.html b/tests/Protocol/HTTP.html
index 56a27b4476..72685969a8 100644
--- a/tests/Protocol/HTTP.html
+++ b/tests/Protocol/HTTP.html
@@ -104,6 +104,31 @@
var resp = protocol.read(readOptions);
}
+ function test_read_bbox(t) {
+ t.plan(1);
+ var protocol = new OpenLayers.Protocol.HTTP();
+
+ // fake XHR request object
+ var request = {'status': 200};
+
+ var _get = OpenLayers.Request.GET;
+
+ var bounds = new OpenLayers.Bounds(1, 2, 3, 4);
+ var filter = new OpenLayers.Filter.Spatial({
+ type: OpenLayers.Filter.Spatial.BBOX,
+ value: bounds,
+ projection: "foo"
+ });
+
+ OpenLayers.Request.GET = function(options) {
+ t.eq(options.params['bbox'].toString(), bounds.toArray().toString(),
+ 'GET called with bbox filter in params');
+ return request;
+ };
+
+ var resp = protocol.read({filter: filter});
+ }
+
function test_parseFeatures(t) {
t.plan(5);
diff --git a/tests/Strategy/BBOX.html b/tests/Strategy/BBOX.html
new file mode 100644
index 0000000000..bb019de260
--- /dev/null
+++ b/tests/Strategy/BBOX.html
@@ -0,0 +1,166 @@
+
+
+
+
+
+
+
+
+
diff --git a/tests/list-tests.html b/tests/list-tests.html
index 68cb559996..b762e8ddf0 100644
--- a/tests/list-tests.html
+++ b/tests/list-tests.html
@@ -125,6 +125,7 @@