openlayers源码阅读(六):渲染从Map说起

openlayers源码阅读(六):渲染从Map说起

前面几篇主要是从地图服务的使用角度,去阅读了图层数据源相关的源码。便于我们去很好的理解OGC的WMS、WMTS、WFS等地图服务标准及调用,以及针对ArcGIS Server 发布的服务和OSM、bing等使用。而接下来的几篇,会从渲染的角度去阅读源码,更好地理解从数据到图的过程。

一、地图初始化

<div id = 'map'></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">OpenStreetMap</a> 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-layer // 图层
          • canvas // 画布
        • ol-layer // 图层
          • canvas // 画布
      • ol-overlaycontainer // 用于弹出图层 —— Map.overlays

      • ol-overlaycontainer-stopevent // 用于弹出事件 —— Map.controls

        • ol-control // 控件
        • ol-control // 控件

地图渲染流程主要包括:

1、获取数据

2、构建DOM

扫描二维码关注公众号,回复: 12890832 查看本文章

3、解析视图参数,定位视角

4、解析图层参数,渲染到canvas上

5、解析控件参数,添加到DOM上,并监听事件

6、解析交互参数,添加监听事件

二、ol.Map及父类ol.PluggableMap

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

猜你喜欢

转载自blog.csdn.net/u013240519/article/details/104997512