| 1 |
package com.cloudmade.mappingtool.model;
|
| 2 |
|
| 3 |
import com.cloudmade.mappingtool.business.externalization.IXMLListSerializer;
|
| 4 |
import com.cloudmade.mappingtool.business.externalization.IXMLSerializer;
|
| 5 |
import com.cloudmade.mappingtool.business.externalization.osm.ListExternalizer;
|
| 6 |
import com.cloudmade.mappingtool.business.externalization.osm.TagExternalizer;
|
| 7 |
import com.cloudmade.mappingtool.business.externalization.osm.ValidTagsSerializer;
|
| 8 |
import com.cloudmade.mappingtool.events.OSMProxyCommitErrorEvent;
|
| 9 |
import com.cloudmade.mappingtool.events.OSMProxyLoadErrorEvent;
|
| 10 |
import com.cloudmade.mappingtool.events.OSMProxyResultEvent;
|
| 11 |
import com.cloudmade.mappingtool.events.OSMProxySaveEvent;
|
| 12 |
import com.cloudmade.mappingtool.map.geo.LatLong;
|
| 13 |
import com.cloudmade.mappingtool.map.geo.LatLongBounds;
|
| 14 |
import com.cloudmade.mappingtool.map.model.Member;
|
| 15 |
import com.cloudmade.mappingtool.map.model.Node;
|
| 16 |
import com.cloudmade.mappingtool.map.model.Relation;
|
| 17 |
import com.cloudmade.mappingtool.map.model.Tag;
|
| 18 |
import com.cloudmade.mappingtool.map.model.Way;
|
| 19 |
import com.cloudmade.mappingtool.model.osm.ElementDiff;
|
| 20 |
import com.cloudmade.mappingtool.model.osm.OAuthAuthorizator;
|
| 21 |
import com.cloudmade.mappingtool.model.osm.Transaction;
|
| 22 |
import com.cloudmade.mappingtool.model.osm.TransactionQueue;
|
| 23 |
|
| 24 |
import flash.errors.IOError;
|
| 25 |
import flash.events.ErrorEvent;
|
| 26 |
import flash.events.Event;
|
| 27 |
import flash.events.EventDispatcher;
|
| 28 |
import flash.events.HTTPStatusEvent;
|
| 29 |
import flash.events.IOErrorEvent;
|
| 30 |
import flash.events.SecurityErrorEvent;
|
| 31 |
import flash.net.URLLoader;
|
| 32 |
import flash.net.URLRequest;
|
| 33 |
import flash.system.Security;
|
| 34 |
import flash.utils.ByteArray;
|
| 35 |
|
| 36 |
import mx.managers.CursorManager;
|
| 37 |
|
| 38 |
/*[Event(name="save", type="com.cloudmade.mappingtool.events.OSMProxyResultEvent")]*/
|
| 39 |
/*[Event(name="result", type="com.cloudmade.mappingtool.events.OSMProxyResultEvent")]*/
|
| 40 |
/*[Event(name="saveFailed", type="com.cloudmade.mappingtool.events.OSMProxySaveEvent")]*/
|
| 41 |
/*[Event(name="saveComplete", type="com.cloudmade.mappingtool.events.OSMProxySaveEvent")]*/
|
| 42 |
/*[Event(name="loadError", type="com.cloudmade.mappingtool.events.OSMProxyLoadErrorEvent")]*/
|
| 43 |
/*[Event(name="commitError", type="com.cloudmade.mappingtool.events.OSMProxyCommitErrorEvent")]*/
|
| 44 |
/*[Event(name="loadingChanged", type="flash.events.Event")]*/
|
| 45 |
|
| 46 |
// TODO: Use polymorphism.
|
| 47 |
|
| 48 |
public final class OSMProxy extends EventDispatcher {
|
| 49 |
|
| 50 |
public var changesetOpened(getChangesetOpened, null) : Bool ;
|
| 51 |
public var loading(getLoading, null) : Bool ;
|
| 52 |
/*[Embed(source="../../../../config/map.xml", mimeType="application/octet-stream")]*/
|
| 53 |
CONFIG::local
|
| 54 |
static var LOCAL_MAP:Class<Dynamic>;
|
| 55 |
|
| 56 |
static var instance:OSMProxy = null;
|
| 57 |
|
| 58 |
public static function getInstance():OSMProxy {
|
| 59 |
if (instance == null) {
|
| 60 |
instance = new OSMProxy(new SingletonEnforcer());
|
| 61 |
}
|
| 62 |
return instance;
|
| 63 |
}
|
| 64 |
|
| 65 |
CONFIG::public debug
|
| 66 |
var errorModeEnabled:Bool ;
|
| 67 |
|
| 68 |
var nodeMap:Dynamic;
|
| 69 |
var changesetId:String;
|
| 70 |
var lastHttpStatus:Int;
|
| 71 |
var createdWays:Array<Dynamic> ;
|
| 72 |
var modifiedWays:Array<Dynamic> ;
|
| 73 |
var deletedWays:Array<Dynamic> ;
|
| 74 |
var createdNodes:Array<Dynamic> ;
|
| 75 |
var modifiedNodes:Array<Dynamic> ;
|
| 76 |
var deletedNodes:Array<Dynamic> ;
|
| 77 |
var modifiedRelations:Array<Dynamic> ;
|
| 78 |
var mapLoader:URLLoader ;
|
| 79 |
var authorizator:OAuthAuthorizator ;
|
| 80 |
var openTransaction:Transaction (authorizator, true, CONFIG::osm + "changeset/create");
|
| 81 |
var closeTransaction:Transaction (authorizator, true, CONFIG::osm + "changeset/");
|
| 82 |
var commitTransaction:Transaction (authorizator, false, CONFIG::osm + "changeset/");
|
| 83 |
var transactionQueue:TransactionQueue ;
|
| 84 |
|
| 85 |
var tagsSerializer:IXMLListSerializer;
|
| 86 |
|
| 87 |
public function new(enforcer:SingletonEnforcer) {
|
| 88 |
|
| 89 |
errorModeEnabled = false;
|
| 90 |
createdWays = new Array();
|
| 91 |
modifiedWays = new Array();
|
| 92 |
deletedWays = new Array();
|
| 93 |
createdNodes = new Array();
|
| 94 |
modifiedNodes = new Array();
|
| 95 |
deletedNodes = new Array();
|
| 96 |
modifiedRelations = new Array();
|
| 97 |
mapLoader = new URLLoader();
|
| 98 |
authorizator = new OAuthAuthorizator();
|
| 99 |
openTransaction = new Transaction;
|
| 100 |
closeTransaction = new Transaction;
|
| 101 |
commitTransaction = new Transaction;
|
| 102 |
transactionQueue = new TransactionQueue();
|
| 103 |
super();
|
| 104 |
Security.loadPolicyFile(CONFIG::crossdomain);
|
| 105 |
transactionQueue.addTransaction(openTransaction);
|
| 106 |
transactionQueue.addTransaction(commitTransaction);
|
| 107 |
transactionQueue.addTransaction(closeTransaction);
|
| 108 |
mapLoader.addEventListener(Event.COMPLETE, mapLoader_completeHandler);
|
| 109 |
mapLoader.addEventListener(IOErrorEvent.IO_ERROR, mapLoader_errorHandler);
|
| 110 |
mapLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, mapLoader_errorHandler);
|
| 111 |
transactionQueue.addEventListener(Event.COMPLETE, transactionQueue_completeHandler);
|
| 112 |
transactionQueue.addEventListener(IOErrorEvent.IO_ERROR, transactionQueue_ioErrorHandler);
|
| 113 |
commitTransaction.addEventListener(IOErrorEvent.IO_ERROR, commitTransaction_ioErrorHandler);
|
| 114 |
transactionQueue.addEventListener(HTTPStatusEvent.HTTP_STATUS, transactionQueue_httpStatusHandler);
|
| 115 |
openTransaction.addEventListener(Event.COMPLETE, openTransaction_completeHandler, false, 1);
|
| 116 |
|
| 117 |
tagsSerializer = createTagsSerializer();
|
| 118 |
}
|
| 119 |
|
| 120 |
var _changesetOpened:Bool;
|
| 121 |
public function getChangesetOpened():Bool {
|
| 122 |
return _changesetOpened;
|
| 123 |
}
|
| 124 |
|
| 125 |
var _loading:Bool ;
|
| 126 |
|
| 127 |
/*[Bindable("loadingChanged")]*/
|
| 128 |
public function getLoading():Bool {
|
| 129 |
return _loading;
|
| 130 |
}
|
| 131 |
|
| 132 |
public function getMap(bounds:LatLongBounds):Void {
|
| 133 |
CONFIG::local {
|
| 134 |
var bytes:ByteArray = new LOCAL_MAP();
|
| 135 |
mapLoader.data = bytes.toString();
|
| 136 |
mapLoader_completeHandler(null);
|
| 137 |
return;
|
| 138 |
}
|
| 139 |
setLoading(true);
|
| 140 |
var boundsArray:Array<Dynamic> = [bounds.westLong, bounds.southLat, bounds.eastLong, bounds.northLat];
|
| 141 |
var url:String = CONFIG::osm + "map?bbox=" + boundsArray.join(",");
|
| 142 |
mapLoader.load(new URLRequest(url));
|
| 143 |
}
|
| 144 |
|
| 145 |
public function close():Void {
|
| 146 |
try {
|
| 147 |
mapLoader.close();
|
| 148 |
} catch (error:IOError) {
|
| 149 |
}
|
| 150 |
setLoading(false);
|
| 151 |
}
|
| 152 |
|
| 153 |
public function setCredentials(username:String, password:String):Void {
|
| 154 |
transactionQueue.setCredentials(username, password);
|
| 155 |
}
|
| 156 |
|
| 157 |
public function openChangeset(tags:Array<Dynamic>):Void {
|
| 158 |
if (!_changesetOpened) {
|
| 159 |
_changesetOpened = true;
|
| 160 |
var xml:XML = Xml.parse("<changeset></changeset>");
|
| 161 |
serializeTags(xml, tags);
|
| 162 |
openTransaction.requestData = "<osm>" + xml.toXMLString() + "</osm>";
|
| 163 |
}
|
| 164 |
}
|
| 165 |
|
| 166 |
public function createNode(node:Node):Void {
|
| 167 |
createdNodes.push(node);
|
| 168 |
}
|
| 169 |
|
| 170 |
public function modifyNode(node:Node):Void {
|
| 171 |
modifiedNodes.push(node);
|
| 172 |
}
|
| 173 |
|
| 174 |
public function deleteNode(node:Node):Void {
|
| 175 |
deletedNodes.push(node);
|
| 176 |
}
|
| 177 |
|
| 178 |
public function createWay(way:Way):Void {
|
| 179 |
createdWays.push(way);
|
| 180 |
}
|
| 181 |
|
| 182 |
public function modifyWay(way:Way):Void {
|
| 183 |
modifiedWays.push(way);
|
| 184 |
}
|
| 185 |
|
| 186 |
public function deleteWay(way:Way):Void {
|
| 187 |
deletedWays.push(way);
|
| 188 |
}
|
| 189 |
|
| 190 |
public function modifyRelation(relation:Relation):Void {
|
| 191 |
modifiedRelations.push(relation);
|
| 192 |
}
|
| 193 |
|
| 194 |
public function closeChangeset():Void {
|
| 195 |
if (_changesetOpened) {
|
| 196 |
lastHttpStatus = 0;
|
| 197 |
transactionQueue.send();
|
| 198 |
}
|
| 199 |
}
|
| 200 |
|
| 201 |
function serializeNodes(xml:XML, nodes:Array<Dynamic>):Void {
|
| 202 |
for (node in nodes) {
|
| 203 |
var nodeXml:XML = Xml.parse("<node></node>");
|
| 204 |
nodeXml.@id = node.id;
|
| 205 |
nodeXml.@version = node.version;
|
| 206 |
nodeXml.@lat = node.position.lat;
|
| 207 |
nodeXml.@lon = node.position.long;
|
| 208 |
nodeXml.@changeset = changesetId;
|
| 209 |
serializeTags(nodeXml, node.tags.source);
|
| 210 |
xml.appendChild(nodeXml);
|
| 211 |
}
|
| 212 |
}
|
| 213 |
|
| 214 |
function serializeWays(xml:XML, ways:Array<Dynamic>):Void {
|
| 215 |
for (way in ways) {
|
| 216 |
var wayXml:XML = Xml.parse("<way></way>");
|
| 217 |
wayXml.@id = way.id;
|
| 218 |
wayXml.@version = way.version;
|
| 219 |
wayXml.@changeset = changesetId;
|
| 220 |
var nodes:Array<Dynamic> = way.nodes.source;
|
| 221 |
for (node in nodes) {
|
| 222 |
wayXml.appendChild(Xml.parse("<nd ref={node.id}/>"));
|
| 223 |
}
|
| 224 |
serializeTags(wayXml, way.tags.source);
|
| 225 |
xml.appendChild(wayXml);
|
| 226 |
}
|
| 227 |
}
|
| 228 |
|
| 229 |
function serializeRelations(xml:XML, relations:Array<Dynamic>):Void {
|
| 230 |
for (relation in relations) {
|
| 231 |
var relationXml:XML = Xml.parse("<relation></relation>");
|
| 232 |
relationXml.@id = relation.id;
|
| 233 |
relationXml.@version = relation.version;
|
| 234 |
relationXml.@changeset = changesetId;
|
| 235 |
serializeMembers(relationXml, relation.members.source);
|
| 236 |
serializeTags(relationXml, relation.tags.source);
|
| 237 |
xml.appendChild(relationXml);
|
| 238 |
}
|
| 239 |
}
|
| 240 |
|
| 241 |
function serializeMembers(xml:XML, members:Array<Dynamic>):Void {
|
| 242 |
for (member in members) {
|
| 243 |
var memberXml:XML = Xml.parse("<member/>");
|
| 244 |
memberXml.@type = member.type;
|
| 245 |
memberXml.@ref = member.elementId;
|
| 246 |
memberXml.@role = member.role;
|
| 247 |
xml.appendChild(memberXml);
|
| 248 |
}
|
| 249 |
}
|
| 250 |
|
| 251 |
function serializeTags(xml:XML, tags:Array<Dynamic>):Void {
|
| 252 |
var tagsXml:XMLList = tagsSerializer.serialize(tags);
|
| 253 |
for (tagXml in tagsXml) {
|
| 254 |
xml.appendChild(tagXml);
|
| 255 |
}
|
| 256 |
}
|
| 257 |
|
| 258 |
function createTagsSerializer():IXMLListSerializer {
|
| 259 |
var tagSerializer:IXMLSerializer = new TagExternalizer();
|
| 260 |
var tagsSerializer:IXMLListSerializer = new ListExternalizer(tagSerializer, null);
|
| 261 |
return new ValidTagsSerializer(tagsSerializer);
|
| 262 |
}
|
| 263 |
|
| 264 |
function deserializeNodes(xml:XMLList):Array<Dynamic> {
|
| 265 |
var nodes:Array<Dynamic> = new Array();
|
| 266 |
var node:Node;
|
| 267 |
xml.(
|
| 268 |
node = new Node(@id, deserializeTags(elements("tag")), new LatLong(@lat, @lon)),
|
| 269 |
node.version = @version,
|
| 270 |
nodes.push(node)
|
| 271 |
);
|
| 272 |
return nodes;
|
| 273 |
}
|
| 274 |
|
| 275 |
function deserializeWays(xml:XMLList):Array<Dynamic> {
|
| 276 |
var ways:Array<Dynamic> = new Array();
|
| 277 |
var nodes:Array<Dynamic>;
|
| 278 |
var way:Way;
|
| 279 |
xml.(
|
| 280 |
nodes = new Array(),
|
| 281 |
nd.(nodes.push(nodeMap[@ref])),
|
| 282 |
way = new Way(@id, deserializeTags(elements("tag")), nodes),
|
| 283 |
way.version = @version,
|
| 284 |
ways.push(way)
|
| 285 |
);
|
| 286 |
return ways;
|
| 287 |
}
|
| 288 |
|
| 289 |
function dererializeRelations(xml:XMLList):Array<Dynamic> {
|
| 290 |
var relations:Array<Dynamic> = new Array();
|
| 291 |
var members:Array<Dynamic>;
|
| 292 |
var relation:Relation;
|
| 293 |
xml.(
|
| 294 |
members = deserializeRelationMembers(elements("member")),
|
| 295 |
relation = new Relation(@id, deserializeTags(elements("tag")), members),
|
| 296 |
relation.version = @version,
|
| 297 |
relations.push(relation)
|
| 298 |
);
|
| 299 |
return relations;
|
| 300 |
}
|
| 301 |
|
| 302 |
function deserializeTags(xml:XMLList):Array<Dynamic> {
|
| 303 |
var tags:Array<Dynamic> = new Array();
|
| 304 |
xml.(tags.push(new Tag(@k, @v)));
|
| 305 |
return tags;
|
| 306 |
}
|
| 307 |
|
| 308 |
function deserializeRelationMembers(xml:XMLList):Array<Dynamic> {
|
| 309 |
var members:Array<Dynamic> = new Array();
|
| 310 |
xml.(members.push(new Member(@ref, @type, @role)));
|
| 311 |
return members;
|
| 312 |
}
|
| 313 |
|
| 314 |
function deserializeDiffs(xml:XMLList):Array<Dynamic> {
|
| 315 |
var diffs:Array<Dynamic> = new Array();
|
| 316 |
// Parse create or modify, but not delete.
|
| 317 |
xml.(hasOwnProperty("@new_version")).
|
| 318 |
(diffs.push(new ElementDiff(@old_id, @new_id, @new_version)));
|
| 319 |
return diffs;
|
| 320 |
}
|
| 321 |
|
| 322 |
function setLoading(value:Bool):Void {
|
| 323 |
if (value == _loading) return;
|
| 324 |
_loading = value;
|
| 325 |
dispatchEvent(new Event("loadingChanged"));
|
| 326 |
/*if (value) {
|
| 327 |
CursorManager.setBusyCursor();
|
| 328 |
} else {
|
| 329 |
CursorManager.removeBusyCursor();
|
| 330 |
}*/
|
| 331 |
}
|
| 332 |
|
| 333 |
function endChangeset():Void {
|
| 334 |
createdWays = new Array();
|
| 335 |
modifiedWays = new Array();
|
| 336 |
deletedWays = new Array();
|
| 337 |
createdNodes = new Array();
|
| 338 |
modifiedNodes = new Array();
|
| 339 |
deletedNodes = new Array();
|
| 340 |
modifiedRelations = new Array();
|
| 341 |
_changesetOpened = false;
|
| 342 |
}
|
| 343 |
|
| 344 |
function mapLoader_completeHandler(event:Event):Void {
|
| 345 |
var xml:XML = new XML(mapLoader.data);
|
| 346 |
var nodes:Array<Dynamic> = deserializeNodes(xml.node);
|
| 347 |
nodeMap = new Object();
|
| 348 |
for (node in nodes) {
|
| 349 |
nodeMap[node.id] = node;
|
| 350 |
}
|
| 351 |
var ways:Array<Dynamic> = deserializeWays(xml.way);
|
| 352 |
var relations:Array<Dynamic> = dererializeRelations(xml.relation);
|
| 353 |
dispatchEvent(new OSMProxyResultEvent(OSMProxyResultEvent.RESULT, nodes, ways, relations));
|
| 354 |
setLoading(false);
|
| 355 |
}
|
| 356 |
|
| 357 |
function mapLoader_errorHandler(event:ErrorEvent):Void {
|
| 358 |
setLoading(false);
|
| 359 |
dispatchEvent(new OSMProxyLoadErrorEvent(OSMProxyLoadErrorEvent.LOAD_ERROR));
|
| 360 |
}
|
| 361 |
|
| 362 |
function openTransaction_completeHandler(event:Event):Void {
|
| 363 |
changesetId = String(openTransaction.data);
|
| 364 |
var xml:XML = new XML("<osmChange></osmChange>");
|
| 365 |
var createXml:XML = new XML("<create></create>");
|
| 366 |
serializeNodes(createXml, createdNodes);
|
| 367 |
serializeWays(createXml, createdWays);
|
| 368 |
xml.appendChild(createXml);
|
| 369 |
var modifyXml:XML = new XML("<modify></modify>");
|
| 370 |
serializeNodes(modifyXml, modifiedNodes);
|
| 371 |
serializeWays(modifyXml, modifiedWays);
|
| 372 |
serializeRelations(modifyXml, modifiedRelations);
|
| 373 |
xml.appendChild(modifyXml);
|
| 374 |
var deleteXml:XML = new XML("<delete></delete>");
|
| 375 |
serializeWays(deleteXml, deletedWays);
|
| 376 |
serializeNodes(deleteXml, deletedNodes);
|
| 377 |
xml.appendChild(deleteXml);
|
| 378 |
commitTransaction.requestData = xml.toXMLString();
|
| 379 |
commitTransaction.url = changesetId + "/upload";
|
| 380 |
closeTransaction.url = changesetId + "/close";
|
| 381 |
CONFIG::debug {
|
| 382 |
if (errorModeEnabled) {
|
| 383 |
commitTransaction.requestData = commitTransaction.requestData.substr(1);
|
| 384 |
}
|
| 385 |
}
|
| 386 |
}
|
| 387 |
|
| 388 |
function transactionQueue_completeHandler(event:Event):Void {
|
| 389 |
endChangeset();
|
| 390 |
var xml:XML = new XML(commitTransaction.data);
|
| 391 |
var nodeDiffs:Array<Dynamic> = deserializeDiffs(xml.node);
|
| 392 |
var wayDiffs:Array<Dynamic> = deserializeDiffs(xml.way);
|
| 393 |
var relationDiffs:Array<Dynamic> = deserializeDiffs(xml.relation);
|
| 394 |
dispatchEvent(new OSMProxySaveEvent(OSMProxySaveEvent.SAVE_COMPLETE, changesetId));
|
| 395 |
dispatchEvent(new OSMProxyResultEvent(OSMProxyResultEvent.SAVE, nodeDiffs, wayDiffs, relationDiffs));
|
| 396 |
}
|
| 397 |
|
| 398 |
function transactionQueue_ioErrorHandler(event:IOErrorEvent):Void {
|
| 399 |
endChangeset();
|
| 400 |
//Alert.show("httpStatus: " + lastHttpStatus);
|
| 401 |
dispatchEvent(new OSMProxySaveEvent(OSMProxySaveEvent.SAVE_FAILED));
|
| 402 |
}
|
| 403 |
|
| 404 |
function transactionQueue_httpStatusHandler(event:HTTPStatusEvent):Void {
|
| 405 |
lastHttpStatus = event.status;
|
| 406 |
}
|
| 407 |
|
| 408 |
function commitTransaction_ioErrorHandler(event:IOErrorEvent):Void { |
| 409 |
dispatchEvent(new OSMProxyCommitErrorEvent(OSMProxyCommitErrorEvent.COMMIT_ERROR, commitTransaction.requestData)); |
| 410 |
}
|
| 411 |
}
|