Created a TransformFeature control and added a documentDrag option to the DragFeature control. r=elemoine (closes #2433)
git-svn-id: http://svn.openlayers.org/trunk/openlayers@9999 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf
This commit is contained in:
105
examples/transform-feature.html
Normal file
105
examples/transform-feature.html
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<title>OpenLayers: Transformation Box</title>
|
||||||
|
<link rel="stylesheet" href="../theme/default/style.css" type="text/css" />
|
||||||
|
<link rel="stylesheet" href="style.css" type="text/css" />
|
||||||
|
<script src="../lib/OpenLayers.js" type="text/javascript"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
var map, control;
|
||||||
|
|
||||||
|
function init(){
|
||||||
|
map = new OpenLayers.Map('map', {allOverlays: true});
|
||||||
|
|
||||||
|
// context for appropriate scale/resize cursors
|
||||||
|
var cursors = ["sw-resize", "s-resize", "se-resize",
|
||||||
|
"e-resize", "ne-resize", "n-resize", "nw-resize", "w-resize"];
|
||||||
|
var context = {
|
||||||
|
getCursor: function(feature){
|
||||||
|
var i = OpenLayers.Util.indexOf(control.handles, feature);
|
||||||
|
var cursor = "inherit";
|
||||||
|
if(i !== -1) {
|
||||||
|
i = (i + 8 + Math.round(control.rotation / 90) * 2) % 8;
|
||||||
|
cursor = cursors[i];
|
||||||
|
}
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// a nice style for the transformation box
|
||||||
|
var style = new OpenLayers.Style({
|
||||||
|
cursor: "${getCursor}",
|
||||||
|
pointRadius: 5,
|
||||||
|
fillColor: "white",
|
||||||
|
fillOpacity: 1,
|
||||||
|
strokeColor: "black"
|
||||||
|
}, {context: context});
|
||||||
|
|
||||||
|
// the layer that we want to transform features on
|
||||||
|
var vectorLayer = new OpenLayers.Layer.Vector("Simple Geometry", {
|
||||||
|
styleMap: new OpenLayers.StyleMap({
|
||||||
|
"transform": style
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
// create the TransformFeature control, using the renderIntent
|
||||||
|
// from above
|
||||||
|
control = new OpenLayers.Control.TransformFeature(vectorLayer, {
|
||||||
|
renderIntent: "transform"
|
||||||
|
});
|
||||||
|
map.addControl(control);
|
||||||
|
|
||||||
|
// create a polygon feature from a linear ring of points
|
||||||
|
var point = new OpenLayers.Geometry.Point(-111.04, 45.68);
|
||||||
|
var pointList = [];
|
||||||
|
for(var p=0; p<6; ++p) {
|
||||||
|
var a = p * (2 * Math.PI) / 7;
|
||||||
|
var r = Math.random(1) + 2;
|
||||||
|
var newPoint = new OpenLayers.Geometry.Point(point.x + (r * Math.cos(a)),
|
||||||
|
point.y + (r * Math.sin(a)));
|
||||||
|
pointList.push(newPoint);
|
||||||
|
}
|
||||||
|
pointList.push(pointList[0]);
|
||||||
|
|
||||||
|
var linearRing = new OpenLayers.Geometry.LinearRing(pointList);
|
||||||
|
var polygonFeature = new OpenLayers.Feature.Vector(
|
||||||
|
new OpenLayers.Geometry.Polygon([linearRing]));
|
||||||
|
|
||||||
|
|
||||||
|
map.addLayer(vectorLayer);
|
||||||
|
map.setCenter(new OpenLayers.LonLat(point.x, point.y), 5);
|
||||||
|
var anotherFeature = polygonFeature.clone();
|
||||||
|
polygonFeature.geometry.move(-3, 0);
|
||||||
|
anotherFeature.geometry.move(3, 0);
|
||||||
|
vectorLayer.addFeatures([polygonFeature, anotherFeature]);
|
||||||
|
|
||||||
|
// start with the transformation box on polygonFeature
|
||||||
|
control.setFeature(polygonFeature, {rotation: 45, scale: 0.5, ratio: 1.5});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body onload="init()">
|
||||||
|
<h1 id="title">Vector Feature Transformation Box Example</h1>
|
||||||
|
|
||||||
|
<div id="tags">
|
||||||
|
</div>
|
||||||
|
<p id="shortdesc">
|
||||||
|
Shows the use of the TransformFeature control.
|
||||||
|
</p>
|
||||||
|
<div style="text-align: right">
|
||||||
|
<div dir="rtl" id="map" class="smallmap"></div>
|
||||||
|
</div>
|
||||||
|
<div id="docs">
|
||||||
|
<p>This example shows transformation of vector features with a
|
||||||
|
tranformation box. Grab one of the handles to resize the feature.
|
||||||
|
Holding the SHIFT key will preserve the aspect ratio. Position the
|
||||||
|
mouse right outside one of the corner handles to rotate the feature,
|
||||||
|
and hold the SHIFT key to only rotate in 45<34> increments.</p>
|
||||||
|
<p>In this example, the transformation box has been set on the left
|
||||||
|
feature, with a rotation preset of 45<34>. Clicking on the right feature
|
||||||
|
will set it for transformation, starting with an unrotated box.
|
||||||
|
Dragging a feature or the box edges will move it around.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
@@ -178,6 +178,7 @@
|
|||||||
"OpenLayers/Control/Measure.js",
|
"OpenLayers/Control/Measure.js",
|
||||||
"OpenLayers/Control/WMSGetFeatureInfo.js",
|
"OpenLayers/Control/WMSGetFeatureInfo.js",
|
||||||
"OpenLayers/Control/Graticule.js",
|
"OpenLayers/Control/Graticule.js",
|
||||||
|
"OpenLayers/Control/TransformFeature.js",
|
||||||
"OpenLayers/Geometry.js",
|
"OpenLayers/Geometry.js",
|
||||||
"OpenLayers/Geometry/Rectangle.js",
|
"OpenLayers/Geometry/Rectangle.js",
|
||||||
"OpenLayers/Geometry/Collection.js",
|
"OpenLayers/Geometry/Collection.js",
|
||||||
|
|||||||
@@ -64,6 +64,13 @@ OpenLayers.Control.DragFeature = OpenLayers.Class(OpenLayers.Control, {
|
|||||||
*/
|
*/
|
||||||
onComplete: function(feature, pixel) {},
|
onComplete: function(feature, pixel) {},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIProperty: documentDrag
|
||||||
|
* {Boolean} If set to true, mouse dragging will continue even if the
|
||||||
|
* mouse cursor leaves the map viewport. Default is false.
|
||||||
|
*/
|
||||||
|
documentDrag: false,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Property: layer
|
* Property: layer
|
||||||
* {<OpenLayers.Layer.Vector>}
|
* {<OpenLayers.Layer.Vector>}
|
||||||
@@ -115,7 +122,9 @@ OpenLayers.Control.DragFeature = OpenLayers.Class(OpenLayers.Control, {
|
|||||||
up: this.upFeature,
|
up: this.upFeature,
|
||||||
out: this.cancel,
|
out: this.cancel,
|
||||||
done: this.doneDragging
|
done: this.doneDragging
|
||||||
}, this.dragCallbacks)
|
}, this.dragCallbacks), {
|
||||||
|
documentDrag: this.documentDrag
|
||||||
|
}
|
||||||
),
|
),
|
||||||
feature: new OpenLayers.Handler.Feature(
|
feature: new OpenLayers.Handler.Feature(
|
||||||
this, this.layer, OpenLayers.Util.extend({
|
this, this.layer, OpenLayers.Util.extend({
|
||||||
|
|||||||
579
lib/OpenLayers/Control/TransformFeature.js
Normal file
579
lib/OpenLayers/Control/TransformFeature.js
Normal file
@@ -0,0 +1,579 @@
|
|||||||
|
/* Copyright (c) 2009 MetaCarta, Inc., published under the Clear BSD license.
|
||||||
|
* See http://svn.openlayers.org/trunk/openlayers/license.txt
|
||||||
|
* for the full text of the license. */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @requires OpenLayers/Control.js
|
||||||
|
* @requires OpenLayers/Control/DragFeature.js
|
||||||
|
* @requires OpenLayers/Feature/Vector.js
|
||||||
|
* @requires OpenLayers/Geometry/LineString.js
|
||||||
|
* @requires OpenLayers/Geometry/Point.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class: OpenLayers.Control.TransformFeature
|
||||||
|
* Control to transform features with a standard transformation box.
|
||||||
|
*
|
||||||
|
* Inherits From:
|
||||||
|
* - <OpenLayers.Control>
|
||||||
|
*/
|
||||||
|
OpenLayers.Control.TransformFeature = OpenLayers.Class(OpenLayers.Control, {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant: EVENT_TYPES
|
||||||
|
*
|
||||||
|
* Supported event types:
|
||||||
|
* - *beforesetfeature* Triggered before a feature is set for
|
||||||
|
* tranformation. The feature will not be set if a listener returns
|
||||||
|
* false. Listeners receive a *feature* property, with the feature
|
||||||
|
* that will be set for transformation. Listeners are allowed to
|
||||||
|
* set the control's *scale*, *ratio* and *rotation* properties,
|
||||||
|
* which will set the initial scale, ratio and rotation of the
|
||||||
|
* feature, like the <setFeature> method's initialParams argument.
|
||||||
|
* - *setfeature* Triggered when a feature is set for tranformation.
|
||||||
|
* Listeners receive a *feature* property, with the feature that
|
||||||
|
* is now set for transformation.
|
||||||
|
* - *beforetransform* Triggered while dragging, before a feature is
|
||||||
|
* transformed. The feature will not be transformed if a listener
|
||||||
|
* returns false (but the box still will). Listeners receive one or
|
||||||
|
* more of *center*, *scale*, *ratio* and *rotation*. The *center*
|
||||||
|
* property is an <OpenLayers.Geometry.Point> object with the new
|
||||||
|
* center of the transformed feature, the others are Floats with the
|
||||||
|
* scale, ratio or rotation change since the last transformation.
|
||||||
|
* - *transform* Triggered while dragging, when a feature is transformed.
|
||||||
|
* Listeners receive an event object with one or more of *center*,
|
||||||
|
* *scale*, *ratio* and *rotation*. The *center* property is an
|
||||||
|
* <OpenLayers.Geometry.Point> object with the new center of the
|
||||||
|
* transformed feature, the others are Floats with the scale, ratio
|
||||||
|
* or rotation change of the feature since the last transformation.
|
||||||
|
* - *transformcomplete" Triggered after dragging. Listeners receive
|
||||||
|
* an event object with the transformed *feature*.
|
||||||
|
*/
|
||||||
|
EVENT_TYPES: ["beforesetfeature", "setfeature", "beforetransform",
|
||||||
|
"transform", "transformcomplete"],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIProperty: geometryTypes
|
||||||
|
* {Array(String)} To restrict transformation to a limited set of geometry
|
||||||
|
* types, send a list of strings corresponding to the geometry class
|
||||||
|
* names.
|
||||||
|
*/
|
||||||
|
geometryTypes: null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Property: layer
|
||||||
|
* {<OpenLayers.Layer.Vector>}
|
||||||
|
*/
|
||||||
|
layer: null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIProperty: preserveAspectRatio
|
||||||
|
* {Boolean} set to true to not change the feature's aspect ratio.
|
||||||
|
*/
|
||||||
|
preserveAspectRatio: false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIProperty: rotate
|
||||||
|
* {Boolean} set to false if rotation should be disabled. Default is true.
|
||||||
|
* To be passed with the constructor or set when the control is not
|
||||||
|
* active.
|
||||||
|
*/
|
||||||
|
rotate: true,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIProperty: feature
|
||||||
|
* {<OpenLayers.Feature.Vector>} Feature currently available for
|
||||||
|
* transformation. Read-only, use <setFeature> to set it manually.
|
||||||
|
*/
|
||||||
|
feature: null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIProperty: renderIntent
|
||||||
|
* {String|Object} Render intent for the transformation box and
|
||||||
|
* handles. A symbolizer object can also be provided here.
|
||||||
|
*/
|
||||||
|
renderIntent: "temporary",
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIProperty: rotationHandleSymbolizer
|
||||||
|
* {Object|String} Optional. A custom symbolizer for the rotation handles.
|
||||||
|
* A render intent can also be provided here. Defaults to
|
||||||
|
* (code)
|
||||||
|
* {
|
||||||
|
* stroke: false,
|
||||||
|
* pointRadius: 10,
|
||||||
|
* fillOpacity: 0,
|
||||||
|
* cursor: "pointer"
|
||||||
|
* }
|
||||||
|
* (end)
|
||||||
|
*/
|
||||||
|
rotationHandleSymbolizer: null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIProperty: box
|
||||||
|
* {<OpenLayers.Feature.Vector>} The transformation box rectangle.
|
||||||
|
* Read-only.
|
||||||
|
*/
|
||||||
|
box: null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIProperty: center
|
||||||
|
* {<OpenLayers.Geometry.Point>} The center of the feature bounds.
|
||||||
|
* Read-only.
|
||||||
|
*/
|
||||||
|
center: null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIProperty: scale
|
||||||
|
* {Float} The scale of the feature, relative to the scale the time the
|
||||||
|
* feature was set. Read-only, except for *beforesetfeature*
|
||||||
|
* listeners.
|
||||||
|
*/
|
||||||
|
scale: 1,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIProperty: ratio
|
||||||
|
* {Float} The ratio of the feature relative to the ratio the time the
|
||||||
|
* feature was set. Read-only, except for *beforesetfeature*
|
||||||
|
* listeners.
|
||||||
|
*/
|
||||||
|
ratio: 1,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Property: rotation
|
||||||
|
* {Integer} the current rotation angle of the box. Read-only, except for
|
||||||
|
* *beforesetfeature* listeners.
|
||||||
|
*/
|
||||||
|
rotation: 0,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIProperty: handles
|
||||||
|
* {Array(<OpenLayers.Feature.Vector>)} The 8 handles currently available
|
||||||
|
* for scaling/resizing. Numbered counterclockwise, starting from the
|
||||||
|
* southwest corner. Read-only.
|
||||||
|
*/
|
||||||
|
handles: null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIProperty: rotationHandles
|
||||||
|
* {Array(<OpenLayers.Feature.Vector>)} The 4 rotation handles currently
|
||||||
|
* available for rotating. Numbered counterclockwise, starting from
|
||||||
|
* the southwest corner. Read-only.
|
||||||
|
*/
|
||||||
|
rotationHandles: null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Property: dragControl
|
||||||
|
* {<OpenLayers.Control.DragFeature>}
|
||||||
|
*/
|
||||||
|
dragControl: null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor: OpenLayers.Control.TransformFeature
|
||||||
|
* Create a new transform feature control.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* layer - {<OpenLayers.Layer.Vector>} Layer that contains features that
|
||||||
|
* will be transformed.
|
||||||
|
* options - {Object} Optional object whose properties will be set on the
|
||||||
|
* control.
|
||||||
|
*/
|
||||||
|
initialize: function(layer, options) {
|
||||||
|
// concatenate events specific to this control with those from the base
|
||||||
|
this.EVENT_TYPES =
|
||||||
|
OpenLayers.Control.TransformFeature.prototype.EVENT_TYPES.concat(
|
||||||
|
OpenLayers.Control.prototype.EVENT_TYPES
|
||||||
|
);
|
||||||
|
OpenLayers.Control.prototype.initialize.apply(this, [options]);
|
||||||
|
|
||||||
|
this.layer = layer;
|
||||||
|
|
||||||
|
if(!this.rotationHandleSymbolizer) {
|
||||||
|
this.rotationHandleSymbolizer = {
|
||||||
|
stroke: false,
|
||||||
|
pointRadius: 10,
|
||||||
|
fillOpacity: 0,
|
||||||
|
cursor: "pointer"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.createBox();
|
||||||
|
this.createControl();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIMethod: activate
|
||||||
|
* Activates the control.
|
||||||
|
*/
|
||||||
|
activate: function() {
|
||||||
|
var activated = false;
|
||||||
|
if(OpenLayers.Control.prototype.activate.apply(this, arguments)) {
|
||||||
|
this.dragControl.activate();
|
||||||
|
this.layer.addFeatures([this.box]);
|
||||||
|
this.rotate && this.layer.addFeatures(this.rotationHandles);
|
||||||
|
this.layer.addFeatures(this.handles);
|
||||||
|
activated = true;
|
||||||
|
}
|
||||||
|
return activated;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIMethod: deactivate
|
||||||
|
* Deactivates the control.
|
||||||
|
*/
|
||||||
|
deactivate: function() {
|
||||||
|
var deactivated = false;
|
||||||
|
if(OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {
|
||||||
|
this.layer.removeFeatures(this.handles);
|
||||||
|
this.rotate && this.layer.removeFeatures(this.rotationHandles);
|
||||||
|
this.layer.removeFeatures([this.box]);
|
||||||
|
this.dragControl.deactivate();
|
||||||
|
deactivated = true;
|
||||||
|
}
|
||||||
|
return deactivated;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method: setMap
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* map - {<OpenLayers.Map>}
|
||||||
|
*/
|
||||||
|
setMap: function(map) {
|
||||||
|
this.dragControl.setMap(map);
|
||||||
|
OpenLayers.Control.prototype.setMap.apply(this, arguments);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIMethod: setFeature
|
||||||
|
* Place the transformation box on a feature and start transforming it.
|
||||||
|
* If the control is not active, it will be activated.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* feature - {<OpenLayers.Feature.Vector>}
|
||||||
|
* initialParams - {Object} Initial values for rotation, scale or ratio.
|
||||||
|
* Setting a rotation value here will cause the transformation box to
|
||||||
|
* start rotated. Setting a scale or ratio will not affect the
|
||||||
|
* transormation box, but applications may use this to keep track of
|
||||||
|
* scale and ratio of a feature across multiple transforms.
|
||||||
|
*/
|
||||||
|
setFeature: function(feature, initialParams) {
|
||||||
|
initialParams = OpenLayers.Util.applyDefaults(initialParams, {
|
||||||
|
rotation: 0,
|
||||||
|
scale: 1,
|
||||||
|
ratio: 1
|
||||||
|
});
|
||||||
|
var evt = {feature: feature};
|
||||||
|
|
||||||
|
var oldRotation = this.rotation;
|
||||||
|
var oldCenter = this.center;
|
||||||
|
OpenLayers.Util.extend(this, initialParams);
|
||||||
|
|
||||||
|
if(this.events.triggerEvent("beforesetfeature", evt) === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.feature = feature;
|
||||||
|
this.activate();
|
||||||
|
|
||||||
|
this._setfeature = true;
|
||||||
|
|
||||||
|
var featureBounds = this.feature.geometry.getBounds();
|
||||||
|
this.box.move(featureBounds.getCenterLonLat());
|
||||||
|
this.box.geometry.rotate(-oldRotation, oldCenter);
|
||||||
|
this._angle = 0;
|
||||||
|
|
||||||
|
var ll;
|
||||||
|
if(this.rotation) {
|
||||||
|
var geom = feature.geometry.clone();
|
||||||
|
geom.rotate(-this.rotation, this.center);
|
||||||
|
var box = new OpenLayers.Feature.Vector(
|
||||||
|
geom.getBounds().toGeometry());
|
||||||
|
box.geometry.rotate(this.rotation, this.center);
|
||||||
|
this.box.geometry.rotate(this.rotation, this.center);
|
||||||
|
this.box.move(box.geometry.getBounds().getCenterLonLat());
|
||||||
|
var llGeom = box.geometry.components[0].components[0];
|
||||||
|
ll = llGeom.getBounds().getCenterLonLat();
|
||||||
|
} else {
|
||||||
|
ll = new OpenLayers.LonLat(featureBounds.left, featureBounds.bottom);
|
||||||
|
}
|
||||||
|
this.handles[0].move(ll);
|
||||||
|
|
||||||
|
delete this._setfeature;
|
||||||
|
|
||||||
|
this.events.triggerEvent("setfeature", evt);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method: createBox
|
||||||
|
* Creates the box with all handles and transformation handles.
|
||||||
|
*/
|
||||||
|
createBox: function() {
|
||||||
|
var control = this;
|
||||||
|
|
||||||
|
this.center = new OpenLayers.Geometry.Point(0, 0);
|
||||||
|
var box = new OpenLayers.Feature.Vector(
|
||||||
|
new OpenLayers.Geometry.LineString([
|
||||||
|
new OpenLayers.Geometry.Point(-1, -1),
|
||||||
|
new OpenLayers.Geometry.Point(0, -1),
|
||||||
|
new OpenLayers.Geometry.Point(1, -1),
|
||||||
|
new OpenLayers.Geometry.Point(1, 0),
|
||||||
|
new OpenLayers.Geometry.Point(1, 1),
|
||||||
|
new OpenLayers.Geometry.Point(0, 1),
|
||||||
|
new OpenLayers.Geometry.Point(-1, 1),
|
||||||
|
new OpenLayers.Geometry.Point(-1, 0),
|
||||||
|
new OpenLayers.Geometry.Point(-1, -1)
|
||||||
|
]), null,
|
||||||
|
typeof this.renderIntent == "string" ? null : this.renderIntent
|
||||||
|
);
|
||||||
|
|
||||||
|
// Override for box move - make sure that the center gets updated
|
||||||
|
box.geometry.move = function(x, y) {
|
||||||
|
control._moving = true;
|
||||||
|
OpenLayers.Geometry.LineString.prototype.move.apply(this, arguments);
|
||||||
|
control.center.move(x, y);
|
||||||
|
delete control._moving;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Overrides for vertex move, resize and rotate - make sure that
|
||||||
|
// handle and rotationHandle geometries are also moved, resized and
|
||||||
|
// rotated.
|
||||||
|
var vertexMoveFn = function(x, y) {
|
||||||
|
OpenLayers.Geometry.Point.prototype.move.apply(this, arguments);
|
||||||
|
this._rotationHandle && this._rotationHandle.geometry.move(x, y);
|
||||||
|
this._handle.geometry.move(x, y);
|
||||||
|
};
|
||||||
|
var vertexResizeFn = function(scale, center, ratio) {
|
||||||
|
OpenLayers.Geometry.Point.prototype.resize.apply(this, arguments);
|
||||||
|
this._rotationHandle && this._rotationHandle.geometry.resize(
|
||||||
|
scale, center, ratio);
|
||||||
|
this._handle.geometry.resize(scale, center, ratio);
|
||||||
|
};
|
||||||
|
var vertexRotateFn = function(angle, center) {
|
||||||
|
OpenLayers.Geometry.Point.prototype.rotate.apply(this, arguments);
|
||||||
|
this._rotationHandle && this._rotationHandle.geometry.rotate(
|
||||||
|
angle, center);
|
||||||
|
this._handle.geometry.rotate(angle, center);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Override for handle move - make sure that the box and other handles
|
||||||
|
// are updated, and finally transform the feature.
|
||||||
|
var handleMoveFn = function(x, y) {
|
||||||
|
var oldX = this.x, oldY = this.y;
|
||||||
|
OpenLayers.Geometry.Point.prototype.move.call(this, x, y);
|
||||||
|
if(control._moving) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var evt = control.dragControl.handlers.drag.evt;
|
||||||
|
var preserveAspectRatio = !control._setfeature &&
|
||||||
|
control.preserveAspectRatio;
|
||||||
|
var reshape = !preserveAspectRatio && !(evt && evt.shiftKey);
|
||||||
|
var oldGeom = new OpenLayers.Geometry.Point(oldX, oldY);
|
||||||
|
var centerGeometry = control.center;
|
||||||
|
this.rotate(-control.rotation, centerGeometry);
|
||||||
|
oldGeom.rotate(-control.rotation, centerGeometry);
|
||||||
|
var dx1 = this.x - centerGeometry.x;
|
||||||
|
var dy1 = this.y - centerGeometry.y;
|
||||||
|
var dx0 = dx1 - (this.x - oldGeom.x);
|
||||||
|
var dy0 = dy1 - (this.y - oldGeom.y);
|
||||||
|
this.x = oldX;
|
||||||
|
this.y = oldY;
|
||||||
|
var scale, ratio = 1;
|
||||||
|
if (reshape) {
|
||||||
|
scale = Math.abs(dy0) < 0.00001 ? 1 : dy1 / dy0;
|
||||||
|
ratio = (Math.abs(dx0) < 0.00001 ? 1 : (dx1 / dx0)) / scale;
|
||||||
|
} else {
|
||||||
|
var l0 = Math.sqrt((dx0 * dx0) + (dy0 * dy0));
|
||||||
|
var l1 = Math.sqrt((dx1 * dx1) + (dy1 * dy1));
|
||||||
|
scale = l1 / l0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// rotate the box to 0 before resizing - saves us some
|
||||||
|
// calculations and is inexpensive because we don't drawFeature.
|
||||||
|
control._moving = true;
|
||||||
|
control.box.geometry.rotate(-control.rotation, centerGeometry);
|
||||||
|
delete control._moving;
|
||||||
|
|
||||||
|
control.box.geometry.resize(scale, centerGeometry, ratio);
|
||||||
|
control.box.geometry.rotate(control.rotation, centerGeometry);
|
||||||
|
control.transformFeature({scale: scale, ratio: ratio});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Override for rotation handle move - make sure that the box and
|
||||||
|
// other handles are updated, and finally transform the feature.
|
||||||
|
var rotationHandleMoveFn = function(x, y){
|
||||||
|
var oldX = this.x, oldY = this.y;
|
||||||
|
OpenLayers.Geometry.Point.prototype.move.call(this, x, y);
|
||||||
|
if(control._moving) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var evt = control.dragControl.handlers.drag.evt;
|
||||||
|
var constrain = (evt && evt.shiftKey) ? 45 : 1;
|
||||||
|
var centerGeometry = control.center;
|
||||||
|
var dx1 = this.x - centerGeometry.x;
|
||||||
|
var dy1 = this.y - centerGeometry.y;
|
||||||
|
var dx0 = dx1 - x;
|
||||||
|
var dy0 = dy1 - y;
|
||||||
|
this.x = oldX;
|
||||||
|
this.y = oldY;
|
||||||
|
var a0 = Math.atan2(dy0, dx0);
|
||||||
|
var a1 = Math.atan2(dy1, dx1);
|
||||||
|
var angle = a1 - a0;
|
||||||
|
angle *= 180 / Math.PI;
|
||||||
|
control._angle = (control._angle + angle) % 360;
|
||||||
|
var diff = control.rotation % constrain;
|
||||||
|
if(Math.abs(control._angle) >= constrain || diff !== 0) {
|
||||||
|
angle = Math.round(control._angle / constrain) * constrain -
|
||||||
|
diff;
|
||||||
|
control._angle = 0;
|
||||||
|
control.box.geometry.rotate(angle, centerGeometry);
|
||||||
|
control.transformFeature({rotation: angle});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var handles = new Array(8);
|
||||||
|
var rotationHandles = new Array(4);
|
||||||
|
var geom, handle, rotationHandle;
|
||||||
|
for(var i=0; i<8; ++i) {
|
||||||
|
geom = box.geometry.components[i];
|
||||||
|
handle = new OpenLayers.Feature.Vector(geom.clone(), null,
|
||||||
|
typeof this.renderIntent == "string" ? null :
|
||||||
|
this.renderIntent);
|
||||||
|
if(i % 2 == 0) {
|
||||||
|
rotationHandle = new OpenLayers.Feature.Vector(geom.clone(),
|
||||||
|
null, typeof this.rotationHandleSymbolizer == "string" ?
|
||||||
|
null : this.rotationHandleSymbolizer);
|
||||||
|
rotationHandle.geometry.move = rotationHandleMoveFn;
|
||||||
|
geom._rotationHandle = rotationHandle;
|
||||||
|
rotationHandles[i/2] = rotationHandle;
|
||||||
|
}
|
||||||
|
geom.move = vertexMoveFn;
|
||||||
|
geom.resize = vertexResizeFn;
|
||||||
|
geom.rotate = vertexRotateFn;
|
||||||
|
handle.geometry.move = handleMoveFn;
|
||||||
|
geom._handle = handle;
|
||||||
|
handles[i] = handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.box = box;
|
||||||
|
this.rotationHandles = rotationHandles;
|
||||||
|
this.handles = handles;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method: createControl
|
||||||
|
* Creates a DragFeature control for this control.
|
||||||
|
*/
|
||||||
|
createControl: function() {
|
||||||
|
var control = this;
|
||||||
|
this.dragControl = new OpenLayers.Control.DragFeature(this.layer, {
|
||||||
|
documentDrag: true,
|
||||||
|
// avoid moving the feature itself - move the box instead
|
||||||
|
moveFeature: function(pixel) {
|
||||||
|
if(this.feature === control.feature) {
|
||||||
|
this.feature = control.box;
|
||||||
|
}
|
||||||
|
OpenLayers.Control.DragFeature.prototype.moveFeature.apply(this,
|
||||||
|
arguments);
|
||||||
|
},
|
||||||
|
// transform while dragging
|
||||||
|
onDrag: function(feature, pixel) {
|
||||||
|
var geom = feature.geometry;
|
||||||
|
if(feature === control.box) {
|
||||||
|
control.transformFeature({center: control.center});
|
||||||
|
control.drawHandles();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// set a new feature
|
||||||
|
onStart: function(feature, pixel) {
|
||||||
|
var eligible = !control.geometryTypes ||
|
||||||
|
OpenLayers.Util.indexOf(control.geometryTypes,
|
||||||
|
feature.geometry.CLASS_NAME) !== -1;
|
||||||
|
var i = OpenLayers.Util.indexOf(control.handles, feature);
|
||||||
|
i += OpenLayers.Util.indexOf(control.rotationHandles,
|
||||||
|
feature);
|
||||||
|
if(feature !== control.feature && feature !== control.box &&
|
||||||
|
i == -2 && eligible) {
|
||||||
|
control.setFeature(feature);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onComplete: function(feature, pixel) {
|
||||||
|
control.events.triggerEvent("transformcomplete",
|
||||||
|
{feature: feature});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method: drawHandles
|
||||||
|
* Draws the handles to match the box.
|
||||||
|
*/
|
||||||
|
drawHandles: function() {
|
||||||
|
var layer = this.layer;
|
||||||
|
for(var i=0; i<8; ++i) {
|
||||||
|
if(this.rotate && i % 2 === 0) {
|
||||||
|
layer.drawFeature(this.rotationHandles[i/2],
|
||||||
|
this.rotationHandleSymbolizer);
|
||||||
|
}
|
||||||
|
layer.drawFeature(this.handles[i], this.renderIntent);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method: transformFeature
|
||||||
|
* Transforms the feature.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* mods - {Object} An object with optional scale, ratio, rotation and
|
||||||
|
* center properties.
|
||||||
|
*/
|
||||||
|
transformFeature: function(mods) {
|
||||||
|
if(!this._setfeature) {
|
||||||
|
this.scale *= (mods.scale || 1);
|
||||||
|
this.ratio *= (mods.ratio || 1);
|
||||||
|
var oldRotation = this.rotation;
|
||||||
|
this.rotation = (this.rotation + (mods.rotation || 0)) % 360;
|
||||||
|
|
||||||
|
if(this.events.triggerEvent("beforetransform", mods) !== false) {
|
||||||
|
var feature = this.feature;
|
||||||
|
var geom = feature.geometry;
|
||||||
|
var center = this.center;
|
||||||
|
geom.rotate(-oldRotation, center);
|
||||||
|
if(mods.scale || mods.ratio) {
|
||||||
|
geom.resize(mods.scale, center, mods.ratio);
|
||||||
|
} else if(mods.center) {
|
||||||
|
feature.move(mods.center.getBounds().getCenterLonLat());
|
||||||
|
}
|
||||||
|
geom.rotate(this.rotation, center);
|
||||||
|
this.layer.drawFeature(feature);
|
||||||
|
feature.toState(OpenLayers.State.UPDATE);
|
||||||
|
this.events.triggerEvent("transform", mods);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.layer.drawFeature(this.box, this.renderIntent);
|
||||||
|
this.drawHandles();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIMethod: destroy
|
||||||
|
* Take care of things that are not handled in superclass.
|
||||||
|
*/
|
||||||
|
destroy: function() {
|
||||||
|
var geom;
|
||||||
|
for(var i=0; i<8; ++i) {
|
||||||
|
geom = this.box.geometry.components[i];
|
||||||
|
geom._handle.destroy();
|
||||||
|
geom._handle = null;
|
||||||
|
geom._rotationHandle && geom._rotationHandle.destroy();
|
||||||
|
geom._rotationHandle = null;
|
||||||
|
};
|
||||||
|
this.box.destroy();
|
||||||
|
this.box = null;
|
||||||
|
this.layer = null;
|
||||||
|
this.dragControl.destroy();
|
||||||
|
OpenLayers.Control.prototype.destroy.apply(this, arguments);
|
||||||
|
},
|
||||||
|
|
||||||
|
CLASS_NAME: "OpenLayers.Control.TransformFeature"
|
||||||
|
});
|
||||||
83
tests/Control/TransformFeature.html
Normal file
83
tests/Control/TransformFeature.html
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src="../../lib/OpenLayers.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
function test_initialize(t) {
|
||||||
|
t.plan(6);
|
||||||
|
var layer = "foo";
|
||||||
|
var control = new OpenLayers.Control.TransformFeature(layer);
|
||||||
|
|
||||||
|
t.ok(control.layer == layer,
|
||||||
|
"constructor sets layer correctly");
|
||||||
|
t.ok(control.dragControl instanceof OpenLayers.Control.DragFeature,
|
||||||
|
"constructor sets the drag control correctly");
|
||||||
|
t.ok(control.box instanceof OpenLayers.Feature.Vector,
|
||||||
|
"box feature created");
|
||||||
|
t.eq(control.handles.length, 8, "8 handles created");
|
||||||
|
t.eq(control.rotationHandles.length, 4, "4 rotation handles created")
|
||||||
|
t.eq(typeof control.rotationHandleSymbolizer, "object",
|
||||||
|
"rotationHandleSymbolizer initialized");
|
||||||
|
control.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_destroy(t) {
|
||||||
|
t.plan(1);
|
||||||
|
var map = new OpenLayers.Map("map");
|
||||||
|
var layer = new OpenLayers.Layer.Vector();
|
||||||
|
map.addLayer(layer);
|
||||||
|
var control = new OpenLayers.Control.TransformFeature(layer);
|
||||||
|
control.dragControl.destroy = function() {
|
||||||
|
t.ok(true,
|
||||||
|
"control.destroy calls destroy on drag control");
|
||||||
|
};
|
||||||
|
control.destroy();
|
||||||
|
map.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_activate(t) {
|
||||||
|
t.plan(3);
|
||||||
|
var map = new OpenLayers.Map("map");
|
||||||
|
var layer = new OpenLayers.Layer.Vector();
|
||||||
|
map.addLayer(layer);
|
||||||
|
var control = new OpenLayers.Control.TransformFeature(layer);
|
||||||
|
map.addControl(control);
|
||||||
|
|
||||||
|
t.ok(!control.dragControl.active,
|
||||||
|
"drag control is not active prior to activating control");
|
||||||
|
control.activate();
|
||||||
|
t.ok(control.dragControl.active,
|
||||||
|
"drag control is active after activating control");
|
||||||
|
t.ok(control.box.layer === layer, "box added to layer");
|
||||||
|
|
||||||
|
map.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_setFeature(t) {
|
||||||
|
t.plan(4);
|
||||||
|
var map = new OpenLayers.Map("map", {allOverlays: true});
|
||||||
|
var layer = new OpenLayers.Layer.Vector();
|
||||||
|
var feature = new OpenLayers.Feature.Vector(
|
||||||
|
OpenLayers.Geometry.fromWKT("POLYGON((-1 -1, 1 -1, 1 1, -1 1))"));
|
||||||
|
layer.addFeatures([feature]);
|
||||||
|
map.addLayer(layer);
|
||||||
|
map.setCenter(new OpenLayers.LonLat(0, 0), 18);
|
||||||
|
var control = new OpenLayers.Control.TransformFeature(layer);
|
||||||
|
map.addControl(control);
|
||||||
|
control.setFeature(feature);
|
||||||
|
|
||||||
|
t.ok(control.active, "control activated on setFeature");
|
||||||
|
t.ok(feature.geometry.getBounds().equals(control.box.geometry.getBounds()), "box positioned correctly");
|
||||||
|
t.geom_eq(control.handles[0].geometry, control.box.geometry.components[0], "handle positioned with box");
|
||||||
|
|
||||||
|
var center = new OpenLayers.LonLat(1, 1);
|
||||||
|
control.box.move(center);
|
||||||
|
t.geom_eq(control.handles[0].geometry, control.box.geometry.components[0], "handle moved with box");
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="map" style="width: 400px; height: 250px;"/>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -35,6 +35,7 @@
|
|||||||
<li>Control/SelectFeature.html</li>
|
<li>Control/SelectFeature.html</li>
|
||||||
<li>Control/Snapping.html</li>
|
<li>Control/Snapping.html</li>
|
||||||
<li>Control/Split.html</li>
|
<li>Control/Split.html</li>
|
||||||
|
<li>Control/TransformFeature.html</li>
|
||||||
<li>Control/WMSGetFeatureInfo.html</li>
|
<li>Control/WMSGetFeatureInfo.html</li>
|
||||||
<li>Control/PanPanel.html</li>
|
<li>Control/PanPanel.html</li>
|
||||||
<li>Events.html</li>
|
<li>Events.html</li>
|
||||||
|
|||||||
Reference in New Issue
Block a user