前面几篇主要是从地图服务的使用角度,去阅读了图层数据源相关的源码。便于我们去很好的理解OGC的WMS、WMTS、WFS等地图服务标准及调用,以及针对ArcGIS Server 发布的服务和OSM、bing等使用。而接下来的几篇,会从渲染的角度去阅读源码,更好地理解从数据到图的过程。
div>
var map = new ol.Map({
view: new ol.View({
center: [0, 0],
zoom: 1
}),
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
target: 'map'
});
生成后的的html效果:
<div id="map" class="map">
<div class="ol-viewport" style="position: relative; overflow: hidden; width: 100%; height: 100%;">
<div style="position: absolute; width: 100%; height: 100%; z-index: 0;" class="ol-unselectable ol-layers">
<div class="ol-layer" style="position: absolute; width: 100%; height: 100%;">
<canvas style="position: absolute; left: 0px; transform-origin: left top 0px; transform: matrix(1, 0, 0, 1, 0, 0);" width="1489" height="400">canvas>
div>
div>
<div style="position: absolute; z-index: 0; width: 100%; height: 100%;" class="ol-overlaycontainer">div>
<div style="position: absolute; z-index: 0; width: 100%; height: 100%;" class="ol-overlaycontainer-stopevent">
<div class="ol-zoom ol-unselectable ol-control">
<button class="ol-zoom-in" type="button" title="Zoom in">+button>
<button class="ol-zoom-out" type="button" title="Zoom out">−button>
div>
<div class="ol-rotate ol-unselectable ol-control ol-hidden">
<button class="ol-rotate-reset" type="button" title="Reset rotation">
<span class="ol-compass" style="transform: rotate(0rad);">⇧span>
button>
div>
<div class="ol-attribution ol-unselectable ol-control ol-uncollapsible">
<ul>
<li>© <a href="https://www.openstreetmap.org/copyright" target="_blank">OpenStreetMapa> contributors.li>
ul>
<button type="button" title="Attributions">
<span>»span>
button>
div>
div>
div>
div>
地图的初始化后,原有的一个空的div容器,添加了如canvas、button等标签,结构如下:
map —— Map.targert
ol-viewport // 视图 —— Map.view
ol-layers // 用于图层 —— Map.layers
ol-overlaycontainer // 用于弹出图层 —— Map.overlays
ol-overlaycontainer-stopevent // 用于弹出事件 —— Map.controls
地图渲染流程主要包括:
1、获取数据
2、构建DOM
3、解析视图参数,定位视角
4、解析图层参数,渲染到canvas上
5、解析控件参数,添加到DOM上,并监听事件
6、解析交互参数,添加监听事件
ol.Map为主入口,继承自ol.PluggableMap,主要逻辑在ol.PluggableMap中实现
2.1:执行ol.Map的构造函数,接收参数,判断控件和交互事件
// ol.map
ol.Map = function(options) {
options = ol.obj.assign({}, options);
if (!options.controls) {
options.controls = ol.control.defaults(); // 默认控件
}
if (!options.interactions) {
options.interactions = ol.interaction.defaults(); // 默认交互事件
}
ol.PluggableMap.call(this, options);
};
ol.inherits(ol.Map, ol.PluggableMap);
2.2:ol.PluggableMap接受参数,并执行构造函数
2.2.1:执行createOptionsInternal()方法,创建内部选项
var optionsInternal = ol.PluggableMap.createOptionsInternal(options);
/**
* @param {olx.MapOptions} options Map options.
* @return {ol.MapOptionsInternal} Internal map options.
*/
ol.PluggableMap.createOptionsInternal = function(options) {
……
return {
controls: controls,
interactions: interactions,
keyboardEventTarget: keyboardEventTarget,
logos: logos,
overlays: overlays,
mapRendererPlugin: mapRendererPlugin, // 地图渲染器插件
values: values
};
};
2.2.2:构建DOM——ol-viewport 及子容器ol-overlaycontainer、ol-overlaycontainer-stopevent
this.viewport_ = document.createElement('DIV');
this.viewport_.className = 'ol-viewport' + (ol.has.TOUCH ? ' ol-touch' : '');
this.viewport_.style.position = 'relative';
this.viewport_.style.overflow = 'hidden';
this.viewport_.style.width = '100%';
this.viewport_.style.height = '100%';
// prevent page zoom on IE >= 10 browsers
this.viewport_.style.msTouchAction = 'none';
this.viewport_.style.touchAction = 'none';
this.overlayContainer_ = document.createElement('DIV');
this.overlayContainer_.className = 'ol-overlaycontainer';
this.viewport_.appendChild(this.overlayContainer_);
this.overlayContainerStopEvent_ = document.createElement('DIV');
this.overlayContainerStopEvent_.className = 'ol-overlaycontainer-stopevent';
var overlayEvents = [
ol.events.EventType.CLICK,
ol.events.EventType.DBLCLICK,
ol.events.EventType.MOUSEDOWN,
ol.events.EventType.TOUCHSTART,
ol.events.EventType.MSPOINTERDOWN,
ol.MapBrowserEventType.POINTERDOWN,
ol.events.EventType.MOUSEWHEEL,
ol.events.EventType.WHEEL
];
// 监听ol-overlaycontainer-stopevent事件、阻止冒泡
for (var i = 0, ii = overlayEvents.length; i < ii; ++i) {
ol.events.listen(this.overlayContainerStopEvent_, overlayEvents[i],
ol.events.Event.stopPropagation);
}
this.viewport_.appendChild(this.overlayContainerStopEvent_);
2.2.3: 监听事件:地图浏览事件、LAYERGROUP、VIEW、SIZE、SIZE的change事件,
// map browser events
this.mapBrowserEventHandler_ = new ol.MapBrowserEventHandler(this, options.moveTolerance);
for (var key in ol.MapBrowserEventType) {
ol.events.listen(this.mapBrowserEventHandler_, ol.MapBrowserEventType[key],
this.handleMapBrowserEvent, this);
}
// LAYERGROUP、VIEW、SIZE、SIZE的change事件
ol.events.listen(
this, ol.Object.getChangeEventType(ol.MapProperty.LAYERGROUP),
this.handleLayerGroupChanged_, this);
ol.events.listen(this, ol.Object.getChangeEventType(ol.MapProperty.VIEW),
this.handleViewChanged_, this);
ol.events.listen(this, ol.Object.getChangeEventType(ol.MapProperty.SIZE),
this.handleSizeChanged_, this);
ol.events.listen(this, ol.Object.getChangeEventType(ol.MapProperty.SIZE),
this.handleTargetChanged_, this);
2.2.4:创建切片队列
/**
* @private
* @type {ol.TileQueue}
*/
this.tileQueue_ = new ol.TileQueue(
this.getTilePriority.bind(this),
this.handleTileChange_.bind(this));
2.2.5: control、interaction、overlay的绑定、添加、移除
// ol.PluggableMap 绑定控件、添加控件、删除控件
this.controls.forEach(
/**
* @param {ol.control.Control} control Control.
* @this {ol.PluggableMap}
*/
function(control) {
control.setMap(this);
}, this);
ol.events.listen(this.controls, ol.CollectionEventType.ADD,
/**
* @param {ol.Collection.Event} event Collection event.
*/
function(event) {
event.element.setMap(this);
}, this);
ol.events.listen(this.controls, ol.CollectionEventType.REMOVE,
/**
* @param {ol.Collection.Event} event Collection event.
*/
function(event) {
event.element.setMap(null);
}, this);
// ol.PluggableMap 绑定交互、添加交互、删除交互
this.interactions.forEach(
/**
* @param {ol.interaction.Interaction} interaction Interaction.
* @this {ol.PluggableMap}
*/
function(interaction) {
interaction.setMap(this);
}, this);
ol.events.listen(this.interactions, ol.CollectionEventType.ADD,
/**
* @param {ol.Collection.Event} event Collection event.
*/
function(event) {
event.element.setMap(this);
}, this);
ol.events.listen(this.interactions, ol.CollectionEventType.REMOVE,
/**
* @param {ol.Collection.Event} event Collection event.
*/
function(event) {
event.element.setMap(null);
}, this);
// ol.PluggableMap 绑定弹出层、添加弹出层、删除弹出层
this.overlays_.forEach(this.addOverlayInternal_, this);
ol.events.listen(this.overlays_, ol.CollectionEventType.ADD,
/**
* @param {ol.Collection.Event} event Collection event.
*/
function(event) {
this.addOverlayInternal_(/** @type {ol.Overlay} */ (event.element));
}, this);
ol.events.listen(this.overlays_, ol.CollectionEventType.REMOVE,
/**
* @param {ol.Collection.Event} event Collection event.
*/
function(event) {
var overlay = /** @type {ol.Overlay} */ (event.element);
var id = overlay.getId();
if (id !== undefined) {
delete this.overlayIdIndex_[id.toString()];
}
event.element.setMap(null);
}, this);
};
2.2.6 执行renderFrame_函数(*)
// 指定渲染器
/**
* @type {ol.renderer.Map}
* @private
*/
this.renderer_ = optionsInternal.mapRendererPlugin['create'](this.viewport_, this);
/**
* @private
* @type {number|undefined}
*/
this.animationDelayKey_;
/**
* @private
*/
this.animationDelay_ = function() {
this.animationDelayKey_ = undefined;
this.renderFrame_.call(this, Date.now());
}.bind(this);
/**
* @param {number} time Time.
* @private
*/
ol.PluggableMap.prototype.renderFrame_ = function(time) {
var i, ii, viewState;
var size = this.getSize();
var view = this.getView();
var extent = ol.extent.createEmpty();
var previousFrameState = this.frameState_;
/** @type {?olx.FrameState} */
var frameState = null;
if (size !== undefined && ol.size.hasArea(size) && view && view.isDef()) {
var viewHints = view.getHints(this.frameState_ ? this.frameState_.viewHints : undefined);
var layerStatesArray = this.getLayerGroup().getLayerStatesArray();
var layerStates = {};
for (i = 0, ii = layerStatesArray.length; i < ii; ++i) {
layerStates[ol.getUid(layerStatesArray[i].layer)] = layerStatesArray[i];
}
viewState = view.getState();
var center = viewState.center;
var pixelResolution = viewState.resolution / this.pixelRatio_;
center[0] = Math.round(center[0] / pixelResolution) * pixelResolution;
center[1] = Math.round(center[1] / pixelResolution) * pixelResolution;
frameState = /** @type {olx.FrameState} */ ({
animate: false,
coordinateToPixelTransform: this.coordinateToPixelTransform_,
extent: extent,
focus: !this.focus_ ? center : this.focus_,
index: this.frameIndex_++,
layerStates: layerStates,
layerStatesArray: layerStatesArray,
logos: ol.obj.assign({}, this.logos_),
pixelRatio: this.pixelRatio_,
pixelToCoordinateTransform: this.pixelToCoordinateTransform_,
postRenderFunctions: [],
size: size,
skippedFeatureUids: this.skippedFeatureUids_,
tileQueue: this.tileQueue_,
time: time,
usedTiles: {},
viewState: viewState,
viewHints: viewHints,
wantedTiles: {}
});
}
if (frameState) {
frameState.extent = ol.extent.getForViewAndSize(viewState.center,
viewState.resolution, viewState.rotation, frameState.size, extent);
}
this.frameState_ = frameState;
// 构建frameState,然后执行渲染器(ol.renderer.canvas.Map等)的渲染过程
this.renderer_.renderFrame(frameState);
if (frameState) {
if (frameState.animate) {
this.render();
}
Array.prototype.push.apply(
this.postRenderFunctions_, frameState.postRenderFunctions);
if (previousFrameState) {
var moveStart = !this.previousExtent_ ||
(!ol.extent.isEmpty(this.previousExtent_) &&
!ol.extent.equals(frameState.extent, this.previousExtent_));
if (moveStart) {
this.dispatchEvent(
new ol.MapEvent(ol.MapEventType.MOVESTART, this, previousFrameState));
this.previousExtent_ = ol.extent.createOrUpdateEmpty(this.previousExtent_);
}
}
var idle = this.previousExtent_ &&
!frameState.viewHints[ol.ViewHint.ANIMATING] &&
!frameState.viewHints[ol.ViewHint.INTERACTING] &&
!ol.extent.equals(frameState.extent, this.previousExtent_);
if (idle) {
this.dispatchEvent(
new ol.MapEvent(ol.MapEventType.MOVEEND, this, frameState));
ol.extent.clone(frameState.extent, this.previousExtent_);
}
}
this.dispatchEvent(
new ol.MapEvent(ol.MapEventType.POSTRENDER, this, frameState));
setTimeout(this.handlePostRender.bind(this), 0);
};
2.2.7 触发ol.renderer.canvas.Map的renderFrame事件、
/**
* @inheritDoc
*/
ol.renderer.canvas.Map.prototype.renderFrame = function(frameState) {
if (!frameState) {
if (this.renderedVisible_) {
this.canvas_.style.display = 'none';
this.renderedVisible_ = false;
}
return;
}
var context = this.context_;
var pixelRatio = frameState.pixelRatio;
var width = Math.round(frameState.size[0] * pixelRatio);
var height = Math.round(frameState.size[1] * pixelRatio);
if (this.canvas_.width != width || this.canvas_.height != height) {
this.canvas_.width = width;
this.canvas_.height = height;
} else {
context.clearRect(0, 0, width, height);
}
var rotation = frameState.viewState.rotation;
this.calculateMatrices2D(frameState);
this.dispatchComposeEvent_(ol.render.EventType.PRECOMPOSE, frameState);
var layerStatesArray = frameState.layerStatesArray;
ol.array.stableSort(layerStatesArray, ol.renderer.Map.sortByZIndex);
if (rotation) {
context.save();
ol.render.canvas.rotateAtOffset(context, rotation, width / 2, height / 2);
}
var viewResolution = frameState.viewState.resolution;
var i, ii, layer, layerRenderer, layerState;
// 从frameState中取出图层组信息layerStatesArray,渲染图层
for (i = 0, ii = layerStatesArray.length; i < ii; ++i) {
layerState = layerStatesArray[i];
layer = layerState.layer;
layerRenderer = /** @type {ol.renderer.canvas.Layer} */ (this.getLayerRenderer(layer));
if (!ol.layer.Layer.visibleAtResolution(layerState, viewResolution) ||
layerState.sourceState != ol.source.State.READY) {
continue;
}
if (layerRenderer.prepareFrame(frameState, layerState)) {
layerRenderer.composeFrame(frameState, layerState, context);
}
}
if (rotation) {
context.restore();
}
this.dispatchComposeEvent_(
ol.render.EventType.POSTCOMPOSE, frameState);
if (!this.renderedVisible_) {
this.canvas_.style.display = '';
this.renderedVisible_ = true;
}
this.scheduleRemoveUnusedLayerRenderers(frameState);
this.scheduleExpireIconCache(frameState);
};
思路整理
路线:ol.Map -> ol.PluggableMap ——>ol.renderer.canvas.Map
- ol.Map 赋值
- ol.PluggableMap 构建dom、监听事件、frameState
- ol.renderer.canvas.Map 渲染renderFrame(frameState),其中依次渲染图层
重要变量frameState
frameState = /** @type {olx.FrameState} */ ({
animate: false,
coordinateToPixelTransform: this.coordinateToPixelTransform_,
extent: extent,
focus: !this.focus_ ? center : this.focus_,
index: this.frameIndex_++,
layerStates: layerStates,
layerStatesArray: layerStatesArray,
logos: ol.obj.assign({}, this.logos_),
pixelRatio: this.pixelRatio_,
pixelToCoordinateTransform: this.pixelToCoordinateTransform_,
postRenderFunctions: [],
size: size,
skippedFeatureUids: this.skippedFeatureUids_,
tileQueue: this.tileQueue_,
time: time,
usedTiles: {},
viewState: viewState,
viewHints: viewHints,
wantedTiles: {}
});重点关注的属性:extent、layerStates、layerStatesArray、size、tileQueue、viewState