split ways by pressing X (needs a shiny button!)
[potlatch2.git] / net / systemeD / halcyon / connection / Way.as
1 package net.systemeD.halcyon.connection {
2     import flash.geom.Point;
3
4     public class Way extends Entity {
5         private var nodes:Array;
6                 public static var entity_type:String = 'way';
7
8         public function Way(id:Number, version:uint, tags:Object, loaded:Boolean, nodes:Array) {
9             super(id, version, tags, loaded);
10             this.nodes = nodes;
11                         for each (var node:Node in nodes) { node.addParent(this); }
12         }
13
14                 public function update(version:uint, tags:Object, loaded:Boolean, nodes:Array):void {
15                         var node:Node;
16                         for each (node in this.nodes) { node.removeParent(this); }
17                         updateEntityProperties(version,tags,loaded); this.nodes=nodes;
18                         for each (node in nodes) { node.addParent(this); }
19                 }
20                 
21         public function get length():uint {
22             return nodes.length;
23         }
24         
25         public function getNode(index:uint):Node {
26             return nodes[index];
27         }
28
29         public function insertNode(index:uint, node:Node):void {
30                         node.addParent(this);
31             nodes.splice(index, 0, node);
32             markDirty();
33             dispatchEvent(new WayNodeEvent(Connection.WAY_NODE_ADDED, node, this, index));
34         }
35
36         public function appendNode(node:Node):uint {
37                         node.addParent(this);
38             nodes.push(node);
39             markDirty();
40             dispatchEvent(new WayNodeEvent(Connection.WAY_NODE_ADDED, node, this, nodes.length - 1));
41             return nodes.length;
42         }
43         
44         public function indexOfNode(node:Node):uint {
45             return nodes.indexOf(node);
46         }
47
48         public function removeNode(index:uint):void {
49             var removed:Array=nodes.splice(index, 1);
50                         removed[0].removeParent(this);
51                         markDirty();
52             dispatchEvent(new WayNodeEvent(Connection.WAY_NODE_REMOVED, removed[0], this, index));
53         }
54
55                 public function removeAllNodes():void {
56                         var node:Node;
57                         while (nodes.length) { 
58                                 node=nodes.pop();
59                                 dispatchEvent(new WayNodeEvent(Connection.WAY_NODE_REMOVED, node, this, 0));
60                                 // ** the event mechanism calls redraw once per wayNodeRemoved, which isn't too efficient
61                                 //    so we should probably add a 'redraw' flag to WayNodeEvent
62                                 node.removeParent(this);
63                         }
64                         // ** we should send an event to delete the entire way
65                 }
66
67                 public function sliceNodes(start:int,end:int):Array {
68                         return nodes.slice(start,end);
69                 }
70
71                 public function deleteNodesFrom(start:int):void {
72                         nodes.splice(start);
73                 }
74
75
76         /**
77          * Finds the 1st way segment which intersects the projected
78          * coordinate and adds the node to that segment. If snap is
79          * specified then the node is moved to exactly bisect the
80          * segment.
81          */
82         public function insertNodeAtClosestPosition(newNode:Node, isSnap:Boolean):int {
83             var closestProportion:Number = 1;
84             var newIndex:uint = 0;
85             var nP:Point = new Point(newNode.lon, newNode.latp);
86             var snapped:Point = null;
87             
88             for ( var i:uint; i < length - 1; i++ ) {
89                 var node1:Node = getNode(i);
90                 var node2:Node = getNode(i+1);
91                 var p1:Point = new Point(node1.lon, node1.latp);
92                 var p2:Point = new Point(node2.lon, node2.latp);
93                 
94                 var directDist:Number = Point.distance(p1, p2);
95                 var viaNewDist:Number = Point.distance(p1, nP) + Point.distance(nP, p2);
96                         
97                 var proportion:Number = Math.abs(viaNewDist/directDist - 1);
98                 if ( proportion < closestProportion ) {
99                     newIndex = i+1;
100                     closestProportion = proportion;
101                     snapped = calculateSnappedPoint(p1, p2, nP);
102                 }
103             }
104             
105             // splice in new node
106             if ( isSnap ) {
107                 newNode.latp = snapped.y;
108                 newNode.lon = snapped.x;
109             }
110             insertNode(newIndex, newNode);
111             return newIndex;
112         }
113         
114         private function calculateSnappedPoint(p1:Point, p2:Point, nP:Point):Point {
115             var w:Number = p2.x - p1.x;
116             var h:Number = p2.y - p1.y;
117             var u:Number = ((nP.x-p1.x) * w + (nP.y-p1.y) * h) / (w*w + h*h);
118             return new Point(p1.x + u*w, p1.y+u*h);
119         }
120         
121         public override function toString():String {
122             return "Way("+id+"@"+version+"): "+getTagList()+
123                      " "+nodes.map(function(item:Node,index:int, arr:Array):String {return item.id.toString();}).join(",");
124         }
125
126                 public function isArea():Boolean {
127                         return (nodes[0].id==nodes[nodes.length-1].id && nodes.length>2);
128                 }
129
130                 public override function getType():String {
131                         return 'way';
132                 }
133     }
134
135 }