Add floating imagery window
[potlatch2.git] / net / systemeD / halcyon / TileSet.as
index 8f81d7d30d549515615fa3b237a564dfc04088d1..db541f5170c834eb62f0d317019370b3cf2995e8 100644 (file)
@@ -6,6 +6,14 @@ package net.systemeD.halcyon {
        import flash.net.*;
        import flash.system.LoaderContext;
        import flash.utils.Timer;
+       import flash.text.TextField;
+       import flash.text.TextFormat;
+
+       import net.systemeD.potlatch2.collections.*;
+       /* -------
+          This currently requires potlatch2.collections.Imagery and
+                                  potlatch2.collections.CollectionEvent which break Halcyon.
+          ------- */
 
     public class TileSet extends Sprite {
 
@@ -26,7 +34,8 @@ package net.systemeD.halcyon {
                private var count:Number=0;                     // counter incremented to provide a/b/c/d tile swapping
                private static const ROUNDROBIN:RegExp =/\{switch\:([^}]+)\}/;
 
-               private var map:Map;
+               private var _map:Map;
+               private var _overlay:Sprite;
                private const MAXTILESLOADED:uint=30;
 
                private var sharpenFilter:BitmapFilter = new ConvolutionFilter(3, 3, 
@@ -36,10 +45,13 @@ package net.systemeD.halcyon {
                private var sharpening:Boolean = false;
                // http://flylib.com/books/en/2.701.1.170/1/
 
-        public function TileSet(map:Map) {
-                       this.map=map;
+        public function TileSet(map:Map, overlay:Sprite) {
+                       _map=map;
+                       _overlay=overlay;
                        createSprites();
-                       map.addEventListener(MapEvent.NUDGE_BACKGROUND, nudgeHandler);
+                       _map.addEventListener(MapEvent.NUDGE_BACKGROUND, nudgeHandler);
+                       _map.addEventListener(MapEvent.MOVE_END, moveHandler);
+                       _map.addEventListener(MapEvent.RESIZE, resizeHandler);
                }
        
                /** @param params Currently includes "url" and "scheme"
@@ -63,7 +75,7 @@ package net.systemeD.halcyon {
                }
 
                private function createSprites():void {
-                       for (var i:uint=map.MINSCALE; i<=map.MAXSCALE; i++) {
+                       for (var i:uint=_map.MINSCALE; i<=_map.MAXSCALE; i++) {
                                this.addChild(new Sprite());
                        }
                }
@@ -96,34 +108,34 @@ package net.systemeD.halcyon {
 
                /** Set zoom scale (no update triggerd). */
                public function changeScale(scale:uint):void {
-                       for (var i:uint=map.MINSCALE; i<=map.MAXSCALE; i++) {
-                               this.getChildAt(i-map.MINSCALE).visible=(scale==i);
+                       for (var i:uint=_map.MINSCALE; i<=_map.MAXSCALE; i++) {
+                               this.getChildAt(i-_map.MINSCALE).visible=(scale==i);
                        }
-                       x=map.lon2coord(map.centre_lon+offset_lon)-map.lon2coord(map.centre_lon);
-                       y=map.lat2coord(map.centre_lat+offset_lat)-map.lat2coord(map.centre_lat);
+                       x=_map.lon2coord(_map.centre_lon+offset_lon)-_map.lon2coord(_map.centre_lon);
+                       y=_map.lat2coord(_map.centre_lat+offset_lat)-_map.lat2coord(_map.centre_lat);
                }
                        
                /** Update bounds of tile area, and request new tiles if needed.  */
                
                public function update():void {
                        if (!baseurl) { return; }
-                       tile_l=lon2tile(map.edge_l-offset_lon);
-                       tile_r=lon2tile(map.edge_r-offset_lon);
-                       tile_t=lat2tile(map.edge_t-offset_lat);
-                       tile_b=lat2tile(map.edge_b-offset_lat);
+                       tile_l=lon2tile(_map.edge_l-offset_lon);
+                       tile_r=lon2tile(_map.edge_r-offset_lon);
+                       tile_t=lat2tile(_map.edge_t-offset_lat);
+                       tile_b=lat2tile(_map.edge_b-offset_lat);
                        for (var tx:int=tile_l; tx<=tile_r; tx++) {
                                for (var ty:int=tile_t; ty<=tile_b; ty++) {
-                                       if (!tiles[map.scale+','+tx+','+ty]) { 
+                                       if (!tiles[_map.scale+','+tx+','+ty]) { 
                                                var loader:Loader = new Loader();
-                                               tiles[map.scale+','+tx+','+ty]=loader;
+                                               tiles[_map.scale+','+tx+','+ty]=loader;
                                                loader.contentLoaderInfo.addEventListener(Event.INIT, doImgInit, false, 0, true);
                                                loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, missingTileError, false, 0, true);
-                                               loader.contentLoaderInfo.addEventListener(HTTPStatusEvent.HTTP_STATUS, function(e:HTTPStatusEvent):void { tileLoadStatus(e,map.scale,tx,ty); }, false, 0, true);
-                                               loader.load(new URLRequest(tileURL(tx,ty,map.scale)), 
+                                               loader.contentLoaderInfo.addEventListener(HTTPStatusEvent.HTTP_STATUS, function(e:HTTPStatusEvent):void { tileLoadStatus(e,_map.scale,tx,ty); }, false, 0, true);
+                                               loader.load(new URLRequest(tileURL(tx,ty,_map.scale)), 
                                                            new LoaderContext(true));
-                                               Sprite(this.getChildAt(map.scale-map.MINSCALE)).addChild(loader);
-                                               loader.x=map.lon2coord(tile2lon(tx));
-                                               loader.y=map.lat2coord(tile2lat(ty));
+                                               Sprite(this.getChildAt(_map.scale-_map.MINSCALE)).addChild(loader);
+                                               loader.x=_map.lon2coord(tile2lon(tx));
+                                               loader.y=_map.lat2coord(tile2lat(ty));
                                                if (sharpening) { loader.filters=[sharpenFilter]; }
                                        }
                                }
@@ -151,7 +163,7 @@ package net.systemeD.halcyon {
                        for (var tile:String in tiles) {
                                if (tiles[tile] is Sprite) {
                                        var coords:Array=tile.split(','); var tz:uint=coords[0]; var tx:uint=coords[1]; var ty:uint=coords[1];
-                                       if (tz!=map.scale || tx<tile_l || tx>tile_r || ty<tile_t || ty<tile_b) {
+                                       if (tz!=_map.scale || tx<tile_l || tx>tile_r || ty<tile_t || ty<tile_b) {
                                                if (tiles[tile].parent) tiles[tile].parent.removeChild(tiles[tile]);
                                                delete tiles[tile];
                                                loadcount--;
@@ -181,11 +193,11 @@ package net.systemeD.halcyon {
 
                                default:
                                        if (baseurl.indexOf('{x}')>-1) {
-                                               t=baseurl.replace('{zoom}',map.scale).replace('{x}',tx).replace('{y}',ty).replace('{-y}',tmsy);
+                                               t=baseurl.replace('{zoom}',_map.scale).replace('{x}',tx).replace('{y}',ty).replace('{-y}',tmsy);
                                        } else if (baseurl.indexOf('$x')>-1) {
-                                               t=baseurl.replace('$z',map.scale).replace('$x',tx).replace('$y',ty).replace('$-y',tmsy);
+                                               t=baseurl.replace('$z',_map.scale).replace('$x',tx).replace('$y',ty).replace('$-y',tmsy);
                                        } else {
-                                               t=baseurl.replace('!',map.scale).replace('!',tx).replace('!',ty);
+                                               t=baseurl.replace('!',_map.scale).replace('!',tx).replace('!',ty);
                                        }
                                        // also, someone should invent yet another variable substitution scheme
                                        break;
@@ -211,8 +223,8 @@ package net.systemeD.halcyon {
                public function nudgeHandler(event:MapEvent):void {
                        if (!baseurl) { return; }
                        this.x+=event.params.x; this.y+=event.params.y;
-                       offset_lat=map.centre_lat-map.coord2lat(map.lat2coord(map.centre_lat)-this.y);
-                       offset_lon=map.centre_lon-map.coord2lon(map.lon2coord(map.centre_lon)-this.x);
+                       offset_lat=_map.centre_lat-_map.coord2lat(_map.lat2coord(_map.centre_lat)-this.y);
+                       offset_lon=_map.centre_lon-_map.coord2lon(_map.lon2coord(_map.centre_lon)-this.x);
                        update();
                }
 
@@ -221,18 +233,143 @@ package net.systemeD.halcyon {
                // Co-ordinate conversion functions
 
                private function lon2tile(lon:Number):int {
-                       return (Math.floor((lon+180)/360*Math.pow(2,map.scale)));
+                       return (Math.floor((lon+180)/360*Math.pow(2,_map.scale)));
                }
                private function lat2tile(lat:Number):int { 
-                       return (Math.floor((1-Math.log(Math.tan(lat*Math.PI/180) + 1/Math.cos(lat*Math.PI/180))/Math.PI)/2 *Math.pow(2,map.scale)));
+                       return (Math.floor((1-Math.log(Math.tan(lat*Math.PI/180) + 1/Math.cos(lat*Math.PI/180))/Math.PI)/2 *Math.pow(2,_map.scale)));
                }
                private function tile2lon(t:int):Number {
-                       return (t/Math.pow(2,map.scale)*360-180);
+                       return (t/Math.pow(2,_map.scale)*360-180);
                }
                private function tile2lat(t:int):Number { 
-                       var n:Number=Math.PI-2*Math.PI*t/Math.pow(2,map.scale);
+                       var n:Number=Math.PI-2*Math.PI*t/Math.pow(2,_map.scale);
                        return (180/Math.PI*Math.atan(0.5*(Math.exp(n)-Math.exp(-n))));
                }
 
+               // ------------------------------------------------------------------
+               // Attribution/terms management
+               // (moved from Imagery.as)
+
+               private var _selected:Object={};
+               public function get selected():Object { return _selected; }
+
+               public function setAttribution():void {
+                       var tf:TextField=TextField(_overlay.getChildAt(0));
+                       tf.text='';
+                       if (!_selected.attribution) return;
+                       var attr:Array=[];
+                       if (_selected.attribution.providers) {
+                               // Bing attribution scheme
+                               for (var provider:String in _selected.attribution.providers) {
+                                       for each (var bounds:Array in _selected.attribution.providers[provider]) {
+                                               if (_map.scale>=bounds[0] && _map.scale<=bounds[1] &&
+                                                 ((_map.edge_l>bounds[3] && _map.edge_l<bounds[5]) ||
+                                                  (_map.edge_r>bounds[3] && _map.edge_r<bounds[5]) ||
+                                          (_map.edge_l<bounds[3] && _map.edge_r>bounds[5])) &&
+                                                 ((_map.edge_b>bounds[2] && _map.edge_b<bounds[4]) ||
+                                                  (_map.edge_t>bounds[2] && _map.edge_t<bounds[4]) ||
+                                                  (_map.edge_b<bounds[2] && _map.edge_t>bounds[4]))) {
+                                                       attr.push(provider);
+                                               }
+                                       }
+                               }
+                       }
+                       if (attr.length==0) return;
+                       tf.text="Background "+attr.join(", ");
+                       positionAttribution();
+                       dispatchEvent(new MapEvent(MapEvent.BUMP, { y: tf.textHeight }));       // don't let the toolbox obscure it
+               }
+               public function positionAttribution():void {
+                       if (!_selected.attribution) return;
+                       var tf:TextField=TextField(_overlay.getChildAt(0));
+                       tf.x=_map.mapwidth  - 5 - tf.textWidth;
+                       tf.y=_map.mapheight - 5 - tf.textHeight;
+               }
+
+               public function setLogo():void {
+                       while (_overlay.numChildren>2) { _overlay.removeChildAt(2); }
+                       if (!_selected.logoData) return;
+                       var logo:Sprite=new Sprite();
+                       logo.addChild(new Bitmap(_selected.logoData));
+                       if (_selected.attribution.url) { logo.buttonMode=true; logo.addEventListener(MouseEvent.CLICK, launchLogoLink, false, 0, true); }
+                       _overlay.addChild(logo);
+                       positionLogo();
+               }
+               public function positionLogo():void {
+                       if (_overlay.numChildren<3) return;
+                       _overlay.getChildAt(2).x=5;
+                       _overlay.getChildAt(2).y=_map.mapheight - 5 - _selected.logoHeight - (_selected.terms_url ? 10 : 0);
+               }
+               private function launchLogoLink(e:Event):void {
+                       if (!_selected.attribution.url) return;
+                       navigateToURL(new URLRequest(_selected.attribution.url), '_blank');
+               }
+               public function setTerms():void {
+                       var terms:TextField=TextField(_overlay.getChildAt(1));
+                       if (!_selected.attribution) { terms.text=''; return; }
+                       if (_selected.attribution && _selected.attribution.text) { terms.text=_selected.attribution.text; }
+                       else { terms.text="Background terms of use"; }
+                       positionTerms();
+                       terms.addEventListener(MouseEvent.CLICK, launchTermsLink, false, 0, true);
+               }
+               private function positionTerms():void {
+                       _overlay.getChildAt(1).x=5;
+                       _overlay.getChildAt(1).y=_map.mapheight - 15;
+               }
+               private function launchTermsLink(e:Event):void {
+                       if (!_selected.attribution.url) return;
+                       navigateToURL(new URLRequest(_selected.attribution.url), '_blank');
+               }
+
+               public function resizeHandler(event:MapEvent):void {
+                       positionLogo();
+                       positionTerms();
+                       positionAttribution();
+               }
+               private function moveHandler(event:MapEvent):void {
+                       setAttribution();
+                       // strictly speaking we should review the collection on every move, but slow
+                       // dispatchEvent(new Event("collection_changed"));
+               }
+
+               // Create overlay sprite
+               public static function overlaySprite():Sprite {
+                       var overlay:Sprite=new Sprite();
+                       var attribution:TextField=new TextField();
+                       attribution.width=220; attribution.height=300;
+                       attribution.multiline=true;
+                       attribution.wordWrap=true;
+                       attribution.selectable=false;
+                       attribution.defaultTextFormat=new TextFormat("_sans", 9, 0, false, false, false);
+                       overlay.addChild(attribution);
+                       var terms:TextField=new TextField();
+                       terms.width=200; terms.height=15;
+                       terms.selectable=false;
+                       terms.defaultTextFormat=new TextFormat("_sans", 9, 0, false, false, true);
+                       overlay.addChild(terms);
+                       return overlay;
+               }
+
+               // ------------------------------------------------------------------
+               // Choose a new background
+               // (moved from setBackground in Imagery.as)
+               
+               public function setBackgroundFromImagery(bg:Object,remember:Boolean):void {
+                       // set background
+                       _selected=bg;
+//                     dispatchEvent(new CollectionEvent(CollectionEvent.SELECT, bg));
+                       _map.tileset.init(bg, bg!='');
+                       // update attribution and logo
+                       _overlay.visible=bg.hasOwnProperty('attribution');
+                       setLogo(); setAttribution(); setTerms();
+                       // save as SharedObject for next time
+                       if (remember) {
+                               var obj:SharedObject = SharedObject.getLocal("user_state","/");
+                               obj.setProperty('background_url' ,String(bg.url));
+                               obj.setProperty('background_name',String(bg.name));
+                               try { obj.flush(); } catch (e:Error) {}
+                       }
+               }
+
        }
 }