1 package net.systemeD.halcyon {
3 // ** Need to support different zoom levels
4 // When zoom level changes:
5 // - double or halve xoffset/yoffset accordingly
6 // - blank the tile queue
8 import flash.display.DisplayObjectContainer;
9 import flash.display.Bitmap;
10 import flash.events.*;
13 import net.systemeD.halcyon.ImageLoader;
14 import net.systemeD.halcyon.Globals;
16 public class TileSet extends DisplayObjectContainer {
18 public var baseurl:String;
20 public var tile_l:int;
21 public var tile_r:int;
22 public var tile_b:int;
23 public var tile_t:int;
25 public var xoffset:Number;
26 public var yoffset:Number;
28 private var requests:Array=[];
29 private var tiles:Object={}; // key is "z,x,y"; value "true" (needed) or reference to sprite
30 private var waiting:int=0; // number of tiles currently being downloaded
35 public function TileSet(map:Map) {
39 public function init(url:String):void {
42 // Update bounds - called on every move
44 public function update():void {
45 tile_l=lon2tile(map.coord2lon(-xoffset-map.x));
46 tile_r=lon2tile(map.coord2lon(-xoffset-map.x+map.mapwidth));
47 tile_t=lat2tile(map.coord2lat(-yoffset-map.y));
48 tile_b=lat2tile(map.coord2lat(-yoffset-map.y+map.mapheight));
50 for (var tx:int=tile_l; tx<=tile_r; tx++) {
51 for (var ty:int=tile_t; ty<=tile_b; ty++) {
52 if (!tiles[map.scale+','+tx+','+ty]) { addRequest(tx,ty); }
57 // Mark that a tile needs to be loaded
59 public function addRequest(tx:int,ty:int):void {
60 tiles[map.scale+','+tx+','+ty]=true;
61 requests.push([map.scale,tx,ty]);
64 // Service tile queue - called on every frame to download new tiles
66 public function serviceQueue():void {
67 if (waiting==4 || requests.length==0) { return; }
68 var r:Array, tx:int, ty:int, tz:int;
69 var loader:ImageLoader, urlreq:URLRequest;
71 for (var i:uint=0; i<Math.min(requests.length, 4-waiting); i++) {
72 r=requests.shift(); tz=r[0]; tx=r[1]; ty=r[2];
73 if (tx>=tile_l && tx<=tile_r && ty>=tile_t && ty<=tile_b) {
74 // Tile is on-screen, so load
75 urlreq=new URLRequest(tileURL(tx,ty));
76 loader=new ImageLoader();
77 loader.dataFormat=URLLoaderDataFormat.BINARY;
78 loader.filename=[tz,tx,ty];
79 loader.addEventListener(Event.COMPLETE, loadedTile, false, 0, true);
80 loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler, false, 0, true);
81 loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler, false, 0, true);
82 loader.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler, false, 0, true);
88 // Tile has loaded, so place on display list
90 private function loadedTile(event:Event):void {
91 var r:Array=event.target.filename as Array;
92 var tz:int=r[0]; var tx:int=r[1]; var ty:int=r[2];
94 var image:Bitmap = event.target.content as Bitmap;
96 image.x=map.lon2coord(tile2lon(tx));
97 image.y=map.lat2coord(tile2lat(ty));
105 private function tileURL(tx:int,ty:int):String {
110 private function httpStatusHandler( event:HTTPStatusEvent ):void { }
111 private function securityErrorHandler( event:SecurityErrorEvent ):void { Globals.vars.root.addDebug("securityerrorevent"); }
112 private function ioErrorHandler( event:IOErrorEvent ):void { Globals.vars.root.addDebug("ioerrorevent"); }
115 // ------------------------------------------------------------------
116 // Co-ordinate conversion functions
118 private function lon2tile(lon:Number):int {
119 return (Math.floor((lon+180)/360*Math.pow(2,map.scale)));
121 private function lat2tile(lat:Number):int {
122 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)));
124 private function tile2lon(t:int):Number {
125 return (t/Math.pow(2,map.scale)*360-180);
127 private function tile2lat(t:int):Number {
128 var n:Number=Math.PI-2*Math.PI*t/Math.pow(2,map.scale);
129 return (180/Math.PI*Math.atan(0.5*(Math.exp(n)-Math.exp(-n))));