OpenLayers OpenLayers

Ticket #941: modify-features.patch

File modify-features.patch, 41.0 kB (added by tschaub, 11 months ago)

modify features patch

  • tests/Control/test_ModifyFeature.html

    old new  
     1<html> 
     2<head> 
     3    <script src="../../lib/OpenLayers.js"></script> 
     4    <script type="text/javascript"> 
     5     
     6    function test_ModifyFeature_constructor(t) { 
     7        t.plan(2); 
     8        var layer = "foo";         
     9        var options = { 
     10            geometryTypes: "bar" 
     11        }; 
     12        var control = new OpenLayers.Control.ModifyFeature(layer, options); 
     13         
     14        t.eq(control.layer, "foo", 
     15             "constructor sets layer correctly"); 
     16        t.eq(control.selectControl.geometryTypes, "bar", 
     17             "constructor sets options correctly on feature handler"); 
     18    } 
     19 
     20    function test_ModifyFeature_destroy(t) { 
     21        t.plan(2); 
     22        var map = new OpenLayers.Map("map"); 
     23        var layer = new OpenLayers.Layer.Vector(); 
     24        map.addLayer(layer); 
     25        var control = new OpenLayers.Control.ModifyFeature(layer); 
     26        control.selectControl.destroy = function() { 
     27            t.ok(true, 
     28                 "control.destroy calls destroy on select control"); 
     29        } 
     30        control.dragControl.destroy = function() { 
     31            t.ok(true, 
     32                 "control.destroy calls destroy on feature handler"); 
     33        } 
     34        control.destroy(); 
     35    } 
     36     
     37    function test_ModifyFeature_activate(t) { 
     38        t.plan(2); 
     39        var map = new OpenLayers.Map("map"); 
     40        var layer = new OpenLayers.Layer.Vector(); 
     41        map.addLayer(layer); 
     42        var control = new OpenLayers.Control.ModifyFeature(layer); 
     43        map.addControl(control); 
     44        t.ok(!control.selectControl.active, 
     45             "select control is not active prior to activating control"); 
     46        control.activate(); 
     47        t.ok(control.selectControl.active, 
     48             "select control is active after activating control"); 
     49    } 
     50 
     51    function test_ModifyFeature_initDeleteCodes(t) { 
     52        t.plan(3); 
     53        var layer = new OpenLayers.Layer.Vector(); 
     54        var control = new OpenLayers.Control.ModifyFeature(layer, {'deleteCodes': 46}); 
     55        t.eq(control.deleteCodes[0], 46, "Delete code properly turned into an array."); 
     56        var control = new OpenLayers.Control.ModifyFeature(layer); 
     57        t.eq(control.deleteCodes[0], 46, "Default deleteCodes include delete");  
     58        t.eq(control.deleteCodes[1], 100, "Default deleteCodes include 'd'");  
     59         
     60    } 
     61     
     62    function test_ModifyFeature_handleKeypress(t) { 
     63        t.plan(8); 
     64 
     65        /** 
     66         * There are two things that we want to test here 
     67         * 1) test that control.deleteCodes are respected 
     68         * 3) test that a vertex is properly deleted 
     69         * 
     70         * In the future, feature deletion may be added to the control. 
     71         */ 
     72         
     73        var control = new OpenLayers.Control.ModifyFeature({style: null}); 
     74        var delKey = 46; 
     75        var dKey = 100; 
     76        control.deleteCodes = [delKey, dKey]; 
     77 
     78        //// test that point is deleted for all delete codes 
     79        //var point = new OpenLayers.Feature.Vector( 
     80        //    new OpenLayers.Geometry.Point() 
     81        //); 
     82        //// mock up deletion before dragging (but after selection) 
     83        //control.dragControl.feature = null; 
     84        //control.feature = point; 
     85        //var oldUnselect = control.unselectFeature; 
     86        //control.unselectFeature = function(feature) { 
     87        //    t.eq(feature.id, point.id, 
     88        //         "point deletion before drag: unselectFeature called with the correct feature"); 
     89        //}; 
     90        //control.layer = { 
     91        //    removeFeatures: function(features) { 
     92        //        t.ok(features.length == 1, 
     93        //             "point deletion before drag: removeFeatures called with a single feature"); 
     94        //        t.eq(features[0].id, point.id, 
     95        //             "point deletion before drag: removeFeatures called with the correct feature"); 
     96        //    } 
     97        //}; 
     98        //control.onDelete = function(feature) { 
     99        //    t.eq(feature.id, point.id, 
     100        //         "point deletion before drag: onDelete called with the correct feature"); 
     101        //}; 
     102        //// run the above four tests twice 
     103        //control.handleKeypress(delKey); 
     104        //control.handleKeypress(dKey); 
     105        //// reset modified methods 
     106        //control.unselectFeatures = oldUnselect; 
     107        //control.onDelete = function() {}; 
     108        // 
     109        //// mock up deletion during dragging - these repeat the above tests 
     110        //control.dragControl.feature = point; 
     111        //control.feature = point; 
     112        //var oldUnselect = control.unselectFeature; 
     113        //control.unselectFeature = function(feature) { 
     114        //    t.eq(feature.id, point.id, 
     115        //         "point deletion during drag: unselectFeature called with the correct feature"); 
     116        //}; 
     117        //control.layer = { 
     118        //    removeFeatures: function(features) { 
     119        //        t.ok(features.length == 1, 
     120        //             "point deletion during drag: removeFeatures called with a single feature"); 
     121        //        t.eq(features[0].id, point.id, 
     122        //             "point deletion during drag: removeFeatures called with the correct feature"); 
     123        //    } 
     124        //}; 
     125        //control.onDelete = function(feature) { 
     126        //    t.eq(feature.id, point.id, 
     127        //         "point deletion during drag: onDelete called with the correct feature"); 
     128        //}; 
     129        //// run the above four tests twice 
     130        //control.handleKeypress(delKey); 
     131        //control.handleKeypress(dKey); 
     132        //// reset modified methods 
     133        //control.unselectFeatures = oldUnselect; 
     134        //control.onDelete = function() {}; 
     135         
     136        // test that a polygon vertex is deleted for all delete codes 
     137        var point = new OpenLayers.Feature.Vector( 
     138            new OpenLayers.Geometry.Point() 
     139        ); 
     140        var poly = new OpenLayers.Feature.Vector( 
     141            new OpenLayers.Geometry.Polygon() 
     142        ); 
     143         
     144        // mock up vertex deletion 
     145        control.dragControl.feature = point; 
     146        control.feature = poly; 
     147        control.vertices = [point]; 
     148        point.geometry.parent = { 
     149            removeComponent: function(geometry) { 
     150                t.eq(geometry.id, point.geometry.id, 
     151                     "vertex deletion: removeComponent called on parent with proper geometry"); 
     152            } 
     153        }; 
     154        control.layer = { 
     155            drawFeature: function(feature) { 
     156                t.eq(feature.id, poly.id, 
     157                     "vertex deletion: drawFeature called with the proper feature"); 
     158            } 
     159        }; 
     160        var oldReset = control.resetVertices; 
     161        control.resetVertices = function() { 
     162            t.ok(true, "vertex deletion: resetVertices called"); 
     163        }; 
     164        control.onModification = function(feature) { 
     165            t.eq(feature.id, poly.id, 
     166                 "vertex deletion: onModification called with the proper feature"); 
     167        }; 
     168        // run the above four tests twice 
     169        control.handleKeypress(delKey); 
     170        control.handleKeypress(dKey); 
     171        // reset modified methods 
     172        control.onModification = function() {}; 
     173         
     174    }     
     175         
     176 
     177    function test_ModifyFeature_onUnSelect(t) { 
     178        t.plan(5); 
     179        var layer = new OpenLayers.Layer.Vector(); 
     180        var control = new OpenLayers.Control.ModifyFeature(layer); 
     181        var fakeFeature = {'id':'myid'}; 
     182        control.vertices = 'a'; 
     183        control.virtualVertices = 'b'; 
     184        control.features = true; 
     185        control.dragControl.deactivate = function() { t.ok(true, "Deactivate called on drag control"); } 
     186        control.onModificationEnd = function (feature) { t.eq(feature.id, fakeFeature.id, "onModificationEnd got feature.") } 
     187        layer.removeFeatures = function(verts) { 
     188            t.ok(verts == 'a' || verts == 'b', "Verts removed correctly") 
     189        } 
     190        control.unselectFeature(fakeFeature); 
     191        t.eq(control.feature, null, "feature is set to null"); 
     192    }     
     193    function test_ModifyFeature_selectFeature(t) { 
     194        t.plan(12); 
     195        var layer = new OpenLayers.Layer.Vector(); 
     196        var control = new OpenLayers.Control.ModifyFeature(layer); 
     197        control.vertices = []; 
     198        control.virtualVertices = []; 
     199        control.dragControl.activate = function() { t.ok(true, "drag Control activated"); } 
     200        control.onModificationStart = function(feature)  { t.eq(feature.id, fakeFeature.id, "On Modification Start called with correct feature."); }  
     201         
     202         
     203        // Start of testing 
     204         
     205        control.collectVertices = function() { t.fail("Collect vertices called when geom is a point"); } 
     206        var fakeFeature = {'id':'myFakeFeature','geometry':{'CLASS_NAME':'OpenLayers.Geometry.Point'}}; 
     207         
     208        // Points don't call collectVertices 
     209        control.selectFeature(fakeFeature); 
     210         
     211        control.collectVertices = function() {  
     212          this.vertices = 'a'; 
     213          this.virtualVertices = 'd'; 
     214          t.ok(true, "collectVertices called");  
     215        } 
     216         
     217        layer.addFeatures = function(features) {  
     218            t.ok(features == 'a' || features == 'd', "features passed correctly");  
     219        } 
     220        fakeFeature.geometry.CLASS_NAME='OpenLayers.Geometry.Polygon'; 
     221         
     222        // OnSelect calls collectVertices and passes features to layer  
     223        control.selectFeature(fakeFeature); 
     224         
     225        control.vertices = ['a']; 
     226        control.virtualVertices = ['b']; 
     227         
     228        layer.addFeatures = function(features) {}  
     229         
     230        layer.removeFeatures = function(features) {  
     231            t.eq(features.length, 1, "Correct feature length passed in"); 
     232        }     
     233 
     234        // Features are removed whenever they exist 
     235        control.selectFeature(fakeFeature); 
     236         
     237    }   
     238 
     239    function test_ModifyFeature_resetVertices(t) { 
     240        t.plan(9); 
     241        var layer = new OpenLayers.Layer.Vector(); 
     242        var control = new OpenLayers.Control.ModifyFeature(layer); 
     243        var point = new OpenLayers.Geometry.Point(5,6); 
     244        var point2 = new OpenLayers.Geometry.Point(7,8); 
     245        var point3 = new OpenLayers.Geometry.Point(9,10); 
     246         
     247        control.feature = new OpenLayers.Feature.Vector(point); 
     248        control.resetVertices(); 
     249        t.eq(control.vertices.length, 0, "Correct vertices length"); 
     250        t.eq(control.virtualVertices.length, 0, "Correct virtual vertices length."); 
     251         
     252        var multiPoint = new OpenLayers.Geometry.MultiPoint([point, point2]); 
     253        control.feature = new OpenLayers.Feature.Vector(multiPoint); 
     254        control.resetVertices(); 
     255        t.eq(control.vertices.length, 2, "Correct vertices length with multipoint"); 
     256        t.eq(control.virtualVertices.length, 0, "Correct virtual vertices length (multipoint)."); 
     257 
     258        var line = new OpenLayers.Geometry.LineString([point, point2]); 
     259        control.feature = new OpenLayers.Feature.Vector(line); 
     260        control.resetVertices(); 
     261        t.eq(control.vertices.length, 2, "Correct vertices length with line"); 
     262        t.eq(control.virtualVertices.length, 1, "Correct virtual vertices length (linestring)."); 
     263         
     264        var polygon = new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing([point, point2, point3])]); 
     265        control.feature = new OpenLayers.Feature.Vector(polygon); 
     266        control.resetVertices(); 
     267        t.eq(control.vertices.length, 4, "Correct vertices length with polygon"); 
     268        t.eq(control.vertices[0].geometry.id, control.vertices[3].geometry.id, "First and last vertices are the same"); 
     269        t.eq(control.virtualVertices.length, 3, "Correct virtual vertices length (polygon)."); 
     270 
     271    }     
     272         
     273    function test_ModifyFeature_onDrag(t) { 
     274        t.plan(1); 
     275        t.ok(true, "onDrag not tested yet."); 
     276    } 
     277     
     278    function test_ModifyFeature_dragComplete(t) { 
     279        t.plan(6); 
     280        var layer = new OpenLayers.Layer.Vector(); 
     281        var control = new OpenLayers.Control.ModifyFeature(layer); 
     282        
     283        var fakeFeature = { 
     284         'geometry': { 'id':'myGeom'}, 
     285         'id': 'fakeFeature' 
     286        }; 
     287        control.collectVertices = function(geom) { 
     288            t.eq(geom.id, 'myGeom', "collect geom called"); 
     289            this.vertices = 'normal'; 
     290            this.virtualVertices = 'virtual'; 
     291        } 
     292        layer.addFeatures = function (verts) { 
     293            t.ok(verts == 'virtual' || verts == 'normal', verts + " verts correct"); 
     294        } 
     295        layer.removeFeatures = function (verts) { 
     296            t.ok(verts == 'previous virtual' || verts == 'previous normal', verts + " verts correct"); 
     297        } 
     298        control.onModification = function(feat) { 
     299            t.eq(feat.id, fakeFeature.id, "onModification gets correct feat"); 
     300        } 
     301        control.feature = fakeFeature; 
     302        control.vertices = 'previous normal'; 
     303        control.virtualVertices = 'previous virtual'; 
     304        control.dragComplete(); 
     305    } 
     306     
     307    function test_ModifyFeature_deactivate(t) { 
     308        t.plan(2); 
     309        var map = new OpenLayers.Map("map"); 
     310        var layer = new OpenLayers.Layer.Vector(); 
     311        map.addLayer(layer); 
     312        var control = new OpenLayers.Control.ModifyFeature(layer); 
     313        map.addControl(control); 
     314         
     315        control.selectControl.deactivate = function() { 
     316            t.ok(true, 
     317                 "control.deactivate calls deactivate on select control"); 
     318        } 
     319        control.dragControl.deactivate = function() { 
     320            t.ok(true, 
     321                 "control.deactivate calls deactivate on drag control"); 
     322        } 
     323        control.active = true; 
     324        control.deactivate(); 
     325    } 
     326 
     327    function test_ModifyFeature_onModificationStart(t) { 
     328        t.plan(1); 
     329        var map = new OpenLayers.Map("map"); 
     330        var layer = new OpenLayers.Layer.Vector(); 
     331        map.addLayer(layer); 
     332        var control = new OpenLayers.Control.ModifyFeature(layer); 
     333        map.addControl(control); 
     334        control.activate(); 
     335         
     336        // make sure onModificationStart is called on feature selection 
     337        var testFeature = new OpenLayers.Feature.Vector( 
     338            new OpenLayers.Geometry.Point(Math.random(), Math.random()) 
     339        ); 
     340        control.onModificationStart = function(feature) { 
     341            t.eq(feature.id, testFeature.id, 
     342                 "onModificationStart called with the right feature"); 
     343        }; 
     344        control.selectFeature(testFeature); 
     345    } 
     346     
     347    function test_ModifyFeature_onModification(t) { 
     348        t.plan(2); 
     349        var map = new OpenLayers.Map("map"); 
     350        var layer = new OpenLayers.Layer.Vector(); 
     351        map.addLayer(layer); 
     352        var control = new OpenLayers.Control.ModifyFeature(layer); 
     353        map.addControl(control); 
     354        control.activate(); 
     355         
     356        // make sure onModification is called on drag complete 
     357        var point = new OpenLayers.Feature.Vector( 
     358            new OpenLayers.Geometry.Point(Math.random(), Math.random()) 
     359        ); 
     360        control.feature = point; 
     361        control.onModification = function(feature) { 
     362            t.eq(feature.id, point.id, 
     363                "onModification called with the right feature on drag complete"); 
     364        }; 
     365        control.dragComplete(); 
     366         
     367        // make sure onModification is called on vertex deletion 
     368        var poly = new OpenLayers.Feature.Vector( 
     369            new OpenLayers.Geometry.Polygon() 
     370        ); 
     371        var oldDraw = layer.drawFeature; 
     372        layer.drawFeature = function() { 
     373            return; 
     374        }; 
     375        control.feature = poly; 
     376        control.vertices = [point]; 
     377        control.onModification = function(feature) { 
     378            t.eq(feature.id, poly.id, 
     379                "onModification called with the right feature on vertex delete"); 
     380        }; 
     381        point.geometry.parent = poly.geometry; 
     382        control.dragControl.feature = point; 
     383        control.handleKeypress(46); 
     384        layer.drawFeature = oldDraw; 
     385         
     386    } 
     387 
     388    function test_ModifyFeature_onModificationEnd(t) { 
     389        t.plan(1); 
     390        var map = new OpenLayers.Map("map"); 
     391        var layer = new OpenLayers.Layer.Vector(); 
     392        map.addLayer(layer); 
     393        var control = new OpenLayers.Control.ModifyFeature(layer); 
     394        map.addControl(control); 
     395        control.activate(); 
     396         
     397        // make sure onModificationEnd is called on unselect feature 
     398        var testFeature = new OpenLayers.Feature.Vector( 
     399            new OpenLayers.Geometry.Point(Math.random(), Math.random()) 
     400        ); 
     401        control.onModificationEnd = function(feature) { 
     402            t.eq(feature.id, testFeature.id, 
     403                 "onModificationEnd called with the right feature"); 
     404        }; 
     405        control.unselectFeature(testFeature); 
     406    } 
     407 
     408 
     409    //function t//est_ModifyFeature_onDelete(t) { 
     410    //    t.plan(2); 
     411    //    var map = new OpenLayers.Map("map"); 
     412    //    var layer = new OpenLayers.Layer.Vector(); 
     413    //    map.addLayer(layer); 
     414    //    var control = new OpenLayers.Control.ModifyFeature(layer); 
     415    //    map.addControl(control); 
     416    //    control.activate(); 
     417    //     
     418    //    // make sure onDelete is called on point deletion (before dragging) 
     419    //    var point = new OpenLayers.Feature.Vector( 
     420    //        new OpenLayers.Geometry.Point(Math.random(), Math.random()) 
     421    //    ); 
     422    //    control.feature = point; 
     423    //    control.onDelete = function(feature) { 
     424    //        t.eq(feature.id, point.id, 
     425    //            "onDelete called with the right feature before drag"); 
     426    //    }; 
     427    //    control.handleKeypress(46); 
     428    // 
     429    //    // make sure onDelete is called on point deletion (during dragging) 
     430    //    var point = new OpenLayers.Feature.Vector( 
     431    //        new OpenLayers.Geometry.Point(Math.random(), Math.random()) 
     432    //    ); 
     433    //    control.dragControl.feature = point; 
     434    //    control.feature = point; 
     435    //    control.onDelete = function(feature) { 
     436    //        t.eq(feature.id, point.id, 
     437    //            "onDelete called with the right feature during drag"); 
     438    //    }; 
     439    //    control.handleKeypress(46); 
     440    // 
     441    //} 
     442 
     443    </script> 
     444</head> 
     445<body> 
     446    <div id="map" style="width: 400px; height: 250px;"/> 
     447</body> 
     448</html> 
  • tests/list-tests.html

    old new  
    6666    <li>Control/test_Attribution.html</li> 
    6767    <li>Control/test_SelectFeature.html</li> 
    6868    <li>Control/test_DragFeature.html</li> 
     69    <li>Control/test_ModifyFeature.html</li> 
    6970    <li>Control/test_DragPan.html</li> 
    7071    <li>Control/test_OverviewMap.html</li> 
    7172    <li>Control/test_NavToolbar.html</li> 
  • lib/OpenLayers/Control/ModifyFeature.js

    old new  
     1/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license. 
     2 * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt  
     3 * for the full text of the license. */ 
     4 
     5 
     6/** 
     7 * @requires OpenLayers/Control/DragFeature.js 
     8 * @requires OpenLayers/Control/SelectFeature.js 
     9 * @requires OpenLayers/Handler/Keyboard.js 
     10 *  
     11 * Class: OpenLayers.Control.ModifyFeature 
     12 * Control to modify features.  When activated, a click renders the vertices 
     13 *     of a feature - these vertices can then be dragged.  By default, the 
     14 *     delete key will delete the vertex under the mouse.  New features are 
     15 *     added by dragging "virtual vertices" between vertices.  Create a new 
     16 *     control with the <OpenLayers.Control.ModifyFeature> constructor. 
     17 * 
     18 * Inherits From: 
     19 *  - <OpenLayers.Control> 
     20 */ 
     21OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, { 
     22 
     23    /** 
     24     * APIProperty: geometryTypes 
     25     * {Array(String)} To restrict modification to a limited set of geometry 
     26     *     types, send a list of strings corresponding to the geometry class 
     27     *     names. 
     28     */ 
     29    geometryTypes: null, 
     30 
     31    /** 
     32     * Property: layer 
     33     * {OpenLayers.Layer.Vector} 
     34     */ 
     35    layer: null, 
     36     
     37    /** 
     38     * Property: feature 
     39     * {<OpenLayers.Feature.Vector>} Feature currently available for modification. 
     40     */ 
     41    feature: null, 
     42     
     43    /** 
     44     * Property: vertices 
     45     * {Array(<OpenLayers.Feature.Vector>)} Verticies currently available 
     46     *     for dragging. 
     47     */ 
     48    vertices: null, 
     49     
     50    /** 
     51     * Property: virtualVertices 
     52     * {Array(<OpenLayers.Feature.Vector>)} Virtual vertices in the middle 
     53     *     of each edge. 
     54     */ 
     55    virtualVertices: null, 
     56 
     57    /** 
     58     * Property: selectControl 
     59     * {<OpenLayers.Control.Select>} 
     60     */ 
     61    selectControl: null, 
     62     
     63    /** 
     64     * Property: dragControl 
     65     * {<OpenLayers.Control.DragFeature>} 
     66     */ 
     67    dragControl: null, 
     68     
     69    /** 
     70     * Property: keyboardHandler 
     71     * {<OpenLayers.Handler.Keyboard>} 
     72     */ 
     73    keyboardHandler: null, 
     74     
     75    /** 
     76     * APIProperty: deleteCodes 
     77     * {Array(Integer)} Keycodes for deleting verticies.  Set to null to disable 
     78     *     vertex deltion by keypress.  If non-null, keypresses with codes 
     79     *     in this array will delete vertices under the mouse. Default 
     80     *     is 46 and 100, the 'delete' and lowercase 'd' keys. 
     81     */ 
     82    deleteCodes: null, 
     83 
     84    /** 
     85     * APIProperty: virtualStyle 
     86     * {<OpenLayers.Feature.Vector.Style>} 
     87     */ 
     88    virtualStyle: null, 
     89     
     90    /** 
     91     * APIProperty: onModificationStart  
     92     * {Function} Optional function to be called when a feature is selected 
     93     *     to be modified. The function should expect to be called with a 
     94     *     feature.  This could be used for example to allow to lock the 
     95     *     feature on server-side. 
     96     */ 
     97    onModificationStart: function() {}, 
     98 
     99    /** 
     100     * APIProperty: onModification 
     101     * {Function} Optional function to be called when a feature has been 
     102     *     modified.  The function should expect to be called with a feature. 
     103     */ 
     104    onModification: function() {}, 
     105 
     106    /** 
     107     * APIProperty: onModificationEnd 
     108     * {Function} Optional function to be called when a feature is finished  
     109     *     being modified.  The function should expect to be called with a 
     110     *     feature. 
     111     */ 
     112    onModificationEnd: function() {}, 
     113 
     114    /** 
     115     * Constructor: OpenLayers.Control.ModifyFeature 
     116     * Create a new modify feature control. 
     117     * 
     118     * Parameters: 
     119     * layer - {OpenLayers.Layer.Vector} Layer that contains features that 
     120     *     will be modified. 
     121     * options - {Object} Optional object whose properties will be set on the 
     122     *     control. 
     123     */ 
     124    initialize: function(layer, options) { 
     125        this.layer = layer; 
     126        this.vertices = []; 
     127        this.virtualVertices = []; 
     128        this.styleVirtual = OpenLayers.Util.extend({}, this.layer.style); 
     129        this.styleVirtual.fillOpacity = 0.3; 
     130        this.styleVirtual.strokeOpacity = 0.3; 
     131        this.deleteCodes = [46, 100]; 
     132        OpenLayers.Control.prototype.initialize.apply(this, [options]); 
     133        if(!(this.deleteCodes instanceof Array)) { 
     134            this.deleteCodes = [this.deleteCodes]; 
     135        } 
     136        var control = this; 
     137 
     138        // configure the select control 
     139        var selectOptions = { 
     140            geometryTypes: this.geometryTypes, 
     141            onSelect: function(feature) { 
     142                control.selectFeature.apply(control, [feature]); 
     143            }, 
     144            onUnselect: function(feature) { 
     145                control.unselectFeature.apply(control, [feature]); 
     146            } 
     147        }; 
     148        this.selectControl = new OpenLayers.Control.SelectFeature( 
     149            layer, selectOptions 
     150        ); 
     151 
     152        // configure the drag control 
     153        var dragOptions = { 
     154            geometryTypes: ["OpenLayers.Geometry.Point"], 
     155            snappingOptions: this.snappingOptions, 
     156            onStart: function(feature, pixel) { 
     157                control.dragStart.apply(control, [feature, pixel]); 
     158            }, 
     159            onDrag: function(feature) { 
     160                control.dragVertex.apply(control, [feature]); 
     161            }, 
     162            onComplete: function(feature) { 
     163                control.dragComplete.apply(control, [feature]); 
     164            } 
     165        }; 
     166        this.dragControl = new OpenLayers.Control.DragFeature( 
     167            layer, dragOptions 
     168        ); 
     169 
     170        // configure the keyboard handler 
     171        var keyboardOptions = { 
     172            keypress: this.handleKeypress 
     173        }; 
     174        this.keyboardHandler = new OpenLayers.Handler.Keyboard( 
     175            this, keyboardOptions 
     176        ); 
     177    }, 
     178 
     179    /** 
     180     * APIMethod: destroy 
     181     * Take care of things that are not handled in superclass. 
     182     */ 
     183    destroy: function() { 
     184        this.layer = null; 
     185        this.selectControl.destroy(); 
     186        this.dragControl.destroy(); 
     187        this.keyboardHandler.destroy(); 
     188        OpenLayers.Control.prototype.destroy.apply(this, []); 
     189    }, 
     190 
     191    /** 
     192     * APIMethod: activate 
     193     * Activate the control and the feature handler. 
     194     *  
     195     * Returns: 
     196     * {Boolean} Successfully activated the control and feature handler. 
     197     */ 
     198    activate: function() { 
     199        return (this.selectControl.activate() && 
     200                this.keyboardHandler.activate() && 
     201                OpenLayers.Control.prototype.activate.apply(this, arguments)); 
     202    }, 
     203 
     204    /** 
     205     * APIMethod: deactivate 
     206     * Deactivate the controls. 
     207     * 
     208     * Returns:  
     209     * {Boolean} Successfully deactivated the control. 
     210     */ 
     211    deactivate: function() { 
     212        var deactivated = false; 
     213        // the return from the controls is unimportant in this case 
     214        if(OpenLayers.Control.prototype.deactivate.apply(this, arguments)) { 
     215            this.layer.removeFeatures(this.vertices); 
     216            this.layer.removeFeatures(this.virtualVertices); 
     217            this.vertices = []; 
     218            this.dragControl.deactivate(); 
     219            if(this.feature) { 
     220                this.selectControl.unselect.apply(this.selectControl, 
     221                                                  [this.feature]); 
     222            } 
     223            this.selectControl.deactivate(); 
     224            this.keyboardHandler.deactivate(); 
     225            deactivated = true; 
     226        } 
     227        return deactivated; 
     228    }, 
     229 
     230    /** 
     231     * Method: selectFeature 
     232     * Called when the select feature control selects a feature. 
     233     * 
     234     * Parameters: 
     235     * feature - {<OpenLayers.Feature.Vector>} The selected feature. 
     236     */ 
     237    selectFeature: function(feature) { 
     238        this.feature = feature; 
     239        this.resetVertices(); 
     240        this.dragControl.activate(); 
     241        this.onModificationStart(this.feature); 
     242    }, 
     243 
     244    /** 
     245     * Method: unselectFeature 
     246     * Called when the select feature control unselects a feature. 
     247     * 
     248     * Parameters: 
     249     * feature - {<OpenLayers.Feature.Vector>} The unselected feature. 
     250     */ 
     251    unselectFeature: function(feature) { 
     252        this.layer.removeFeatures(this.vertices); 
     253        this.layer.removeFeatures(this.virtualVertices); 
     254        this.vertices = []; 
     255        this.virtualVertices = []; 
     256        this.feature = null; 
     257        this.dragControl.deactivate(); 
     258        this.onModificationEnd(feature); 
     259    }, 
     260 
     261    /** 
     262     * Method: dragStart 
     263     * Called by the drag feature control with before a feature is dragged. 
     264     *     This method is used to differentiate between points and vertices 
     265     *     of higher order geometries.  This respects the <geometryTypes> 
     266     *     property and forces a select of points when the drag control is 
     267     *     already active (and stops events from propagating to the select 
     268     *     control). 
     269     * 
     270     * Parameters: 
     271     * feature - {<OpenLayers.Feature.Vector>} The point or vertex about to be 
     272     *     dragged. 
     273     * pixel - {<OpenLayers.Pixel>} Pixel location of the mouse event. 
     274     */ 
     275    dragStart: function(feature, pixel) { 
     276        // only change behavior if the feature is not in the vertices array 
     277        if(feature != this.feature && 
     278           OpenLayers.Util.indexOf(this.vertices, feature) == -1 && 
     279           OpenLayers.Util.indexOf(this.virtualVertices, feature) == -1) { 
     280            if(this.feature) { 
     281                // unselect the currently selected feature 
     282                this.selectControl.clickFeature.apply(this.selectControl, 
     283                                                      [this.feature]); 
     284            } 
     285            // check any constraints on the geometry type 
     286            if(this.geometryTypes == null || 
     287               OpenLayers.Util.indexOf(this.geometryTypes, 
     288                                       feature.geometry.CLASS_NAME) != -1) { 
     289                // select the point 
     290                this.selectControl.clickFeature.apply(this.selectControl, 
     291                                                      [feature]); 
     292                /** 
     293                 * TBD: These lines improve workflow by letting the user 
     294                 *     immediately start dragging after the mouse down. 
     295                 *     However, it is very ugly to be messing with controls 
     296                 *     and their handlers in this way.  I'd like a better 
     297                 *     solution if the workflow change is necessary. 
     298                 */ 
     299                // prepare the point for dragging 
     300                this.dragControl.overFeature.apply(this.dragControl, 
     301                                                   [feature]); 
     302                this.dragControl.lastPixel = pixel; 
     303                this.dragControl.dragHandler.started = true; 
     304                this.dragControl.dragHandler.start = pixel; 
     305                this.dragControl.dragHandler.last = pixel; 
     306            } 
     307        } 
     308    }, 
     309     
     310    /** 
     311     * Method: dragVertex 
     312     * Called by the drag feature control with each drag move of a vertex. 
     313     * 
     314     * Parameters: 
     315     * vertex - {<OpenLayers.Feature.Vector>} The vertex being dragged. 
     316     */ 
     317    dragVertex: function(vertex) { 
     318        if(this.feature.geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { 
     319            if(this.feature != vertex) { 
     320                this.feature = vertex; 
     321            } 
     322        } else { 
     323            if(OpenLayers.Util.indexOf(this.virtualVertices, vertex) != -1) { 
     324                vertex.geometry.parent.addComponent(vertex.geometry, 
     325                                                    vertex._index); 
     326                delete vertex._index; 
     327                OpenLayers.Util.removeItem(this.virtualVertices, vertex); 
     328                this.layer.removeFeatures(vertex); 
     329            } 
     330        } 
     331        this.layer.drawFeature(this.feature, this.selectControl.selectStyle); 
     332        this.layer.removeFeatures(this.virtualVertices); 
     333        // keep the vertex on top so it gets the mouseout after dragging 
     334        // this should be removed in favor of an option to draw under or 
     335        // maintain node z-index 
     336        this.layer.drawFeature(vertex); 
     337    }, 
     338     
     339    /** 
     340     * Method: dragComplete 
     341     * Called by the drag feature control when the feature dragging is complete. 
     342     * 
     343     * Parameters: 
     344     * vertex - {<OpenLayers.Feature.Vector>} The vertex being dragged. 
     345     */ 
     346    dragComplete: function(vertex) { 
     347        this.resetVertices(); 
     348        this.onModification(this.feature); 
     349    }, 
     350     
     351    /** 
     352     * Method: resetVertices 
     353     */ 
     354    resetVertices: function() { 
     355        if(this.vertices.length > 0) { 
     356            this.layer.removeFeatures(this.vertices); 
     357            this.vertices = []; 
     358        } 
     359        if(this.virtualVertices.length > 0) { 
     360            this.layer.removeFeatures(this.virtualVertices); 
     361            this.virtualVertices = []; 
     362        } 
     363        if(this.feature && 
     364           this.feature.geometry.CLASS_NAME != "OpenLayers.Geometry.Point") { 
     365            this.collectVertices(this.feature.geometry); 
     366            this.layer.addFeatures(this.vertices); 
     367            this.layer.addFeatures(this.virtualVertices); 
     368        } 
     369    }, 
     370     
     371    /** 
     372     * Method: handleKeypress 
     373     * Called by the feature handler on keypress.  This is used to delete 
     374     *     vertices and point features.  If the <deleteCode> property is set, 
     375     *     vertices and points will be deleted when a feature is selected 
     376     *     for modification and the mouse is over a vertex. 
     377     * 
     378     * Parameters: 
     379     * {Integer} Key code corresponding to the keypress event. 
     380     */ 
     381    handleKeypress: function(code) { 
     382        // check for delete key 
     383        if(this.feature && 
     384           OpenLayers.Util.indexOf(this.deleteCodes, code) != -1) { 
     385            var vertex = this.dragControl.feature; 
     386            if(vertex && 
     387               OpenLayers.Util.indexOf(this.vertices, vertex) != -1) { 
     388                // remove the vertex 
     389                vertex.geometry.parent.removeComponent(vertex.geometry); 
     390                this.layer.drawFeature(this.feature, 
     391                                       this.selectControl.selectStyle); 
     392                this.resetVertices(); 
     393                this.onModification(this.feature); 
     394            } 
     395        } 
     396    }, 
     397 
     398    /** 
     399     * Method: collectVertices 
     400     * Collect the vertices from the modifiable feature's geometry and push 
     401     *     them on to the control's vertices array. 
     402     */ 
     403    collectVertices: function() { 
     404        this.vertices = []; 
     405        this.virtualVirtices = [];         
     406        var control = this; 
     407        function collectComponentVertices(geometry) { 
     408            var i, vertex, component; 
     409            if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { 
     410                vertex = new OpenLayers.Feature.Vector(geometry); 
     411                control.vertices.push(vertex); 
     412            } else { 
     413                for(i=0; i<geometry.components.length; ++i) { 
     414                    component = geometry.components[i]; 
     415                    if(component.CLASS_NAME == "OpenLayers.Geometry.Point") { 
     416                        vertex = new OpenLayers.Feature.Vector(component); 
     417                        control.vertices.push(vertex); 
     418                    } else { 
     419                        collectComponentVertices(component); 
     420                    } 
     421                } 
     422                 
     423                // add virtual vertices in the middle of each edge 
     424                if(geometry.CLASS_NAME != "OpenLayers.Geometry.MultiPoint") { 
     425                    for(i=0; i<geometry.components.length-1; ++i) { 
     426                        var prevVertex = geometry.components[i]; 
     427                        var nextVertex = geometry.components[i + 1]; 
     428                        if(prevVertex.CLASS_NAME == "OpenLayers.Geometry.Point" && 
     429                           nextVertex.CLASS_NAME == "OpenLayers.Geometry.Point") { 
     430                            var x = (prevVertex.x + nextVertex.x) / 2; 
     431                            var y = (prevVertex.y + nextVertex.y) / 2; 
     432                            var point = new OpenLayers.Feature.Vector( 
     433                                new OpenLayers.Geometry.Point(x, y), 
     434                                null, control.styleVirtual 
     435                            ); 
     436                            // set the virtual parent and intended index 
     437                            point.geometry.parent = geometry; 
     438                            point._index = i + 1; 
     439                            control.virtualVertices.push(point); 
     440                        } 
     441                    } 
     442                } 
     443            } 
     444        }        
     445        collectComponentVertices(this.feature.geometry); 
     446    }, 
     447 
     448    /** 
     449     * Method: setMap 
     450     * Set the map property for the control and all handlers. 
     451     * 
     452     * Parameters: 
     453     * map - {OpenLayers.Map} The control's map. 
     454     */ 
     455    setMap: function(map) { 
     456        this.selectControl.setMap(map); 
     457        this.dragControl.setMap(map); 
     458        OpenLayers.Control.prototype.setMap.apply(this, arguments); 
     459    }, 
     460 
     461    CLASS_NAME: "OpenLayers.Control.ModifyFeature" 
     462}); 
  • lib/OpenLayers.js

    old new  
    142142            "OpenLayers/Control/LayerSwitcher.js", 
    143143            "OpenLayers/Control/DrawFeature.js", 
    144144            "OpenLayers/Control/DragFeature.js", 
     145            "OpenLayers/Control/ModifyFeature.js", 
    145146            "OpenLayers/Control/Panel.js", 
    146147            "OpenLayers/Control/SelectFeature.js", 
    147148            "OpenLayers/Geometry.js", 
  • examples/modify-feature.html

    old new  
     1<html xmlns="http://www.w3.org/1999/xhtml"> 
     2  <head> 
     3    <title>Modify Feature</title> 
     4    <style type="text/css"> 
     5        #map { 
     6            width: 512px; 
     7            height: 350px; 
     8            border: 1px solid gray; 
     9        } 
     10        #controls { 
     11            width: 512px; 
     12        } 
     13        #controlToggle { 
     14            padding-left: 1em; 
     15        } 
     16        #controlToggle li { 
     17            list-style: none; 
     18        } 
     19    </style> 
     20    <script src="../lib/Firebug/firebug.js"></script> 
     21    <script src="../lib/OpenLayers.js"></script> 
     22    <script type="text/javascript"> 
     23        var map, vectors, controls; 
     24        function init(){ 
     25            map = new OpenLayers.Map('map'); 
     26            var wms = new OpenLayers.Layer.WMS( "OpenLayers WMS",  
     27