]> dev.renevier.net Git - syp.git/blob - openlayers/lib/OpenLayers/Strategy/BBOX.js
initial commit
[syp.git] / openlayers / lib / OpenLayers / Strategy / BBOX.js
1 /* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
2  * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
3  * full text of the license. */
4
5 /**
6  * @requires OpenLayers/Strategy.js
7  * @requires OpenLayers/Filter/Spatial.js
8  */
9
10 /**
11  * Class: OpenLayers.Strategy.BBOX
12  * A simple strategy that reads new features when the viewport invalidates
13  *     some bounds.
14  *
15  * Inherits from:
16  *  - <OpenLayers.Strategy>
17  */
18 OpenLayers.Strategy.BBOX = OpenLayers.Class(OpenLayers.Strategy, {
19     
20     /**
21      * Property: bounds
22      * {<OpenLayers.Bounds>} The current data bounds (in the same projection
23      *     as the layer - not always the same projection as the map).
24      */
25     bounds: null,
26     
27     /** 
28      * Property: resolution 
29      * {Float} The current data resolution. 
30      */ 
31     resolution: null, 
32            
33     /**
34      * APIProperty: ratio
35      * {Float} The ratio of the data bounds to the viewport bounds (in each
36      *     dimension).  Default is 2.
37      */
38     ratio: 2,
39
40     /** 
41      * Property: resFactor 
42      * {Float} Optional factor used to determine when previously requested 
43      *     features are invalid.  If set, the resFactor will be compared to the
44      *     resolution of the previous request to the current map resolution.
45      *     If resFactor > (old / new) and 1/resFactor < (old / new).  If you
46      *     set a resFactor of 1, data will be requested every time the
47      *     resolution changes.  If you set a resFactor of 3, data will be
48      *     requested if the old resolution is 3 times the new, or if the new is
49      *     3 times the old.  If the old bounds do not contain the new bounds
50      *     new data will always be requested (with or without considering
51      *     resFactor). 
52      */ 
53     resFactor: null, 
54     
55     /**
56      * Property: response
57      * {<OpenLayers.Protocol.Response>} The protocol response object returned
58      *      by the layer protocol.
59      */
60     response: null,
61
62     /**
63      * Constructor: OpenLayers.Strategy.BBOX
64      * Create a new BBOX strategy.
65      *
66      * Parameters:
67      * options - {Object} Optional object whose properties will be set on the
68      *     instance.
69      */
70     initialize: function(options) {
71         OpenLayers.Strategy.prototype.initialize.apply(this, [options]);
72     },
73     
74     /**
75      * Method: activate
76      * Set up strategy with regard to reading new batches of remote data.
77      * 
78      * Returns:
79      * {Boolean} The strategy was successfully activated.
80      */
81     activate: function() {
82         var activated = OpenLayers.Strategy.prototype.activate.call(this);
83         if(activated) {
84             this.layer.events.on({
85                 "moveend": this.update,
86                 scope: this
87             });
88             this.layer.events.on({
89                 "refresh": this.update,
90                 scope: this
91             });
92         }
93         return activated;
94     },
95     
96     /**
97      * Method: deactivate
98      * Tear down strategy with regard to reading new batches of remote data.
99      * 
100      * Returns:
101      * {Boolean} The strategy was successfully deactivated.
102      */
103     deactivate: function() {
104         var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);
105         if(deactivated) {
106             this.layer.events.un({
107                 "moveend": this.update,
108                 scope: this
109             });
110             this.layer.events.un({
111                 "refresh": this.update,
112                 scope: this
113             });
114         }
115         return deactivated;
116     },
117
118     /**
119      * Method: update
120      * Callback function called on "moveend" or "refresh" layer events.
121      *
122      * Parameters:
123      * options - {Object} An object with a property named "force", this
124      *      property references a boolean value indicating if new data
125      *      must be incondtionally read.
126      */
127     update: function(options) {
128         var mapBounds = this.getMapBounds();
129         if ((options && options.force) || this.invalidBounds(mapBounds)) {
130             this.calculateBounds(mapBounds);
131             this.resolution = this.layer.map.getResolution(); 
132             this.triggerRead();
133         }
134     },
135     
136     /**
137      * Method: getMapBounds
138      * Get the map bounds expressed in the same projection as this layer.
139      *
140      * Returns:
141      * {<OpenLayers.Bounds>} Map bounds in the projection of the layer.
142      */
143     getMapBounds: function() {
144         var bounds = this.layer.map.getExtent();
145         if(!this.layer.projection.equals(this.layer.map.getProjectionObject())) {
146             bounds = bounds.clone().transform(
147                 this.layer.map.getProjectionObject(), this.layer.projection
148             );
149         }
150         return bounds;
151     },
152
153     /**
154      * Method: invalidBounds
155      * Determine whether the previously requested set of features is invalid. 
156      *     This occurs when the new map bounds do not contain the previously 
157      *     requested bounds.  In addition, if <resFactor> is set, it will be 
158      *     considered.
159      *
160      * Parameters:
161      * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be
162      *      retrieved from the map object if not provided
163      *
164      * Returns:
165      * {Boolean} 
166      */
167     invalidBounds: function(mapBounds) {
168         if(!mapBounds) {
169             mapBounds = this.getMapBounds();
170         }
171         var invalid = !this.bounds || !this.bounds.containsBounds(mapBounds);
172         if(!invalid && this.resFactor) {
173             var ratio = this.resolution / this.layer.map.getResolution();
174             invalid = (ratio >= this.resFactor || ratio <= (1 / this.resFactor));
175         }
176         return invalid;
177     },
178  
179     /**
180      * Method: calculateBounds
181      *
182      * Parameters:
183      * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be
184      *      retrieved from the map object if not provided
185      */
186     calculateBounds: function(mapBounds) {
187         if(!mapBounds) {
188             mapBounds = this.getMapBounds();
189         }
190         var center = mapBounds.getCenterLonLat();
191         var dataWidth = mapBounds.getWidth() * this.ratio;
192         var dataHeight = mapBounds.getHeight() * this.ratio;
193         this.bounds = new OpenLayers.Bounds(
194             center.lon - (dataWidth / 2),
195             center.lat - (dataHeight / 2),
196             center.lon + (dataWidth / 2),
197             center.lat + (dataHeight / 2)
198         );
199     },
200     
201     /**
202      * Method: triggerRead
203      *
204      * Returns:
205      * {<OpenLayers.Protocol.Response>} The protocol response object
206      *      returned by the layer protocol.
207      */
208     triggerRead: function() {
209         this.layer.protocol.abort(this.response);
210         this.layer.events.triggerEvent("loadstart");
211         this.response = this.layer.protocol.read({
212             filter: this.createFilter(),
213             callback: this.merge,
214             scope: this
215         });
216     },
217  
218     /**
219      * Method: createFilter
220      *
221      * Returns
222      * {<OpenLayers.Filter>} The filter object.
223      */
224     createFilter: function() {
225         var filter = new OpenLayers.Filter.Spatial({
226             type: OpenLayers.Filter.Spatial.BBOX,
227             value: this.bounds,
228             projection: this.layer.projection
229         });
230         if (this.layer.filter) {
231             filter = new OpenLayers.Filter.Logical({
232                 type: OpenLayers.Filter.Logical.AND,
233                 filters: [this.layer.filter, filter]
234             });
235         }
236         return filter;
237     },
238    
239     /**
240      * Method: merge
241      * Given a list of features, determine which ones to add to the layer.
242      *     If the layer projection differs from the map projection, features
243      *     will be transformed from the layer projection to the map projection.
244      *
245      * Parameters:
246      * resp - {<OpenLayers.Protocol.Response>} The response object passed
247      *      by the protocol.
248      */
249     merge: function(resp) {
250         this.layer.destroyFeatures();
251         var features = resp.features;
252         if(features && features.length > 0) {
253             var remote = this.layer.projection;
254             var local = this.layer.map.getProjectionObject();
255             if(!local.equals(remote)) {
256                 var geom;
257                 for(var i=0, len=features.length; i<len; ++i) {
258                     geom = features[i].geometry;
259                     if(geom) {
260                         geom.transform(remote, local);
261                     }
262                 }
263             }
264             this.layer.addFeatures(features);
265         }
266         this.layer.events.triggerEvent("loadend");
267     },
268    
269     CLASS_NAME: "OpenLayers.Strategy.BBOX" 
270 });