git-svn-id: http://svn.openlayers.org/trunk/openlayers@10871 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf
2417 lines
82 KiB
JavaScript
2417 lines
82 KiB
JavaScript
/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for
|
|
* full list of contributors). Published under the Clear BSD license.
|
|
* See http://svn.openlayers.org/trunk/openlayers/license.txt for the
|
|
* full text of the license. */
|
|
|
|
/**
|
|
* @requires OpenLayers/Util.js
|
|
* @requires OpenLayers/Events.js
|
|
* @requires OpenLayers/Tween.js
|
|
* @requires OpenLayers/Console.js
|
|
*/
|
|
|
|
/**
|
|
* Class: OpenLayers.Map
|
|
* Instances of OpenLayers.Map are interactive maps embedded in a web page.
|
|
* Create a new map with the <OpenLayers.Map> constructor.
|
|
*
|
|
* On their own maps do not provide much functionality. To extend a map
|
|
* it's necessary to add controls (<OpenLayers.Control>) and
|
|
* layers (<OpenLayers.Layer>) to the map.
|
|
*/
|
|
OpenLayers.Map = OpenLayers.Class({
|
|
|
|
/**
|
|
* Constant: Z_INDEX_BASE
|
|
* {Object} Base z-indexes for different classes of thing
|
|
*/
|
|
Z_INDEX_BASE: {
|
|
BaseLayer: 100,
|
|
Overlay: 325,
|
|
Feature: 725,
|
|
Popup: 750,
|
|
Control: 1000
|
|
},
|
|
|
|
/**
|
|
* Constant: EVENT_TYPES
|
|
* {Array(String)} Supported application event types. Register a listener
|
|
* for a particular event with the following syntax:
|
|
* (code)
|
|
* map.events.register(type, obj, listener);
|
|
* (end)
|
|
*
|
|
* Listeners will be called with a reference to an event object. The
|
|
* properties of this event depends on exactly what happened.
|
|
*
|
|
* All event objects have at least the following properties:
|
|
* - *object* {Object} A reference to map.events.object.
|
|
* - *element* {DOMElement} A reference to map.events.element.
|
|
*
|
|
* Browser events have the following additional properties:
|
|
* - *xy* {<OpenLayers.Pixel>} The pixel location of the event (relative
|
|
* to the the map viewport).
|
|
* - other properties that come with browser events
|
|
*
|
|
* Supported map event types:
|
|
* - *preaddlayer* triggered before a layer has been added. The event
|
|
* object will include a *layer* property that references the layer
|
|
* to be added.
|
|
* - *addlayer* triggered after a layer has been added. The event object
|
|
* will include a *layer* property that references the added layer.
|
|
* - *removelayer* triggered after a layer has been removed. The event
|
|
* object will include a *layer* property that references the removed
|
|
* layer.
|
|
* - *changelayer* triggered after a layer name change, order change,
|
|
* opacity change, params change or visibility change
|
|
* (due to resolution thresholds). Listeners will receive an event
|
|
* object with *layer* and *property* properties. The *layer*
|
|
* property will be a reference to the changed layer.
|
|
* The *property* property will be a key to the
|
|
* changed property (name, order, opacity, params or visibility).
|
|
* - *movestart* triggered after the start of a drag, pan, or zoom
|
|
* - *move* triggered after each drag, pan, or zoom
|
|
* - *moveend* triggered after a drag, pan, or zoom completes
|
|
* - *zoomend* triggered after a zoom completes
|
|
* - *mouseover* triggered after mouseover the map
|
|
* - *mouseout* triggered after mouseout the map
|
|
* - *mousemove* triggered after mousemove the map
|
|
* - *changebaselayer* triggered after the base layer changes
|
|
*/
|
|
EVENT_TYPES: [
|
|
"preaddlayer", "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,
|
|
|
|
/**
|
|
* Property: fractionalZoom
|
|
* {Boolean} For a base layer that supports it, allow the map resolution
|
|
* to be set to a value between one of the values in the resolutions
|
|
* array. Default is false.
|
|
*
|
|
* When fractionalZoom is set to true, it is possible to zoom to
|
|
* an arbitrary extent. This requires a base layer from a source
|
|
* that supports requests for arbitrary extents (i.e. not cached
|
|
* tiles on a regular lattice). This means that fractionalZoom
|
|
* will not work with commercial layers (Google, Yahoo, VE), layers
|
|
* using TileCache, or any other pre-cached data sources.
|
|
*
|
|
* If you are using fractionalZoom, then you should also use
|
|
* <getResolutionForZoom> instead of layer.resolutions[zoom] as the
|
|
* former works for non-integer zoom levels.
|
|
*/
|
|
fractionalZoom: false,
|
|
|
|
/**
|
|
* APIProperty: events
|
|
* {<OpenLayers.Events>} An events object that handles all
|
|
* events on the map
|
|
*/
|
|
events: null,
|
|
|
|
/**
|
|
* APIProperty: allOverlays
|
|
* {Boolean} Allow the map to function with "overlays" only. Defaults to
|
|
* false. If true, the lowest layer in the draw order will act as
|
|
* the base layer. In addition, if set to true, all layers will
|
|
* have isBaseLayer set to false when they are added to the map.
|
|
*
|
|
* Note:
|
|
* If you set map.allOverlays to true, then you *cannot* use
|
|
* map.setBaseLayer or layer.setIsBaseLayer. With allOverlays true,
|
|
* the lowest layer in the draw layer is the base layer. So, to change
|
|
* the base layer, use <setLayerIndex> or <raiseLayer> to set the layer
|
|
* index to 0.
|
|
*/
|
|
allOverlays: false,
|
|
|
|
/**
|
|
* APIProperty: div
|
|
* {DOMElement|String} The element that contains the map (or an id for
|
|
* that element). If the <OpenLayers.Map> constructor is called
|
|
* with two arguments, this should be provided as the first argument.
|
|
* Alternatively, the map constructor can be called with the options
|
|
* object as the only argument. In this case (one argument), a
|
|
* div property may or may not be provided. If the div property
|
|
* is not provided, the map can be rendered to a container later
|
|
* using the <render> method.
|
|
*
|
|
* Note:
|
|
* If you are calling <render> after map construction, do not use
|
|
* <maxResolution> auto. Instead, divide your <maxExtent> by your
|
|
* maximum expected dimension.
|
|
*/
|
|
div: null,
|
|
|
|
/**
|
|
* Property: dragging
|
|
* {Boolean} The map is currently being dragged.
|
|
*/
|
|
dragging: false,
|
|
|
|
/**
|
|
* Property: size
|
|
* {<OpenLayers.Size>} Size of the main div (this.div)
|
|
*/
|
|
size: null,
|
|
|
|
/**
|
|
* Property: viewPortDiv
|
|
* {HTMLDivElement} The element that represents the map viewport
|
|
*/
|
|
viewPortDiv: null,
|
|
|
|
/**
|
|
* Property: layerContainerOrigin
|
|
* {<OpenLayers.LonLat>} 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,
|
|
|
|
/**
|
|
* APIProperty: layers
|
|
* {Array(<OpenLayers.Layer>)} Ordered list of layers in the map
|
|
*/
|
|
layers: null,
|
|
|
|
/**
|
|
* Property: controls
|
|
* {Array(<OpenLayers.Control>)} List of controls associated with the map.
|
|
*
|
|
* If not provided in the map options at construction, the map will
|
|
* be given the following controls by default:
|
|
* - <OpenLayers.Control.Navigation>
|
|
* - <OpenLayers.Control.PanZoom>
|
|
* - <OpenLayers.Control.ArgParser>
|
|
* - <OpenLayers.Control.Attribution>
|
|
*/
|
|
controls: null,
|
|
|
|
/**
|
|
* Property: popups
|
|
* {Array(<OpenLayers.Popup>)} List of popups associated with the map
|
|
*/
|
|
popups: null,
|
|
|
|
/**
|
|
* APIProperty: baseLayer
|
|
* {<OpenLayers.Layer>} The currently selected base layer. This determines
|
|
* min/max zoom level, projection, etc.
|
|
*/
|
|
baseLayer: null,
|
|
|
|
/**
|
|
* Property: center
|
|
* {<OpenLayers.LonLat>} The current center of the map
|
|
*/
|
|
center: null,
|
|
|
|
/**
|
|
* Property: resolution
|
|
* {Float} The resolution of the map.
|
|
*/
|
|
resolution: null,
|
|
|
|
/**
|
|
* Property: zoom
|
|
* {Integer} The current zoom level of the map
|
|
*/
|
|
zoom: 0,
|
|
|
|
/**
|
|
* Property: panRatio
|
|
* {Float} The ratio of the current extent within
|
|
* which panning will tween.
|
|
*/
|
|
panRatio: 1.5,
|
|
|
|
/**
|
|
* 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
|
|
* {<OpenLayers.Size>} 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. Default is "EPSG:4326".
|
|
*/
|
|
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(Float)} 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
|
|
* {<OpenLayers.Bounds>} 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
|
|
* {<OpenLayers.Bounds>}
|
|
*/
|
|
minExtent: null,
|
|
|
|
/**
|
|
* APIProperty: restrictedExtent
|
|
* {<OpenLayers.Bounds>} Limit map navigation to this extent where possible.
|
|
* If a non-null restrictedExtent is set, panning will be restricted
|
|
* to the given bounds. In addition, zooming to a resolution that
|
|
* displays more than the restricted extent will center the map
|
|
* on the restricted extent. If you wish to limit the zoom level
|
|
* or resolution, use maxResolution.
|
|
*/
|
|
restrictedExtent: 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: displayProjection
|
|
* {<OpenLayers.Projection>} Requires proj4js support.Projection used by
|
|
* several controls to display data to user. If this property is set,
|
|
* it will be set on any control which has a null displayProjection
|
|
* property at the time the control is added to the map.
|
|
*/
|
|
displayProjection: 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 fall through.
|
|
*/
|
|
fallThrough: true,
|
|
|
|
/**
|
|
* Property: panTween
|
|
* {OpenLayers.Tween} Animated panning tween object, see panTo()
|
|
*/
|
|
panTween: null,
|
|
|
|
/**
|
|
* APIProperty: eventListeners
|
|
* {Object} If set as an option at construction, the eventListeners
|
|
* object will be registered with <OpenLayers.Events.on>. Object
|
|
* structure must be a listeners object as shown in the example for
|
|
* the events.on method.
|
|
*/
|
|
eventListeners: null,
|
|
|
|
/**
|
|
* APIProperty: panMethod
|
|
* {Function} The Easing function to be used for tweening. Default is
|
|
* OpenLayers.Easing.Expo.easeOut. Setting this to 'null' turns off
|
|
* animated panning.
|
|
*/
|
|
panMethod: OpenLayers.Easing.Expo.easeOut,
|
|
|
|
/**
|
|
* Property: panDuration
|
|
* {Integer} The number of steps to be passed to the
|
|
* OpenLayers.Tween.start() method when the map is
|
|
* panned.
|
|
* Default is 50.
|
|
*/
|
|
panDuration: 50,
|
|
|
|
/**
|
|
* Property: paddingForPopups
|
|
* {<OpenLayers.Bounds>} Outside margin of the popup. Used to prevent
|
|
* the popup from getting too close to the map border.
|
|
*/
|
|
paddingForPopups : null,
|
|
|
|
/**
|
|
* Constructor: OpenLayers.Map
|
|
* Constructor for a new OpenLayers.Map instance. There are two possible
|
|
* ways to call the map constructor. See the examples below.
|
|
*
|
|
* Parameters:
|
|
* div - {DOMElement|String} The element or id of an element in your page
|
|
* that will contain the map. May be omitted if the <div> option is
|
|
* provided or if you intend to call the <render> method later.
|
|
* options - {Object} Optional object with properties to tag onto the map.
|
|
*
|
|
* Examples (method one):
|
|
* (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: 'm',
|
|
* projection: "EPSG:41001"
|
|
* };
|
|
* var map = new OpenLayers.Map("map2", options);
|
|
* (end)
|
|
*
|
|
* Examples (method two - single argument):
|
|
* (code)
|
|
* // create a map with non-default options
|
|
* var map = new OpenLayers.Map({
|
|
* div: "map_id",
|
|
* maxExtent: new OpenLayers.Bounds(-200000, -200000, 200000, 200000),
|
|
* maxResolution: 156543,
|
|
* units: 'm',
|
|
* projection: "EPSG:41001"
|
|
* });
|
|
*
|
|
* // create a map without a reference to a container - call render later
|
|
* var map = new OpenLayers.Map({
|
|
* maxExtent: new OpenLayers.Bounds(-200000, -200000, 200000, 200000),
|
|
* maxResolution: 156543,
|
|
* units: 'm',
|
|
* projection: "EPSG:41001"
|
|
* });
|
|
*/
|
|
initialize: function (div, options) {
|
|
|
|
// If only one argument is provided, check if it is an object.
|
|
if(arguments.length === 1 && typeof div === "object") {
|
|
options = div;
|
|
div = options && options.div;
|
|
}
|
|
|
|
// 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.paddingForPopups = new OpenLayers.Bounds(15, 15, 15, 15);
|
|
|
|
this.theme = OpenLayers._getScriptLocation() +
|
|
'theme/default/style.css';
|
|
|
|
// now override default options
|
|
OpenLayers.Util.extend(this, options);
|
|
|
|
// initialize layers array
|
|
this.layers = [];
|
|
|
|
this.id = OpenLayers.Util.createUniqueID("OpenLayers.Map_");
|
|
|
|
this.div = OpenLayers.Util.getElement(div);
|
|
if(!this.div) {
|
|
this.div = document.createElement("div");
|
|
this.div.style.height = "1px";
|
|
this.div.style.width = "1px";
|
|
}
|
|
|
|
OpenLayers.Element.addClass(this.div, 'olMap');
|
|
|
|
// the viewPortDiv is the outermost div we modify
|
|
var id = this.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 = this.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,
|
|
this.viewPortDiv,
|
|
this.EVENT_TYPES,
|
|
this.fallThrough,
|
|
{includeXY: true});
|
|
this.updateSize();
|
|
if(this.eventListeners instanceof Object) {
|
|
this.events.on(this.eventListeners);
|
|
}
|
|
|
|
// 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 (OpenLayers.String.contains(navigator.appName, "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.
|
|
this.updateSizeDestroy = OpenLayers.Function.bind(this.updateSize,
|
|
this);
|
|
OpenLayers.Event.observe(window, 'resize',
|
|
this.updateSizeDestroy);
|
|
}
|
|
|
|
// 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, len=nodes.length; i<len; ++i) {
|
|
if(OpenLayers.Util.isEquivalentUrl(nodes.item(i).href,
|
|
this.theme)) {
|
|
addNode = false;
|
|
break;
|
|
}
|
|
}
|
|
// only add a new node if one with an equivalent url hasn't already
|
|
// been added
|
|
if(addNode) {
|
|
var cssNode = document.createElement('link');
|
|
cssNode.setAttribute('rel', 'stylesheet');
|
|
cssNode.setAttribute('type', 'text/css');
|
|
cssNode.setAttribute('href', this.theme);
|
|
document.getElementsByTagName('head')[0].appendChild(cssNode);
|
|
}
|
|
}
|
|
|
|
if (this.controls == null) {
|
|
if (OpenLayers.Control != null) { // running full or lite?
|
|
this.controls = [ new OpenLayers.Control.Navigation(),
|
|
new OpenLayers.Control.PanZoom(),
|
|
new OpenLayers.Control.ArgParser(),
|
|
new OpenLayers.Control.Attribution()
|
|
];
|
|
} else {
|
|
this.controls = [];
|
|
}
|
|
}
|
|
|
|
for(var i=0, len=this.controls.length; i<len; i++) {
|
|
this.addControlToMap(this.controls[i]);
|
|
}
|
|
|
|
this.popups = [];
|
|
|
|
this.unloadDestroy = OpenLayers.Function.bind(this.destroy, this);
|
|
|
|
|
|
// always call map.destroy()
|
|
OpenLayers.Event.observe(window, 'unload', this.unloadDestroy);
|
|
|
|
// add any initial layers
|
|
if (options && options.layers) {
|
|
this.addLayers(options.layers);
|
|
// set center (and optionally zoom)
|
|
if (options.center) {
|
|
// zoom can be undefined here
|
|
this.setCenter(options.center, options.zoom);
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* APIMethod: render
|
|
* Render the map to a specified container.
|
|
*
|
|
* Parameters:
|
|
* div - {String|DOMElement} The container that the map should be rendered
|
|
* to. If different than the current container, the map viewport
|
|
* will be moved from the current to the new container.
|
|
*/
|
|
render: function(div) {
|
|
this.div = OpenLayers.Util.getElement(div);
|
|
OpenLayers.Element.addClass(this.div, 'olMap');
|
|
this.viewPortDiv.parentNode.removeChild(this.viewPortDiv);
|
|
this.div.appendChild(this.viewPortDiv);
|
|
this.updateSize();
|
|
},
|
|
|
|
/**
|
|
* Method: unloadDestroy
|
|
* Function that is called to destroy the map on page unload. stored here
|
|
* so that if map is manually destroyed, we can unregister this.
|
|
*/
|
|
unloadDestroy: null,
|
|
|
|
/**
|
|
* Method: updateSizeDestroy
|
|
* When the map is destroyed, we need to stop listening to updateSize
|
|
* events: this method stores the function we need to unregister in
|
|
* non-IE browsers.
|
|
*/
|
|
updateSizeDestroy: null,
|
|
|
|
/**
|
|
* APIMethod: destroy
|
|
* Destroy this map
|
|
*/
|
|
destroy:function() {
|
|
// if unloadDestroy is null, we've already been destroyed
|
|
if (!this.unloadDestroy) {
|
|
return false;
|
|
}
|
|
|
|
// make sure panning doesn't continue after destruction
|
|
if(this.panTween) {
|
|
this.panTween.stop();
|
|
this.panTween = null;
|
|
}
|
|
|
|
// map has been destroyed. dont do it again!
|
|
OpenLayers.Event.stopObserving(window, 'unload', this.unloadDestroy);
|
|
this.unloadDestroy = null;
|
|
|
|
if (this.updateSizeDestroy) {
|
|
OpenLayers.Event.stopObserving(window, 'resize',
|
|
this.updateSizeDestroy);
|
|
} else {
|
|
this.events.unregister("resize", this, this.updateSize);
|
|
}
|
|
|
|
this.paddingForPopups = null;
|
|
|
|
if (this.controls != null) {
|
|
for (var i = this.controls.length - 1; i>=0; --i) {
|
|
this.controls[i].destroy();
|
|
}
|
|
this.controls = null;
|
|
}
|
|
if (this.layers != null) {
|
|
for (var i = this.layers.length - 1; 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.viewPortDiv) {
|
|
this.div.removeChild(this.viewPortDiv);
|
|
}
|
|
this.viewPortDiv = null;
|
|
|
|
if(this.eventListeners) {
|
|
this.events.un(this.eventListeners);
|
|
this.eventListeners = 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) {
|
|
OpenLayers.Util.extend(this, options);
|
|
},
|
|
|
|
/**
|
|
* APIMethod: getTileSize
|
|
* Get the tile size for the map
|
|
*
|
|
* Returns:
|
|
* {<OpenLayers.Size>}
|
|
*/
|
|
getTileSize: function() {
|
|
return this.tileSize;
|
|
},
|
|
|
|
|
|
/**
|
|
* APIMethod: getBy
|
|
* Get a list of objects given a property and a match item.
|
|
*
|
|
* Parameters:
|
|
* array - {String} A property on the map whose value is an array.
|
|
* property - {String} A property on each item of the given array.
|
|
* match - {String | Object} A string to match. Can also be a regular
|
|
* expression literal or object. In addition, it can be any object
|
|
* with a method named test. For reqular expressions or other, if
|
|
* match.test(map[array][i][property]) evaluates to true, the item will
|
|
* be included in the array returned. If no items are found, an empty
|
|
* array is returned.
|
|
*
|
|
* Returns:
|
|
* {Array} An array of items where the given property matches the given
|
|
* criteria.
|
|
*/
|
|
getBy: function(array, property, match) {
|
|
var test = (typeof match.test == "function");
|
|
var found = OpenLayers.Array.filter(this[array], function(item) {
|
|
return item[property] == match || (test && match.test(item[property]));
|
|
});
|
|
return found;
|
|
},
|
|
|
|
/**
|
|
* APIMethod: getLayersBy
|
|
* Get a list of layers with properties matching the given criteria.
|
|
*
|
|
* Parameter:
|
|
* property - {String} A layer property to be matched.
|
|
* match - {String | Object} A string to match. Can also be a regular
|
|
* expression literal or object. In addition, it can be any object
|
|
* with a method named test. For reqular expressions or other, if
|
|
* match.test(layer[property]) evaluates to true, the layer will be
|
|
* included in the array returned. If no layers are found, an empty
|
|
* array is returned.
|
|
*
|
|
* Returns:
|
|
* {Array(<OpenLayers.Layer>)} A list of layers matching the given criteria.
|
|
* An empty array is returned if no matches are found.
|
|
*/
|
|
getLayersBy: function(property, match) {
|
|
return this.getBy("layers", property, match);
|
|
},
|
|
|
|
/**
|
|
* APIMethod: getLayersByName
|
|
* Get a list of layers with names matching the given name.
|
|
*
|
|
* Parameter:
|
|
* match - {String | Object} A layer name. The name can also be a regular
|
|
* expression literal or object. In addition, it can be any object
|
|
* with a method named test. For reqular expressions or other, if
|
|
* name.test(layer.name) evaluates to true, the layer will be included
|
|
* in the list of layers returned. If no layers are found, an empty
|
|
* array is returned.
|
|
*
|
|
* Returns:
|
|
* {Array(<OpenLayers.Layer>)} A list of layers matching the given name.
|
|
* An empty array is returned if no matches are found.
|
|
*/
|
|
getLayersByName: function(match) {
|
|
return this.getLayersBy("name", match);
|
|
},
|
|
|
|
/**
|
|
* APIMethod: getLayersByClass
|
|
* Get a list of layers of a given class (CLASS_NAME).
|
|
*
|
|
* Parameter:
|
|
* match - {String | Object} A layer class name. The match can also be a
|
|
* regular expression literal or object. In addition, it can be any
|
|
* object with a method named test. For reqular expressions or other,
|
|
* if type.test(layer.CLASS_NAME) evaluates to true, the layer will
|
|
* be included in the list of layers returned. If no layers are
|
|
* found, an empty array is returned.
|
|
*
|
|
* Returns:
|
|
* {Array(<OpenLayers.Layer>)} A list of layers matching the given class.
|
|
* An empty array is returned if no matches are found.
|
|
*/
|
|
getLayersByClass: function(match) {
|
|
return this.getLayersBy("CLASS_NAME", match);
|
|
},
|
|
|
|
/**
|
|
* APIMethod: getControlsBy
|
|
* Get a list of controls with properties matching the given criteria.
|
|
*
|
|
* Parameter:
|
|
* property - {String} A control property to be matched.
|
|
* match - {String | Object} A string to match. Can also be a regular
|
|
* expression literal or object. In addition, it can be any object
|
|
* with a method named test. For reqular expressions or other, if
|
|
* match.test(layer[property]) evaluates to true, the layer will be
|
|
* included in the array returned. If no layers are found, an empty
|
|
* array is returned.
|
|
*
|
|
* Returns:
|
|
* {Array(<OpenLayers.Control>)} A list of controls matching the given
|
|
* criteria. An empty array is returned if no matches are found.
|
|
*/
|
|
getControlsBy: function(property, match) {
|
|
return this.getBy("controls", property, match);
|
|
},
|
|
|
|
/**
|
|
* APIMethod: getControlsByClass
|
|
* Get a list of controls of a given class (CLASS_NAME).
|
|
*
|
|
* Parameter:
|
|
* match - {String | Object} A control class name. The match can also be a
|
|
* regular expression literal or object. In addition, it can be any
|
|
* object with a method named test. For reqular expressions or other,
|
|
* if type.test(control.CLASS_NAME) evaluates to true, the control will
|
|
* be included in the list of controls returned. If no controls are
|
|
* found, an empty array is returned.
|
|
*
|
|
* Returns:
|
|
* {Array(<OpenLayers.Control>)} A list of controls matching the given class.
|
|
* An empty array is returned if no matches are found.
|
|
*/
|
|
getControlsByClass: function(match) {
|
|
return this.getControlsBy("CLASS_NAME", match);
|
|
},
|
|
|
|
/********************************************************/
|
|
/* */
|
|
/* 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
|
|
*
|
|
* Returns:
|
|
* {<OpenLayers.Layer>} 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, len=this.layers.length; i<len; i++) {
|
|
var layer = this.layers[i];
|
|
if (layer.id == id) {
|
|
foundLayer = layer;
|
|
break;
|
|
}
|
|
}
|
|
return foundLayer;
|
|
},
|
|
|
|
/**
|
|
* Method: setLayerZIndex
|
|
*
|
|
* Parameters:
|
|
* layer - {<OpenLayers.Layer>}
|
|
* zIdx - {int}
|
|
*/
|
|
setLayerZIndex: function (layer, zIdx) {
|
|
layer.setZIndex(
|
|
this.Z_INDEX_BASE[layer.isBaseLayer ? 'BaseLayer' : 'Overlay']
|
|
+ zIdx * 5 );
|
|
},
|
|
|
|
/**
|
|
* Method: resetLayersZIndex
|
|
* Reset each layer's z-index based on layer's array index
|
|
*/
|
|
resetLayersZIndex: function() {
|
|
for (var i=0, len=this.layers.length; i<len; i++) {
|
|
var layer = this.layers[i];
|
|
this.setLayerZIndex(layer, i);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* APIMethod: addLayer
|
|
*
|
|
* Parameters:
|
|
* layer - {<OpenLayers.Layer>}
|
|
*/
|
|
addLayer: function (layer) {
|
|
for(var i=0, len=this.layers.length; i <len; i++) {
|
|
if (this.layers[i] == layer) {
|
|
var msg = OpenLayers.i18n('layerAlreadyAdded',
|
|
{'layerName':layer.name});
|
|
OpenLayers.Console.warn(msg);
|
|
return false;
|
|
}
|
|
}
|
|
if(this.allOverlays) {
|
|
layer.isBaseLayer = false;
|
|
}
|
|
|
|
if (this.events.triggerEvent("preaddlayer", {layer: layer}) === false) {
|
|
return;
|
|
}
|
|
|
|
layer.div.className = "olLayerDiv";
|
|
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 || (this.allOverlays && !this.baseLayer)) {
|
|
if (this.baseLayer == null) {
|
|
// set the first baselaye we add as the baselayer
|
|
this.setBaseLayer(layer);
|
|
} else {
|
|
layer.setVisibility(false);
|
|
}
|
|
} else {
|
|
layer.redraw();
|
|
}
|
|
|
|
this.events.triggerEvent("addlayer", {layer: layer});
|
|
layer.afterAdd();
|
|
},
|
|
|
|
/**
|
|
* APIMethod: addLayers
|
|
*
|
|
* Parameters:
|
|
* layers - {Array(<OpenLayers.Layer>)}
|
|
*/
|
|
addLayers: function (layers) {
|
|
for (var i=0, len=layers.length; i<len; 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 - {<OpenLayers.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);
|
|
}
|
|
OpenLayers.Util.removeItem(this.layers, layer);
|
|
layer.removeMap(this);
|
|
layer.map = null;
|
|
|
|
// if we removed the base layer, need to set a new one
|
|
if(this.baseLayer == layer) {
|
|
this.baseLayer = null;
|
|
if(setNewBaseLayer) {
|
|
for(var i=0, len=this.layers.length; i<len; i++) {
|
|
var iLayer = this.layers[i];
|
|
if (iLayer.isBaseLayer || this.allOverlays) {
|
|
this.setBaseLayer(iLayer);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
this.resetLayersZIndex();
|
|
|
|
this.events.triggerEvent("removelayer", {layer: layer});
|
|
},
|
|
|
|
/**
|
|
* APIMethod: getNumLayers
|
|
*
|
|
* Returns:
|
|
* {Int} The number of layers attached to the map.
|
|
*/
|
|
getNumLayers: function () {
|
|
return this.layers.length;
|
|
},
|
|
|
|
/**
|
|
* APIMethod: getLayerIndex
|
|
*
|
|
* Parameters:
|
|
* layer - {<OpenLayers.Layer>}
|
|
*
|
|
* Returns:
|
|
* {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 - {<OpenLayers.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, len=this.layers.length; i<len; i++) {
|
|
this.setLayerZIndex(this.layers[i], i);
|
|
}
|
|
this.events.triggerEvent("changelayer", {
|
|
layer: layer, property: "order"
|
|
});
|
|
if(this.allOverlays) {
|
|
if(idx === 0) {
|
|
this.setBaseLayer(layer);
|
|
} else if(this.baseLayer !== this.layers[0]) {
|
|
this.setBaseLayer(this.layers[0]);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 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 - {<OpenLayers.Layer>}
|
|
* delta - {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 - {<OpenLayers.Layer>}
|
|
*/
|
|
setBaseLayer: function(newBaseLayer) {
|
|
|
|
if (newBaseLayer != this.baseLayer) {
|
|
|
|
// ensure newBaseLayer is already loaded
|
|
if (OpenLayers.Util.indexOf(this.layers, newBaseLayer) != -1) {
|
|
|
|
// preserve center and scale when changing base layers
|
|
var center = this.getCenter();
|
|
var newResolution = OpenLayers.Util.getResolutionFromScale(
|
|
this.getScale(), newBaseLayer.units
|
|
);
|
|
|
|
// make the old base layer invisible
|
|
if (this.baseLayer != null && !this.allOverlays) {
|
|
this.baseLayer.setVisibility(false);
|
|
}
|
|
|
|
// set new baselayer
|
|
this.baseLayer = newBaseLayer;
|
|
|
|
// Increment viewRequestID since the baseLayer is
|
|
// changing. This is used by tiles to check if they should
|
|
// draw themselves.
|
|
this.viewRequestID++;
|
|
if(!this.allOverlays || this.baseLayer.visibility) {
|
|
this.baseLayer.setVisibility(true);
|
|
}
|
|
|
|
// recenter the map
|
|
if (center != null) {
|
|
// new zoom level derived from old scale
|
|
var newZoom = this.getZoomForResolution(
|
|
newResolution || this.resolution, true
|
|
);
|
|
// zoom and force zoom change
|
|
this.setCenter(center, newZoom, false, true);
|
|
}
|
|
|
|
this.events.triggerEvent("changebaselayer", {
|
|
layer: this.baseLayer
|
|
});
|
|
}
|
|
}
|
|
},
|
|
|
|
|
|
/********************************************************/
|
|
/* */
|
|
/* Control Functions */
|
|
/* */
|
|
/* The following functions deal with adding and */
|
|
/* removing Controls to and from the Map */
|
|
/* */
|
|
/********************************************************/
|
|
|
|
/**
|
|
* APIMethod: addControl
|
|
* Add the passed over control to the map. Optionally
|
|
* position the control at the given pixel.
|
|
*
|
|
* Parameters:
|
|
* control - {<OpenLayers.Control>}
|
|
* px - {<OpenLayers.Pixel>}
|
|
*/
|
|
addControl: function (control, px) {
|
|
this.controls.push(control);
|
|
this.addControlToMap(control, px);
|
|
},
|
|
|
|
/**
|
|
* APIMethod: addControls
|
|
* Add all of the passed over controls to the map.
|
|
* You can pass over an optional second array
|
|
* with pixel-objects to position the controls.
|
|
* The indices of the two arrays should match and
|
|
* you can add null as pixel for those controls
|
|
* you want to be autopositioned.
|
|
*
|
|
* Parameters:
|
|
* controls - {Array(<OpenLayers.Control>)}
|
|
* pixels - {Array(<OpenLayers.Pixel>)}
|
|
*/
|
|
addControls: function (controls, pixels) {
|
|
var pxs = (arguments.length === 1) ? [] : pixels;
|
|
for (var i=0, len=controls.length; i<len; i++) {
|
|
var ctrl = controls[i];
|
|
var px = (pxs[i]) ? pxs[i] : null;
|
|
this.addControl( ctrl, px );
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Method: addControlToMap
|
|
*
|
|
* Parameters:
|
|
*
|
|
* control - {<OpenLayers.Control>}
|
|
* px - {<OpenLayers.Pixel>}
|
|
*/
|
|
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);
|
|
|
|
// If the map has a displayProjection, and the control doesn't, set
|
|
// the display projection.
|
|
if (this.displayProjection && !control.displayProjection) {
|
|
control.displayProjection = this.displayProjection;
|
|
}
|
|
|
|
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 );
|
|
}
|
|
}
|
|
if(control.autoActivate) {
|
|
control.activate();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* APIMethod: getControl
|
|
*
|
|
* Parameters:
|
|
* id - {String} ID of the control to return.
|
|
*
|
|
* Returns:
|
|
* {<OpenLayers.Control>} 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, len=this.controls.length; i<len; 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 - {<OpenLayers.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.div && (control.div.parentNode == this.viewPortDiv)) {
|
|
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 - {<OpenLayers.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 = this.popups.length - 1; i >= 0; --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 - {<OpenLayers.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
|
|
*
|
|
* Returns:
|
|
* {<OpenLayers.Size>} An <OpenLayers.Size> 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
|
|
var newSize = this.getCurrentSize();
|
|
if (newSize && !isNaN(newSize.h) && !isNaN(newSize.w)) {
|
|
this.events.clearMouseCache();
|
|
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, len=this.layers.length; i<len; i++) {
|
|
this.layers[i].onMapResize();
|
|
}
|
|
|
|
var center = this.getCenter();
|
|
|
|
if (this.baseLayer != null && center != null) {
|
|
var zoom = this.getZoom();
|
|
this.zoom = null;
|
|
this.setCenter(center, zoom);
|
|
}
|
|
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Method: getCurrentSize
|
|
*
|
|
* Returns:
|
|
* {<OpenLayers.Size>} A new <OpenLayers.Size> object with the dimensions
|
|
* of the map div
|
|
*/
|
|
getCurrentSize: function() {
|
|
|
|
var size = new OpenLayers.Size(this.div.clientWidth,
|
|
this.div.clientHeight);
|
|
|
|
if (size.w == 0 && size.h == 0 || isNaN(size.w) && isNaN(size.h)) {
|
|
size.w = this.div.offsetWidth;
|
|
size.h = this.div.offsetHeight;
|
|
}
|
|
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 - {<OpenLayers.LonLat>} Default is this.getCenter()
|
|
* resolution - {float} Default is this.getResolution()
|
|
*
|
|
* Returns:
|
|
* {<OpenLayers.Bounds>} 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
|
|
*
|
|
* Returns:
|
|
* {<OpenLayers.LonLat>}
|
|
*/
|
|
getCenter: function () {
|
|
var center = null;
|
|
if (this.center) {
|
|
center = this.center.clone();
|
|
}
|
|
return center;
|
|
},
|
|
|
|
|
|
/**
|
|
* APIMethod: getZoom
|
|
*
|
|
* Returns:
|
|
* {Integer}
|
|
*/
|
|
getZoom: function () {
|
|
return this.zoom;
|
|
},
|
|
|
|
/**
|
|
* APIMethod: pan
|
|
* Allows user to pan by a value of screen pixels
|
|
*
|
|
* Parameters:
|
|
* dx - {Integer}
|
|
* dy - {Integer}
|
|
* options - {Object} Options to configure panning:
|
|
* - *animate* {Boolean} Use panTo instead of setCenter. Default is true.
|
|
* - *dragging* {Boolean} Call setCenter with dragging true. Default is
|
|
* false.
|
|
*/
|
|
pan: function(dx, dy, options) {
|
|
options = OpenLayers.Util.applyDefaults(options, {
|
|
animate: true,
|
|
dragging: false
|
|
});
|
|
// getCenter
|
|
var centerPx = this.getViewPortPxFromLonLat(this.getCenter());
|
|
|
|
// adjust
|
|
var newCenterPx = centerPx.add(dx, dy);
|
|
|
|
// only call setCenter if not dragging or there has been a change
|
|
if (!options.dragging || !newCenterPx.equals(centerPx)) {
|
|
var newCenterLonLat = this.getLonLatFromViewPortPx(newCenterPx);
|
|
if (options.animate) {
|
|
this.panTo(newCenterLonLat);
|
|
} else {
|
|
this.setCenter(newCenterLonLat, null, options.dragging);
|
|
}
|
|
}
|
|
|
|
},
|
|
|
|
/**
|
|
* APIMethod: panTo
|
|
* Allows user to pan to a new lonlat
|
|
* If the new lonlat is in the current extent the map will slide smoothly
|
|
*
|
|
* Parameters:
|
|
* lonlat - {<OpenLayers.Lonlat>}
|
|
*/
|
|
panTo: function(lonlat) {
|
|
if (this.panMethod && this.getExtent().scale(this.panRatio).containsLonLat(lonlat)) {
|
|
if (!this.panTween) {
|
|
this.panTween = new OpenLayers.Tween(this.panMethod);
|
|
}
|
|
var center = this.getCenter();
|
|
|
|
// center will not change, don't do nothing
|
|
if (lonlat.lon == center.lon &&
|
|
lonlat.lat == center.lat) {
|
|
return;
|
|
}
|
|
|
|
var from = {
|
|
lon: center.lon,
|
|
lat: center.lat
|
|
};
|
|
var to = {
|
|
lon: lonlat.lon,
|
|
lat: lonlat.lat
|
|
};
|
|
this.panTween.start(from, to, this.panDuration, {
|
|
callbacks: {
|
|
start: OpenLayers.Function.bind(function(lonlat) {
|
|
this.events.triggerEvent("movestart");
|
|
}, this),
|
|
eachStep: OpenLayers.Function.bind(function(lonlat) {
|
|
lonlat = new OpenLayers.LonLat(lonlat.lon, lonlat.lat);
|
|
this.moveTo(lonlat, this.zoom, {
|
|
'dragging': true,
|
|
'noEvent': true
|
|
});
|
|
}, this),
|
|
done: OpenLayers.Function.bind(function(lonlat) {
|
|
lonlat = new OpenLayers.LonLat(lonlat.lon, lonlat.lat);
|
|
this.moveTo(lonlat, this.zoom, {
|
|
'noEvent': true
|
|
});
|
|
this.events.triggerEvent("moveend");
|
|
}, this)
|
|
}
|
|
});
|
|
} else {
|
|
this.setCenter(lonlat);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* APIMethod: setCenter
|
|
* Set the map center (and optionally, the zoom level).
|
|
*
|
|
* Parameters:
|
|
* lonlat - {<OpenLayers.LonLat>} The new center location.
|
|
* zoom - {Integer} Optional zoom level.
|
|
* 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) {
|
|
this.moveTo(lonlat, zoom, {
|
|
'dragging': dragging,
|
|
'forceZoomChange': forceZoomChange,
|
|
'caller': 'setCenter'
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Method: moveTo
|
|
*
|
|
* Parameters:
|
|
* lonlat - {<OpenLayers.LonLat>}
|
|
* zoom - {Integer}
|
|
* options - {Object}
|
|
*/
|
|
moveTo: function(lonlat, zoom, options) {
|
|
if (!options) {
|
|
options = {};
|
|
}
|
|
if (zoom != null) {
|
|
zoom = parseFloat(zoom);
|
|
if (!this.fractionalZoom) {
|
|
zoom = Math.round(zoom);
|
|
}
|
|
}
|
|
// dragging is false by default
|
|
var dragging = options.dragging;
|
|
// forceZoomChange is false by default
|
|
var forceZoomChange = options.forceZoomChange;
|
|
// noEvent is false by default
|
|
var noEvent = options.noEvent;
|
|
|
|
if (this.panTween && options.caller == "setCenter") {
|
|
this.panTween.stop();
|
|
}
|
|
|
|
if (!this.center && !this.isValidLonLat(lonlat)) {
|
|
lonlat = this.maxExtent.getCenterLonLat();
|
|
}
|
|
|
|
if(this.restrictedExtent != null) {
|
|
// In 3.0, decide if we want to change interpretation of maxExtent.
|
|
if(lonlat == null) {
|
|
lonlat = this.getCenter();
|
|
}
|
|
if(zoom == null) {
|
|
zoom = this.getZoom();
|
|
}
|
|
var resolution = this.getResolutionForZoom(zoom);
|
|
var extent = this.calculateBounds(lonlat, resolution);
|
|
if(!this.restrictedExtent.containsBounds(extent)) {
|
|
var maxCenter = this.restrictedExtent.getCenterLonLat();
|
|
if(extent.getWidth() > this.restrictedExtent.getWidth()) {
|
|
lonlat = new OpenLayers.LonLat(maxCenter.lon, lonlat.lat);
|
|
} else if(extent.left < this.restrictedExtent.left) {
|
|
lonlat = lonlat.add(this.restrictedExtent.left -
|
|
extent.left, 0);
|
|
} else if(extent.right > this.restrictedExtent.right) {
|
|
lonlat = lonlat.add(this.restrictedExtent.right -
|
|
extent.right, 0);
|
|
}
|
|
if(extent.getHeight() > this.restrictedExtent.getHeight()) {
|
|
lonlat = new OpenLayers.LonLat(lonlat.lon, maxCenter.lat);
|
|
} else if(extent.bottom < this.restrictedExtent.bottom) {
|
|
lonlat = lonlat.add(0, this.restrictedExtent.bottom -
|
|
extent.bottom);
|
|
}
|
|
else if(extent.top > this.restrictedExtent.top) {
|
|
lonlat = lonlat.add(0, this.restrictedExtent.top -
|
|
extent.top);
|
|
}
|
|
}
|
|
}
|
|
|
|
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 (!this.dragging && !noEvent) {
|
|
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;
|
|
this.resolution = this.getResolutionForZoom(zoom);
|
|
// zoom level has changed, increment viewRequestID.
|
|
this.viewRequestID++;
|
|
}
|
|
|
|
var bounds = this.getExtent();
|
|
|
|
//send the move call to the baselayer and all the overlays
|
|
|
|
if(this.baseLayer.visibility) {
|
|
this.baseLayer.moveTo(bounds, zoomChanged, dragging);
|
|
if(dragging) {
|
|
this.baseLayer.events.triggerEvent("move");
|
|
} else {
|
|
this.baseLayer.events.triggerEvent("moveend",
|
|
{"zoomChanged": zoomChanged}
|
|
);
|
|
}
|
|
}
|
|
|
|
bounds = this.baseLayer.getExtent();
|
|
|
|
for (var i=0, len=this.layers.length; i<len; i++) {
|
|
var layer = this.layers[i];
|
|
if (layer !== this.baseLayer && !layer.isBaseLayer) {
|
|
var inRange = layer.calculateInRange();
|
|
if (layer.inRange != inRange) {
|
|
// the inRange property has changed. If the layer is
|
|
// no longer in range, we turn it off right away. If
|
|
// the layer is no longer out of range, the moveTo
|
|
// call below will turn on the layer.
|
|
layer.inRange = inRange;
|
|
if (!inRange) {
|
|
layer.display(false);
|
|
}
|
|
this.events.triggerEvent("changelayer", {
|
|
layer: layer, property: "visibility"
|
|
});
|
|
}
|
|
if (inRange && layer.visibility) {
|
|
layer.moveTo(bounds, zoomChanged, dragging);
|
|
if(dragging) {
|
|
layer.events.triggerEvent("move");
|
|
} else {
|
|
layer.events.triggerEvent("moveend",
|
|
{"zoomChanged": zoomChanged}
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (zoomChanged) {
|
|
//redraw popups
|
|
for (var i=0, len=this.popups.length; i<len; i++) {
|
|
this.popups[i].updatePosition();
|
|
}
|
|
}
|
|
|
|
this.events.triggerEvent("move");
|
|
|
|
if (zoomChanged) { this.events.triggerEvent("zoomend"); }
|
|
}
|
|
|
|
// even if nothing was done, we want to notify of this
|
|
if (!dragging && !noEvent) {
|
|
this.events.triggerEvent("moveend");
|
|
}
|
|
|
|
// Store the map dragging state for later use
|
|
this.dragging = !!dragging;
|
|
|
|
},
|
|
|
|
/**
|
|
* Method: centerLayerContainer
|
|
* This function takes care to recenter the layerContainerDiv.
|
|
*
|
|
* Parameters:
|
|
* lonlat - {<OpenLayers.LonLat>}
|
|
*/
|
|
centerLayerContainer: function (lonlat) {
|
|
|
|
var originPx = this.getViewPortPxFromLonLat(this.layerContainerOrigin);
|
|
var newPx = this.getViewPortPxFromLonLat(lonlat);
|
|
|
|
if ((originPx != null) && (newPx != null)) {
|
|
this.layerContainerDiv.style.left = Math.round(originPx.x - newPx.x) + "px";
|
|
this.layerContainerDiv.style.top = Math.round(originPx.y - newPx.y) + "px";
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Method: isValidZoomLevel
|
|
*
|
|
* Parameters:
|
|
* zoomLevel - {Integer}
|
|
*
|
|
* Returns:
|
|
* {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 - {<OpenLayers.LonLat>}
|
|
*
|
|
* Returns:
|
|
* {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
|
|
* This method returns a string representing the projection. In
|
|
* the case of projection support, this will be the srsCode which
|
|
* is loaded -- otherwise it will simply be the string value that
|
|
* was passed to the projection at startup.
|
|
*
|
|
* FIXME: In 3.0, we will remove getProjectionObject, and instead
|
|
* return a Projection object from this function.
|
|
*
|
|
* Returns:
|
|
* {String} The Projection string from the base layer or null.
|
|
*/
|
|
getProjection: function() {
|
|
var projection = this.getProjectionObject();
|
|
return projection ? projection.getCode() : null;
|
|
},
|
|
|
|
/**
|
|
* APIMethod: getProjectionObject
|
|
* Returns the projection obect from the baselayer.
|
|
*
|
|
* Returns:
|
|
* {<OpenLayers.Projection>} The Projection of the base layer.
|
|
*/
|
|
getProjectionObject: function() {
|
|
var projection = null;
|
|
if (this.baseLayer != null) {
|
|
projection = this.baseLayer.projection;
|
|
}
|
|
return projection;
|
|
},
|
|
|
|
/**
|
|
* APIMethod: getMaxResolution
|
|
*
|
|
* Returns:
|
|
* {String} The Map's Maximum Resolution
|
|
*/
|
|
getMaxResolution: function() {
|
|
var maxResolution = null;
|
|
if (this.baseLayer != null) {
|
|
maxResolution = this.baseLayer.maxResolution;
|
|
}
|
|
return maxResolution;
|
|
},
|
|
|
|
/**
|
|
* APIMethod: getMaxExtent
|
|
*
|
|
* Parameters:
|
|
* options - {Object}
|
|
*
|
|
* Allowed Options:
|
|
* restricted - {Boolean} If true, returns restricted extent (if it is
|
|
* available.)
|
|
*
|
|
* Returns:
|
|
* {<OpenLayers.Bounds>} The maxExtent property as set on the current
|
|
* baselayer, unless the 'restricted' option is set, in which case
|
|
* the 'restrictedExtent' option from the map is returned (if it
|
|
* is set).
|
|
*/
|
|
getMaxExtent: function (options) {
|
|
var maxExtent = null;
|
|
if(options && options.restricted && this.restrictedExtent){
|
|
maxExtent = this.restrictedExtent;
|
|
} else if (this.baseLayer != null) {
|
|
maxExtent = this.baseLayer.maxExtent;
|
|
}
|
|
return maxExtent;
|
|
},
|
|
|
|
/**
|
|
* APIMethod: getNumZoomLevels
|
|
*
|
|
* Returns:
|
|
* {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
|
|
*
|
|
* Returns:
|
|
* {<OpenLayers.Bounds>} 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
|
|
*
|
|
* Returns:
|
|
* {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();
|
|
} else if(this.allOverlays === true && this.layers.length > 0) {
|
|
// while adding the 1st layer to the map in allOverlays mode,
|
|
// this.baseLayer is not set yet when we need the resolution
|
|
// for calculateInRange.
|
|
resolution = this.layers[0].getResolution();
|
|
}
|
|
return resolution;
|
|
},
|
|
|
|
/**
|
|
* APIMethod: getUnits
|
|
*
|
|
* Returns:
|
|
* {Float} The current units of the map.
|
|
* If no baselayer is set, returns null.
|
|
*/
|
|
getUnits: function () {
|
|
var units = null;
|
|
if (this.baseLayer != null) {
|
|
units = this.baseLayer.units;
|
|
}
|
|
return units;
|
|
},
|
|
|
|
/**
|
|
* APIMethod: getScale
|
|
*
|
|
* Returns:
|
|
* {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: getZoomForExtent
|
|
*
|
|
* Parameters:
|
|
* bounds - {<OpenLayers.Bounds>}
|
|
* closest - {Boolean} Find the zoom level that most closely fits the
|
|
* specified bounds. Note that this may result in a zoom that does
|
|
* not exactly contain the entire extent.
|
|
* Default is false.
|
|
*
|
|
* Returns:
|
|
* {Integer} A suitable zoom level for the specified bounds.
|
|
* If no baselayer is set, returns null.
|
|
*/
|
|
getZoomForExtent: function (bounds, closest) {
|
|
var zoom = null;
|
|
if (this.baseLayer != null) {
|
|
zoom = this.baseLayer.getZoomForExtent(bounds, closest);
|
|
}
|
|
return zoom;
|
|
},
|
|
|
|
/**
|
|
* APIMethod: getResolutionForZoom
|
|
*
|
|
* Parameter:
|
|
* zoom - {Float}
|
|
*
|
|
* Returns:
|
|
* {Float} A suitable resolution for the specified zoom. If no baselayer
|
|
* is set, returns null.
|
|
*/
|
|
getResolutionForZoom: function(zoom) {
|
|
var resolution = null;
|
|
if(this.baseLayer) {
|
|
resolution = this.baseLayer.getResolutionForZoom(zoom);
|
|
}
|
|
return resolution;
|
|
},
|
|
|
|
/**
|
|
* APIMethod: getZoomForResolution
|
|
*
|
|
* Parameter:
|
|
* resolution - {Float}
|
|
* closest - {Boolean} Find the zoom level that corresponds to the absolute
|
|
* closest resolution, which may result in a zoom whose corresponding
|
|
* resolution is actually smaller than we would have desired (if this
|
|
* is being called from a getZoomForExtent() call, then this means that
|
|
* the returned zoom index might not actually contain the entire
|
|
* extent specified... but it'll be close).
|
|
* Default is false.
|
|
*
|
|
* Returns:
|
|
* {Integer} A suitable zoom level for the specified resolution.
|
|
* If no baselayer is set, returns null.
|
|
*/
|
|
getZoomForResolution: function(resolution, closest) {
|
|
var zoom = null;
|
|
if (this.baseLayer != null) {
|
|
zoom = this.baseLayer.getZoomForResolution(resolution, closest);
|
|
}
|
|
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 - {<OpenLayers.Bounds>}
|
|
* closest - {Boolean} Find the zoom level that most closely fits the
|
|
* specified bounds. Note that this may result in a zoom that does
|
|
* not exactly contain the entire extent.
|
|
* Default is false.
|
|
*
|
|
*/
|
|
zoomToExtent: function(bounds, closest) {
|
|
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, closest));
|
|
},
|
|
|
|
/**
|
|
* APIMethod: zoomToMaxExtent
|
|
* Zoom to the full extent and recenter.
|
|
*
|
|
* Parameters:
|
|
* options -
|
|
*
|
|
* Allowed Options:
|
|
* restricted - {Boolean} True to zoom to restricted extent if it is
|
|
* set. Defaults to true.
|
|
*/
|
|
zoomToMaxExtent: function(options) {
|
|
//restricted is true by default
|
|
var restricted = (options) ? options.restricted : true;
|
|
|
|
var maxExtent = this.getMaxExtent({
|
|
'restricted': restricted
|
|
});
|
|
this.zoomToExtent(maxExtent);
|
|
},
|
|
|
|
/**
|
|
* APIMethod: zoomToScale
|
|
* Zoom to a specified scale
|
|
*
|
|
* Parameters:
|
|
* scale - {float}
|
|
* closest - {Boolean} Find the zoom level that most closely fits the
|
|
* specified scale. Note that this may result in a zoom that does
|
|
* not exactly contain the entire extent.
|
|
* Default is false.
|
|
*
|
|
*/
|
|
zoomToScale: function(scale, closest) {
|
|
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, closest);
|
|
},
|
|
|
|
/********************************************************/
|
|
/* */
|
|
/* Translation Functions */
|
|
/* */
|
|
/* The following functions translate between */
|
|
/* LonLat, LayerPx, and ViewPortPx */
|
|
/* */
|
|
/********************************************************/
|
|
|
|
//
|
|
// TRANSLATION: LonLat <-> ViewPortPx
|
|
//
|
|
|
|
/**
|
|
* Method: getLonLatFromViewPortPx
|
|
*
|
|
* Parameters:
|
|
* viewPortPx - {<OpenLayers.Pixel>}
|
|
*
|
|
* Returns:
|
|
* {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view
|
|
* port <OpenLayers.Pixel>, 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 - {<OpenLayers.LonLat>}
|
|
*
|
|
* Returns:
|
|
* {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in
|
|
* <OpenLayers.LonLat>, 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 - {<OpenLayers.Pixel>}
|
|
*
|
|
* Returns:
|
|
* {<OpenLayers.LonLat>} 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
|
|
* Returns a pixel location given a map location. The map location is
|
|
* translated to an integer pixel location (in viewport pixel
|
|
* coordinates) by the current base layer.
|
|
*
|
|
* Parameters:
|
|
* lonlat - {<OpenLayers.LonLat>} A map location.
|
|
*
|
|
* Returns:
|
|
* {<OpenLayers.Pixel>} An OpenLayers.Pixel corresponding to the
|
|
* <OpenLayers.LonLat> translated into view port pixels by the current
|
|
* base layer.
|
|
*/
|
|
getPixelFromLonLat: function (lonlat) {
|
|
var px = this.getViewPortPxFromLonLat(lonlat);
|
|
px.x = Math.round(px.x);
|
|
px.y = Math.round(px.y);
|
|
return px;
|
|
},
|
|
|
|
/**
|
|
* Method: getGeodesicPixelSize
|
|
*
|
|
* Parameters:
|
|
* px - {<OpenLayers.Pixel>} The pixel to get the geodesic length for. If
|
|
* not provided, the center pixel of the map viewport will be used.
|
|
*
|
|
* Returns:
|
|
* {<OpenLayers.Size>} The geodesic size of the pixel in kilometers.
|
|
*/
|
|
getGeodesicPixelSize: function(px) {
|
|
var lonlat = px ? this.getLonLatFromPixel(px) : (this.getCenter() ||
|
|
new OpenLayers.LonLat(0, 0));
|
|
var res = this.getResolution();
|
|
var left = lonlat.add(-res / 2, 0);
|
|
var right = lonlat.add(res / 2, 0);
|
|
var bottom = lonlat.add(0, -res / 2);
|
|
var top = lonlat.add(0, res / 2);
|
|
var dest = new OpenLayers.Projection("EPSG:4326");
|
|
var source = this.getProjectionObject() || dest;
|
|
if(!source.equals(dest)) {
|
|
left.transform(source, dest);
|
|
right.transform(source, dest);
|
|
bottom.transform(source, dest);
|
|
top.transform(source, dest);
|
|
}
|
|
|
|
return new OpenLayers.Size(
|
|
OpenLayers.Util.distVincenty(left, right),
|
|
OpenLayers.Util.distVincenty(bottom, top)
|
|
);
|
|
},
|
|
|
|
|
|
|
|
//
|
|
// TRANSLATION: ViewPortPx <-> LayerPx
|
|
//
|
|
|
|
/**
|
|
* APIMethod: getViewPortPxFromLayerPx
|
|
*
|
|
* Parameters:
|
|
* layerPx - {<OpenLayers.Pixel>}
|
|
*
|
|
* Returns:
|
|
* {<OpenLayers.Pixel>} 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 - {<OpenLayers.Pixel>}
|
|
*
|
|
* Returns:
|
|
* {<OpenLayers.Pixel>} 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
|
|
//
|
|
|
|
/**
|
|
* Method: getLonLatFromLayerPx
|
|
*
|
|
* Parameters:
|
|
* px - {<OpenLayers.Pixel>}
|
|
*
|
|
* Returns:
|
|
* {<OpenLayers.LonLat>}
|
|
*/
|
|
getLonLatFromLayerPx: function (px) {
|
|
//adjust for displacement of layerContainerDiv
|
|
px = this.getViewPortPxFromLayerPx(px);
|
|
return this.getLonLatFromViewPortPx(px);
|
|
},
|
|
|
|
/**
|
|
* APIMethod: getLayerPxFromLonLat
|
|
*
|
|
* Parameters:
|
|
* lonlat - {<OpenLayers.LonLat>} lonlat
|
|
*
|
|
* Returns:
|
|
* {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in
|
|
* <OpenLayers.LonLat>, translated into layer pixels
|
|
* by the current base layer
|
|
*/
|
|
getLayerPxFromLonLat: function (lonlat) {
|
|
//adjust for displacement of layerContainerDiv
|
|
var px = this.getPixelFromLonLat(lonlat);
|
|
return this.getLayerPxFromViewPortPx(px);
|
|
},
|
|
|
|
CLASS_NAME: "OpenLayers.Map"
|
|
});
|
|
|
|
/**
|
|
* 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;
|