change relation.appendMember to use the undo system, and update various places as...
[potlatch2.git] / net / systemeD / halcyon / connection / OSMConnection.as
1 package net.systemeD.halcyon.connection {
2
3     import flash.events.*;
4         import flash.net.*;
5         import flash.utils.Dictionary;
6         import flash.system.Security;
7
8         import net.systemeD.halcyon.ExtendedURLLoader;
9         import net.systemeD.halcyon.Globals;
10
11     /**
12     * Read-only connection from local OSM-XML format (.osm) files.
13     * This is used by Halcyon; Potlatch 2 needs a full server connection
14     * of the type provided by XMLConnection
15     */
16
17         // For a limited set of arbitrary files, invoke it like this:
18         //              fo.addVariable("api","http://127.0.0.1/~richard/potlatch2");            // base URL
19         //              fo.addVariable("connection","OSM");
20         //              fo.addVariable("files","-1.5_-1.4_52.1_52.2.osm");
21
22         // For evenly arranged tiles, invoke it like this:
23         //              fo.addVariable("api","http://127.0.0.1/~richard/potlatch2");            // base URL
24         //              fo.addVariable("connection","OSM");
25         //              fo.addVariable("tile_resolution","0.2");
26         // and it'll then look for '-1.4_-1.2_52_52.2.osm', '-1.2_-1_52_52.2.osm', and so on
27         // (this needs some more testing)
28
29         public class OSMConnection extends XMLBaseConnection {
30
31                 private var filemode:uint;
32                 private static const NAMED:uint=0;
33                 private static const TILED:uint=1;
34                 // are we running from a limited set of files, or can we request tiles for any bbox?
35
36                 private var bboxes:Dictionary=new Dictionary();
37                 private static const AVAILABLE:uint=0;
38                 private static const LOADED:uint=1;
39                 private static const LOADING:uint=2;
40                 private static const UNAVAILABLE:uint=3;
41                 // a hash of known files [left,right,top,bottom], and their current status
42
43                 private var tileResolution:Number;
44                 // degree resolution for tiles (e.g. 0.2)
45
46                 private static const FILENAME:RegExp=/([\-\d\.]+)_([\-\d\.]+)_([\-\d\.]+)_([\-\d\.]+)\./i;
47
48                 public function OSMConnection() {
49
50                         if (Connection.policyURL!='')
51                 Security.loadPolicyFile(Connection.policyURL);
52
53             tileResolution = Number(Connection.getParam("tile_resolution", "0.2"));
54
55                         var o:Object = new Object();
56                         var files:String = Connection.getParam("files","");
57                         if (files=="") {
58                                 filemode=TILED;
59                         } else {
60                                 filemode=NAMED;
61                                 for each (var file:String in files.split(/,/)) {
62                                         if ((o=FILENAME.exec(file))) {
63                                                 bboxes[[o[1],o[2],o[3],o[4]]]=AVAILABLE;
64                                         }
65                                 }
66                         }
67                 }
68                 
69                 override public function loadBbox(left:Number,right:Number,
70                                                                 top:Number,bottom:Number):void {
71                         var l:Number, r:Number, t:Number, b:Number, x:Number, y:Number, k:Array;
72
73                         // look through bboxes, assemble any within the requested bbox that are AVAILABLE
74                         for (var box:* in bboxes) {
75                                 k=box as Array;
76                                 l=k[0]; r=k[1]; t=k[2]; b=k[3];
77                                 if ( ( (left>=l && left<=r) || (right>=l && right<=r) || (left<l && right>r) ) &&
78                                          ( (top>=b && top<=t) || (bottom>=b && bottom<=t) || (bottom<b && top>t) ) ) {
79                                         // yay, it intersects
80                                         if (bboxes[box]==AVAILABLE) { loadFile(box); }
81                                 }
82                         }
83                         if (filemode==NAMED) { return; }
84                         
85                         // look through tiles for any areas that are not covered
86                         for (x=roundDown(left, tileResolution); x<=roundUp(right, tileResolution); x+=tileResolution) {
87                                 for (y=roundDown(bottom, tileResolution); y<=roundUp(top, tileResolution); y+=tileResolution) {
88                                         k=[x,x+tileResolution,y,y+tileResolution];
89                                         if (bboxes[k]) { 
90                                                 if (bboxes[k]==AVAILABLE) { loadFile(k); }
91                                         } else {
92                                                 loadFile(k);
93                                         }
94                                 }
95                         }
96                 }
97                 
98                 private function loadFile(box:Array):void {
99                         Globals.vars.root.addDebug("called loadFile for "+box);
100                         bboxes[box]=LOADING;
101
102             var mapRequest:URLRequest = new URLRequest(Connection.apiBaseURL+"/"+box[0]+"_"+box[1]+"_"+box[2]+"_"+box[3]+".osm");
103                         var mapLoader:ExtendedURLLoader = new ExtendedURLLoader();
104                         mapLoader.info['bbox']=box;
105                         mapLoader.addEventListener(Event.COMPLETE, markMapLoaded);
106                         mapLoader.addEventListener(IOErrorEvent.IO_ERROR, markMapUnloadable);
107                         mapLoader.load(mapRequest);
108                         dispatchEvent(new Event(LOAD_STARTED));
109                 }
110                 
111                 private function markMapLoaded(e:Event):void {
112                         bboxes[e.target.info['bbox']]=LOADED;
113                         loadedMap(e);
114                 }
115                 
116                 private function markMapUnloadable(e:Event):void {
117                         bboxes[e.target.info['bbox']]=UNAVAILABLE;
118                 }
119
120                 override public function purgeOutside(left:Number, right:Number, top:Number, bottom:Number):void {
121                         // we don't purge in an OSMConnection
122                 }
123
124                 private function roundUp(a:Number,i:Number):Number {
125                         if (a/i==Math.floor(a/i)) { return a/i; }
126                         return Math.floor(a/i+1)*i;
127                 }
128                 private function roundDown(a:Number,i:Number):Number {
129                         return Math.floor(a/i)*i;
130                 }
131
132         }
133 }