PointGrid.js 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  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/Layer/Vector.js
  7. * @requires OpenLayers/Geometry/Polygon.js
  8. */
  9. /**
  10. * Class: OpenLayers.Layer.PointGrid
  11. * A point grid layer dynamically generates a regularly spaced grid of point
  12. * features. This is a specialty layer for cases where an application needs
  13. * a regular grid of points. It can be used, for example, in an editing
  14. * environment to snap to a grid.
  15. *
  16. * Create a new vector layer with the <OpenLayers.Layer.PointGrid> constructor.
  17. * (code)
  18. * // create a grid with points spaced at 10 map units
  19. * var points = new OpenLayers.Layer.PointGrid({dx: 10, dy: 10});
  20. *
  21. * // create a grid with different x/y spacing rotated 15 degrees clockwise.
  22. * var points = new OpenLayers.Layer.PointGrid({dx: 5, dy: 10, rotation: 15});
  23. * (end)
  24. *
  25. * Inherits from:
  26. * - <OpenLayers.Layer.Vector>
  27. */
  28. OpenLayers.Layer.PointGrid = OpenLayers.Class(OpenLayers.Layer.Vector, {
  29. /**
  30. * APIProperty: dx
  31. * {Number} Point grid spacing in the x-axis direction (map units).
  32. * Read-only. Use the <setSpacing> method to modify this value.
  33. */
  34. dx: null,
  35. /**
  36. * APIProperty: dy
  37. * {Number} Point grid spacing in the y-axis direction (map units).
  38. * Read-only. Use the <setSpacing> method to modify this value.
  39. */
  40. dy: null,
  41. /**
  42. * APIProperty: ratio
  43. * {Number} Ratio of the desired grid size to the map viewport size.
  44. * Default is 1.5. Larger ratios mean the grid is recalculated less often
  45. * while panning. The <maxFeatures> setting has precedence when determining
  46. * grid size. Read-only. Use the <setRatio> method to modify this value.
  47. */
  48. ratio: 1.5,
  49. /**
  50. * APIProperty: maxFeatures
  51. * {Number} The maximum number of points to generate in the grid. Default
  52. * is 250. Read-only. Use the <setMaxFeatures> method to modify this value.
  53. */
  54. maxFeatures: 250,
  55. /**
  56. * APIProperty: rotation
  57. * {Number} Grid rotation (in degrees clockwise from the positive x-axis).
  58. * Default is 0. Read-only. Use the <setRotation> method to modify this
  59. * value.
  60. */
  61. rotation: 0,
  62. /**
  63. * APIProperty: origin
  64. * {OpenLayers.LonLat} Grid origin. The grid lattice will be aligned with
  65. * the origin. If not set at construction, the center of the map's maximum
  66. * extent is used. Read-only. Use the <setOrigin> method to modify this
  67. * value.
  68. */
  69. origin: null,
  70. /**
  71. * Property: gridBounds
  72. * {<OpenLayers.Bounds>} Internally cached grid bounds (with optional
  73. * rotation applied).
  74. */
  75. gridBounds: null,
  76. /**
  77. * Constructor: OpenLayers.Layer.PointGrid
  78. * Creates a new point grid layer.
  79. *
  80. * Parameters:
  81. * config - {Object} An object containing all configuration properties for
  82. * the layer. The <dx> and <dy> properties are required to be set at
  83. * construction. Any other layer properties may be set in this object.
  84. */
  85. initialize: function(config) {
  86. config = config || {};
  87. OpenLayers.Layer.Vector.prototype.initialize.apply(this, [config.name, config]);
  88. },
  89. /**
  90. * Method: setMap
  91. * The layer has been added to the map.
  92. *
  93. * Parameters:
  94. * map - {<OpenLayers.Map>}
  95. */
  96. setMap: function(map) {
  97. OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);
  98. map.events.register("moveend", this, this.onMoveEnd);
  99. },
  100. /**
  101. * Method: removeMap
  102. * The layer has been removed from the map.
  103. *
  104. * Parameters:
  105. * map - {<OpenLayers.Map>}
  106. */
  107. removeMap: function(map) {
  108. map.events.unregister("moveend", this, this.onMoveEnd);
  109. OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments);
  110. },
  111. /**
  112. * APIMethod: setRatio
  113. * Set the grid <ratio> property and update the grid. Can only be called
  114. * after the layer has been added to a map with a center/extent.
  115. *
  116. * Parameters:
  117. * ratio - {Number}
  118. */
  119. setRatio: function(ratio) {
  120. this.ratio = ratio;
  121. this.updateGrid(true);
  122. },
  123. /**
  124. * APIMethod: setMaxFeatures
  125. * Set the grid <maxFeatures> property and update the grid. Can only be
  126. * called after the layer has been added to a map with a center/extent.
  127. *
  128. * Parameters:
  129. * maxFeatures - {Number}
  130. */
  131. setMaxFeatures: function(maxFeatures) {
  132. this.maxFeatures = maxFeatures;
  133. this.updateGrid(true);
  134. },
  135. /**
  136. * APIMethod: setSpacing
  137. * Set the grid <dx> and <dy> properties and update the grid. If only one
  138. * argument is provided, it will be set as <dx> and <dy>. Can only be
  139. * called after the layer has been added to a map with a center/extent.
  140. *
  141. * Parameters:
  142. * dx - {Number}
  143. * dy - {Number}
  144. */
  145. setSpacing: function(dx, dy) {
  146. this.dx = dx;
  147. this.dy = dy || dx;
  148. this.updateGrid(true);
  149. },
  150. /**
  151. * APIMethod: setOrigin
  152. * Set the grid <origin> property and update the grid. Can only be called
  153. * after the layer has been added to a map with a center/extent.
  154. *
  155. * Parameters:
  156. * origin - {<OpenLayers.LonLat>}
  157. */
  158. setOrigin: function(origin) {
  159. this.origin = origin;
  160. this.updateGrid(true);
  161. },
  162. /**
  163. * APIMethod: getOrigin
  164. * Get the grid <origin> property.
  165. *
  166. * Returns:
  167. * {<OpenLayers.LonLat>} The grid origin.
  168. */
  169. getOrigin: function() {
  170. if (!this.origin) {
  171. this.origin = this.map.getExtent().getCenterLonLat();
  172. }
  173. return this.origin;
  174. },
  175. /**
  176. * APIMethod: setRotation
  177. * Set the grid <rotation> property and update the grid. Rotation values
  178. * are in degrees clockwise from the positive x-axis (negative values
  179. * for counter-clockwise rotation). Can only be called after the layer
  180. * has been added to a map with a center/extent.
  181. *
  182. * Parameters:
  183. * rotation - {Number} Degrees clockwise from the positive x-axis.
  184. */
  185. setRotation: function(rotation) {
  186. this.rotation = rotation;
  187. this.updateGrid(true);
  188. },
  189. /**
  190. * Method: onMoveEnd
  191. * Listener for map "moveend" events.
  192. */
  193. onMoveEnd: function() {
  194. this.updateGrid();
  195. },
  196. /**
  197. * Method: getViewBounds
  198. * Gets the (potentially rotated) view bounds for grid calculations.
  199. *
  200. * Returns:
  201. * {<OpenLayers.Bounds>}
  202. */
  203. getViewBounds: function() {
  204. var bounds = this.map.getExtent();
  205. if (this.rotation) {
  206. var origin = this.getOrigin();
  207. var rotationOrigin = new OpenLayers.Geometry.Point(origin.lon, origin.lat);
  208. var rect = bounds.toGeometry();
  209. rect.rotate(-this.rotation, rotationOrigin);
  210. bounds = rect.getBounds();
  211. }
  212. return bounds;
  213. },
  214. /**
  215. * Method: updateGrid
  216. * Update the grid.
  217. *
  218. * Parameters:
  219. * force - {Boolean} Update the grid even if the previous bounds are still
  220. * valid.
  221. */
  222. updateGrid: function(force) {
  223. if (force || this.invalidBounds()) {
  224. var viewBounds = this.getViewBounds();
  225. var origin = this.getOrigin();
  226. var rotationOrigin = new OpenLayers.Geometry.Point(origin.lon, origin.lat);
  227. var viewBoundsWidth = viewBounds.getWidth();
  228. var viewBoundsHeight = viewBounds.getHeight();
  229. var aspectRatio = viewBoundsWidth / viewBoundsHeight;
  230. var maxHeight = Math.sqrt(this.dx * this.dy * this.maxFeatures / aspectRatio);
  231. var maxWidth = maxHeight * aspectRatio;
  232. var gridWidth = Math.min(viewBoundsWidth * this.ratio, maxWidth);
  233. var gridHeight = Math.min(viewBoundsHeight * this.ratio, maxHeight);
  234. var center = viewBounds.getCenterLonLat();
  235. this.gridBounds = new OpenLayers.Bounds(
  236. center.lon - (gridWidth / 2),
  237. center.lat - (gridHeight / 2),
  238. center.lon + (gridWidth / 2),
  239. center.lat + (gridHeight / 2)
  240. );
  241. var rows = Math.floor(gridHeight / this.dy);
  242. var cols = Math.floor(gridWidth / this.dx);
  243. var gridLeft = origin.lon + (this.dx * Math.ceil((this.gridBounds.left - origin.lon) / this.dx));
  244. var gridBottom = origin.lat + (this.dy * Math.ceil((this.gridBounds.bottom - origin.lat) / this.dy));
  245. var features = new Array(rows * cols);
  246. var x, y, point;
  247. for (var i=0; i<cols; ++i) {
  248. x = gridLeft + (i * this.dx);
  249. for (var j=0; j<rows; ++j) {
  250. y = gridBottom + (j * this.dy);
  251. point = new OpenLayers.Geometry.Point(x, y);
  252. if (this.rotation) {
  253. point.rotate(this.rotation, rotationOrigin);
  254. }
  255. features[(i*rows)+j] = new OpenLayers.Feature.Vector(point);
  256. }
  257. }
  258. this.destroyFeatures(this.features, {silent: true});
  259. this.addFeatures(features, {silent: true});
  260. }
  261. },
  262. /**
  263. * Method: invalidBounds
  264. * Determine whether the previously generated point grid is invalid.
  265. * This occurs when the map bounds extends beyond the previously
  266. * generated grid bounds.
  267. *
  268. * Returns:
  269. * {Boolean}
  270. */
  271. invalidBounds: function() {
  272. return !this.gridBounds || !this.gridBounds.containsBounds(this.getViewBounds());
  273. },
  274. CLASS_NAME: "OpenLayers.Layer.PointGrid"
  275. });