1
|
/* Copyright (c) 2006 MetaCarta, Inc., published under the BSD license.
|
2
|
* See http://svn.openlayers.org/trunk/openlayers/license.txt for the full
|
3
|
* text of the license. */
|
4
|
|
5
|
/**
|
6
|
* @class
|
7
|
*
|
8
|
* @requires OpenLayers/Util.js
|
9
|
* @requires OpenLayers/Events.js
|
10
|
*/
|
11
|
OpenLayers.Map = Class.create();
|
12
|
OpenLayers.Map.TILE_WIDTH = 256;
|
13
|
OpenLayers.Map.TILE_HEIGHT = 256;
|
14
|
OpenLayers.Map.prototype = {
|
15
|
|
16
|
/** base z-indexes for different classes of thing
|
17
|
*
|
18
|
* @type Object
|
19
|
*/
|
20
|
Z_INDEX_BASE: { BaseLayer: 100, Overlay: 325, Popup: 750, Control: 1000 },
|
21
|
|
22
|
/** supported application event types
|
23
|
*
|
24
|
* @type Array */
|
25
|
EVENT_TYPES: [
|
26
|
"addlayer", "removelayer", "changelayer", "movestart", "move",
|
27
|
"moveend", "zoomend", "popupopen", "popupclose",
|
28
|
"addmarker", "removemarker", "clearmarkers", "mouseover",
|
29
|
"mouseout", "mousemove", "dragstart", "drag", "dragend",
|
30
|
"changebaselayer"],
|
31
|
|
32
|
/** @type OpenLayers.Events */
|
33
|
events: null,
|
34
|
|
35
|
/** the div that our map lives in
|
36
|
*
|
37
|
* @type DOMElement */
|
38
|
div: null,
|
39
|
|
40
|
/** Size of the main div (this.div)
|
41
|
*
|
42
|
* @type OpenLayers.Size */
|
43
|
size: null,
|
44
|
|
45
|
/** @type HTMLDivElement */
|
46
|
viewPortDiv: null,
|
47
|
|
48
|
/** The lonlat at which the later container was re-initialized (on-zoom)
|
49
|
* @type OpenLayers.LonLat */
|
50
|
layerContainerOrigin: null,
|
51
|
|
52
|
/** @type HTMLDivElement */
|
53
|
layerContainerDiv: null,
|
54
|
|
55
|
/** ordered list of layers in the map
|
56
|
*
|
57
|
* @type Array(OpenLayers.Layer)
|
58
|
*/
|
59
|
layers: null,
|
60
|
|
61
|
/** @type Array(OpenLayers.Control) */
|
62
|
controls: null,
|
63
|
|
64
|
/** @type Array(OpenLayers.Popup) */
|
65
|
popups: null,
|
66
|
|
67
|
/** The currently selected base layer - this determines min/max zoom level,
|
68
|
* projection, etc.
|
69
|
*
|
70
|
* @type OpenLayers.Layer */
|
71
|
baseLayer: null,
|
72
|
|
73
|
/** @type OpenLayers.LonLat */
|
74
|
center: null,
|
75
|
|
76
|
/** @type int */
|
77
|
zoom: 0,
|
78
|
|
79
|
// Options
|
80
|
|
81
|
/** @type OpenLayers.Size */
|
82
|
tileSize: null,
|
83
|
|
84
|
/** @type String */
|
85
|
projection: "EPSG:4326",
|
86
|
|
87
|
/** @type String */
|
88
|
units: 'degrees',
|
89
|
|
90
|
/** default max is 360 deg / 256 px, which corresponds to
|
91
|
* zoom level 0 on gmaps
|
92
|
*
|
93
|
* @type float */
|
94
|
maxResolution: 1.40625,
|
95
|
|
96
|
/** @type float */
|
97
|
minResolution: null,
|
98
|
|
99
|
/** @type float */
|
100
|
maxScale: null,
|
101
|
|
102
|
/** @type float */
|
103
|
minScale: null,
|
104
|
|
105
|
/** @type OpenLayers.Bounds */
|
106
|
maxExtent: null,
|
107
|
|
108
|
/** @type OpenLayers.Bounds */
|
109
|
minExtent: null,
|
110
|
|
111
|
/** @type int */
|
112
|
numZoomLevels: 16,
|
113
|
|
114
|
|
115
|
/**
|
116
|
* @constructor
|
117
|
*
|
118
|
* @param {DOMElement} div
|
119
|
* @param {Object} options Hashtable of extra options to tag onto the map
|
120
|
*/
|
121
|
initialize: function (div, options) {
|
122
|
|
123
|
this.div = div = $(div);
|
124
|
|
125
|
// the viewPortDiv is the outermost div we modify
|
126
|
var id = div.id + "_OpenLayers_ViewPort";
|
127
|
this.viewPortDiv = OpenLayers.Util.createDiv(id, null, null, null,
|
128
|
"relative", null,
|
129
|
"hidden");
|
130
|
this.viewPortDiv.style.width = "100%";
|
131
|
this.viewPortDiv.style.height = "100%";
|
132
|
this.div.appendChild(this.viewPortDiv);
|
133
|
|
134
|
// the layerContainerDiv is the one that holds all the layers
|
135
|
id = div.id + "_OpenLayers_Container";
|
136
|
this.layerContainerDiv = OpenLayers.Util.createDiv(id);
|
137
|
this.layerContainerDiv.style.zIndex=this.Z_INDEX_BASE['Popup']-1;
|
138
|
|
139
|
this.viewPortDiv.appendChild(this.layerContainerDiv);
|
140
|
|
141
|
this.events = new OpenLayers.Events(this, div, this.EVENT_TYPES);
|
142
|
|
143
|
this.updateSize();
|
144
|
|
145
|
// Because Mozilla does not support the "resize" event for elements other
|
146
|
// than "window", we need to put a hack here.
|
147
|
//
|
148
|
if (navigator.appName.contains("Microsoft")) {
|
149
|
// If IE, register the resize on the div
|
150
|
this.events.register("resize", this, this.updateSize);
|
151
|
} else {
|
152
|
// Else updateSize on catching the window's resize
|
153
|
// Note that this is ok, as updateSize() does nothing if the
|
154
|
// map's size has not actually changed.
|
155
|
Event.observe(window, 'resize',
|
156
|
this.updateSize.bindAsEventListener(this));
|
157
|
}
|
158
|
|
159
|
//set the default options
|
160
|
this.setOptions(options);
|
161
|
|
162
|
this.layers = [];
|
163
|
|
164
|
if (this.controls == null) {
|
165
|
this.controls = [ new OpenLayers.Control.MouseDefaults(),
|
166
|
new OpenLayers.Control.PanZoom()];
|
167
|
}
|
168
|
|
169
|
for(var i=0; i < this.controls.length; i++) {
|
170
|
this.addControlToMap(this.controls[i]);
|
171
|
}
|
172
|
|
173
|
this.popups = new Array();
|
174
|
|
175
|
// always call map.destroy()
|
176
|
Event.observe(window,
|
177
|
'unload',
|
178
|
this.destroy.bindAsEventListener(this));
|
179
|
|
180
|
},
|
181
|
|
182
|
/**
|
183
|
* @private
|
184
|
*/
|
185
|
destroy:function() {
|
186
|
if (this.layers != null) {
|
187
|
for(var i=0; i< this.layers.length; i++) {
|
188
|
this.layers[i].destroy();
|
189
|
}
|
190
|
this.layers = null;
|
191
|
}
|
192
|
if (this.controls != null) {
|
193
|
for(var i=0; i< this.controls.length; i++) {
|
194
|
this.controls[i].destroy();
|
195
|
}
|
196
|
this.controls = null;
|
197
|
}
|
198
|
},
|
199
|
|
200
|
/**
|
201
|
* @private
|
202
|
*
|
203
|
* @param {Object} options Hashtable of options to tag to the map
|
204
|
*/
|
205
|
setOptions: function(options) {
|
206
|
|
207
|
// Simple-type defaults are set in class definition.
|
208
|
// Now set complex-type defaults
|
209
|
this.tileSize = new OpenLayers.Size(OpenLayers.Map.TILE_WIDTH,
|
210
|
OpenLayers.Map.TILE_HEIGHT);
|
211
|
|
212
|
this.maxExtent = new OpenLayers.Bounds(-180, -90, 180, 90);
|
213
|
|
214
|
// now add the options declared by the user
|
215
|
// (these will override defaults)
|
216
|
Object.extend(this, options);
|
217
|
},
|
218
|
|
219
|
/**
|
220
|
* @type OpenLayers.Size
|
221
|
*/
|
222
|
getTileSize: function() {
|
223
|
return this.tileSize;
|
224
|
},
|
225
|
|
226
|
/********************************************************/
|
227
|
/* */
|
228
|
/* Layers, Controls, Popup Functions */
|
229
|
/* */
|
230
|
/* The following functions deal with adding and */
|
231
|
/* removing Layers, Controls, and Popups */
|
232
|
/* to and from the Map */
|
233
|
/* */
|
234
|
/********************************************************/
|
235
|
|
236
|
/**
|
237
|
* @param {String} name
|
238
|
*
|
239
|
* @returns The Layer with the corresponding id from the map's
|
240
|
* layer collection, or null if not found.
|
241
|
* @type OpenLayers.Layer
|
242
|
*/
|
243
|
getLayer: function(id) {
|
244
|
var foundLayer = null;
|
245
|
for (var i = 0; i < this.layers.length; i++) {
|
246
|
var layer = this.layers[i];
|
247
|
if (layer.id == id) {
|
248
|
foundLayer = layer;
|
249
|
}
|
250
|
}
|
251
|
return foundLayer;
|
252
|
},
|
253
|
|
254
|
/**
|
255
|
* @param {OpenLayers.Layer} layer
|
256
|
*/
|
257
|
addLayer: function (layer) {
|
258
|
layer.div.style.overflow = "";
|
259
|
layer.div.style.zIndex =
|
260
|
this.Z_INDEX_BASE[layer.isBaseLayer ? 'BaseLayer' : 'Overlay']
|
261
|
+ this.layers.length * 5;
|
262
|
|
263
|
if (layer.isFixed) {
|
264
|
this.viewPortDiv.appendChild(layer.div);
|
265
|
} else {
|
266
|
this.layerContainerDiv.appendChild(layer.div);
|
267
|
}
|
268
|
this.layers.push(layer);
|
269
|
layer.setMap(this);
|
270
|
|
271
|
this.events.triggerEvent("addlayer");
|
272
|
|
273
|
//make sure layer draws itself!
|
274
|
if (this.center != null) {
|
275
|
var bounds = this.getExtent();
|
276
|
layer.moveTo(bounds, true);
|
277
|
}
|
278
|
|
279
|
if (layer.isBaseLayer) {
|
280
|
// set the first baselaye we add as the baselayer
|
281
|
if (this.baseLayer == null) {
|
282
|
this.setBaseLayer(layer);
|
283
|
this.events.triggerEvent("changebaselayer");
|
284
|
} else {
|
285
|
layer.setVisibility(false);
|
286
|
}
|
287
|
}
|
288
|
},
|
289
|
|
290
|
/**
|
291
|
* @param {Array(OpenLayers.Layer)} layers
|
292
|
*/
|
293
|
addLayers: function (layers) {
|
294
|
for (var i = 0; i < layers.length; i++) {
|
295
|
this.addLayer(layers[i]);
|
296
|
}
|
297
|
},
|
298
|
|
299
|
/** Removes a layer from the map by removing its visual element (the
|
300
|
* layer.div property), then removing it from the map's internal list
|
301
|
* of layers, setting the layer's map property to null.
|
302
|
*
|
303
|
* a "removelayer" event is triggered.
|
304
|
*
|
305
|
* very worthy of mention is that simply removing a layer from a map
|
306
|
* will not cause the removal of any popups which may have been created
|
307
|
* by the layer. this is due to the fact that it was decided at some
|
308
|
* point that popups would not belong to layers. thus there is no way
|
309
|
* for us to know here to which layer the popup belongs.
|
310
|
*
|
311
|
* A simple solution to this is simply to call destroy() on the layer.
|
312
|
* the default OpenLayers.Layer class's destroy() function
|
313
|
* automatically takes care to remove itself from whatever map it has
|
314
|
* been attached to.
|
315
|
*
|
316
|
* The correct solution is for the layer itself to register an
|
317
|
* event-handler on "removelayer" and when it is called, if it
|
318
|
* recognizes itself as the layer being removed, then it cycles through
|
319
|
* its own personal list of popups, removing them from the map.
|
320
|
*
|
321
|
* @param {OpenLayers.Layer} layer
|
322
|
*/
|
323
|
removeLayer: function(layer) {
|
324
|
if (layer.isFixed) {
|
325
|
this.viewPortDiv.removeChild(layer.div);
|
326
|
} else {
|
327
|
this.layerContainerDiv.removeChild(layer.div);
|
328
|
}
|
329
|
layer.map = null;
|
330
|
this.layers.remove(layer);
|
331
|
|
332
|
// if we removed the base layer, need to set a new one
|
333
|
if (this.baseLayer == layer) {
|
334
|
this.baseLayer = null;
|
335
|
for(i=0; i < this.layers.length; i++) {
|
336
|
var iLayer = this.layers[i];
|
337
|
if (iLayer.isBaseLayer) {
|
338
|
this.setBaseLayer(iLayer);
|
339
|
break;
|
340
|
}
|
341
|
}
|
342
|
}
|
343
|
this.events.triggerEvent("removelayer");
|
344
|
},
|
345
|
|
346
|
/** Allows user to specify one of the currently-loaded layers as the Map's
|
347
|
* new base layer.
|
348
|
*
|
349
|
* @param {OpenLayers.Layer} newBaseLayer
|
350
|
* @param {Boolean} noEvent
|
351
|
*/
|
352
|
setBaseLayer: function(newBaseLayer, noEvent) {
|
353
|
|
354
|
if (newBaseLayer != this.baseLayer) {
|
355
|
|
356
|
// is newBaseLayer an already loaded layer?
|
357
|
var foundLayer = (this.layers.indexOf(newBaseLayer) != -1);
|
358
|
if (foundLayer) {
|
359
|
|
360
|
// make the old base layer invisible
|
361
|
if (this.baseLayer != null) {
|
362
|
this.baseLayer.setVisibility(false, noEvent);
|
363
|
}
|
364
|
|
365
|
// set new baselayer and make it visible
|
366
|
this.baseLayer = newBaseLayer;
|
367
|
this.baseLayer.setVisibility(true, noEvent);
|
368
|
|
369
|
//redraw all layers
|
370
|
var center = this.getCenter();
|
371
|
if (center != null) {
|
372
|
var zoom = this.getZoom();
|
373
|
this.zoom = null;
|
374
|
if (zoom > this.baseLayer.numZoomLevels - 1) {
|
375
|
zoom = this.baseLayer.numZoomLevels - 1;
|
376
|
}
|
377
|
this.setCenter(center, zoom);
|
378
|
|
379
|
}
|
380
|
if ((noEvent == null) || (noEvent == false)) {
|
381
|
this.events.triggerEvent("changebaselayer");
|
382
|
}
|
383
|
}
|
384
|
}
|
385
|
},
|
386
|
|
387
|
/**
|
388
|
* @param {OpenLayers.Control} control
|
389
|
* @param {OpenLayers.Pixel} px
|
390
|
*/
|
391
|
addControl: function (control, px) {
|
392
|
this.controls.push(control);
|
393
|
this.addControlToMap(control, px);
|
394
|
},
|
395
|
|
396
|
/**
|
397
|
* @private
|
398
|
*
|
399
|
* @param {OpenLayers.Control} control
|
400
|
* @param {OpenLayers.Pixel} px
|
401
|
*/
|
402
|
addControlToMap: function (control, px) {
|
403
|
control.setMap(this);
|
404
|
var div = control.draw(px);
|
405
|
if (div) {
|
406
|
div.style.zIndex = this.Z_INDEX_BASE['Control'] +
|
407
|
this.controls.length;
|
408
|
this.viewPortDiv.appendChild( div );
|
409
|
}
|
410
|
},
|
411
|
|
412
|
/**
|
413
|
* @param {OpenLayers.Popup} popup
|
414
|
* @param {Boolean} exclusive If true, closes all other popups first
|
415
|
*/
|
416
|
addPopup: function(popup, exclusive) {
|
417
|
|
418
|
if (exclusive) {
|
419
|
//remove all other popups from screen
|
420
|
for(var i=0; i < this.popups.length; i++) {
|
421
|
this.removePopup(this.popups[i]);
|
422
|
}
|
423
|
}
|
424
|
|
425
|
popup.map = this;
|
426
|
this.popups.push(popup);
|
427
|
var popupDiv = popup.draw();
|
428
|
if (popupDiv) {
|
429
|
popupDiv.style.zIndex = this.Z_INDEX_BASE['Popup'] +
|
430
|
this.popups.length;
|
431
|
this.layerContainerDiv.appendChild(popupDiv);
|
432
|
}
|
433
|
},
|
434
|
|
435
|
/**
|
436
|
* @param {OpenLayers.Popup} popup
|
437
|
*/
|
438
|
removePopup: function(popup) {
|
439
|
this.popups.remove(popup);
|
440
|
if (popup.div) {
|
441
|
this.layerContainerDiv.removeChild(popup.div);
|
442
|
}
|
443
|
popup.map = null;
|
444
|
},
|
445
|
|
446
|
/********************************************************/
|
447
|
/* */
|
448
|
/* Container Div Functions */
|
449
|
/* */
|
450
|
/* The following functions deal with the access to */
|
451
|
/* and maintenance of the size of the container div */
|
452
|
/* */
|
453
|
/********************************************************/
|
454
|
|
455
|
/**
|
456
|
* @returns {OpenLayers.Size}
|
457
|
*/
|
458
|
getSize: function () {
|
459
|
return this.size;
|
460
|
},
|
461
|
|
462
|
/**
|
463
|
* This function should be called by any external code which dynamically
|
464
|
* changes the size of the map div (because mozilla wont let us catch the
|
465
|
* "onresize" for an element)
|
466
|
*/
|
467
|
updateSize: function() {
|
468
|
var newSize = this.getCurrentSize();
|
469
|
var oldSize = this.getSize();
|
470
|
if (oldSize == null)
|
471
|
this.size = oldSize = newSize;
|
472
|
if (!newSize.equals(oldSize)) {
|
473
|
|
474
|
//notify layers of mapresize
|
475
|
for(var i=0; i < this.layers.length; i++) {
|
476
|
this.layers[i].onMapResize();
|
477
|
}
|
478
|
|
479
|
var center = new OpenLayers.Pixel(newSize.w /2, newSize.h / 2);
|
480
|
|
481
|
var zoom = this.getZoom();
|
482
|
this.zoom = null;
|
483
|
this.setCenter(center, zoom);
|
484
|
|
485
|
// store the new size
|
486
|
this.size = newSize;
|
487
|
// the div might have moved on the page, also
|
488
|
this.events.element.offsets = null;
|
489
|
}
|
490
|
},
|
491
|
|
492
|
/**
|
493
|
* @private
|
494
|
*
|
495
|
* @returns A new OpenLayers.Size object with the dimensions of the map div
|
496
|
* @type OpenLayers.Size
|
497
|
*/
|
498
|
getCurrentSize: function() {
|
499
|
|
500
|
var size = new OpenLayers.Size(this.div.clientWidth,
|
501
|
this.div.clientHeight);
|
502
|
|
503
|
// Workaround for the fact that hidden elements return 0 for size.
|
504
|
if (size.w == 0 && size.h == 0) {
|
505
|
var dim = Element.getDimensions(this.div);
|
506
|
size.w = dim.width;
|
507
|
size.h = dim.height;
|
508
|
}
|
509
|
if (size.w == 0 && size.h == 0) {
|
510
|
size.w = parseInt(this.div.style.width);
|
511
|
size.h = parseInt(this.div.style.height);
|
512
|
}
|
513
|
return size;
|
514
|
},
|
515
|
|
516
|
/********************************************************/
|
517
|
/* */
|
518
|
/* Zoom, Center, Pan Functions */
|
519
|
/* */
|
520
|
/* The following functions handle the validation, */
|
521
|
/* getting and setting of the Zoom Level and Center */
|
522
|
/* as well as the panning of the Map */
|
523
|
/* */
|
524
|
/********************************************************/
|
525
|
/**
|
526
|
* @return {OpenLayers.LonLat}
|
527
|
*/
|
528
|
getCenter: function () {
|
529
|
return this.center;
|
530
|
},
|
531
|
|
532
|
|
533
|
/**
|
534
|
* @return {int}
|
535
|
*/
|
536
|
getZoom: function () {
|
537
|
return this.zoom;
|
538
|
},
|
539
|
|
540
|
/** Allows user to pan by a value of screen pixels
|
541
|
*
|
542
|
* @param {int} dx
|
543
|
* @param {int} dy
|
544
|
*/
|
545
|
pan: function(dx, dy) {
|
546
|
|
547
|
// getCenter
|
548
|
var centerPx = this.getViewPortPxFromLonLat(this.getCenter());
|
549
|
|
550
|
// adjust
|
551
|
var newCenterPx = centerPx.add(dx, dy);
|
552
|
|
553
|
// only call setCenter if there has been a change
|
554
|
if (!newCenterPx.equals(centerPx)) {
|
555
|
var newCenterLonLat = this.getLonLatFromViewPortPx(newCenterPx);
|
556
|
this.setCenter(newCenterLonLat);
|
557
|
}
|
558
|
|
559
|
},
|
560
|
|
561
|
/**
|
562
|
* @param {OpenLayers.LonLat} lonlat
|
563
|
* @param {int} zoom
|
564
|
* @param {Boolean} minor Specifies whether or not to
|
565
|
* trigger movestart/end events
|
566
|
*/
|
567
|
setCenter: function (lonlat, zoom, minor) {
|
568
|
var zoomChanged = (this.isValidZoomLevel(zoom)) &&
|
569
|
(zoom != this.getZoom());
|
570
|
|
571
|
var centerChanged = (this.isValidLonLat(lonlat)) &&
|
572
|
(!lonlat.equals(this.center));
|
573
|
|
574
|
|
575
|
// if neither center nor zoom will change, no need to do anything
|
576
|
if (zoomChanged || centerChanged || !minor) {
|
577
|
|
578
|
if (!minor) { this.events.triggerEvent("movestart"); }
|
579
|
|
580
|
if (centerChanged) {
|
581
|
if ((!zoomChanged) && (this.center)) {
|
582
|
// if zoom hasnt changed, just slide layerContainer
|
583
|
// (must be done before setting this.center to new value)
|
584
|
this.centerLayerContainer(lonlat);
|
585
|
}
|
586
|
this.center = lonlat.clone();
|
587
|
}
|
588
|
|
589
|
// (re)set the layerContainerDiv's location
|
590
|
if ((zoomChanged) || (this.layerContainerOrigin == null)) {
|
591
|
this.layerContainerOrigin = this.center.clone();
|
592
|
this.layerContainerDiv.style.left = "0px";
|
593
|
this.layerContainerDiv.style.top = "0px";
|
594
|
}
|
595
|
|
596
|
if (zoomChanged) {
|
597
|
this.zoom = zoom;
|
598
|
|
599
|
//redraw popups
|
600
|
for (var i = 0; i < this.popups.length; i++) {
|
601
|
this.popups[i].updatePosition();
|
602
|
}
|
603
|
}
|
604
|
|
605
|
//send the move call to the baselayer and all the overlays
|
606
|
var bounds = this.getExtent();
|
607
|
for (var i = 0; i < this.layers.length; i++) {
|
608
|
var layer = this.layers[i];
|
609
|
if ((layer == this.baseLayer) || !layer.isBaseLayer) {
|
610
|
layer.moveTo(bounds, zoomChanged, minor);
|
611
|
}
|
612
|
}
|
613
|
|
614
|
this.events.triggerEvent("move");
|
615
|
|
616
|
if (zoomChanged) { this.events.triggerEvent("zoomend"); }
|
617
|
}
|
618
|
|
619
|
// even if nothing was done, we want to notify of this
|
620
|
if (!minor) { this.events.triggerEvent("moveend"); }
|
621
|
},
|
622
|
|
623
|
/** This function takes care to recenter the layerContainerDiv
|
624
|
*
|
625
|
* @private
|
626
|
*
|
627
|
* @param {OpenLayers.LonLat} lonlat
|
628
|
*/
|
629
|
centerLayerContainer: function (lonlat) {
|
630
|
|
631
|
var originPx = this.getViewPortPxFromLonLat(this.layerContainerOrigin);
|
632
|
var newPx = this.getViewPortPxFromLonLat(lonlat);
|
633
|
|
634
|
this.layerContainerDiv.style.left = (originPx.x - newPx.x) + "px";
|
635
|
this.layerContainerDiv.style.top = (originPx.y - newPx.y) + "px";
|
636
|
},
|
637
|
|
638
|
/**
|
639
|
* @private
|
640
|
*
|
641
|
* @param {int} zoomLevel
|
642
|
*
|
643
|
* @returns Whether or not the zoom level passed in is non-null and
|
644
|
* within the min/max range of zoom levels.
|
645
|
* @type Boolean
|
646
|
*/
|
647
|
isValidZoomLevel: function(zoomLevel) {
|
648
|
return ( (zoomLevel != null) &&
|
649
|
(zoomLevel >= 0) &&
|
650
|
(zoomLevel < this.getNumZoomLevels()) );
|
651
|
},
|
652
|
|
653
|
/**
|
654
|
* @private
|
655
|
*
|
656
|
* @param {OpenLayers.LonLat} lonlat
|
657
|
*
|
658
|
* @returns Whether or not the lonlat passed in is non-null and within
|
659
|
* the maxExtent bounds
|
660
|
*
|
661
|
* @type Boolean
|
662
|
*/
|
663
|
isValidLonLat: function(lonlat) {
|
664
|
var valid = false;
|
665
|
if (lonlat != null) {
|
666
|
var maxExtent = this.getMaxExtent();
|
667
|
valid = maxExtent.contains(lonlat.lon, lonlat.lat);
|
668
|
}
|
669
|
return valid;
|
670
|
},
|
671
|
|
672
|
/********************************************************/
|
673
|
/* */
|
674
|
/* Layer Options */
|
675
|
/* */
|
676
|
/* Accessor functions to Layer Options parameters */
|
677
|
/* */
|
678
|
/********************************************************/
|
679
|
|
680
|
/**
|
681
|
* @returns The Projection of the base layer
|
682
|
* @type String
|
683
|
*/
|
684
|
getProjection: function() {
|
685
|
var projection = null;
|
686
|
if (this.baseLayer != null) {
|
687
|
projection = this.baseLayer.getProjection();
|
688
|
}
|
689
|
return projection;
|
690
|
},
|
691
|
|
692
|
/**
|
693
|
* @returns The Map's Maximum Resolution
|
694
|
* @type String
|
695
|
*/
|
696
|
getMaxResolution: function() {
|
697
|
var maxResolution = null;
|
698
|
if (this.baseLayer != null) {
|
699
|
maxResolution = this.baseLayer.getMaxResolution();
|
700
|
}
|
701
|
return maxResolution;
|
702
|
},
|
703
|
|
704
|
/**
|
705
|
* @type OpenLayers.Bounds
|
706
|
*/
|
707
|
getMaxExtent: function () {
|
708
|
var maxExtent = null;
|
709
|
if (this.baseLayer != null) {
|
710
|
maxExtent = this.baseLayer.getMaxExtent();
|
711
|
}
|
712
|
return maxExtent;
|
713
|
},
|
714
|
|
715
|
/**
|
716
|
* @returns The total number of zoom levels that can be displayed by the
|
717
|
* current baseLayer.
|
718
|
* @type int
|
719
|
*/
|
720
|
getNumZoomLevels: function() {
|
721
|
var numZoomLevels = null;
|
722
|
if (this.baseLayer != null) {
|
723
|
numZoomLevels = this.baseLayer.getNumZoomLevels();
|
724
|
}
|
725
|
return numZoomLevels;
|
726
|
},
|
727
|
|
728
|
/********************************************************/
|
729
|
/* */
|
730
|
/* Baselayer Functions */
|
731
|
/* */
|
732
|
/* The following functions, all publicly exposed */
|
733
|
/* in the API?, are all merely wrappers to the */
|
734
|
/* the same calls on whatever layer is set as */
|
735
|
/* the current base layer */
|
736
|
/* */
|
737
|
/********************************************************/
|
738
|
|
739
|
/**
|
740
|
* @returns A Bounds object which represents the lon/lat bounds of the
|
741
|
* current viewPort.
|
742
|
* If no baselayer is set, returns null.
|
743
|
* @type OpenLayers.Bounds
|
744
|
*/
|
745
|
getExtent: function () {
|
746
|
var extent = null;
|
747
|
if (this.baseLayer != null) {
|
748
|
extent = this.baseLayer.getExtent();
|
749
|
}
|
750
|
return extent;
|
751
|
},
|
752
|
|
753
|
/**
|
754
|
* @returns The current resolution of the map.
|
755
|
* If no baselayer is set, returns null.
|
756
|
* @type float
|
757
|
*/
|
758
|
getResolution: function () {
|
759
|
var resolution = null;
|
760
|
if (this.baseLayer != null) {
|
761
|
resolution = this.baseLayer.getResolution();
|
762
|
}
|
763
|
return resolution;
|
764
|
},
|
765
|
|
766
|
/**
|
767
|
* @returns The current scale denominator of the map.
|
768
|
* If no baselayer is set, returns null.
|
769
|
* @type float
|
770
|
*/
|
771
|
getScale: function () {
|
772
|
var scale = null;
|
773
|
if (this.baseLayer != null) {
|
774
|
var res = this.getResolution();
|
775
|
var units = this.baseLayer.units;
|
776
|
scale = res * OpenLayers.INCHES_PER_UNIT[units] *
|
777
|
OpenLayers.DOTS_PER_INCH;
|
778
|
}
|
779
|
return scale;
|
780
|
},
|
781
|
|
782
|
|
783
|
/**
|
784
|
* @param {OpenLayers.Bounds} bounds
|
785
|
*
|
786
|
* @returns A suitable zoom level for the specified bounds.
|
787
|
* If no baselayer is set, returns null.
|
788
|
* @type int
|
789
|
*/
|
790
|
getZoomForExtent: function (bounds) {
|
791
|
zoom = null;
|
792
|
if (this.baseLayer != null) {
|
793
|
zoom = this.baseLayer.getZoomForExtent(bounds);
|
794
|
}
|
795
|
return zoom;
|
796
|
},
|
797
|
|
798
|
/********************************************************/
|
799
|
/* */
|
800
|
/* Zooming Functions */
|
801
|
/* */
|
802
|
/* The following functions, all publicly exposed */
|
803
|
/* in the API, are all merely wrappers to the */
|
804
|
/* the setCenter() function */
|
805
|
/* */
|
806
|
/********************************************************/
|
807
|
|
808
|
/** Zoom to a specific zoom level
|
809
|
*
|
810
|
* @param {int} zoom
|
811
|
*/
|
812
|
zoomTo: function(zoom) {
|
813
|
if (this.isValidZoomLevel(zoom)) {
|
814
|
this.setCenter(null, zoom);
|
815
|
}
|
816
|
},
|
817
|
|
818
|
/**
|
819
|
* @param {int} zoom
|
820
|
*/
|
821
|
zoomIn: function() {
|
822
|
this.zoomTo(this.getZoom() + 1);
|
823
|
},
|
824
|
|
825
|
/**
|
826
|
* @param {int} zoom
|
827
|
*/
|
828
|
zoomOut: function() {
|
829
|
this.zoomTo(this.getZoom() - 1);
|
830
|
},
|
831
|
|
832
|
/** Zoom to the passed in bounds, recenter
|
833
|
*
|
834
|
* @param {OpenLayers.Bounds} bounds
|
835
|
*/
|
836
|
zoomToExtent: function(bounds) {
|
837
|
this.setCenter(bounds.getCenterLonLat(),
|
838
|
this.getZoomForExtent(bounds));
|
839
|
},
|
840
|
|
841
|
/** Zoom to the full extent and recenter.
|
842
|
*/
|
843
|
zoomToMaxExtent: function() {
|
844
|
this.zoomToExtent(this.getMaxExtent());
|
845
|
},
|
846
|
|
847
|
/** zoom to a specified scale
|
848
|
*
|
849
|
* @param {float} scale
|
850
|
*/
|
851
|
zoomToScale: function(scale) {
|
852
|
var res = OpenLayers.Util.getResolutionFromScale(scale,
|
853
|
this.baseLayer.units);
|
854
|
var size = this.getSize();
|
855
|
var w_deg = size.w * res;
|
856
|
var h_deg = size.h * res;
|
857
|
var center = this.getCenter();
|
858
|
|
859
|
var extent = new OpenLayers.Bounds(center.lon - w_deg / 2,
|
860
|
center.lat - h_deg / 2,
|
861
|
center.lon + w_deg / 2,
|
862
|
center.lat + h_deg / 2);
|
863
|
this.zoomToExtent(extent);
|
864
|
},
|
865
|
|
866
|
/********************************************************/
|
867
|
/* */
|
868
|
/* Translation Functions */
|
869
|
/* */
|
870
|
/* The following functions translate between */
|
871
|
/* LonLat, LayerPx, and ViewPortPx */
|
872
|
/* */
|
873
|
/********************************************************/
|
874
|
|
875
|
//
|
876
|
// TRANSLATION: LonLat <-> ViewPortPx
|
877
|
//
|
878
|
|
879
|
/**
|
880
|
* @param {OpenLayers.Pixel} viewPortPx
|
881
|
*
|
882
|
* @returns An OpenLayers.LonLat which is the passed-in view port
|
883
|
* OpenLayers.Pixel, translated into lon/lat by the
|
884
|
* current base layer
|
885
|
* @type OpenLayers.LonLat
|
886
|
* @private
|
887
|
*/
|
888
|
getLonLatFromViewPortPx: function (viewPortPx) {
|
889
|
var lonlat = null;
|
890
|
if (this.baseLayer != null) {
|
891
|
lonlat = this.baseLayer.getLonLatFromViewPortPx(viewPortPx);
|
892
|
}
|
893
|
return lonlat;
|
894
|
},
|
895
|
|
896
|
/**
|
897
|
* @param {OpenLayers.LonLat} lonlat
|
898
|
*
|
899
|
* @returns An OpenLayers.Pixel which is the passed-in OpenLayers.LonLat,
|
900
|
* translated into view port pixels by the
|
901
|
* current base layer
|
902
|
* @type OpenLayers.Pixel
|
903
|
* @private
|
904
|
*/
|
905
|
getViewPortPxFromLonLat: function (lonlat) {
|
906
|
var px = null;
|
907
|
if (this.baseLayer != null) {
|
908
|
px = this.baseLayer.getViewPortPxFromLonLat(lonlat);
|
909
|
}
|
910
|
return px;
|
911
|
},
|
912
|
|
913
|
|
914
|
//
|
915
|
// CONVENIENCE TRANSLATION FUNCTIONS FOR API
|
916
|
//
|
917
|
|
918
|
/**
|
919
|
* @param {OpenLayers.Pixel} pixel
|
920
|
*
|
921
|
* @returns An OpenLayers.LonLat corresponding to the given
|
922
|
* OpenLayers.Pixel, translated into lon/lat by the
|
923
|
* current base layer
|
924
|
* @type OpenLayers.LonLat
|
925
|
*/
|
926
|
getLonLatFromPixel: function (px) {
|
927
|
return this.getLonLatFromViewPortPx(px);
|
928
|
},
|
929
|
|
930
|
/**
|
931
|
* @param {OpenLayers.LonLat} lonlat
|
932
|
*
|
933
|
* @returns An OpenLayers.Pixel corresponding to the OpenLayers.LonLat
|
934
|
* translated into view port pixels by the
|
935
|
* current base layer
|
936
|
* @type OpenLayers.Pixel
|
937
|
*/
|
938
|
getPixelFromLonLat: function (lonlat) {
|
939
|
return this.getViewPortPxFromLonLat(lonlat);
|
940
|
},
|
941
|
|
942
|
|
943
|
|
944
|
//
|
945
|
// TRANSLATION: ViewPortPx <-> LayerPx
|
946
|
//
|
947
|
|
948
|
/**
|
949
|
* @private
|
950
|
*
|
951
|
* @param {OpenLayers.Pixel} layerPx
|
952
|
*
|
953
|
* @returns Layer Pixel translated into ViewPort Pixel coordinates
|
954
|
* @type OpenLayers.Pixel
|
955
|
*/
|
956
|
getViewPortPxFromLayerPx:function(layerPx) {
|
957
|
var viewPortPx = null;
|
958
|
if (layerPx != null) {
|
959
|
var dX = parseInt(this.layerContainerDiv.style.left);
|
960
|
var dY = parseInt(this.layerContainerDiv.style.top);
|
961
|
viewPortPx = layerPx.add(dX, dY);
|
962
|
}
|
963
|
return viewPortPx;
|
964
|
},
|
965
|
|
966
|
/**
|
967
|
* @private
|
968
|
*
|
969
|
* @param {OpenLayers.Pixel} viewPortPx
|
970
|
*
|
971
|
* @returns ViewPort Pixel translated into Layer Pixel coordinates
|
972
|
* @type OpenLayers.Pixel
|
973
|
*/
|
974
|
getLayerPxFromViewPortPx:function(viewPortPx) {
|
975
|
var layerPx = null;
|
976
|
if (viewPortPx != null) {
|
977
|
var dX = -parseInt(this.layerContainerDiv.style.left);
|
978
|
var dY = -parseInt(this.layerContainerDiv.style.top);
|
979
|
layerPx = viewPortPx.add(dX, dY);
|
980
|
}
|
981
|
if (!isNaN(layerPx.x) && !isNaN(layerPx.y)) {
|
982
|
return layerPx;
|
983
|
}
|
984
|
return null;
|
985
|
},
|
986
|
|
987
|
//
|
988
|
// TRANSLATION: LonLat <-> LayerPx
|
989
|
//
|
990
|
|
991
|
/**
|
992
|
* @param {OpenLayers.Pixel} px
|
993
|
*
|
994
|
* @type OpenLayers.LonLat
|
995
|
*/
|
996
|
getLonLatFromLayerPx: function (px) {
|
997
|
//adjust for displacement of layerContainerDiv
|
998
|
px = this.getViewPortPxFromLayerPx(px);
|
999
|
return this.getLonLatFromViewPortPx(px);
|
1000
|
},
|
1001
|
|
1002
|
/**
|
1003
|
* @param {OpenLayers.LonLat} lonlat
|
1004
|
*
|
1005
|
* @returns An OpenLayers.Pixel which is the passed-in OpenLayers.LonLat,
|
1006
|
* translated into layer pixels by the current base layer
|
1007
|
* @type OpenLayers.Pixel
|
1008
|
*/
|
1009
|
getLayerPxFromLonLat: function (lonlat) {
|
1010
|
//adjust for displacement of layerContainerDiv
|
1011
|
var px = this.getViewPortPxFromLonLat(lonlat);
|
1012
|
return this.getLayerPxFromViewPortPx(px);
|
1013
|
},
|
1014
|
|
1015
|
|
1016
|
CLASS_NAME: "OpenLayers.Map"
|
1017
|
};
|