BBOX.js 9.3 KB

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