Reimplement pullThrough in a more undo-friendly fashion - only one undo to reverse...
[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.styleparser.RuleSet;
8
9     /** The VectorLayer class is used for the concept of Vector Background Layers.
10     * It is similar in concept to the various Connection layers used for the core
11     * OpenStreetMap data, and as such it stores its own list of nodes, ways and relations.
12     * The most interesting function is pullThrough which allows moving entities out
13     * of a VectorLayer and into the main map layer
14     */
15         public class VectorLayer extends Object {
16
17                 public var map:Map;
18                 public var paint:MapPaint;                                              // sprites
19                 public var name:String;
20                 public var url:String;
21                 public var style:String='';
22
23                 public var ways:Object=new Object();                    // geodata
24                 public var nodes:Object=new Object();                   //  |
25                 public var relations:Object=new Object();               //  |
26                 private var pois:Array=[];                                              //  |
27
28         private var markers:Object=new Object();        // markers
29         private var negativeID:Number = -1;
30
31         /** Create a new VectorLayer
32         * @param n The name of the VectorLayer (eg 'GPS tracks')
33         * @param m The map. You probably have a global reference to this
34         * @param s The style you wish to use (eg 'gpx.css')
35         */
36                 public function VectorLayer(n:String,m:Map,s:String) {
37                         name=n;
38                         map=m;
39                         style=s;
40                                 // >>>> REFACTOR: VectorLayer commented out
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. <code>layer.paint.updateEntityUIs(...);</code>
54         */
55                 public function createNode(tags:Object,lat:Number,lon:Number):Node {
56                                 // >>>> REFACTOR: VectorLayer commented out
57 //                      var node:Node = new Node(this, negativeID, 0, tags, true, lat, lon);
58 //                      nodes[negativeID]=node; negativeID--;
59 //                      return node;
60                         return null;
61                 }
62
63         /**
64         * @param tags The tags for the new Way
65         * @param nodes An array of Node objects
66         */
67                 public function createWay(tags:Object,nodes:Array):Way {
68                                 // >>>> REFACTOR: VectorLayer commented out
69 //                      var way:Way = new Way(this, negativeID, 0, tags, true, nodes.concat());
70 //                      ways[negativeID]=way; negativeID--;
71 //                      return way;
72                         return null;
73                 }
74
75         /**
76         * @param tags The tags for the new relation
77         * @param members An array of RelationMember objects
78         */
79                 public function createRelation(tags:Object,members:Array):Relation {
80                                 // >>>> REFACTOR: VectorLayer commented out
81 //                      var relation:Relation = new Relation(this, negativeID, 0, tags, true, members.concat());
82 //                      relations[negativeID]=relation; negativeID--;
83 //                      return relation;
84                         return null;
85                 }
86
87         /**
88         * Create a new Marker on the VectorLayer. If you pass in an id it'll check first whether that
89         * marker has been created already, and won't duplicate it.
90         *
91         * @param tags The tags for the new Marker
92         * @param lat The latitude
93         * @param lon The longitude
94         * @param id Use this id for the marker, useful for when layer might be reloaded during panning
95         */
96         public function createMarker(tags:Object,lat:Number,lon:Number,id:Number=NaN):Marker {
97             if (!id) {
98               id = negativeID;
99               negativeID--;
100             }
101                                 // >>>> REFACTOR: VectorLayer commented out
102 //            var marker:Marker = markers[id];
103 //            if (marker == null) {
104 //              marker = new Marker(this, id, 0, tags, true, lat, lon);
105 //              markers[id]=marker;
106 //            }
107 //            return marker;
108                         return null;
109         }
110
111         public function registerPOI(node:Node):void {
112             if (pois.indexOf(node)<0) { pois.push(node); }
113         }
114         public function unregisterPOI(node:Node):void {
115                         var index:uint = pois.indexOf(node);
116                         if ( index >= 0 ) { pois.splice(index,1); }
117         }
118
119                 public function getObjectsByBbox(left:Number, right:Number, top:Number, bottom:Number):Object {
120                         // ** FIXME: this is just copied-and-pasted from Connection.as, which really isn't very
121                         // good practice. Is there a more elegant way of doing it?
122                         var o:Object = { poisInside: [], poisOutside: [], waysInside: [], waysOutside: [],
123                               markersInside: [], markersOutside: [] };
124                               
125                         for each (var way:Way in ways) {
126                                 if (way.within(left,right,top,bottom)) { o.waysInside.push(way); }
127                                                                   else { o.waysOutside.push(way); }
128                         }
129                         for each (var poi:Node in pois) {
130                                 if (poi.within(left,right,top,bottom)) { o.poisInside.push(poi); }
131                                                                   else { o.poisOutside.push(poi); }
132                         }
133             for each (var marker:Marker in markers) {
134                 if (marker.within(left,right,top,bottom)) { o.markersInside.push(marker); }
135                                                      else { o.markersOutside.push(marker); }
136             }
137                         return o;
138                 }
139
140         /**
141         * Transfers an entity from the VectorLayer into the main layer
142         * @param entity The entity from the VectorLayer that you want to transfer.
143         * @param connection The Connection instance to transfer to (eg Connection.getConnection() )
144         *
145         * @return either the newly created entity, or null
146         */
147                 public function pullThrough(entity:Entity,connection:Connection):Entity {
148                         var i:uint=0;
149                         var oldNode:Node, newNode:Node;
150                         if (entity is Way) {
151                                 // copy way through to main layer
152                                 // ** shouldn't do this if the nodes are already in the main layer
153                                 //    (or maybe we should just match on lat/long to avoid ways in background having nodes in foreground)
154                                 var oldWay:Way=Way(entity);
155                                 var newWay:Way=connection.createWay(oldWay.getTagsCopy(), [], MainUndoStack.getGlobalStack().addAction);
156                                 var nodemap:Object={};
157                                 for (i=0; i<oldWay.length; i++) {
158                                         oldNode = oldWay.getNode(i);
159                                         newNode = nodemap[oldNode.id] ? nodemap[oldNode.id] : connection.createNode(
160                                                 oldNode.getTagsCopy(), oldNode.lat, oldNode.lon, 
161                                                 MainUndoStack.getGlobalStack().addAction);
162                                         newWay.appendNode(newNode, MainUndoStack.getGlobalStack().addAction);
163                                         nodemap[oldNode.id]=newNode;
164                                 }
165                                 // delete this way
166                                 while (oldWay.length) { 
167                                         var id:Number=oldWay.getNode(0).id;
168                                         oldWay.removeNodeByIndex(0,MainUndoStack.getGlobalStack().addAction,false);
169                                         delete nodes[id];
170                                 }
171                                 paint.wayuis[oldWay.id].redraw();
172                                 delete ways[oldWay.id];
173                                 paint.createWayUI(newWay);
174                                 return newWay;
175
176                         } else if (entity is Node && !entity.hasParentWays) {
177                                 // copy node through to main layer
178                                 // ** should be properly undoable
179                                 oldNode=Node(entity);
180                                 unregisterPOI(oldNode);
181                                 // >>>> REFACTOR: VectorLayer commented out
182 //                              var newPoiAction:CreatePOIAction = new CreatePOIAction(
183 //                                      this, oldNode.getTagsCopy(), oldNode.lat, oldNode.lon);
184 //                              MainUndoStack.getGlobalStack().addAction(newPoiAction);
185 //                              paint.deleteNodeUI(oldNode);
186 //                              delete nodes[oldNode.id];
187 //                              return newPoiAction.getNode();
188                                 return null;
189                         }
190                         return null;
191                 }
192
193         /**
194         * Remove all the nodes, ways, and relations from the VectorLayer.
195         * Also removes the associated NodeUIs, WayUIs and POIs
196         */
197                 public function blank():void {
198                         for each (var node:Node in nodes) { paint.deleteNodeUI(node); }
199                         for each (var way:Way in ways) { paint.deleteWayUI(way); }
200                         relations={}; nodes={}; ways={}; pois=[];
201                 }
202
203         /**
204         * Load more data, eg during panning of the map. This can be overridden by
205         * vector layers if it makes sense to support it.
206         */
207         public function loadBbox(left:Number, right:Number,
208                                 top:Number, bottom:Number):void {
209         }
210
211         }
212 }