diff --git a/examples/point-grid.html b/examples/point-grid.html new file mode 100644 index 0000000000..4cff89d85b --- /dev/null +++ b/examples/point-grid.html @@ -0,0 +1,75 @@ + + + + + + OpenLayers Point Grid Example + + + + + +

Point Grid Example

+ +
+ point grid +
+ +
Use a PointGrid layer to display a grid of regularly spaced points
+ +
+ + Grid rotation: + + +   + Grid spacing: + x + + +   + Max points: + + +
+

+ This example demonstrates a OpenLayers.Layer.PointGrid + layer to render a regularly spaced grid of point features. +

+ See the + point-grid.js source to see how this is done. +

+
+ + + + diff --git a/examples/point-grid.js b/examples/point-grid.js new file mode 100644 index 0000000000..e3a29e89ca --- /dev/null +++ b/examples/point-grid.js @@ -0,0 +1,33 @@ +var points = new OpenLayers.Layer.PointGrid({ + isBaseLayer: true, dx: 15, dy: 15 +}); + +var map = new OpenLayers.Map({ + div: "map", + layers: [points], + center: new OpenLayers.LonLat(0, 0), + zoom: 2 +}); + +var rotation = document.getElementById("rotation"); +rotation.value = String(points.rotation); +rotation.onchange = function() { + points.setRotation(Number(rotation.value)); +} + +var dx = document.getElementById("dx"); +var dy = document.getElementById("dy"); +dx.value = String(points.dx); +dy.value = String(points.dy); +dx.onchange = function() { + points.setSpacing(Number(dx.value), Number(dy.value)); +} +dy.onchange = function() { + points.setSpacing(Number(dx.value), Number(dy.value)); +} + +var max = document.getElementById("max"); +max.value = String(points.maxFeatures); +max.onchange = function() { + points.setMaxFeatures(Number(max.value)); +} diff --git a/examples/snap-grid.html b/examples/snap-grid.html new file mode 100644 index 0000000000..b6b592d23e --- /dev/null +++ b/examples/snap-grid.html @@ -0,0 +1,78 @@ + + + + + + OpenLayers Snap Grid Example + + + + + +

Snap Grid Example

+ +
+ snap grid +
+ +
Use a PointGrid layer and a Snapping control to snap to a grid of regularly spaced points
+ +
+ + Grid rotation: + + +   + Grid spacing: + + +   + Max points: + + +
+

+ This example demonstrates feature editing with snapping to a regular + grid. The map is configured with a OpenLayers.Layer.PointGrid + layer and a OpenLayers.Control.Snapping agent. For the + best performance, the point grid layer should not made visible. + Snapping still works with layers that are not visible. +

+ See the + snap-grid.js source to see how this is done. +

+
+ + + + + diff --git a/examples/snap-grid.js b/examples/snap-grid.js new file mode 100644 index 0000000000..81a72dab11 --- /dev/null +++ b/examples/snap-grid.js @@ -0,0 +1,81 @@ +var points = new OpenLayers.Layer.PointGrid({ + name: "Snap Grid", + dx: 600, dy: 600, + styleMap: new OpenLayers.StyleMap({ + pointRadius: 1, + strokeColor: "#3333ff", + strokeWidth: 1, + fillOpacity: 1, + fillColor: "#ffffff", + graphicName: "square" + }) +}); + +var lines = new OpenLayers.Layer.Vector("Lines", { + styleMap: new OpenLayers.StyleMap({ + pointRadius: 3, + strokeColor: "#ff3300", + strokeWidth: 3, + fillOpacity: 0 + }) +}); + +var map = new OpenLayers.Map({ + div: "map", + layers: [new OpenLayers.Layer.OSM(), points, lines], + controls: [ + new OpenLayers.Control.Navigation(), + new OpenLayers.Control.LayerSwitcher(), + new OpenLayers.Control.Attribution() + ], + restrictedExtent: new OpenLayers.Bounds( + 1035374, 7448940, 1074510, 7468508 + ), + center: new OpenLayers.LonLat(1054942, 7458724), + zoom: 13 +}); + +// configure the snapping agent +var snap = new OpenLayers.Control.Snapping({ + layer: lines, + targets: [{ + layer: points, + tolerance: 15 + }] +}); +snap.activate(); + +// add some editing tools to a panel +var panel = new OpenLayers.Control.Panel({ + displayClass: "olControlEditingToolbar" +}); +var draw = new OpenLayers.Control.DrawFeature( + lines, OpenLayers.Handler.Path, + {displayClass: "olControlDrawFeaturePath", title: "Draw Features"} +); +modify = new OpenLayers.Control.ModifyFeature( + lines, {displayClass: "olControlModifyFeature", title: "Modify Features"} +); +panel.addControls([ + new OpenLayers.Control.Navigation({title: "Navigate"}), + modify, draw +]); +map.addControl(panel); + +var rotation = document.getElementById("rotation"); +rotation.value = String(points.rotation); +rotation.onchange = function() { + points.setRotation(Number(rotation.value)); +} + +var spacing = document.getElementById("spacing"); +spacing.value = String(points.dx); +spacing.onchange = function() { + points.setSpacing(Number(spacing.value)); +} + +var max = document.getElementById("max"); +max.value = String(points.maxFeatures); +max.onchange = function() { + points.setMaxFeatures(Number(max.value)); +} diff --git a/lib/OpenLayers.js b/lib/OpenLayers.js index fe395f7ddf..2351f0288c 100644 --- a/lib/OpenLayers.js +++ b/lib/OpenLayers.js @@ -232,6 +232,7 @@ "OpenLayers/Renderer/Canvas.js", "OpenLayers/Renderer/VML.js", "OpenLayers/Layer/Vector.js", + "OpenLayers/Layer/PointGrid.js", "OpenLayers/Layer/Vector/RootContainer.js", "OpenLayers/Strategy.js", "OpenLayers/Strategy/Filter.js", diff --git a/lib/OpenLayers/Layer/PointGrid.js b/lib/OpenLayers/Layer/PointGrid.js new file mode 100644 index 0000000000..7a82835d52 --- /dev/null +++ b/lib/OpenLayers/Layer/PointGrid.js @@ -0,0 +1,299 @@ +/* Copyright (c) 2006-2011 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/Layer/Vector.js + * @requires OpenLayers/Geometry/Polygon.js + */ + +/** + * Class: OpenLayers.Layer.PointGrid + * A point grid layer dynamically generates a regularly spaced grid of point + * features. This is a specialty layer for cases where an application needs + * a regular grid of points. It can be used, for example, in an editing + * environment to snap to a grid. + * + * Create a new vector layer with the constructor. + * (code) + * // create a grid with points spaced at 10 map units + * var points = new OpenLayers.Layer.PointGrid({dx: 10, dy: 10}); + * + * // create a grid with different x/y spacing rotated 15 degrees clockwise. + * var points = new OpenLayers.Layer.PointGrid({dx: 5, dy: 10, rotation: 15}); + * (end) + * + * Inherits from: + * - + */ +OpenLayers.Layer.PointGrid = OpenLayers.Class(OpenLayers.Layer.Vector, { + + /** + * APIProperty: dx + * {Number} Point grid spacing in the x-axis direction (map units). + * Read-only. Use the method to modify this value. + */ + dx: null, + + /** + * APIProperty: dy + * {Number} Point grid spacing in the y-axis direction (map units). + * Read-only. Use the method to modify this value. + */ + dy: null, + + /** + * APIProperty: ratio + * {Number} Ratio of the desired grid size to the map viewport size. + * Default is 1.5. Larger ratios mean the grid is recalculated less often + * while panning. The setting has precedence when determining + * grid size. Read-only. Use the method to modify this value. + */ + ratio: 1.5, + + /** + * APIProperty: maxFeatures + * {Number} The maximum number of points to generate in the grid. Default + * is 250. Read-only. Use the method to modify this value. + */ + maxFeatures: 250, + + /** + * APIProperty: rotation + * {Number} Grid rotation (in degrees clockwise from the positive x-axis). + * Default is 0. Read-only. Use the method to modify this + * value. + */ + rotation: 0, + + /** + * APIProperty: origin + * {OpenLayers.LonLat} Grid origin. The grid lattice will be aligned with + * the origin. If not set at construction, the center of the map's maximum + * extent is used. Read-only. Use the method to modify this + * value. + */ + origin: null, + + /** + * Property: gridBounds + * {} Internally cached grid bounds (with optional + * rotation applied). + */ + gridBounds: null, + + /** + * Constructor: OpenLayers.Layer.PointGrid + * Creates a new point grid layer. + * + * Parameters: + * config - {Object} An object containing all configuration properties for + * the layer. The and properties are required to be set at + * construction. Any other layer properties may be set in this object. + */ + initialize: function(config) { + config = config || {}; + OpenLayers.Layer.Vector.prototype.initialize.apply(this, [config.name, config]); + }, + + /** + * Method: setMap + * The layer has been added to the map. + * + * Parameters: + * map - {} + */ + setMap: function(map) { + OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments); + map.events.register("moveend", this, this.onMoveEnd); + }, + + /** + * Method: removeMap + * The layer has been removed from the map. + * + * Parameters: + * map - {} + */ + removeMap: function(map) { + map.events.unregister("moveend", this, this.onMoveEnd); + OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments); + }, + + /** + * APIMethod: setRatio + * Set the grid property and update the grid. Can only be called + * after the layer has been added to a map with a center/extent. + * + * Parameters: + * ratio - {Number} + */ + setRatio: function(ratio) { + this.ratio = ratio; + this.updateGrid(true); + }, + + /** + * APIMethod: setMaxFeatures + * Set the grid property and update the grid. Can only be + * called after the layer has been added to a map with a center/extent. + * + * Parameters: + * maxFeatures - {Number} + */ + setMaxFeatures: function(maxFeatures) { + this.maxFeatures = maxFeatures; + this.updateGrid(true); + }, + + /** + * APIMethod: setSpacing + * Set the grid and properties and update the grid. If only one + * argument is provided, it will be set as and . Can only be + * called after the layer has been added to a map with a center/extent. + * + * Parameters: + * dx - {Number} + * dy - {Number} + */ + setSpacing: function(dx, dy) { + this.dx = dx; + this.dy = dy || dx; + this.updateGrid(true); + }, + + /** + * APIMethod: setOrigin + * Set the grid property and update the grid. Can only be called + * after the layer has been added to a map with a center/extent. + * + * Parameters: + * origin - {} + */ + setOrigin: function(origin) { + this.origin = origin; + this.updateGrid(true); + }, + + /** + * APIMethod: getOrigin + * Get the grid property. + * + * Returns: + * {} The grid origin. + */ + getOrigin: function() { + if (!this.origin) { + this.origin = this.map.getExtent().getCenterLonLat(); + } + return this.origin; + }, + + /** + * APIMethod: setRotation + * Set the grid property and update the grid. Rotation values + * are in degrees clockwise from the positive x-axis (negative values + * for counter-clockwise rotation). Can only be called after the layer + * has been added to a map with a center/extent. + * + * Parameters: + * rotation - {Number} Degrees clockwise from the positive x-axis. + */ + setRotation: function(rotation) { + this.rotation = rotation; + this.updateGrid(true); + }, + + /** + * Method: onMoveEnd + * Listener for map "moveend" events. + */ + onMoveEnd: function() { + this.updateGrid(); + }, + + /** + * Method: getViewBounds + * Gets the (potentially rotated) view bounds for grid calculations. + * + * Returns: + * {} + */ + getViewBounds: function() { + var bounds = this.map.getExtent(); + if (this.rotation) { + var origin = this.getOrigin(); + var rotationOrigin = new OpenLayers.Geometry.Point(origin.lon, origin.lat); + var rect = bounds.toGeometry(); + rect.rotate(-this.rotation, rotationOrigin); + bounds = rect.getBounds(); + } + return bounds; + }, + + /** + * Method: updateGrid + * Update the grid. + * + * Parameters: + * force - {Boolean} Update the grid even if the previous bounds are still + * valid. + */ + updateGrid: function(force) { + if (force || this.invalidBounds()) { + var viewBounds = this.getViewBounds(); + var origin = this.getOrigin(); + var rotationOrigin = new OpenLayers.Geometry.Point(origin.lon, origin.lat); + var viewBoundsWidth = viewBounds.getWidth(); + var viewBoundsHeight = viewBounds.getHeight(); + var aspectRatio = viewBoundsWidth / viewBoundsHeight; + var maxHeight = Math.sqrt(this.dx * this.dy * this.maxFeatures / aspectRatio); + var maxWidth = maxHeight * aspectRatio; + var gridWidth = Math.min(viewBoundsWidth * this.ratio, maxWidth); + var gridHeight = Math.min(viewBoundsHeight * this.ratio, maxHeight); + var center = viewBounds.getCenterLonLat(); + this.gridBounds = new OpenLayers.Bounds( + center.lon - (gridWidth / 2), + center.lat - (gridHeight / 2), + center.lon + (gridWidth / 2), + center.lat + (gridHeight / 2) + ); + var rows = Math.floor(gridHeight / this.dy); + var cols = Math.floor(gridWidth / this.dx); + var gridLeft = origin.lon + (this.dx * Math.ceil((this.gridBounds.left - origin.lon) / this.dx)); + var gridBottom = origin.lat + (this.dy * Math.ceil((this.gridBounds.bottom - origin.lat) / this.dy)); + var features = new Array(rows * cols); + var x, y, point; + for (var i=0; i + + + + + + +
+ + diff --git a/tests/list-tests.html b/tests/list-tests.html index 3f378098f1..a56c678896 100644 --- a/tests/list-tests.html +++ b/tests/list-tests.html @@ -156,6 +156,7 @@
  • Layer/MapServer.html
  • Layer/Markers.html
  • Layer/MultiMap.html
  • +
  • Layer/PointGrid.html
  • Layer/PointTrack.html
  • Layer/SphericalMercator.html
  • Layer/Text.html