源码分析之Openlayers中的核心基类Layer类介绍

访问Openlayers网站(https://jinuss.github.io/Openlayers_map_pages/,网站是基于Vue3 + Openlayers,里面有大量的实践和案例。觉得还不错,可以在这里插入图片描述 给个小星星Star,鼓励一波 https://github.com/Jinuss/OpenlayersMap哦~

概述

本文将介绍Layer类,该类是所有图层类型都会继承的基类,一般情况下,在添加自定义图层,并使用自定义的render函数时,该类才应该被实例化。

源码剖析

Layer类继承于BaseLayer类,在前面提过BaseLayer类主要是设置对象的一些属性值,以及提供GETSET两类方法。Layer类的实现如下:

class Layer extends BaseLayer {
  constructor(options) {
    const baseOptions = Object.assign({}, options);
    delete baseOptions.source;

    super(baseOptions);
    this.on;
    this.once;
    this.un;
    this.mapPrecomposeKey_ = null;
    this.mapRenderKey_ = null;
    this.sourceChangeKey_ = null;
    this.renderer_ = null;
    this.sourceReady_ = false;
    this.rendered = false;
    if (options.render) {
      this.render = options.render;
    }

    if (options.map) {
      this.setMap(options.map);
    }

    this.addChangeListener(
      LayerProperty.SOURCE,
      this.handleSourcePropertyChange_
    );

    const source = options.source ? options.source : null;
    this.setSource(source);
  }

  getLayersArray(array) {
    array = array ? array : [];
    array.push(this);
    return array;
  }
  getLayerStatesArray(states) {
    states = states ? states : [];
    states.push(this.getLayerState());
    return states;
  }
  getSource() {
    return this.get(LayerProperty.SOURCE) || null;
  }
  getRenderSource() {
    return this.getSource();
  }
  getSourceState() {
    const source = this.getSource();
    return !source ? "undefined" : source.getState();
  }

  handleSourceChange_() {
    this.changed();
    if (this.sourceReady_ || this.getSource().getState() !== "ready") {
      return;
    }
    this.sourceReady_ = true;
    this.dispatchEvent("sourceready");
  }

  handleSourcePropertyChange_() {
    if (this.sourceChangeKey_) {
      unlistenByKey(this.sourceChangeKey_);
      this.sourceChangeKey_ = null;
    }
    this.sourceReady_ = false;
    const source = this.getSource();
    if (source) {
      this.sourceChangeKey_ = listen(
        source,
        EventType.CHANGE,
        this.handleSourceChange_,
        this
      );
      if (source.getState() === "ready") {
        this.sourceReady_ = true;
        setTimeout(() => {
          this.dispatchEvent("sourceready");
        }, 0);
      }
    }
    this.changed();
  }

  getFeatures(pixel) {
    if (!this.renderer_) {
      return Promise.resolve([]);
    }
    return this.renderer_.getFeatures(pixel);
  }

  getData(pixel) {
    if (!this.renderer_ || !this.rendered) {
      return null;
    }
    return this.renderer_.getData(pixel);
  }
  isVisible(view) {
    let frameState;
    const map = this.getMapInternal();
    if (!view && map) {
      view = map.getView();
    }
    if (view instanceof View) {
      frameState = {
        viewState: view.getState(),
        extent: view.calculateExtent(),
      };
    } else {
      frameState = view;
    }
    if (!frameState.layerStatesArray && map) {
      frameState.layerStatesArray = map.getLayerGroup().getLayerStatesArray();
    }
    let layerState;
    if (frameState.layerStatesArray) {
      layerState = frameState.layerStatesArray.find(
        (layerState) => layerState.layer === this
      );
    } else {
      layerState = this.getLayerState();
    }

    const layerExtent = this.getExtent();

    return (
      inView(layerState, frameState.viewState) &&
      (!layerExtent || intersects(layerExtent, frameState.extent))
    );
  }

  getAttributions(view) {
    if (!this.isVisible(view)) {
      return [];
    }
    const getAttributions = this.getSource()?.getAttributions();
    if (!getAttributions) {
      return [];
    }
    const frameState =
      view instanceof View ? view.getViewStateAndExtent() : view;
    let attributions = getAttributions(frameState);
    if (!Array.isArray(attributions)) {
      attributions = [attributions];
    }
    return attributions;
  }

  render(frameState, target) {
    const layerRenderer = this.getRenderer();

    if (layerRenderer.prepareFrame(frameState)) {
      this.rendered = true;
      return layerRenderer.renderFrame(frameState, target);
    }
    return null;
  }

  unrender() {
    this.rendered = false;
  }
  getDeclutter() {
    return undefined;
  }
  renderDeclutter(frameState, layerState) {}

  renderDeferred(frameState) {
    const layerRenderer = this.getRenderer();
    if (!layerRenderer) {
      return;
    }
    layerRenderer.renderDeferred(frameState);
  }
  setMapInternal(map) {
    if (!map) {
      this.unrender();
    }
    this.set(LayerProperty.MAP, map);
  }

  getMapInternal() {
    return this.get(LayerProperty.MAP);
  }

  setMap(map) {
    if (this.mapPrecomposeKey_) {
      unlistenByKey(this.mapPrecomposeKey_);
      this.mapPrecomposeKey_ = null;
    }
    if (!map) {
      this.changed();
    }
    if (this.mapRenderKey_) {
      unlistenByKey(this.mapRenderKey_);
      this.mapRenderKey_ = null;
    }
    if (map) {
      this.mapPrecomposeKey_ = listen(
        map,
        RenderEventType.PRECOMPOSE,
        this.handlePrecompose_,
        this
      );
      this.mapRenderKey_ = listen(this, EventType.CHANGE, map.render, map);
      this.changed();
    }
  }

  handlePrecompose_(renderEvent) {
    const layerStatesArray = renderEvent.frameState.layerStatesArray;
    const layerState = this.getLayerState(false);
    assert(
      !layerStatesArray.some(
        (arrayLayerState) => arrayLayerState.layer === layerState.layer
      ),
      "A layer can only be added to the map once. Use either `layer.setMap()` or `map.addLayer()`, not both."
    );
    layerStatesArray.push(layerState);
  }

  setSource(source) {
    this.set(LayerProperty.SOURCE, source);
  }

  getRenderer() {
    if (!this.renderer_) {
      this.renderer_ = this.createRenderer();
    }
    return this.renderer_;
  }

  hasRenderer() {
    return !!this.renderer_;
  }

  createRenderer() {
    return null;
  }
  disposeInternal() {
    if (this.renderer_) {
      this.renderer_.dispose();
      delete this.renderer_;
    }

    this.setSource(null);
    super.disposeInternal();
  }
}

Layer类的构造函数

Layer类构造函数接受一个参数Options对象将其赋值给变量baseOptions,并删除baseOptionssource属性,通过super(baseOptions)初始化父类;然后初始化了几个变量;

然后判断options.render是否存在,若存在则赋值给this.render,覆盖Layer类中的render方法;判断options.map是否存在,若存在,则调用this.setMap方法,如下:

  • render方法
  render(frameState, target) {
    const layerRenderer = this.getRenderer();

    if (layerRenderer.prepareFrame(frameState)) {
      this.rendered = true;
      return layerRenderer.renderFrame(frameState, target);
    }
    return null;
  }

render方法的作用就是负责渲染图层,每种类型的图层都有之与其对应的图层渲染器。它接受两个参数帧状态frameState和目标容器targetrender方法内部就是获取调用getRenderer获取图层渲染器,然后判断layerRenderer.prepareFrame(frameState)是否准备渲染帧,若返回true,则修改this.rendered值为true,调用layerRenderer.renderFrame(frameState, target)将帧渲染到target即网页容器中;若返回false,则render方法返回null

  • setMap方法
  setMap(map) {
    if (this.mapPrecomposeKey_) {
      unlistenByKey(this.mapPrecomposeKey_);
      this.mapPrecomposeKey_ = null;
    }
    if (!map) {
      this.changed();
    }
    if (this.mapRenderKey_) {
      unlistenByKey(this.mapRenderKey_);
      this.mapRenderKey_ = null;
    }
    if (map) {
      this.mapPrecomposeKey_ = listen(
        map,
        RenderEventType.PRECOMPOSE,
        this.handlePrecompose_,
        this,
      );
      this.mapRenderKey_ = listen(this, EventType.CHANGE, map.render, map);
      this.changed();
    }
  }

setMap方法在Layer类中调用时,参数map肯定存在,因此现阶段只讨论map存在的情况;map存在时,会调用listen方法注册postcompose类型的this.handlePrecompose_事件和change类型的map.render事件,最后调用this.changed方法。
listen方法,listen方法在中提过,本质上还是调用EventTarget类中的addEventListener方法,而最后调用的this.changed方法会调用Observable类中的changed方法,继而调用dispatchEvent派发change类型的注册事件,前面注册的map.render事件,即参数maprender方法。

最后调用this.addeventListener方法注册source类型的this.handleSourcePropertyChange_事件;然后调用setSource方法。

  • setSource方法
  setSource(source) {
    this.set(LayerProperty.SOURCE, source);
  }

setSource方法就是调用BaseObject类中的set方法,第一次调用时(新旧值不一样时)会触发source的注册事件,即handleSourcePropertyChange_方法

  • handleSourcePropertyChange_
handleSourcePropertyChange_() {
    if (this.sourceChangeKey_) {
      unlistenByKey(this.sourceChangeKey_);
      this.sourceChangeKey_ = null;
    }
    this.sourceReady_ = false;
    const source = this.getSource();
    if (source) {
      this.sourceChangeKey_ = listen(
        source,
        EventType.CHANGE,
        this.handleSourceChange_,
        this,
      );
      if (source.getState() === 'ready') {
        this.sourceReady_ = true;
        setTimeout(() => {
          this.dispatchEvent('sourceready');
        }, 0);
      }
    }
    this.changed();
  }

handleSourcePropertyChange_方法首先会判断this.sourceChangeKey_是否存在,若存在则解除EventKey键为this.sourceChangeKey_的监听,然后将其置为null;然后设置this.sourceReady_false。调用this.getSource()方法,它内部就是调用BaseObject类的get方法获取source的值并返回,若source存在,则注册change 类型的handleSourceChange_事件;
判断图层源source的状态是否为ready,若是,则设置this.sourceReady_true,然后设置一个定时器调用dispatcheEvent('sourceready')方法。
最后调用changed事件,派发事件,执行类型为change的注册事件,调用handleSourceChange_方法。而handleSourceChange_内部还是会在this.sourceReady_false或者sourceready的前提下去调用dispatcheEvent('sourceready')方法。

Layer类的其它函数

  • getLayersArray方法:获取图层数组
  • getLayerStatesArray方法:获取图层状态数组
  • getRenderSource方法:获取渲染的图层源
  • getSourceState方法:获取图层源状态
  • getFeatures方法:接受一个参数pixel,若this.renderer渲染器不存在,则返回一个空数组;否则调用渲染器的getFeatures方法
  • getData方法:同上,若渲染器不存在则返回null,否则调用渲染器上的getData方法
  • isVisible方法:
  isVisible(view) {
    let frameState;
    const map = this.getMapInternal();
    if (!view && map) {
      view = map.getView();
    }
    if (view instanceof View) {
      frameState = {
        viewState: view.getState(),
        extent: view.calculateExtent(),
      };
    } else {
      frameState = view;
    }
    if (!frameState.layerStatesArray && map) {
      frameState.layerStatesArray = map.getLayerGroup().getLayerStatesArray();
    }
    let layerState;
    if (frameState.layerStatesArray) {
      layerState = frameState.layerStatesArray.find(
        (layerState) => layerState.layer === this,
      );
    } else {
      layerState = this.getLayerState();
    }

    const layerExtent = this.getExtent();

    return (
      inView(layerState, frameState.viewState) &&
      (!layerExtent || intersects(layerExtent, frameState.extent))
    );
  }

isVisible方法用于判断图层在当前地图中是否可见,主要判断图层的状态和地图视图view的帧状态,涉及到范围和分辨率以及visiable

  • getAttributions方法:
  getAttributions(view) {
    if (!this.isVisible(view)) {
      return [];
    }
    const getAttributions = this.getSource()?.getAttributions();
    if (!getAttributions) {
      return [];
    }
    const frameState =
      view instanceof View ? view.getViewStateAndExtent() : view;
    let attributions = getAttributions(frameState);
    if (!Array.isArray(attributions)) {
      attributions = [attributions];
    }
    return attributions;
  }

getAttributions方法用于获取图层源的各种属性,参数是一个view,首先判断视图的可见性,若不可见,则返回一个空数组,否则调用图层源的getAttributions()方法,返回值保存在变量getAttributions中,若变量getAttributions不存在,则返回一个空数组;否则调用getAttributions(frameState)

  • setMapInternalgetMapInternal方法就是用于设置和获取对象的map
  • getRenderer方法:获取渲染器
  • hasRenderer方法:判断是否存在渲染器
  • createRender方法:创建一个渲染器,在Layer类中,返回一个null,该方法应该会在Layer的子类被重写
  • disposeInternal方法:Layer类的清理方法,判断渲染器是否存在,若存在则调用渲染器的dispose方法,然后删除渲染器;然后调用this.setSource(null),设置空的图层源,重置一些状态,最后调用父类的disposeInternal方法,执行父类的清除函数。

总结

本文主要介绍Layer类中重要的方法,如render方法和setSource的逻辑处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jinuss

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值