add node drawing + dragging & highlight selected way using stylesheet
authorDave Stubbs <osm@randomjunk.co.uk>
Mon, 31 Aug 2009 20:42:54 +0000 (20:42 +0000)
committerDave Stubbs <osm@randomjunk.co.uk>
Mon, 31 Aug 2009 20:42:54 +0000 (20:42 +0000)
halcyon.mxml
net/systemeD/halcyon/Map.as
net/systemeD/halcyon/WayUI.as
net/systemeD/halcyon/connection/Connection.as
net/systemeD/halcyon/connection/Entity.as
net/systemeD/halcyon/connection/Node.as
net/systemeD/halcyon/connection/NodeMovedEvent.as [new file with mode: 0644]
net/systemeD/halcyon/connection/Way.as
net/systemeD/potlatch2/EditController.as
resources/test.yaml

index 8c78fe2..513d6fc 100755 (executable)
 
                        // mouse-up handler attached to stage, so the user can release outside the map
                        stage.addEventListener(MouseEvent.MOUSE_UP, theMap.mouseUpHandler);
-                       theMap.backdrop.addEventListener(MouseEvent.MOUSE_MOVE, theMap.mouseMoveHandler);
-                       theMap.backdrop.addEventListener(MouseEvent.MOUSE_DOWN, theMap.mouseDownHandler);
+                       Globals.vars.map_area.addEventListener(MouseEvent.MOUSE_MOVE, theMap.mouseMoveHandler);
+                       Globals.vars.map_area.addEventListener(MouseEvent.MOUSE_DOWN, theMap.mouseDownHandler);
 
                        // keyboard event attached to stage
                        stage.addEventListener(KeyboardEvent.KEY_UP, theMap.keyUpHandler);
                        t.multiline=true;
                        _root.addChild(t);
                        Globals.vars.debug=t;
-            t.visible = true;
+            t.visible = loaderInfo.parameters["show_debug"] == 'true';
 
             var controller:EditController = new EditController(theMap, tagViewer);
             controller.setActive();
index 7a5692c..45344c3 100755 (executable)
@@ -83,7 +83,7 @@ package net.systemeD.halcyon {
                        // [layer][0]                   - fill
 
                        for (var l:int=0; l<13; l++) {                          // 11 layers (10 is +5, 0 is -5)
-                               var s:Sprite = getPaintSprite();                //  |
+                               var s:Sprite = getHitSprite();          //  |
                                s.addChild(getPaintSprite());                   //      | 0 fill
                                var t:Sprite = getPaintSprite();                //  | 1 stroke
                                for (var j:int=0; j<11; j++) {                  //      |  | ten sublayers
@@ -91,10 +91,11 @@ package net.systemeD.halcyon {
                                }                                                                               //  |  |  |
                                s.addChild(t);                                                  //  |  |
                                s.addChild(getPaintSprite());                   //      | 2 names
+                               s.addChild(getHitSprite());                         //  | 3 entity hit tests
                                addChild(s);                                                    //  |
                        }
-                       s=getPaintSprite(); addChild(s);                        // 11 - POIs
-                       s=getPaintSprite(); addChild(s);                        // 12 - shields and POI names
+                       addChild(getPaintSprite());                             // 11 - POIs
+                       addChild(getPaintSprite());                             // 12 - shields and POI names
 
                        this.initparams=initparams;
                        connection = Connection.getConnection(initparams);
@@ -106,6 +107,12 @@ package net.systemeD.halcyon {
         private function getPaintSprite():Sprite {
             var s:Sprite = new Sprite();
             s.mouseEnabled = false;
+            s.mouseChildren = false;
+            return s;
+        }
+
+        private function getHitSprite():Sprite {
+            var s:Sprite = new Sprite();
             return s;
         }
 
@@ -234,11 +241,11 @@ package net.systemeD.halcyon {
             pois[node.id] = new POI(node, this);
         }
 
-        public function setHighlight(entity:Entity, highlight:Boolean):void {
+        public function setHighlight(entity:Entity, stateType:String, isOn:Boolean):void {
             if ( entity is Way ) {
                 var wayUI:WayUI = ways[entity.id];
                 if ( wayUI != null )
-                    wayUI.setHighlight(highlight);
+                    wayUI.setHighlight(stateType, isOn);
             }
         }
 
@@ -249,9 +256,9 @@ package net.systemeD.halcyon {
             this.mapController = controller;
         }
 
-        public function wayMouseEvent(event:MouseEvent, way:Way):void {
+        public function entityMouseEvent(event:MouseEvent, entity:Entity):void {
             if ( mapController != null )
-                mapController.entityMouseEvent(event, way);
+                mapController.entityMouseEvent(event, entity);
                                
         }
 
index fe0527a..8dae6c8 100755 (executable)
@@ -22,7 +22,9 @@ package net.systemeD.halcyon {
                public var layer:int=0;                                         // map layer
                public var map:Map;                                                     // reference to parent map
                public var sprites:Array=new Array();           // instances in display list
+               private var stateClasses:Object = new Object();
         private var hitzone:Sprite;
+        private var listenSprite:Sprite;
 
                public static const DEFAULT_TEXTFIELD_PARAMS:Object = {
                        embedFonts: true,
@@ -37,11 +39,17 @@ package net.systemeD.halcyon {
                        this.map = map;
             init();
             way.addEventListener(Connection.TAG_CHANGE, wayTagChanged);
+            for (var i:uint = 0; i < way.length; i++ ) {
+                way.getNode(i).addEventListener(Connection.NODE_MOVED, nodeMoved);
+            }
                }
                
         private function wayTagChanged(event:TagEvent):void {
             redraw();
         }
+        private function nodeMoved(event:NodeMovedEvent):void {
+            redraw();
+        }
 
                private function init():void {
                        recalculate();
@@ -91,6 +99,12 @@ package net.systemeD.halcyon {
 
                public function redraw():void {
             var tags:Object = way.getTagsCopy();
+            // add states to the tags -- we should probably treat these
+            // more explicitly, but if Richard is still playing with styles
+            // this will work for now
+            for ( var stateKey:String in stateClasses ) {
+                tags["__state__"+stateKey] = stateKey;
+            }
 
                        // remove all currently existing sprites
                        while (sprites.length>0) {
@@ -164,29 +178,54 @@ package net.systemeD.halcyon {
                 solidLine(def.graphics);
                 addToLayer(def, 1);
             }
+            
+            if ( stateClasses["showNodes"] != null ) {
+                var nodes:Sprite = new Sprite();
+                drawNodes(nodes.graphics);
+                addToLayer(nodes, 2);
+            }
 
             // create a generic "way" hitzone sprite
             hitzone = new Sprite();
             hitzone.graphics.lineStyle(4, 0x000000, 1, false, "normal", CapsStyle.ROUND, JointStyle.ROUND);
             solidLine(hitzone.graphics);
-            addToLayer(hitzone, 2);
+            addToLayer(hitzone, 3);
             hitzone.visible = false;
 
-            var listenSprite:Sprite = new Sprite();
+            if ( listenSprite == null ) {
+                listenSprite = new Sprite();
+                listenSprite.addEventListener(MouseEvent.CLICK, mouseEvent);
+                listenSprite.addEventListener(MouseEvent.DOUBLE_CLICK, mouseEvent);
+                listenSprite.addEventListener(MouseEvent.MOUSE_OVER, mouseEvent);
+                listenSprite.addEventListener(MouseEvent.MOUSE_OUT, mouseEvent);
+                listenSprite.addEventListener(MouseEvent.MOUSE_DOWN, mouseEvent);
+                listenSprite.addEventListener(MouseEvent.MOUSE_UP, mouseEvent);
+                listenSprite.addEventListener(MouseEvent.MOUSE_MOVE, mouseEvent);
+            }
             listenSprite.hitArea = hitzone;
-            addToLayer(listenSprite, 2);
+            addToLayer(listenSprite, 3);
             listenSprite.buttonMode = true;
             listenSprite.mouseEnabled = true;
-            listenSprite.addEventListener(MouseEvent.CLICK, mouseEvent);
-            listenSprite.addEventListener(MouseEvent.DOUBLE_CLICK, mouseEvent);
-            listenSprite.addEventListener(MouseEvent.MOUSE_OVER, mouseEvent);
-            listenSprite.addEventListener(MouseEvent.MOUSE_OUT, mouseEvent);
 
                }
                
                // ------------------------------------------------------------------------------------------
                // Drawing support functions
 
+               private function drawNodes(g:Graphics):void {
+            g.lineStyle(1, 0xff0000, 1, false, "normal", CapsStyle.ROUND, JointStyle.ROUND);
+                       for (var i:uint = 0; i < way.length; i++) {
+                var node:Node = way.getNode(i);
+                var x:Number = map.lon2coord(node.lon);
+                var y:Number = map.latp2coord(node.latp);
+                g.moveTo(x-2, y-2);
+                g.lineTo(x+2, y-2);
+                g.lineTo(x+2, y+2);
+                g.lineTo(x-2, y+2);
+                g.lineTo(x-2, y-2);
+                       }
+               }
+
                // Draw solid polyline
                
                private function solidLine(g:Graphics):void {
@@ -340,15 +379,40 @@ package net.systemeD.halcyon {
                        if (sublayer!=-1) { o=Sprite(o).getChildAt(sublayer); }
                        Sprite(o).addChild(s);
                        sprites.push(s);
-            if ( s is Sprite ) Sprite(s).mouseEnabled = false;
+            if ( s is Sprite ) {
+                Sprite(s).mouseEnabled = false;
+                Sprite(s).mouseChildren = false;
+            }
+               }
+
+               public function getNodeAt(x:Number, y:Number):Node {
+                       for (var i:uint = 0; i < way.length; i++) {
+                var node:Node = way.getNode(i);
+                var nodeX:Number = map.lon2coord(node.lon);
+                var nodeY:Number = map.latp2coord(node.latp);
+                if ( nodeX >= x-2 && nodeX <= x+2 &&
+                     nodeY >= y-2 && nodeY <= y+2 )
+                    return node;
+            }
+            return null;
                }
 
         private function mouseEvent(event:MouseEvent):void {
-            map.wayMouseEvent(event, way);
+            var node:Node = getNodeAt(event.localX, event.localY);
+            if ( node == null )
+                map.entityMouseEvent(event, way);
+            else
+                map.entityMouseEvent(event, node);
         }
 
-        public function setHighlight(highlight:Boolean):void {
-            hitzone.visible = highlight;
+        public function setHighlight(stateType:String, isOn:Boolean):void {
+            if ( isOn && stateClasses[stateType] == null ) {
+                stateClasses[stateType] = true;
+                redraw();
+            } else if ( !isOn && stateClasses[stateType] != null ) {
+                delete stateClasses[stateType];
+                redraw();
+            }
         }
        }
 }
index 4675830..be31ca0 100755 (executable)
@@ -30,6 +30,7 @@ package net.systemeD.halcyon.connection {
         }
 
         public static function getParam(name:String, defaultValue:String):String {
+            trace("Returning param "+name+" as "+(params[name] == null ? defaultValue : params[name]));
             return params[name] == null ? defaultValue : params[name];
         }
 
@@ -59,6 +60,7 @@ package net.systemeD.halcyon.connection {
         public static var NEW_RELATION:String = "new_relation";
         public static var NEW_POI:String = "new_poi";
         public static var TAG_CHANGE:String = "tag_change";
+        public static var NODE_MOVED:String = "node_moved";
 
         // store the data we download
         private var negativeID:Number = -1;
index d0e7274..6b52c64 100644 (file)
@@ -40,7 +40,7 @@ package net.systemeD.halcyon.connection {
                     delete tags[key];
                 else
                     tags[key] = value;
-                modified = true;
+                markDirty();
                 dispatchEvent(new TagEvent(Connection.TAG_CHANGE, this, key, key, old, value));
             }
         }
@@ -50,7 +50,7 @@ package net.systemeD.halcyon.connection {
             if ( oldKey != newKey ) {
                 delete tags[oldKey];
                 tags[newKey] = value;
-                modified = true;
+                markDirty();
                 dispatchEvent(new TagEvent(Connection.TAG_CHANGE, this, oldKey, newKey, value, value));
             }
         }
index d20f53d..ac106ad 100644 (file)
@@ -4,6 +4,7 @@ package net.systemeD.halcyon.connection {
         private var _lat:Number;
         private var _latproj:Number;
         private var _lon:Number;
+        private var _ways:Array = new Array();
 
         public function Node(id:Number, version:uint, tags:Object, lat:Number, lon:Number) {
             super(id, version, tags);
@@ -24,8 +25,16 @@ package net.systemeD.halcyon.connection {
         }
 
         public function set lat(lat:Number):void {
+            var oldLat:Number = this._lat;
             this._lat = lat;
             this._latproj = lat2latp(lat);
+            markDirty();
+            dispatchEvent(new NodeMovedEvent(Connection.NODE_MOVED, this, oldLat, _lon));
+        }
+
+        public function set latp(latproj:Number):void {
+            this._latproj = latproj;
+            this._lat = latp2lat(lat);
         }
 
         public function set lon(lon:Number):void {
@@ -40,6 +49,25 @@ package net.systemeD.halcyon.connection {
             return 180/Math.PI * Math.log(Math.tan(Math.PI/4+lat*(Math.PI/180)/2));
         }
 
+               public function latp2lat(a:Number):Number {
+                   return 180/Math.PI * (2 * Math.atan(Math.exp(a*Math.PI/180)) - Math.PI/2);
+               }
+               
+               public function get ways():Array {
+                   return _ways;
+               }
+
+        public function registerAddedToWay(way:Way):void {
+            if ( _ways.indexOf(way) < 0 )
+                _ways.push(way);
+        }
+        
+        public function registerRemovedFromWay(way:Way):void {
+            var i:int = _ways.indexOf(way);
+            if ( i >= 0 )
+                _ways.splice(i, 1);
+        }
+        
                public override function getType():String {
                        return 'node';
                }
diff --git a/net/systemeD/halcyon/connection/NodeMovedEvent.as b/net/systemeD/halcyon/connection/NodeMovedEvent.as
new file mode 100644 (file)
index 0000000..5448ba6
--- /dev/null
@@ -0,0 +1,24 @@
+package net.systemeD.halcyon.connection {
+
+    import flash.events.Event;
+
+    public class NodeMovedEvent extends EntityEvent {
+        private var _oldLat:Number;
+        private var _oldLon:Number;
+
+        public function NodeMovedEvent(type:String, item:Node, oldLat:Number, oldLon:Number) {
+            super(type, item);
+            this._oldLat = oldLat;
+            this._oldLon = oldLon;
+        }
+
+        public function get oldLat():Number { return _oldLat; }
+        public function get oldLon():Number { return _oldLon; }
+
+        public override function toString():String {
+            return super.toString() + "::from "+_oldLat+","+_oldLon;
+        }
+    }
+
+}
+
index 2886b5b..ef5ee40 100644 (file)
@@ -7,6 +7,8 @@ package net.systemeD.halcyon.connection {
         public function Way(id:Number, version:uint, tags:Object, nodes:Array) {
             super(id, version, tags);
             this.nodes = nodes;
+            for each(var node:Node in nodes)
+                node.registerAddedToWay(this);
         }
 
         public function get length():uint {
@@ -19,15 +21,18 @@ package net.systemeD.halcyon.connection {
 
         public function insertNode(index:uint, node:Node):void {
             nodes.splice(index, 0, node);
+            node.registerAddedToWay(this);
         }
 
         public function appendNode(node:Node):uint {
             nodes.push(node);
+            node.registerAddedToWay(this);
             return nodes.length;
         }
 
         public function removeNode(index:uint):void {
-            nodes.splice(index, 1);
+            var splicedNodes:Array = nodes.splice(index, 1);
+            splicedNodes[0].registerRemovedFromWay(this);
         }
 
         public override function toString():String {
@@ -36,7 +41,7 @@ package net.systemeD.halcyon.connection {
         }
 
                public function isArea():Boolean {
-                       return (nodes[0].id==nodes[nodes.length-1].id  && nodes.length>2);
+                       return (nodes[0].id==nodes[nodes.length-1].id && nodes.length>2);
                }
 
                public override function getType():String {
index ccd7799..3b7881a 100644 (file)
@@ -3,31 +3,78 @@ package net.systemeD.potlatch2 {
     import net.systemeD.halcyon.MapController;
     import net.systemeD.halcyon.connection.*;
        import flash.events.*;
+       import flash.geom.*;
 
     public class EditController implements MapController {
 
         private var map:Map;
         private var tagViewer:TagViewer;
+        private var selectedEntity:Entity;
+        
+        private var draggingNode:Node = null;
+        
 
         public function EditController(map:Map, tagViewer:TagViewer) {
             this.map = map;
             this.tagViewer = tagViewer;
+            
+            map.parent.addEventListener(MouseEvent.MOUSE_MOVE, mapMouseEvent);
+            map.parent.addEventListener(MouseEvent.MOUSE_UP, mapMouseEvent);
+            map.parent.addEventListener(MouseEvent.MOUSE_DOWN, mapMouseEvent);
         }
 
         public function setActive():void {
             map.setController(this);
         }
 
+        private function mapMouseEvent(event:MouseEvent):void {
+            if ( draggingNode != null ) {
+                var mapLoc:Point = map.globalToLocal(new Point(event.stageX, event.stageY));
+                event.localX = mapLoc.x;
+                event.localY = mapLoc.y;
+
+                processNodeEvent(event, null);
+            }
+        }
+        
         public function entityMouseEvent(event:MouseEvent, entity:Entity):void {
-            if ( event.type == MouseEvent.CLICK )
+            if ( event.type == MouseEvent.MOUSE_DOWN )
+                event.stopPropagation();
+
+            if ( entity is Node || draggingNode != null ) {
+                processNodeEvent(event, entity);
+                return;
+            }
+            
+            if ( event.type == MouseEvent.CLICK ) {
+                if ( selectedEntity != null ) {
+                    map.setHighlight(selectedEntity, "selected", false);
+                    map.setHighlight(selectedEntity, "showNodes", false);
+                }
                 tagViewer.setEntity(entity);
-            else if ( event.type == MouseEvent.MOUSE_OVER )
-                map.setHighlight(entity, true);
+                map.setHighlight(entity, "selected", true);
+                map.setHighlight(entity, "showNodes", true);
+                selectedEntity = entity;
+            } else if ( event.type == MouseEvent.MOUSE_OVER )
+                map.setHighlight(entity, "hover", true);
             else if ( event.type == MouseEvent.MOUSE_OUT )
-                map.setHighlight(entity, false);
+                map.setHighlight(entity, "hover", false);
 
         }
 
+        private function processNodeEvent(event:MouseEvent, entity:Entity):void {
+            if ( draggingNode != null ) {
+                if ( event.type == MouseEvent.MOUSE_UP ) {
+                    draggingNode = null;
+                } else if ( event.type == MouseEvent.MOUSE_MOVE ) {
+                    draggingNode.lat = map.coord2lat(event.localY);
+                    draggingNode.lon = map.coord2lon(event.localX);
+                }
+            } else if ( event.type == MouseEvent.MOUSE_DOWN ) {
+                draggingNode = entity as Node;
+            }
+        }
+        
     }
 
 }
index dbb6d03..c40851b 100644 (file)
@@ -2,6 +2,66 @@
 # Motorways
 # ====================================================
 
+# selected line style
+- !actionscript/object:net.systemeD.halcyon.styleparser.ShapeRule
+  minScale: 18
+  maxScale: 12
+  isAnd: true
+  breaker: false
+  conditions:
+    - !actionscript/object:net.systemeD.halcyon.styleparser.Condition
+      type: set
+      params: ['__state__hover']
+    - !actionscript/object:net.systemeD.halcyon.styleparser.Condition
+      type: set
+      params: ['__state__selected']
+  shapeStyle: !actionscript/object:net.systemeD.halcyon.styleparser.ShapeStyle
+    isFilled: false
+    isStroked: true
+    stroke_opacity: 50
+    stroke_width: 20
+    stroke_colour: 0xffff00
+
+# selected line style
+- !actionscript/object:net.systemeD.halcyon.styleparser.ShapeRule
+  minScale: 18
+  maxScale: 12
+  isAnd: true
+  breaker: false
+  conditions:
+    - !actionscript/object:net.systemeD.halcyon.styleparser.Condition
+      type: set
+      params: ['__state__selected']
+    - !actionscript/object:net.systemeD.halcyon.styleparser.Condition
+      type: unset
+      params: ['__state__hover']
+  shapeStyle: !actionscript/object:net.systemeD.halcyon.styleparser.ShapeStyle
+    isFilled: false
+    isStroked: true
+    stroke_opacity: 50
+    stroke_width: 20
+    stroke_colour: 0xffb000
+
+# selected line style
+- !actionscript/object:net.systemeD.halcyon.styleparser.ShapeRule
+  minScale: 18
+  maxScale: 12
+  isAnd: true
+  breaker: false
+  conditions:
+    - !actionscript/object:net.systemeD.halcyon.styleparser.Condition
+      type: set
+      params: ['__state__hover']
+    - !actionscript/object:net.systemeD.halcyon.styleparser.Condition
+      type: unset
+      params: ['__state__selected']
+  shapeStyle: !actionscript/object:net.systemeD.halcyon.styleparser.ShapeStyle
+    isFilled: false
+    isStroked: true
+    stroke_opacity: 50
+    stroke_width: 20
+    stroke_colour: 0x00ff00
+
 # Zoom levels 13-14 (originally not in tunnels)
 
 - !actionscript/object:net.systemeD.halcyon.styleparser.ShapeRule
     stroke_opacity: 100
     stroke_width: 2
     stroke_colour: 10066329
+    
+