|
/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for |
|
* full list of contributors). 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> |
|
*/ |
|
OpenLayers.Strategy.BBOX = OpenLayers.Class(OpenLayers.Strategy, { |
|
|
|
/** |
|
* Property: bounds |
|
* {<OpenLayers.Bounds>} The current data bounds (in the same projection |
|
* as the layer - not always the same projection as the map). |
|
*/ |
|
bounds: null, |
|
|
|
/** |
|
* Property: resolution |
|
* {Float} The current data resolution. |
|
*/ |
|
resolution: null, |
|
|
|
/** |
|
* APIProperty: ratio |
|
* {Float} The ratio of the data bounds to the viewport bounds (in each |
|
* dimension). Default is 2. |
|
*/ |
|
ratio: 2, |
|
|
|
/** |
|
* Property: resFactor |
|
* {Float} Optional factor used to determine when previously requested |
|
* features are invalid. If set, the resFactor will be compared to the |
|
* resolution of the previous request to the current map resolution. |
|
* If resFactor > (old / new) and 1/resFactor < (old / new). If you |
|
* set a resFactor of 1, data will be requested every time the |
|
* resolution changes. If you set a resFactor of 3, data will be |
|
* requested if the old resolution is 3 times the new, or if the new is |
|
* 3 times the old. If the old bounds do not contain the new bounds |
|
* new data will always be requested (with or without considering |
|
* resFactor). |
|
*/ |
|
resFactor: null, |
|
|
|
/** |
|
* Property: response |
|
* {<OpenLayers.Protocol.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.getMapBounds(); |
|
if ((options && options.force) || this.invalidBounds(mapBounds)) { |
|
this.calculateBounds(mapBounds); |
|
this.resolution = this.layer.map.getResolution(); |
|
this.triggerRead(); |
|
} |
|
}, |
|
|
|
/** |
|
* Method: getMapBounds |
|
* Get the map bounds expressed in the same projection as this layer. |
|
* |
|
* Returns: |
|
* {<OpenLayers.Bounds>} Map bounds in the projection of the layer. |
|
*/ |
|
getMapBounds: function() { |
|
var bounds = this.layer.map.getExtent(); |
|
if(!this.layer.projection.equals(this.layer.map.getProjectionObject())) { |
|
bounds = bounds.clone().transform( |
|
this.layer.map.getProjectionObject(), this.layer.projection |
|
); |
|
} |
|
return bounds; |
|
}, |
|
|
|
/** |
|
* Method: invalidBounds |
|
* Determine whether the previously requested set of features is invalid. |
|
* This occurs when the new map bounds do not contain the previously |
|
* requested bounds. In addition, if <resFactor> is set, it will be |
|
* considered. |
|
* |
|
* Parameters: |
|
* mapBounds - {<OpenLayers.Bounds>} the current map extent, will be |
|
* retrieved from the map object if not provided |
|
* |
|
* Returns: |
|
* {Boolean} |
|
*/ |
|
invalidBounds: function(mapBounds) { |
|
if(!mapBounds) { |
|
mapBounds = this.getMapBounds(); |
|
} |
|
var invalid = !this.bounds || !this.bounds.containsBounds(mapBounds); |
|
if(!invalid && this.resFactor) { |
|
var ratio = this.resolution / this.layer.map.getResolution(); |
|
invalid = (ratio >= this.resFactor || ratio <= (1 / this.resFactor)); |
|
} |
|
return invalid; |
|
}, |
|
|
|
/** |
|
* Method: calculateBounds |
|
* |
|
* Parameters: |
|
* mapBounds - {<OpenLayers.Bounds>} the current map extent, will be |
|
* retrieved from the map object if not provided |
|
*/ |
|
calculateBounds: function(mapBounds) { |
|
if(!mapBounds) { |
|
mapBounds = this.getMapBounds(); |
|
} |
|
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: |
|
* {<OpenLayers.Protocol.Response>} The protocol response object |
|
* returned by the layer protocol. |
|
*/ |
|
triggerRead: function() { |
|
if (this.response) { |
|
this.layer.protocol.abort(this.response); |
|
this.layer.events.triggerEvent("loadend"); |
|
} |
|
this.layer.events.triggerEvent("loadstart"); |
|
this.response = this.layer.protocol.read({ |
|
filter: this.createFilter(), |
|
callback: this.merge, |
|
scope: this |
|
}); |
|
}, |
|
|
|
/** |
|
* Method: createFilter |
|
* Creates a spatial BBOX filter. If the layer that this strategy belongs |
|
* to has a filter property, this filter will be combined with the BBOX |
|
* filter. |
|
* |
|
* Returns |
|
* {<OpenLayers.Filter>} 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. |
|
* If the layer projection differs from the map projection, features |
|
* will be transformed from the layer projection to the map projection. |
|
* |
|
* Parameters: |
|
* resp - {<OpenLayers.Protocol.Response>} The response object passed |
|
* by the protocol. |
|
*/ |
|
merge: function(resp) { |
|
this.layer.destroyFeatures(); |
|
var features = resp.features; |
|
if(features && features.length > 0) { |
|
var remote = this.layer.projection; |
|
var local = this.layer.map.getProjectionObject(); |
|
if(!local.equals(remote)) { |
|
var geom; |
|
for(var i=0, len=features.length; i<len; ++i) { |
|
geom = features[i].geometry; |
|
if(geom) { |
|
geom.transform(remote, local); |
|
} |
|
} |
|
} |
|
this.layer.addFeatures(features); |
|
} |
|
this.response = null; |
|
this.layer.events.triggerEvent("loadend"); |
|
}, |
|
|
|
CLASS_NAME: "OpenLayers.Strategy.BBOX" |
|
}); |
|
|