1 package net.systemeD.halcyon.connection {
2 import flash.geom.Point;
3 import net.systemeD.halcyon.Globals;
4 import net.systemeD.halcyon.connection.actions.*;
6 public class Way extends Entity {
7 private var nodes:Array;
8 public static var entity_type:String = 'way';
10 public function Way(id:Number, version:uint, tags:Object, loaded:Boolean, nodes:Array) {
11 super(id, version, tags, loaded);
13 for each (var node:Node in nodes) { node.addParent(this); }
16 public function update(version:uint, tags:Object, loaded:Boolean, nodes:Array):void {
18 for each (node in this.nodes) { node.removeParent(this); }
19 updateEntityProperties(version,tags,loaded); this.nodes=nodes;
20 for each (node in nodes) { node.addParent(this); }
23 public function get length():uint {
27 public function getNode(index:uint):Node {
31 public function getLastNode():Node {
32 return nodes[nodes.length-1];
35 public function insertNode(index:uint, node:Node):void {
37 nodes.splice(index, 0, node);
39 dispatchEvent(new WayNodeEvent(Connection.WAY_NODE_ADDED, node, this, index));
42 public function appendNode(node:Node):uint {
46 dispatchEvent(new WayNodeEvent(Connection.WAY_NODE_ADDED, node, this, nodes.length - 1));
50 public function prependNode(node:Node):uint {
54 dispatchEvent(new WayNodeEvent(Connection.WAY_NODE_ADDED, node, this, 0));
58 public function indexOfNode(node:Node):uint {
59 return nodes.indexOf(node);
62 public function removeNode(node:Node, performAction:Function):void {
63 performAction(new RemoveNodeFromWayAction(this, node, nodes));
66 public function removeNodeByIndex(index:uint):void {
67 var removed:Array=nodes.splice(index, 1);
68 if (nodes.indexOf(removed[0])==-1) { removed[0].removeParent(this); }
70 dispatchEvent(new WayNodeEvent(Connection.WAY_NODE_REMOVED, removed[0], this, index));
73 public function sliceNodes(start:int,end:int):Array {
74 return nodes.slice(start,end);
77 public function deleteNodesFrom(start:int):void {
78 for (var i:int=start; i<nodes.length; i++) {
79 nodes[i].removeParent(this);
85 public function mergeWith(way:Way,topos:int,frompos:int, performAction:Function):void {
86 performAction(new MergeWaysAction(this, way, topos, frompos));
89 public function addToEnd(topos:int,node:Node):void {
91 if (nodes[0]==node) { return; }
94 if (nodes[nodes.length-1]==node) { return; }
102 * Finds the 1st way segment which intersects the projected
103 * coordinate and adds the node to that segment. If snap is
104 * specified then the node is moved to exactly bisect the
107 public function insertNodeAtClosestPosition(newNode:Node, isSnap:Boolean):int {
108 var closestProportion:Number = 1;
109 var newIndex:uint = 0;
110 var nP:Point = new Point(newNode.lon, newNode.latp);
111 var snapped:Point = null;
113 for ( var i:uint; i < length - 1; i++ ) {
114 var node1:Node = getNode(i);
115 var node2:Node = getNode(i+1);
116 var p1:Point = new Point(node1.lon, node1.latp);
117 var p2:Point = new Point(node2.lon, node2.latp);
119 var directDist:Number = Point.distance(p1, p2);
120 var viaNewDist:Number = Point.distance(p1, nP) + Point.distance(nP, p2);
122 var proportion:Number = Math.abs(viaNewDist/directDist - 1);
123 if ( proportion < closestProportion ) {
125 closestProportion = proportion;
126 snapped = calculateSnappedPoint(p1, p2, nP);
130 // splice in new node
132 newNode.latp = snapped.y;
133 newNode.lon = snapped.x;
135 insertNode(newIndex, newNode);
139 private function calculateSnappedPoint(p1:Point, p2:Point, nP:Point):Point {
140 var w:Number = p2.x - p1.x;
141 var h:Number = p2.y - p1.y;
142 var u:Number = ((nP.x-p1.x) * w + (nP.y-p1.y) * h) / (w*w + h*h);
143 return new Point(p1.x + u*w, p1.y+u*h);
146 public override function toString():String {
147 return "Way("+id+"@"+version+"): "+getTagList()+
148 " "+nodes.map(function(item:Node,index:int, arr:Array):String {return item.id.toString();}).join(",");
151 public function isArea():Boolean {
152 return (nodes[0].id==nodes[nodes.length-1].id && nodes.length>2);
155 public override function remove(performAction:Function):void {
156 performAction(new DeleteWayAction(this, setDeletedState, nodes));
159 public function get clockwise():Boolean {
161 var xmin:Number=-999999; var ymin:Number=-999999;
162 for (var i:uint=0; i<nodes.length; i++) {
163 if (nodes[i].latp> ymin) { lowest=i; xmin=nodes[i].lon; ymin=nodes[i].latp; }
164 else if (nodes[i].latp==ymin
165 && nodes[i].lon > xmin) { lowest=i; xmin=nodes[i].lon; ymin=nodes[i].latp; }
167 return (this.onLeft(lowest)>0);
170 private function onLeft(j:uint):Number {
173 if (nodes.length>=3) {
174 i=j-1; if (i==-1) { i=nodes.length-2; }
175 k=j+1; if (k==nodes.length) { k=1; }
176 left=((nodes[j].lon-nodes[i].lon) * (nodes[k].latp-nodes[i].latp) -
177 (nodes[k].lon-nodes[i].lon) * (nodes[j].latp-nodes[i].latp));
182 internal override function isEmpty():Boolean {
183 return (deleted || (nodes.length==0));
186 public override function getType():String {