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.*;
12 import net.systemeD.halcyon.ImageURLLoader;
13 import flash.system.LoaderContext;
15 public class TileSet extends Sprite {
17 public var baseurl:String;
19 public var tile_l:int;
20 public var tile_r:int;
21 public var tile_b:int;
22 public var tile_t:int;
24 public var xoffset:Number=0;
25 public var yoffset:Number=0;
27 private var requests:Array=[];
28 private var tiles:Object={}; // key is "z,x,y"; value "true" (needed) or reference to sprite
29 private var waiting:int=0; // number of tiles currently being downloaded
34 public function TileSet(map:Map) {
38 public function init(url:String):void {
41 // Update bounds - called on every move
43 public function update():void {
44 tile_l=lon2tile(map.edge_l+xoffset);
45 tile_r=lon2tile(map.edge_r+xoffset);
46 tile_t=lat2tile(map.edge_t+yoffset);
47 tile_b=lat2tile(map.edge_b+yoffset);
48 for (var tx:int=tile_l; tx<=tile_r; tx++) {
49 for (var ty:int=tile_t; ty<=tile_b; ty++) {
50 if (!tiles[map.scale+','+tx+','+ty]) { addRequest(tx,ty); }
55 // Mark that a tile needs to be loaded
57 public function addRequest(tx:int,ty:int):void {
58 tiles[map.scale+','+tx+','+ty]=true;
59 requests.push([map.scale,tx,ty]);
62 // Service tile queue - called on every frame to download new tiles
64 public function serviceQueue():void {
65 if (waiting==4 || requests.length==0) { return; }
66 var r:Array, tx:int, ty:int, tz:int;
68 for (var i:uint=0; i<Math.min(requests.length, 4-waiting); i++) {
69 r=requests.shift(); tz=r[0]; tx=r[1]; ty=r[2];
70 if (tx>=tile_l && tx<=tile_r && ty>=tile_t && ty<=tile_b) {
71 // Tile is on-screen, so load
73 var loader:Loader = new Loader();
74 loader.contentLoaderInfo.addEventListener(Event.INIT, doImgInit);
75 loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, missingTileError);
76 loader.load(new URLRequest(tileURL(tx,ty)),
77 new LoaderContext(true));
78 this.addChild(loader);
79 loader.x=map.lon2coord(tile2lon(tx));
80 loader.y=map.lat2coord(tile2lat(ty));
86 private function missingTileError(event:Event):void {
91 protected function doImgInit(event:Event):void {
99 private function tileURL(tx:int,ty:int):String {
100 return "http://npe.openstreetmap.org/"+map.scale+"/"+tx+"/"+ty+".png";
101 // return "http://andy.sandbox.cloudmade.com/tiles/cycle/"+map.scale+"/"+tx+"/"+ty+".png";
106 // ------------------------------------------------------------------
107 // Co-ordinate conversion functions
109 private function lon2tile(lon:Number):int {
110 return (Math.floor((lon+180)/360*Math.pow(2,map.scale)));
112 private function lat2tile(lat:Number):int {
113 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)));
115 private function tile2lon(t:int):Number {
116 return (t/Math.pow(2,map.scale)*360-180);
118 private function tile2lat(t:int):Number {
119 var n:Number=Math.PI-2*Math.PI*t/Math.pow(2,map.scale);
120 return (180/Math.PI*Math.atan(0.5*(Math.exp(n)-Math.exp(-n))));