Project

General

Profile

1
/* Copyright (c) 2006 MetaCarta, Inc., published under the BSD license.
2
 * See http://svn.openlayers.org/trunk/openlayers/license.txt for the full
3
 * text of the license. */
4

    
5
/**
6
 * @class
7
 * 
8
 * @requires OpenLayers/Util.js
9
 * @requires OpenLayers/Events.js
10
 */
11
OpenLayers.Map = Class.create();
12
OpenLayers.Map.TILE_WIDTH = 256;
13
OpenLayers.Map.TILE_HEIGHT = 256;
14
OpenLayers.Map.prototype = {
15
    
16
    /** base z-indexes for different classes of thing 
17
     * 
18
     * @type Object
19
     */
20
    Z_INDEX_BASE: { BaseLayer: 100, Overlay: 325, Popup: 750, Control: 1000 },
21

    
22
    /** supported application event types
23
     * 
24
     * @type Array */
25
    EVENT_TYPES: [ 
26
        "addlayer", "removelayer", "changelayer", "movestart", "move", 
27
        "moveend", "zoomend", "popupopen", "popupclose",
28
        "addmarker", "removemarker", "clearmarkers", "mouseover",
29
        "mouseout", "mousemove", "dragstart", "drag", "dragend",
30
        "changebaselayer"],
31

    
32
    /** @type OpenLayers.Events */
33
    events: null,
34

    
35
    /** the div that our map lives in
36
     * 
37
     * @type DOMElement */
38
    div: null,
39

    
40
    /** Size of the main div (this.div)
41
     * 
42
     * @type OpenLayers.Size */
43
    size: null,
44
    
45
    /** @type HTMLDivElement  */
46
    viewPortDiv: null,
47

    
48
    /** The lonlat at which the later container was re-initialized (on-zoom)
49
     * @type OpenLayers.LonLat */
50
    layerContainerOrigin: null,
51

    
52
    /** @type HTMLDivElement */
53
    layerContainerDiv: null,
54

    
55
    /** ordered list of layers in the map
56
     * 
57
     * @type Array(OpenLayers.Layer)
58
     */
59
    layers: null,
60

    
61
    /** @type Array(OpenLayers.Control) */
62
    controls: null,
63

    
64
    /** @type Array(OpenLayers.Popup) */
65
    popups: null,
66

    
67
    /** The currently selected base layer - this determines min/max zoom level, 
68
     *  projection, etc.
69
     * 
70
     * @type OpenLayers.Layer */
71
    baseLayer: null,
72
    
73
    /** @type OpenLayers.LonLat */
74
    center: null,
75

    
76
    /** @type int */
77
    zoom: 0,    
78

    
79
  // Options
80

    
81
    /** @type OpenLayers.Size */
82
    tileSize: null,
83

    
84
    /** @type String */
85
    projection: "EPSG:4326",    
86
        
87
    /** @type String */
88
    units: 'degrees',
89

    
90
    /** default max is 360 deg / 256 px, which corresponds to
91
     *    zoom level 0 on gmaps
92
     * 
93
     * @type float */
94
    maxResolution: 1.40625,
95

    
96
    /** @type float */
97
    minResolution: null,
98

    
99
    /** @type float */
100
    maxScale: null,
101

    
102
    /** @type float */
103
    minScale: null,
104

    
105
    /** @type OpenLayers.Bounds */
106
    maxExtent: null,
107
    
108
    /** @type OpenLayers.Bounds */
109
    minExtent: null,
110
    
111
    /** @type int */
112
    numZoomLevels: 16,
113

    
114

    
115
    /**
116
     * @constructor
117
     * 
118
     * @param {DOMElement} div
119
     * @param {Object} options Hashtable of extra options to tag onto the map
120
     */    
121
    initialize: function (div, options) {
122

    
123
        this.div = div = $(div);
124

    
125
        // the viewPortDiv is the outermost div we modify
126
        var id = div.id + "_OpenLayers_ViewPort";
127
        this.viewPortDiv = OpenLayers.Util.createDiv(id, null, null, null,
128
                                                     "relative", null,
129
                                                     "hidden");
130
        this.viewPortDiv.style.width = "100%";
131
        this.viewPortDiv.style.height = "100%";
132
        this.div.appendChild(this.viewPortDiv);
133

    
134
        // the layerContainerDiv is the one that holds all the layers
135
        id = div.id + "_OpenLayers_Container";
136
        this.layerContainerDiv = OpenLayers.Util.createDiv(id);
137
        this.layerContainerDiv.style.zIndex=this.Z_INDEX_BASE['Popup']-1;
138
        
139
        this.viewPortDiv.appendChild(this.layerContainerDiv);
140

    
141
        this.events = new OpenLayers.Events(this, div, this.EVENT_TYPES);
142

    
143
        this.updateSize();
144
 
145
    // Because Mozilla does not support the "resize" event for elements other
146
    //  than "window", we need to put a hack here. 
147
    // 
148
        if (navigator.appName.contains("Microsoft")) {
149
            // If IE, register the resize on the div
150
            this.events.register("resize", this, this.updateSize);
151
        } else {
152
            // Else updateSize on catching the window's resize
153
            //  Note that this is ok, as updateSize() does nothing if the 
154
            //  map's size has not actually changed.
155
            Event.observe(window, 'resize', 
156
                          this.updateSize.bindAsEventListener(this));
157
        }
158
        
159
        //set the default options
160
        this.setOptions(options);
161

    
162
        this.layers = [];
163
        
164
        if (this.controls == null) {
165
            this.controls = [ new OpenLayers.Control.MouseDefaults(),
166
                              new OpenLayers.Control.PanZoom()];
167
        }
168

    
169
        for(var i=0; i < this.controls.length; i++) {
170
            this.addControlToMap(this.controls[i]);
171
        }
172

    
173
        this.popups = new Array();
174

    
175
        // always call map.destroy()
176
        Event.observe(window, 
177
                      'unload', 
178
                      this.destroy.bindAsEventListener(this));
179

    
180
    },
181

    
182
    /**
183
    * @private
184
    */
185
    destroy:function() {
186
        if (this.layers != null) {
187
            for(var i=0; i< this.layers.length; i++) {
188
                this.layers[i].destroy();
189
            } 
190
            this.layers = null;
191
        }
192
        if (this.controls != null) {
193
            for(var i=0; i< this.controls.length; i++) {
194
                this.controls[i].destroy();
195
            } 
196
            this.controls = null;
197
        }
198
    },
199

    
200
    /**
201
     * @private
202
     * 
203
     * @param {Object} options Hashtable of options to tag to the map
204
     */
205
    setOptions: function(options) {
206

    
207
        // Simple-type defaults are set in class definition. 
208
        //  Now set complex-type defaults 
209
        this.tileSize = new OpenLayers.Size(OpenLayers.Map.TILE_WIDTH,
210
                                            OpenLayers.Map.TILE_HEIGHT);
211
        
212
        this.maxExtent = new OpenLayers.Bounds(-180, -90, 180, 90);
213

    
214
        // now add the options declared by the user
215
        //  (these will override defaults)
216
        Object.extend(this, options);
217
    },
218

    
219
    /**
220
     * @type OpenLayers.Size
221
     */
222
     getTileSize: function() {
223
         return this.tileSize;
224
     },
225

    
226
  /********************************************************/
227
  /*                                                      */
228
  /*           Layers, Controls, Popup Functions          */
229
  /*                                                      */
230
  /*     The following functions deal with adding and     */
231
  /*        removing Layers, Controls, and Popups         */
232
  /*                to and from the Map                   */
233
  /*                                                      */
234
  /********************************************************/         
235

    
236
    /**
237
     * @param {String} name
238
     * 
239
     * @returns The Layer with the corresponding id from the map's 
240
     *           layer collection, or null if not found.
241
     * @type OpenLayers.Layer
242
     */
243
    getLayer: function(id) {
244
        var foundLayer = null;
245
        for (var i = 0; i < this.layers.length; i++) {
246
            var layer = this.layers[i];
247
            if (layer.id == id) {
248
                foundLayer = layer;
249
            }
250
        }
251
        return foundLayer;
252
    },
253

    
254
    /**
255
    * @param {OpenLayers.Layer} layer
256
    */    
257
    addLayer: function (layer) {
258
        layer.div.style.overflow = "";
259
        layer.div.style.zIndex = 
260
            this.Z_INDEX_BASE[layer.isBaseLayer ? 'BaseLayer' : 'Overlay']
261
            + this.layers.length * 5;
262

    
263
        if (layer.isFixed) {
264
            this.viewPortDiv.appendChild(layer.div);
265
        } else {
266
            this.layerContainerDiv.appendChild(layer.div);
267
        }
268
        this.layers.push(layer);
269
        layer.setMap(this);
270

    
271
        this.events.triggerEvent("addlayer");
272

    
273
        //make sure layer draws itself!
274
        if (this.center != null) {
275
            var bounds = this.getExtent();
276
            layer.moveTo(bounds, true);
277
        }
278

    
279
        if (layer.isBaseLayer) {
280
            // set the first baselaye we add as the baselayer
281
            if (this.baseLayer == null) {
282
                this.setBaseLayer(layer);
283
                this.events.triggerEvent("changebaselayer");
284
            } else {
285
                layer.setVisibility(false);
286
            }
287
        }
288
    },
289

    
290
    /**
291
    * @param {Array(OpenLayers.Layer)} layers
292
    */    
293
    addLayers: function (layers) {
294
        for (var i = 0; i <  layers.length; i++) {
295
            this.addLayer(layers[i]);
296
        }
297
    },
298

    
299
    /** Removes a layer from the map by removing its visual element (the 
300
     *   layer.div property), then removing it from the map's internal list 
301
     *   of layers, setting the layer's map property to null. 
302
     * 
303
     *   a "removelayer" event is triggered.
304
     * 
305
     *   very worthy of mention is that simply removing a layer from a map
306
     *   will not cause the removal of any popups which may have been created
307
     *   by the layer. this is due to the fact that it was decided at some
308
     *   point that popups would not belong to layers. thus there is no way 
309
     *   for us to know here to which layer the popup belongs.
310
     *    
311
     *     A simple solution to this is simply to call destroy() on the layer.
312
     *     the default OpenLayers.Layer class's destroy() function
313
     *     automatically takes care to remove itself from whatever map it has
314
     *     been attached to. 
315
     * 
316
     *     The correct solution is for the layer itself to register an 
317
     *     event-handler on "removelayer" and when it is called, if it 
318
     *     recognizes itself as the layer being removed, then it cycles through
319
     *     its own personal list of popups, removing them from the map.
320
     * 
321
     * @param {OpenLayers.Layer} layer
322
     */
323
    removeLayer: function(layer) {
324
        if (layer.isFixed) {
325
            this.viewPortDiv.removeChild(layer.div);
326
        } else {
327
            this.layerContainerDiv.removeChild(layer.div);
328
        }
329
        layer.map = null;
330
        this.layers.remove(layer);
331

    
332
        // if we removed the base layer, need to set a new one
333
        if (this.baseLayer == layer) {
334
            this.baseLayer = null;
335
            for(i=0; i < this.layers.length; i++) {
336
                var iLayer = this.layers[i];
337
                if (iLayer.isBaseLayer) {
338
                    this.setBaseLayer(iLayer);
339
                    break;
340
                }
341
            }
342
        }
343
        this.events.triggerEvent("removelayer");
344
    },
345
    
346
    /** Allows user to specify one of the currently-loaded layers as the Map's
347
     *   new base layer.
348
     * 
349
     * @param {OpenLayers.Layer} newBaseLayer
350
     * @param {Boolean} noEvent
351
     */
352
    setBaseLayer: function(newBaseLayer, noEvent) {
353

    
354
        if (newBaseLayer != this.baseLayer) {
355
          
356
            // is newBaseLayer an already loaded layer?
357
            var foundLayer = (this.layers.indexOf(newBaseLayer) != -1);    
358
            if (foundLayer) {
359

    
360
                // make the old base layer invisible 
361
                if (this.baseLayer != null) {
362
                    this.baseLayer.setVisibility(false, noEvent);
363
                }
364

    
365
                // set new baselayer and make it visible
366
                this.baseLayer = newBaseLayer;
367
                this.baseLayer.setVisibility(true, noEvent);
368

    
369
                //redraw all layers
370
                var center = this.getCenter();
371
                if (center != null) {
372
                    var zoom = this.getZoom();
373
                    this.zoom = null;
374
                    if (zoom > this.baseLayer.numZoomLevels - 1) {
375
                        zoom = this.baseLayer.numZoomLevels - 1;
376
                    }    
377
                    this.setCenter(center, zoom);            
378
                
379
                }
380
                if ((noEvent == null) || (noEvent == false)) {
381
                    this.events.triggerEvent("changebaselayer");
382
                }
383
            }        
384
        }
385
    },
386

    
387
    /**
388
    * @param {OpenLayers.Control} control
389
    * @param {OpenLayers.Pixel} px
390
    */    
391
    addControl: function (control, px) {
392
        this.controls.push(control);
393
        this.addControlToMap(control, px);
394
    },
395

    
396
    /**
397
     * @private
398
     * 
399
     * @param {OpenLayers.Control} control
400
     * @param {OpenLayers.Pixel} px
401
     */    
402
    addControlToMap: function (control, px) {
403
        control.setMap(this);
404
        var div = control.draw(px);
405
        if (div) {
406
            div.style.zIndex = this.Z_INDEX_BASE['Control'] +
407
                                this.controls.length;
408
            this.viewPortDiv.appendChild( div );
409
        }
410
    },
411
    
412
    /** 
413
    * @param {OpenLayers.Popup} popup
414
    * @param {Boolean} exclusive If true, closes all other popups first
415
    */
416
    addPopup: function(popup, exclusive) {
417

    
418
        if (exclusive) {
419
            //remove all other popups from screen
420
            for(var i=0; i < this.popups.length; i++) {
421
                this.removePopup(this.popups[i]);
422
            }
423
        }
424

    
425
        popup.map = this;
426
        this.popups.push(popup);
427
        var popupDiv = popup.draw();
428
        if (popupDiv) {
429
            popupDiv.style.zIndex = this.Z_INDEX_BASE['Popup'] +
430
                                    this.popups.length;
431
            this.layerContainerDiv.appendChild(popupDiv);
432
        }
433
    },
434
    
435
    /** 
436
    * @param {OpenLayers.Popup} popup
437
    */
438
    removePopup: function(popup) {
439
        this.popups.remove(popup);
440
        if (popup.div) {
441
            this.layerContainerDiv.removeChild(popup.div);
442
        }
443
        popup.map = null;
444
    },
445

    
446
  /********************************************************/
447
  /*                                                      */
448
  /*              Container Div Functions                 */
449
  /*                                                      */
450
  /*   The following functions deal with the access to    */
451
  /*    and maintenance of the size of the container div  */
452
  /*                                                      */
453
  /********************************************************/     
454

    
455
    /**
456
    * @returns {OpenLayers.Size}
457
    */
458
    getSize: function () {
459
        return this.size;
460
    },
461

    
462
    /**
463
    * This function should be called by any external code which dynamically
464
    * changes the size of the map div (because mozilla wont let us catch the
465
    * "onresize" for an element)
466
    */
467
    updateSize: function() {
468
        var newSize = this.getCurrentSize();
469
        var oldSize = this.getSize();
470
        if (oldSize == null)
471
            this.size = oldSize = newSize;
472
        if (!newSize.equals(oldSize)) {
473
            
474
            //notify layers of mapresize
475
            for(var i=0; i < this.layers.length; i++) {
476
                this.layers[i].onMapResize();                
477
            }
478

    
479
            var center = new OpenLayers.Pixel(newSize.w /2, newSize.h / 2);
480
            
481
            var zoom = this.getZoom();
482
            this.zoom = null;
483
            this.setCenter(center, zoom);
484
            
485
            // store the new size
486
            this.size = newSize;
487
            // the div might have moved on the page, also
488
            this.events.element.offsets = null;
489
        }
490
    },
491
    
492
    /**
493
     * @private 
494
     * 
495
     * @returns A new OpenLayers.Size object with the dimensions of the map div
496
     * @type OpenLayers.Size
497
     */
498
    getCurrentSize: function() {
499

    
500
        var size = new OpenLayers.Size(this.div.clientWidth, 
501
                                       this.div.clientHeight);
502

    
503
        // Workaround for the fact that hidden elements return 0 for size.
504
        if (size.w == 0 && size.h == 0) {
505
            var dim = Element.getDimensions(this.div);
506
            size.w = dim.width;
507
            size.h = dim.height;
508
        }
509
        if (size.w == 0 && size.h == 0) {
510
            size.w = parseInt(this.div.style.width);
511
            size.h = parseInt(this.div.style.height);
512
        }
513
        return size;
514
    },
515

    
516
  /********************************************************/
517
  /*                                                      */
518
  /*            Zoom, Center, Pan Functions               */
519
  /*                                                      */
520
  /*    The following functions handle the validation,    */
521
  /*   getting and setting of the Zoom Level and Center   */
522
  /*       as well as the panning of the Map              */
523
  /*                                                      */
524
  /********************************************************/
525
    /**
526
    * @return {OpenLayers.LonLat}
527
    */
528
    getCenter: function () {
529
        return this.center;
530
    },
531

    
532

    
533
    /**
534
    * @return {int}
535
    */
536
    getZoom: function () {
537
        return this.zoom;
538
    },
539
    
540
    /** Allows user to pan by a value of screen pixels
541
     * 
542
     * @param {int} dx
543
     * @param {int} dy
544
     */
545
    pan: function(dx, dy) {
546

    
547
        // getCenter
548
        var centerPx = this.getViewPortPxFromLonLat(this.getCenter());
549

    
550
        // adjust
551
        var newCenterPx = centerPx.add(dx, dy);
552
        
553
        // only call setCenter if there has been a change
554
        if (!newCenterPx.equals(centerPx)) {
555
            var newCenterLonLat = this.getLonLatFromViewPortPx(newCenterPx);
556
            this.setCenter(newCenterLonLat);
557
        }
558

    
559
   },
560

    
561
    /**
562
    * @param {OpenLayers.LonLat} lonlat
563
    * @param {int} zoom
564
    * @param {Boolean} minor Specifies whether or not to 
565
    *                        trigger movestart/end events
566
    */
567
    setCenter: function (lonlat, zoom, minor) {
568
        var zoomChanged = (this.isValidZoomLevel(zoom)) && 
569
                          (zoom != this.getZoom());
570

    
571
        var centerChanged = (this.isValidLonLat(lonlat)) && 
572
                            (!lonlat.equals(this.center));
573

    
574

    
575
        // if neither center nor zoom will change, no need to do anything
576
        if (zoomChanged || centerChanged || !minor) {
577

    
578
            if (!minor) { this.events.triggerEvent("movestart"); }
579

    
580
            if (centerChanged) {
581
                if ((!zoomChanged) && (this.center)) { 
582
                    // if zoom hasnt changed, just slide layerContainer
583
                    //  (must be done before setting this.center to new value)
584
                    this.centerLayerContainer(lonlat);
585
                }
586
                this.center = lonlat.clone();
587
            }
588

    
589
            // (re)set the layerContainerDiv's location
590
            if ((zoomChanged) || (this.layerContainerOrigin == null)) {
591
                this.layerContainerOrigin = this.center.clone();
592
                this.layerContainerDiv.style.left = "0px";
593
                this.layerContainerDiv.style.top  = "0px";
594
            }
595

    
596
            if (zoomChanged) {
597
                this.zoom = zoom;
598
                    
599
                //redraw popups
600
                for (var i = 0; i < this.popups.length; i++) {
601
                    this.popups[i].updatePosition();
602
                }
603
            }    
604
            
605
            //send the move call to the baselayer and all the overlays    
606
            var bounds = this.getExtent();
607
            for (var i = 0; i < this.layers.length; i++) {
608
                var layer = this.layers[i];
609
                if ((layer == this.baseLayer) || !layer.isBaseLayer) {
610
                    layer.moveTo(bounds, zoomChanged, minor);
611
                }
612
            }
613
            
614
            this.events.triggerEvent("move");
615
    
616
            if (zoomChanged) { this.events.triggerEvent("zoomend"); }
617
        }
618

    
619
        // even if nothing was done, we want to notify of this
620
        if (!minor) { this.events.triggerEvent("moveend"); }
621
    },
622

    
623
    /** This function takes care to recenter the layerContainerDiv 
624
     * 
625
     * @private 
626
     * 
627
     * @param {OpenLayers.LonLat} lonlat
628
     */
629
    centerLayerContainer: function (lonlat) {
630

    
631
        var originPx = this.getViewPortPxFromLonLat(this.layerContainerOrigin);
632
        var newPx = this.getViewPortPxFromLonLat(lonlat);
633

    
634
        this.layerContainerDiv.style.left = (originPx.x - newPx.x) + "px";
635
        this.layerContainerDiv.style.top  = (originPx.y - newPx.y) + "px";
636
    },
637

    
638
    /**
639
     * @private 
640
     * 
641
     * @param {int} zoomLevel
642
     * 
643
     * @returns Whether or not the zoom level passed in is non-null and 
644
     *           within the min/max range of zoom levels.
645
     * @type Boolean
646
     */
647
    isValidZoomLevel: function(zoomLevel) {
648
       return ( (zoomLevel != null) &&
649
                (zoomLevel >= 0) && 
650
                (zoomLevel < this.getNumZoomLevels()) );
651
    },
652
    
653
    /**
654
     * @private 
655
     * 
656
     * @param {OpenLayers.LonLat} lonlat
657
     * 
658
     * @returns Whether or not the lonlat passed in is non-null and within
659
     *             the maxExtent bounds
660
     * 
661
     * @type Boolean
662
     */
663
    isValidLonLat: function(lonlat) {
664
        var valid = false;
665
        if (lonlat != null) {
666
            var maxExtent = this.getMaxExtent();
667
            valid = maxExtent.contains(lonlat.lon, lonlat.lat);        
668
        }
669
        return valid;
670
    },
671

    
672
  /********************************************************/
673
  /*                                                      */
674
  /*                 Layer Options                        */
675
  /*                                                      */
676
  /*    Accessor functions to Layer Options parameters    */
677
  /*                                                      */
678
  /********************************************************/
679
    
680
    /**
681
     * @returns The Projection of the base layer
682
     * @type String
683
     */
684
    getProjection: function() {
685
        var projection = null;
686
        if (this.baseLayer != null) {
687
            projection = this.baseLayer.getProjection();
688
        }
689
        return projection;
690
    },
691
    
692
    /**
693
     * @returns The Map's Maximum Resolution
694
     * @type String
695
     */
696
    getMaxResolution: function() {
697
        var maxResolution = null;
698
        if (this.baseLayer != null) {
699
            maxResolution = this.baseLayer.getMaxResolution();
700
        }
701
        return maxResolution;
702
    },
703
        
704
    /**
705
    * @type OpenLayers.Bounds
706
    */
707
    getMaxExtent: function () {
708
        var maxExtent = null;
709
        if (this.baseLayer != null) {
710
            maxExtent = this.baseLayer.getMaxExtent();
711
        }        
712
        return maxExtent;
713
    },
714
    
715
    /**
716
     * @returns The total number of zoom levels that can be displayed by the 
717
     *           current baseLayer.
718
     * @type int
719
     */
720
    getNumZoomLevels: function() {
721
        var numZoomLevels = null;
722
        if (this.baseLayer != null) {
723
            numZoomLevels = this.baseLayer.getNumZoomLevels();
724
        }
725
        return numZoomLevels;
726
    },
727

    
728
  /********************************************************/
729
  /*                                                      */
730
  /*                 Baselayer Functions                  */
731
  /*                                                      */
732
  /*    The following functions, all publicly exposed     */
733
  /*       in the API?, are all merely wrappers to the    */
734
  /*       the same calls on whatever layer is set as     */
735
  /*                the current base layer                */
736
  /*                                                      */
737
  /********************************************************/
738

    
739
    /**
740
     * @returns A Bounds object which represents the lon/lat bounds of the 
741
     *          current viewPort. 
742
     *          If no baselayer is set, returns null.
743
     * @type OpenLayers.Bounds
744
     */
745
    getExtent: function () {
746
        var extent = null;
747
        if (this.baseLayer != null) {
748
            extent = this.baseLayer.getExtent();
749
        }
750
        return extent;
751
    },
752

    
753
    /**
754
     * @returns The current resolution of the map. 
755
     *          If no baselayer is set, returns null.
756
     * @type float
757
     */
758
    getResolution: function () {
759
        var resolution = null;
760
        if (this.baseLayer != null) {
761
            resolution = this.baseLayer.getResolution();
762
        }
763
        return resolution;
764
    },
765

    
766
     /**
767
      * @returns The current scale denominator of the map. 
768
      *          If no baselayer is set, returns null.
769
      * @type float
770
      */
771
    getScale: function () {
772
        var scale = null;
773
        if (this.baseLayer != null) {
774
            var res = this.getResolution();
775
            var units = this.baseLayer.units;
776
            scale = res * OpenLayers.INCHES_PER_UNIT[units] *
777
                    OpenLayers.DOTS_PER_INCH;
778
        }
779
        return scale;
780
    },
781

    
782

    
783
    /**
784
    * @param {OpenLayers.Bounds} bounds
785
    *
786
    * @returns A suitable zoom level for the specified bounds.
787
    *          If no baselayer is set, returns null.
788
    * @type int
789
    */
790
    getZoomForExtent: function (bounds) {
791
        zoom = null;
792
        if (this.baseLayer != null) {
793
            zoom = this.baseLayer.getZoomForExtent(bounds);
794
        }
795
        return zoom;
796
    },
797

    
798
  /********************************************************/
799
  /*                                                      */
800
  /*                  Zooming Functions                   */
801
  /*                                                      */
802
  /*    The following functions, all publicly exposed     */
803
  /*       in the API, are all merely wrappers to the     */
804
  /*               the setCenter() function               */
805
  /*                                                      */
806
  /********************************************************/
807
  
808
    /** Zoom to a specific zoom level
809
     * 
810
     * @param {int} zoom
811
     */
812
    zoomTo: function(zoom) {
813
        if (this.isValidZoomLevel(zoom)) {
814
            this.setCenter(null, zoom);
815
        }
816
    },
817
    
818
    /**
819
     * @param {int} zoom
820
     */
821
    zoomIn: function() {
822
        this.zoomTo(this.getZoom() + 1);
823
    },
824
    
825
    /**
826
     * @param {int} zoom
827
     */
828
    zoomOut: function() {
829
        this.zoomTo(this.getZoom() - 1);
830
    },
831

    
832
    /** Zoom to the passed in bounds, recenter
833
     * 
834
     * @param {OpenLayers.Bounds} bounds
835
     */
836
    zoomToExtent: function(bounds) {
837
        this.setCenter(bounds.getCenterLonLat(), 
838
                       this.getZoomForExtent(bounds));
839
    },
840

    
841
    /** Zoom to the full extent and recenter.
842
     */
843
    zoomToMaxExtent: function() {
844
        this.zoomToExtent(this.getMaxExtent());
845
    },
846

    
847
    /** zoom to a specified scale 
848
     * 
849
     * @param {float} scale
850
     */
851
    zoomToScale: function(scale) {
852
        var res = OpenLayers.Util.getResolutionFromScale(scale, 
853
                                                         this.baseLayer.units);
854
        var size = this.getSize();
855
        var w_deg = size.w * res;
856
        var h_deg = size.h * res;
857
        var center = this.getCenter();
858

    
859
        var extent = new OpenLayers.Bounds(center.lon - w_deg / 2,
860
                                           center.lat - h_deg / 2,
861
                                           center.lon + w_deg / 2,
862
                                           center.lat + h_deg / 2);
863
        this.zoomToExtent(extent);
864
    },
865
    
866
  /********************************************************/
867
  /*                                                      */
868
  /*             Translation Functions                    */
869
  /*                                                      */
870
  /*      The following functions translate between       */
871
  /*           LonLat, LayerPx, and ViewPortPx            */
872
  /*                                                      */
873
  /********************************************************/
874
      
875
  //
876
  // TRANSLATION: LonLat <-> ViewPortPx
877
  //
878

    
879
    /**
880
    * @param {OpenLayers.Pixel} viewPortPx
881
    *
882
    * @returns An OpenLayers.LonLat which is the passed-in view port
883
    *          OpenLayers.Pixel, translated into lon/lat by the 
884
    *          current base layer
885
    * @type OpenLayers.LonLat
886
    * @private
887
    */
888
    getLonLatFromViewPortPx: function (viewPortPx) {
889
        var lonlat = null; 
890
        if (this.baseLayer != null) {
891
            lonlat = this.baseLayer.getLonLatFromViewPortPx(viewPortPx);
892
        }
893
        return lonlat;
894
    },
895

    
896
    /**
897
    * @param {OpenLayers.LonLat} lonlat
898
    *
899
    * @returns An OpenLayers.Pixel which is the passed-in OpenLayers.LonLat, 
900
    *          translated into view port pixels by the 
901
    *          current base layer
902
    * @type OpenLayers.Pixel
903
    * @private
904
    */
905
    getViewPortPxFromLonLat: function (lonlat) {
906
        var px = null; 
907
        if (this.baseLayer != null) {
908
            px = this.baseLayer.getViewPortPxFromLonLat(lonlat);
909
        }
910
        return px;
911
    },
912

    
913
    
914
  //
915
  // CONVENIENCE TRANSLATION FUNCTIONS FOR API
916
  //
917

    
918
    /**
919
     * @param {OpenLayers.Pixel} pixel
920
     *
921
     * @returns An OpenLayers.LonLat corresponding to the given
922
     *          OpenLayers.Pixel, translated into lon/lat by the 
923
     *          current base layer
924
     * @type OpenLayers.LonLat
925
     */
926
    getLonLatFromPixel: function (px) {
927
        return this.getLonLatFromViewPortPx(px);
928
    },
929

    
930
    /**
931
     * @param {OpenLayers.LonLat} lonlat
932
     *
933
     * @returns An OpenLayers.Pixel corresponding to the OpenLayers.LonLat
934
     *          translated into view port pixels by the 
935
     *          current base layer
936
     * @type OpenLayers.Pixel
937
     */
938
    getPixelFromLonLat: function (lonlat) {
939
        return this.getViewPortPxFromLonLat(lonlat);
940
    },
941

    
942

    
943

    
944
  //
945
  // TRANSLATION: ViewPortPx <-> LayerPx
946
  //
947

    
948
    /**
949
     * @private
950
     * 
951
     * @param {OpenLayers.Pixel} layerPx
952
     * 
953
     * @returns Layer Pixel translated into ViewPort Pixel coordinates
954
     * @type OpenLayers.Pixel
955
     */
956
    getViewPortPxFromLayerPx:function(layerPx) {
957
        var viewPortPx = null;
958
        if (layerPx != null) {
959
            var dX = parseInt(this.layerContainerDiv.style.left);
960
            var dY = parseInt(this.layerContainerDiv.style.top);
961
            viewPortPx = layerPx.add(dX, dY);            
962
        }
963
        return viewPortPx;
964
    },
965
    
966
    /**
967
     * @private
968
     * 
969
     * @param {OpenLayers.Pixel} viewPortPx
970
     * 
971
     * @returns ViewPort Pixel translated into Layer Pixel coordinates
972
     * @type OpenLayers.Pixel
973
     */
974
    getLayerPxFromViewPortPx:function(viewPortPx) {
975
        var layerPx = null;
976
        if (viewPortPx != null) {
977
            var dX = -parseInt(this.layerContainerDiv.style.left);
978
            var dY = -parseInt(this.layerContainerDiv.style.top);
979
            layerPx = viewPortPx.add(dX, dY);
980
        }
981
        if (!isNaN(layerPx.x) && !isNaN(layerPx.y)) {
982
            return layerPx;
983
        }
984
        return null;
985
    },
986
    
987
  //
988
  // TRANSLATION: LonLat <-> LayerPx
989
  //
990

    
991
    /**
992
    * @param {OpenLayers.Pixel} px
993
    *
994
    * @type OpenLayers.LonLat
995
    */
996
    getLonLatFromLayerPx: function (px) {
997
       //adjust for displacement of layerContainerDiv
998
       px = this.getViewPortPxFromLayerPx(px);
999
       return this.getLonLatFromViewPortPx(px);         
1000
    },
1001
    
1002
    /**
1003
    * @param {OpenLayers.LonLat} lonlat
1004
    *
1005
    * @returns An OpenLayers.Pixel which is the passed-in OpenLayers.LonLat, 
1006
    *          translated into layer pixels by the current base layer
1007
    * @type OpenLayers.Pixel
1008
    */
1009
    getLayerPxFromLonLat: function (lonlat) {
1010
       //adjust for displacement of layerContainerDiv
1011
       var px = this.getViewPortPxFromLonLat(lonlat);
1012
       return this.getLayerPxFromViewPortPx(px);         
1013
    },
1014

    
1015

    
1016
    CLASS_NAME: "OpenLayers.Map"
1017
};
(8-8/13)