1 package net.systemeD.halcyon.connection {
5 import flash.system.Security;
7 import org.iotashan.oauth.*;
9 import net.systemeD.halcyon.Globals;
11 public class XMLConnection extends Connection {
13 //public var readConnection:NetConnection;
15 public function XMLConnection() {
17 if (Connection.policyURL!='')
18 Security.loadPolicyFile(Connection.policyURL);
19 var oauthPolicy:String = Connection.getParam("oauth_policy", "");
20 if ( oauthPolicy != "" ) {
22 Security.loadPolicyFile(oauthPolicy);
26 override public function loadBbox(left:Number,right:Number,
27 top:Number,bottom:Number):void {
28 var mapVars:URLVariables = new URLVariables();
29 mapVars.bbox= left+","+bottom+","+right+","+top;
31 var mapRequest:URLRequest = new URLRequest(Connection.apiBaseURL+"map");
32 mapRequest.data = mapVars;
34 var mapLoader:URLLoader = new URLLoader();
35 mapLoader.addEventListener(Event.COMPLETE, loadedMap);
36 mapLoader.addEventListener(IOErrorEvent.IO_ERROR, errorOnMapLoad);
37 mapLoader.addEventListener(HTTPStatusEvent.HTTP_STATUS, mapLoadStatus);
38 mapLoader.load(mapRequest);
39 dispatchEvent(new Event(LOAD_STARTED));
42 private function parseTags(tagElements:XMLList):Object {
44 for each (var tagEl:XML in tagElements)
45 tags[tagEl.@k] = tagEl.@v;
49 private function errorOnMapLoad(event:Event):void {
50 trace("error loading map");
52 private function mapLoadStatus(event:HTTPStatusEvent):void {
53 trace("loading map status = "+event.status);
56 private function loadedMap(event:Event):void {
57 dispatchEvent(new Event(LOAD_COMPLETED));
59 var map:XML = new XML(URLLoader(event.target).data);
64 for each(var nodeData:XML in map.node) {
65 id = Number(nodeData.@id);
66 version = uint(nodeData.@version);
68 var node:Node = getNode(id);
69 if ( node == null || !node.loaded ) {
70 var lat:Number = Number(nodeData.@lat);
71 var lon:Number = Number(nodeData.@lon);
72 tags = parseTags(nodeData.tag);
74 setNode(new Node(id, version, tags, true, lat, lon),false);
76 node.update(version, tags, true, lat, lon);
77 sendEvent(new EntityEvent(NEW_NODE, node), false);
82 for each(var data:XML in map.way) {
83 id = Number(data.@id);
84 version = uint(data.@version);
86 var way:Way = getWay(id);
87 if ( way == null || !way.loaded ) {
89 for each(var nd:XML in data.nd)
90 nodes.push(getNode(Number(nd.@ref)));
91 tags = parseTags(data.tag);
93 setWay(new Way(id, version, tags, true, nodes),false);
95 way.update(version, tags, true, nodes);
96 sendEvent(new EntityEvent(NEW_WAY, way), false);
101 for each(var relData:XML in map.relation) {
102 id = Number(relData.@id);
103 version = uint(relData.@version);
105 var rel:Relation = getRelation(id);
106 if ( rel == null || !rel.loaded ) {
107 tags = parseTags(relData.tag);
108 var members:Array = [];
109 for each(var memberXML:XML in relData.member) {
110 var type:String = memberXML.@type.toLowerCase();
111 var role:String = memberXML.@role;
112 var memberID:Number = Number(memberXML.@ref);
113 var member:Entity = null;
114 if ( type == "node" ) {
115 member = getNode(memberID);
116 if ( member == null ) {
117 member = new Node(memberID,0,{},false,0,0);
118 setNode(Node(member),true);
120 } else if ( type == "way" ) {
121 member = getWay(memberID);
122 if (member == null) {
123 member = new Way(memberID,0,{},false,[]);
124 setWay(Way(member),true);
126 } else if ( type == "relation" ) {
127 member = getRelation(memberID);
128 if (member == null) {
129 member = new Relation(memberID,0,{},false,[]);
130 setRelation(Relation(member),true);
134 if ( member != null )
135 members.push(new RelationMember(member, role));
139 setRelation(new Relation(id, version, tags, true, members), false);
141 rel.update(version,tags,true,members);
142 sendEvent(new EntityEvent(NEW_RELATION, rel), false);
150 protected function registerPOINodes():void {
151 for each (var nodeID:Number in getAllNodeIDs()) {
152 var node:Node = getNode(nodeID);
153 if (!node.hasParentWays)
158 protected var appID:OAuthConsumer;
159 protected var authToken:OAuthToken;
161 override public function setAppID(id:Object):void {
162 appID = OAuthConsumer(id);
165 override public function setAuthToken(id:Object):void {
166 authToken = OAuthToken(id);
169 private var httpStatus:int = 0;
171 private function recordStatus(event:HTTPStatusEvent):void {
172 httpStatus = event.status;
175 private var lastUploadedChangesetTags:Object;
177 override public function createChangeset(tags:Object):void {
178 lastUploadedChangesetTags = tags;
180 var changesetXML:XML = <osm version="0.6"><changeset /></osm>;
181 var changeset:XML = <changeset />;
182 for (var tagKey:Object in tags) {
183 var tagXML:XML = <tag/>;
185 tagXML.@v = tags[tagKey];
186 changesetXML.changeset.appendChild(tagXML);
189 // make an OAuth query
190 var sig:IOAuthSignatureMethod = new OAuthSignatureMethod_HMAC_SHA1();
191 var url:String = Connection.apiBaseURL+"changeset/create";
192 //var params:Object = { _method: "PUT" };
193 var oauthRequest:OAuthRequest = new OAuthRequest("PUT", url, null, appID, authToken);
194 var urlStr:Object = oauthRequest.buildRequest(sig, OAuthRequest.RESULT_TYPE_URL_STRING)
196 // build the actual request
197 var urlReq:URLRequest = new URLRequest(String(urlStr));
198 urlReq.method = "POST";
199 urlReq.data = changesetXML.toXMLString();
200 urlReq.contentType = "application/xml";
201 urlReq.requestHeaders = new Array(new URLRequestHeader("X_HTTP_METHOD_OVERRIDE", "PUT"));
202 var loader:URLLoader = new URLLoader();
203 loader.addEventListener(Event.COMPLETE, changesetCreateComplete);
204 loader.addEventListener(IOErrorEvent.IO_ERROR, changesetCreateError);
205 loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, recordStatus);
209 private function changesetCreateComplete(event:Event):void {
210 // response should be a Number changeset id
211 var id:Number = Number(URLLoader(event.target).data);
213 // which means we now have a new changeset!
214 setActiveChangeset(new Changeset(id, lastUploadedChangesetTags));
217 private function changesetCreateError(event:IOErrorEvent):void {
218 dispatchEvent(new Event(NEW_CHANGESET_ERROR));
221 override public function uploadChanges():void {
222 var changeset:Changeset = getActiveChangeset();
223 var upload:XML = <osmChange version="0.6"/>
224 upload.appendChild(addCreated(changeset, getAllNodeIDs, getNode, serialiseNode));
225 upload.appendChild(addCreated(changeset, getAllWayIDs, getWay, serialiseWay));
226 upload.appendChild(addCreated(changeset, getAllRelationIDs, getRelation, serialiseRelation));
227 upload.appendChild(addModified(changeset, getAllNodeIDs, getNode, serialiseNode));
228 upload.appendChild(addModified(changeset, getAllWayIDs, getWay, serialiseWay));
229 upload.appendChild(addModified(changeset, getAllRelationIDs, getRelation, serialiseRelation));
230 upload.appendChild(addDeleted(changeset, getAllRelationIDs, getRelation, serialiseEntityRoot));
231 upload.appendChild(addDeleted(changeset, getAllWayIDs, getWay, serialiseEntityRoot));
232 upload.appendChild(addDeleted(changeset, getAllNodeIDs, getNode, serialiseEntityRoot));
236 // now actually upload them
237 // make an OAuth query
238 var sig:IOAuthSignatureMethod = new OAuthSignatureMethod_HMAC_SHA1();
239 var url:String = Connection.apiBaseURL+"changeset/" + changeset.id + "/upload";
240 var oauthRequest:OAuthRequest = new OAuthRequest("POST", url, null, appID, authToken);
241 var urlStr:Object = oauthRequest.buildRequest(sig, OAuthRequest.RESULT_TYPE_URL_STRING)
243 // build the actual request
244 var urlReq:URLRequest = new URLRequest(String(urlStr));
245 urlReq.method = "POST";
246 urlReq.data = upload.toXMLString();
247 urlReq.contentType = "text/xml";
248 var loader:URLLoader = new URLLoader();
249 loader.addEventListener(Event.COMPLETE, diffUploadComplete);
250 loader.addEventListener(IOErrorEvent.IO_ERROR, diffUploadError);
251 loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, recordStatus);
254 dispatchEvent(new Event(SAVE_STARTED));
257 private function diffUploadComplete(event:Event):void {
258 // response should be XML describing the progress
259 var results:XML = new XML((URLLoader(event.target).data));
261 for each( var update:XML in results.child("*") ) {
262 var oldID:Number = Number(update.@old_id);
263 var newID:Number = Number(update.@new_id);
264 var version:uint = uint(update.@new_version);
265 var type:String = update.name();
269 if ( type == "node" ) killNode(oldID);
270 else if ( type == "way" ) killWay(oldID);
271 else if ( type == "relation" ) killRelation(oldID);
276 if ( type == "node" ) entity = getNode(oldID);
277 else if ( type == "way" ) entity = getWay(oldID);
278 else if ( type == "relation" ) entity = getRelation(oldID);
279 entity.markClean(newID, version);
281 if ( oldID != newID ) {
282 if ( type == "node" ) renumberNode(oldID, entity as Node, false);
283 else if ( type == "way" ) renumberWay(oldID, entity as Way, false);
284 else if ( type == "relation" ) renumberRelation(oldID, entity as Relation, false);
289 dispatchEvent(new SaveCompleteEvent(SAVE_COMPLETED, true));
292 private function diffUploadError(event:IOErrorEvent):void {
293 trace("error "+URLLoader(event.target).data + " "+httpStatus+ " " + event.text);
295 dispatchEvent(new SaveCompleteEvent(SAVE_COMPLETED, false));
298 private function addCreated(changeset:Changeset, getIDs:Function, get:Function, serialise:Function):XML {
299 var create:XML = <create version="0.6"/>
300 for each( var id:Number in getIDs() ) {
301 var entity:Entity = get(id);
302 if ( id >= 0 || entity.deleted )
305 var xml:XML = serialise(entity);
306 xml.@changeset = changeset.id;
307 create.appendChild(xml);
309 return create.hasComplexContent() ? create : <!-- blank create section -->;
312 private function addDeleted(changeset:Changeset, getIDs:Function, get:Function, serialise:Function):XML {
313 var del:XML = <delete version="0.6"/>
314 for each( var id:Number in getIDs() ) {
315 var entity:Entity = get(id);
316 // creates are already included
317 if ( id < 0 || !entity.deleted )
320 var xml:XML = serialise(entity);
321 xml.@changeset = changeset.id;
322 del.appendChild(xml);
324 return del.hasComplexContent() ? del : <!-- blank delete section -->;
327 private function addModified(changeset:Changeset, getIDs:Function, get:Function, serialise:Function):XML {
328 var modify:XML = <modify version="0.6"/>
329 for each( var id:Number in getIDs() ) {
330 var entity:Entity = get(id);
331 // creates and deletes are already included
332 if ( id < 0 || entity.deleted || !entity.isDirty )
335 var xml:XML = serialise(entity);
336 xml.@changeset = changeset.id;
337 modify.appendChild(xml);
339 return modify.hasComplexContent() ? modify : <!-- blank modify section -->;
342 private function serialiseNode(node:Node):XML {
343 var xml:XML = serialiseEntityRoot(node); //<node/>
344 serialiseEntityTags(node, xml);
350 private function serialiseWay(way:Way):XML {
351 var xml:XML = serialiseEntityRoot(way); //<node/>
352 serialiseEntityTags(way, xml);
353 for ( var i:uint = 0; i < way.length; i++ ) {
355 nd.@ref = way.getNode(i).id;
361 private function serialiseRelation(relation:Relation):XML {
362 var xml:XML = serialiseEntityRoot(Relation); //<node/>
363 serialiseEntityTags(relation, xml);
364 for ( var i:uint = 0; i < relation.length; i++ ) {
365 var relMember:RelationMember = relation.getMember(i);
366 var member:XML = <member/>
367 member.@ref = relMember.entity.id;
368 member.@type = relMember.entity.getType();
369 member.@role = relMember.role;
370 xml.appendChild(member);
375 private function serialiseEntityRoot(entity:Object):XML {
377 if (entity is Way ) { xml = <way/> }
378 else if (entity is Node ) { xml = <node/> }
379 else if (entity is Relation) { xml = <relation/> }
381 xml.@version = entity.version;
385 private function serialiseEntityTags(entity:Entity, xml:XML):void {
387 xml.@version = entity.version;
388 for each( var tag:Tag in entity.getTagArray() ) {
389 var tagXML:XML = <tag/>
391 tagXML.@v = tag.value;
392 xml.appendChild(tagXML);