1 package net.systemeD.halcyon.connection {
5 import flash.events.EventDispatcher;
6 import flash.events.Event;
7 import net.systemeD.halcyon.Globals;
8 import net.systemeD.halcyon.connection.actions.*;
10 public class Connection extends EventDispatcher {
12 private static var connectionInstance:Connection = null;
14 protected static var policyURL:String;
15 protected static var apiBaseURL:String;
16 protected static var params:Object;
18 public static function getConnection(initparams:Object=null):Connection {
19 if ( connectionInstance == null ) {
21 params = initparams == null ? new Object() : initparams;
22 policyURL = getParam("policy", "http://127.0.0.1:3000/api/crossdomain.xml");
23 apiBaseURL = getParam("api", "http://127.0.0.1:3000/api/0.6/");
24 var connectType:String = getParam("connection", "XML");
26 if ( connectType == "XML" )
27 connectionInstance = new XMLConnection();
28 else if ( connectType == "OSM" )
29 connectionInstance = new OSMConnection();
31 connectionInstance = new AMFConnection();
33 return connectionInstance;
36 public static function getParam(name:String, defaultValue:String):String {
37 return params[name] == null ? defaultValue : params[name];
40 public function get apiBase():String {
44 public static function get serverName():String {
45 return getParam("serverName", "Localhost");
48 public static function getConnectionInstance():Connection {
49 return connectionInstance;
52 public function getEnvironment(responder:Responder):void {}
55 public static var LOAD_STARTED:String = "load_started";
56 public static var LOAD_COMPLETED:String = "load_completed";
57 public static var SAVE_STARTED:String = "save_started";
58 public static var SAVE_COMPLETED:String = "save_completed";
59 public static var DATA_DIRTY:String = "data_dirty";
60 public static var DATA_CLEAN:String = "data_clean";
61 public static var NEW_CHANGESET:String = "new_changeset";
62 public static var NEW_CHANGESET_ERROR:String = "new_changeset_error";
63 public static var NEW_NODE:String = "new_node";
64 public static var NEW_WAY:String = "new_way";
65 public static var NEW_RELATION:String = "new_relation";
66 public static var NEW_POI:String = "new_poi";
67 public static var NODE_RENUMBERED:String = "node_renumbered";
68 public static var WAY_RENUMBERED:String = "way_renumbered";
69 public static var RELATION_RENUMBERED:String = "relation_renumbered";
70 public static var TAG_CHANGED:String = "tag_change";
71 public static var NODE_MOVED:String = "node_moved";
72 public static var NODE_ALTERED:String = "node_altered";
73 public static var WAY_NODE_ADDED:String = "way_node_added";
74 public static var WAY_NODE_REMOVED:String = "way_node_removed";
75 public static var WAY_REORDERED:String = "way_reordered";
76 public static var WAY_DRAGGED:String = "way_dragged";
77 public static var NODE_DELETED:String = "node_deleted";
78 public static var WAY_DELETED:String = "way_deleted";
79 public static var RELATION_DELETED:String = "relation_deleted";
80 public static var RELATION_MEMBER_ADDED:String = "relation_member_added";
81 public static var RELATION_MEMBER_REMOVED:String = "relation_member_deleted";
82 public static var ADDED_TO_RELATION:String = "added_to_relation";
83 public static var REMOVED_FROM_RELATION:String = "removed_from_relation";
84 public static var SUSPEND_REDRAW:String = "suspend_redraw";
85 public static var RESUME_REDRAW:String = "resume_redraw";
86 public static var TRACES_LOADED:String = "traces_loaded";
88 // store the data we download
89 private var negativeID:Number = -1;
90 private var nodes:Object = {};
91 private var ways:Object = {};
92 private var relations:Object = {};
93 private var pois:Array = [];
94 private var changeset:Changeset = null;
95 private var changesetUpdated:Number;
96 private var modified:Boolean = false;
97 public var nodecount:int=0;
98 public var waycount:int=0;
99 public var relationcount:int=0;
100 private var traces:Array = [];
101 private var nodePositions:Object = {};
102 protected var traces_loaded:Boolean = false;
104 protected function get nextNegative():Number {
108 protected function setNode(node:Node, queue:Boolean):void {
109 if (!nodes[node.id]) { nodecount++; }
110 nodes[node.id] = node;
112 if (node.loaded) { sendEvent(new EntityEvent(NEW_NODE, node),queue); }
115 protected function setWay(way:Way, queue:Boolean):void {
116 if (!ways[way.id]) { waycount++; }
118 if (way.loaded) { sendEvent(new EntityEvent(NEW_WAY, way),queue); }
121 protected function setRelation(relation:Relation, queue:Boolean):void {
122 if (!relations[relation.id]) { relationcount++; }
123 relations[relation.id] = relation;
124 if (relation.loaded) { sendEvent(new EntityEvent(NEW_RELATION, relation),queue); }
127 protected function renumberNode(oldID:Number, newID:Number, version:uint):void {
128 var node:Node=nodes[oldID];
129 if (oldID!=newID) { removeDupe(node); }
130 node.renumber(newID, version);
131 if (oldID==newID) return; // if only a version change, return
134 if (node.loaded) { sendEvent(new EntityRenumberedEvent(NODE_RENUMBERED, node, oldID),false); }
138 protected function renumberWay(oldID:Number, newID:Number, version:uint):void {
139 var way:Way=ways[oldID];
140 way.renumber(newID, version);
141 if (oldID==newID) return;
143 if (way.loaded) { sendEvent(new EntityRenumberedEvent(WAY_RENUMBERED, way, oldID),false); }
147 protected function renumberRelation(oldID:Number, newID:Number, version:uint):void {
148 var relation:Relation=relations[oldID];
149 relation.renumber(newID, version);
150 if (oldID==newID) return;
151 relations[newID] = relation;
152 if (relation.loaded) { sendEvent(new EntityRenumberedEvent(RELATION_RENUMBERED, relation, oldID),false); }
153 delete relations[oldID];
157 public function sendEvent(e:*,queue:Boolean):void {
158 // queue is only used for AMFConnection
162 public function registerPOI(node:Node):void {
163 if ( pois.indexOf(node) < 0 ) {
165 sendEvent(new EntityEvent(NEW_POI, node),false);
169 public function unregisterPOI(node:Node):void {
170 var index:uint = pois.indexOf(node);
172 pois.splice(index,1);
176 public function getNode(id:Number):Node {
180 public function getWay(id:Number):Way {
184 public function getRelation(id:Number):Relation {
185 return relations[id];
188 // Remove data from Connection
189 // These functions are used only internally to stop redundant data hanging around
190 // (either because it's been deleted on the server, or because we have panned away
191 // and need to reduce memory usage)
193 protected function killNode(id:Number):void {
194 if (!nodes[id]) return;
195 nodes[id].dispatchEvent(new EntityEvent(Connection.NODE_DELETED, nodes[id]));
196 removeDupe(nodes[id]);
197 if (nodes[id].parentRelations.length>0) {
205 protected function killWay(id:Number):void {
206 if (!ways[id]) return;
207 ways[id].dispatchEvent(new EntityEvent(Connection.WAY_DELETED, ways[id]));
208 if (ways[id].parentRelations.length>0) {
216 protected function killRelation(id:Number):void {
217 if (!relations[id]) return;
218 relations[id].dispatchEvent(new EntityEvent(Connection.RELATION_DELETED, relations[id]));
219 if (relations[id].parentRelations.length>0) {
220 relations[id].nullify();
222 delete relations[id];
227 protected function killWayWithNodes(id:Number):void {
228 var way:Way=ways[id];
230 for (var i:uint=0; i<way.length; i++) {
232 if (node.isDirty) { continue; }
233 if (node.parentWays.length>1) {
234 node.removeParent(way);
244 public function createNode(tags:Object, lat:Number, lon:Number, performCreate:Function):Node {
245 var node:Node = new Node(nextNegative, 0, tags, true, lat, lon);
246 performCreate(new CreateEntityAction(node, setNode));
251 public function createWay(tags:Object, nodes:Array, performCreate:Function):Way {
252 var way:Way = new Way(nextNegative, 0, tags, true, nodes.concat());
253 performCreate(new CreateEntityAction(way, setWay));
258 public function createRelation(tags:Object, members:Array, performCreate:Function):Relation {
259 var relation:Relation = new Relation(nextNegative, 0, tags, true, members.concat());
260 performCreate(new CreateEntityAction(relation, setRelation));
265 public function getAllNodeIDs():Array {
267 for each (var node:Node in nodes)
272 public function getAllWayIDs():Array {
274 for each (var way:Way in ways)
279 public function getAllRelationIDs():Array {
281 for each (var relation:Relation in relations)
282 list.push(relation.id);
286 public function getMatchingRelationIDs(match:Object):Array {
289 for each (var relation:Relation in relations) {
291 if (relation.deleted) { ok=false; }
292 for (var k:String in match) {
293 if (!relation.getTagsHash()[k] || relation.getTagsHash()[k]!=match[k]) { ok=false; }
295 if (ok) { list.push(relation.id); }
300 public function getObjectsByBbox(left:Number, right:Number, top:Number, bottom:Number):Object {
301 var o:Object = { poisInside: [], poisOutside: [], waysInside: [], waysOutside: [] };
302 for each (var way:Way in ways) {
303 if (way.within(left,right,top,bottom)) { o.waysInside.push(way); }
304 else { o.waysOutside.push(way); }
306 for each (var poi:Node in pois) {
307 if (poi.within(left,right,top,bottom)) { o.poisInside.push(poi); }
308 else { o.poisOutside.push(poi); }
313 public function purgeOutside(left:Number, right:Number, top:Number, bottom:Number):void {
314 for each (var way:Way in ways) {
315 if (!way.within(left,right,top,bottom) && !way.isDirty) {
316 killWayWithNodes(way.id);
319 for each (var poi:Node in pois) {
320 if (!poi.within(left,right,top,bottom) && !poi.isDirty) {
324 // ** should purge relations too, if none of their members are on-screen
327 public function markDirty():void {
328 if (!modified) { dispatchEvent(new Event(DATA_DIRTY)); }
331 public function markClean():void {
332 if (modified) { dispatchEvent(new Event(DATA_CLEAN)); }
335 public function get isDirty():Boolean {
339 // Changeset tracking
341 protected function setActiveChangeset(changeset:Changeset):void {
342 this.changeset = changeset;
343 changesetUpdated = new Date().getTime();
344 sendEvent(new EntityEvent(NEW_CHANGESET, changeset),false);
347 protected function freshenActiveChangeset():void {
348 changesetUpdated = new Date().getTime();
351 protected function closeActiveChangeset():void {
355 public function getActiveChangeset():Changeset {
356 if (changeset && (new Date().getTime()) > (changesetUpdated+58*60*1000)) {
357 closeActiveChangeset();
362 protected function addTrace(t:Object):void {
366 protected function clearTraces():void {
370 public function getTraces():Array {
374 public function addDupe(node:Node):void {
375 if (getNode(node.id) != node) { return; } // make sure it's on this connection
376 var a:String = node.lat+","+node.lon;
377 if(!nodePositions[a]) {
378 nodePositions[a] = [];
380 nodePositions[a].push(node);
381 if (nodePositions[a].length > 1) { // don't redraw if it's the only node in town
382 for each (var n:Node in nodePositions[a]) {
383 n.dispatchEvent(new Event(Connection.NODE_ALTERED));
388 public function removeDupe(node:Node):void {
389 if (getNode(node.id) != node) { return; } // make sure it's on this connection
390 var a:String = node.lat+","+node.lon;
391 var redraw:Boolean=node.isDupe();
392 var dupes:Array = [];
393 for each (var dupe:Node in nodePositions[a]) {
394 if (dupe!=node) { dupes.push(dupe); }
396 nodePositions[a] = dupes;
397 for each (var n:Node in nodePositions[a]) { // redraw any nodes remaining
398 n.dispatchEvent(new Event(Connection.NODE_ALTERED));
400 if (redraw) { node.dispatchEvent(new Event(Connection.NODE_ALTERED)); } //redraw the one being moved
403 public function nodesAtPosition(lat:Number, lon:Number):uint {
404 if (nodePositions[lat+","+lon]) {
405 return nodePositions[lat+","+lon].length;
410 // these are functions that the Connection implementation is expected to
411 // provide. This class has some generic helpers for the implementation.
412 public function loadBbox(left:Number, right:Number,
413 top:Number, bottom:Number):void {
416 public function setAuthToken(id:Object):void {}
417 public function setAccessToken(key:String, secret:String):void {}
418 public function createChangeset(tags:Object):void {}
419 public function closeChangeset():void {}
420 public function uploadChanges():void {}
421 public function fetchUserTraces(refresh:Boolean=false):void {}
422 public function fetchTrace(id:Number, callback:Function):void {}
423 public function hasAccessToken():Boolean { return false; }