git-svn-id: http://svn.openlayers.org/trunk/openlayers@6203 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf
2068 lines
69 KiB
JavaScript
2068 lines
69 KiB
JavaScript
/* Copyright (c) 2006-2008 MetaCarta, Inc., 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
|
|
*/
|
|
|
|
/**
|
|
* 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, 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:
|
|
* - *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, 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,
|
|
* visibility, or order).
|
|
* - *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
|
|
* - *popupopen* triggered after a popup opens
|
|
* - *popupclose* triggered after a popup opens
|
|
* - *addmarker* triggered after a marker has been added
|
|
* - *removemarker* triggered after a marker has been removed
|
|
* - *clearmarkers* triggered after markers have been cleared
|
|
* - *mouseover* triggered after mouseover the map
|
|
* - *mouseout* triggered after mouseout the map
|
|
* - *mousemove* triggered after mousemove the map
|
|
* - *dragstart* triggered after the start of a drag
|
|
* - *drag* triggered after a drag
|
|
* - *dragend* triggered after the end of a drag
|
|
* - *changebaselayer* triggered after the base layer changes
|
|
*/
|
|
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,
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
fractionalZoom: false,
|
|
|
|
/**
|
|
* APIProperty: events
|
|
* {<OpenLayers.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: 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,
|
|
|
|
/**
|
|
* Property: 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
|
|
*/
|
|
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: 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.
|
|
*/
|
|
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,
|
|
|
|
/**
|
|
* 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: 'm',
|
|
* projection: "EPSG:41001"
|
|
* };
|
|
* var map = new OpenLayers.Map("map2", options);
|
|
* (end)
|
|
*/
|
|
initialize: function (div, 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 override default options
|
|
OpenLayers.Util.extend(this, options);
|
|
|
|
this.id = OpenLayers.Util.createUniqueID("OpenLayers.Map_");
|
|
|
|
this.div = OpenLayers.Util.getElement(div);
|
|
|
|
// the viewPortDiv is the outermost div we modify
|
|
var id = this.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 = this.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,
|
|
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 (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; i<nodes.length; ++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);
|
|
}
|
|
}
|
|
|
|
this.layers = [];
|
|
|
|
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; i < this.controls.length; 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);
|
|
|
|
},
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
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;
|
|
|
|
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; i < this.layers.length; 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; i < this.layers.length; i++) {
|
|
var layer = this.layers[i];
|
|
this.setLayerZIndex(layer, i);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* APIMethod: addLayer
|
|
*
|
|
* Parameters:
|
|
* layer - {<OpenLayers.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.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) {
|
|
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});
|
|
},
|
|
|
|
/**
|
|
* APIMethod: addLayers
|
|
*
|
|
* Parameters:
|
|
* layers - Array({<OpenLayers.Layer>})
|
|
*/
|
|
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 - {<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; i < this.layers.length; i++) {
|
|
var iLayer = this.layers[i];
|
|
if (iLayer.isBaseLayer) {
|
|
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; i < this.layers.length; i++) {
|
|
this.setLayerZIndex(this.layers[i], i);
|
|
}
|
|
this.events.triggerEvent("changelayer", {
|
|
layer: layer, property: "order"
|
|
});
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 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>}
|
|
* 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 - {<OpenLayers.Layer>}
|
|
*/
|
|
setBaseLayer: function(newBaseLayer) {
|
|
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);
|
|
}
|
|
|
|
// 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++;
|
|
this.baseLayer.visibility = true;
|
|
|
|
//redraw all layers
|
|
var center = this.getCenter();
|
|
if (center != null) {
|
|
|
|
//either get the center from the old Extent or just from
|
|
// the current center of the map.
|
|
var newCenter = (oldExtent)
|
|
? oldExtent.getCenterLonLat()
|
|
: center;
|
|
|
|
//the new zoom will either come from the old Extent or
|
|
// from the current resolution of the map
|
|
var newZoom = (oldExtent)
|
|
? this.getZoomForExtent(oldExtent, true)
|
|
: this.getZoomForResolution(this.resolution, true);
|
|
|
|
// zoom and force zoom change
|
|
this.setCenter(newCenter, 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
|
|
*
|
|
* Parameters:
|
|
* control - {<OpenLayers.Control>}
|
|
* px - {<OpenLayers.Pixel>}
|
|
*/
|
|
addControl: function (control, px) {
|
|
this.controls.push(control);
|
|
this.addControlToMap(control, 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 );
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 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; 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 - {<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
|
|
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
|
|
*
|
|
* 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);
|
|
|
|
// 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 - {<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 () {
|
|
return this.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} Only one at this time: "animate", which uses
|
|
* panTo instead of setCenter. Default is true.
|
|
*/
|
|
pan: function(dx, dy, options) {
|
|
|
|
if (!options) {
|
|
options = {animate: true}
|
|
}
|
|
// 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);
|
|
if (options.animate) {
|
|
this.panTo(newCenterLonLat);
|
|
} else {
|
|
this.setCenter(newCenterLonLat);
|
|
}
|
|
}
|
|
|
|
},
|
|
|
|
/**
|
|
* 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.getExtent().containsLonLat(lonlat)) {
|
|
if (!this.panTween) {
|
|
this.panTween = new OpenLayers.Tween(OpenLayers.Easing.Expo.easeOut);
|
|
}
|
|
var center = this.getCenter();
|
|
var from = {
|
|
lon: center.lon,
|
|
lat: center.lat
|
|
};
|
|
var to = {
|
|
lon: lonlat.lon,
|
|
lat: lonlat.lat
|
|
};
|
|
this.panTween.start(from, to, 50, {
|
|
callbacks: {
|
|
start: OpenLayers.Function.bind(function(lonlat) {
|
|
this.events.triggerEvent("movestart");
|
|
}, this),
|
|
eachStep: OpenLayers.Function.bind(function(lonlat) {
|
|
var lonlat = new OpenLayers.LonLat(lonlat.lon, lonlat.lat);
|
|
this.moveTo(lonlat, this.zoom, {
|
|
'dragging': true,
|
|
'noEvent': true
|
|
});
|
|
}, this),
|
|
done: OpenLayers.Function.bind(function(lonlat) {
|
|
var 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
|
|
*
|
|
* Parameters:
|
|
* lonlat - {<OpenLayers.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) {
|
|
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 = {};
|
|
}
|
|
// 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 (!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
|
|
this.baseLayer.moveTo(bounds, zoomChanged, dragging);
|
|
|
|
bounds = this.baseLayer.getExtent();
|
|
|
|
for (var i = 0; i < this.layers.length; i++) {
|
|
var layer = this.layers[i];
|
|
if (!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 (zoomChanged) {
|
|
//redraw popups
|
|
for (var i = 0; i < this.popups.length; 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");
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 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 = (originPx.x - newPx.x) + "px";
|
|
this.layerContainerDiv.style.top = (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
|
|
*
|
|
* Returns:
|
|
* {<OpenLayers.Bounds>}
|
|
*/
|
|
getMaxExtent: function () {
|
|
var maxExtent = null;
|
|
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();
|
|
}
|
|
return resolution;
|
|
},
|
|
|
|
/**
|
|
* 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>}
|
|
*/
|
|
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 - {<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
|
|
*
|
|
* Parameters:
|
|
* lonlat - {<OpenLayers.LonLat>}
|
|
*
|
|
* Returns:
|
|
* {<OpenLayers.Pixel>} An OpenLayers.Pixel corresponding to the
|
|
* <OpenLayers.LonLat> translated into view port
|
|
* pixels by the current base layer.
|
|
*/
|
|
getPixelFromLonLat: function (lonlat) {
|
|
return this.getViewPortPxFromLonLat(lonlat);
|
|
},
|
|
|
|
|
|
|
|
//
|
|
// 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
|
|
//
|
|
|
|
/**
|
|
* APIMethod: 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.getViewPortPxFromLonLat(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;
|