give Halcyon the ability to load .osm files
authorRichard Fairhurst <richard@systemed.net>
Sun, 6 Jun 2010 22:01:06 +0000 (22:01 +0000)
committerRichard Fairhurst <richard@systemed.net>
Sun, 6 Jun 2010 22:01:06 +0000 (22:01 +0000)
TODO.txt
net/systemeD/halcyon/connection/Connection.as
net/systemeD/halcyon/connection/OSMConnection.as [new file with mode: 0644]
net/systemeD/halcyon/connection/XMLBaseConnection.as [new file with mode: 0644]
net/systemeD/halcyon/connection/XMLConnection.as

index 90b99a30c1b8f552fba285ff950685bd29be0467..4c0c8601490cacbb15adc26900c9b8d1c7782cf8 100644 (file)
--- a/TODO.txt
+++ b/TODO.txt
@@ -57,6 +57,7 @@ Potlatch 2: main outstanding issues
 * Background imagery layers should match those of p1
 * Dialog for adding custom imagery url
 * Draggin a node under the toolbox and then mouseuping doesn't get passed to the map, so you end up with a node "stuck" to the pointer
+* onDataComplete fires the first time a map call has returned - but multiple calls might have been made, so really we should count them and only reset dataWorking when it's back to 0 again
 
 == Miscellaneous data model ==
 
index f9906226f8b256f43b245cd8f4ee5b66ad138407..c4b5818dcd8b1980a509573745b082263f9f398a 100755 (executable)
@@ -25,6 +25,8 @@ package net.systemeD.halcyon.connection {
                 
                 if ( connectType == "XML" )
                     connectionInstance = new XMLConnection();
+                else if ( connectType == "OSM" )
+                    connectionInstance = new OSMConnection();
                 else
                     connectionInstance = new AMFConnection();
             }
diff --git a/net/systemeD/halcyon/connection/OSMConnection.as b/net/systemeD/halcyon/connection/OSMConnection.as
new file mode 100644 (file)
index 0000000..252c4bd
--- /dev/null
@@ -0,0 +1,129 @@
+package net.systemeD.halcyon.connection {
+
+    import flash.events.*;
+       import flash.net.*;
+       import flash.utils.Dictionary;
+       import flash.system.Security;
+
+       import net.systemeD.halcyon.ExtendedURLLoader;
+       import net.systemeD.halcyon.Globals;
+
+       // Read-only connection from local files (for Halcyon)
+
+       // For a limited set of arbitrary files, invoke it like this:
+       //              fo.addVariable("api","http://127.0.0.1/~richard/potlatch2");            // base URL
+       //              fo.addVariable("connection","OSM");
+       //              fo.addVariable("files","-1.5_-1.4_52.1_52.2.osm");
+
+       // For evenly arranged tiles, invoke it like this:
+       //              fo.addVariable("api","http://127.0.0.1/~richard/potlatch2");            // base URL
+       //              fo.addVariable("connection","OSM");
+       //              fo.addVariable("tile_resolution","0.2");
+       // and it'll then look for '-1.4_-1.2_52_52.2.osm', '-1.2_-1_52_52.2.osm', and so on
+       // (this needs some more testing)
+
+       public class OSMConnection extends XMLBaseConnection {
+
+               private var filemode:uint;
+               private static const NAMED:uint=0;
+               private static const TILED:uint=1;
+               // are we running from a limited set of files, or can we request tiles for any bbox?
+
+               private var bboxes:Dictionary=new Dictionary();
+               private static const AVAILABLE:uint=0;
+               private static const LOADED:uint=1;
+               private static const LOADING:uint=2;
+               private static const UNAVAILABLE:uint=3;
+               // a hash of known files [left,right,top,bottom], and their current status
+
+               private var tileResolution:Number;
+               // degree resolution for tiles (e.g. 0.2)
+
+               private static const FILENAME:RegExp=/([\-\d\.]+)_([\-\d\.]+)_([\-\d\.]+)_([\-\d\.]+)\./i;
+
+               public function OSMConnection() {
+
+                       if (Connection.policyURL!='')
+                Security.loadPolicyFile(Connection.policyURL);
+
+            tileResolution = Number(Connection.getParam("tile_resolution", "0.2"));
+
+                       var o:Object = new Object();
+                       var files:String = Connection.getParam("files","");
+                       if (files=="") {
+                               filemode=TILED;
+                       } else {
+                               filemode=NAMED;
+                               for each (var file:String in files.split(/,/)) {
+                                       if ((o=FILENAME.exec(file))) {
+                                               bboxes[[o[1],o[2],o[3],o[4]]]=AVAILABLE;
+                                       }
+                               }
+                       }
+               }
+               
+               override public function loadBbox(left:Number,right:Number,
+                                                               top:Number,bottom:Number):void {
+                       var l:Number, r:Number, t:Number, b:Number, x:Number, y:Number, k:Array;
+
+                       // look through bboxes, assemble any within the requested bbox that are AVAILABLE
+                       for (var box:* in bboxes) {
+                               k=box as Array;
+                               l=k[0]; r=k[1]; t=k[2]; b=k[3];
+                               if ( ( (left>=l && left<=r) || (right>=l && right<=r) || (left<l && right>r) ) &&
+                                        ( (top>=b && top<=t) || (bottom>=b && bottom<=t) || (bottom<b && top>t) ) ) {
+                                       // yay, it intersects
+                                       if (bboxes[box]==AVAILABLE) { loadFile(box); }
+                               }
+                       }
+                       if (filemode==NAMED) { return; }
+                       
+                       // look through tiles for any areas that are not covered
+                       for (x=roundDown(left, tileResolution); x<=roundUp(right, tileResolution); x+=tileResolution) {
+                               for (y=roundDown(bottom, tileResolution); y<=roundUp(top, tileResolution); y+=tileResolution) {
+                                       k=[x,x+tileResolution,y,y+tileResolution];
+                                       if (bboxes[k]) { 
+                                               if (bboxes[k]==AVAILABLE) { loadFile(k); }
+                                       } else {
+                                               loadFile(k);
+                                       }
+                               }
+                       }
+               }
+               
+               private function loadFile(box:Array):void {
+                       Globals.vars.root.addDebug("called loadFile for "+box);
+                       bboxes[box]=LOADING;
+
+            var mapRequest:URLRequest = new URLRequest(Connection.apiBaseURL+"/"+box[0]+"_"+box[1]+"_"+box[2]+"_"+box[3]+".osm");
+                       var mapLoader:ExtendedURLLoader = new ExtendedURLLoader();
+                       mapLoader.info['bbox']=box;
+                       mapLoader.addEventListener(Event.COMPLETE, markMapLoaded);
+                       mapLoader.addEventListener(IOErrorEvent.IO_ERROR, markMapUnloadable);
+                       mapLoader.load(mapRequest);
+                       dispatchEvent(new Event(LOAD_STARTED));
+               }
+               
+               private function markMapLoaded(e:Event):void {
+                       bboxes[e.target.info['bbox']]=LOADED;
+                       loadedMap(e);
+               }
+               
+               private function markMapUnloadable(e:Event):void {
+                       bboxes[e.target.info['bbox']]=UNAVAILABLE;
+               }
+
+               override public function purgeOutside(left:Number, right:Number, top:Number, bottom:Number):void {
+                       // we don't purge in an OSMConnection
+               }
+
+               private function roundUp(a:Number,i:Number):Number {
+                       if (a/i==Math.floor(a/i)) { return a/i; }
+                       return Math.floor(a/i+1)*i;
+               }
+               private function roundDown(a:Number,i:Number):Number {
+                       return Math.floor(a/i)*i;
+               }
+
+       }
+}
diff --git a/net/systemeD/halcyon/connection/XMLBaseConnection.as b/net/systemeD/halcyon/connection/XMLBaseConnection.as
new file mode 100644 (file)
index 0000000..9c68b0e
--- /dev/null
@@ -0,0 +1,126 @@
+package net.systemeD.halcyon.connection {
+
+    import flash.events.*;
+
+       import flash.system.Security;
+       import flash.net.*;
+    import org.iotashan.oauth.*;
+
+       import net.systemeD.halcyon.Globals;
+
+       public class XMLBaseConnection extends Connection {
+
+               public function XMLBaseConnection() {
+               }
+               
+        protected function loadedMap(event:Event):void {
+            dispatchEvent(new Event(LOAD_COMPLETED));
+
+            var map:XML = new XML(URLLoader(event.target).data);
+            var id:Number;
+            var version:uint;
+            var tags:Object;
+
+            for each(var nodeData:XML in map.node) {
+                id = Number(nodeData.@id);
+                version = uint(nodeData.@version);
+
+                var node:Node = getNode(id);
+                if ( node == null || !node.loaded ) {
+                    var lat:Number = Number(nodeData.@lat);
+                    var lon:Number = Number(nodeData.@lon);
+                    tags = parseTags(nodeData.tag);
+                    if ( node == null )
+                        setNode(new Node(id, version, tags, true, lat, lon),false);
+                    else {
+                        node.update(version, tags, true, lat, lon);
+                        sendEvent(new EntityEvent(NEW_NODE, node), false);
+                    }
+                }
+            }
+
+            for each(var data:XML in map.way) {
+                id = Number(data.@id);
+                version = uint(data.@version);
+
+                var way:Way = getWay(id);
+                if ( way == null || !way.loaded ) {
+                    var nodes:Array = [];
+                    for each(var nd:XML in data.nd)
+                        nodes.push(getNode(Number(nd.@ref)));
+                    tags = parseTags(data.tag);
+                    if ( way == null )
+                        setWay(new Way(id, version, tags, true, nodes),false);
+                    else {
+                        way.update(version, tags, true, nodes);
+                        sendEvent(new EntityEvent(NEW_WAY, way), false);
+                    }
+                }
+            }
+            
+            for each(var relData:XML in map.relation) {
+                id = Number(relData.@id);
+                version = uint(relData.@version);
+                
+                var rel:Relation = getRelation(id);
+                if ( rel == null || !rel.loaded ) {
+                    tags = parseTags(relData.tag);
+                    var members:Array = [];
+                    for each(var memberXML:XML in relData.member) {
+                        var type:String = memberXML.@type.toLowerCase();
+                        var role:String = memberXML.@role;
+                        var memberID:Number = Number(memberXML.@ref);
+                        var member:Entity = null;
+                        if ( type == "node" ) {
+                            member = getNode(memberID);
+                            if ( member == null ) {
+                                member = new Node(memberID,0,{},false,0,0);
+                                setNode(Node(member),true);
+                            }
+                        } else if ( type == "way" ) {
+                            member = getWay(memberID);
+                            if (member == null) {
+                                member = new Way(memberID,0,{},false,[]);
+                                setWay(Way(member),true);
+                            }
+                        } else if ( type == "relation" ) {
+                            member = getRelation(memberID);
+                            if (member == null) {
+                                member = new Relation(memberID,0,{},false,[]);
+                                setRelation(Relation(member),true);
+                            }
+                        }
+                        
+                        if ( member != null )
+                            members.push(new RelationMember(member, role));
+                    }
+                    
+                    if ( rel == null )
+                        setRelation(new Relation(id, version, tags, true, members), false);
+                    else {
+                        rel.update(version,tags,true,members);
+                        sendEvent(new EntityEvent(NEW_RELATION, rel), false);
+                    }
+                }
+            }
+            
+            registerPOINodes();
+        }
+        
+        protected function registerPOINodes():void {
+            for each (var nodeID:Number in getAllNodeIDs()) {
+                var node:Node = getNode(nodeID);
+                if (!node.hasParentWays)
+                    registerPOI(node);
+            }
+        }
+
+        private function parseTags(tagElements:XMLList):Object {
+            var tags:Object = {};
+            for each (var tagEl:XML in tagElements)
+                tags[tagEl.@k] = tagEl.@v;
+            return tags;
+        }
+
+       }
+}
index 224d3e958314d94b55fe7ee2ddd76e4729485311..d2291575b928500fb289b2810f79041312ff22ab 100644 (file)
@@ -8,7 +8,7 @@ package net.systemeD.halcyon.connection {
 
        import net.systemeD.halcyon.Globals;
 
-       public class XMLConnection extends Connection {
+       public class XMLConnection extends XMLBaseConnection {
 
         //public var readConnection:NetConnection;
 
@@ -39,121 +39,12 @@ package net.systemeD.halcyon.connection {
             dispatchEvent(new Event(LOAD_STARTED));
                }
 
-        private function parseTags(tagElements:XMLList):Object {
-            var tags:Object = {};
-            for each (var tagEl:XML in tagElements)
-                tags[tagEl.@k] = tagEl.@v;
-            return tags;
-        }
-
         private function errorOnMapLoad(event:Event):void {
             trace("error loading map");
         }
         private function mapLoadStatus(event:HTTPStatusEvent):void {
             trace("loading map status = "+event.status);
         }
-        
-        private function loadedMap(event:Event):void {
-            dispatchEvent(new Event(LOAD_COMPLETED));
-
-            var map:XML = new XML(URLLoader(event.target).data);
-            var id:Number;
-            var version:uint;
-            var tags:Object;
-
-            for each(var nodeData:XML in map.node) {
-                id = Number(nodeData.@id);
-                version = uint(nodeData.@version);
-
-                var node:Node = getNode(id);
-                if ( node == null || !node.loaded ) {
-                    var lat:Number = Number(nodeData.@lat);
-                    var lon:Number = Number(nodeData.@lon);
-                    tags = parseTags(nodeData.tag);
-                    if ( node == null )
-                        setNode(new Node(id, version, tags, true, lat, lon),false);
-                    else {
-                        node.update(version, tags, true, lat, lon);
-                        sendEvent(new EntityEvent(NEW_NODE, node), false);
-                    }
-                }
-            }
-
-            for each(var data:XML in map.way) {
-                id = Number(data.@id);
-                version = uint(data.@version);
-
-                var way:Way = getWay(id);
-                if ( way == null || !way.loaded ) {
-                    var nodes:Array = [];
-                    for each(var nd:XML in data.nd)
-                        nodes.push(getNode(Number(nd.@ref)));
-                    tags = parseTags(data.tag);
-                    if ( way == null )
-                        setWay(new Way(id, version, tags, true, nodes),false);
-                    else {
-                        way.update(version, tags, true, nodes);
-                        sendEvent(new EntityEvent(NEW_WAY, way), false);
-                    }
-                }
-            }
-            
-            for each(var relData:XML in map.relation) {
-                id = Number(relData.@id);
-                version = uint(relData.@version);
-                
-                var rel:Relation = getRelation(id);
-                if ( rel == null || !rel.loaded ) {
-                    tags = parseTags(relData.tag);
-                    var members:Array = [];
-                    for each(var memberXML:XML in relData.member) {
-                        var type:String = memberXML.@type.toLowerCase();
-                        var role:String = memberXML.@role;
-                        var memberID:Number = Number(memberXML.@ref);
-                        var member:Entity = null;
-                        if ( type == "node" ) {
-                            member = getNode(memberID);
-                            if ( member == null ) {
-                                member = new Node(memberID,0,{},false,0,0);
-                                setNode(Node(member),true);
-                            }
-                        } else if ( type == "way" ) {
-                            member = getWay(memberID);
-                            if (member == null) {
-                                member = new Way(memberID,0,{},false,[]);
-                                setWay(Way(member),true);
-                            }
-                        } else if ( type == "relation" ) {
-                            member = getRelation(memberID);
-                            if (member == null) {
-                                member = new Relation(memberID,0,{},false,[]);
-                                setRelation(Relation(member),true);
-                            }
-                        }
-                        
-                        if ( member != null )
-                            members.push(new RelationMember(member, role));
-                    }
-                    
-                    if ( rel == null )
-                        setRelation(new Relation(id, version, tags, true, members), false);
-                    else {
-                        rel.update(version,tags,true,members);
-                        sendEvent(new EntityEvent(NEW_RELATION, rel), false);
-                    }
-                }
-            }
-            
-            registerPOINodes();
-        }
-        
-        protected function registerPOINodes():void {
-            for each (var nodeID:Number in getAllNodeIDs()) {
-                var node:Node = getNode(nodeID);
-                if (!node.hasParentWays)
-                    registerPOI(node);
-            }
-        }
 
         protected var appID:OAuthConsumer;
         protected var authToken:OAuthToken;