美文网首页
Openlayers 源码分析-Map

Openlayers 源码分析-Map

作者: 写前端的大叔 | 来源:发表于2020-02-22 19:34 被阅读0次

Map为提供给应用层的一个类,代码不多,主要是继承自PluggableMap,在Map中只是设置了controlsinteractions,重写createRenderer渲染方法。然后再使用super方法进行初始化。所有初始化相关的操作都是在PluggableMap中进行的,这里主要对PluggableMap进行分析。

1.地图初始化

PluggableMap的作用主要是进行初始化相关的操作,主要包括地图初始化、添加地图事件,加载队列初化,layerscontrolsoverlays等初始化,以及一些公共方法的实现。

1.1内部初始化

在执行PluggableMap构造函数时,首先调用了createOptionsInternal方法进行初始化,主要是对controlsinteractionskeyboardEventTargetoverlaysvalues等参数的初始化。

function createOptionsInternal(options) {

  //初始化键盘事件的目标元素
  let keyboardEventTarget = null;
  if (options.keyboardEventTarget !== undefined) {
    keyboardEventTarget = typeof options.keyboardEventTarget === 'string' ?
      document.getElementById(options.keyboardEventTarget) :
      options.keyboardEventTarget;
  }

  //用于存储属性值
  const values = {};

  //图层组
  const layerGroup = options.layers && typeof (options.layers).getLayers === 'function' ?
    (options.layers) : new LayerGroup({layers: (options.layers)});
  values[MapProperty.LAYERGROUP] = layerGroup;

  values[MapProperty.TARGET] = options.target;

  values[MapProperty.VIEW] = options.view !== undefined ?
    options.view : new View();

  //初始化控件
  let controls;
  if (options.controls !== undefined) {
    if (Array.isArray(options.controls)) {
      controls = new Collection(options.controls.slice());
    } else {
      assert(typeof (options.controls).getArray === 'function',
        47); 
      controls = (options.controls);
    }
  }

  //初始化地图交互
  let interactions;
  if (options.interactions !== undefined) {
    if (Array.isArray(options.interactions)) {
      interactions = new Collection(options.interactions.slice());
    } else {
      assert(typeof /** @type {?} */ (options.interactions).getArray === 'function',
        48); 
      interactions = /** @type {Collection} */ (options.interactions);
    }
  }

  //初始化覆盖物
  let overlays;
  if (options.overlays !== undefined) {
    if (Array.isArray(options.overlays)) {
      overlays = new Collection(options.overlays.slice());
    } else {
      assert(typeof (options.overlays).getArray === 'function',
        49); 
      overlays = options.overlays;
    }
  } else {
    overlays = new Collection();
  }

  return {
    controls: controls,
    interactions: interactions,
    keyboardEventTarget: keyboardEventTarget,
    overlays: overlays,
    values: values
  };

}

1.2Collection

在执行初始化操作时,用到了一个数组Collection,该类是对array的一个封装,主要是对更新的操作进行一个事件派发,当往Collection对象中执行新增、修改、删除等操作时,可以派发事件,方便应用层做一些处理。如下所示为添加元素的一个方法:

insertAt(index, elem) {
    if (this.unique_) {
      this.assertUnique_(elem);
    }
    this.array_.splice(index, 0, elem);//添加数据
    this.updateLength_();//更新长度
    //添加数据后派发事件
    this.dispatchEvent(
      new CollectionEvent(CollectionEventType.ADD, elem, index));
  }

1.3添加元素

在地图控件上,除了地图元素外,还需要添加了一些比较重要的div元素,在构造函数中将看到一些如下所示的代码:

this.viewport_ = document.createElement('div');
    this.viewport_.className = 'ol-viewport' + ('ontouchstart' in window ? ' ol-touch' : '');
    this.viewport_.style.position = 'relative';
    this.viewport_.style.overflow = 'hidden';
    this.viewport_.style.width = '100%';
    this.viewport_.style.height = '100%';
    this.overlayContainer_ = document.createElement('div');
    this.overlayContainer_.style.position = 'absolute';
    this.overlayContainer_.style.zIndex = '0';
    this.overlayContainer_.style.width = '100%';
    this.overlayContainer_.style.height = '100%';
    this.overlayContainer_.className = 'ol-overlaycontainer';
    this.viewport_.appendChild(this.overlayContainer_);

    this.overlayContainerStopEvent_ = document.createElement('div');
    this.overlayContainerStopEvent_.style.position = 'absolute';
    this.overlayContainerStopEvent_.style.zIndex = '0';
    this.overlayContainerStopEvent_.style.width = '100%';
    this.overlayContainerStopEvent_.style.height = '100%';
    this.overlayContainerStopEvent_.className = 'ol-overlaycontainer-stopevent';
    this.viewport_.appendChild(this.overlayContainerStopEvent_);

其中viewport_为一个比较重要的元素,是将它添加在地图上的,后面讲到的一些事件都是基于viewport_来处理的,可以通过MapBrowserEventHandler的构造函数可以看到,事件是添加在viewport_上的,如下所示:

//获取viewport_
    const element = this.map_.getViewport();
    this.activePointers_ = 0;
    this.trackedTouches_ = {};
    this.element_ = element;
    this.pointerdownListenerKey_ = listen(element,
      PointerEventType.POINTERDOWN,
      this.handlePointerDown_, this);//给viewport_添加鼠标按下的事件
    this.originalPointerMoveEvent_;
    this.relayedListenerKey_ = listen(element,
      PointerEventType.POINTERMOVE,
      this.relayEvent_, this);//给viewport_添加鼠标移开的事件
    this.boundHandleTouchMove_ = this.handleTouchMove_.bind(this);
    this.element_.addEventListener(EventType.TOUCHMOVE, this.boundHandleTouchMove_,
      PASSIVE_EVENT_LISTENERS ? {passive: false} : false);//给viewport_添加手势移开的事件

1.4优先队列

使用TileQueue可以进行切片队列的初始化,如下所示:

this.tileQueue_ = new TileQueue(
      this.getTilePriority.bind(this),
      this.handleTileChange_.bind(this));

TileQueue继承于PriorityQueue,相关的队列逻辑都是在PriorityQueue中完成的。使用的是一个优先队列的算法。离中心点最近的切片最先获取。

2.地图事件

在初始化PluggableMap的时候,会添加地图事件,先看下包括哪些事件,进入MapBrowserEventType文件中,可以看到所有的事件,如下所示:

export default {
  SINGLECLICK: 'singleclick',
  CLICK: EventType.CLICK,
  DBLCLICK: EventType.DBLCLICK,
  POINTERDRAG: 'pointerdrag',
  POINTERMOVE: 'pointermove',
  POINTERDOWN: 'pointerdown',
  POINTERUP: 'pointerup',
  POINTEROVER: 'pointerover',
  POINTEROUT: 'pointerout',
  POINTERENTER: 'pointerenter',
  POINTERLEAVE: 'pointerleave',
  POINTERCANCEL: 'pointercancel'
};

地图事件的处理主要都是在MapBrowserEventHandler中完成,主要是实现了一些点击事件、鼠标移动事件,手势操作事件,对于鼠标弹起事件主要用于判断是否为单击事件还是双击事件,鼠标按钮事件来判断是否为拖动事件,可以看下一些事件的实现方式:

 //鼠标弹起事件
  handlePointerUp_(pointerEvent) {
    //更新点击事件
    this.updateActivePointers_(pointerEvent);
    const newEvent = new MapBrowserPointerEvent(
      MapBrowserEventType.POINTERUP, this.map_, pointerEvent);
    this.dispatchEvent(newEvent);//创建一个新事件
    if (!newEvent.propagationStopped && !this.dragging_ && this.isMouseActionButton_(pointerEvent)) {
      this.emulateClick_(this.down_);//执行点击事件
    }

    //移除事件
    if (this.activePointers_ === 0) {
      this.dragListenerKeys_.forEach(unlistenByKey);
      this.dragListenerKeys_.length = 0;
      this.dragging_ = false;
      this.down_ = null;
    }
  }

//模拟点击事件,通过250ms来判断是否为双击事件
  emulateClick_(pointerEvent) {
    let newEvent = new MapBrowserPointerEvent(
      MapBrowserEventType.CLICK, this.map_, pointerEvent);
    this.dispatchEvent(newEvent);
    if (this.clickTimeoutId_ !== undefined) {
      // 双击
      clearTimeout(this.clickTimeoutId_);
      this.clickTimeoutId_ = undefined;
      newEvent = new MapBrowserPointerEvent(
        MapBrowserEventType.DBLCLICK, this.map_, pointerEvent);
      this.dispatchEvent(newEvent);
    } else {
      // 单击
      this.clickTimeoutId_ = setTimeout(function() {
        this.clickTimeoutId_ = undefined;
        const newEvent = new MapBrowserPointerEvent(
          MapBrowserEventType.SINGLECLICK, this.map_, pointerEvent);
        this.dispatchEvent(newEvent);
      }.bind(this), 250);
    }
  }

//鼠标按下事件
  handlePointerDown_(pointerEvent) {
    this.updateActivePointers_(pointerEvent);
    const newEvent = new MapBrowserPointerEvent(
      MapBrowserEventType.POINTERDOWN, this.map_, pointerEvent);
    this.dispatchEvent(newEvent);

    this.down_ = pointerEvent;

    if (this.dragListenerKeys_.length === 0) {
      //给document元素添加移动,弹起、取消等事件
      this.dragListenerKeys_.push(
        listen(document,
          MapBrowserEventType.POINTERMOVE,
          this.handlePointerMove_, this),
        listen(document,
          MapBrowserEventType.POINTERUP,
          this.handlePointerUp_, this),
        listen(this.element_,
          MapBrowserEventType.POINTERCANCEL,
          this.handlePointerUp_, this)
      );
    }
  }

  //鼠标移动事件
  handlePointerMove_(pointerEvent) {
    if (this.isMoving_(pointerEvent)) {
      this.dragging_ = true;
      const newEvent = new MapBrowserPointerEvent(
        MapBrowserEventType.POINTERDRAG, this.map_, pointerEvent,
        this.dragging_);
      this.dispatchEvent(newEvent);
    }
  }

//处理获取viewport_鼠标移动的事件
relayEvent_(pointerEvent) {
    this.originalPointerMoveEvent_ = pointerEvent;
    const dragging = !!(this.down_ && this.isMoving_(pointerEvent));
    this.dispatchEvent(new MapBrowserPointerEvent(
      pointerEvent.type, this.map_, pointerEvent, dragging));
  }

//判断是否在拖拽
isMoving_(pointerEvent) {
    return this.dragging_ ||
        Math.abs(pointerEvent.clientX - this.down_.clientX) > this.moveTolerance_ ||
        Math.abs(pointerEvent.clientY - this.down_.clientY) > this.moveTolerance_;
  }

个人博客

相关文章

  • Openlayers 源码分析-Map

    Map为提供给应用层的一个类,代码不多,主要是继承自PluggableMap,在Map中只是设置了controls...

  • Openlayers 源码分析-BaseObject

    要分析框架的源码,首先需要从基类开发,在Openlayers中BaseObject可以说是很多类的基类,比如Map...

  • 大数据综合笔记

    map reduce分析 参考文章 Hadoop-2.4.1源码分析--MapReduce作业(job)提交源码跟...

  • rxjava笔记(2)

    rxjava笔记(2) map与flatMap的原理 map的原理,可以查看map的源码进行分析。会发现map()...

  • openlayers源码分析(2)

    Feature Feature的父类是BaseObject,先从构造函数开始 构造函数参数可以是一个Geometr...

  • openlayers源码分析(1)

    1 准备 2 入口文件 src/ol/index.js ol是将不同的模块拆分成不同的文件集中放置在ol文件下,再...

  • Java HashMap 源码分析及文档归纳

    Java Hash Map 源码分析及文档归纳 源码分析 常量定义 put方法 putVal 中 hash相关代码...

  • Openlayers API整理

    Openlayers API整理 openlayers 发布于 2019-09-05 一、创建地图 1、地图Map...

  • RxSwift-Map源码解析

    Map 使用 源码解析 map 这步创建了一个Map对象 subscribe 因为之前分析过Obserable的核...

  • Openlayers 源码分析-类结构

    下载了Openlayers的源码,源码的版本是v6.2.1。粗略的看了下,结构清晰,代码简洁,是一个值得深入分析的...

网友评论

      本文标题:Openlayers 源码分析-Map

      本文链接:https://www.haomeiwen.com/subject/zhglqhtx.html