Openlayers 源码分析-Map

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_;
  }

个人博客

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容