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