Methods for programmatically manipulating sketches while digitizing features. r=bartvde (closes #3343)
git-svn-id: http://svn.openlayers.org/trunk/openlayers@12103 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf
This commit is contained in:
38
examples/draw-undo-redo.html
Normal file
38
examples/draw-undo-redo.html
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>OpenLayers Undo/Redo Drawing Methods</title>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0;">
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
|
<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"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1 id="title">Undo/Redo Drawing</h1>
|
||||||
|
<p id="shortdesc">
|
||||||
|
Demonstrates the use of undo & redo methods while drawing.
|
||||||
|
</p>
|
||||||
|
<div id="map" class="smallmap"></div>
|
||||||
|
|
||||||
|
<div id="docs">
|
||||||
|
<p>
|
||||||
|
Use <code>Ctrl-Z</code> or <code>⌘-Z</code> to undo while drawing.
|
||||||
|
Use <code>Ctrl-Y</code> or <code>⌘-Y</code> to redo what you have
|
||||||
|
undone. Use <code>Esc</code> to cancel the current sketch.
|
||||||
|
<p>
|
||||||
|
The <code>control.undo</code> method works on the current
|
||||||
|
sketch, removing the most recently added point.
|
||||||
|
The <code>control.redo</code> method adds back items that were
|
||||||
|
removed from an undo. To fully erase a sketch, call the
|
||||||
|
<code>control.cancel</code> method.
|
||||||
|
</p><p>
|
||||||
|
View the <a href="draw-undo-redo.js" target="_blank">draw-undo-redo.js</a>
|
||||||
|
source to see how this is done.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="draw-undo-redo.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
56
examples/draw-undo-redo.js
Normal file
56
examples/draw-undo-redo.js
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
var map = new OpenLayers.Map({
|
||||||
|
div: "map",
|
||||||
|
layers: [
|
||||||
|
new OpenLayers.Layer.WMS(
|
||||||
|
"Global Imagery",
|
||||||
|
"http://maps.opengeo.org/geowebcache/service/wms",
|
||||||
|
{layers: "bluemarble"},
|
||||||
|
{tileOrigin: new OpenLayers.LonLat(-180, -90)}
|
||||||
|
),
|
||||||
|
new OpenLayers.Layer.Vector()
|
||||||
|
],
|
||||||
|
center: new OpenLayers.LonLat(0, 0),
|
||||||
|
zoom: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
var draw = new OpenLayers.Control.DrawFeature(
|
||||||
|
map.layers[1], OpenLayers.Handler.Path
|
||||||
|
);
|
||||||
|
map.addControl(draw);
|
||||||
|
draw.activate();
|
||||||
|
|
||||||
|
OpenLayers.Event.observe(document, "keydown", function(evt) {
|
||||||
|
var code = evt.keyCode;
|
||||||
|
var handled = false;
|
||||||
|
if (code === 90) {
|
||||||
|
// z
|
||||||
|
if ("metaKey" in evt) {
|
||||||
|
if (evt.metaKey) {
|
||||||
|
draw.undo();
|
||||||
|
handled = true;
|
||||||
|
}
|
||||||
|
} else if (evt.ctrlKey) {
|
||||||
|
draw.undo();
|
||||||
|
handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (code === 89) {
|
||||||
|
// y
|
||||||
|
if ("metaKey" in evt) {
|
||||||
|
if (evt.metaKey) {
|
||||||
|
draw.redo();
|
||||||
|
handled = true;
|
||||||
|
}
|
||||||
|
} else if (evt.ctrlKey) {
|
||||||
|
draw.redo();
|
||||||
|
handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (handled) {
|
||||||
|
OpenLayers.Event.stop(evt);
|
||||||
|
}
|
||||||
|
if (code === 27) {
|
||||||
|
// esc
|
||||||
|
draw.cancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
58
examples/editing-methods.html
Normal file
58
examples/editing-methods.html
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>OpenLayers Editing Methods</title>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0;">
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
|
<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"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1 id="title">Editing Methods</h1>
|
||||||
|
<p id="shortdesc">
|
||||||
|
Demonstrates the use of editing methods for manipulating geometries
|
||||||
|
while drawing.
|
||||||
|
</p>
|
||||||
|
<div id="map" class="smallmap"></div>
|
||||||
|
<ul id="methods">
|
||||||
|
<li><a href="#" id="insertXY">insert x,y</a></li>
|
||||||
|
<li><a href="#" id="insertDeltaXY">insert dx,dy</a></li>
|
||||||
|
<li><a href="#" id="insertDirectionLength">insert direction/length</a></li>
|
||||||
|
<li><a href="#" id="insertDeflectionLength">insert deflection/length</a></li>
|
||||||
|
<li><a href="#" id="finishSketch">finish sketch</a></li>
|
||||||
|
<li><a href="#" id="cancel">cancel sketch</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div id="docs">
|
||||||
|
<p>
|
||||||
|
The <code>control.insertXY</code> method inserts a point at the given
|
||||||
|
map coordinates (x, y) immediately prior to the most recent point
|
||||||
|
(under the mouse).
|
||||||
|
The <code>control.insertDeltaXY</code> method inserts a point at
|
||||||
|
the given offset values (dx, dy) from the previously added point.
|
||||||
|
The <code>control.insertDirectionLength</code> method inserts a
|
||||||
|
point at offset direction and length from the previously added point.
|
||||||
|
Direction is measured counter-clockwise from the positive x-axis.
|
||||||
|
The <code>control.insertDeflectionLength</code> method inserts a
|
||||||
|
point at offset deflection and length from the previously added point.
|
||||||
|
Deflection is measured counter-clockwise from the previous line
|
||||||
|
segment.
|
||||||
|
The <code>control.finishSketch</code> method completes the current
|
||||||
|
sketch without adding the point under the user's mouse. This
|
||||||
|
allows a sketch to be finished without a double-click.
|
||||||
|
The <code>control.cancel</code> method discards the current
|
||||||
|
sketch and leaves the control active.
|
||||||
|
The <code>control.insertXY</code> method may be called before
|
||||||
|
any points are digitized manually. The other methods have no
|
||||||
|
effect until at least one point has been added to the sketch.
|
||||||
|
</p><p>
|
||||||
|
View the <a href="editing-methods.js" target="_blank">editing-methods.js</a>
|
||||||
|
source to see how this is done.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="editing-methods.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
83
examples/editing-methods.js
Normal file
83
examples/editing-methods.js
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
var map = new OpenLayers.Map({
|
||||||
|
div: "map",
|
||||||
|
layers: [
|
||||||
|
new OpenLayers.Layer.WMS(
|
||||||
|
"Global Imagery",
|
||||||
|
"http://maps.opengeo.org/geowebcache/service/wms",
|
||||||
|
{layers: "bluemarble"},
|
||||||
|
{tileOrigin: new OpenLayers.LonLat(-180, -90)}
|
||||||
|
),
|
||||||
|
new OpenLayers.Layer.Vector()
|
||||||
|
],
|
||||||
|
center: new OpenLayers.LonLat(0, 0),
|
||||||
|
zoom: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
var draw = new OpenLayers.Control.DrawFeature(
|
||||||
|
map.layers[1], OpenLayers.Handler.Path
|
||||||
|
);
|
||||||
|
map.addControl(draw);
|
||||||
|
draw.activate();
|
||||||
|
|
||||||
|
// handle clicks on method links
|
||||||
|
$("insertXY").onclick = function() {
|
||||||
|
var values = parseInput(
|
||||||
|
window.prompt(
|
||||||
|
"Enter map coordinates for new point (e.g. '-111, 46')", "x, y"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if (values != null) {
|
||||||
|
draw.insertXY(values[0], values[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$("insertDeltaXY").onclick = function() {
|
||||||
|
var values = parseInput(
|
||||||
|
window.prompt(
|
||||||
|
"Enter offset values for new point (e.g. '15, -10')", "dx, dy"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if (values != null) {
|
||||||
|
draw.insertDeltaXY(values[0], values[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$("insertDirectionLength").onclick = function() {
|
||||||
|
var values = parseInput(
|
||||||
|
window.prompt(
|
||||||
|
"Enter direction and length offset values for new point (e.g. '-45, 10')", "direction, length"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if (values != null) {
|
||||||
|
draw.insertDirectionLength(values[0], values[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$("insertDeflectionLength").onclick = function() {
|
||||||
|
var values = parseInput(
|
||||||
|
window.prompt(
|
||||||
|
"Enter deflection and length offset values for new point (e.g. '15, 20')", "deflection, length"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if (values != null) {
|
||||||
|
draw.insertDeflectionLength(values[0], values[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$("cancel").onclick = function() {
|
||||||
|
draw.cancel();
|
||||||
|
}
|
||||||
|
$("finishSketch").onclick = function() {
|
||||||
|
draw.finishSketch();
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseInput(text) {
|
||||||
|
var values = text.split(",");
|
||||||
|
if (values.length !== 2) {
|
||||||
|
values = null;
|
||||||
|
} else {
|
||||||
|
values[0] = parseFloat(values[0]);
|
||||||
|
values[1] = parseFloat(values[1]);
|
||||||
|
if (isNaN(values[0]) || isNaN(values[1])) {
|
||||||
|
window.alert("The two values must be numeric.");
|
||||||
|
values = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
@@ -121,6 +121,106 @@ OpenLayers.Control.DrawFeature = OpenLayers.Class(OpenLayers.Control, {
|
|||||||
this.events.triggerEvent("featureadded",{feature : feature});
|
this.events.triggerEvent("featureadded",{feature : feature});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIMethod: insertXY
|
||||||
|
* Insert a point in the current sketch given x & y coordinates.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* x - {Number} The x-coordinate of the point.
|
||||||
|
* y - {Number} The y-coordinate of the point.
|
||||||
|
*/
|
||||||
|
insertXY: function(x, y) {
|
||||||
|
if (this.handler && this.handler.line) {
|
||||||
|
this.handler.insertXY(x, y);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIMethod: insertDeltaXY
|
||||||
|
* Insert a point given offsets from the previously inserted point.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* dx - {Number} The x-coordinate offset of the point.
|
||||||
|
* dy - {Number} The y-coordinate offset of the point.
|
||||||
|
*/
|
||||||
|
insertDeltaXY: function(dx, dy) {
|
||||||
|
if (this.handler && this.handler.line) {
|
||||||
|
this.handler.insertDeltaXY(dx, dy);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIMethod: insertDirectionLength
|
||||||
|
* Insert a point in the current sketch given a direction and a length.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* direction - {Number} Degrees clockwise from the positive x-axis.
|
||||||
|
* length - {Number} Distance from the previously drawn point.
|
||||||
|
*/
|
||||||
|
insertDirectionLength: function(direction, length) {
|
||||||
|
if (this.handler && this.handler.line) {
|
||||||
|
this.handler.insertDirectionLength(direction, length);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIMethod: insertDeflectionLength
|
||||||
|
* Insert a point in the current sketch given a deflection and a length.
|
||||||
|
* The deflection should be degrees clockwise from the previously
|
||||||
|
* digitized segment.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* deflection - {Number} Degrees clockwise from the previous segment.
|
||||||
|
* length - {Number} Distance from the previously drawn point.
|
||||||
|
*/
|
||||||
|
insertDeflectionLength: function(deflection, length) {
|
||||||
|
if (this.handler && this.handler.line) {
|
||||||
|
this.handler.insertDeflectionLength(deflection, length);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIMethod: undo
|
||||||
|
* Remove the most recently added point in the current sketch geometry.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* {Boolean} An edit was undone.
|
||||||
|
*/
|
||||||
|
undo: function() {
|
||||||
|
return this.handler.undo && this.handler.undo();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIMethod: redo
|
||||||
|
* Reinsert the most recently removed point resulting from an <undo> call.
|
||||||
|
* The undo stack is deleted whenever a point is added by other means.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* {Boolean} An edit was redone.
|
||||||
|
*/
|
||||||
|
redo: function() {
|
||||||
|
return this.handler.redo && this.handler.redo();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIMethod: finishSketch
|
||||||
|
* Finishes the sketch without including the currently drawn point.
|
||||||
|
* This method can be called to terminate drawing programmatically
|
||||||
|
* instead of waiting for the user to end the sketch.
|
||||||
|
*/
|
||||||
|
finishSketch: function() {
|
||||||
|
this.handler.finishGeometry();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APIMethod: cancel
|
||||||
|
* Cancel the current sketch. This removes the current sketch and keeps
|
||||||
|
* the drawing control active.
|
||||||
|
*/
|
||||||
|
cancel: function() {
|
||||||
|
this.handler.cancel();
|
||||||
|
},
|
||||||
|
|
||||||
CLASS_NAME: "OpenLayers.Control.DrawFeature"
|
CLASS_NAME: "OpenLayers.Control.DrawFeature"
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -183,14 +183,20 @@ OpenLayers.Geometry.Collection = OpenLayers.Class(OpenLayers.Geometry, {
|
|||||||
*
|
*
|
||||||
* Parameters:
|
* Parameters:
|
||||||
* components - {Array(<OpenLayers.Geometry>)} The components to be removed
|
* components - {Array(<OpenLayers.Geometry>)} The components to be removed
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* {Boolean} A component was removed.
|
||||||
*/
|
*/
|
||||||
removeComponents: function(components) {
|
removeComponents: function(components) {
|
||||||
|
var removed = false;
|
||||||
|
|
||||||
if(!(OpenLayers.Util.isArray(components))) {
|
if(!(OpenLayers.Util.isArray(components))) {
|
||||||
components = [components];
|
components = [components];
|
||||||
}
|
}
|
||||||
for(var i=components.length-1; i>=0; --i) {
|
for(var i=components.length-1; i>=0; --i) {
|
||||||
this.removeComponent(components[i]);
|
removed = this.removeComponent(components[i]) || removed;
|
||||||
}
|
}
|
||||||
|
return removed;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -199,6 +205,9 @@ OpenLayers.Geometry.Collection = OpenLayers.Class(OpenLayers.Geometry, {
|
|||||||
*
|
*
|
||||||
* Parameters:
|
* Parameters:
|
||||||
* component - {<OpenLayers.Geometry>}
|
* component - {<OpenLayers.Geometry>}
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* {Boolean} The component was removed.
|
||||||
*/
|
*/
|
||||||
removeComponent: function(component) {
|
removeComponent: function(component) {
|
||||||
|
|
||||||
@@ -207,6 +216,7 @@ OpenLayers.Geometry.Collection = OpenLayers.Class(OpenLayers.Geometry, {
|
|||||||
// clearBounds() so that it gets recalculated on the next call
|
// clearBounds() so that it gets recalculated on the next call
|
||||||
// to this.getBounds();
|
// to this.getBounds();
|
||||||
this.clearBounds();
|
this.clearBounds();
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -37,12 +37,17 @@ OpenLayers.Geometry.LineString = OpenLayers.Class(OpenLayers.Geometry.Curve, {
|
|||||||
*
|
*
|
||||||
* Parameters:
|
* Parameters:
|
||||||
* point - {<OpenLayers.Geometry.Point>} The point to be removed
|
* point - {<OpenLayers.Geometry.Point>} The point to be removed
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* {Boolean} The component was removed.
|
||||||
*/
|
*/
|
||||||
removeComponent: function(point) {
|
removeComponent: function(point) {
|
||||||
if ( this.components && (this.components.length > 2)) {
|
var removed = this.components && (this.components.length > 2);
|
||||||
|
if (removed) {
|
||||||
OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this,
|
OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this,
|
||||||
arguments);
|
arguments);
|
||||||
}
|
}
|
||||||
|
return removed;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -91,10 +91,13 @@ OpenLayers.Geometry.LinearRing = OpenLayers.Class(
|
|||||||
*
|
*
|
||||||
* Parameters:
|
* Parameters:
|
||||||
* point - {<OpenLayers.Geometry.Point>}
|
* point - {<OpenLayers.Geometry.Point>}
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* {Boolean} The component was removed.
|
||||||
*/
|
*/
|
||||||
removeComponent: function(point) {
|
removeComponent: function(point) {
|
||||||
if (this.components.length > 3) {
|
var removed = this.components && (this.components.length > 3);
|
||||||
|
if (removed) {
|
||||||
//remove last point
|
//remove last point
|
||||||
this.components.pop();
|
this.components.pop();
|
||||||
|
|
||||||
@@ -106,6 +109,7 @@ OpenLayers.Geometry.LinearRing = OpenLayers.Class(
|
|||||||
OpenLayers.Geometry.Collection.prototype.addComponent.apply(this,
|
OpenLayers.Geometry.Collection.prototype.addComponent.apply(this,
|
||||||
[firstPoint]);
|
[firstPoint]);
|
||||||
}
|
}
|
||||||
|
return removed;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -67,6 +67,12 @@ OpenLayers.Handler.Path = OpenLayers.Class(OpenLayers.Handler.Point, {
|
|||||||
*/
|
*/
|
||||||
timerId: null,
|
timerId: null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Property: redoStack
|
||||||
|
* {Array} Stack containing points removed with <undo>.
|
||||||
|
*/
|
||||||
|
redoStack: null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor: OpenLayers.Handler.Path
|
* Constructor: OpenLayers.Handler.Path
|
||||||
* Create a new path hander
|
* Create a new path hander
|
||||||
@@ -170,6 +176,129 @@ OpenLayers.Handler.Path = OpenLayers.Class(OpenLayers.Handler.Point, {
|
|||||||
this.callback("point", [this.point.geometry, this.getGeometry()]);
|
this.callback("point", [this.point.geometry, this.getGeometry()]);
|
||||||
this.callback("modify", [this.point.geometry, this.getSketch()]);
|
this.callback("modify", [this.point.geometry, this.getSketch()]);
|
||||||
this.drawFeature();
|
this.drawFeature();
|
||||||
|
delete this.redoStack;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method: insertXY
|
||||||
|
* Insert a point in the current sketch given x & y coordinates. The new
|
||||||
|
* point is inserted immediately before the most recently drawn point.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* x - {Number} The x-coordinate of the point.
|
||||||
|
* y - {Number} The y-coordinate of the point.
|
||||||
|
*/
|
||||||
|
insertXY: function(x, y) {
|
||||||
|
this.line.geometry.addComponent(
|
||||||
|
new OpenLayers.Geometry.Point(x, y),
|
||||||
|
this.getCurrentPointIndex()
|
||||||
|
);
|
||||||
|
this.drawFeature();
|
||||||
|
delete this.redoStack;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method: insertDeltaXY
|
||||||
|
* Insert a point given offsets from the previously inserted point.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* dx - {Number} The x-coordinate offset of the point.
|
||||||
|
* dy - {Number} The y-coordinate offset of the point.
|
||||||
|
*/
|
||||||
|
insertDeltaXY: function(dx, dy) {
|
||||||
|
var previousIndex = this.getCurrentPointIndex() - 1;
|
||||||
|
var p0 = this.line.geometry.components[previousIndex];
|
||||||
|
if (p0 && !isNaN(p0.x) && !isNaN(p0.y)) {
|
||||||
|
this.insertXY(p0.x + dx, p0.y + dy);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method: insertDirectionLength
|
||||||
|
* Insert a point in the current sketch given a direction and a length.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* direction - {Number} Degrees clockwise from the positive x-axis.
|
||||||
|
* length - {Number} Distance from the previously drawn point.
|
||||||
|
*/
|
||||||
|
insertDirectionLength: function(direction, length) {
|
||||||
|
direction *= Math.PI / 180;
|
||||||
|
var dx = length * Math.cos(direction);
|
||||||
|
var dy = length * Math.sin(direction);
|
||||||
|
this.insertDeltaXY(dx, dy);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method: insertDeflectionLength
|
||||||
|
* Insert a point in the current sketch given a deflection and a length.
|
||||||
|
* The deflection should be degrees clockwise from the previously
|
||||||
|
* digitized segment.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* deflection - {Number} Degrees clockwise from the previous segment.
|
||||||
|
* length - {Number} Distance from the previously drawn point.
|
||||||
|
*/
|
||||||
|
insertDeflectionLength: function(deflection, length) {
|
||||||
|
var previousIndex = this.getCurrentPointIndex() - 1;
|
||||||
|
if (previousIndex > 0) {
|
||||||
|
var p1 = this.line.geometry.components[previousIndex];
|
||||||
|
var p0 = this.line.geometry.components[previousIndex-1];
|
||||||
|
var theta = Math.atan2(p1.y - p0.y, p1.x - p0.x);
|
||||||
|
this.insertDirectionLength(
|
||||||
|
(theta * 180 / Math.PI) + deflection, length
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method: getCurrentPointIndex
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* {Number} The index of the most recently drawn point.
|
||||||
|
*/
|
||||||
|
getCurrentPointIndex: function() {
|
||||||
|
return this.line.geometry.components.length - 1;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method: undo
|
||||||
|
* Remove the most recently added point in the sketch geometry.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* {Boolean} A point was removed.
|
||||||
|
*/
|
||||||
|
undo: function() {
|
||||||
|
var geometry = this.line.geometry;
|
||||||
|
var components = geometry.components;
|
||||||
|
var index = this.getCurrentPointIndex() - 1;
|
||||||
|
var target = components[index];
|
||||||
|
var undone = geometry.removeComponent(target);
|
||||||
|
if (undone) {
|
||||||
|
if (!this.redoStack) {
|
||||||
|
this.redoStack = [];
|
||||||
|
}
|
||||||
|
this.redoStack.push(target);
|
||||||
|
this.drawFeature();
|
||||||
|
}
|
||||||
|
return undone;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method: redo
|
||||||
|
* Reinsert the most recently removed point resulting from an <undo> call.
|
||||||
|
* The undo stack is deleted whenever a point is added by other means.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* {Boolean} A point was added.
|
||||||
|
*/
|
||||||
|
redo: function() {
|
||||||
|
var target = this.redoStack && this.redoStack.pop();
|
||||||
|
if (target) {
|
||||||
|
this.line.geometry.addComponent(target, this.getCurrentPointIndex());
|
||||||
|
this.drawFeature();
|
||||||
|
}
|
||||||
|
return !!target;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -127,7 +127,17 @@ OpenLayers.Handler.Polygon = OpenLayers.Class(OpenLayers.Handler.Path, {
|
|||||||
}
|
}
|
||||||
OpenLayers.Handler.Path.prototype.addPoint.apply(this, arguments);
|
OpenLayers.Handler.Path.prototype.addPoint.apply(this, arguments);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method: getCurrentPointIndex
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* {Number} The index of the most recently drawn point.
|
||||||
|
*/
|
||||||
|
getCurrentPointIndex: function() {
|
||||||
|
return this.line.geometry.components.length - 2;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method: enforceTopology
|
* Method: enforceTopology
|
||||||
* Simple topology enforcement for drawing interior rings. Ensures vertices
|
* Simple topology enforcement for drawing interior rings. Ensures vertices
|
||||||
|
|||||||
@@ -534,6 +534,306 @@
|
|||||||
map.destroy();
|
map.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper functions for editing method tests
|
||||||
|
*/
|
||||||
|
function editingMethodsSetup() {
|
||||||
|
var map = new OpenLayers.Map("map", {
|
||||||
|
resolutions: [1]
|
||||||
|
});
|
||||||
|
var layer = new OpenLayers.Layer.Vector("foo", {
|
||||||
|
maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
|
||||||
|
isBaseLayer: true
|
||||||
|
});
|
||||||
|
map.addLayer(layer);
|
||||||
|
var control = new OpenLayers.Control.DrawFeature(
|
||||||
|
layer, OpenLayers.Handler.Path
|
||||||
|
);
|
||||||
|
map.addControl(control);
|
||||||
|
map.setCenter(new OpenLayers.LonLat(0, 0), 0);
|
||||||
|
|
||||||
|
control.activate();
|
||||||
|
return {
|
||||||
|
handler: control.handler,
|
||||||
|
map: map
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function userClick(handler, x, y) {
|
||||||
|
var px = new OpenLayers.Pixel(x, y);
|
||||||
|
handler.mousemove({type: "mousemove", xy: px});
|
||||||
|
handler.mousedown({type: "mousedown", xy: px});
|
||||||
|
handler.mouseup({type: "mouseup", xy: px});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Editing method tests: insertXY, insertDeltaXY, insertDirectionXY,
|
||||||
|
* insertDeflectionXY, undo, and redo
|
||||||
|
*/
|
||||||
|
function test_insertXY(t) {
|
||||||
|
t.plan(3);
|
||||||
|
var obj = editingMethodsSetup();
|
||||||
|
var map = obj.map;
|
||||||
|
var handler = obj.handler;
|
||||||
|
|
||||||
|
// add points at px(0, 0) and px(10, 10)
|
||||||
|
userClick(handler, 0, 0);
|
||||||
|
userClick(handler, 10, 10);
|
||||||
|
handler.mousemove({type: "mousemove", xy: new OpenLayers.Pixel(50, 50)});
|
||||||
|
|
||||||
|
t.eq(handler.line.geometry.components.length, 3, "line has three points after two clicks");
|
||||||
|
|
||||||
|
// programmatically add a point
|
||||||
|
handler.insertXY(5, 6);
|
||||||
|
t.eq(handler.line.geometry.components.length, 4, "line has four points after insertXY");
|
||||||
|
t.geom_eq(
|
||||||
|
handler.line.geometry.components[2],
|
||||||
|
new OpenLayers.Geometry.Point(5, 6),
|
||||||
|
"third point comes from insertXY"
|
||||||
|
);
|
||||||
|
|
||||||
|
map.destroy();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_insertDeltaXY(t) {
|
||||||
|
t.plan(3);
|
||||||
|
var obj = editingMethodsSetup();
|
||||||
|
var map = obj.map;
|
||||||
|
var handler = obj.handler;
|
||||||
|
|
||||||
|
// add points at px(0, 0) and px(10, 10)
|
||||||
|
userClick(handler, 0, 0);
|
||||||
|
userClick(handler, 10, 10);
|
||||||
|
handler.mousemove({type: "mousemove", xy: new OpenLayers.Pixel(50, 50)});
|
||||||
|
|
||||||
|
t.eq(handler.line.geometry.components.length, 3, "line has three points after two clicks");
|
||||||
|
|
||||||
|
// programmatically add a point
|
||||||
|
handler.insertDeltaXY(1, 2);
|
||||||
|
t.eq(handler.line.geometry.components.length, 4, "line has four points after insert");
|
||||||
|
// expect a point that is offset from previous point
|
||||||
|
var exp = handler.line.geometry.components[1].clone();
|
||||||
|
exp.move(1, 2);
|
||||||
|
t.geom_eq(
|
||||||
|
handler.line.geometry.components[2], exp,
|
||||||
|
"third point is offset by dx,dy from second point"
|
||||||
|
);
|
||||||
|
|
||||||
|
map.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_insertDirectionLength(t) {
|
||||||
|
t.plan(4);
|
||||||
|
var obj = editingMethodsSetup();
|
||||||
|
var map = obj.map;
|
||||||
|
var handler = obj.handler;
|
||||||
|
|
||||||
|
// add points at px(0, 0) and px(10, 10)
|
||||||
|
userClick(handler, 0, 0);
|
||||||
|
userClick(handler, 10, 10);
|
||||||
|
handler.mousemove({type: "mousemove", xy: new OpenLayers.Pixel(50, 50)});
|
||||||
|
|
||||||
|
t.eq(handler.line.geometry.components.length, 3, "line has three points after two clicks");
|
||||||
|
|
||||||
|
// programmatically add a point
|
||||||
|
handler.insertDirectionLength(45, 2);
|
||||||
|
t.eq(handler.line.geometry.components.length, 4, "line has four points after insert");
|
||||||
|
var p1 = handler.line.geometry.components[1];
|
||||||
|
var p2 = handler.line.geometry.components[2];
|
||||||
|
|
||||||
|
var direction = Math.atan2(p2.y - p1.y, p2.x - p1.x) * 180 / Math.PI;
|
||||||
|
t.eq(direction.toFixed(4), (45).toFixed(4), "inserted point offset with correct direction");
|
||||||
|
var length = Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
|
||||||
|
t.eq(length.toFixed(4), (2).toFixed(4), "inserted point offset with correct length");
|
||||||
|
|
||||||
|
map.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_insertDeflectionLength(t) {
|
||||||
|
t.plan(4);
|
||||||
|
var obj = editingMethodsSetup();
|
||||||
|
var map = obj.map;
|
||||||
|
var handler = obj.handler;
|
||||||
|
|
||||||
|
// add points at px(0, 0) and px(10, 10)
|
||||||
|
userClick(handler, 0, 0);
|
||||||
|
userClick(handler, 10, 10);
|
||||||
|
handler.mousemove({type: "mousemove", xy: new OpenLayers.Pixel(50, 50)});
|
||||||
|
|
||||||
|
t.eq(handler.line.geometry.components.length, 3, "line has three points after two clicks");
|
||||||
|
var p0 = handler.line.geometry.components[0];
|
||||||
|
var p1 = handler.line.geometry.components[1];
|
||||||
|
// angle of first segment
|
||||||
|
var dir0 = Math.atan2(p1.y - p0.y, p1.x - p0.x) * 180 / Math.PI;
|
||||||
|
|
||||||
|
// programmatically add a point
|
||||||
|
handler.insertDeflectionLength(-30, 5);
|
||||||
|
t.eq(handler.line.geometry.components.length, 4, "line has four points after insert");
|
||||||
|
var p2 = handler.line.geometry.components[2];
|
||||||
|
// angle of second segment
|
||||||
|
var dir1 = Math.atan2(p2.y - p1.y, p2.x - p1.x) * 180 / Math.PI;
|
||||||
|
|
||||||
|
var deflection = dir1 - dir0;
|
||||||
|
t.eq(deflection.toFixed(4), (-30).toFixed(4), "inserted point offset with correct deflection");
|
||||||
|
|
||||||
|
var length = Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
|
||||||
|
t.eq(length.toFixed(4), (5).toFixed(4), "inserted point offset with correct length");
|
||||||
|
|
||||||
|
map.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_undoredo1(t) {
|
||||||
|
t.plan(4);
|
||||||
|
var obj = editingMethodsSetup();
|
||||||
|
var map = obj.map;
|
||||||
|
var handler = obj.handler;
|
||||||
|
|
||||||
|
// add points and move mouse
|
||||||
|
userClick(handler, 0, 0);
|
||||||
|
userClick(handler, 10, 10);
|
||||||
|
userClick(handler, 50, 10);
|
||||||
|
handler.mousemove({type: "mousemove", xy: new OpenLayers.Pixel(50, 50)});
|
||||||
|
var original = handler.line.geometry.clone();
|
||||||
|
var len = original.components.length;
|
||||||
|
t.eq(len, 4, "original has four points after three clicks");
|
||||||
|
|
||||||
|
// one undo
|
||||||
|
handler.undo();
|
||||||
|
var currentLen = handler.line.geometry.components.length;
|
||||||
|
t.eq(currentLen, len-1, "one point removed on undo");
|
||||||
|
t.geom_eq(
|
||||||
|
handler.line.geometry.components[currentLen-1],
|
||||||
|
original.components[len-1],
|
||||||
|
"current point (mouse position) remains the same after undo"
|
||||||
|
);
|
||||||
|
// one redo
|
||||||
|
handler.redo();
|
||||||
|
t.geom_eq(original, handler.line.geometry, "one redo undoes one undo");
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
map.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_undoredo2(t) {
|
||||||
|
t.plan(8);
|
||||||
|
var obj = editingMethodsSetup();
|
||||||
|
var map = obj.map;
|
||||||
|
var handler = obj.handler;
|
||||||
|
|
||||||
|
// add points and move mouse
|
||||||
|
userClick(handler, 0, 0);
|
||||||
|
userClick(handler, 10, 10);
|
||||||
|
userClick(handler, 50, 10);
|
||||||
|
handler.mousemove({type: "mousemove", xy: new OpenLayers.Pixel(50, 50)});
|
||||||
|
var original = handler.line.geometry.clone();
|
||||||
|
var len = original.components.length;
|
||||||
|
t.eq(len, 4, "original has four points after three clicks");
|
||||||
|
|
||||||
|
// two undos
|
||||||
|
handler.undo();
|
||||||
|
handler.undo();
|
||||||
|
var currentLen = handler.line.geometry.components.length;
|
||||||
|
t.eq(currentLen, len-2, "two points removed on two undos");
|
||||||
|
t.geom_eq(
|
||||||
|
handler.line.geometry.components[currentLen-1],
|
||||||
|
original.components[len-1],
|
||||||
|
"current point (mouse position) remains the same after two undos"
|
||||||
|
);
|
||||||
|
// first redo
|
||||||
|
handler.redo();
|
||||||
|
currentLen = handler.line.geometry.components.length;
|
||||||
|
t.eq(currentLen, len-1, "point added in first redo");
|
||||||
|
t.geom_eq(
|
||||||
|
handler.line.geometry.components[currentLen-2],
|
||||||
|
original.components[len-3],
|
||||||
|
"correct point restored in first redo"
|
||||||
|
);
|
||||||
|
|
||||||
|
// second redo
|
||||||
|
handler.redo();
|
||||||
|
currentLen = handler.line.geometry.components.length;
|
||||||
|
t.eq(currentLen, len, "point added in second redo");
|
||||||
|
t.geom_eq(
|
||||||
|
handler.line.geometry.components[currentLen-2],
|
||||||
|
original.components[len-2],
|
||||||
|
"correct point restored in second redo"
|
||||||
|
);
|
||||||
|
t.geom_eq(handler.line.geometry, original, "correct geometry");
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
map.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_undoredo3(t) {
|
||||||
|
t.plan(3);
|
||||||
|
var obj = editingMethodsSetup();
|
||||||
|
var map = obj.map;
|
||||||
|
var handler = obj.handler;
|
||||||
|
|
||||||
|
// add points and move mouse
|
||||||
|
userClick(handler, 0, 0);
|
||||||
|
userClick(handler, 10, 10);
|
||||||
|
userClick(handler, 50, 10);
|
||||||
|
handler.mousemove({type: "mousemove", xy: new OpenLayers.Pixel(50, 50)});
|
||||||
|
var original = handler.line.geometry.clone();
|
||||||
|
var len = original.components.length;
|
||||||
|
t.eq(len, 4, "original has four points after three clicks");
|
||||||
|
|
||||||
|
// gratuitous redos
|
||||||
|
var trouble = false;
|
||||||
|
try {
|
||||||
|
handler.undo();
|
||||||
|
handler.undo();
|
||||||
|
handler.redo();
|
||||||
|
handler.redo();
|
||||||
|
handler.redo();
|
||||||
|
handler.redo();
|
||||||
|
handler.redo();
|
||||||
|
} catch (err) {
|
||||||
|
trouble = true;
|
||||||
|
}
|
||||||
|
t.ok(!trouble, "extra redos cause no ill effects");
|
||||||
|
t.geom_eq(handler.line.geometry, original, "correct geometry");
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
map.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_undoredo4(t) {
|
||||||
|
t.plan(3);
|
||||||
|
var obj = editingMethodsSetup();
|
||||||
|
var map = obj.map;
|
||||||
|
var handler = obj.handler;
|
||||||
|
|
||||||
|
// add points and move mouse
|
||||||
|
userClick(handler, 0, 0);
|
||||||
|
userClick(handler, 10, 10);
|
||||||
|
userClick(handler, 50, 10);
|
||||||
|
handler.mousemove({type: "mousemove", xy: new OpenLayers.Pixel(50, 50)});
|
||||||
|
var original = handler.line.geometry.clone();
|
||||||
|
var len = original.components.length;
|
||||||
|
t.eq(len, 4, "original has four points after three clicks");
|
||||||
|
|
||||||
|
// gratuitous undos
|
||||||
|
var trouble = false;
|
||||||
|
try {
|
||||||
|
handler.undo();
|
||||||
|
handler.undo();
|
||||||
|
handler.undo();
|
||||||
|
handler.undo();
|
||||||
|
handler.undo();
|
||||||
|
handler.undo();
|
||||||
|
handler.undo();
|
||||||
|
} catch (err) {
|
||||||
|
trouble = true;
|
||||||
|
}
|
||||||
|
t.ok(!trouble, "extra undos cause no ill effects");
|
||||||
|
t.eq(handler.line.geometry.components.length, 2, "still left with two points after many undos")
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
map.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Sequence tests
|
// Sequence tests
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -717,6 +717,50 @@
|
|||||||
map.destroy();
|
map.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function test_insertXY(t) {
|
||||||
|
t.plan(3);
|
||||||
|
var map = new OpenLayers.Map("map", {
|
||||||
|
resolutions: [1]
|
||||||
|
});
|
||||||
|
var layer = new OpenLayers.Layer.Vector("foo", {
|
||||||
|
maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
|
||||||
|
isBaseLayer: true
|
||||||
|
});
|
||||||
|
map.addLayer(layer);
|
||||||
|
var control = new OpenLayers.Control.DrawFeature(
|
||||||
|
layer, OpenLayers.Handler.Polygon
|
||||||
|
);
|
||||||
|
map.addControl(control);
|
||||||
|
map.setCenter(new OpenLayers.LonLat(0, 0), 0);
|
||||||
|
|
||||||
|
control.activate();
|
||||||
|
var handler = control.handler;
|
||||||
|
|
||||||
|
function userClick(x, y) {
|
||||||
|
var px = new OpenLayers.Pixel(x, y);
|
||||||
|
handler.mousemove({type: "mousemove", xy: px});
|
||||||
|
handler.mousedown({type: "mousedown", xy: px});
|
||||||
|
handler.mouseup({type: "mouseup", xy: px});
|
||||||
|
}
|
||||||
|
|
||||||
|
// add points at px(0, 0) and px(10, 10)
|
||||||
|
userClick(0, 0);
|
||||||
|
userClick(10, 10);
|
||||||
|
t.eq(handler.line.geometry.components.length, 4, "ring has four points after two clicks");
|
||||||
|
|
||||||
|
// programmatically add a point
|
||||||
|
handler.insertXY(5, 6);
|
||||||
|
t.eq(handler.line.geometry.components.length, 5, "ring has five points after insertXY");
|
||||||
|
t.geom_eq(
|
||||||
|
handler.line.geometry.components[2],
|
||||||
|
new OpenLayers.Geometry.Point(5, 6),
|
||||||
|
"third point comes from insertXY"
|
||||||
|
);
|
||||||
|
|
||||||
|
map.destroy();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Sequence tests
|
// Sequence tests
|
||||||
//
|
//
|
||||||
|
|||||||
Reference in New Issue
Block a user