Layer是 leaflet api 使用中接触最多的对象,各种数据都是通过 layer 来展示的,比如最基础的瓦片底图、点、线、面矢量图层等。
和Map一样,Layer 也是扩展自Evented类,自带了 on、off 事件函数。Layer是所有图层(除了icon之外)的基类,我们也可以基于它和它的子类进一步扩展。
图层之间的继承关系:
下面是 Layer 的继承扩展关系图。
Layer.js 源码
下面是 Layer 代码的解析,基类比较简单,不涉及复杂的数学和地图知识。
import {Evented} from '../core/Events';
import {Map} from '../map/Map';
import * as Util from '../core/Util';
/*
* @class Layer
* @inherits Evented
* @aka L.Layer
* @aka ILayer
*
* 从 L.Evented 扩展而来,是所有图层的基类
*
* @event add: Event
* layer 添加到地图的时候触发
*
* @event remove: Event
* layer 从地图上移除的时候触发
*/
export const Layer = Evented.extend({
// 扩展自 Layer 的图层将继承以下 option:
options: {
// @option pane: String = 'overlayPane'
// 默认所有的图层会被添加到 overlaypane 对应的 dom 中,覆盖这个属性会导致图层被添加到新的 pane dom 容器中
pane: 'overlayPane',
// @option attribution: String = null
// attribution control 中展示的内容,通常是描述图层的数据内容、数据提供者或者版权信息.
attribution: null,
bubblingMouseEvents: true
},
/* 将 layer 添加到地图 */
addTo(map) {
map.addLayer(this);
return this;
},
/** 从地图中移除这个图层 */
remove() {
return this.removeFrom(this._map || this._mapToAdd);
},
/** 从地图或者图层组中移除图层 */
removeFrom(obj) {
if (obj) {
obj.removeLayer(this);
}
return this;
},
/** 返回指定名称的 pane dom 元素,没有传 name 就返回当前图层的 pane */
getPane(name) {
return this._map.getPane(name ? (this.options[name] || name) : this.options.pane);
},
/** 将当前图层添加到 map._targets,在 map 响应事件的时候查找事件来源 */
addInteractiveTarget(targetEl) {
this._map._targets[Util.stamp(targetEl)] = this;
return this;
},
/** 将当前图层从 map._targets 移除,该图层的事件不会响应到 map 上 */
removeInteractiveTarget(targetEl) {
delete this._map._targets[Util.stamp(targetEl)];
return this;
},
// @method getAttribution: String
// attribution control 里调用, returns the [attribution option](#gridlayer-attribution).
getAttribution() {
return this.options.attribution;
},
_layerAdd(e) {
const map = e.target;
// check in case layer gets added and then removed before the map is ready
if (!map.hasLayer(this)) { return; }
this._map = map;
this._zoomAnimated = map._zoomAnimated;
if (this.getEvents) {
const events = this.getEvents();
map.on(events, this);
this.once('remove', function () {
map.off(events, this);
}, this);
}
this.onAdd(map);
// 触发用户注册的事件
this.fire('add');
map.fire('layeradd', {layer: this});
}
});
/* @section
* @uninheritable
*
* 所有扩展子类实现以下方法
*
* @method onAdd(map: Map): this
* 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).
* 创建图层对应的 dom 元素,并添加到 map pane,并监听相关的地图事件,在 map.addLayer 的时候调用
*
* @method onRemove(map: Map): this
* 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).
* 移除图层对应的 dom 元素,并添加到 map pane,并移除监听的相关的地图事件,在 map.removeLayer 的时候调用
*
* @method getEvents(): Object
* 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.
* 可选的方法,返回一个类似 { viewreset: this._reset } 格式的对象,addEventListener 方法会随着图层的添加和删除自动从 map 中注册和取消。
*
* @method getAttribution(): String
* This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible.
* 可选的方法,不论图层是否可见,这个方法都返回 Attribution 字符串
*
* @method beforeAdd(map: Map): this
* 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.
* 可选的方法,调用 map.addLayer(layer) 时候执行,在图层被添加到地图之前、在事件被初始化之前执行,不用等到地图处于可用状态。仅用于早期初始化。
*/
/* @namespace Map
* 向 map 中添加了两个事件
* @event layeradd: LayerEvent
* Fired when a new layer is added to the map.
*
* @event layerremove: LayerEvent
* Fired when some layer is removed from the map
*/
Map.include({
// 扩展的 map 中的方法
/** 添加图层 */
addLayer(layer) {
if (!layer._layerAdd) {
throw new Error('The provided object is not a Layer.');
}
const id = Util.stamp(layer);
if (this._layers[id]) { return this; }
this._layers[id] = layer;
layer._mapToAdd = this;
if (layer.beforeAdd) {
layer.beforeAdd(this);
}
this.whenReady(layer._layerAdd, layer);
return this;
},
/** 移除图层 */
removeLayer(layer) {
const id = Util.stamp(layer);
if (!this._layers[id]) { return this; }
if (this._loaded) {
layer.onRemove(this);
}
delete this._layers[id];
if (this._loaded) {
this.fire('layerremove', {layer});
layer.fire('remove');
}
layer._map = layer._mapToAdd = null;
return this;
},
/** 检查 map 中是否存在该图层 */
hasLayer(layer) {
// 通过判断唯一 id 是否存在
return Util.stamp(layer) in this._layers;
},
/* @method eachLayer(fn: Function, context?: Object): this
* Iterates over the layers of the map, optionally specifying context of the iterator function.
* 遍历所有图层,可以指定上下文
*/
eachLayer(method, context) {
for (const i in this._layers) {
method.call(context, this._layers[i]);
}
return this;
},
_addLayers(layers) {
// 判断传入的参数是不是个数组
layers = layers ? (Array.isArray(layers) ? layers : [layers]) : [];
// 循环加入
for (let i = 0, len = layers.length; i < len; i++) {
this.addLayer(layers[i]);
}
},
/** 添加图层的时候更新地图的 minZoom maxZoom */
_addZoomLimit(layer) {
// minZoom 和 minZoom 都没有设置,则从图层里更新
if (!isNaN(layer.options.minZoom) || !isNaN(layer.options.minZoom)) {
this._zoomBoundLayers[Util.stamp(layer)] = layer;
this._updateZoomLevels();
}
},
/** 移除图层的时候更新地图的 minZoom maxZoom */
_removeZoomLimit(layer) {
const id = Util.stamp(layer);
// 这个图层参与了 map 的 zoom 计算,删除这个图层后重新计算map的最小和最大 Zoom
if (this._zoomBoundLayers[id]) {
delete this._zoomBoundLayers[id];
this._updateZoomLevels();
}
},
/** 从所有图层中,取最小 minZoom 和最大 maxZoom,如果 zoom 不在新的 zoomSpan 内,更新 map的 zoom 缩放层级 */
_updateZoomLevels() {
let minZoom = Infinity,
maxZoom = -Infinity;
const oldZoomSpan = this._getZoomSpan();
// 遍历所有图层,取图层里的最大、最小 zoom
for (const i in this._zoomBoundLayers) {
const options = this._zoomBoundLayers[i].options;
minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom);
maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom);
}
// 图层里也没有 maxZoom,那么还是赋值为 undefined
this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom;
// 图层里也没有 minZoom,那么还是赋值为 undefined
this._layersMinZoom = minZoom === Infinity ? undefined : minZoom;
// @section Map state change events
// @event zoomlevelschange: Event
// 如果添加了一个图层导致map的 zoomSpan 变了,触发这个事件
if (oldZoomSpan !== this._getZoomSpan()) {
this.fire('zoomlevelschange');
}
// 如果当前的地图 zoom 超出了最新的 maxZoom,就设置为 maxZoom
if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) {
this.setZoom(this._layersMaxZoom);
}
// 如果当前的地图 zoom 超出了最新的 minZoom,就设置为 minZoom
if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) {
this.setZoom(this._layersMinZoom);
}
}
});