[OpenLayers-Commits] r12103 - in trunk/openlayers: examples lib/OpenLayers/Control lib/OpenLayers/Geometry lib/OpenLayers/Handler tests/Handler

commits-20090109 at openlayers.org commits-20090109 at openlayers.org
Fri Jun 17 14:59:18 EDT 2011


Author: tschaub
Date: 2011-06-17 11:59:16 -0700 (Fri, 17 Jun 2011)
New Revision: 12103

Added:
   trunk/openlayers/examples/draw-undo-redo.html
   trunk/openlayers/examples/draw-undo-redo.js
   trunk/openlayers/examples/editing-methods.html
   trunk/openlayers/examples/editing-methods.js
Modified:
   trunk/openlayers/lib/OpenLayers/Control/DrawFeature.js
   trunk/openlayers/lib/OpenLayers/Geometry/Collection.js
   trunk/openlayers/lib/OpenLayers/Geometry/LineString.js
   trunk/openlayers/lib/OpenLayers/Geometry/LinearRing.js
   trunk/openlayers/lib/OpenLayers/Handler/Path.js
   trunk/openlayers/lib/OpenLayers/Handler/Polygon.js
   trunk/openlayers/tests/Handler/Path.html
   trunk/openlayers/tests/Handler/Polygon.html
Log:
Methods for programmatically manipulating sketches while digitizing features. r=bartvde (closes #3343)

Added: trunk/openlayers/examples/draw-undo-redo.html
===================================================================
--- trunk/openlayers/examples/draw-undo-redo.html	                        (rev 0)
+++ trunk/openlayers/examples/draw-undo-redo.html	2011-06-17 18:59:16 UTC (rev 12103)
@@ -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 &amp; 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>

Added: trunk/openlayers/examples/draw-undo-redo.js
===================================================================
--- trunk/openlayers/examples/draw-undo-redo.js	                        (rev 0)
+++ trunk/openlayers/examples/draw-undo-redo.js	2011-06-17 18:59:16 UTC (rev 12103)
@@ -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();
+    }
+});
\ No newline at end of file

Added: trunk/openlayers/examples/editing-methods.html
===================================================================
--- trunk/openlayers/examples/editing-methods.html	                        (rev 0)
+++ trunk/openlayers/examples/editing-methods.html	2011-06-17 18:59:16 UTC (rev 12103)
@@ -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>

Added: trunk/openlayers/examples/editing-methods.js
===================================================================
--- trunk/openlayers/examples/editing-methods.js	                        (rev 0)
+++ trunk/openlayers/examples/editing-methods.js	2011-06-17 18:59:16 UTC (rev 12103)
@@ -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;
+}
\ No newline at end of file

Modified: trunk/openlayers/lib/OpenLayers/Control/DrawFeature.js
===================================================================
--- trunk/openlayers/lib/OpenLayers/Control/DrawFeature.js	2011-06-17 18:45:03 UTC (rev 12102)
+++ trunk/openlayers/lib/OpenLayers/Control/DrawFeature.js	2011-06-17 18:59:16 UTC (rev 12103)
@@ -121,6 +121,106 @@
             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"
 });

Modified: trunk/openlayers/lib/OpenLayers/Geometry/Collection.js
===================================================================
--- trunk/openlayers/lib/OpenLayers/Geometry/Collection.js	2011-06-17 18:45:03 UTC (rev 12102)
+++ trunk/openlayers/lib/OpenLayers/Geometry/Collection.js	2011-06-17 18:59:16 UTC (rev 12103)
@@ -183,14 +183,20 @@
      *
      * Parameters:
      * components - {Array(<OpenLayers.Geometry>)} The components to be removed
+     *
+     * Returns: 
+     * {Boolean} A component was removed.
      */
     removeComponents: function(components) {
+        var removed = false;
+
         if(!(OpenLayers.Util.isArray(components))) {
             components = [components];
         }
         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 @@
      *
      * Parameters:
      * component - {<OpenLayers.Geometry>} 
+     *
+     * Returns: 
+     * {Boolean} The component was removed.
      */
     removeComponent: function(component) {
         
@@ -207,6 +216,7 @@
         // clearBounds() so that it gets recalculated on the next call
         // to this.getBounds();
         this.clearBounds();
+        return true;
     },
 
     /**

Modified: trunk/openlayers/lib/OpenLayers/Geometry/LineString.js
===================================================================
--- trunk/openlayers/lib/OpenLayers/Geometry/LineString.js	2011-06-17 18:45:03 UTC (rev 12102)
+++ trunk/openlayers/lib/OpenLayers/Geometry/LineString.js	2011-06-17 18:59:16 UTC (rev 12103)
@@ -37,12 +37,17 @@
      *
      * Parameters: 
      * point - {<OpenLayers.Geometry.Point>} The point to be removed
+     *
+     * Returns: 
+     * {Boolean} The component was removed.
      */
     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, 
                                                                   arguments);
         }
+        return removed;
     },
     
     /**

Modified: trunk/openlayers/lib/OpenLayers/Geometry/LinearRing.js
===================================================================
--- trunk/openlayers/lib/OpenLayers/Geometry/LinearRing.js	2011-06-17 18:45:03 UTC (rev 12102)
+++ trunk/openlayers/lib/OpenLayers/Geometry/LinearRing.js	2011-06-17 18:59:16 UTC (rev 12103)
@@ -91,10 +91,13 @@
      *
      * Parameters:
      * point - {<OpenLayers.Geometry.Point>}
+     *
+     * Returns: 
+     * {Boolean} The component was removed.
      */
     removeComponent: function(point) {
-        if (this.components.length > 3) {
-
+        var removed = this.components && (this.components.length > 3);
+        if (removed) {
             //remove last point
             this.components.pop();
             
@@ -106,6 +109,7 @@
             OpenLayers.Geometry.Collection.prototype.addComponent.apply(this, 
                                                                 [firstPoint]);
         }
+        return removed;
     },
     
     /**

Modified: trunk/openlayers/lib/OpenLayers/Handler/Path.js
===================================================================
--- trunk/openlayers/lib/OpenLayers/Handler/Path.js	2011-06-17 18:45:03 UTC (rev 12102)
+++ trunk/openlayers/lib/OpenLayers/Handler/Path.js	2011-06-17 18:59:16 UTC (rev 12103)
@@ -68,6 +68,12 @@
     timerId: null,
 
     /**
+     * Property: redoStack
+     * {Array} Stack containing points removed with <undo>.
+     */
+    redoStack: null,
+
+    /**
      * Constructor: OpenLayers.Handler.Path
      * Create a new path hander
      *
@@ -170,9 +176,132 @@
         this.callback("point", [this.point.geometry, this.getGeometry()]);
         this.callback("modify", [this.point.geometry, this.getSketch()]);
         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;
+    },
+    
+    /**
      * Method: freehandMode
      * Determine whether to behave in freehand mode or not.
      *

Modified: trunk/openlayers/lib/OpenLayers/Handler/Polygon.js
===================================================================
--- trunk/openlayers/lib/OpenLayers/Handler/Polygon.js	2011-06-17 18:45:03 UTC (rev 12102)
+++ trunk/openlayers/lib/OpenLayers/Handler/Polygon.js	2011-06-17 18:59:16 UTC (rev 12103)
@@ -127,8 +127,18 @@
         }
         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
      * Simple topology enforcement for drawing interior rings.  Ensures vertices
      *     of interior rings are contained by exterior ring.  Other topology 

Modified: trunk/openlayers/tests/Handler/Path.html
===================================================================
--- trunk/openlayers/tests/Handler/Path.html	2011-06-17 18:45:03 UTC (rev 12102)
+++ trunk/openlayers/tests/Handler/Path.html	2011-06-17 18:59:16 UTC (rev 12103)
@@ -534,6 +534,306 @@
         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
     // 

Modified: trunk/openlayers/tests/Handler/Polygon.html
===================================================================
--- trunk/openlayers/tests/Handler/Polygon.html	2011-06-17 18:45:03 UTC (rev 12102)
+++ trunk/openlayers/tests/Handler/Polygon.html	2011-06-17 18:59:16 UTC (rev 12103)
@@ -717,6 +717,50 @@
         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
     // 



More information about the Commits mailing list