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; |
---|