1 | import {Evented} from '../core/Events';
|
2 | import {Map} from '../map/Map';
|
3 | import * as Util from '../core/Util';
|
4 |
|
5 | /*
|
6 | * @class Layer
|
7 | * @inherits Evented
|
8 | * @aka L.Layer
|
9 | * @aka ILayer
|
10 | *
|
11 | * A set of methods from the Layer base class that all Leaflet layers use.
|
12 | * Inherits all methods, options and events from `L.Evented`.
|
13 | *
|
14 | * @example
|
15 | *
|
16 | * ```js
|
17 | * var layer = L.marker(latlng).addTo(map);
|
18 | * layer.addTo(map);
|
19 | * layer.remove();
|
20 | * ```
|
21 | *
|
22 | * @event add: Event
|
23 | * Fired after the layer is added to a map
|
24 | *
|
25 | * @event remove: Event
|
26 | * Fired after the layer is removed from a map
|
27 | */
|
28 |
|
29 |
|
30 | export var Layer = Evented.extend({
|
31 |
|
32 | // Classes extending `L.Layer` will inherit the following options:
|
33 | options: {
|
34 | // @option pane: String = 'overlayPane'
|
35 | // By default the layer will be added to the map's [overlay pane](#map-overlaypane). Overriding this option will cause the layer to be placed on another pane by default.
|
36 | pane: 'overlayPane',
|
37 |
|
38 | // @option attribution: String = null
|
39 | // String to be shown in the attribution control, e.g. "© OpenStreetMap contributors". It describes the layer data and is often a legal obligation towards copyright holders and tile providers.
|
40 | attribution: null,
|
41 |
|
42 | bubblingMouseEvents: true
|
43 | },
|
44 |
|
45 | /* @section
|
46 | * Classes extending `L.Layer` will inherit the following methods:
|
47 | *
|
48 | * @method addTo(map: Map|LayerGroup): this
|
49 | * Adds the layer to the given map or layer group.
|
50 | */
|
51 | addTo: function (map) {
|
52 | map.addLayer(this);
|
53 | return this;
|
54 | },
|
55 |
|
56 | // @method remove: this
|
57 | // Removes the layer from the map it is currently active on.
|
58 | remove: function () {
|
59 | return this.removeFrom(this._map || this._mapToAdd);
|
60 | },
|
61 |
|
62 | // @method removeFrom(map: Map): this
|
63 | // Removes the layer from the given map
|
64 | //
|
65 | // @alternative
|
66 | // @method removeFrom(group: LayerGroup): this
|
67 | // Removes the layer from the given `LayerGroup`
|
68 | removeFrom: function (obj) {
|
69 | if (obj) {
|
70 | obj.removeLayer(this);
|
71 | }
|
72 | return this;
|
73 | },
|
74 |
|
75 | // @method getPane(name? : String): HTMLElement
|
76 | // Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer.
|
77 | getPane: function (name) {
|
78 | return this._map.getPane(name ? (this.options[name] || name) : this.options.pane);
|
79 | },
|
80 |
|
81 | addInteractiveTarget: function (targetEl) {
|
82 | this._map._targets[Util.stamp(targetEl)] = this;
|
83 | return this;
|
84 | },
|
85 |
|
86 | removeInteractiveTarget: function (targetEl) {
|
87 | delete this._map._targets[Util.stamp(targetEl)];
|
88 | return this;
|
89 | },
|
90 |
|
91 | // @method getAttribution: String
|
92 | // Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution).
|
93 | getAttribution: function () {
|
94 | return this.options.attribution;
|
95 | },
|
96 |
|
97 | _layerAdd: function (e) {
|
98 | var map = e.target;
|
99 |
|
100 | // check in case layer gets added and then removed before the map is ready
|
101 | if (!map.hasLayer(this)) { return; }
|
102 |
|
103 | this._map = map;
|
104 | this._zoomAnimated = map._zoomAnimated;
|
105 |
|
106 | if (this.getEvents) {
|
107 | var events = this.getEvents();
|
108 | map.on(events, this);
|
109 | this.once('remove', function () {
|
110 | map.off(events, this);
|
111 | }, this);
|
112 | }
|
113 |
|
114 | this.onAdd(map);
|
115 |
|
116 | this.fire('add');
|
117 | map.fire('layeradd', {layer: this});
|
118 | }
|
119 | });
|
120 |
|
121 | /* @section Extension methods
|
122 | * @uninheritable
|
123 | *
|
124 | * Every layer should extend from `L.Layer` and (re-)implement the following methods.
|
125 | *
|
126 | * @method onAdd(map: Map): this
|
127 | * Should contain code that creates DOM elements for the layer, adds them to `map panes` where they should belong and puts listeners on relevant map events. Called on [`map.addLayer(layer)`](#map-addlayer).
|
128 | *
|
129 | * @method onRemove(map: Map): this
|
130 | * Should contain all clean up code that removes the layer's elements from the DOM and removes listeners previously added in [`onAdd`](#layer-onadd). Called on [`map.removeLayer(layer)`](#map-removelayer).
|
131 | *
|
132 | * @method getEvents(): Object
|
133 | * This optional method should return an object like `{ viewreset: this._reset }` for [`addEventListener`](#evented-addeventlistener). The event handlers in this object will be automatically added and removed from the map with your layer.
|
134 | *
|
135 | * @method getAttribution(): String
|
136 | * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible.
|
137 | *
|
138 | * @method beforeAdd(map: Map): this
|
139 | * Optional method. Called on [`map.addLayer(layer)`](#map-addlayer), before the layer is added to the map, before events are initialized, without waiting until the map is in a usable state. Use for early initialization only.
|
140 | */
|
141 |
|
142 |
|
143 | /* @namespace Map
|
144 | * @section Layer events
|
145 | *
|
146 | * @event layeradd: LayerEvent
|
147 | * Fired when a new layer is added to the map.
|
148 | *
|
149 | * @event layerremove: LayerEvent
|
150 | * Fired when some layer is removed from the map
|
151 | *
|
152 | * @section Methods for Layers and Controls
|
153 | */
|
154 | Map.include({
|
155 | // @method addLayer(layer: Layer): this
|
156 | // Adds the given layer to the map
|
157 | addLayer: function (layer) {
|
158 | if (!layer._layerAdd) {
|
159 | throw new Error('The provided object is not a Layer.');
|
160 | }
|
161 |
|
162 | var id = Util.stamp(layer);
|
163 | if (this._layers[id]) { return this; }
|
164 | this._layers[id] = layer;
|
165 |
|
166 | layer._mapToAdd = this;
|
167 |
|
168 | if (layer.beforeAdd) {
|
169 | layer.beforeAdd(this);
|
170 | }
|
171 |
|
172 | this.whenReady(layer._layerAdd, layer);
|
173 |
|
174 | return this;
|
175 | },
|
176 |
|
177 | // @method removeLayer(layer: Layer): this
|
178 | // Removes the given layer from the map.
|
179 | removeLayer: function (layer) {
|
180 | var id = Util.stamp(layer);
|
181 |
|
182 | if (!this._layers[id]) { return this; }
|
183 |
|
184 | if (this._loaded) {
|
185 | layer.onRemove(this);
|
186 | }
|
187 |
|
188 | delete this._layers[id];
|
189 |
|
190 | if (this._loaded) {
|
191 | this.fire('layerremove', {layer: layer});
|
192 | layer.fire('remove');
|
193 | }
|
194 |
|
195 | layer._map = layer._mapToAdd = null;
|
196 |
|
197 | return this;
|
198 | },
|
199 |
|
200 | // @method hasLayer(layer: Layer): Boolean
|
201 | // Returns `true` if the given layer is currently added to the map
|
202 | hasLayer: function (layer) {
|
203 | return Util.stamp(layer) in this._layers;
|
204 | },
|
205 |
|
206 | /* @method eachLayer(fn: Function, context?: Object): this
|
207 | * Iterates over the layers of the map, optionally specifying context of the iterator function.
|
208 | * ```
|
209 | * map.eachLayer(function(layer){
|
210 | * layer.bindPopup('Hello');
|
211 | * });
|
212 | * ```
|
213 | */
|
214 | eachLayer: function (method, context) {
|
215 | for (var i in this._layers) {
|
216 | method.call(context, this._layers[i]);
|
217 | }
|
218 | return this;
|
219 | },
|
220 |
|
221 | _addLayers: function (layers) {
|
222 | layers = layers ? (Util.isArray(layers) ? layers : [layers]) : [];
|
223 |
|
224 | for (var i = 0, len = layers.length; i < len; i++) {
|
225 | this.addLayer(layers[i]);
|
226 | }
|
227 | },
|
228 |
|
229 | _addZoomLimit: function (layer) {
|
230 | if (!isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) {
|
231 | this._zoomBoundLayers[Util.stamp(layer)] = layer;
|
232 | this._updateZoomLevels();
|
233 | }
|
234 | },
|
235 |
|
236 | _removeZoomLimit: function (layer) {
|
237 | var id = Util.stamp(layer);
|
238 |
|
239 | if (this._zoomBoundLayers[id]) {
|
240 | delete this._zoomBoundLayers[id];
|
241 | this._updateZoomLevels();
|
242 | }
|
243 | },
|
244 |
|
245 | _updateZoomLevels: function () {
|
246 | var minZoom = Infinity,
|
247 | maxZoom = -Infinity,
|
248 | oldZoomSpan = this._getZoomSpan();
|
249 |
|
250 | for (var i in this._zoomBoundLayers) {
|
251 | var options = this._zoomBoundLayers[i].options;
|
252 |
|
253 | minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom);
|
254 | maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom);
|
255 | }
|
256 |
|
257 | this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom;
|
258 | this._layersMinZoom = minZoom === Infinity ? undefined : minZoom;
|
259 |
|
260 | // @section Map state change events
|
261 | // @event zoomlevelschange: Event
|
262 | // Fired when the number of zoomlevels on the map is changed due
|
263 | // to adding or removing a layer.
|
264 | if (oldZoomSpan !== this._getZoomSpan()) {
|
265 | this.fire('zoomlevelschange');
|
266 | }
|
267 |
|
268 | if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) {
|
269 | this.setZoom(this._layersMaxZoom);
|
270 | }
|
271 | if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) {
|
272 | this.setZoom(this._layersMinZoom);
|
273 | }
|
274 | }
|
275 | });
|