1 package net.systemeD.halcyon {
3 import flash.display.*;
5 import flash.text.AntiAliasType;
6 import flash.text.GridFitType;
7 import flash.text.TextField;
8 import flash.text.TextFormat;
9 import flash.geom.Matrix;
10 import flash.geom.Point;
11 import net.systemeD.halcyon.styleparser.*;
12 import net.systemeD.halcyon.connection.*;
13 import net.systemeD.halcyon.Globals;
15 /** The graphical representation of a Node (including POIs and nodes that are part of Ways). */
16 public class NodeUI extends EntityUI {
18 public var loaded:Boolean=false;
19 private var iconnames:Object={}; // name of icon on each sublayer
20 private var heading:Number=0; // heading within way
21 private var rotation:Number=0; // rotation applied to this POI
22 private static const NO_LAYER:int=-99999;
25 * @param node The corresponding Node.
26 * @param paint MapPaint object to attach this NodeUI to.
27 * @param heading Optional angle.
28 * @param layer Which layer on the MapPaint object it sits on. @default Top layer
29 * @param stateClasses A settings object definining the initial state of the node (eg, highlighted, hover...) */
30 public function NodeUI(node:Node, paint:MapPaint, heading:Number=0, layer:int=NO_LAYER, stateClasses:Object=null) {
32 if (layer==NO_LAYER) { this.layer=paint.maxlayer; } else { this.layer=layer; }
33 this.heading = heading;
35 for (var state:String in stateClasses) {
36 if (stateClasses[state]) { this.stateClasses[state]=stateClasses[state]; }
39 entity.addEventListener(Connection.NODE_MOVED, nodeMoved, false, 0, true);
40 entity.addEventListener(Connection.NODE_ALTERED, nodeAltered, false, 0, true);
41 entity.addEventListener(Connection.ENTITY_DRAGGED, nodeDragged, false, 0, true);
42 attachRelationListeners();
46 public function removeEventListeners():void {
47 removeGenericEventListeners();
48 entity.removeEventListener(Connection.NODE_MOVED, nodeMoved);
49 entity.removeEventListener(Connection.NODE_ALTERED, nodeAltered);
50 entity.removeEventListener(Connection.ENTITY_DRAGGED, nodeDragged);
51 removeRelationListeners();
54 /** Respond to movement event. */
55 public function nodeMoved(event:Event):void {
59 private function nodeAltered(event:Event):void {
63 private function nodeDragged(event:EntityDraggedEvent):void {
64 updatePosition(event.xDelta,event.yDelta);
67 /** Update settings then draw node. */
68 override public function doRedraw():Boolean {
69 if (!paint.ready) { return false; }
70 if (entity.deleted) { return false; }
72 var tags:Object = entity.getTagsCopy();
73 setStateClass('poi', !entity.hasParentWays);
74 setStateClass('hasTags', entity.hasInterestingTags());
75 setStateClass('dupe', Node(entity).isDupe());
76 tags=applyStateClasses(tags);
77 if (!styleList || !styleList.isValidAt(paint.map.scale)) {
78 styleList=paint.ruleset.getStyles(entity,tags,paint.map.scale);
81 var suggestedLayer:Number=styleList.layerOverride();
82 if (!isNaN(suggestedLayer)) { layer=suggestedLayer; }
84 var inWay:Boolean=entity.hasParentWays;
85 var hasStyles:Boolean=styleList.hasStyles();
87 removeSprites(); iconnames={};
88 return renderFromStyle(tags);
91 /** Assemble the layers of icons to draw the node, with hit zone if interactive. */
92 private function renderFromStyle(tags:Object):Boolean {
93 var r:Boolean=false; // ** rendered
94 var maxwidth:Number=4; // biggest width
98 for each (var sublayer:Number in styleList.sublayers) {
100 if (styleList.pointStyles[sublayer]) {
101 var s:PointStyle=styleList.pointStyles[sublayer];
102 interactive||=s.interactive;
104 if (s.rotation) { rotation=s.rotation; }
105 if (s.icon_image!=iconnames[sublayer]) {
106 if (s.icon_image=='square') {
109 addToLayer(icon,STROKESPRITE,sublayer);
110 w=styleIcon(icon,sublayer);
111 icon.graphics.drawRect(0,0,w,w);
112 if (s.interactive) { maxwidth=Math.max(w,maxwidth); }
113 iconnames[sublayer]='_square';
115 } else if (s.icon_image=='circle') {
118 addToLayer(icon,STROKESPRITE,sublayer);
119 w=styleIcon(icon,sublayer);
120 icon.graphics.drawCircle(w,w,w);
121 if (s.interactive) { maxwidth=Math.max(w,maxwidth); }
122 iconnames[sublayer]='_circle';
124 } else if (paint.ruleset.images[s.icon_image]) {
125 // 'load' icon (actually just from library)
126 var loader:ExtendedLoader = new ExtendedLoader();
127 loader.info['sublayer']=sublayer;
128 loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadedIcon, false, 0, true);
129 loader.loadBytes(paint.ruleset.images[s.icon_image]);
130 iconnames[sublayer]=s.icon_image;
136 var a:String='', t:TextStyle;
137 if (styleList.textStyles[sublayer]) {
138 t=styleList.textStyles[sublayer];
139 interactive||=t.interactive;
144 var name:Sprite=new Sprite();
145 addToLayer(name,NAMESPRITE);
146 t.writeNameLabel(name,a,0,0);
150 if (!r) { return false; }
151 if (interactive) { addHitSprite(maxwidth); }
157 private function styleIcon(icon:Sprite, sublayer:Number):Number {
161 if (styleList.shapeStyles[sublayer]) {
162 var s:ShapeStyle=styleList.shapeStyles[sublayer];
163 if (!isNaN(s.color)) { icon.graphics.beginFill(s.color, s.opacity ? s.opacity : 1);
165 if (s.casing_width || !isNaN(s.casing_color)) {
166 icon.graphics.lineStyle(s.casing_width ? s.casing_width : 1,
167 s.casing_color ? s.casing_color : 0,
168 s.casing_opacity ? s.casing_opacity : 1);
173 return styleList.pointStyles[sublayer].icon_width;
176 private function addHitSprite(w:uint):void {
177 hitzone = new Sprite();
178 hitzone.graphics.lineStyle(4, 0x000000, 1, false, "normal", CapsStyle.ROUND, JointStyle.ROUND);
179 hitzone.graphics.beginFill(0);
180 hitzone.graphics.drawRect(0,0,w,w);
181 hitzone.visible = false;
185 private function loadedIcon(event:Event):void {
186 var icon:Sprite=new Sprite();
187 var sublayer:Number=event.target.loader.info['sublayer'];
188 addToLayer(icon,STROKESPRITE,sublayer);
189 icon.addChild(Bitmap(event.target.content));
190 addHitSprite(icon.width);
195 private function updatePosition(xDelta:Number=0,yDelta:Number=0):void {
196 if (!loaded) { return; }
198 var baseX:Number=paint.map.lon2coord(Node(entity).lon);
199 var baseY:Number=paint.map.latp2coord(Node(entity).latp);
200 for (var i:uint=0; i<sprites.length; i++) {
201 var d:DisplayObject=sprites[i];
202 d.x=0; d.y=0; d.rotation=0;
204 var m:Matrix=new Matrix();
205 m.translate(-d.width/2,-d.height/2);
207 m.translate(baseX+xDelta,baseY+yDelta);
208 d.transform.matrix=m;
211 public function hitTest(x:Number, y:Number):Node {
212 if (hitzone && hitzone.hitTestPoint(x,y,true)) { return entity as Node; }