summaryrefslogtreecommitdiff
path: root/misc/openlayers/lib/OpenLayers/Control/ModifyFeature.js
diff options
context:
space:
mode:
Diffstat (limited to 'misc/openlayers/lib/OpenLayers/Control/ModifyFeature.js')
-rw-r--r--misc/openlayers/lib/OpenLayers/Control/ModifyFeature.js835
1 files changed, 835 insertions, 0 deletions
diff --git a/misc/openlayers/lib/OpenLayers/Control/ModifyFeature.js b/misc/openlayers/lib/OpenLayers/Control/ModifyFeature.js
new file mode 100644
index 0000000..e574608
--- /dev/null
+++ b/misc/openlayers/lib/OpenLayers/Control/ModifyFeature.js
@@ -0,0 +1,835 @@
+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
+ * full list of contributors). Published under the 2-clause BSD license.
+ * See license.txt in the OpenLayers distribution or repository for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Control.js
+ * @requires OpenLayers/Handler/Drag.js
+ * @requires OpenLayers/Handler/Keyboard.js
+ */
+
+/**
+ * Class: OpenLayers.Control.ModifyFeature
+ * Control to modify features. When activated, a click renders the vertices
+ * of a feature - these vertices can then be dragged. By default, the
+ * delete key will delete the vertex under the mouse. New features are
+ * added by dragging "virtual vertices" between vertices. Create a new
+ * control with the <OpenLayers.Control.ModifyFeature> constructor.
+ *
+ * Inherits From:
+ * - <OpenLayers.Control>
+ */
+OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, {
+
+ /**
+ * APIProperty: documentDrag
+ * {Boolean} If set to true, dragging vertices will continue even if the
+ * mouse cursor leaves the map viewport. Default is false.
+ */
+ documentDrag: false,
+
+ /**
+ * APIProperty: geometryTypes
+ * {Array(String)} To restrict modification to a limited set of geometry
+ * types, send a list of strings corresponding to the geometry class
+ * names.
+ */
+ geometryTypes: null,
+
+ /**
+ * APIProperty: clickout
+ * {Boolean} Unselect features when clicking outside any feature.
+ * Default is true.
+ */
+ clickout: true,
+
+ /**
+ * APIProperty: toggle
+ * {Boolean} Unselect a selected feature on click.
+ * Default is true.
+ */
+ toggle: true,
+
+ /**
+ * APIProperty: standalone
+ * {Boolean} Set to true to create a control without SelectFeature
+ * capabilities. Default is false. If standalone is true, to modify
+ * a feature, call the <selectFeature> method with the target feature.
+ * Note that you must call the <unselectFeature> method to finish
+ * feature modification in standalone mode (before starting to modify
+ * another feature).
+ */
+ standalone: false,
+
+ /**
+ * Property: layer
+ * {<OpenLayers.Layer.Vector>}
+ */
+ layer: null,
+
+ /**
+ * Property: feature
+ * {<OpenLayers.Feature.Vector>} Feature currently available for modification.
+ */
+ feature: null,
+
+ /**
+ * Property: vertex
+ * {<OpenLayers.Feature.Vector>} Vertex currently being modified.
+ */
+ vertex: null,
+
+ /**
+ * Property: vertices
+ * {Array(<OpenLayers.Feature.Vector>)} Verticies currently available
+ * for dragging.
+ */
+ vertices: null,
+
+ /**
+ * Property: virtualVertices
+ * {Array(<OpenLayers.Feature.Vector>)} Virtual vertices in the middle
+ * of each edge.
+ */
+ virtualVertices: null,
+
+ /**
+ * Property: handlers
+ * {Object}
+ */
+ handlers: null,
+
+ /**
+ * APIProperty: deleteCodes
+ * {Array(Integer)} Keycodes for deleting verticies. Set to null to disable
+ * vertex deltion by keypress. If non-null, keypresses with codes
+ * in this array will delete vertices under the mouse. Default
+ * is 46 and 68, the 'delete' and lowercase 'd' keys.
+ */
+ deleteCodes: null,
+
+ /**
+ * APIProperty: virtualStyle
+ * {Object} A symbolizer to be used for virtual vertices.
+ */
+ virtualStyle: null,
+
+ /**
+ * APIProperty: vertexRenderIntent
+ * {String} The renderIntent to use for vertices. If no <virtualStyle> is
+ * provided, this renderIntent will also be used for virtual vertices, with
+ * a fillOpacity and strokeOpacity of 0.3. Default is null, which means
+ * that the layer's default style will be used for vertices.
+ */
+ vertexRenderIntent: null,
+
+ /**
+ * APIProperty: mode
+ * {Integer} Bitfields specifying the modification mode. Defaults to
+ * OpenLayers.Control.ModifyFeature.RESHAPE. To set the mode to a
+ * combination of options, use the | operator. For example, to allow
+ * the control to both resize and rotate features, use the following
+ * syntax
+ * (code)
+ * control.mode = OpenLayers.Control.ModifyFeature.RESIZE |
+ * OpenLayers.Control.ModifyFeature.ROTATE;
+ * (end)
+ */
+ mode: null,
+
+ /**
+ * APIProperty: createVertices
+ * {Boolean} Create new vertices by dragging the virtual vertices
+ * in the middle of each edge. Default is true.
+ */
+ createVertices: true,
+
+ /**
+ * Property: modified
+ * {Boolean} The currently selected feature has been modified.
+ */
+ modified: false,
+
+ /**
+ * Property: radiusHandle
+ * {<OpenLayers.Feature.Vector>} A handle for rotating/resizing a feature.
+ */
+ radiusHandle: null,
+
+ /**
+ * Property: dragHandle
+ * {<OpenLayers.Feature.Vector>} A handle for dragging a feature.
+ */
+ dragHandle: null,
+
+ /**
+ * APIProperty: onModificationStart
+ * {Function} *Deprecated*. Register for "beforefeaturemodified" instead.
+ * The "beforefeaturemodified" event is triggered on the layer before
+ * any modification begins.
+ *
+ * Optional function to be called when a feature is selected
+ * to be modified. The function should expect to be called with a
+ * feature. This could be used for example to allow to lock the
+ * feature on server-side.
+ */
+ onModificationStart: function() {},
+
+ /**
+ * APIProperty: onModification
+ * {Function} *Deprecated*. Register for "featuremodified" instead.
+ * The "featuremodified" event is triggered on the layer with each
+ * feature modification.
+ *
+ * Optional function to be called when a feature has been
+ * modified. The function should expect to be called with a feature.
+ */
+ onModification: function() {},
+
+ /**
+ * APIProperty: onModificationEnd
+ * {Function} *Deprecated*. Register for "afterfeaturemodified" instead.
+ * The "afterfeaturemodified" event is triggered on the layer after
+ * a feature has been modified.
+ *
+ * Optional function to be called when a feature is finished
+ * being modified. The function should expect to be called with a
+ * feature.
+ */
+ onModificationEnd: function() {},
+
+ /**
+ * Constructor: OpenLayers.Control.ModifyFeature
+ * Create a new modify feature control.
+ *
+ * Parameters:
+ * layer - {<OpenLayers.Layer.Vector>} Layer that contains features that
+ * will be modified.
+ * options - {Object} Optional object whose properties will be set on the
+ * control.
+ */
+ initialize: function(layer, options) {
+ options = options || {};
+ this.layer = layer;
+ this.vertices = [];
+ this.virtualVertices = [];
+ this.virtualStyle = OpenLayers.Util.extend({},
+ this.layer.style ||
+ this.layer.styleMap.createSymbolizer(null, options.vertexRenderIntent)
+ );
+ this.virtualStyle.fillOpacity = 0.3;
+ this.virtualStyle.strokeOpacity = 0.3;
+ this.deleteCodes = [46, 68];
+ this.mode = OpenLayers.Control.ModifyFeature.RESHAPE;
+ OpenLayers.Control.prototype.initialize.apply(this, [options]);
+ if(!(OpenLayers.Util.isArray(this.deleteCodes))) {
+ this.deleteCodes = [this.deleteCodes];
+ }
+
+ // configure the drag handler
+ var dragCallbacks = {
+ down: function(pixel) {
+ this.vertex = null;
+ var feature = this.layer.getFeatureFromEvent(
+ this.handlers.drag.evt);
+ if (feature) {
+ this.dragStart(feature);
+ } else if (this.clickout) {
+ this._unselect = this.feature;
+ }
+ },
+ move: function(pixel) {
+ delete this._unselect;
+ if (this.vertex) {
+ this.dragVertex(this.vertex, pixel);
+ }
+ },
+ up: function() {
+ this.handlers.drag.stopDown = false;
+ if (this._unselect) {
+ this.unselectFeature(this._unselect);
+ delete this._unselect;
+ }
+ },
+ done: function(pixel) {
+ if (this.vertex) {
+ this.dragComplete(this.vertex);
+ }
+ }
+ };
+ var dragOptions = {
+ documentDrag: this.documentDrag,
+ stopDown: false
+ };
+
+ // configure the keyboard handler
+ var keyboardOptions = {
+ keydown: this.handleKeypress
+ };
+ this.handlers = {
+ keyboard: new OpenLayers.Handler.Keyboard(this, keyboardOptions),
+ drag: new OpenLayers.Handler.Drag(this, dragCallbacks, dragOptions)
+ };
+ },
+
+ /**
+ * APIMethod: destroy
+ * Take care of things that are not handled in superclass.
+ */
+ destroy: function() {
+ if (this.map) {
+ this.map.events.un({
+ "removelayer": this.handleMapEvents,
+ "changelayer": this.handleMapEvents,
+ scope: this
+ });
+ }
+ this.layer = null;
+ OpenLayers.Control.prototype.destroy.apply(this, []);
+ },
+
+ /**
+ * APIMethod: activate
+ * Activate the control.
+ *
+ * Returns:
+ * {Boolean} Successfully activated the control.
+ */
+ activate: function() {
+ this.moveLayerToTop();
+ this.map.events.on({
+ "removelayer": this.handleMapEvents,
+ "changelayer": this.handleMapEvents,
+ scope: this
+ });
+ return (this.handlers.keyboard.activate() &&
+ this.handlers.drag.activate() &&
+ OpenLayers.Control.prototype.activate.apply(this, arguments));
+ },
+
+ /**
+ * APIMethod: deactivate
+ * Deactivate the control.
+ *
+ * Returns:
+ * {Boolean} Successfully deactivated the control.
+ */
+ deactivate: function() {
+ var deactivated = false;
+ // the return from the controls is unimportant in this case
+ if(OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {
+ this.moveLayerBack();
+ this.map.events.un({
+ "removelayer": this.handleMapEvents,
+ "changelayer": this.handleMapEvents,
+ scope: this
+ });
+ this.layer.removeFeatures(this.vertices, {silent: true});
+ this.layer.removeFeatures(this.virtualVertices, {silent: true});
+ this.vertices = [];
+ this.handlers.drag.deactivate();
+ this.handlers.keyboard.deactivate();
+ var feature = this.feature;
+ if (feature && feature.geometry && feature.layer) {
+ this.unselectFeature(feature);
+ }
+ deactivated = true;
+ }
+ return deactivated;
+ },
+
+ /**
+ * Method: beforeSelectFeature
+ * Called before a feature is selected.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>} The feature about to be selected.
+ */
+ beforeSelectFeature: function(feature) {
+ return this.layer.events.triggerEvent(
+ "beforefeaturemodified", {feature: feature}
+ );
+ },
+
+ /**
+ * APIMethod: selectFeature
+ * Select a feature for modification in standalone mode. In non-standalone
+ * mode, this method is called when a feature is selected by clicking.
+ * Register a listener to the beforefeaturemodified event and return false
+ * to prevent feature modification.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>} the selected feature.
+ */
+ selectFeature: function(feature) {
+ if (this.feature === feature ||
+ (this.geometryTypes && OpenLayers.Util.indexOf(this.geometryTypes,
+ feature.geometry.CLASS_NAME) == -1)) {
+ return;
+ }
+ if (this.beforeSelectFeature(feature) !== false) {
+ if (this.feature) {
+ this.unselectFeature(this.feature);
+ }
+ this.feature = feature;
+ this.layer.selectedFeatures.push(feature);
+ this.layer.drawFeature(feature, 'select');
+ this.modified = false;
+ this.resetVertices();
+ this.onModificationStart(this.feature);
+ }
+ // keep track of geometry modifications
+ var modified = feature.modified;
+ if (feature.geometry && !(modified && modified.geometry)) {
+ this._originalGeometry = feature.geometry.clone();
+ }
+ },
+
+ /**
+ * APIMethod: unselectFeature
+ * Called when the select feature control unselects a feature.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>} The unselected feature.
+ */
+ unselectFeature: function(feature) {
+ this.layer.removeFeatures(this.vertices, {silent: true});
+ this.vertices = [];
+ this.layer.destroyFeatures(this.virtualVertices, {silent: true});
+ this.virtualVertices = [];
+ if(this.dragHandle) {
+ this.layer.destroyFeatures([this.dragHandle], {silent: true});
+ delete this.dragHandle;
+ }
+ if(this.radiusHandle) {
+ this.layer.destroyFeatures([this.radiusHandle], {silent: true});
+ delete this.radiusHandle;
+ }
+ this.layer.drawFeature(this.feature, 'default');
+ this.feature = null;
+ OpenLayers.Util.removeItem(this.layer.selectedFeatures, feature);
+ this.onModificationEnd(feature);
+ this.layer.events.triggerEvent("afterfeaturemodified", {
+ feature: feature,
+ modified: this.modified
+ });
+ this.modified = false;
+ },
+
+
+ /**
+ * Method: dragStart
+ * Called by the drag handler before a feature is dragged. This method is
+ * used to differentiate between points and vertices
+ * of higher order geometries.
+ *
+ * Parameters:
+ * feature - {<OpenLayers.Feature.Vector>} The point or vertex about to be
+ * dragged.
+ */
+ dragStart: function(feature) {
+ var isPoint = feature.geometry.CLASS_NAME ==
+ 'OpenLayers.Geometry.Point';
+ if (!this.standalone &&
+ ((!feature._sketch && isPoint) || !feature._sketch)) {
+ if (this.toggle && this.feature === feature) {
+ // mark feature for unselection
+ this._unselect = feature;
+ }
+ this.selectFeature(feature);
+ }
+ if (feature._sketch || isPoint) {
+ // feature is a drag or virtual handle or point
+ this.vertex = feature;
+ this.handlers.drag.stopDown = true;
+ }
+ },
+
+ /**
+ * Method: dragVertex
+ * Called by the drag handler with each drag move of a vertex.
+ *
+ * Parameters:
+ * vertex - {<OpenLayers.Feature.Vector>} The vertex being dragged.
+ * pixel - {<OpenLayers.Pixel>} Pixel location of the mouse event.
+ */
+ dragVertex: function(vertex, pixel) {
+ var pos = this.map.getLonLatFromViewPortPx(pixel);
+ var geom = vertex.geometry;
+ geom.move(pos.lon - geom.x, pos.lat - geom.y);
+ this.modified = true;
+ /**
+ * Five cases:
+ * 1) dragging a simple point
+ * 2) dragging a virtual vertex
+ * 3) dragging a drag handle
+ * 4) dragging a real vertex
+ * 5) dragging a radius handle
+ */
+ if(this.feature.geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
+ // dragging a simple point
+ this.layer.events.triggerEvent("vertexmodified", {
+ vertex: vertex.geometry,
+ feature: this.feature,
+ pixel: pixel
+ });
+ } else {
+ if(vertex._index) {
+ // dragging a virtual vertex
+ vertex.geometry.parent.addComponent(vertex.geometry,
+ vertex._index);
+ // move from virtual to real vertex
+ delete vertex._index;
+ OpenLayers.Util.removeItem(this.virtualVertices, vertex);
+ this.vertices.push(vertex);
+ } else if(vertex == this.dragHandle) {
+ // dragging a drag handle
+ this.layer.removeFeatures(this.vertices, {silent: true});
+ this.vertices = [];
+ if(this.radiusHandle) {
+ this.layer.destroyFeatures([this.radiusHandle], {silent: true});
+ this.radiusHandle = null;
+ }
+ } else if(vertex !== this.radiusHandle) {
+ // dragging a real vertex
+ this.layer.events.triggerEvent("vertexmodified", {
+ vertex: vertex.geometry,
+ feature: this.feature,
+ pixel: pixel
+ });
+ }
+ // dragging a radius handle - no special treatment
+ if(this.virtualVertices.length > 0) {
+ this.layer.destroyFeatures(this.virtualVertices, {silent: true});
+ this.virtualVertices = [];
+ }
+ this.layer.drawFeature(this.feature, this.standalone ? undefined :
+ 'select');
+ }
+ // keep the vertex on top so it gets the mouseout after dragging
+ // this should be removed in favor of an option to draw under or
+ // maintain node z-index
+ this.layer.drawFeature(vertex);
+ },
+
+ /**
+ * Method: dragComplete
+ * Called by the drag handler when the feature dragging is complete.
+ *
+ * Parameters:
+ * vertex - {<OpenLayers.Feature.Vector>} The vertex being dragged.
+ */
+ dragComplete: function(vertex) {
+ this.resetVertices();
+ this.setFeatureState();
+ this.onModification(this.feature);
+ this.layer.events.triggerEvent("featuremodified",
+ {feature: this.feature});
+ },
+
+ /**
+ * Method: setFeatureState
+ * Called when the feature is modified. If the current state is not
+ * INSERT or DELETE, the state is set to UPDATE.
+ */
+ setFeatureState: function() {
+ if(this.feature.state != OpenLayers.State.INSERT &&
+ this.feature.state != OpenLayers.State.DELETE) {
+ this.feature.state = OpenLayers.State.UPDATE;
+ if (this.modified && this._originalGeometry) {
+ var feature = this.feature;
+ feature.modified = OpenLayers.Util.extend(feature.modified, {
+ geometry: this._originalGeometry
+ });
+ delete this._originalGeometry;
+ }
+ }
+ },
+
+ /**
+ * Method: resetVertices
+ */
+ resetVertices: function() {
+ if(this.vertices.length > 0) {
+ this.layer.removeFeatures(this.vertices, {silent: true});
+ this.vertices = [];
+ }
+ if(this.virtualVertices.length > 0) {
+ this.layer.removeFeatures(this.virtualVertices, {silent: true});
+ this.virtualVertices = [];
+ }
+ if(this.dragHandle) {
+ this.layer.destroyFeatures([this.dragHandle], {silent: true});
+ this.dragHandle = null;
+ }
+ if(this.radiusHandle) {
+ this.layer.destroyFeatures([this.radiusHandle], {silent: true});
+ this.radiusHandle = null;
+ }
+ if(this.feature &&
+ this.feature.geometry.CLASS_NAME != "OpenLayers.Geometry.Point") {
+ if((this.mode & OpenLayers.Control.ModifyFeature.DRAG)) {
+ this.collectDragHandle();
+ }
+ if((this.mode & (OpenLayers.Control.ModifyFeature.ROTATE |
+ OpenLayers.Control.ModifyFeature.RESIZE))) {
+ this.collectRadiusHandle();
+ }
+ if(this.mode & OpenLayers.Control.ModifyFeature.RESHAPE){
+ // Don't collect vertices when we're resizing
+ if (!(this.mode & OpenLayers.Control.ModifyFeature.RESIZE)){
+ this.collectVertices();
+ }
+ }
+ }
+ },
+
+ /**
+ * Method: handleKeypress
+ * Called by the feature handler on keypress. This is used to delete
+ * vertices. If the <deleteCode> property is set, vertices will
+ * be deleted when a feature is selected for modification and
+ * the mouse is over a vertex.
+ *
+ * Parameters:
+ * evt - {Event} Keypress event.
+ */
+ handleKeypress: function(evt) {
+ var code = evt.keyCode;
+
+ // check for delete key
+ if(this.feature &&
+ OpenLayers.Util.indexOf(this.deleteCodes, code) != -1) {
+ var vertex = this.layer.getFeatureFromEvent(this.handlers.drag.evt);
+ if (vertex &&
+ OpenLayers.Util.indexOf(this.vertices, vertex) != -1 &&
+ !this.handlers.drag.dragging && vertex.geometry.parent) {
+ // remove the vertex
+ vertex.geometry.parent.removeComponent(vertex.geometry);
+ this.layer.events.triggerEvent("vertexremoved", {
+ vertex: vertex.geometry,
+ feature: this.feature,
+ pixel: evt.xy
+ });
+ this.layer.drawFeature(this.feature, this.standalone ?
+ undefined : 'select');
+ this.modified = true;
+ this.resetVertices();
+ this.setFeatureState();
+ this.onModification(this.feature);
+ this.layer.events.triggerEvent("featuremodified",
+ {feature: this.feature});
+ }
+ }
+ },
+
+ /**
+ * Method: collectVertices
+ * Collect the vertices from the modifiable feature's geometry and push
+ * them on to the control's vertices array.
+ */
+ collectVertices: function() {
+ this.vertices = [];
+ this.virtualVertices = [];
+ var control = this;
+ function collectComponentVertices(geometry) {
+ var i, vertex, component, len;
+ if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
+ vertex = new OpenLayers.Feature.Vector(geometry);
+ vertex._sketch = true;
+ vertex.renderIntent = control.vertexRenderIntent;
+ control.vertices.push(vertex);
+ } else {
+ var numVert = geometry.components.length;
+ if(geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") {
+ numVert -= 1;
+ }
+ for(i=0; i<numVert; ++i) {
+ component = geometry.components[i];
+ if(component.CLASS_NAME == "OpenLayers.Geometry.Point") {
+ vertex = new OpenLayers.Feature.Vector(component);
+ vertex._sketch = true;
+ vertex.renderIntent = control.vertexRenderIntent;
+ control.vertices.push(vertex);
+ } else {
+ collectComponentVertices(component);
+ }
+ }
+
+ // add virtual vertices in the middle of each edge
+ if (control.createVertices && geometry.CLASS_NAME != "OpenLayers.Geometry.MultiPoint") {
+ for(i=0, len=geometry.components.length; i<len-1; ++i) {
+ var prevVertex = geometry.components[i];
+ var nextVertex = geometry.components[i + 1];
+ if(prevVertex.CLASS_NAME == "OpenLayers.Geometry.Point" &&
+ nextVertex.CLASS_NAME == "OpenLayers.Geometry.Point") {
+ var x = (prevVertex.x + nextVertex.x) / 2;
+ var y = (prevVertex.y + nextVertex.y) / 2;
+ var point = new OpenLayers.Feature.Vector(
+ new OpenLayers.Geometry.Point(x, y),
+ null, control.virtualStyle
+ );
+ // set the virtual parent and intended index
+ point.geometry.parent = geometry;
+ point._index = i + 1;
+ point._sketch = true;
+ control.virtualVertices.push(point);
+ }
+ }
+ }
+ }
+ }
+ collectComponentVertices.call(this, this.feature.geometry);
+ this.layer.addFeatures(this.virtualVertices, {silent: true});
+ this.layer.addFeatures(this.vertices, {silent: true});
+ },
+
+ /**
+ * Method: collectDragHandle
+ * Collect the drag handle for the selected geometry.
+ */
+ collectDragHandle: function() {
+ var geometry = this.feature.geometry;
+ var center = geometry.getBounds().getCenterLonLat();
+ var originGeometry = new OpenLayers.Geometry.Point(
+ center.lon, center.lat
+ );
+ var origin = new OpenLayers.Feature.Vector(originGeometry);
+ originGeometry.move = function(x, y) {
+ OpenLayers.Geometry.Point.prototype.move.call(this, x, y);
+ geometry.move(x, y);
+ };
+ origin._sketch = true;
+ this.dragHandle = origin;
+ this.dragHandle.renderIntent = this.vertexRenderIntent;
+ this.layer.addFeatures([this.dragHandle], {silent: true});
+ },
+
+ /**
+ * Method: collectRadiusHandle
+ * Collect the radius handle for the selected geometry.
+ */
+ collectRadiusHandle: function() {
+ var geometry = this.feature.geometry;
+ var bounds = geometry.getBounds();
+ var center = bounds.getCenterLonLat();
+ var originGeometry = new OpenLayers.Geometry.Point(
+ center.lon, center.lat
+ );
+ var radiusGeometry = new OpenLayers.Geometry.Point(
+ bounds.right, bounds.bottom
+ );
+ var radius = new OpenLayers.Feature.Vector(radiusGeometry);
+ var resize = (this.mode & OpenLayers.Control.ModifyFeature.RESIZE);
+ var reshape = (this.mode & OpenLayers.Control.ModifyFeature.RESHAPE);
+ var rotate = (this.mode & OpenLayers.Control.ModifyFeature.ROTATE);
+
+ radiusGeometry.move = function(x, y) {
+ OpenLayers.Geometry.Point.prototype.move.call(this, x, y);
+ var dx1 = this.x - originGeometry.x;
+ var dy1 = this.y - originGeometry.y;
+ var dx0 = dx1 - x;
+ var dy0 = dy1 - y;
+ if(rotate) {
+ var a0 = Math.atan2(dy0, dx0);
+ var a1 = Math.atan2(dy1, dx1);
+ var angle = a1 - a0;
+ angle *= 180 / Math.PI;
+ geometry.rotate(angle, originGeometry);
+ }
+ if(resize) {
+ var scale, ratio;
+ // 'resize' together with 'reshape' implies that the aspect
+ // ratio of the geometry will not be preserved whilst resizing
+ if (reshape) {
+ scale = dy1 / dy0;
+ ratio = (dx1 / dx0) / scale;
+ } else {
+ var l0 = Math.sqrt((dx0 * dx0) + (dy0 * dy0));
+ var l1 = Math.sqrt((dx1 * dx1) + (dy1 * dy1));
+ scale = l1 / l0;
+ }
+ geometry.resize(scale, originGeometry, ratio);
+ }
+ };
+ radius._sketch = true;
+ this.radiusHandle = radius;
+ this.radiusHandle.renderIntent = this.vertexRenderIntent;
+ this.layer.addFeatures([this.radiusHandle], {silent: true});
+ },
+
+ /**
+ * Method: setMap
+ * Set the map property for the control and all handlers.
+ *
+ * Parameters:
+ * map - {<OpenLayers.Map>} The control's map.
+ */
+ setMap: function(map) {
+ this.handlers.drag.setMap(map);
+ OpenLayers.Control.prototype.setMap.apply(this, arguments);
+ },
+
+ /**
+ * Method: handleMapEvents
+ *
+ * Parameters:
+ * evt - {Object}
+ */
+ handleMapEvents: function(evt) {
+ if (evt.type == "removelayer" || evt.property == "order") {
+ this.moveLayerToTop();
+ }
+ },
+
+ /**
+ * Method: moveLayerToTop
+ * Moves the layer for this handler to the top, so mouse events can reach
+ * it.
+ */
+ moveLayerToTop: function() {
+ var index = Math.max(this.map.Z_INDEX_BASE['Feature'] - 1,
+ this.layer.getZIndex()) + 1;
+ this.layer.setZIndex(index);
+
+ },
+
+ /**
+ * Method: moveLayerBack
+ * Moves the layer back to the position determined by the map's layers
+ * array.
+ */
+ moveLayerBack: function() {
+ var index = this.layer.getZIndex() - 1;
+ if (index >= this.map.Z_INDEX_BASE['Feature']) {
+ this.layer.setZIndex(index);
+ } else {
+ this.map.setLayerZIndex(this.layer,
+ this.map.getLayerIndex(this.layer));
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Control.ModifyFeature"
+});
+
+/**
+ * Constant: RESHAPE
+ * {Integer} Constant used to make the control work in reshape mode
+ */
+OpenLayers.Control.ModifyFeature.RESHAPE = 1;
+/**
+ * Constant: RESIZE
+ * {Integer} Constant used to make the control work in resize mode
+ */
+OpenLayers.Control.ModifyFeature.RESIZE = 2;
+/**
+ * Constant: ROTATE
+ * {Integer} Constant used to make the control work in rotate mode
+ */
+OpenLayers.Control.ModifyFeature.ROTATE = 4;
+/**
+ * Constant: DRAG
+ * {Integer} Constant used to make the control work in drag mode
+ */
+OpenLayers.Control.ModifyFeature.DRAG = 8;