Merge branch 'master' into history
[potlatch2.git] / net / systemeD / halcyon / connection / Trace.as
1 package net.systemeD.halcyon.connection {
2
3     import flash.events.EventDispatcher;
4     import flash.utils.Dictionary;
5     import flash.events.*;
6     import flash.net.URLLoader;
7
8     import net.systemeD.halcyon.connection.*;
9     import net.systemeD.halcyon.Map;
10     import net.systemeD.halcyon.Globals;
11     import net.systemeD.halcyon.MapPaint;
12
13     /**
14     * Implements trace objects loaded from the OSM API.
15     * See also potlatch2's utils GpxImporter.as and Importer.as classes, which can handle
16     * loading GPX files (and other formats) from arbitrary urls.
17     */
18     public class Trace extends EventDispatcher {
19         private var _id:Number; // id of the trace, as reported by the server
20         private var _description:String; // description, as reported by the server
21         private var tags:Array = []; // N.B. trace tags are NOT k/v pairs
22         private var _isLoaded:Boolean; // Flag for when the data has been downloaded and parsed
23         private var _filename:String; // The original name of the file, as reported by the server
24         private var _traceData:String; // the trace data, saved as a string
25         private var map:Map;
26         private var _layer:MapPaint;
27         private var masterConnection:Connection; // The authenticated connection
28         private var _connection:Connection; // The one we store our fake nodes/ways in.
29         private var simplify:Boolean = false;
30
31         private static const STYLESHEET:String="stylesheets/gpx.css";
32
33         /** Create a new trace.
34         * @param masterConnection The authenticated connection to communicate with the server
35         */
36         public function Trace(masterConnection:Connection, id:int=0) {
37             this.masterConnection = masterConnection;
38             map = Globals.vars.root; // REFACTOR this prevents traces being added to arbitrary maps
39                         if (id!=0) _id=id;
40         }
41
42         /** Create a new trace, from the XML description given by the user/traces call.
43         * This only creates the object itself, the actual trace contents (trkseg etc) are
44         * lazily downloaded later. */
45         public function fromXML(xml:XML):Trace {
46             _id = Number(xml.@id);
47             _filename = xml.@name;
48             _description = xml.description;
49             for each(var tag:XML in xml.tag) {
50               tags.push(String(tag));
51             }
52             return this;
53         }
54
55         public function get id():Number {
56             return _id;
57         }
58
59         public function get description():String {
60             return _description;
61         }
62
63         public function get filename():String {
64             return _filename;
65         }
66
67         public function get tagsText():String {
68             return tags.join(", ");
69         }
70
71         private function fetchFromServer():void {
72             // todo - needs proper error handling
73             masterConnection.fetchTrace(id, saveTraceData);
74             dispatchEvent(new Event("loading_data"));
75         }
76
77         private function saveTraceData(event:Event):void {
78             _traceData = String(URLLoader(event.target).data);
79             dispatchEvent(new Event("loaded_data"));
80         }
81
82         private function get connection():Connection {
83             if (!_connection) {
84                 // create a new connection so that the ids don't impact the main layer.
85                 _connection = new Connection(filename, null, null, null);
86             }
87             return _connection
88         }
89
90         private function get layer():MapPaint {
91             if (!_layer) {
92                 // create a new layer for every trace, so they can be turned on/off individually
93                 _layer = map.addLayer(connection, STYLESHEET);
94             }
95             return _layer;
96         }
97
98         public function addToMap():void {
99             // this allows adding and removing traces from the map, without re-downloading
100             // the data from the server repeatedly.
101             if (!_isLoaded) {
102               addEventListener("loaded_data", processEvent);
103               fetchFromServer();
104               return;
105             } else {
106               process();
107             }
108         }
109
110         public function removeFromMap():void {
111             //todo
112         }
113
114         private function processEvent(e:Event):void {
115             removeEventListener("loaded_data", processEvent);
116             _isLoaded=true;
117             process();
118         }
119
120         private function process():void {
121             var file:XML = new XML(_traceData);
122             var action:CompositeUndoableAction = new CompositeUndoableAction("add trace objects");
123                         for each (var ns:Namespace in file.namespaceDeclarations()) {
124                                 if (ns.uri.match(/^http:\/\/www\.topografix\.com\/GPX\/1\/[01]$/)) {
125                                         default xml namespace = ns;
126                                 }
127                         }
128
129             for each (var trkseg:XML in file..trkseg) {
130                 var way:Way;
131                 var nodestring:Array = [];
132                 for each (var trkpt:XML in trkseg.trkpt) {
133                     nodestring.push(connection.createNode({}, trkpt.@lat, trkpt.@lon, action.push));
134                 }
135                 if (nodestring.length > 0) {
136                     way = connection.createWay({}, nodestring, action.push);
137                     //if (simplify) { Simplify.simplify(way, paint.map, false); }
138                 }
139             }
140
141             for each (var wpt:XML in file.wpt) {
142                 var tags:Object = {};
143                 for each (var tag:XML in wpt.children()) {
144                     tags[tag.name().localName]=tag.toString();
145                 }
146                 var node:Node = connection.createNode(tags, wpt.@lat, wpt.@lon, action.push);
147                                 connection.registerPOI(node);
148             }
149
150             action.doAction(); /* just do it, don't add to undo stack */
151                         default xml namespace = new Namespace("");
152             layer.updateEntityUIs(true, false);
153         }
154     }
155 }