/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license. * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt * for the full text of the license. */ /** * @requires OpenLayers/Util.js * @requires OpenLayers/Events.js * * Class: OpenLayers.Map * Instances of OpenLayers.Map are interactive maps embedded in a web page. * Create a new map with the constructor. */ OpenLayers.Map = OpenLayers.Class.create(); /** * Constant: TILE_WIDTH * {Integer} 256 Default tile width (unless otherwise specified) */ OpenLayers.Map.TILE_WIDTH = 256; /** * Constant: TILE_HEIGHT * {Integer} 256 Default tile height (unless otherwise specified) */ OpenLayers.Map.TILE_HEIGHT = 256; OpenLayers.Map.prototype = { /** * Constant: Z_INDEX_BASE * {Object} Base z-indexes for different classes of thing */ Z_INDEX_BASE: { BaseLayer: 100, Overlay: 325, Popup: 750, Control: 1000 }, /** * Constant: EVENT_TYPES * {Array} supported application event types */ EVENT_TYPES: [ "addlayer", "removelayer", "changelayer", "movestart", "move", "moveend", "zoomend", "popupopen", "popupclose", "addmarker", "removemarker", "clearmarkers", "mouseover", "mouseout", "mousemove", "dragstart", "drag", "dragend", "changebaselayer"], /** * Property: id * {String} Unique identifier for the map */ id: null, /** * APIProperty: events * {} An events object that handles all * events on the map */ events: null, /** * APIProperty: div * {DOMElement} The element that contains the map */ div: null, /** * Property: size * {} Size of the main div (this.div) */ size: null, /** * Property: viewPortDiv * {HTMLDivElement} The element that represents the map viewport */ viewPortDiv: null, /** * Property: layerContainerOrigin * {} The lonlat at which the later container was * re-initialized (on-zoom) */ layerContainerOrigin: null, /** * Property: layerContainerDiv * {HTMLDivElement} The element that contains the layers. */ layerContainerDiv: null, /** * Property: layers * {Array()} Ordered list of layers in the map */ layers: null, /** * Property: controls * {Array()} List of controls associated with the map */ controls: null, /** * Property: popups * {Array()} List of popups associated with the map */ popups: null, /** * APIProperty: baseLayer * {} The currently selected base layer. This determines * min/max zoom level, projection, etc. */ baseLayer: null, /** * Property: center * {} The current center of the map */ center: null, /** * Property: zoom * {Integer} The current zoom level of the map */ zoom: 0, /** * Property: viewRequestID * {String} Used to store a unique identifier that changes when the map * view changes. viewRequestID should be used when adding data * asynchronously to the map: viewRequestID is incremented when * you initiate your request (right now during changing of * baselayers and changing of zooms). It is stored here in the * map and also in the data that will be coming back * asynchronously. Before displaying this data on request * completion, we check that the viewRequestID of the data is * still the same as that of the map. Fix for #480 */ viewRequestID: 0, // Options /** * APIProperty: tileSize * {} Set in the map options to override the default tile * size for this map. */ tileSize: null, /** * APIProperty: projection * {String} Set in the map options to override the default projection * string this map - also set maxExtent, maxResolution, and * units if appropriate. */ projection: "EPSG:4326", /** * APIProperty: units * {String} The map units. Defaults to 'degrees'. Possible values are * 'degrees' (or 'dd'), 'm', 'ft', 'km', 'mi', 'inches'. */ units: 'degrees', /** * APIProperty: resolutions * {Array} A list of map resolutions (map units per pixel) in descending * order. If this is not set in the layer constructor, it will be set * based on other resolution related properties (maxExtent, maxResolution, * maxScale, etc.). */ resolutions: null, /** * APIProperty: maxResolution * {Float} Default max is 360 deg / 256 px, which corresponds to * zoom level 0 on gmaps. Specify a different value in the map * options if you are not using a geographic projection and * displaying the whole world. */ maxResolution: 1.40625, /** * APIProperty: minResolution * {Float} */ minResolution: null, /** * APIProperty: maxScale * {Float} */ maxScale: null, /** * APIProperty: minScale * {Float} */ minScale: null, /** * APIProperty: maxExtent * {} The maximum extent for the map. Defaults to the * whole world in decimal degrees * (-180, -90, 180, 90). Specify a different * extent in the map options if you are not using a * geographic projection and displaying the whole * world. */ maxExtent: null, /** * APIProperty: minExtent * {} */ minExtent: null, /** * APIProperty: numZoomLevels * {Integer} Number of zoom levels for the map. Defaults to 16. Set a * different value in the map options if needed. */ numZoomLevels: 16, /** * APIProperty: theme * {String} Relative path to a CSS file from which to load theme styles. * Specify null in the map options (e.g. {theme: null}) if you * want to get cascading style declarations - by putting links to * stylesheets or style declarations directly in your page. */ theme: null, /** * APIProperty: fallThrough * {Boolean} Should OpenLayers allow events on the map to fall through to * other elements on the page, or should it swallow them? (#457) * Default is to swallow them. */ fallThrough: false, /** * Constructor: OpenLayers.Map * Constructor for a new OpenLayers.Map instance. * * Parameters: * div - {String} Id of an element in your page that will contain the map. * options - {Object} Optional object with properties to tag onto the map. * * Examples: * (code) * // create a map with default options in an element with the id "map1" * var map = new OpenLayers.Map("map1"); * * // create a map with non-default options in an element with id "map2" * var options = { * maxExtent: new OpenLayers.Bounds(-200000, -200000, 200000, 200000), * maxResolution: 156543, * units: 'meters', * projection: "EPSG:41001" * }; * var map = new OpenLayers.Map("map2", options); * (end) */ initialize: function (div, options) { //set the default options this.setOptions(options); this.id = OpenLayers.Util.createUniqueID("OpenLayers.Map_"); this.div = div = OpenLayers.Util.getElement(div); // the viewPortDiv is the outermost div we modify var id = div.id + "_OpenLayers_ViewPort"; this.viewPortDiv = OpenLayers.Util.createDiv(id, null, null, null, "relative", null, "hidden"); this.viewPortDiv.style.width = "100%"; this.viewPortDiv.style.height = "100%"; this.viewPortDiv.className = "olMapViewport"; this.div.appendChild(this.viewPortDiv); // the layerContainerDiv is the one that holds all the layers id = div.id + "_OpenLayers_Container"; this.layerContainerDiv = OpenLayers.Util.createDiv(id); this.layerContainerDiv.style.zIndex=this.Z_INDEX_BASE['Popup']-1; this.viewPortDiv.appendChild(this.layerContainerDiv); this.events = new OpenLayers.Events(this, div, this.EVENT_TYPES, this.fallThrough); this.updateSize(); // update the map size and location before the map moves this.events.register("movestart", this, this.updateSize); // Because Mozilla does not support the "resize" event for elements // other than "window", we need to put a hack here. if (navigator.appName.contains("Microsoft")) { // If IE, register the resize on the div this.events.register("resize", this, this.updateSize); } else { // Else updateSize on catching the window's resize // Note that this is ok, as updateSize() does nothing if the // map's size has not actually changed. OpenLayers.Event.observe(window, 'resize', this.updateSize.bindAsEventListener(this)); } // only append link stylesheet if the theme property is set if(this.theme) { // check existing links for equivalent url var addNode = true; var nodes = document.getElementsByTagName('link'); for(var i=0; i=0; --i) { //pass 'false' to destroy so that map wont try to set a new // baselayer after each baselayer is removed this.layers[i].destroy(false); } this.layers = null; } if (this.controls != null) { for (var i = this.controls.length - 1; i>=0; --i) { this.controls[i].destroy(); } this.controls = null; } if (this.viewPortDiv) { this.div.removeChild(this.viewPortDiv); } this.viewPortDiv = null; this.events.destroy(); this.events = null; }, /** * APIMethod: setOptions * Change the map options * * Parameters: * options - {Object} Hashtable of options to tag to the map */ setOptions: function(options) { // Simple-type defaults are set in class definition. // Now set complex-type defaults this.tileSize = new OpenLayers.Size(OpenLayers.Map.TILE_WIDTH, OpenLayers.Map.TILE_HEIGHT); this.maxExtent = new OpenLayers.Bounds(-180, -90, 180, 90); this.theme = OpenLayers._getScriptLocation() + 'theme/default/style.css'; // now add the options declared by the user // (these will override defaults) OpenLayers.Util.extend(this, options); }, /** * APIMethod: getTileSize * Get the tile size for the map * * Return: * {} */ getTileSize: function() { return this.tileSize; }, /********************************************************/ /* */ /* Layer Functions */ /* */ /* The following functions deal with adding and */ /* removing Layers to and from the Map */ /* */ /********************************************************/ /** * APIMethod: getLayer * Get a layer based on its id * * Parameter: * id - {String} A layer id * * Return: * {} The Layer with the corresponding id from the map's * layer collection, or null if not found. */ getLayer: function(id) { var foundLayer = null; for (var i = 0; i < this.layers.length; i++) { var layer = this.layers[i]; if (layer.id == id) { foundLayer = layer; } } return foundLayer; }, /** * Method: setLayerZIndex * * Parameters: * layer - {} * zIdx - {int} */ setLayerZIndex: function (layer, zIdx) { layer.setZIndex( this.Z_INDEX_BASE[layer.isBaseLayer ? 'BaseLayer' : 'Overlay'] + zIdx * 5 ); }, /** * APIMethod: addLayer * * Parameters: * layer - {} */ addLayer: function (layer) { for(var i=0; i < this.layers.length; i++) { if (this.layers[i] == layer) { var msg = "You tried to add the layer: " + layer.name + " to the map, but it has already been added"; OpenLayers.Console.warn(msg); return false; } } layer.div.style.overflow = ""; this.setLayerZIndex(layer, this.layers.length); if (layer.isFixed) { this.viewPortDiv.appendChild(layer.div); } else { this.layerContainerDiv.appendChild(layer.div); } this.layers.push(layer); layer.setMap(this); if (layer.isBaseLayer) { if (this.baseLayer == null) { // set the first baselaye we add as the baselayer this.setBaseLayer(layer); } else { layer.setVisibility(false); } } else { if (this.getCenter() != null) { layer.moveTo(this.getExtent(), true); } } this.events.triggerEvent("addlayer"); }, /** * APIMethod: addLayers * * Parameters: * layers - Array({}) */ addLayers: function (layers) { for (var i = 0; i < layers.length; i++) { this.addLayer(layers[i]); } }, /** * APIMethod: removeLayer * Removes a layer from the map by removing its visual element (the * layer.div property), then removing it from the map's internal list * of layers, setting the layer's map property to null. * * a "removelayer" event is triggered. * * very worthy of mention is that simply removing a layer from a map * will not cause the removal of any popups which may have been created * by the layer. this is due to the fact that it was decided at some * point that popups would not belong to layers. thus there is no way * for us to know here to which layer the popup belongs. * * A simple solution to this is simply to call destroy() on the layer. * the default OpenLayers.Layer class's destroy() function * automatically takes care to remove itself from whatever map it has * been attached to. * * The correct solution is for the layer itself to register an * event-handler on "removelayer" and when it is called, if it * recognizes itself as the layer being removed, then it cycles through * its own personal list of popups, removing them from the map. * * Parameters: * layer - {} * setNewBaseLayer - {Boolean} Default is true */ removeLayer: function(layer, setNewBaseLayer) { if (setNewBaseLayer == null) { setNewBaseLayer = true; } if (layer.isFixed) { this.viewPortDiv.removeChild(layer.div); } else { this.layerContainerDiv.removeChild(layer.div); } layer.map = null; OpenLayers.Util.removeItem(this.layers, layer); // if we removed the base layer, need to set a new one if (setNewBaseLayer && (this.baseLayer == layer)) { this.baseLayer = null; for(i=0; i < this.layers.length; i++) { var iLayer = this.layers[i]; if (iLayer.isBaseLayer) { this.setBaseLayer(iLayer); break; } } } this.events.triggerEvent("removelayer"); }, /** * APIMethod: getNumLayers * * Return: {Int} The number of layers attached to the map. */ getNumLayers: function () { return this.layers.length; }, /** * APIMethod: getLayerIndex * * Parameters: * layer - {} * * Return: * {Integer} The current (zero-based) index of the given layer in the map's * layer stack. Returns -1 if the layer isn't on the map. */ getLayerIndex: function (layer) { return OpenLayers.Util.indexOf(this.layers, layer); }, /** * APIMethod: setLayerIndex * Move the given layer to the specified (zero-based) index in the layer * list, changing its z-index in the map display. Use * map.getLayerIndex() to find out the current index of a layer. Note * that this cannot (or at least should not) be effectively used to * raise base layers above overlays. * * Parameters: * layer - {} * idx - {int} */ setLayerIndex: function (layer, idx) { var base = this.getLayerIndex(layer); if (idx < 0) idx = 0; else if (idx > this.layers.length) idx = this.layers.length; if (base != idx) { this.layers.splice(base, 1); this.layers.splice(idx, 0, layer); for (var i = 0; i < this.layers.length; i++) this.setLayerZIndex(this.layers[i], i); this.events.triggerEvent("changelayer"); } }, /** * APIMethod: raiseLayer * Change the index of the given layer by delta. If delta is positive, * the layer is moved up the map's layer stack; if delta is negative, * the layer is moved down. Again, note that this cannot (or at least * should not) be effectively used to raise base layers above overlays. * * Paremeters: * layer - {} * idx - {int} */ raiseLayer: function (layer, delta) { var idx = this.getLayerIndex(layer) + delta; this.setLayerIndex(layer, idx); }, /** * APIMethod: setBaseLayer * Allows user to specify one of the currently-loaded layers as the Map's * new base layer. * * Parameters: * newBaseLayer - {} * noEvent - {Boolean} */ setBaseLayer: function(newBaseLayer, noEvent) { var oldExtent = null; if(this.baseLayer) { oldExtent = this.baseLayer.getExtent(); } if (newBaseLayer != this.baseLayer) { // is newBaseLayer an already loaded layer?m if (OpenLayers.Util.indexOf(this.layers, newBaseLayer) != -1) { // make the old base layer invisible if (this.baseLayer != null) { this.baseLayer.setVisibility(false, noEvent); } // set new baselayer and make it visible this.baseLayer = newBaseLayer; // Increment viewRequestID since the baseLayer is // changing. This is used by tiles to check if they should // draw themselves. this.viewRequestID++; this.baseLayer.setVisibility(true, noEvent); //redraw all layers var center = this.getCenter(); if (center != null) { if (oldExtent == null) { // simply set center but force zoom change this.setCenter(center, this.getZoom(), false, true); } else { // zoom to oldExtent *and* force zoom change this.setCenter(oldExtent.getCenterLonLat(), this.getZoomForExtent(oldExtent), false, true); } } if ((noEvent == null) || (noEvent == false)) { this.events.triggerEvent("changebaselayer"); } } } }, /********************************************************/ /* */ /* Control Functions */ /* */ /* The following functions deal with adding and */ /* removing Controls to and from the Map */ /* */ /********************************************************/ /** * APIMethod: addControl * * Parameters: * control - {} * px - {} */ addControl: function (control, px) { this.controls.push(control); this.addControlToMap(control, px); }, /** * Method: addControlToMap * * Parameters: * * control - {} * px - {} */ addControlToMap: function (control, px) { // If a control doesn't have a div at this point, it belongs in the // viewport. control.outsideViewport = (control.div != null); control.setMap(this); var div = control.draw(px); if (div) { if(!control.outsideViewport) { div.style.zIndex = this.Z_INDEX_BASE['Control'] + this.controls.length; this.viewPortDiv.appendChild( div ); } } }, /** * APIMethod: getControl * * Parameters: * id - {String} ID of the control to return. * * Return: * {} The control from the map's list of controls * which has a matching 'id'. If none found, * returns null. */ getControl: function (id) { var returnControl = null; for(var i=0; i < this.controls.length; i++) { var control = this.controls[i]; if (control.id == id) { returnControl = control; break; } } return returnControl; }, /** * APIMethod: removeControl * Remove a control from the map. Removes the control both from the map * object's internal array of controls, as well as from the map's * viewPort (assuming the control was not added outsideViewport) * * Parameters: * control - {} The control to remove. */ removeControl: function (control) { //make sure control is non-null and actually part of our map if ( (control) && (control == this.getControl(control.id)) ) { if (!control.outsideViewport) { this.viewPortDiv.removeChild(control.div) } OpenLayers.Util.removeItem(this.controls, control); } }, /********************************************************/ /* */ /* Popup Functions */ /* */ /* The following functions deal with adding and */ /* removing Popups to and from the Map */ /* */ /********************************************************/ /** * APIMethod: addPopup * * Parameters: * popup - {} * exclusive - {Boolean} If true, closes all other popups first */ addPopup: function(popup, exclusive) { if (exclusive) { //remove all other popups from screen for(var i=0; i < this.popups.length; i++) { this.removePopup(this.popups[i]); } } popup.map = this; this.popups.push(popup); var popupDiv = popup.draw(); if (popupDiv) { popupDiv.style.zIndex = this.Z_INDEX_BASE['Popup'] + this.popups.length; this.layerContainerDiv.appendChild(popupDiv); } }, /** * APIMethod: removePopup * * Parameters: * popup - {} */ removePopup: function(popup) { OpenLayers.Util.removeItem(this.popups, popup); if (popup.div) { try { this.layerContainerDiv.removeChild(popup.div); } catch (e) { } // Popups sometimes apparently get disconnected // from the layerContainerDiv, and cause complaints. } popup.map = null; }, /********************************************************/ /* */ /* Container Div Functions */ /* */ /* The following functions deal with the access to */ /* and maintenance of the size of the container div */ /* */ /********************************************************/ /** * APIMethod: getSize * * Return: * {} An object that represents the * size, in pixels, of the div into which OpenLayers * has been loaded. * Note - A clone() of this locally cached variable is * returned, so as not to allow users to modify it. */ getSize: function () { var size = null; if (this.size != null) { size = this.size.clone(); } return size; }, /** * APIMethod: updateSize * This function should be called by any external code which dynamically * changes the size of the map div (because mozilla wont let us catch * the "onresize" for an element) */ updateSize: function() { // the div might have moved on the page, also this.events.element.offsets = null; var newSize = this.getCurrentSize(); var oldSize = this.getSize(); if (oldSize == null) this.size = oldSize = newSize; if (!newSize.equals(oldSize)) { // store the new size this.size = newSize; //notify layers of mapresize for(var i=0; i < this.layers.length; i++) { this.layers[i].onMapResize(); } if (this.baseLayer != null) { var center = new OpenLayers.Pixel(newSize.w /2, newSize.h / 2); var centerLL = this.getLonLatFromViewPortPx(center); var zoom = this.getZoom(); this.zoom = null; this.setCenter(this.getCenter(), zoom); } } }, /** * Method: getCurrentSize * * Return: * {} A new object with the dimensions * of the map div */ getCurrentSize: function() { var size = new OpenLayers.Size(this.div.clientWidth, this.div.clientHeight); // Workaround for the fact that hidden elements return 0 for size. if (size.w == 0 && size.h == 0 || isNaN(size.w) && isNaN(size.h)) { var dim = OpenLayers.Element.getDimensions(this.div); size.w = dim.width; size.h = dim.height; } if (size.w == 0 && size.h == 0 || isNaN(size.w) && isNaN(size.h)) { size.w = parseInt(this.div.style.width); size.h = parseInt(this.div.style.height); } return size; }, /** * Method: calculateBounds * * Parameters: * center - {} Default is this.getCenter() * resolution - {float} Default is this.getResolution() * * Return: * {} A bounds based on resolution, center, and * current mapsize. */ calculateBounds: function(center, resolution) { var extent = null; if (center == null) { center = this.getCenter(); } if (resolution == null) { resolution = this.getResolution(); } if ((center != null) && (resolution != null)) { var size = this.getSize(); var w_deg = size.w * resolution; var h_deg = size.h * resolution; extent = new OpenLayers.Bounds(center.lon - w_deg / 2, center.lat - h_deg / 2, center.lon + w_deg / 2, center.lat + h_deg / 2); } return extent; }, /********************************************************/ /* */ /* Zoom, Center, Pan Functions */ /* */ /* The following functions handle the validation, */ /* getting and setting of the Zoom Level and Center */ /* as well as the panning of the Map */ /* */ /********************************************************/ /** * APIMethod: getCenter * * Return: * {} */ getCenter: function () { return this.center; }, /** * APIMethod: getZoom * * Return: * {Integer} */ getZoom: function () { return this.zoom; }, /** * APIMethod: pan * Allows user to pan by a value of screen pixels * * Parameters: * dx - {Integer} * dy - {Integer} */ pan: function(dx, dy) { // getCenter var centerPx = this.getViewPortPxFromLonLat(this.getCenter()); // adjust var newCenterPx = centerPx.add(dx, dy); // only call setCenter if there has been a change if (!newCenterPx.equals(centerPx)) { var newCenterLonLat = this.getLonLatFromViewPortPx(newCenterPx); this.setCenter(newCenterLonLat); } }, /** * APIMethod: setCenter * * Parameters: * lonlat - {} * zoom - {Integer} * dragging - {Boolean} Specifies whether or not to trigger * movestart/end events * forceZoomChange - {Boolean} Specifies whether or not to trigger zoom * change events (needed on baseLayer change) * * TBD: reconsider forceZoomChange in 3.0 */ setCenter: function (lonlat, zoom, dragging, forceZoomChange) { if (!this.center && !this.isValidLonLat(lonlat)) { lonlat = this.maxExtent.getCenterLonLat(); } var zoomChanged = forceZoomChange || ( (this.isValidZoomLevel(zoom)) && (zoom != this.getZoom()) ); var centerChanged = (this.isValidLonLat(lonlat)) && (!lonlat.equals(this.center)); // if neither center nor zoom will change, no need to do anything if (zoomChanged || centerChanged || !dragging) { if (!dragging) { this.events.triggerEvent("movestart"); } if (centerChanged) { if ((!zoomChanged) && (this.center)) { // if zoom hasnt changed, just slide layerContainer // (must be done before setting this.center to new value) this.centerLayerContainer(lonlat); } this.center = lonlat.clone(); } // (re)set the layerContainerDiv's location if ((zoomChanged) || (this.layerContainerOrigin == null)) { this.layerContainerOrigin = this.center.clone(); this.layerContainerDiv.style.left = "0px"; this.layerContainerDiv.style.top = "0px"; } if (zoomChanged) { this.zoom = zoom; //redraw popups for (var i = 0; i < this.popups.length; i++) { this.popups[i].updatePosition(); } // zoom level has changed, increment viewRequestID. this.viewRequestID++; } var bounds = this.getExtent(); //send the move call to the baselayer and all the overlays this.baseLayer.moveTo(bounds, zoomChanged, dragging); for (var i = 0; i < this.layers.length; i++) { var layer = this.layers[i]; if (!layer.isBaseLayer) { var moveLayer; var inRange = layer.calculateInRange(); if (layer.inRange != inRange) { // Layer property has changed. We are going // to call moveLayer so that the layer can be turned // off or on. layer.inRange = inRange; moveLayer = true; this.events.triggerEvent("changelayer"); } else { // If nothing has changed, then we only move the layer // if it is visible and inrange. moveLayer = (layer.visibility && layer.inRange); } if (moveLayer) { layer.moveTo(bounds, zoomChanged, dragging); } } } this.events.triggerEvent("move"); if (zoomChanged) { this.events.triggerEvent("zoomend"); } } // even if nothing was done, we want to notify of this if (!dragging) { this.events.triggerEvent("moveend"); } }, /** * Method: centerLayerContainer * This function takes care to recenter the layerContainerDiv. * * Parameters: * lonlat - {} */ centerLayerContainer: function (lonlat) { var originPx = this.getViewPortPxFromLonLat(this.layerContainerOrigin); var newPx = this.getViewPortPxFromLonLat(lonlat); if ((originPx != null) && (newPx != null)) { this.layerContainerDiv.style.left = (originPx.x - newPx.x) + "px"; this.layerContainerDiv.style.top = (originPx.y - newPx.y) + "px"; } }, /** * Method: isValidZoomLevel * * Parameters: * zoomLevel - {Integer} * * Return: * {Boolean} Whether or not the zoom level passed in is non-null and * within the min/max range of zoom levels. */ isValidZoomLevel: function(zoomLevel) { return ( (zoomLevel != null) && (zoomLevel >= 0) && (zoomLevel < this.getNumZoomLevels()) ); }, /** * Method: isValidLonLat * * Parameters: * lonlat - {} * * Return: * {Boolean} Whether or not the lonlat passed in is non-null and within * the maxExtent bounds */ isValidLonLat: function(lonlat) { var valid = false; if (lonlat != null) { var maxExtent = this.getMaxExtent(); valid = maxExtent.containsLonLat(lonlat); } return valid; }, /********************************************************/ /* */ /* Layer Options */ /* */ /* Accessor functions to Layer Options parameters */ /* */ /********************************************************/ /** * APIMethod: getProjection * * Return: * {String} The Projection of the base layer. */ getProjection: function() { var projection = null; if (this.baseLayer != null) { projection = this.baseLayer.projection; } return projection; }, /** * APIMethod: getMaxResolution * * Return: * {String} The Map's Maximum Resolution */ getMaxResolution: function() { var maxResolution = null; if (this.baseLayer != null) { maxResolution = this.baseLayer.maxResolution; } return maxResolution; }, /** * APIMethod: getMaxExtent * * Return: * {} */ getMaxExtent: function () { var maxExtent = null; if (this.baseLayer != null) { maxExtent = this.baseLayer.maxExtent; } return maxExtent; }, /** * APIMethod: getNumZoomLevels * * Return: * {Integer} The total number of zoom levels that can be displayed by the * current baseLayer. */ getNumZoomLevels: function() { var numZoomLevels = null; if (this.baseLayer != null) { numZoomLevels = this.baseLayer.numZoomLevels; } return numZoomLevels; }, /********************************************************/ /* */ /* Baselayer Functions */ /* */ /* The following functions, all publicly exposed */ /* in the API?, are all merely wrappers to the */ /* the same calls on whatever layer is set as */ /* the current base layer */ /* */ /********************************************************/ /** * APIMethod: getExtent * * Return: * {} A Bounds object which represents the lon/lat * bounds of the current viewPort. * If no baselayer is set, returns null. */ getExtent: function () { var extent = null; if (this.baseLayer != null) { extent = this.baseLayer.getExtent(); } return extent; }, /** * APIMethod: getResolution * * Return: * {Float} The current resolution of the map. * If no baselayer is set, returns null. */ getResolution: function () { var resolution = null; if (this.baseLayer != null) { resolution = this.baseLayer.getResolution(); } return resolution; }, /** * APIMethod: getScale * * Return: * {Float} The current scale denominator of the map. * If no baselayer is set, returns null. */ getScale: function () { var scale = null; if (this.baseLayer != null) { var res = this.getResolution(); var units = this.baseLayer.units; scale = OpenLayers.Util.getScaleFromResolution(res, units); } return scale; }, /** * APIMethod: getZoomForExteng * * Parameters: * bounds - {} * * Return: * {Integer} A suitable zoom level for the specified bounds. * If no baselayer is set, returns null. */ getZoomForExtent: function (bounds) { var zoom = null; if (this.baseLayer != null) { zoom = this.baseLayer.getZoomForExtent(bounds); } return zoom; }, /** * APIMethod: getZoomForResolution * * Parameter: * resolution - {Float} * * Return: * {Integer} A suitable zoom level for the specified resolution. * If no baselayer is set, returns null. */ getZoomForResolution: function(resolution) { var zoom = null; if (this.baseLayer != null) { zoom = this.baseLayer.getZoomForResolution(resolution); } return zoom; }, /********************************************************/ /* */ /* Zooming Functions */ /* */ /* The following functions, all publicly exposed */ /* in the API, are all merely wrappers to the */ /* the setCenter() function */ /* */ /********************************************************/ /** * APIMethod: zoomTo * Zoom to a specific zoom level * * Parameters: * zoom - {Integer} */ zoomTo: function(zoom) { if (this.isValidZoomLevel(zoom)) { this.setCenter(null, zoom); } }, /** * APIMethod: zoomIn * * Parameters: * zoom - {int} */ zoomIn: function() { this.zoomTo(this.getZoom() + 1); }, /** * APIMethod: zoomOut * * Parameters: * zoom - {int} */ zoomOut: function() { this.zoomTo(this.getZoom() - 1); }, /** * APIMethod: zoomToExtent * Zoom to the passed in bounds, recenter * * Parameters: * bounds - {} */ zoomToExtent: function(bounds) { var center = bounds.getCenterLonLat(); if (this.baseLayer.wrapDateLine) { var maxExtent = this.getMaxExtent(); //fix straddling bounds (in the case of a bbox that straddles the // dateline, it's left and right boundaries will appear backwards. // we fix this by allowing a right value that is greater than the // max value at the dateline -- this allows us to pass a valid // bounds to calculate zoom) // bounds = bounds.clone(); while (bounds.right < bounds.left) { bounds.right += maxExtent.getWidth(); } //if the bounds was straddling (see above), then the center point // we got from it was wrong. So we take our new bounds and ask it // for the center. Because our new bounds is at least partially // outside the bounds of maxExtent, the new calculated center // might also be. We don't want to pass a bad center value to // setCenter, so we have it wrap itself across the date line. // center = bounds.getCenterLonLat().wrapDateLine(maxExtent); } this.setCenter(center, this.getZoomForExtent(bounds)); }, /** * APIMethod: zoomToMaxExtent * Zoom to the full extent and recenter. */ zoomToMaxExtent: function() { this.zoomToExtent(this.getMaxExtent()); }, /** * APIMethod: zoomToScale * Zoom to a specified scale * * Parameters: * scale - {float} */ zoomToScale: function(scale) { var res = OpenLayers.Util.getResolutionFromScale(scale, this.baseLayer.units); var size = this.getSize(); var w_deg = size.w * res; var h_deg = size.h * res; var center = this.getCenter(); var extent = new OpenLayers.Bounds(center.lon - w_deg / 2, center.lat - h_deg / 2, center.lon + w_deg / 2, center.lat + h_deg / 2); this.zoomToExtent(extent); }, /********************************************************/ /* */ /* Translation Functions */ /* */ /* The following functions translate between */ /* LonLat, LayerPx, and ViewPortPx */ /* */ /********************************************************/ // // TRANSLATION: LonLat <-> ViewPortPx // /** * APIMethod: getLonLatFromViewPortPx * * Parameters: * viewPortPx - {} * * Return: * {} An OpenLayers.LonLat which is the passed-in view * port , translated into lon/lat * by the current base layer. */ getLonLatFromViewPortPx: function (viewPortPx) { var lonlat = null; if (this.baseLayer != null) { lonlat = this.baseLayer.getLonLatFromViewPortPx(viewPortPx); } return lonlat; }, /** * APIMethod: getViewPortPxFromLonLat * * Parameters: * lonlat - {} * * Return: * {} An OpenLayers.Pixel which is the passed-in * , translated into view port * pixels by the current base layer. */ getViewPortPxFromLonLat: function (lonlat) { var px = null; if (this.baseLayer != null) { px = this.baseLayer.getViewPortPxFromLonLat(lonlat); } return px; }, // // CONVENIENCE TRANSLATION FUNCTIONS FOR API // /** * APIMethod: getLonLatFromPixel * * Parameters: * px - {} * * Return: * {} An OpenLayers.LonLat corresponding to the given * OpenLayers.Pixel, translated into lon/lat by the * current base layer */ getLonLatFromPixel: function (px) { return this.getLonLatFromViewPortPx(px); }, /** * APIMethod: getPixelFromLonLat * * Parameters: * lonlat - {} * * Return: * {} An OpenLayers.Pixel corresponding to the * translated into view port * pixels by the current base layer. */ getPixelFromLonLat: function (lonlat) { return this.getViewPortPxFromLonLat(lonlat); }, // // TRANSLATION: ViewPortPx <-> LayerPx // /** * APIMethod: getViewPortPxFromLayerPx * * Parameters: * layerPx - {} * * Return: * {} Layer Pixel translated into ViewPort Pixel * coordinates */ getViewPortPxFromLayerPx:function(layerPx) { var viewPortPx = null; if (layerPx != null) { var dX = parseInt(this.layerContainerDiv.style.left); var dY = parseInt(this.layerContainerDiv.style.top); viewPortPx = layerPx.add(dX, dY); } return viewPortPx; }, /** * APIMethod: getLayerPxFromViewPortPx * * Parameters: * viewPortPx - {} * * Return: * {} ViewPort Pixel translated into Layer Pixel * coordinates */ getLayerPxFromViewPortPx:function(viewPortPx) { var layerPx = null; if (viewPortPx != null) { var dX = -parseInt(this.layerContainerDiv.style.left); var dY = -parseInt(this.layerContainerDiv.style.top); layerPx = viewPortPx.add(dX, dY); if (isNaN(layerPx.x) || isNaN(layerPx.y)) { layerPx = null; } } return layerPx; }, // // TRANSLATION: LonLat <-> LayerPx // /** * APIMethod: getLonLatFromLayerPx * * Parameters: * px - {} * * Return: * {} */ getLonLatFromLayerPx: function (px) { //adjust for displacement of layerContainerDiv px = this.getViewPortPxFromLayerPx(px); return this.getLonLatFromViewPortPx(px); }, /** * APIMethod: getLayerPxFromLonLat * * Parameters: * lonlat - {} lonlat * * Return: * {} An OpenLayers.Pixel which is the passed-in * , translated into layer pixels * by the current base layer */ getLayerPxFromLonLat: function (lonlat) { //adjust for displacement of layerContainerDiv var px = this.getViewPortPxFromLonLat(lonlat); return this.getLayerPxFromViewPortPx(px); }, /** @final @type String */ CLASS_NAME: "OpenLayers.Map" };