forked from openlayers/ol2
/
Map.js
2658 lines (2421 loc) · 92.7 KB
/
Map.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* Copyright (c) 2006-2012 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the 2-clause BSD license.
* See license.txt in the OpenLayers distribution or repository for the
* full text of the license. */
/**
* @requires OpenLayers/BaseTypes/Class.js
* @requires OpenLayers/Util.js
* @requires OpenLayers/Events.js
* @requires OpenLayers/Tween.js
* @requires OpenLayers/Projection.js
*/
/**
* Class: OpenLayers.Map
* Instances of OpenLayers.Map are interactive maps embedded in a web page.
* Create a new map with the <OpenLayers.Map> constructor.
*
* On their own maps do not provide much functionality. To extend a map
* it's necessary to add controls (<OpenLayers.Control>) and
* layers (<OpenLayers.Layer>) to the map.
*/
OpenLayers.Map = OpenLayers.Class({
/**
* Constant: Z_INDEX_BASE
* {Object} Base z-indexes for different classes of thing
*/
Z_INDEX_BASE: {
BaseLayer: 100,
Overlay: 325,
Feature: 725,
Popup: 750,
Control: 1000
},
/**
* APIProperty: events
* {<OpenLayers.Events>}
*
* Register a listener for a particular event with the following syntax:
* (code)
* map.events.register(type, obj, listener);
* (end)
*
* Listeners will be called with a reference to an event object. The
* properties of this event depends on exactly what happened.
*
* All event objects have at least the following properties:
* object - {Object} A reference to map.events.object.
* element - {DOMElement} A reference to map.events.element.
*
* Browser events have the following additional properties:
* xy - {<OpenLayers.Pixel>} The pixel location of the event (relative
* to the the map viewport).
*
* Supported map event types:
* preaddlayer - triggered before a layer has been added. The event
* object will include a *layer* property that references the layer
* to be added. When a listener returns "false" the adding will be
* aborted.
* addlayer - triggered after a layer has been added. The event object
* will include a *layer* property that references the added layer.
* preremovelayer - triggered before a layer has been removed. The event
* object will include a *layer* property that references the layer
* to be removed. When a listener returns "false" the removal will be
* aborted.
* removelayer - triggered after a layer has been removed. The event
* object will include a *layer* property that references the removed
* layer.
* changelayer - triggered after a layer name change, order change,
* opacity change, params change, visibility change (due to resolution
* thresholds) or attribution change (due to extent change). Listeners
* will receive an event object with *layer* and *property* properties.
* The *layer* property will be a reference to the changed layer. The
* *property* property will be a key to the changed property (name,
* order, opacity, params, visibility or attribution).
* movestart - triggered after the start of a drag, pan, or zoom
* move - triggered after each drag, pan, or zoom
* moveend - triggered after a drag, pan, or zoom completes
* zoomend - triggered after a zoom completes
* mouseover - triggered after mouseover the map
* mouseout - triggered after mouseout the map
* mousemove - triggered after mousemove the map
* changebaselayer - triggered after the base layer changes
* updatesize - triggered after the <updateSize> method was executed
*/
/**
* Property: id
* {String} Unique identifier for the map
*/
id: null,
/**
* Property: fractionalZoom
* {Boolean} For a base layer that supports it, allow the map resolution
* to be set to a value between one of the values in the resolutions
* array. Default is false.
*
* When fractionalZoom is set to true, it is possible to zoom to
* an arbitrary extent. This requires a base layer from a source
* that supports requests for arbitrary extents (i.e. not cached
* tiles on a regular lattice). This means that fractionalZoom
* will not work with commercial layers (Google, Yahoo, VE), layers
* using TileCache, or any other pre-cached data sources.
*
* If you are using fractionalZoom, then you should also use
* <getResolutionForZoom> instead of layer.resolutions[zoom] as the
* former works for non-integer zoom levels.
*/
fractionalZoom: false,
/**
* APIProperty: events
* {<OpenLayers.Events>} An events object that handles all
* events on the map
*/
events: null,
/**
* APIProperty: allOverlays
* {Boolean} Allow the map to function with "overlays" only. Defaults to
* false. If true, the lowest layer in the draw order will act as
* the base layer. In addition, if set to true, all layers will
* have isBaseLayer set to false when they are added to the map.
*
* Note:
* If you set map.allOverlays to true, then you *cannot* use
* map.setBaseLayer or layer.setIsBaseLayer. With allOverlays true,
* the lowest layer in the draw layer is the base layer. So, to change
* the base layer, use <setLayerIndex> or <raiseLayer> to set the layer
* index to 0.
*/
allOverlays: false,
/**
* APIProperty: div
* {DOMElement|String} The element that contains the map (or an id for
* that element). If the <OpenLayers.Map> constructor is called
* with two arguments, this should be provided as the first argument.
* Alternatively, the map constructor can be called with the options
* object as the only argument. In this case (one argument), a
* div property may or may not be provided. If the div property
* is not provided, the map can be rendered to a container later
* using the <render> method.
*
* Note:
* If you are calling <render> after map construction, do not use
* <maxResolution> auto. Instead, divide your <maxExtent> by your
* maximum expected dimension.
*/
div: null,
/**
* Property: dragging
* {Boolean} The map is currently being dragged.
*/
dragging: false,
/**
* Property: size
* {<OpenLayers.Size>} Size of the main div (this.div)
*/
size: null,
/**
* Property: viewPortDiv
* {HTMLDivElement} The element that represents the map viewport
*/
viewPortDiv: null,
/**
* Property: layerContainerOrigin
* {<OpenLayers.LonLat>} The lonlat at which the later container was
* re-initialized (on-zoom)
*/
layerContainerOrigin: null,
/**
* Property: layerContainerDiv
* {HTMLDivElement} The element that contains the layers.
*/
layerContainerDiv: null,
/**
* APIProperty: layers
* {Array(<OpenLayers.Layer>)} Ordered list of layers in the map
*/
layers: null,
/**
* APIProperty: controls
* {Array(<OpenLayers.Control>)} List of controls associated with the map.
*
* If not provided in the map options at construction, the map will
* by default be given the following controls if present in the build:
* - <OpenLayers.Control.Navigation> or <OpenLayers.Control.TouchNavigation>
* - <OpenLayers.Control.Zoom> or <OpenLayers.Control.PanZoom>
* - <OpenLayers.Control.ArgParser>
* - <OpenLayers.Control.Attribution>
*/
controls: null,
/**
* Property: popups
* {Array(<OpenLayers.Popup>)} List of popups associated with the map
*/
popups: null,
/**
* APIProperty: baseLayer
* {<OpenLayers.Layer>} The currently selected base layer. This determines
* min/max zoom level, projection, etc.
*/
baseLayer: null,
/**
* Property: center
* {<OpenLayers.LonLat>} The current center of the map
*/
center: null,
/**
* Property: resolution
* {Float} The resolution of the map.
*/
resolution: null,
/**
* Property: zoom
* {Integer} The current zoom level of the map
*/
zoom: 0,
/**
* Property: panRatio
* {Float} The ratio of the current extent within
* which panning will tween.
*/
panRatio: 1.5,
/**
* APIProperty: options
* {Object} The options object passed to the class constructor. Read-only.
*/
options: null,
// Options
/**
* APIProperty: tileSize
* {<OpenLayers.Size>} Set in the map options to override the default tile
* size for this map.
*/
tileSize: null,
/**
* APIProperty: projection
* {String} Set in the map options to specify the default projection
* for layers added to this map. When using a projection other than EPSG:4326
* (CRS:84, Geographic) or EPSG:3857 (EPSG:900913, Web Mercator),
* also set maxExtent, maxResolution or resolutions. Default is "EPSG:4326".
* Note that the projection of the map is usually determined
* by that of the current baseLayer (see <baseLayer> and <getProjectionObject>).
*/
projection: "EPSG:4326",
/**
* APIProperty: units
* {String} The map units. Possible values are 'degrees' (or 'dd'), 'm',
* 'ft', 'km', 'mi', 'inches'. Normally taken from the projection.
* Only required if both map and layers do not define a projection,
* or if they define a projection which does not define units
*/
units: null,
/**
* APIProperty: resolutions
* {Array(Float)} A list of map resolutions (map units per pixel) in
* descending order. If this is not set in the layer constructor, it
* will be set based on other resolution related properties
* (maxExtent, maxResolution, maxScale, etc.).
*/
resolutions: null,
/**
* APIProperty: maxResolution
* {Float} Required if you are not displaying the whole world on a tile
* with the size specified in <tileSize>.
*/
maxResolution: null,
/**
* APIProperty: minResolution
* {Float}
*/
minResolution: null,
/**
* APIProperty: maxScale
* {Float}
*/
maxScale: null,
/**
* APIProperty: minScale
* {Float}
*/
minScale: null,
/**
* APIProperty: maxExtent
* {<OpenLayers.Bounds>|Array} If provided as an array, the array
* should consist of four values (left, bottom, right, top).
* The maximum extent for the map. Defaults to the
* whole world in decimal degrees (-180, -90, 180, 90). Specify a
* different extent in the map options if you are not using a geographic
* projection and displaying the whole world. To restrict user panning
* and zooming of the map, use <restrictedExtent> instead. The value
* for <maxExtent> will change calculations for tile URLs.
*/
maxExtent: null,
/**
* APIProperty: minExtent
* {<OpenLayers.Bounds>|Array} If provided as an array, the array
* should consist of four values (left, bottom, right, top).
* The minimum extent for the map. Defaults to null.
*/
minExtent: null,
/**
* APIProperty: restrictedExtent
* {<OpenLayers.Bounds>|Array} If provided as an array, the array
* should consist of four values (left, bottom, right, top).
* Limit map navigation to this extent where possible.
* If a non-null restrictedExtent is set, panning will be restricted
* to the given bounds. In addition, zooming to a resolution that
* displays more than the restricted extent will center the map
* on the restricted extent. If you wish to limit the zoom level
* or resolution, use maxResolution.
*/
restrictedExtent: null,
/**
* APIProperty: numZoomLevels
* {Integer} Number of zoom levels for the map. Defaults to 16. Set a
* different value in the map options if needed.
*/
numZoomLevels: 16,
/**
* APIProperty: theme
* {String} Relative path to a CSS file from which to load theme styles.
* Specify null in the map options (e.g. {theme: null}) if you
* want to get cascading style declarations - by putting links to
* stylesheets or style declarations directly in your page.
*/
theme: null,
/**
* APIProperty: displayProjection
* {<OpenLayers.Projection>} Requires proj4js support for projections other
* than EPSG:4326 or EPSG:900913/EPSG:3857. Projection used by
* several controls to display data to user. If this property is set,
* it will be set on any control which has a null displayProjection
* property at the time the control is added to the map.
*/
displayProjection: null,
/**
* APIProperty: fallThrough
* {Boolean} Should OpenLayers allow events on the map to fall through to
* other elements on the page, or should it swallow them? (#457)
* Default is to fall through.
*/
fallThrough: true,
/**
* Property: panTween
* {<OpenLayers.Tween>} Animated panning tween object, see panTo()
*/
panTween: null,
/**
* APIProperty: eventListeners
* {Object} If set as an option at construction, the eventListeners
* object will be registered with <OpenLayers.Events.on>. Object
* structure must be a listeners object as shown in the example for
* the events.on method.
*/
eventListeners: null,
/**
* APIProperty: panMethod
* {Function} The Easing function to be used for tweening. Default is
* OpenLayers.Easing.Expo.easeOut. Setting this to 'null' turns off
* animated panning.
*/
panMethod: OpenLayers.Easing.Expo.easeOut,
/**
* Property: panDuration
* {Integer} The number of steps to be passed to the
* OpenLayers.Tween.start() method when the map is
* panned.
* Default is 50.
*/
panDuration: 50,
/**
* Property: paddingForPopups
* {<OpenLayers.Bounds>} Outside margin of the popup. Used to prevent
* the popup from getting too close to the map border.
*/
paddingForPopups : null,
/**
* Property: minPx
* {Object} An object with a 'x' and 'y' values that is the lower
* left of maxExtent in viewport pixel space.
* Used to verify in moveByPx that the new location we're moving to
* is valid. It is also used in the getLonLatFromViewPortPx function
* of Layer.
*/
minPx: null,
/**
* Property: maxPx
* {Object} An object with a 'x' and 'y' values that is the top
* right of maxExtent in viewport pixel space.
* Used to verify in moveByPx that the new location we're moving to
* is valid.
*/
maxPx: null,
/**
* Constructor: OpenLayers.Map
* Constructor for a new OpenLayers.Map instance. There are two possible
* ways to call the map constructor. See the examples below.
*
* Parameters:
* div - {DOMElement|String} The element or id of an element in your page
* that will contain the map. May be omitted if the <div> option is
* provided or if you intend to call the <render> method later.
* options - {Object} Optional object with properties to tag onto the map.
*
* Valid options (in addition to the listed API properties):
* center - {<OpenLayers.LonLat>|Array} The default initial center of the map.
* If provided as array, the first value is the x coordinate,
* and the 2nd value is the y coordinate.
* Only specify if <layers> is provided.
* Note that if an ArgParser/Permalink control is present,
* and the querystring contains coordinates, center will be set
* by that, and this option will be ignored.
* zoom - {Number} The initial zoom level for the map. Only specify if
* <layers> is provided.
* Note that if an ArgParser/Permalink control is present,
* and the querystring contains a zoom level, zoom will be set
* by that, and this option will be ignored.
* extent - {<OpenLayers.Bounds>|Array} The initial extent of the map.
* If provided as an array, the array should consist of
* four values (left, bottom, right, top).
* Only specify if <center> and <zoom> are not provided.
*
* Examples:
* (code)
* // create a map with default options in an element with the id "map1"
* var map = new OpenLayers.Map("map1");
*
* // create a map with non-default options in an element with id "map2"
* var options = {
* projection: "EPSG:3857",
* maxExtent: new OpenLayers.Bounds(-200000, -200000, 200000, 200000),
* center: new OpenLayers.LonLat(-12356463.476333, 5621521.4854095)
* };
* var map = new OpenLayers.Map("map2", options);
*
* // map with non-default options - same as above but with a single argument,
* // a restricted extent, and using arrays for bounds and center
* var map = new OpenLayers.Map({
* div: "map_id",
* projection: "EPSG:3857",
* maxExtent: [-18924313.432222, -15538711.094146, 18924313.432222, 15538711.094146],
* restrictedExtent: [-13358338.893333, -9608371.5085962, 13358338.893333, 9608371.5085962],
* center: [-12356463.476333, 5621521.4854095]
* });
*
* // create a map without a reference to a container - call render later
* var map = new OpenLayers.Map({
* projection: "EPSG:3857",
* maxExtent: new OpenLayers.Bounds(-200000, -200000, 200000, 200000)
* });
* (end)
*/
initialize: function (div, options) {
// If only one argument is provided, check if it is an object.
if(arguments.length === 1 && typeof div === "object") {
options = div;
div = options && options.div;
}
// Simple-type defaults are set in class definition.
// Now set complex-type defaults
this.tileSize = new OpenLayers.Size(OpenLayers.Map.TILE_WIDTH,
OpenLayers.Map.TILE_HEIGHT);
this.paddingForPopups = new OpenLayers.Bounds(15, 15, 15, 15);
this.theme = OpenLayers._getScriptLocation() +
'theme/default/style.css';
// backup original options
this.options = OpenLayers.Util.extend({}, options);
// now override default options
OpenLayers.Util.extend(this, options);
var projCode = this.projection instanceof OpenLayers.Projection ?
this.projection.projCode : this.projection;
OpenLayers.Util.applyDefaults(this, OpenLayers.Projection.defaults[projCode]);
// allow extents and center to be arrays
if (this.maxExtent && !(this.maxExtent instanceof OpenLayers.Bounds)) {
this.maxExtent = new OpenLayers.Bounds(this.maxExtent);
}
if (this.minExtent && !(this.minExtent instanceof OpenLayers.Bounds)) {
this.minExtent = new OpenLayers.Bounds(this.minExtent);
}
if (this.restrictedExtent && !(this.restrictedExtent instanceof OpenLayers.Bounds)) {
this.restrictedExtent = new OpenLayers.Bounds(this.restrictedExtent);
}
if (this.center && !(this.center instanceof OpenLayers.LonLat)) {
this.center = new OpenLayers.LonLat(this.center);
}
// initialize layers array
this.layers = [];
this.id = OpenLayers.Util.createUniqueID("OpenLayers.Map_");
this.div = OpenLayers.Util.getElement(div);
if(!this.div) {
this.div = document.createElement("div");
this.div.style.height = "1px";
this.div.style.width = "1px";
}
OpenLayers.Element.addClass(this.div, 'olMap');
// the viewPortDiv is the outermost div we modify
var id = this.id + "_OpenLayers_ViewPort";
this.viewPortDiv = OpenLayers.Util.createDiv(id, null, null, null,
"relative", null,
"hidden");
this.viewPortDiv.style.width = "100%";
this.viewPortDiv.style.height = "100%";
this.viewPortDiv.className = "olMapViewport";
this.div.appendChild(this.viewPortDiv);
this.events = new OpenLayers.Events(
this, this.viewPortDiv, null, this.fallThrough,
{includeXY: true}
);
// the layerContainerDiv is the one that holds all the layers
id = this.id + "_OpenLayers_Container";
this.layerContainerDiv = OpenLayers.Util.createDiv(id);
this.layerContainerDiv.style.width = '100px';
this.layerContainerDiv.style.height = '100px';
this.layerContainerDiv.style.zIndex=this.Z_INDEX_BASE['Popup']-1;
this.viewPortDiv.appendChild(this.layerContainerDiv);
this.updateSize();
if(this.eventListeners instanceof Object) {
this.events.on(this.eventListeners);
}
// Because Mozilla does not support the "resize" event for elements
// other than "window", we need to put a hack here.
if (parseFloat(navigator.appVersion.split("MSIE")[1]) < 9) {
// If IE < 9, register the resize on the div
this.events.register("resize", this, this.updateSize);
} else {
// Else updateSize on catching the window's resize
// Note that this is ok, as updateSize() does nothing if the
// map's size has not actually changed.
var me = this;
// We need to call this in the next cycle, otherwise it can cause
// issues on mobile devices on orientation change. It will use the
// height of the previous orientation otherwise.
this.updateSizeDestroy = function() {
window.setTimeout(function() { me.updateSize(); }, 0);
};
OpenLayers.Event.observe(window, 'resize',
this.updateSizeDestroy);
}
// only append link stylesheet if the theme property is set
if(this.theme) {
// check existing links for equivalent url
var addNode = true;
var nodes = document.getElementsByTagName('link');
for(var i=0, len=nodes.length; i<len; ++i) {
if(OpenLayers.Util.isEquivalentUrl(nodes.item(i).href,
this.theme)) {
addNode = false;
break;
}
}
// only add a new node if one with an equivalent url hasn't already
// been added
if(addNode) {
var cssNode = document.createElement('link');
cssNode.setAttribute('rel', 'stylesheet');
cssNode.setAttribute('type', 'text/css');
cssNode.setAttribute('href', this.theme);
document.getElementsByTagName('head')[0].appendChild(cssNode);
}
}
if (this.controls == null) { // default controls
this.controls = [];
if (OpenLayers.Control != null) { // running full or lite?
// Navigation or TouchNavigation depending on what is in build
if (OpenLayers.Control.Navigation) {
this.controls.push(new OpenLayers.Control.Navigation());
} else if (OpenLayers.Control.TouchNavigation) {
this.controls.push(new OpenLayers.Control.TouchNavigation());
}
if (OpenLayers.Control.Zoom) {
this.controls.push(new OpenLayers.Control.Zoom());
} else if (OpenLayers.Control.PanZoom) {
this.controls.push(new OpenLayers.Control.PanZoom());
}
if (OpenLayers.Control.ArgParser) {
this.controls.push(new OpenLayers.Control.ArgParser());
}
if (OpenLayers.Control.Attribution) {
this.controls.push(new OpenLayers.Control.Attribution());
}
}
}
for(var i=0, len=this.controls.length; i<len; i++) {
this.addControlToMap(this.controls[i]);
}
this.popups = [];
this.unloadDestroy = OpenLayers.Function.bind(this.destroy, this);
// always call map.destroy()
OpenLayers.Event.observe(window, 'unload', this.unloadDestroy);
// add any initial layers
if (options && options.layers) {
/**
* If you have set options.center, the map center property will be
* set at this point. However, since setCenter has not been called,
* addLayers gets confused. So we delete the map center in this
* case. Because the check below uses options.center, it will
* be properly set below.
*/
delete this.center;
delete this.zoom;
this.addLayers(options.layers);
// set center (and optionally zoom)
if (options.center && !this.getCenter()) {
// zoom can be undefined here
this.setCenter(options.center, options.zoom);
}
}
},
/**
* APIMethod: getViewport
* Get the DOMElement representing the view port.
*
* Returns:
* {DOMElement}
*/
getViewport: function() {
return this.viewPortDiv;
},
/**
* APIMethod: render
* Render the map to a specified container.
*
* Parameters:
* div - {String|DOMElement} The container that the map should be rendered
* to. If different than the current container, the map viewport
* will be moved from the current to the new container.
*/
render: function(div) {
this.div = OpenLayers.Util.getElement(div);
OpenLayers.Element.addClass(this.div, 'olMap');
this.viewPortDiv.parentNode.removeChild(this.viewPortDiv);
this.div.appendChild(this.viewPortDiv);
this.updateSize();
},
/**
* Method: unloadDestroy
* Function that is called to destroy the map on page unload. stored here
* so that if map is manually destroyed, we can unregister this.
*/
unloadDestroy: null,
/**
* Method: updateSizeDestroy
* When the map is destroyed, we need to stop listening to updateSize
* events: this method stores the function we need to unregister in
* non-IE browsers.
*/
updateSizeDestroy: null,
/**
* APIMethod: destroy
* Destroy this map.
* Note that if you are using an application which removes a container
* of the map from the DOM, you need to ensure that you destroy the
* map *before* this happens; otherwise, the page unload handler
* will fail because the DOM elements that map.destroy() wants
* to clean up will be gone. (See
* http://trac.osgeo.org/openlayers/ticket/2277 for more information).
* This will apply to GeoExt and also to other applications which
* modify the DOM of the container of the OpenLayers Map.
*/
destroy:function() {
// if unloadDestroy is null, we've already been destroyed
if (!this.unloadDestroy) {
return false;
}
// make sure panning doesn't continue after destruction
if(this.panTween) {
this.panTween.stop();
this.panTween = null;
}
// map has been destroyed. dont do it again!
OpenLayers.Event.stopObserving(window, 'unload', this.unloadDestroy);
this.unloadDestroy = null;
if (this.updateSizeDestroy) {
OpenLayers.Event.stopObserving(window, 'resize',
this.updateSizeDestroy);
} else {
this.events.unregister("resize", this, this.updateSize);
}
this.paddingForPopups = null;
if (this.controls != null) {
for (var i = this.controls.length - 1; i>=0; --i) {
this.controls[i].destroy();
}
this.controls = null;
}
if (this.layers != null) {
for (var i = this.layers.length - 1; i>=0; --i) {
//pass 'false' to destroy so that map wont try to set a new
// baselayer after each baselayer is removed
this.layers[i].destroy(false);
}
this.layers = null;
}
if (this.viewPortDiv) {
this.div.removeChild(this.viewPortDiv);
}
this.viewPortDiv = null;
if(this.eventListeners) {
this.events.un(this.eventListeners);
this.eventListeners = null;
}
this.events.destroy();
this.events = null;
this.options = null;
},
/**
* APIMethod: setOptions
* Change the map options
*
* Parameters:
* options - {Object} Hashtable of options to tag to the map
*/
setOptions: function(options) {
var updatePxExtent = this.minPx &&
options.restrictedExtent != this.restrictedExtent;
OpenLayers.Util.extend(this, options);
// force recalculation of minPx and maxPx
updatePxExtent && this.moveTo(this.getCachedCenter(), this.zoom, {
forceZoomChange: true
});
},
/**
* APIMethod: getTileSize
* Get the tile size for the map
*
* Returns:
* {<OpenLayers.Size>}
*/
getTileSize: function() {
return this.tileSize;
},
/**
* APIMethod: getBy
* Get a list of objects given a property and a match item.
*
* Parameters:
* array - {String} A property on the map whose value is an array.
* property - {String} A property on each item of the given array.
* match - {String | Object} A string to match. Can also be a regular
* expression literal or object. In addition, it can be any object
* with a method named test. For reqular expressions or other, if
* match.test(map[array][i][property]) evaluates to true, the item will
* be included in the array returned. If no items are found, an empty
* array is returned.
*
* Returns:
* {Array} An array of items where the given property matches the given
* criteria.
*/
getBy: function(array, property, match) {
var test = (typeof match.test == "function");
var found = OpenLayers.Array.filter(this[array], function(item) {
return item[property] == match || (test && match.test(item[property]));
});
return found;
},
/**
* APIMethod: getLayersBy
* Get a list of layers with properties matching the given criteria.
*
* Parameters:
* property - {String} A layer property to be matched.
* match - {String | Object} A string to match. Can also be a regular
* expression literal or object. In addition, it can be any object
* with a method named test. For reqular expressions or other, if
* match.test(layer[property]) evaluates to true, the layer will be
* included in the array returned. If no layers are found, an empty
* array is returned.
*
* Returns:
* {Array(<OpenLayers.Layer>)} A list of layers matching the given criteria.
* An empty array is returned if no matches are found.
*/
getLayersBy: function(property, match) {
return this.getBy("layers", property, match);
},
/**
* APIMethod: getLayersByName
* Get a list of layers with names matching the given name.
*
* Parameters:
* match - {String | Object} A layer name. The name can also be a regular
* expression literal or object. In addition, it can be any object
* with a method named test. For reqular expressions or other, if
* name.test(layer.name) evaluates to true, the layer will be included
* in the list of layers returned. If no layers are found, an empty
* array is returned.
*
* Returns:
* {Array(<OpenLayers.Layer>)} A list of layers matching the given name.
* An empty array is returned if no matches are found.
*/
getLayersByName: function(match) {
return this.getLayersBy("name", match);
},
/**
* APIMethod: getLayersByClass
* Get a list of layers of a given class (CLASS_NAME).
*
* Parameters:
* match - {String | Object} A layer class name. The match can also be a
* regular expression literal or object. In addition, it can be any
* object with a method named test. For reqular expressions or other,
* if type.test(layer.CLASS_NAME) evaluates to true, the layer will
* be included in the list of layers returned. If no layers are
* found, an empty array is returned.
*
* Returns:
* {Array(<OpenLayers.Layer>)} A list of layers matching the given class.
* An empty array is returned if no matches are found.
*/
getLayersByClass: function(match) {
return this.getLayersBy("CLASS_NAME", match);
},
/**
* APIMethod: getControlsBy
* Get a list of controls with properties matching the given criteria.
*
* Parameters:
* property - {String} A control property to be matched.
* match - {String | Object} A string to match. Can also be a regular
* expression literal or object. In addition, it can be any object
* with a method named test. For reqular expressions or other, if
* match.test(layer[property]) evaluates to true, the layer will be
* included in the array returned. If no layers are found, an empty
* array is returned.
*
* Returns:
* {Array(<OpenLayers.Control>)} A list of controls matching the given
* criteria. An empty array is returned if no matches are found.
*/
getControlsBy: function(property, match) {
return this.getBy("controls", property, match);
},
/**
* APIMethod: getControlsByClass
* Get a list of controls of a given class (CLASS_NAME).
*
* Parameters:
* match - {String | Object} A control class name. The match can also be a
* regular expression literal or object. In addition, it can be any
* object with a method named test. For reqular expressions or other,
* if type.test(control.CLASS_NAME) evaluates to true, the control will
* be included in the list of controls returned. If no controls are
* found, an empty array is returned.
*
* Returns:
* {Array(<OpenLayers.Control>)} A list of controls matching the given class.
* An empty array is returned if no matches are found.
*/
getControlsByClass: function(match) {
return this.getControlsBy("CLASS_NAME", match);
},
/********************************************************/
/* */
/* Layer Functions */
/* */
/* The following functions deal with adding and */
/* removing Layers to and from the Map */
/* */
/********************************************************/
/**
* APIMethod: getLayer
* Get a layer based on its id
*
* Parameters:
* id - {String} A layer id
*
* Returns:
* {<OpenLayers.Layer>} The Layer with the corresponding id from the map's
* layer collection, or null if not found.
*/
getLayer: function(id) {
var foundLayer = null;
for (var i=0, len=this.layers.length; i<len; i++) {
var layer = this.layers[i];
if (layer.id == id) {
foundLayer = layer;
break;
}
}
return foundLayer;
},
/**
* Method: setLayerZIndex
*
* Parameters:
* layer - {<OpenLayers.Layer>}
* zIdx - {int}
*/
setLayerZIndex: function (layer, zIdx) {
layer.setZIndex(
this.Z_INDEX_BASE[layer.isBaseLayer ? 'BaseLayer' : 'Overlay']
+ zIdx * 5 );
},
/**
* Method: resetLayersZIndex
* Reset each layer's z-index based on layer's array index
*/
resetLayersZIndex: function() {
for (var i=0, len=this.layers.length; i<len; i++) {
var layer = this.layers[i];
this.setLayerZIndex(layer, i);
}