Bienvenue sur PostGIS.fr

Bienvenue sur PostGIS.fr , le site de la communauté des utilisateurs francophones de PostGIS.

PostGIS ajoute le support d'objets géographique à la base de données PostgreSQL. En effet, PostGIS "spatialise" le serverur PostgreSQL, ce qui permet de l'utiliser comme une base de données SIG.

Maintenu à jour, en fonction de nos disponibilités et des diverses sorties des outils que nous testons, nous vous proposons l'ensemble de nos travaux publiés en langue française.

source: trunk/workshop-routing-foss4g/web/OpenLayers/lib/OpenLayers/Map.js @ 76

Revision 76, 81.8 KB checked in by djay, 12 years ago (diff)

Ajout du répertoire web

  • Property svn:executable set to *
Line 
1/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for
2 * full list of contributors). Published under the Clear BSD license. 
3 * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
4 * full text of the license. */
5
6/**
7 * @requires OpenLayers/Util.js
8 * @requires OpenLayers/Events.js
9 * @requires OpenLayers/Tween.js
10 * @requires OpenLayers/Console.js
11 */
12
13/**
14 * Class: OpenLayers.Map
15 * Instances of OpenLayers.Map are interactive maps embedded in a web page.
16 * Create a new map with the <OpenLayers.Map> constructor.
17 *
18 * On their own maps do not provide much functionality.  To extend a map
19 * it's necessary to add controls (<OpenLayers.Control>) and
20 * layers (<OpenLayers.Layer>) to the map.
21 */
22OpenLayers.Map = OpenLayers.Class({
23   
24    /**
25     * Constant: Z_INDEX_BASE
26     * {Object} Base z-indexes for different classes of thing
27     */
28    Z_INDEX_BASE: {
29        BaseLayer: 100,
30        Overlay: 325,
31        Feature: 725,
32        Popup: 750,
33        Control: 1000
34    },
35
36    /**
37     * Constant: EVENT_TYPES
38     * {Array(String)} Supported application event types.  Register a listener
39     *     for a particular event with the following syntax:
40     * (code)
41     * map.events.register(type, obj, listener);
42     * (end)
43     *
44     * Listeners will be called with a reference to an event object.  The
45     *     properties of this event depends on exactly what happened.
46     *
47     * All event objects have at least the following properties:
48     *  - *object* {Object} A reference to map.events.object.
49     *  - *element* {DOMElement} A reference to map.events.element.
50     *
51     * Browser events have the following additional properties:
52     *  - *xy* {<OpenLayers.Pixel>} The pixel location of the event (relative
53     *      to the the map viewport).
54     *  - other properties that come with browser events
55     *
56     * Supported map event types:
57     *  - *preaddlayer* triggered before a layer has been added.  The event
58     *      object will include a *layer* property that references the layer 
59     *      to be added.
60     *  - *addlayer* triggered after a layer has been added.  The event object
61     *      will include a *layer* property that references the added layer.
62     *  - *removelayer* triggered after a layer has been removed.  The event
63     *      object will include a *layer* property that references the removed
64     *      layer.
65     *  - *changelayer* triggered after a layer name change, order change,
66     *      opacity change, params change or visibility change
67     *      (due to resolution thresholds). Listeners will receive an event
68     *      object with *layer* and *property* properties. The *layer*
69     *      property will be a reference to the changed layer.
70     *      The *property* property will be a key to the
71     *      changed property (name, order, opacity, params or visibility).
72     *  - *movestart* triggered after the start of a drag, pan, or zoom
73     *  - *move* triggered after each drag, pan, or zoom
74     *  - *moveend* triggered after a drag, pan, or zoom completes
75     *  - *zoomend* triggered after a zoom completes
76     *  - *mouseover* triggered after mouseover the map
77     *  - *mouseout* triggered after mouseout the map
78     *  - *mousemove* triggered after mousemove the map
79     *  - *changebaselayer* triggered after the base layer changes
80     */
81    EVENT_TYPES: [ 
82        "preaddlayer", "addlayer", "removelayer", "changelayer", "movestart",
83        "move", "moveend", "zoomend", "popupopen", "popupclose",
84        "addmarker", "removemarker", "clearmarkers", "mouseover",
85        "mouseout", "mousemove", "dragstart", "drag", "dragend",
86        "changebaselayer"],
87
88    /**
89     * Property: id
90     * {String} Unique identifier for the map
91     */
92    id: null,
93   
94    /**
95     * Property: fractionalZoom
96     * {Boolean} For a base layer that supports it, allow the map resolution
97     *     to be set to a value between one of the values in the resolutions
98     *     array.  Default is false.
99     *
100     * When fractionalZoom is set to true, it is possible to zoom to
101     *     an arbitrary extent.  This requires a base layer from a source
102     *     that supports requests for arbitrary extents (i.e. not cached
103     *     tiles on a regular lattice).  This means that fractionalZoom
104     *     will not work with commercial layers (Google, Yahoo, VE), layers
105     *     using TileCache, or any other pre-cached data sources.
106     *
107     * If you are using fractionalZoom, then you should also use
108     *     <getResolutionForZoom> instead of layer.resolutions[zoom] as the
109     *     former works for non-integer zoom levels.
110     */
111    fractionalZoom: false,
112   
113    /**
114     * APIProperty: events
115     * {<OpenLayers.Events>} An events object that handles all
116     *                       events on the map
117     */
118    events: null,
119   
120    /**
121     * APIProperty: allOverlays
122     * {Boolean} Allow the map to function with "overlays" only.  Defaults to
123     *     false.  If true, the lowest layer in the draw order will act as
124     *     the base layer.  In addition, if set to true, all layers will
125     *     have isBaseLayer set to false when they are added to the map.
126     *
127     * Note:
128     * If you set map.allOverlays to true, then you *cannot* use
129     *     map.setBaseLayer or layer.setIsBaseLayer.  With allOverlays true,
130     *     the lowest layer in the draw layer is the base layer.  So, to change
131     *     the base layer, use <setLayerIndex> or <raiseLayer> to set the layer
132     *     index to 0.
133     */
134    allOverlays: false,
135
136    /**
137     * APIProperty: div
138     * {DOMElement|String} The element that contains the map (or an id for
139     *     that element).  If the <OpenLayers.Map> constructor is called
140     *     with two arguments, this should be provided as the first argument.
141     *     Alternatively, the map constructor can be called with the options
142     *     object as the only argument.  In this case (one argument), a
143     *     div property may or may not be provided.  If the div property
144     *     is not provided, the map can be rendered to a container later
145     *     using the <render> method.
146     *     
147     * Note:
148     * If you are calling <render> after map construction, do not use
149     *     <maxResolution>  auto.  Instead, divide your <maxExtent> by your
150     *     maximum expected dimension.
151     */
152    div: null,
153   
154    /**
155     * Property: dragging
156     * {Boolean} The map is currently being dragged.
157     */
158    dragging: false,
159
160    /**
161     * Property: size
162     * {<OpenLayers.Size>} Size of the main div (this.div)
163     */
164    size: null,
165   
166    /**
167     * Property: viewPortDiv
168     * {HTMLDivElement} The element that represents the map viewport
169     */
170    viewPortDiv: null,
171
172    /**
173     * Property: layerContainerOrigin
174     * {<OpenLayers.LonLat>} The lonlat at which the later container was
175     *                       re-initialized (on-zoom)
176     */
177    layerContainerOrigin: null,
178
179    /**
180     * Property: layerContainerDiv
181     * {HTMLDivElement} The element that contains the layers.
182     */
183    layerContainerDiv: null,
184
185    /**
186     * APIProperty: layers
187     * {Array(<OpenLayers.Layer>)} Ordered list of layers in the map
188     */
189    layers: null,
190
191    /**
192     * Property: controls
193     * {Array(<OpenLayers.Control>)} List of controls associated with the map.
194     *
195     * If not provided in the map options at construction, the map will
196     *     be given the following controls by default:
197     *  - <OpenLayers.Control.Navigation>
198     *  - <OpenLayers.Control.PanZoom>
199     *  - <OpenLayers.Control.ArgParser>
200     *  - <OpenLayers.Control.Attribution>
201     */
202    controls: null,
203
204    /**
205     * Property: popups
206     * {Array(<OpenLayers.Popup>)} List of popups associated with the map
207     */
208    popups: null,
209
210    /**
211     * APIProperty: baseLayer
212     * {<OpenLayers.Layer>} The currently selected base layer.  This determines
213     * min/max zoom level, projection, etc.
214     */
215    baseLayer: null,
216   
217    /**
218     * Property: center
219     * {<OpenLayers.LonLat>} The current center of the map
220     */
221    center: null,
222
223    /**
224     * Property: resolution
225     * {Float} The resolution of the map.
226     */
227    resolution: null,
228
229    /**
230     * Property: zoom
231     * {Integer} The current zoom level of the map
232     */
233    zoom: 0,   
234
235    /**
236     * Property: panRatio
237     * {Float} The ratio of the current extent within
238     *         which panning will tween.
239     */
240    panRatio: 1.5,   
241
242    /**
243     * Property: viewRequestID
244     * {String} Used to store a unique identifier that changes when the map
245     *          view changes. viewRequestID should be used when adding data
246     *          asynchronously to the map: viewRequestID is incremented when
247     *          you initiate your request (right now during changing of
248     *          baselayers and changing of zooms). It is stored here in the
249     *          map and also in the data that will be coming back
250     *          asynchronously. Before displaying this data on request
251     *          completion, we check that the viewRequestID of the data is
252     *          still the same as that of the map. Fix for #480
253     */
254    viewRequestID: 0,
255
256  // Options
257
258    /**
259     * APIProperty: tileSize
260     * {<OpenLayers.Size>} Set in the map options to override the default tile
261     *                     size for this map.
262     */
263    tileSize: null,
264
265    /**
266     * APIProperty: projection
267     * {String} Set in the map options to override the default projection
268     *          string this map - also set maxExtent, maxResolution, and
269     *          units if appropriate.  Default is "EPSG:4326".
270     */
271    projection: "EPSG:4326",   
272       
273    /**
274     * APIProperty: units
275     * {String} The map units.  Defaults to 'degrees'.  Possible values are
276     *          'degrees' (or 'dd'), 'm', 'ft', 'km', 'mi', 'inches'.
277     */
278    units: 'degrees',
279
280    /**
281     * APIProperty: resolutions
282     * {Array(Float)} A list of map resolutions (map units per pixel) in
283     *     descending order.  If this is not set in the layer constructor, it
284     *     will be set based on other resolution related properties
285     *     (maxExtent, maxResolution, maxScale, etc.).
286     */
287    resolutions: null,
288
289    /**
290     * APIProperty: maxResolution
291     * {Float} Default max is 360 deg / 256 px, which corresponds to
292     *          zoom level 0 on gmaps.  Specify a different value in the map
293     *          options if you are not using a geographic projection and
294     *          displaying the whole world.
295     */
296    maxResolution: 1.40625,
297
298    /**
299     * APIProperty: minResolution
300     * {Float}
301     */
302    minResolution: null,
303
304    /**
305     * APIProperty: maxScale
306     * {Float}
307     */
308    maxScale: null,
309
310    /**
311     * APIProperty: minScale
312     * {Float}
313     */
314    minScale: null,
315
316    /**
317     * APIProperty: maxExtent
318     * {<OpenLayers.Bounds>} The maximum extent for the map.  Defaults to the
319     *                       whole world in decimal degrees
320     *                       (-180, -90, 180, 90).  Specify a different
321     *                        extent in the map options if you are not using a
322     *                        geographic projection and displaying the whole
323     *                        world.
324     */
325    maxExtent: null,
326   
327    /**
328     * APIProperty: minExtent
329     * {<OpenLayers.Bounds>}
330     */
331    minExtent: null,
332   
333    /**
334     * APIProperty: restrictedExtent
335     * {<OpenLayers.Bounds>} Limit map navigation to this extent where possible.
336     *     If a non-null restrictedExtent is set, panning will be restricted
337     *     to the given bounds.  In addition, zooming to a resolution that
338     *     displays more than the restricted extent will center the map
339     *     on the restricted extent.  If you wish to limit the zoom level
340     *     or resolution, use maxResolution.
341     */
342    restrictedExtent: null,
343
344    /**
345     * APIProperty: numZoomLevels
346     * {Integer} Number of zoom levels for the map.  Defaults to 16.  Set a
347     *           different value in the map options if needed.
348     */
349    numZoomLevels: 16,
350
351    /**
352     * APIProperty: theme
353     * {String} Relative path to a CSS file from which to load theme styles.
354     *          Specify null in the map options (e.g. {theme: null}) if you
355     *          want to get cascading style declarations - by putting links to
356     *          stylesheets or style declarations directly in your page.
357     */
358    theme: null,
359   
360    /**
361     * APIProperty: displayProjection
362     * {<OpenLayers.Projection>} Requires proj4js support.Projection used by
363     *     several controls to display data to user. If this property is set,
364     *     it will be set on any control which has a null displayProjection
365     *     property at the time the control is added to the map.
366     */
367    displayProjection: null,
368
369    /**
370     * APIProperty: fallThrough
371     * {Boolean} Should OpenLayers allow events on the map to fall through to
372     *           other elements on the page, or should it swallow them? (#457)
373     *           Default is to fall through.
374     */
375    fallThrough: true,
376   
377    /**
378     * Property: panTween
379     * {OpenLayers.Tween} Animated panning tween object, see panTo()
380     */
381    panTween: null,
382
383    /**
384     * APIProperty: eventListeners
385     * {Object} If set as an option at construction, the eventListeners
386     *     object will be registered with <OpenLayers.Events.on>.  Object
387     *     structure must be a listeners object as shown in the example for
388     *     the events.on method.
389     */
390    eventListeners: null,
391
392    /**
393     * APIProperty: panMethod
394     * {Function} The Easing function to be used for tweening.  Default is
395     * OpenLayers.Easing.Expo.easeOut. Setting this to 'null' turns off
396     * animated panning.
397     */
398    panMethod: OpenLayers.Easing.Expo.easeOut,
399   
400    /**
401     * Property: panDuration
402     * {Integer} The number of steps to be passed to the
403     * OpenLayers.Tween.start() method when the map is
404     * panned.
405     * Default is 50.
406     */
407    panDuration: 50,
408   
409    /**
410     * Property: paddingForPopups
411     * {<OpenLayers.Bounds>} Outside margin of the popup. Used to prevent
412     *     the popup from getting too close to the map border.
413     */
414    paddingForPopups : null,
415   
416    /**
417     * Constructor: OpenLayers.Map
418     * Constructor for a new OpenLayers.Map instance.  There are two possible
419     *     ways to call the map constructor.  See the examples below.
420     *
421     * Parameters:
422     * div - {DOMElement|String}  The element or id of an element in your page
423     *     that will contain the map.  May be omitted if the <div> option is
424     *     provided or if you intend to call the <render> method later.
425     * options - {Object} Optional object with properties to tag onto the map.
426     *
427     * Examples (method one):
428     * (code)
429     * // create a map with default options in an element with the id "map1"
430     * var map = new OpenLayers.Map("map1");
431     *
432     * // create a map with non-default options in an element with id "map2"
433     * var options = {
434     *     maxExtent: new OpenLayers.Bounds(-200000, -200000, 200000, 200000),
435     *     maxResolution: 156543,
436     *     units: 'm',
437     *     projection: "EPSG:41001"
438     * };
439     * var map = new OpenLayers.Map("map2", options);
440     * (end)
441     *
442     * Examples (method two - single argument):
443     * (code)
444     * // create a map with non-default options
445     * var map = new OpenLayers.Map({
446     *     div: "map_id",
447     *     maxExtent: new OpenLayers.Bounds(-200000, -200000, 200000, 200000),
448     *     maxResolution: 156543,
449     *     units: 'm',
450     *     projection: "EPSG:41001"
451     * });
452     *
453     * // create a map without a reference to a container - call render later
454     * var map = new OpenLayers.Map({
455     *     maxExtent: new OpenLayers.Bounds(-200000, -200000, 200000, 200000),
456     *     maxResolution: 156543,
457     *     units: 'm',
458     *     projection: "EPSG:41001"
459     * });
460     */   
461    initialize: function (div, options) {
462       
463        // If only one argument is provided, check if it is an object.
464        if(arguments.length === 1 && typeof div === "object") {
465            options = div;
466            div = options && options.div;
467        }
468
469        // Simple-type defaults are set in class definition.
470        //  Now set complex-type defaults
471        this.tileSize = new OpenLayers.Size(OpenLayers.Map.TILE_WIDTH,
472                                            OpenLayers.Map.TILE_HEIGHT);
473       
474        this.maxExtent = new OpenLayers.Bounds(-180, -90, 180, 90);
475       
476        this.paddingForPopups = new OpenLayers.Bounds(15, 15, 15, 15);
477
478        this.theme = OpenLayers._getScriptLocation() + 
479                             'theme/default/style.css'; 
480
481        // now override default options
482        OpenLayers.Util.extend(this, options);
483
484        // initialize layers array
485        this.layers = [];
486
487        this.id = OpenLayers.Util.createUniqueID("OpenLayers.Map_");
488
489        this.div = OpenLayers.Util.getElement(div);
490        if(!this.div) {
491            this.div = document.createElement("div");
492            this.div.style.height = "1px";
493            this.div.style.width = "1px";
494        }
495       
496        OpenLayers.Element.addClass(this.div, 'olMap');
497
498        // the viewPortDiv is the outermost div we modify
499        var id = this.id + "_OpenLayers_ViewPort";
500        this.viewPortDiv = OpenLayers.Util.createDiv(id, null, null, null,
501                                                     "relative", null,
502                                                     "hidden");
503        this.viewPortDiv.style.width = "100%";
504        this.viewPortDiv.style.height = "100%";
505        this.viewPortDiv.className = "olMapViewport";
506        this.div.appendChild(this.viewPortDiv);
507
508        // the layerContainerDiv is the one that holds all the layers
509        id = this.id + "_OpenLayers_Container";
510        this.layerContainerDiv = OpenLayers.Util.createDiv(id);
511        this.layerContainerDiv.style.zIndex=this.Z_INDEX_BASE['Popup']-1;
512       
513        this.viewPortDiv.appendChild(this.layerContainerDiv);
514
515        this.events = new OpenLayers.Events(this, 
516                                            this.div, 
517                                            this.EVENT_TYPES, 
518                                            this.fallThrough, 
519                                            {includeXY: true});
520        this.updateSize();
521        if(this.eventListeners instanceof Object) {
522            this.events.on(this.eventListeners);
523        }
524 
525        // update the map size and location before the map moves
526        this.events.register("movestart", this, this.updateSize);
527
528        // Because Mozilla does not support the "resize" event for elements
529        // other than "window", we need to put a hack here.
530        if (OpenLayers.String.contains(navigator.appName, "Microsoft")) {
531            // If IE, register the resize on the div
532            this.events.register("resize", this, this.updateSize);
533        } else {
534            // Else updateSize on catching the window's resize
535            //  Note that this is ok, as updateSize() does nothing if the
536            //  map's size has not actually changed.
537            this.updateSizeDestroy = OpenLayers.Function.bind(this.updateSize, 
538                this);
539            OpenLayers.Event.observe(window, 'resize',
540                            this.updateSizeDestroy);
541        }
542       
543        // only append link stylesheet if the theme property is set
544        if(this.theme) {
545            // check existing links for equivalent url
546            var addNode = true;
547            var nodes = document.getElementsByTagName('link');
548            for(var i=0, len=nodes.length; i<len; ++i) {
549                if(OpenLayers.Util.isEquivalentUrl(nodes.item(i).href,
550                                                   this.theme)) {
551                    addNode = false;
552                    break;
553                }
554            }
555            // only add a new node if one with an equivalent url hasn't already
556            // been added
557            if(addNode) {
558                var cssNode = document.createElement('link');
559                cssNode.setAttribute('rel', 'stylesheet');
560                cssNode.setAttribute('type', 'text/css');
561                cssNode.setAttribute('href', this.theme);
562                document.getElementsByTagName('head')[0].appendChild(cssNode);
563            }
564        }
565       
566        if (this.controls == null) {
567            if (OpenLayers.Control != null) { // running full or lite?
568                this.controls = [ new OpenLayers.Control.Navigation(),
569                                  new OpenLayers.Control.PanZoom(),
570                                  new OpenLayers.Control.ArgParser(),
571                                  new OpenLayers.Control.Attribution()
572                                ];
573            } else {
574                this.controls = [];
575            }
576        }
577
578        for(var i=0, len=this.controls.length; i<len; i++) {
579            this.addControlToMap(this.controls[i]);
580        }
581
582        this.popups = [];
583
584        this.unloadDestroy = OpenLayers.Function.bind(this.destroy, this);
585       
586
587        // always call map.destroy()
588        OpenLayers.Event.observe(window, 'unload', this.unloadDestroy);
589       
590        // add any initial layers
591        if (options && options.layers) {
592            this.addLayers(options.layers);       
593            // set center (and optionally zoom)
594            if (options.center) {
595                // zoom can be undefined here
596                this.setCenter(options.center, options.zoom);
597            }
598        }
599    },
600   
601    /**
602     * APIMethod: render
603     * Render the map to a specified container.
604     *
605     * Parameters:
606     * div - {String|DOMElement} The container that the map should be rendered
607     *     to. If different than the current container, the map viewport
608     *     will be moved from the current to the new container.
609     */
610    render: function(div) {
611        this.div = OpenLayers.Util.getElement(div);
612        OpenLayers.Element.addClass(this.div, 'olMap');
613        this.events.attachToElement(this.div);
614        this.viewPortDiv.parentNode.removeChild(this.viewPortDiv);
615        this.div.appendChild(this.viewPortDiv);
616        this.updateSize();
617    },
618
619    /**
620     * Method: unloadDestroy
621     * Function that is called to destroy the map on page unload. stored here
622     *     so that if map is manually destroyed, we can unregister this.
623     */
624    unloadDestroy: null,
625   
626    /**
627     * Method: updateSizeDestroy
628     * When the map is destroyed, we need to stop listening to updateSize
629     *    events: this method stores the function we need to unregister in
630     *    non-IE browsers.
631     */
632    updateSizeDestroy: null,
633
634    /**
635     * APIMethod: destroy
636     * Destroy this map
637     */
638    destroy:function() {
639        // if unloadDestroy is null, we've already been destroyed
640        if (!this.unloadDestroy) {
641            return false;
642        }
643       
644        // make sure panning doesn't continue after destruction
645        if(this.panTween) {
646            this.panTween.stop();
647            this.panTween = null;
648        }
649
650        // map has been destroyed. dont do it again!
651        OpenLayers.Event.stopObserving(window, 'unload', this.unloadDestroy);
652        this.unloadDestroy = null;
653
654        if (this.updateSizeDestroy) {
655            OpenLayers.Event.stopObserving(window, 'resize', 
656                                           this.updateSizeDestroy);
657        } else {
658            this.events.unregister("resize", this, this.updateSize);
659        }   
660       
661        this.paddingForPopups = null;   
662
663        if (this.controls != null) {
664            for (var i = this.controls.length - 1; i>=0; --i) {
665                this.controls[i].destroy();
666            } 
667            this.controls = null;
668        }
669        if (this.layers != null) {
670            for (var i = this.layers.length - 1; i>=0; --i) {
671                //pass 'false' to destroy so that map wont try to set a new
672                // baselayer after each baselayer is removed
673                this.layers[i].destroy(false);
674            } 
675            this.layers = null;
676        }
677        if (this.viewPortDiv) {
678            this.div.removeChild(this.viewPortDiv);
679        }
680        this.viewPortDiv = null;
681
682        if(this.eventListeners) {
683            this.events.un(this.eventListeners);
684            this.eventListeners = null;
685        }
686        this.events.destroy();
687        this.events = null;
688
689    },
690
691    /**
692     * APIMethod: setOptions
693     * Change the map options
694     *
695     * Parameters:
696     * options - {Object} Hashtable of options to tag to the map
697     */
698    setOptions: function(options) {
699        OpenLayers.Util.extend(this, options);
700    },
701
702    /**
703     * APIMethod: getTileSize
704     * Get the tile size for the map
705     *
706     * Returns:
707     * {<OpenLayers.Size>}
708     */
709     getTileSize: function() {
710         return this.tileSize;
711     },
712
713
714    /**
715     * APIMethod: getBy
716     * Get a list of objects given a property and a match item.
717     *
718     * Parameters:
719     * array - {String} A property on the map whose value is an array.
720     * property - {String} A property on each item of the given array.
721     * match - {String | Object} A string to match.  Can also be a regular
722     *     expression literal or object.  In addition, it can be any object
723     *     with a method named test.  For reqular expressions or other, if
724     *     match.test(map[array][i][property]) evaluates to true, the item will
725     *     be included in the array returned.  If no items are found, an empty
726     *     array is returned.
727     *
728     * Returns:
729     * {Array} An array of items where the given property matches the given
730     *     criteria.
731     */
732    getBy: function(array, property, match) {
733        var test = (typeof match.test == "function");
734        var found = OpenLayers.Array.filter(this[array], function(item) {
735            return item[property] == match || (test && match.test(item[property]));
736        });
737        return found;
738    },
739
740    /**
741     * APIMethod: getLayersBy
742     * Get a list of layers with properties matching the given criteria.
743     *
744     * Parameter:
745     * property - {String} A layer property to be matched.
746     * match - {String | Object} A string to match.  Can also be a regular
747     *     expression literal or object.  In addition, it can be any object
748     *     with a method named test.  For reqular expressions or other, if
749     *     match.test(layer[property]) evaluates to true, the layer will be
750     *     included in the array returned.  If no layers are found, an empty
751     *     array is returned.
752     *
753     * Returns:
754     * {Array(<OpenLayers.Layer>)} A list of layers matching the given criteria.
755     *     An empty array is returned if no matches are found.
756     */
757    getLayersBy: function(property, match) {
758        return this.getBy("layers", property, match);
759    },
760
761    /**
762     * APIMethod: getLayersByName
763     * Get a list of layers with names matching the given name.
764     *
765     * Parameter:
766     * match - {String | Object} A layer name.  The name can also be a regular
767     *     expression literal or object.  In addition, it can be any object
768     *     with a method named test.  For reqular expressions or other, if
769     *     name.test(layer.name) evaluates to true, the layer will be included
770     *     in the list of layers returned.  If no layers are found, an empty
771     *     array is returned.
772     *
773     * Returns:
774     * {Array(<OpenLayers.Layer>)} A list of layers matching the given name.
775     *     An empty array is returned if no matches are found.
776     */
777    getLayersByName: function(match) {
778        return this.getLayersBy("name", match);
779    },
780
781    /**
782     * APIMethod: getLayersByClass
783     * Get a list of layers of a given class (CLASS_NAME).
784     *
785     * Parameter:
786     * match - {String | Object} A layer class name.  The match can also be a
787     *     regular expression literal or object.  In addition, it can be any
788     *     object with a method named test.  For reqular expressions or other,
789     *     if type.test(layer.CLASS_NAME) evaluates to true, the layer will
790     *     be included in the list of layers returned.  If no layers are
791     *     found, an empty array is returned.
792     *
793     * Returns:
794     * {Array(<OpenLayers.Layer>)} A list of layers matching the given class.
795     *     An empty array is returned if no matches are found.
796     */
797    getLayersByClass: function(match) {
798        return this.getLayersBy("CLASS_NAME", match);
799    },
800
801    /**
802     * APIMethod: getControlsBy
803     * Get a list of controls with properties matching the given criteria.
804     *
805     * Parameter:
806     * property - {String} A control property to be matched.
807     * match - {String | Object} A string to match.  Can also be a regular
808     *     expression literal or object.  In addition, it can be any object
809     *     with a method named test.  For reqular expressions or other, if
810     *     match.test(layer[property]) evaluates to true, the layer will be
811     *     included in the array returned.  If no layers are found, an empty
812     *     array is returned.
813     *
814     * Returns:
815     * {Array(<OpenLayers.Control>)} A list of controls matching the given
816     *     criteria.  An empty array is returned if no matches are found.
817     */
818    getControlsBy: function(property, match) {
819        return this.getBy("controls", property, match);
820    },
821
822    /**
823     * APIMethod: getControlsByClass
824     * Get a list of controls of a given class (CLASS_NAME).
825     *
826     * Parameter:
827     * match - {String | Object} A control class name.  The match can also be a
828     *     regular expression literal or object.  In addition, it can be any
829     *     object with a method named test.  For reqular expressions or other,
830     *     if type.test(control.CLASS_NAME) evaluates to true, the control will
831     *     be included in the list of controls returned.  If no controls are
832     *     found, an empty array is returned.
833     *
834     * Returns:
835     * {Array(<OpenLayers.Control>)} A list of controls matching the given class.
836     *     An empty array is returned if no matches are found.
837     */
838    getControlsByClass: function(match) {
839        return this.getControlsBy("CLASS_NAME", match);
840    },
841
842  /********************************************************/
843  /*                                                      */
844  /*                  Layer Functions                     */
845  /*                                                      */
846  /*     The following functions deal with adding and     */
847  /*        removing Layers to and from the Map           */
848  /*                                                      */
849  /********************************************************/         
850
851    /**
852     * APIMethod: getLayer
853     * Get a layer based on its id
854     *
855     * Parameter:
856     * id - {String} A layer id
857     *
858     * Returns:
859     * {<OpenLayers.Layer>} The Layer with the corresponding id from the map's
860     *                      layer collection, or null if not found.
861     */
862    getLayer: function(id) {
863        var foundLayer = null;
864        for (var i=0, len=this.layers.length; i<len; i++) {
865            var layer = this.layers[i];
866            if (layer.id == id) {
867                foundLayer = layer;
868                break;
869            }
870        }
871        return foundLayer;
872    },
873
874    /**
875    * Method: setLayerZIndex
876    *
877    * Parameters:
878    * layer - {<OpenLayers.Layer>}
879    * zIdx - {int}
880    */   
881    setLayerZIndex: function (layer, zIdx) {
882        layer.setZIndex(
883            this.Z_INDEX_BASE[layer.isBaseLayer ? 'BaseLayer' : 'Overlay']
884            + zIdx * 5 );
885    },
886
887    /**
888     * Method: resetLayersZIndex
889     * Reset each layer's z-index based on layer's array index
890     */
891    resetLayersZIndex: function() {
892        for (var i=0, len=this.layers.length; i<len; i++) {
893            var layer = this.layers[i];
894            this.setLayerZIndex(layer, i);
895        }
896    },
897
898    /**
899    * APIMethod: addLayer
900    *
901    * Parameters:
902    * layer - {<OpenLayers.Layer>}
903    */   
904    addLayer: function (layer) {
905        for(var i=0, len=this.layers.length; i <len; i++) {
906            if (this.layers[i] == layer) {
907                var msg = OpenLayers.i18n('layerAlreadyAdded', 
908                                                      {'layerName':layer.name});
909                OpenLayers.Console.warn(msg);
910                return false;
911            }
912        }
913        if(this.allOverlays) {
914            layer.isBaseLayer = false;
915        }
916
917        if (this.events.triggerEvent("preaddlayer", {layer: layer}) === false) {
918            return;
919        }
920       
921        layer.div.className = "olLayerDiv";
922        layer.div.style.overflow = "";
923        this.setLayerZIndex(layer, this.layers.length);
924
925        if (layer.isFixed) {
926            this.viewPortDiv.appendChild(layer.div);
927        } else {
928            this.layerContainerDiv.appendChild(layer.div);
929        }
930        this.layers.push(layer);
931        layer.setMap(this);
932
933        if (layer.isBaseLayer || (this.allOverlays && !this.baseLayer))  {
934            if (this.baseLayer == null) {
935                // set the first baselaye we add as the baselayer
936                this.setBaseLayer(layer);
937            } else {
938                layer.setVisibility(false);
939            }
940        } else {
941            layer.redraw();
942        }
943
944        this.events.triggerEvent("addlayer", {layer: layer});
945        layer.afterAdd();
946    },
947
948    /**
949    * APIMethod: addLayers
950    *
951    * Parameters:
952    * layers - {Array(<OpenLayers.Layer>)}
953    */   
954    addLayers: function (layers) {
955        for (var i=0, len=layers.length; i<len; i++) {
956            this.addLayer(layers[i]);
957        }
958    },
959
960    /**
961     * APIMethod: removeLayer
962     * Removes a layer from the map by removing its visual element (the
963     *   layer.div property), then removing it from the map's internal list
964     *   of layers, setting the layer's map property to null.
965     *
966     *   a "removelayer" event is triggered.
967     *
968     *   very worthy of mention is that simply removing a layer from a map
969     *   will not cause the removal of any popups which may have been created
970     *   by the layer. this is due to the fact that it was decided at some
971     *   point that popups would not belong to layers. thus there is no way
972     *   for us to know here to which layer the popup belongs.
973     *   
974     *     A simple solution to this is simply to call destroy() on the layer.
975     *     the default OpenLayers.Layer class's destroy() function
976     *     automatically takes care to remove itself from whatever map it has
977     *     been attached to.
978     *
979     *     The correct solution is for the layer itself to register an
980     *     event-handler on "removelayer" and when it is called, if it
981     *     recognizes itself as the layer being removed, then it cycles through
982     *     its own personal list of popups, removing them from the map.
983     *
984     * Parameters:
985     * layer - {<OpenLayers.Layer>}
986     * setNewBaseLayer - {Boolean} Default is true
987     */
988    removeLayer: function(layer, setNewBaseLayer) {
989        if (setNewBaseLayer == null) {
990            setNewBaseLayer = true;
991        }
992
993        if (layer.isFixed) {
994            this.viewPortDiv.removeChild(layer.div);
995        } else {
996            this.layerContainerDiv.removeChild(layer.div);
997        }
998        OpenLayers.Util.removeItem(this.layers, layer);
999        layer.removeMap(this);
1000        layer.map = null;
1001
1002        // if we removed the base layer, need to set a new one
1003        if(this.baseLayer == layer) {
1004            this.baseLayer = null;
1005            if(setNewBaseLayer) {
1006                for(var i=0, len=this.layers.length; i<len; i++) {
1007                    var iLayer = this.layers[i];
1008                    if (iLayer.isBaseLayer || this.allOverlays) {
1009                        this.setBaseLayer(iLayer);
1010                        break;
1011                    }
1012                }
1013            }
1014        }
1015
1016        this.resetLayersZIndex();
1017
1018        this.events.triggerEvent("removelayer", {layer: layer});
1019    },
1020
1021    /**
1022     * APIMethod: getNumLayers
1023     *
1024     * Returns:
1025     * {Int} The number of layers attached to the map.
1026     */
1027    getNumLayers: function () {
1028        return this.layers.length;
1029    },
1030
1031    /**
1032     * APIMethod: getLayerIndex
1033     *
1034     * Parameters:
1035     * layer - {<OpenLayers.Layer>}
1036     *
1037     * Returns:
1038     * {Integer} The current (zero-based) index of the given layer in the map's
1039     *           layer stack. Returns -1 if the layer isn't on the map.
1040     */
1041    getLayerIndex: function (layer) {
1042        return OpenLayers.Util.indexOf(this.layers, layer);
1043    },
1044   
1045    /**
1046     * APIMethod: setLayerIndex
1047     * Move the given layer to the specified (zero-based) index in the layer
1048     *     list, changing its z-index in the map display. Use
1049     *     map.getLayerIndex() to find out the current index of a layer. Note
1050     *     that this cannot (or at least should not) be effectively used to
1051     *     raise base layers above overlays.
1052     *
1053     * Parameters:
1054     * layer - {<OpenLayers.Layer>}
1055     * idx - {int}
1056     */
1057    setLayerIndex: function (layer, idx) {
1058        var base = this.getLayerIndex(layer);
1059        if (idx < 0) {
1060            idx = 0;
1061        } else if (idx > this.layers.length) {
1062            idx = this.layers.length;
1063        }
1064        if (base != idx) {
1065            this.layers.splice(base, 1);
1066            this.layers.splice(idx, 0, layer);
1067            for (var i=0, len=this.layers.length; i<len; i++) {
1068                this.setLayerZIndex(this.layers[i], i);
1069            }
1070            this.events.triggerEvent("changelayer", {
1071                layer: layer, property: "order"
1072            });
1073            if(this.allOverlays) {
1074                if(idx === 0) {
1075                    this.setBaseLayer(layer);
1076                } else if(this.baseLayer !== this.layers[0]) {
1077                    this.setBaseLayer(this.layers[0]);
1078                }
1079            }
1080        }
1081    },
1082
1083    /**
1084     * APIMethod: raiseLayer
1085     * Change the index of the given layer by delta. If delta is positive,
1086     *     the layer is moved up the map's layer stack; if delta is negative,
1087     *     the layer is moved down.  Again, note that this cannot (or at least
1088     *     should not) be effectively used to raise base layers above overlays.
1089     *
1090     * Paremeters:
1091     * layer - {<OpenLayers.Layer>}
1092     * delta - {int}
1093     */
1094    raiseLayer: function (layer, delta) {
1095        var idx = this.getLayerIndex(layer) + delta;
1096        this.setLayerIndex(layer, idx);
1097    },
1098   
1099    /**
1100     * APIMethod: setBaseLayer
1101     * Allows user to specify one of the currently-loaded layers as the Map's
1102     *     new base layer.
1103     *
1104     * Parameters:
1105     * newBaseLayer - {<OpenLayers.Layer>}
1106     */
1107    setBaseLayer: function(newBaseLayer) {
1108       
1109        if (newBaseLayer != this.baseLayer) {
1110         
1111            // ensure newBaseLayer is already loaded
1112            if (OpenLayers.Util.indexOf(this.layers, newBaseLayer) != -1) {
1113
1114                // preserve center and scale when changing base layers
1115                var center = this.getCenter();
1116                var newResolution = OpenLayers.Util.getResolutionFromScale(
1117                    this.getScale(), newBaseLayer.units
1118                );
1119
1120                // make the old base layer invisible
1121                if (this.baseLayer != null && !this.allOverlays) {
1122                    this.baseLayer.setVisibility(false);
1123                }
1124
1125                // set new baselayer
1126                this.baseLayer = newBaseLayer;
1127               
1128                // Increment viewRequestID since the baseLayer is
1129                // changing. This is used by tiles to check if they should
1130                // draw themselves.
1131                this.viewRequestID++;
1132                if(!this.allOverlays || this.baseLayer.visibility) {
1133                    this.baseLayer.setVisibility(true);
1134                }
1135
1136                // recenter the map
1137                if (center != null) {
1138                    // new zoom level derived from old scale
1139                    var newZoom = this.getZoomForResolution(
1140                        newResolution || this.resolution, true
1141                    );
1142                    // zoom and force zoom change
1143                    this.setCenter(center, newZoom, false, true);
1144                }
1145
1146                this.events.triggerEvent("changebaselayer", {
1147                    layer: this.baseLayer
1148                });
1149            }       
1150        }
1151    },
1152
1153
1154  /********************************************************/
1155  /*                                                      */
1156  /*                 Control Functions                    */
1157  /*                                                      */
1158  /*     The following functions deal with adding and     */
1159  /*        removing Controls to and from the Map         */
1160  /*                                                      */
1161  /********************************************************/         
1162
1163    /**
1164     * APIMethod: addControl
1165     * Add the passed over control to the map. Optionally
1166     *     position the control at the given pixel.
1167     *
1168     * Parameters:
1169     * control - {<OpenLayers.Control>}
1170     * px - {<OpenLayers.Pixel>}
1171     */   
1172    addControl: function (control, px) {
1173        this.controls.push(control);
1174        this.addControlToMap(control, px);
1175    },
1176   
1177    /**
1178     * APIMethod: addControls
1179     * Add all of the passed over controls to the map.
1180     *     You can pass over an optional second array
1181     *     with pixel-objects to position the controls.
1182     *     The indices of the two arrays should match and
1183     *     you can add null as pixel for those controls
1184     *     you want to be autopositioned.   
1185     *     
1186     * Parameters:
1187     * controls - {Array(<OpenLayers.Control>)}
1188     * pixels - {Array(<OpenLayers.Pixel>)}
1189     */   
1190    addControls: function (controls, pixels) {
1191        var pxs = (arguments.length === 1) ? [] : pixels;
1192        for (var i=0, len=controls.length; i<len; i++) {
1193            var ctrl = controls[i];
1194            var px = (pxs[i]) ? pxs[i] : null;
1195            this.addControl( ctrl, px );
1196        }
1197    },
1198
1199    /**
1200     * Method: addControlToMap
1201     *
1202     * Parameters:
1203     *
1204     * control - {<OpenLayers.Control>}
1205     * px - {<OpenLayers.Pixel>}
1206     */   
1207    addControlToMap: function (control, px) {
1208        // If a control doesn't have a div at this point, it belongs in the
1209        // viewport.
1210        control.outsideViewport = (control.div != null);
1211       
1212        // If the map has a displayProjection, and the control doesn't, set
1213        // the display projection.
1214        if (this.displayProjection && !control.displayProjection) {
1215            control.displayProjection = this.displayProjection;
1216        }   
1217       
1218        control.setMap(this);
1219        var div = control.draw(px);
1220        if (div) {
1221            if(!control.outsideViewport) {
1222                div.style.zIndex = this.Z_INDEX_BASE['Control'] +
1223                                    this.controls.length;
1224                this.viewPortDiv.appendChild( div );
1225            }
1226        }
1227        if(control.autoActivate) {
1228            control.activate();
1229        }
1230    },
1231   
1232    /**
1233     * APIMethod: getControl
1234     *
1235     * Parameters:
1236     * id - {String} ID of the control to return.
1237     *
1238     * Returns:
1239     * {<OpenLayers.Control>} The control from the map's list of controls
1240     *                        which has a matching 'id'. If none found,
1241     *                        returns null.
1242     */   
1243    getControl: function (id) {
1244        var returnControl = null;
1245        for(var i=0, len=this.controls.length; i<len; i++) {
1246            var control = this.controls[i];
1247            if (control.id == id) {
1248                returnControl = control;
1249                break;
1250            }
1251        }
1252        return returnControl;
1253    },
1254   
1255    /**
1256     * APIMethod: removeControl
1257     * Remove a control from the map. Removes the control both from the map
1258     *     object's internal array of controls, as well as from the map's
1259     *     viewPort (assuming the control was not added outsideViewport)
1260     *
1261     * Parameters:
1262     * control - {<OpenLayers.Control>} The control to remove.
1263     */   
1264    removeControl: function (control) {
1265        //make sure control is non-null and actually part of our map
1266        if ( (control) && (control == this.getControl(control.id)) ) {
1267            if (control.div && (control.div.parentNode == this.viewPortDiv)) {
1268                this.viewPortDiv.removeChild(control.div);
1269            }
1270            OpenLayers.Util.removeItem(this.controls, control);
1271        }
1272    },
1273
1274  /********************************************************/
1275  /*                                                      */
1276  /*                  Popup Functions                     */
1277  /*                                                      */
1278  /*     The following functions deal with adding and     */
1279  /*        removing Popups to and from the Map           */
1280  /*                                                      */
1281  /********************************************************/         
1282
1283    /**
1284     * APIMethod: addPopup
1285     *
1286     * Parameters:
1287     * popup - {<OpenLayers.Popup>}
1288     * exclusive - {Boolean} If true, closes all other popups first
1289     */
1290    addPopup: function(popup, exclusive) {
1291
1292        if (exclusive) {
1293            //remove all other popups from screen
1294            for (var i = this.popups.length - 1; i >= 0; --i) {
1295                this.removePopup(this.popups[i]);
1296            }
1297        }
1298
1299        popup.map = this;
1300        this.popups.push(popup);
1301        var popupDiv = popup.draw();
1302        if (popupDiv) {
1303            popupDiv.style.zIndex = this.Z_INDEX_BASE['Popup'] +
1304                                    this.popups.length;
1305            this.layerContainerDiv.appendChild(popupDiv);
1306        }
1307    },
1308   
1309    /**
1310    * APIMethod: removePopup
1311    *
1312    * Parameters:
1313    * popup - {<OpenLayers.Popup>}
1314    */
1315    removePopup: function(popup) {
1316        OpenLayers.Util.removeItem(this.popups, popup);
1317        if (popup.div) {
1318            try { this.layerContainerDiv.removeChild(popup.div); }
1319            catch (e) { } // Popups sometimes apparently get disconnected
1320                      // from the layerContainerDiv, and cause complaints.
1321        }
1322        popup.map = null;
1323    },
1324
1325  /********************************************************/
1326  /*                                                      */
1327  /*              Container Div Functions                 */
1328  /*                                                      */
1329  /*   The following functions deal with the access to    */
1330  /*    and maintenance of the size of the container div  */
1331  /*                                                      */
1332  /********************************************************/     
1333
1334    /**
1335     * APIMethod: getSize
1336     *
1337     * Returns:
1338     * {<OpenLayers.Size>} An <OpenLayers.Size> object that represents the
1339     *                     size, in pixels, of the div into which OpenLayers
1340     *                     has been loaded.
1341     *                     Note - A clone() of this locally cached variable is
1342     *                     returned, so as not to allow users to modify it.
1343     */
1344    getSize: function () {
1345        var size = null;
1346        if (this.size != null) {
1347            size = this.size.clone();
1348        }
1349        return size;
1350    },
1351
1352    /**
1353     * APIMethod: updateSize
1354     * This function should be called by any external code which dynamically
1355     *     changes the size of the map div (because mozilla wont let us catch
1356     *     the "onresize" for an element)
1357     */
1358    updateSize: function() {
1359        // the div might have moved on the page, also
1360        var newSize = this.getCurrentSize();
1361        if (newSize && !isNaN(newSize.h) && !isNaN(newSize.w)) {
1362            this.events.clearMouseCache();
1363            var oldSize = this.getSize();
1364            if (oldSize == null) {
1365                this.size = oldSize = newSize;
1366            }
1367            if (!newSize.equals(oldSize)) {
1368               
1369                // store the new size
1370                this.size = newSize;
1371   
1372                //notify layers of mapresize
1373                for(var i=0, len=this.layers.length; i<len; i++) {
1374                    this.layers[i].onMapResize();               
1375                }
1376   
1377                var center = this.getCenter();
1378   
1379                if (this.baseLayer != null && center != null) {
1380                    var zoom = this.getZoom();
1381                    this.zoom = null;
1382                    this.setCenter(center, zoom);
1383                }
1384   
1385            }
1386        }
1387    },
1388   
1389    /**
1390     * Method: getCurrentSize
1391     *
1392     * Returns:
1393     * {<OpenLayers.Size>} A new <OpenLayers.Size> object with the dimensions
1394     *                     of the map div
1395     */
1396    getCurrentSize: function() {
1397
1398        var size = new OpenLayers.Size(this.div.clientWidth, 
1399                                       this.div.clientHeight);
1400
1401        if (size.w == 0 && size.h == 0 || isNaN(size.w) && isNaN(size.h)) {
1402            size.w = this.div.offsetWidth;
1403            size.h = this.div.offsetHeight;
1404        }
1405        if (size.w == 0 && size.h == 0 || isNaN(size.w) && isNaN(size.h)) {
1406            size.w = parseInt(this.div.style.width);
1407            size.h = parseInt(this.div.style.height);
1408        }
1409        return size;
1410    },
1411
1412    /**
1413     * Method: calculateBounds
1414     *
1415     * Parameters:
1416     * center - {<OpenLayers.LonLat>} Default is this.getCenter()
1417     * resolution - {float} Default is this.getResolution()
1418     *
1419     * Returns:
1420     * {<OpenLayers.Bounds>} A bounds based on resolution, center, and
1421     *                       current mapsize.
1422     */
1423    calculateBounds: function(center, resolution) {
1424
1425        var extent = null;
1426       
1427        if (center == null) {
1428            center = this.getCenter();
1429        }               
1430        if (resolution == null) {
1431            resolution = this.getResolution();
1432        }
1433   
1434        if ((center != null) && (resolution != null)) {
1435
1436            var size = this.getSize();
1437            var w_deg = size.w * resolution;
1438            var h_deg = size.h * resolution;
1439       
1440            extent = new OpenLayers.Bounds(center.lon - w_deg / 2,
1441                                           center.lat - h_deg / 2,
1442                                           center.lon + w_deg / 2,
1443                                           center.lat + h_deg / 2);
1444       
1445        }
1446
1447        return extent;
1448    },
1449
1450
1451  /********************************************************/
1452  /*                                                      */
1453  /*            Zoom, Center, Pan Functions               */
1454  /*                                                      */
1455  /*    The following functions handle the validation,    */
1456  /*   getting and setting of the Zoom Level and Center   */
1457  /*       as well as the panning of the Map              */
1458  /*                                                      */
1459  /********************************************************/
1460    /**
1461     * APIMethod: getCenter
1462     *
1463     * Returns:
1464     * {<OpenLayers.LonLat>}
1465     */
1466    getCenter: function () {
1467        var center = null;
1468        if (this.center) {
1469            center = this.center.clone();
1470        }
1471        return center;
1472    },
1473
1474
1475    /**
1476     * APIMethod: getZoom
1477     *
1478     * Returns:
1479     * {Integer}
1480     */
1481    getZoom: function () {
1482        return this.zoom;
1483    },
1484   
1485    /**
1486     * APIMethod: pan
1487     * Allows user to pan by a value of screen pixels
1488     *
1489     * Parameters:
1490     * dx - {Integer}
1491     * dy - {Integer}
1492     * options - {Object} Options to configure panning:
1493     *  - *animate* {Boolean} Use panTo instead of setCenter. Default is true.
1494     *  - *dragging* {Boolean} Call setCenter with dragging true.  Default is
1495     *    false.
1496     */
1497    pan: function(dx, dy, options) {
1498        options = OpenLayers.Util.applyDefaults(options, {
1499            animate: true,
1500            dragging: false
1501        });
1502        // getCenter
1503        var centerPx = this.getViewPortPxFromLonLat(this.getCenter());
1504
1505        // adjust
1506        var newCenterPx = centerPx.add(dx, dy);
1507       
1508        // only call setCenter if not dragging or there has been a change
1509        if (!options.dragging || !newCenterPx.equals(centerPx)) {
1510            var newCenterLonLat = this.getLonLatFromViewPortPx(newCenterPx);
1511            if (options.animate) {
1512                this.panTo(newCenterLonLat);
1513            } else {
1514                this.setCenter(newCenterLonLat, null, options.dragging);
1515            }   
1516        }
1517
1518   },
1519   
1520   /**
1521     * APIMethod: panTo
1522     * Allows user to pan to a new lonlat
1523     * If the new lonlat is in the current extent the map will slide smoothly
1524     *
1525     * Parameters:
1526     * lonlat - {<OpenLayers.Lonlat>}
1527     */
1528    panTo: function(lonlat) {
1529        if (this.panMethod && this.getExtent().scale(this.panRatio).containsLonLat(lonlat)) {
1530            if (!this.panTween) {
1531                this.panTween = new OpenLayers.Tween(this.panMethod);
1532            }
1533            var center = this.getCenter();
1534
1535            // center will not change, don't do nothing
1536            if (lonlat.lon == center.lon &&
1537                lonlat.lat == center.lat) {
1538                return;
1539            }
1540
1541            var from = {
1542                lon: center.lon,
1543                lat: center.lat
1544            };
1545            var to = {
1546                lon: lonlat.lon,
1547                lat: lonlat.lat
1548            };
1549            this.panTween.start(from, to, this.panDuration, {
1550                callbacks: {
1551                    start: OpenLayers.Function.bind(function(lonlat) {
1552                        this.events.triggerEvent("movestart");
1553                    }, this),
1554                    eachStep: OpenLayers.Function.bind(function(lonlat) {
1555                        lonlat = new OpenLayers.LonLat(lonlat.lon, lonlat.lat);
1556                        this.moveTo(lonlat, this.zoom, {
1557                            'dragging': true,
1558                            'noEvent': true
1559                        });
1560                    }, this),
1561                    done: OpenLayers.Function.bind(function(lonlat) {
1562                        lonlat = new OpenLayers.LonLat(lonlat.lon, lonlat.lat);
1563                        this.moveTo(lonlat, this.zoom, {
1564                            'noEvent': true
1565                        });
1566                        this.events.triggerEvent("moveend");
1567                    }, this)
1568                }
1569            });
1570        } else {
1571            this.setCenter(lonlat);
1572        }
1573    },
1574
1575    /**
1576     * APIMethod: setCenter
1577     * Set the map center (and optionally, the zoom level).
1578     *
1579     * Parameters:
1580     * lonlat - {<OpenLayers.LonLat>} The new center location.
1581     * zoom - {Integer} Optional zoom level.
1582     * dragging - {Boolean} Specifies whether or not to trigger
1583     *                      movestart/end events
1584     * forceZoomChange - {Boolean} Specifies whether or not to trigger zoom
1585     *                             change events (needed on baseLayer change)
1586     *
1587     * TBD: reconsider forceZoomChange in 3.0
1588     */
1589    setCenter: function(lonlat, zoom, dragging, forceZoomChange) {
1590        this.moveTo(lonlat, zoom, {
1591            'dragging': dragging,
1592            'forceZoomChange': forceZoomChange,
1593            'caller': 'setCenter'
1594        });
1595    },
1596
1597    /**
1598     * Method: moveTo
1599     *
1600     * Parameters:
1601     * lonlat - {<OpenLayers.LonLat>}
1602     * zoom - {Integer}
1603     * options - {Object}
1604     */
1605    moveTo: function(lonlat, zoom, options) {
1606        if (!options) { 
1607            options = {};
1608        }
1609        if (zoom != null) {
1610            zoom = parseFloat(zoom);
1611            if (!this.fractionalZoom) {
1612                zoom = Math.round(zoom);
1613            }
1614        }
1615        // dragging is false by default
1616        var dragging = options.dragging;
1617        // forceZoomChange is false by default
1618        var forceZoomChange = options.forceZoomChange;
1619        // noEvent is false by default
1620        var noEvent = options.noEvent;
1621
1622        if (this.panTween && options.caller == "setCenter") {
1623            this.panTween.stop();
1624        }   
1625             
1626        if (!this.center && !this.isValidLonLat(lonlat)) {
1627            lonlat = this.maxExtent.getCenterLonLat();
1628        }
1629
1630        if(this.restrictedExtent != null) {
1631            // In 3.0, decide if we want to change interpretation of maxExtent.
1632            if(lonlat == null) { 
1633                lonlat = this.getCenter(); 
1634            }
1635            if(zoom == null) { 
1636                zoom = this.getZoom(); 
1637            }
1638            var resolution = this.getResolutionForZoom(zoom);
1639            var extent = this.calculateBounds(lonlat, resolution); 
1640            if(!this.restrictedExtent.containsBounds(extent)) {
1641                var maxCenter = this.restrictedExtent.getCenterLonLat(); 
1642                if(extent.getWidth() > this.restrictedExtent.getWidth()) { 
1643                    lonlat = new OpenLayers.LonLat(maxCenter.lon, lonlat.lat); 
1644                } else if(extent.left < this.restrictedExtent.left) {
1645                    lonlat = lonlat.add(this.restrictedExtent.left -
1646                                        extent.left, 0); 
1647                } else if(extent.right > this.restrictedExtent.right) { 
1648                    lonlat = lonlat.add(this.restrictedExtent.right -
1649                                        extent.right, 0); 
1650                } 
1651                if(extent.getHeight() > this.restrictedExtent.getHeight()) { 
1652                    lonlat = new OpenLayers.LonLat(lonlat.lon, maxCenter.lat); 
1653                } else if(extent.bottom < this.restrictedExtent.bottom) { 
1654                    lonlat = lonlat.add(0, this.restrictedExtent.bottom -
1655                                        extent.bottom); 
1656                } 
1657                else if(extent.top > this.restrictedExtent.top) { 
1658                    lonlat = lonlat.add(0, this.restrictedExtent.top -
1659                                        extent.top); 
1660                } 
1661            }
1662        }
1663       
1664        var zoomChanged = forceZoomChange || (
1665                            (this.isValidZoomLevel(zoom)) && 
1666                            (zoom != this.getZoom()) );
1667
1668        var centerChanged = (this.isValidLonLat(lonlat)) && 
1669                            (!lonlat.equals(this.center));
1670
1671
1672        // if neither center nor zoom will change, no need to do anything
1673        if (zoomChanged || centerChanged || !dragging) {
1674
1675            if (!this.dragging && !noEvent) {
1676                this.events.triggerEvent("movestart");
1677            }
1678
1679            if (centerChanged) {
1680                if ((!zoomChanged) && (this.center)) { 
1681                    // if zoom hasnt changed, just slide layerContainer
1682                    //  (must be done before setting this.center to new value)
1683                    this.centerLayerContainer(lonlat);
1684                }
1685                this.center = lonlat.clone();
1686            }
1687
1688            // (re)set the layerContainerDiv's location
1689            if ((zoomChanged) || (this.layerContainerOrigin == null)) {
1690                this.layerContainerOrigin = this.center.clone();
1691                this.layerContainerDiv.style.left = "0px";
1692                this.layerContainerDiv.style.top  = "0px";
1693            }
1694
1695            if (zoomChanged) {
1696                this.zoom = zoom;
1697                this.resolution = this.getResolutionForZoom(zoom);
1698                // zoom level has changed, increment viewRequestID.
1699                this.viewRequestID++;
1700            }   
1701           
1702            var bounds = this.getExtent();
1703           
1704            //send the move call to the baselayer and all the overlays   
1705
1706            if(this.baseLayer.visibility) {
1707                this.baseLayer.moveTo(bounds, zoomChanged, dragging);
1708                if(dragging) {
1709                    this.baseLayer.events.triggerEvent("move");
1710                } else {
1711                    this.baseLayer.events.triggerEvent("moveend",
1712                        {"zoomChanged": zoomChanged}
1713                    );
1714                }
1715            }
1716           
1717            bounds = this.baseLayer.getExtent();
1718           
1719            for (var i=0, len=this.layers.length; i<len; i++) {
1720                var layer = this.layers[i];
1721                if (layer !== this.baseLayer && !layer.isBaseLayer) {
1722                    var inRange = layer.calculateInRange();
1723                    if (layer.inRange != inRange) {
1724                        // the inRange property has changed. If the layer is
1725                        // no longer in range, we turn it off right away. If
1726                        // the layer is no longer out of range, the moveTo
1727                        // call below will turn on the layer.
1728                        layer.inRange = inRange;
1729                        if (!inRange) {
1730                            layer.display(false);
1731                        }
1732                        this.events.triggerEvent("changelayer", {
1733                            layer: layer, property: "visibility"
1734                        });
1735                    }
1736                    if (inRange && layer.visibility) {
1737                        layer.moveTo(bounds, zoomChanged, dragging);
1738                        if(dragging) {
1739                            layer.events.triggerEvent("move");
1740                        } else {
1741                            layer.events.triggerEvent("moveend",
1742                                {"zoomChanged": zoomChanged}
1743                            );
1744                        }
1745                    }
1746                }               
1747            }
1748           
1749            if (zoomChanged) {
1750                //redraw popups
1751                for (var i=0, len=this.popups.length; i<len; i++) {
1752                    this.popups[i].updatePosition();
1753                }
1754            }   
1755           
1756            this.events.triggerEvent("move");
1757   
1758            if (zoomChanged) { this.events.triggerEvent("zoomend"); }
1759        }
1760
1761        // even if nothing was done, we want to notify of this
1762        if (!dragging && !noEvent) {
1763            this.events.triggerEvent("moveend");
1764        }
1765       
1766        // Store the map dragging state for later use
1767        this.dragging = !!dragging; 
1768
1769    },
1770
1771    /**
1772     * Method: centerLayerContainer
1773     * This function takes care to recenter the layerContainerDiv.
1774     *
1775     * Parameters:
1776     * lonlat - {<OpenLayers.LonLat>}
1777     */
1778    centerLayerContainer: function (lonlat) {
1779
1780        var originPx = this.getViewPortPxFromLonLat(this.layerContainerOrigin);
1781        var newPx = this.getViewPortPxFromLonLat(lonlat);
1782
1783        if ((originPx != null) && (newPx != null)) {
1784            this.layerContainerDiv.style.left = Math.round(originPx.x - newPx.x) + "px";
1785            this.layerContainerDiv.style.top  = Math.round(originPx.y - newPx.y) + "px";
1786        }
1787    },
1788
1789    /**
1790     * Method: isValidZoomLevel
1791     *
1792     * Parameters:
1793     * zoomLevel - {Integer}
1794     *
1795     * Returns:
1796     * {Boolean} Whether or not the zoom level passed in is non-null and
1797     *           within the min/max range of zoom levels.
1798     */
1799    isValidZoomLevel: function(zoomLevel) {
1800       return ( (zoomLevel != null) &&
1801                (zoomLevel >= 0) && 
1802                (zoomLevel < this.getNumZoomLevels()) );
1803    },
1804   
1805    /**
1806     * Method: isValidLonLat
1807     *
1808     * Parameters:
1809     * lonlat - {<OpenLayers.LonLat>}
1810     *
1811     * Returns:
1812     * {Boolean} Whether or not the lonlat passed in is non-null and within
1813     *           the maxExtent bounds
1814     */
1815    isValidLonLat: function(lonlat) {
1816        var valid = false;
1817        if (lonlat != null) {
1818            var maxExtent = this.getMaxExtent();
1819            valid = maxExtent.containsLonLat(lonlat);       
1820        }
1821        return valid;
1822    },
1823
1824  /********************************************************/
1825  /*                                                      */
1826  /*                 Layer Options                        */
1827  /*                                                      */
1828  /*    Accessor functions to Layer Options parameters    */
1829  /*                                                      */
1830  /********************************************************/
1831   
1832    /**
1833     * APIMethod: getProjection
1834     * This method returns a string representing the projection. In
1835     *     the case of projection support, this will be the srsCode which
1836     *     is loaded -- otherwise it will simply be the string value that
1837     *     was passed to the projection at startup.
1838     *
1839     * FIXME: In 3.0, we will remove getProjectionObject, and instead
1840     *     return a Projection object from this function.
1841     *
1842     * Returns:
1843     * {String} The Projection string from the base layer or null.
1844     */
1845    getProjection: function() {
1846        var projection = this.getProjectionObject();
1847        return projection ? projection.getCode() : null;
1848    },
1849   
1850    /**
1851     * APIMethod: getProjectionObject
1852     * Returns the projection obect from the baselayer.
1853     *
1854     * Returns:
1855     * {<OpenLayers.Projection>} The Projection of the base layer.
1856     */
1857    getProjectionObject: function() {
1858        var projection = null;
1859        if (this.baseLayer != null) {
1860            projection = this.baseLayer.projection;
1861        }
1862        return projection;
1863    },
1864   
1865    /**
1866     * APIMethod: getMaxResolution
1867     *
1868     * Returns:
1869     * {String} The Map's Maximum Resolution
1870     */
1871    getMaxResolution: function() {
1872        var maxResolution = null;
1873        if (this.baseLayer != null) {
1874            maxResolution = this.baseLayer.maxResolution;
1875        }
1876        return maxResolution;
1877    },
1878       
1879    /**
1880     * APIMethod: getMaxExtent
1881     *
1882     * Parameters:
1883     * options - {Object}
1884     *
1885     * Allowed Options:
1886     * restricted - {Boolean} If true, returns restricted extent (if it is
1887     *     available.)
1888     *
1889     * Returns:
1890     * {<OpenLayers.Bounds>} The maxExtent property as set on the current
1891     *     baselayer, unless the 'restricted' option is set, in which case
1892     *     the 'restrictedExtent' option from the map is returned (if it
1893     *     is set).
1894     */
1895    getMaxExtent: function (options) {
1896        var maxExtent = null;
1897        if(options && options.restricted && this.restrictedExtent){
1898            maxExtent = this.restrictedExtent;
1899        } else if (this.baseLayer != null) {
1900            maxExtent = this.baseLayer.maxExtent;
1901        }       
1902        return maxExtent;
1903    },
1904   
1905    /**
1906     * APIMethod: getNumZoomLevels
1907     *
1908     * Returns:
1909     * {Integer} The total number of zoom levels that can be displayed by the
1910     *           current baseLayer.
1911     */
1912    getNumZoomLevels: function() {
1913        var numZoomLevels = null;
1914        if (this.baseLayer != null) {
1915            numZoomLevels = this.baseLayer.numZoomLevels;
1916        }
1917        return numZoomLevels;
1918    },
1919
1920  /********************************************************/
1921  /*                                                      */
1922  /*                 Baselayer Functions                  */
1923  /*                                                      */
1924  /*    The following functions, all publicly exposed     */
1925  /*       in the API?, are all merely wrappers to the    */
1926  /*       the same calls on whatever layer is set as     */
1927  /*                the current base layer                */
1928  /*                                                      */
1929  /********************************************************/
1930
1931    /**
1932     * APIMethod: getExtent
1933     *
1934     * Returns:
1935     * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat
1936     *                       bounds of the current viewPort.
1937     *                       If no baselayer is set, returns null.
1938     */
1939    getExtent: function () {
1940        var extent = null;
1941        if (this.baseLayer != null) {
1942            extent = this.baseLayer.getExtent();
1943        }
1944        return extent;
1945    },
1946
1947    /**
1948     * APIMethod: getResolution
1949     *
1950     * Returns:
1951     * {Float} The current resolution of the map.
1952     *         If no baselayer is set, returns null.
1953     */
1954    getResolution: function () {
1955        var resolution = null;
1956        if (this.baseLayer != null) {
1957            resolution = this.baseLayer.getResolution();
1958        } else if(this.allOverlays === true && this.layers.length > 0) {
1959            // while adding the 1st layer to the map in allOverlays mode,
1960            // this.baseLayer is not set yet when we need the resolution
1961            // for calculateInRange.
1962            resolution = this.layers[0].getResolution();
1963        }
1964        return resolution;
1965    },
1966
1967    /**
1968     * APIMethod: getUnits
1969     *
1970     * Returns:
1971     * {Float} The current units of the map.
1972     *         If no baselayer is set, returns null.
1973     */
1974    getUnits: function () {
1975        var units = null;
1976        if (this.baseLayer != null) {
1977            units = this.baseLayer.units;
1978        }
1979        return units;
1980    },
1981
1982     /**
1983      * APIMethod: getScale
1984      *
1985      * Returns:
1986      * {Float} The current scale denominator of the map.
1987      *         If no baselayer is set, returns null.
1988      */
1989    getScale: function () {
1990        var scale = null;
1991        if (this.baseLayer != null) {
1992            var res = this.getResolution();
1993            var units = this.baseLayer.units;
1994            scale = OpenLayers.Util.getScaleFromResolution(res, units);
1995        }
1996        return scale;
1997    },
1998
1999
2000    /**
2001     * APIMethod: getZoomForExtent
2002     *
2003     * Parameters:
2004     * bounds - {<OpenLayers.Bounds>}
2005     * closest - {Boolean} Find the zoom level that most closely fits the
2006     *     specified bounds. Note that this may result in a zoom that does
2007     *     not exactly contain the entire extent.
2008     *     Default is false.
2009     *
2010     * Returns:
2011     * {Integer} A suitable zoom level for the specified bounds.
2012     *           If no baselayer is set, returns null.
2013     */
2014    getZoomForExtent: function (bounds, closest) {
2015        var zoom = null;
2016        if (this.baseLayer != null) {
2017            zoom = this.baseLayer.getZoomForExtent(bounds, closest);
2018        }
2019        return zoom;
2020    },
2021
2022    /**
2023     * APIMethod: getResolutionForZoom
2024     *
2025     * Parameter:
2026     * zoom - {Float}
2027     *
2028     * Returns:
2029     * {Float} A suitable resolution for the specified zoom.  If no baselayer
2030     *     is set, returns null.
2031     */
2032    getResolutionForZoom: function(zoom) {
2033        var resolution = null;
2034        if(this.baseLayer) {
2035            resolution = this.baseLayer.getResolutionForZoom(zoom);
2036        }
2037        return resolution;
2038    },
2039
2040    /**
2041     * APIMethod: getZoomForResolution
2042     *
2043     * Parameter:
2044     * resolution - {Float}
2045     * closest - {Boolean} Find the zoom level that corresponds to the absolute
2046     *     closest resolution, which may result in a zoom whose corresponding
2047     *     resolution is actually smaller than we would have desired (if this
2048     *     is being called from a getZoomForExtent() call, then this means that
2049     *     the returned zoom index might not actually contain the entire
2050     *     extent specified... but it'll be close).
2051     *     Default is false.
2052     *
2053     * Returns:
2054     * {Integer} A suitable zoom level for the specified resolution.
2055     *           If no baselayer is set, returns null.
2056     */
2057    getZoomForResolution: function(resolution, closest) {
2058        var zoom = null;
2059        if (this.baseLayer != null) {
2060            zoom = this.baseLayer.getZoomForResolution(resolution, closest);
2061        }
2062        return zoom;
2063    },
2064
2065  /********************************************************/
2066  /*                                                      */
2067  /*                  Zooming Functions                   */
2068  /*                                                      */
2069  /*    The following functions, all publicly exposed     */
2070  /*       in the API, are all merely wrappers to the     */
2071  /*               the setCenter() function               */
2072  /*                                                      */
2073  /********************************************************/
2074 
2075    /**
2076     * APIMethod: zoomTo
2077     * Zoom to a specific zoom level
2078     *
2079     * Parameters:
2080     * zoom - {Integer}
2081     */
2082    zoomTo: function(zoom) {
2083        if (this.isValidZoomLevel(zoom)) {
2084            this.setCenter(null, zoom);
2085        }
2086    },
2087   
2088    /**
2089     * APIMethod: zoomIn
2090     *
2091     * Parameters:
2092     * zoom - {int}
2093     */
2094    zoomIn: function() {
2095        this.zoomTo(this.getZoom() + 1);
2096    },
2097   
2098    /**
2099     * APIMethod: zoomOut
2100     *
2101     * Parameters:
2102     * zoom - {int}
2103     */
2104    zoomOut: function() {
2105        this.zoomTo(this.getZoom() - 1);
2106    },
2107
2108    /**
2109     * APIMethod: zoomToExtent
2110     * Zoom to the passed in bounds, recenter
2111     *
2112     * Parameters:
2113     * bounds - {<OpenLayers.Bounds>}
2114     * closest - {Boolean} Find the zoom level that most closely fits the
2115     *     specified bounds. Note that this may result in a zoom that does
2116     *     not exactly contain the entire extent.
2117     *     Default is false.
2118     *
2119     */
2120    zoomToExtent: function(bounds, closest) {
2121        var center = bounds.getCenterLonLat();
2122        if (this.baseLayer.wrapDateLine) {
2123            var maxExtent = this.getMaxExtent();
2124
2125            //fix straddling bounds (in the case of a bbox that straddles the
2126            // dateline, it's left and right boundaries will appear backwards.
2127            // we fix this by allowing a right value that is greater than the
2128            // max value at the dateline -- this allows us to pass a valid
2129            // bounds to calculate zoom)
2130            //
2131            bounds = bounds.clone();
2132            while (bounds.right < bounds.left) {
2133                bounds.right += maxExtent.getWidth();
2134            }
2135            //if the bounds was straddling (see above), then the center point
2136            // we got from it was wrong. So we take our new bounds and ask it
2137            // for the center. Because our new bounds is at least partially
2138            // outside the bounds of maxExtent, the new calculated center
2139            // might also be. We don't want to pass a bad center value to
2140            // setCenter, so we have it wrap itself across the date line.
2141            //
2142            center = bounds.getCenterLonLat().wrapDateLine(maxExtent);
2143        }
2144        this.setCenter(center, this.getZoomForExtent(bounds, closest));
2145    },
2146
2147    /**
2148     * APIMethod: zoomToMaxExtent
2149     * Zoom to the full extent and recenter.
2150     *
2151     * Parameters:
2152     * options -
2153     *
2154     * Allowed Options:
2155     * restricted - {Boolean} True to zoom to restricted extent if it is
2156     *     set. Defaults to true.
2157     */
2158    zoomToMaxExtent: function(options) {
2159        //restricted is true by default
2160        var restricted = (options) ? options.restricted : true;
2161
2162        var maxExtent = this.getMaxExtent({
2163            'restricted': restricted 
2164        });
2165        this.zoomToExtent(maxExtent);
2166    },
2167
2168    /**
2169     * APIMethod: zoomToScale
2170     * Zoom to a specified scale
2171     *
2172     * Parameters:
2173     * scale - {float}
2174     * closest - {Boolean} Find the zoom level that most closely fits the
2175     *     specified scale. Note that this may result in a zoom that does
2176     *     not exactly contain the entire extent.
2177     *     Default is false.
2178     *
2179     */
2180    zoomToScale: function(scale, closest) {
2181        var res = OpenLayers.Util.getResolutionFromScale(scale, 
2182                                                         this.baseLayer.units);
2183        var size = this.getSize();
2184        var w_deg = size.w * res;
2185        var h_deg = size.h * res;
2186        var center = this.getCenter();
2187
2188        var extent = new OpenLayers.Bounds(center.lon - w_deg / 2,
2189                                           center.lat - h_deg / 2,
2190                                           center.lon + w_deg / 2,
2191                                           center.lat + h_deg / 2);
2192        this.zoomToExtent(extent, closest);
2193    },
2194   
2195  /********************************************************/
2196  /*                                                      */
2197  /*             Translation Functions                    */
2198  /*                                                      */
2199  /*      The following functions translate between       */
2200  /*           LonLat, LayerPx, and ViewPortPx            */
2201  /*                                                      */
2202  /********************************************************/
2203     
2204  //
2205  // TRANSLATION: LonLat <-> ViewPortPx
2206  //
2207
2208    /**
2209     * Method: getLonLatFromViewPortPx
2210     *
2211     * Parameters:
2212     * viewPortPx - {<OpenLayers.Pixel>}
2213     *
2214     * Returns:
2215     * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view
2216     *                       port <OpenLayers.Pixel>, translated into lon/lat
2217     *                       by the current base layer.
2218     */
2219    getLonLatFromViewPortPx: function (viewPortPx) {
2220        var lonlat = null; 
2221        if (this.baseLayer != null) {
2222            lonlat = this.baseLayer.getLonLatFromViewPortPx(viewPortPx);
2223        }
2224        return lonlat;
2225    },
2226
2227    /**
2228     * APIMethod: getViewPortPxFromLonLat
2229     *
2230     * Parameters:
2231     * lonlat - {<OpenLayers.LonLat>}
2232     *
2233     * Returns:
2234     * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in
2235     *                      <OpenLayers.LonLat>, translated into view port
2236     *                      pixels by the current base layer.
2237     */
2238    getViewPortPxFromLonLat: function (lonlat) {
2239        var px = null; 
2240        if (this.baseLayer != null) {
2241            px = this.baseLayer.getViewPortPxFromLonLat(lonlat);
2242        }
2243        return px;
2244    },
2245
2246   
2247  //
2248  // CONVENIENCE TRANSLATION FUNCTIONS FOR API
2249  //
2250
2251    /**
2252     * APIMethod: getLonLatFromPixel
2253     *
2254     * Parameters:
2255     * px - {<OpenLayers.Pixel>}
2256     *
2257     * Returns:
2258     * {<OpenLayers.LonLat>} An OpenLayers.LonLat corresponding to the given
2259     *                       OpenLayers.Pixel, translated into lon/lat by the
2260     *                       current base layer
2261     */
2262    getLonLatFromPixel: function (px) {
2263        return this.getLonLatFromViewPortPx(px);
2264    },
2265
2266    /**
2267     * APIMethod: getPixelFromLonLat
2268     * Returns a pixel location given a map location.  The map location is
2269     *     translated to an integer pixel location (in viewport pixel
2270     *     coordinates) by the current base layer.
2271     *
2272     * Parameters:
2273     * lonlat - {<OpenLayers.LonLat>} A map location.
2274     *
2275     * Returns:
2276     * {<OpenLayers.Pixel>} An OpenLayers.Pixel corresponding to the
2277     *     <OpenLayers.LonLat> translated into view port pixels by the current
2278     *     base layer.
2279     */
2280    getPixelFromLonLat: function (lonlat) {
2281        var px = this.getViewPortPxFromLonLat(lonlat);
2282        px.x = Math.round(px.x);
2283        px.y = Math.round(px.y);
2284        return px;
2285    },
2286   
2287    /**
2288     * Method: getGeodesicPixelSize
2289     *
2290     * Parameters:
2291     * px - {<OpenLayers.Pixel>} The pixel to get the geodesic length for. If
2292     *     not provided, the center pixel of the map viewport will be used.
2293     *
2294     * Returns:
2295     * {<OpenLayers.Size>} The geodesic size of the pixel in kilometers.
2296     */
2297    getGeodesicPixelSize: function(px) {
2298        var lonlat = px ? this.getLonLatFromPixel(px) : (this.getCenter() ||
2299            new OpenLayers.LonLat(0, 0));
2300        var res = this.getResolution();
2301        var left = lonlat.add(-res / 2, 0);
2302        var right = lonlat.add(res / 2, 0);
2303        var bottom = lonlat.add(0, -res / 2);
2304        var top = lonlat.add(0, res / 2);
2305        var dest = new OpenLayers.Projection("EPSG:4326");
2306        var source = this.getProjectionObject() || dest;
2307        if(!source.equals(dest)) {
2308            left.transform(source, dest);
2309            right.transform(source, dest);
2310            bottom.transform(source, dest);
2311            top.transform(source, dest);
2312        }
2313       
2314        return new OpenLayers.Size(
2315            OpenLayers.Util.distVincenty(left, right),
2316            OpenLayers.Util.distVincenty(bottom, top)
2317        );
2318    },
2319
2320
2321
2322  //
2323  // TRANSLATION: ViewPortPx <-> LayerPx
2324  //
2325
2326    /**
2327     * APIMethod: getViewPortPxFromLayerPx
2328     *
2329     * Parameters:
2330     * layerPx - {<OpenLayers.Pixel>}
2331     *
2332     * Returns:
2333     * {<OpenLayers.Pixel>} Layer Pixel translated into ViewPort Pixel
2334     *                      coordinates
2335     */
2336    getViewPortPxFromLayerPx:function(layerPx) {
2337        var viewPortPx = null;
2338        if (layerPx != null) {
2339            var dX = parseInt(this.layerContainerDiv.style.left);
2340            var dY = parseInt(this.layerContainerDiv.style.top);
2341            viewPortPx = layerPx.add(dX, dY);           
2342        }
2343        return viewPortPx;
2344    },
2345   
2346    /**
2347     * APIMethod: getLayerPxFromViewPortPx
2348     *
2349     * Parameters:
2350     * viewPortPx - {<OpenLayers.Pixel>}
2351     *
2352     * Returns:
2353     * {<OpenLayers.Pixel>} ViewPort Pixel translated into Layer Pixel
2354     *                      coordinates
2355     */
2356    getLayerPxFromViewPortPx:function(viewPortPx) {
2357        var layerPx = null;
2358        if (viewPortPx != null) {
2359            var dX = -parseInt(this.layerContainerDiv.style.left);
2360            var dY = -parseInt(this.layerContainerDiv.style.top);
2361            layerPx = viewPortPx.add(dX, dY);
2362            if (isNaN(layerPx.x) || isNaN(layerPx.y)) {
2363                layerPx = null;
2364            }
2365        }
2366        return layerPx;
2367    },
2368   
2369  //
2370  // TRANSLATION: LonLat <-> LayerPx
2371  //
2372
2373    /**
2374     * Method: getLonLatFromLayerPx
2375     *
2376     * Parameters:
2377     * px - {<OpenLayers.Pixel>}
2378     *
2379     * Returns:
2380     * {<OpenLayers.LonLat>}
2381     */
2382    getLonLatFromLayerPx: function (px) {
2383       //adjust for displacement of layerContainerDiv
2384       px = this.getViewPortPxFromLayerPx(px);
2385       return this.getLonLatFromViewPortPx(px);         
2386    },
2387   
2388    /**
2389     * APIMethod: getLayerPxFromLonLat
2390     *
2391     * Parameters:
2392     * lonlat - {<OpenLayers.LonLat>} lonlat
2393     *
2394     * Returns:
2395     * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in
2396     *                      <OpenLayers.LonLat>, translated into layer pixels
2397     *                      by the current base layer
2398     */
2399    getLayerPxFromLonLat: function (lonlat) {
2400       //adjust for displacement of layerContainerDiv
2401       var px = this.getPixelFromLonLat(lonlat);
2402       return this.getLayerPxFromViewPortPx(px);         
2403    },
2404
2405    CLASS_NAME: "OpenLayers.Map"
2406});
2407
2408/**
2409 * Constant: TILE_WIDTH
2410 * {Integer} 256 Default tile width (unless otherwise specified)
2411 */
2412OpenLayers.Map.TILE_WIDTH = 256;
2413/**
2414 * Constant: TILE_HEIGHT
2415 * {Integer} 256 Default tile height (unless otherwise specified)
2416 */
2417OpenLayers.Map.TILE_HEIGHT = 256;
Note: See TracBrowser for help on using the repository browser.