1 package net.systemeD.potlatch2.collections {
4 import flash.display.*;
6 import flash.text.TextField;
7 import net.systemeD.halcyon.DebugURLRequest;
8 import net.systemeD.halcyon.Map;
9 import net.systemeD.halcyon.MapEvent;
10 import net.systemeD.potlatch2.FunctionKeyManager;
11 import net.systemeD.potlatch2.Yahoo;
12 import mx.collections.ArrayCollection;
15 There's lots of further tidying we can do:
16 - remove all the horrid Yahoo stuff
17 - remove the backreferences to _map and send events instead
18 but this will do for now and help remove the clutter from potlatch2.mxml.
21 public class Imagery extends EventDispatcher {
23 private static const GLOBAL_INSTANCE:Imagery = new Imagery();
24 public static function instance():Imagery { return GLOBAL_INSTANCE; }
26 public var collection:Array=[];
27 private var _selected:Object={};
30 private var _overlay:Sprite;
31 private var _yahoo:Yahoo;
33 /* Load catalogue file */
35 public function init(map:Map, overlay:Sprite, yahoo:Yahoo):void {
41 var request:DebugURLRequest = new DebugURLRequest("imagery.xml");
42 var loader:URLLoader = new URLLoader();
43 loader.addEventListener(Event.COMPLETE, onImageryLoad);
44 loader.load(request.request);
46 // create map listeners
47 map.addEventListener(MapEvent.MOVE, moveHandler);
48 map.addEventListener(MapEvent.RESIZE, resizeHandler);
51 private function onImageryLoad(event:Event):void {
52 var xml:XML = new XML(URLLoader(event.target).data);
53 var saved:Object = {};
55 if (SharedObject.getLocal("user_state").data['background_url']!=undefined) {
56 saved={ name: SharedObject.getLocal("user_state").data['background_name'],
57 url: SharedObject.getLocal("user_state").data['background_url' ] };
60 var isSet:Boolean=false;
61 var backgroundSet:Boolean = false;
63 // Read all values from XML file
64 collection=new Array({ name: "None", url: "" });
65 for each(var set:XML in xml.set) {
68 for each (a in set.@*) { obj[a.name().localName]=a.toString(); }
69 for each (a in set.* ) { obj[a.name() ]=a.toString(); }
71 if ((saved.url && obj.url ==saved.url) ||
72 (saved.name && obj.name==saved.name && obj.name!='Custom')) { isSet=true; }
75 // Add user's previous preference (from SharedObject) if we didn't find it in the XML file
76 if (!isSet && saved.name && saved.url && saved.url!='') {
77 collection.push(saved);
81 // Automatically select the user's previous preference
82 var defaultBackground:Object=null;
83 for each (bg in collection) {
84 if (bg.name==saved.name || bg.url==saved.url) {
87 } else if (bg.default) {
92 // Otherwise, set whatever's specified as default
93 if (!backgroundSet && defaultBackground) {
94 setBackground(defaultBackground);
97 // Get any attribution and logo details
98 for each (bg in collection) {
101 var loader:Loader = new Loader();
102 var thisbg1:Object = bg; // scope it for the closure
103 loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(e:Event):void { onLogoLoad(e,thisbg1); });
104 loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onError);
105 loader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
106 loader.load(new URLRequest(bg.logo));
108 if (bg.attribution_url) {
109 // load the attribution
110 var urlloader:URLLoader = new URLLoader();
111 var thisbg2:Object = bg; // scope it for the closure
112 urlloader.addEventListener(Event.COMPLETE, function(e:Event):void { onAttributionLoad(e,thisbg2); });
113 urlloader.addEventListener(IOErrorEvent.IO_ERROR, onError);
114 urlloader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
115 urlloader.load(new URLRequest(bg.attribution_url));
119 // Tell the function key manager that we'd like to receive function key calls
120 FunctionKeyManager.instance().registerListener('Background imagery',
121 function(o:String):void { setBackground(findBackgroundWithName(o)); });
122 dispatchEvent(new Event("collection_changed"));
125 private function onError(e:Event):void {
126 // placeholder error routine so exception isn't thrown
129 public function onLogoLoad(e:Event, bg:Object):void {
130 bg.logoData = Bitmap(LoaderInfo(e.target).content).bitmapData;
131 bg.logoWidth = e.target.loader.width;
132 bg.logoHeight= e.target.loader.height;
136 public function onAttributionLoad(e:Event,bg: Object):void {
137 // if we ever need to cope with non-Microsoft attribution, then this should look at bg.scheme
138 default xml namespace = Namespace("http://schemas.microsoft.com/search/local/ws/rest/v1");
139 var xml:XML = new XML(e.target.data);
140 var attribution:Object = {};
141 for each (var ImageryProvider:XML in xml..ImageryProvider) {
143 for each (var CoverageArea:XML in ImageryProvider.CoverageArea) {
144 areas.push([CoverageArea.ZoomMin,
145 CoverageArea.ZoomMax,
146 CoverageArea.BoundingBox.SouthLatitude,
147 CoverageArea.BoundingBox.WestLongitude,
148 CoverageArea.BoundingBox.NorthLatitude,
149 CoverageArea.BoundingBox.EastLongitude]);
151 attribution[ImageryProvider.Attribution]=areas;
153 default xml namespace = new Namespace("");
154 bg.attribution=attribution;
158 public function setBackground(bg:Object):void {
161 if (bg.url=='yahoo') { dispatchEvent(new CollectionEvent(CollectionEvent.SELECT, {url:''})); _yahoo.show(); }
162 else { dispatchEvent(new CollectionEvent(CollectionEvent.SELECT, bg )); _yahoo.hide(); }
163 // update attribution and logo
164 _overlay.visible=bg.attribution || bg.logo || bg.terms_url;
165 setLogo(); setAttribution(); setTerms();
166 // save as SharedObject for next time
167 var obj:SharedObject = SharedObject.getLocal("user_state");
168 obj.setProperty('background_url' ,String(bg.url));
169 obj.setProperty('background_name',String(bg.name));
173 public function get selected():Object { return _selected; }
175 private function findBackgroundWithName(name:String):Object {
176 for each (var bg:Object in collection) {
177 if (bg.name==name) { return bg; }
182 private function moveHandler(event:MapEvent):void {
184 dispatchEvent(new Event("collection_changed"));
186 private function setAttribution():void {
187 var tf:TextField=TextField(_overlay.getChildAt(0));
189 if (!_selected.attribution) return;
191 for (var provider:String in _selected.attribution) {
192 for each (var bounds:Array in _selected.attribution[provider]) {
193 if (_map.scale>=bounds[0] && _map.scale<=bounds[1] &&
194 ((_map.edge_l>bounds[3] && _map.edge_l<bounds[5]) ||
195 (_map.edge_r>bounds[3] && _map.edge_r<bounds[5]) ||
196 (_map.edge_l<bounds[3] && _map.edge_r>bounds[5])) &&
197 ((_map.edge_b>bounds[2] && _map.edge_b<bounds[4]) ||
198 (_map.edge_t>bounds[2] && _map.edge_t<bounds[4]) ||
199 (_map.edge_b<bounds[2] && _map.edge_t>bounds[4]))) {
204 if (attr.length==0) return;
205 tf.text="Background "+attr.join(", ");
206 positionAttribution();
207 dispatchEvent(new MapEvent(MapEvent.BUMP, { y: tf.textHeight })); // don't let the toolbox obscure it
209 private function positionAttribution():void {
210 var tf:TextField=TextField(_overlay.getChildAt(0));
211 tf.x=_map.mapwidth - 5 - tf.textWidth;
212 tf.y=_map.mapheight - 5 - tf.textHeight;
215 private function setLogo():void {
216 while (_overlay.numChildren>2) { _overlay.removeChildAt(2); }
217 if (!_selected.logoData) return;
218 var logo:Sprite=new Sprite();
219 logo.addChild(new Bitmap(_selected.logoData));
220 if (_selected.logo_url) { logo.buttonMode=true; logo.addEventListener(MouseEvent.CLICK, launchLogoLink, false, 0, true); }
221 _overlay.addChild(logo);
224 private function positionLogo():void {
225 _overlay.getChildAt(2).x=5;
226 _overlay.getChildAt(2).y=_map.mapheight - 5 - _selected.logoHeight - (_selected.terms_url ? 10 : 0);
228 private function launchLogoLink(e:Event):void {
229 if (!_selected.logo_url) return;
230 navigateToURL(new URLRequest(_selected.logo_url), '_blank');
232 private function setTerms():void {
233 var terms:TextField=TextField(_overlay.getChildAt(1));
234 if (!_selected.terms_url) { terms.text=''; return; }
235 terms.text="Background terms of use";
237 terms.addEventListener(MouseEvent.CLICK, launchTermsLink, false, 0, true);
239 private function positionTerms():void {
240 _overlay.getChildAt(1).x=5;
241 _overlay.getChildAt(1).y=_map.mapheight - 15;
243 private function launchTermsLink(e:Event):void {
244 if (!_selected.terms_url) return;
245 navigateToURL(new URLRequest(_selected.terms_url), '_blank');
248 private function resizeHandler(event:MapEvent):void {
249 if (_selected.logoData) positionLogo();
250 if (_selected.terms_url) positionTerms();
251 if (_selected.attribution) positionAttribution();
254 [Bindable(event="collection_changed")]
255 public function getAvailableImagery():ArrayCollection {
256 var available:Array=[];
257 for each (var bg:Object in collection) {
259 // if there's a bbox, check the current viewport intersects it
260 if (((_map.edge_l>bg.minlon && _map.edge_l<bg.maxlon) ||
261 (_map.edge_r>bg.minlon && _map.edge_r<bg.maxlon) ||
262 (_map.edge_l<bg.minlon && _map.edge_r>bg.maxlon)) &&
263 ((_map.edge_b>bg.minlat && _map.edge_b<bg.maxlat) ||
264 (_map.edge_t>bg.minlat && _map.edge_t<bg.maxlat) ||
265 (_map.edge_b<bg.minlat && _map.edge_t>bg.maxlat))) {
269 // if there's no bbox (i.e. global set), include it anyway
273 return new ArrayCollection(available);