069767dc945b98d22ce26f5d32a4a7a43ef2c858
[rails.git] / public / javascripts / tile.js
1 // June 20th 2005 http://civicactions.net anselm@hook.org public domain version 0.5
2
3 //
4 // miscellaneous
5 //
6
7 var netscape = ( document.getElementById && !document.all ) || document.layers;
8 var defaultEngine = null; // xxx for firefox keyboard events.
9
10 var PI = 3.14159265358979323846;
11
12 var lat_range = PI, lon_range = PI;
13
14 //
15 // Utility - get div position - may not be accurate
16 //
17
18 function getCSSPositionX(parent) 
19 {
20     var offset = parent.x ? parseInt(parent.x) : 0;
21     offset += parent.style.left ? parseInt(parent.style.left) : 0;
22     for(var node = parent; node ; node = node.offsetParent ) 
23         { 
24                 offset += node.offsetLeft; 
25         }
26     return offset;
27 }
28
29 function getCSSPositionY(parent) 
30 {
31     var offset = parent.y ? parseInt(parent.y) : 0;
32     offset += parent.style.top ? parseInt(parent.style.top) : 0;
33     for(var node = parent; node ; node = node.offsetParent ) 
34         { 
35                 offset += node.offsetTop; 
36         }
37     return offset;
38 }
39
40 ///
41 /// initialize a new tile engine object
42 /// usage: var engine = new tile_engine_new(parentdiv,stylehints,wmssource,lon,lat,zoom,optional width, optional height)
43 ///
44 function tile_engine_new(parentname,hints,feedurl,url,lon,lat,zoom,w,h) 
45 {
46     // NW geocoder removed for now
47
48         this.timestamp = new Date().getTime();
49         this.urlAttr = new Array();
50
51     // NW Removed navigation buttons entirely for flexibility    
52
53     this.lonPerPixel = function()
54             { return (this.lon_quant/this.scale)/this.tilewidth; }
55
56     this.latPerPixel = function()
57             { return (this.lat_quant/this.scale)/this.tileheight; }
58
59     this.xToLon = function(x)
60             { return this.lon + (x-this.thewidth/2)*this.lonPerPixel(); } 
61
62     this.yToLat = function(y)
63             { return normallat(this.lat - (y-this.theheight/2)
64                                 *this.latPerPixel()); } 
65
66         this.lonToX = function (lon)
67                         { return ((lon-this.lon)/this.lonPerPixel()) + this.thewidth/2;}
68
69         this.latToY = function(lat)
70                         { return ((this.lat-mercatorlat(lat))/this.latPerPixel()) + 
71                                                         this.theheight/2; }
72
73
74     //
75     // it is possible that this collection is already in use - clean it
76     //
77     this.clean = function() 
78     { 
79                 /*
80         while( this.parent.hasChildNodes() ) 
81             this.parent.removeChild( this.parent.firstChild );
82                         */
83
84                 for(var ct=0; ct<this.parent.childNodes.length; ct++)
85                 {
86                         if(this.parent.childNodes[ct].id != "controls")
87                                 this.parent.removeChild(this.parent.childNodes[ct]);
88                 }
89
90         //
91         // build inner tile container for theoretical speed improvement?
92         // center in parent for simplicity of math
93         // size of inner container is irrelevant since overflow is enabled
94         //
95
96         if( this.dragcontainer ) 
97         {
98             this.tiles = document.createElement('div');
99             this.tiles.style.position = 'absolute';
100             this.tiles.style.left = this.displaywidth/2 + 'px';
101             this.tiles.style.top = this.displayheight/2 + 'px';
102             this.tiles.style.width = '16px';
103             this.tiles.style.height = '16px';
104             if( this.debug ) 
105             {
106                 this.tiles.style.border = 'dashed green 1px';
107             }
108             this.tiles.tile_engine = this;
109             this.parent.appendChild(this.tiles);
110
111         } 
112         else 
113         {
114             this.tiles = this.parent;
115         }
116     }
117
118     /// focus over specified lon/lat and zoom
119     /// user should call this.drag(0,0) after this to force an initial refresh
120     ///
121
122     this.performzoom = function(lon,lat,z) 
123     {
124         // setup for zoom
125         // this engine operates at * scale to try avoid tile errors thrashing 
126         // server cache
127         
128         this.scale = 1000000;
129         this.lon_min_clamp = -180 * this.scale;
130         this.lon_max_clamp = 180 * this.scale;
131         this.lat_min_clamp = -180 * this.scale; //t
132         this.lat_max_clamp = 180 * this.scale; //t
133         this.lon_start_tile = 180 * this.scale;
134         this.lat_start_tile = 90 * this.scale; //t
135         this.zoom_power = 2;
136         this.lon_quant = this.lon_start_tile;
137         this.lat_quant = this.lat_start_tile;
138         this.lon = lon;
139         this.lat = lat;
140
141         // operational lat - = lat due to quirks in our engine and quirks in o
142         // lon/lat design
143         lat = -lat;
144
145         // divide tile size until reach requested zoom
146         // trying to guarantee consistency so as to not thrash the server side tile cache
147         while(z > 0) 
148         {
149             this.lon_quant = this.lon_quant / this.zoom_power;
150             this.lat_quant = this.lat_quant / this.zoom_power;
151             z--;
152         }
153         this.lon_quant = Math.round( this.lon_quant );
154         this.lat_quant = Math.round( this.lat_quant );
155     
156         // get user requested exact lon/lat
157         this.lon_scaled = Math.round( lon * this.scale );
158         this.lat_scaled = Math.round( lat * this.scale );
159     
160
161
162         // convert requested exact lon/lat to quantized lon lat (rounding down 
163         // or up as best suits)
164         this.lon_round = Math.round( this.lon_scaled / this.lon_quant ) * 
165                 this.lon_quant;
166         this.lat_round = Math.round( this.lat_scaled / this.lat_quant ) * 
167                 this.lat_quant;
168     
169         //alert('lon_round=' + this.lon_round+ ' lat_round='+this.lat_round);
170
171         // calculate world extents [ this is the span of all tiles in lon/lat ]
172         this.lon_min = this.lon_round - this.lon_quant;
173         this.lon_max = this.lon_round + this.lon_quant;
174         this.lat_min = this.lat_round - this.lat_quant;
175         this.lat_max = this.lat_round + this.lat_quant;
176     
177         // set tiled region details [ this is the span of all tiles in pixels ]
178         this.centerx = 0;
179         this.centery = 0;
180         this.tilewidth = 256;
181         this.tileheight = 128;
182         this.left = -this.tilewidth;
183         this.right = this.tilewidth;
184         this.top = -this.tileheight;
185         this.bot = this.tileheight;
186
187
188         // adjust the current center position slightly to reflect exact lat/lon
189         // not rounded
190         this.centerx -= (this.lon_scaled-this.lon_round)/
191             (this.lon_max-this.lon_min)*(this.right-this.left);
192         this.centery -= (this.lat_scaled-this.lat_round)/
193             (this.lat_max-this.lat_min)*(this.bot-this.top);
194     }
195
196         this.update_perma_link = function() {
197                 // because we're using mercator
198                 updatelinks(this.lon,normallat(this.lat),this.zoom);
199         }
200
201     ///
202     /// draw the spanning lon/lat range
203     /// drag is simply the mouse delta in pixels
204     ///
205
206     this.drag = function(dragx,dragy) 
207     {
208                 var fred=true;
209
210         // move the drag offset
211         this.centerx += dragx;
212         this.centery += dragy;
213
214         // update where we think the user is actually focused
215         this.lon = ( this.lon_round - ( this.lon_max - this.lon_min ) / 
216             ( this.right - this.left ) * this.centerx ) / this.scale;
217         this.lat = - ( this.lat_round - ( this.lat_max - this.lat_min ) / 
218             ( this.bot - this.top ) * this.centery ) / this.scale;
219
220                 this.update_perma_link();
221
222         // show it
223         var helper = this.navhelp; 
224
225          // extend exposed sections
226         var dirty = false;
227         while( this.left + this.centerx > -this.displaywidth/2 && 
228                 this.lon_min > this.lon_min_clamp ) 
229         {
230             this.left -= this.tilewidth;
231             this.lon_min -= this.lon_quant;
232             dirty = true;
233         }
234         while( this.right + this.centerx < this.displaywidth/2 && 
235                 this.lon_max < this.lon_max_clamp ) 
236         {
237             this.right += this.tilewidth;
238             this.lon_max += this.lon_quant;
239             dirty = true;
240         }
241         while( this.top + this.centery > -this.displayheight/2 && 
242         this.lat_min > this.lat_min_clamp ) 
243         {
244             this.top -= this.tileheight;
245             this.lat_min -= this.lat_quant;
246             dirty = true;
247         }
248
249         while( this.bot + this.centery < this.displayheight/2 && 
250         this.lat_max < this.lat_max_clamp ) 
251         {
252             this.bot += this.tileheight;
253             this.lat_max += this.lat_quant;
254             dirty = true;
255         }
256
257
258         // prepare to walk the container and assure that all nodes are correct
259         var containerx;
260         var containery;
261
262         // in drag container mode we do not have to move the children all the 
263         // time
264         if( this.dragcontainer ) 
265         {
266             this.tiles.style.left = this.displaywidth / 2 + this.centerx + 'px';
267             this.tiles.style.top = this.displayheight / 2 + this.centery + 'px';
268             if( !dirty && this.tiles.hasChildNodes() ) 
269             {
270                 return;
271             }
272             containerx = this.left;
273             containery = this.top;
274         } 
275         else 
276         {
277             containerx = this.left + this.centerx;
278             containery = this.top + this.centery;
279         }
280
281         // walk all tiles and repair as needed
282         // xxx one bug is that it walks the _entire_ width and height... 
283         // not just visible.
284         // xxx this makes cleanup harder and perhaps a bitmap is better
285
286         var removehidden = 1;
287         var removecolumn = 0;
288         var removerow = 0;
289         var containeryreset = containery;
290
291         for( var x = this.lon_min; x < this.lon_max ; x+= this.lon_quant ) 
292         {
293             // will this row be visible in the next round?
294             if( removehidden ) 
295             {
296                 var rx = containerx + this.centerx;
297                 if( rx > this.displaywidth / 2 ) 
298                 {
299                     removerow = 1;
300                     // ideally i would truncate max width here
301                 } 
302                 else if( rx + this.tilewidth < - this.displaywidth / 2 ) 
303                 {
304                     removerow = 1;
305                 } 
306                 else 
307                 {
308                     removerow = 0;
309                 }
310             }
311
312             for( var y = this.lat_min; y < this.lat_max ; y+= this.lat_quant ) 
313             {
314                 // is this column visible?
315                 if( removehidden ) 
316                 {
317                     var ry = containery + this.centery;
318                     if( ry > this.displayheight / 2 ) 
319                     {
320                         removecolumn = 1;
321                     } 
322                     else if( ry + this.tileheight < - this.displayheight/2) 
323                     {
324                         removecolumn = 1;
325                     } 
326                     else 
327                     {
328                         removecolumn = 0;
329                     }
330                 }
331
332                 // convert to WMS compliant coordinate system
333                 var lt = x / this.scale;
334                 var rt = lt + this.lon_quant / this.scale;
335                 var tp = y / this.scale;
336                 var bt = tp + this.lat_quant / this.scale;
337                 var temp = bt;
338                 var bt = -tp;
339                 var tp = -temp;
340
341                 // modify for mercator-projected tiles: 
342                 tp = 180/PI * (2 * Math.atan(Math.exp(tp * PI / 180)) - PI / 2);
343                 bt = 180/PI * (2 * Math.atan(Math.exp(bt * PI / 180)) - PI / 2);
344                 
345                 // make a key
346                 var key = this.url + "?WIDTH="+(this.tilewidth)+"&HEIGHT="+
347                     (this.tileheight)+"&BBOX="+lt+","+tp+","+rt+","+bt;
348
349                 // see if our tile is already present
350                 var node = document.getElementById(key);
351
352                 // create if not present
353                 if(!node) 
354                 {
355                     if( this.debug > 0) 
356                     {
357                         node = document.createElement('div');
358                     } 
359                     else 
360                     {
361                         node = document.createElement('img');
362                     }
363                     node.id = key;
364                     node.className = 'tile';
365                     node.style.position = 'absolute';
366                     node.style.width = this.tilewidth + 'px';
367                     node.style.height = this.tileheight + 'px';
368                     node.style.left = containerx + 'px';
369                     node.style.top = containery + 'px';
370                     node.style.zIndex = 10; // to appear under the rss elements
371                     node.tile_engine = this;
372                     if( this.debug > 0) 
373                     {
374                         node.style.border = "1px solid yellow";
375                         node.innerHTML = key;
376                         if( this.debug > 1 ) 
377                         {
378                             var img = document.createElement('img');
379                             img.src = key;
380                             node.appendChild(img);
381                         }
382                     }
383
384                     var goURL = key + "&zoom=" + this.zoom; 
385                                                         
386                                     for(var k in this.urlAttr) 
387                                         {
388                                                 goURL += "&"+k+"="+this.urlAttr[k];
389                                         }
390                                 
391                                         node.src = goURL;
392
393                     node.alt = "loading tile..";
394                     node.style.color = "#ffffff";
395                     this.tiles.appendChild(node);
396                 }
397                 // adjust if using active style
398                 else if( !this.dragcontainer ) {
399                     node.style.left = containerx + 'px';
400                     node.style.top = containery + 'px';
401                 }
402
403                 containery += this.tileheight;
404             }
405             containery = containeryreset;
406             containerx += this.tilewidth;
407         }
408     }
409
410     this.zoomTo = function(zoom) 
411     {
412
413         this.zoom  = zoom;
414     
415         if (this.zoom < this.minzoom) { this.zoom = this.minzoom; }
416         if (this.zoom > this.maxzoom) { this.zoom = this.maxzoom; }
417
418
419
420
421         ///
422         /// immediately draw and or fit to feed
423         ///
424         this.performzoom(this.lon,this.lat,zoom);
425         this.drag(0,0);
426
427         ///
428     } // CLOSE ZOOM FUNCTION
429
430     this.setLatLon = function(lat,lon)
431     { 
432         this.lon=lon; 
433         this.lat=mercatorlat(lat); 
434         this.clean(); 
435         this.performzoom(lon,mercatorlat(lat),this.zoom);
436         this.drag(0,0); 
437     }
438
439         this.forceRefresh = function()
440         {
441                 this.clean();
442         this.performzoom(this.lon,this.lat,this.zoom);
443         this.drag(0,0); 
444         }
445
446     ///
447     /// zoom a tile group
448     ///
449     this.tile_engine_zoomout = function() 
450     {
451         this.clean();
452         this.zoomTo(this.zoom-1);
453
454         return false; // or safari falls over
455     }
456
457     ///
458     /// zoom a tile group
459     ///
460     this.tile_engine_zoomin = function() 
461     {
462         
463         this.clean();
464         this.zoomTo(this.zoom+1);
465   
466         return false; // or safari falls over
467     }
468
469     this.setURL=function(url) { this.url=url; }
470
471
472
473     ///
474     /// intercept context events to minimize out-of-browser interruptions
475     ///
476     
477     this.event_context = function(e) 
478     {
479         return false;
480     }
481
482     ///
483     /// keys
484     ///
485
486     this.event_key = function(e) 
487     {
488         var key = 0;        
489
490         var hostengine = defaultEngine;
491
492         if( window && window.event && window.event.srcElement ) {
493             hostengine = window.event.srcElement.tile_engine;
494         } else if( e.target ) {
495             hostengine = e.target.tile_engine;
496         } else if( e.srcElement ) {
497             hostengine = e.srcElement.tile_engine;
498         }
499
500         if( hostengine == null ) {
501             hostengine = defaultEngine;
502             if( hostengine == null ) {
503                 return;
504             }
505         }
506
507
508         if( e == null && document.all ) {
509             e = window.event;
510         }
511
512         if( e ) {
513             if( e.keyCode ) {
514                 key = e.keyCode;
515             }
516             else if( e.which ) {
517                 key = e.which;
518             }
519
520             switch(key) {
521             case 97: // a = left
522                 hostengine.drag(16,0);
523                 break;
524             case 100: // d = right
525                 hostengine.drag(-16,0);
526                 break;
527             case 119: // w = up
528                 hostengine.drag(0,16);
529                 break;
530             case 120: // x = dn
531                 hostengine.drag(0,-16);
532                 break;
533             case 115: // s = center
534                 new tile_engine_new(hostengine.parentname,
535                             "FULL",
536                             hostengine.feedurl, // xxx hrm, cache this?
537                             hostengine.url,
538                             hostengine.lon,
539                             hostengine.lat,
540                             hostengine.zoom,
541                             0,0
542                             );
543                 break;
544             case 122: // z = zoom
545                 new tile_engine_new(hostengine.parentname,
546                             "FULL",
547                             hostengine.feedurl, // xxx hrm, cache this?
548                             hostengine.url,
549                             hostengine.lon,
550                             hostengine.lat,
551                             hostengine.zoom + 1,
552                             0,0
553                             );
554                 break;
555             case  99: // c = unzoom
556                 new tile_engine_new(hostengine.parentname,
557                             "FULL",
558                             hostengine.feedurl, // xxx hrm, cache this?
559                             hostengine.url,
560                             hostengine.lon,
561                             hostengine.lat,
562                             hostengine.zoom - 1,
563                             0,0
564                             );
565                 break;
566             }
567         }    
568     }
569
570     ///
571     /// catch mouse move events
572     /// this routine _must_ return false or else the operating system outside-of-browser-scope drag and drop handler will interfere
573     ///
574
575     this.event_mouse_move = function(e) {
576
577         var hostengine = null;
578         if( window && window.event && window.event.srcElement ) {
579             hostengine = window.event.srcElement.tile_engine;
580         } else if( e.target ) {
581             hostengine = e.target.tile_engine;
582         } else if( e.srcElement ) {
583             hostengine = e.srcElement.tile_engine;
584         }
585
586
587         if( hostengine && hostengine.drag ) {
588             if( hostengine.mousedown ) {
589                 if( netscape ) {
590                     hostengine.mousex = parseInt(e.pageX) + 0.0;
591                     hostengine.mousey = parseInt(e.pageY) + 0.0;
592                 } else {
593                     hostengine.mousex = parseInt(window.event.clientX) + 0.0;
594                     hostengine.mousey = parseInt(window.event.clientY) + 0.0;
595                 }
596                 hostengine.drag(hostengine.mousex-hostengine.lastmousex,hostengine.mousey-hostengine.lastmousey);
597             }
598             hostengine.lastmousex = hostengine.mousex;
599             hostengine.lastmousey = hostengine.mousey;
600         }
601
602         // must return false to prevent operating system drag and drop from handling events
603         return false;
604     }
605
606     ///
607     /// catch mouse down
608     ///
609
610     this.event_mouse_down = function(e) {
611
612         var hostengine = null;
613         if( window && window.event && window.event.srcElement ) {
614             hostengine = window.event.srcElement.tile_engine;
615         } else if( e.target ) {
616             hostengine = e.target.tile_engine;
617         } else if( e.srcElement ) {
618             hostengine = e.srcElement.tile_engine;
619         }
620
621
622         if( hostengine ) {
623             if( netscape ) {
624                 hostengine.mousex = parseInt(e.pageX) + 0.0;
625                 hostengine.mousey = parseInt(e.pageY) + 0.0;
626             } else {
627                 hostengine.mousex = parseInt(window.event.clientX) + 0.0;
628                 hostengine.mousey = parseInt(window.event.clientY) + 0.0;
629             }
630             hostengine.lastmousex = hostengine.mousex;
631             hostengine.lastmousey = hostengine.mousey;
632             hostengine.mousedown = 1;
633         }
634
635         // must return false to prevent operating system drag and drop from handling events
636         return false;
637     }
638
639     ///
640     /// catch double click (use to center map)
641     ///
642
643     this.event_double_click = function(e) {
644
645         var hostengine = null;
646         if( window && window.event && window.event.srcElement ) {
647             hostengine = window.event.srcElement.tile_engine;
648         } else if( e.target ) {
649             hostengine = e.target.tile_engine;
650         } else if( e.srcElement ) {
651             hostengine = e.srcElement.tile_engine;
652         }
653
654
655         if( hostengine ) {
656             if( netscape ) {
657                 hostengine.mousex = parseInt(e.pageX) + 0.0;
658                 hostengine.mousey = parseInt(e.pageY) + 0.0;
659             } else {
660                 hostengine.mousex = parseInt(window.event.clientX) + 0.0;
661                 hostengine.mousey = parseInt(window.event.clientY) + 0.0;
662             }
663             var dx = hostengine.mousex-(hostengine.displaywidth/2)-hostengine.parent_x;
664             var dy = hostengine.mousey-(hostengine.displayheight/2)-hostengine.parent_y;
665             hostengine.drag(-dx,-dy); // TODO smooth
666         }
667
668         // must return false to prevent operating system drag and drop from handling events
669         return false;
670
671     }
672
673     ///
674     /// catch mouse up
675     ///
676
677     this.event_mouse_up = function(e) {
678
679         var hostengine = null;
680         if( window && window.event && window.event.srcElement ) {
681             hostengine = window.event.srcElement.tile_engine;
682         } else if( e.target ) {
683             hostengine = e.target.tile_engine;
684         } else if( e.srcElement ) {
685             hostengine = e.srcElement.tile_engine;
686         }
687         
688
689         if( hostengine ) {
690             if( netscape ) {
691                 hostengine.mousex = parseInt(e.pageX) + 0.0;
692                 hostengine.mousey = parseInt(e.pageY) + 0.0;
693             } else {
694                 hostengine.mousex = parseInt(window.event.clientX) + 0.0;
695                 hostengine.mousey = parseInt(window.event.clientY) + 0.0;
696             }
697             hostengine.mousedown = 0;
698         }
699
700         // must return false to prevent operating system drag and drop from handling events
701         return false;
702     }
703
704     ///
705     /// catch mouse out
706     ///
707
708     this.event_mouse_out = function(e) {
709
710         var hostengine = null;
711         if( window && window.event && window.event.srcElement ) {
712             hostengine = window.event.srcElement.tile_engine;
713         } else if( e.target ) {
714             hostengine = e.target.tile_engine;
715         } else if( e.srcElement ) {
716             hostengine = e.srcElement.tile_engine;
717         }
718
719
720         if( hostengine ) {
721             if( netscape ) {
722                 hostengine.mousex = parseInt(e.pageX) + 0.0;
723                 hostengine.mousey = parseInt(e.pageY) + 0.0;
724             } else {
725                 hostengine.mousex = parseInt(window.event.clientX) + 0.0;
726                 hostengine.mousey = parseInt(window.event.clientY) + 0.0;
727             }
728             hostengine.mousedown = 0;
729         }
730
731         // must return false to prevent operating system drag and drop from handling events
732         return false;
733     }
734
735
736     ///
737     /// register new handlers to catch desired events
738     ///
739
740     // NW removed parameter - always use parent
741     this.event_catch = function() {
742
743         this.parent.style.cursor = 'move';
744
745         if( netscape ) {
746             window.captureEvents(Event.MOUSEMOVE);
747             window.captureEvents(Event.KEYPRESS);
748         }
749
750         this.parent.onmousemove = this.event_mouse_move;
751         this.parent.onmousedown = this.event_mouse_down;
752         this.parent.onmouseup = this.event_mouse_up;
753         this.parent.onkeypress = this.event_key;
754         window.ondblclick = this.event_double_click;
755
756         if( window ) {
757             window.onmousemove = this.event_mouse_move;
758             window.onmouseup = this.event_mouse_up;
759             window.ondblclick = this.event_double_click;
760         }
761
762     }
763
764         this.setURLAttribute = function(k,v)
765         {
766                 this.urlAttr[k] = v; 
767         }
768
769         this.getURLAttribute = function(k)
770         {
771                 return this.urlAttr[k];
772         }
773
774         this.getDownloadedTileBounds = function()
775         {
776                 var bounds = new Array();
777                 bounds.w=this.lon_min; 
778                 bounds.s=normallat(this.lat_min);
779                 bounds.e=this.lon_max;
780                 bounds.n=normallat(this.lat_max);
781                 return bounds;
782         }
783
784         this.getVisibleBounds = function()
785         {
786                 var bounds = new Array();
787                 bounds.w = this.xToLon(0);
788                 bounds.s = this.yToLat(this.theheight);
789                 bounds.e = this.xToLon(this.thewidth);
790                 bounds.n = this.yToLat(0);
791                 return bounds;
792         }
793         
794         // navout and navin stuff - START
795         // draw navigation buttons into the parent div
796
797     // ENTRY CODE BEGINS HERE....
798
799
800     // get parent div or fail
801     this.parent = document.getElementById(parentname);
802     if( this.parent == null ) {
803         alert('The tile map engine cannot find a parent container named ['
804                 +parentname+']');
805         return;
806     }
807
808     //
809     // store for later
810     //
811
812     this.parentname = parentname;
813     this.hints = hints;
814     this.feedurl = feedurl;
815     this.url = url;
816     this.lon = lon;
817     this.lat = mercatorlat(lat);
818     this.thewidth = w;
819     this.theheight = h;
820     this.dragcontainer = 1;
821     this.debug = 0;
822
823     // for firefox keyboard
824     defaultEngine = this;
825     document.engine = this;
826
827
828     //
829     // decide on display width and height
830     //
831     if( !w || !h ) 
832         {
833         w = parseInt(this.parent.style.width);
834         h = parseInt(this.parent.style.height);
835         if(!w || !h) 
836                 {
837             w = 512;
838             h = 256;
839             this.parent.style.width = w + 'px';
840             this.parent.style.height = h + 'px';
841         }
842     } 
843         else 
844         {
845         this.parent.style.width = parseInt(w) + 'px';
846         this.parent.style.height = parseInt(h) + 'px';
847     }
848     this.displaywidth = w;
849     this.displayheight = h;
850
851     this.minzoom = 0;
852     this.maxzoom = 20;
853
854     //
855     // enforce parent div style?
856     // position absolute is really only required for firefox
857     // http://www.quirksmode.org/js/findpos.html
858     //
859
860     this.parent_x = getCSSPositionX(this.parent);
861     this.parent_y = getCSSPositionY(this.parent);
862
863     this.parent.style.position = 'relative';
864     this.parent.style.overflow = 'hidden';
865     this.parent.style.backgroundColor = '#000036';
866
867
868         // attach event capture parent div
869         this.event_catch();
870
871     this.clean();
872         //this.makeZoom();
873
874     this.zoomTo(zoom);
875 }
876
877
878 function normallat(mercatorlat)
879 {
880     var tp = 180/PI*(2 * Math.atan(Math.exp(mercatorlat * PI / 180)) - PI / 2);
881     return tp;
882 }
883
884 function mercatorlat(normallat)
885 {
886   var lpi =  3.14159265358979323846;
887   return  Math.log( Math.tan( (lpi / 4.0) + (normallat / 180.0 * lpi / 2.0))) * 
888                       180.0 / lpi ;
889 }