7b4a8dcce584ecad21c511af8e94471c0ed478e5
[potlatch2.git] / net / systemeD / halcyon / VectorLayer.as
1 package net.systemeD.halcyon {
2
3         import net.systemeD.halcyon.Map;
4         import net.systemeD.halcyon.MapPaint;
5         import net.systemeD.halcyon.connection.*;
6     import net.systemeD.halcyon.connection.actions.*;
7         import net.systemeD.halcyon.Globals;
8         import net.systemeD.halcyon.styleparser.RuleSet;
9
10     /** The VectorLayer class is used for the concept of Vector Background Layers.
11     * It is similar in concept to the various Connection layers used for the core
12     * OpenStreetMap data, and as such it stores its own list of nodes, ways and relations.
13     * The most interesting function is pullThrough which allows moving entities out
14     * of a VectorLayer and into the main map layer
15     */
16         public class VectorLayer extends Object {
17
18                 public var map:Map;
19                 public var paint:MapPaint;                                              // sprites
20                 public var name:String;
21                 public var url:String;
22                 public var style:String='';
23
24                 public var ways:Object=new Object();                    // geodata
25                 public var nodes:Object=new Object();                   //  |
26                 public var relations:Object=new Object();               //  |
27                 private var pois:Array=[];                                              //  |
28
29         private var markers:Object=new Object();        // markers
30         private var negativeID:Number = -1;
31
32         /** Create a new VectorLayer
33         * @param n The name of the VectorLayer (eg 'GPS tracks')
34         * @param m The map. You probably have a global reference to this
35         * @param s The style you wish to use (eg 'gpx.css')
36         */
37                 public function VectorLayer(n:String,m:Map,s:String) {
38                         name=n;
39                         map=m;
40                         style=s;
41                         paint=new MapPaint(m,0,0);
42                         redrawFromCSS(style);
43                 }
44
45                 public function redrawFromCSS(style:String):void {
46                         paint.ruleset=new RuleSet(map.MINSCALE,map.MAXSCALE,paint.redraw,paint.redrawPOIs);
47                         paint.ruleset.loadFromCSS(style);
48                 }
49
50         /** Create a new node on the vector layer. Note that the node won't show up until on the map
51         * until the the relevant nodeUI is created, so you will need to instruct the paint to create one
52         *
53         * e.g. layer.paint.updateEntityUIs(layer.getObjectsByBbox(...)...);
54         */
55                 public function createNode(tags:Object,lat:Number,lon:Number):Node {
56                         var node:Node = new Node(negativeID, 0, tags, true, lat, lon);
57                         nodes[negativeID]=node; negativeID--;
58                         return node;
59                 }
60
61         /**
62         * @param tags The tags for the new Way
63         * @param nodes An array of Node objects
64         */
65                 public function createWay(tags:Object,nodes:Array):Way {
66                         var way:Way = new Way(negativeID, 0, tags, true, nodes.concat());
67                         ways[negativeID]=way; negativeID--;
68                         return way;
69                 }
70
71         /**
72         * @param tags The tags for the new relation
73         * @param members An array of RelationMember objects
74         */
75                 public function createRelation(tags:Object,members:Array):Relation {
76             var relation:Relation = new Relation(negativeID, 0, tags, true, members.concat());
77                         relations[negativeID]=relation; negativeID--;
78             return relation;
79                 }
80
81         public function createMarker(tags:Object,lat:Number,lon:Number):Marker {
82             var marker:Marker = new Marker(negativeID, 0, tags, true, lat, lon);
83             markers[negativeID]=marker; negativeID--;
84             return marker;
85         }
86
87         public function registerPOI(node:Node):void {
88             if (pois.indexOf(node)<0) { pois.push(node); }
89         }
90         public function unregisterPOI(node:Node):void {
91                         var index:uint = pois.indexOf(node);
92                         if ( index >= 0 ) { pois.splice(index,1); }
93         }
94
95                 public function getObjectsByBbox(left:Number, right:Number, top:Number, bottom:Number):Object {
96                         // ** FIXME: this is just copied-and-pasted from Connection.as, which really isn't very
97                         // good practice. Is there a more elegant way of doing it?
98                         var o:Object = { poisInside: [], poisOutside: [], waysInside: [], waysOutside: [],
99                               markersInside: [], markersOutside: [] };
100                               
101                         for each (var way:Way in ways) {
102                                 if (way.within(left,right,top,bottom)) { o.waysInside.push(way); }
103                                                                   else { o.waysOutside.push(way); }
104                         }
105                         for each (var poi:Node in pois) {
106                                 if (poi.within(left,right,top,bottom)) { o.poisInside.push(poi); }
107                                                                   else { o.poisOutside.push(poi); }
108                         }
109             for each (var marker:Marker in markers) {
110                 if (marker.within(left,right,top,bottom)) { o.markersInside.push(marker); }
111                                                      else { o.markersOutside.push(marker); }
112             }
113                         return o;
114                 }
115
116         /**
117         * Transfers an entity from the VectorLayer into the main layer
118         * @param entity The entity from the VectorLayer that you want to transfer.
119         * @param connection The Connection instance to transfer to (eg Connection.getConnection() )
120         *
121         * @return either the newly created entity, or null
122         */
123                 public function pullThrough(entity:Entity,connection:Connection):Entity {
124                         var i:uint=0;
125                         var oldNode:Node, newNode:Node;
126                         if (entity is Way) {
127                                 // copy way through to main layer
128                                 // ** shouldn't do this if the nodes are already in the main layer
129                                 //    (or maybe we should just match on lat/long to avoid ways in background having nodes in foreground)
130                                 var oldWay:Way=Way(entity);
131                                 var newWay:Way=connection.createWay(oldWay.getTagsCopy(), [], MainUndoStack.getGlobalStack().addAction);
132                                 var nodemap:Object={};
133                                 for (i=0; i<oldWay.length; i++) {
134                                         oldNode = oldWay.getNode(i);
135                                         newNode = nodemap[oldNode.id] ? nodemap[oldNode.id] : connection.createNode(
136                                                 oldNode.getTagsCopy(), oldNode.lat, oldNode.lon, 
137                                                 MainUndoStack.getGlobalStack().addAction);
138                                         newWay.appendNode(newNode, MainUndoStack.getGlobalStack().addAction);
139                                         nodemap[oldNode.id]=newNode;
140                                 }
141                                 // delete this way
142                                 while (oldWay.length) { 
143                                         var id:Number=oldWay.getNode(0).id;
144                                         oldWay.removeNodeByIndex(0,MainUndoStack.getGlobalStack().addAction,false);
145                                         delete nodes[id];
146                                 }
147                                 paint.wayuis[oldWay.id].redraw();
148                                 delete ways[oldWay.id];
149                                 map.paint.createWayUI(newWay);
150                                 return newWay;
151
152                         } else if (entity is Node && !entity.hasParentWays) {
153                                 // copy node through to main layer
154                                 // ** should be properly undoable
155                                 oldNode=Node(entity);
156                                 unregisterPOI(oldNode);
157                                 var newPoiAction:CreatePOIAction = new CreatePOIAction(
158                                         oldNode.getTagsCopy(), oldNode.lat, oldNode.lon);
159                                 MainUndoStack.getGlobalStack().addAction(newPoiAction);
160                                 paint.deleteNodeUI(oldNode);
161                                 delete nodes[oldNode.id];
162                                 return newPoiAction.getNode();
163                         }
164                         return null;
165                 }
166
167         /**
168         * Remove all the nodes, ways, and relations from the VectorLayer.
169         * Also removes the associated NodeUIs, WayUIs and POIs
170         */
171                 public function blank():void {
172                         for each (var node:Node in nodes) { paint.deleteNodeUI(node); }
173                         for each (var way:Way in ways) { paint.deleteWayUI(way); }
174                         relations={}; nodes={}; ways={}; pois=[];
175                 }
176
177         }
178 }