OpenLayers 3 之 扩展自定义控件--以切换图层控件为例

      openlayers 中的控件,是一个固定在页面某个位置的可见的DOM元素,它们可能包含可操作的按钮,也可能只是单纯的展示信息,它们的位置及其样式是由与其关联的CSS样式决定的。默认情况下,它们都位于一个CSS 类(class)为ol.overlaycontainer-stopevent的元素内,当然也可以使用其他的自定义的DOM容器元素。

      在openlayers 的结构中,控件有很多,比如左上角的缩放控件、右下角的属性控件,这些控件都是继承一个基类 ol.control.Control,基类主要为它的子类提供统一的“阻止事件传播机制”,并且 ol.control.Control继承了 ol.Object,并且控件都放于一个统一的容器 ol.Collection,所以如果继承ol.control.Control基类,自定义的控件不仅可以得益于其“阻止事件传播机制”,也可以使用 ol.Objectol.Collection的方法和事件。

ol.control.Control 简介

      ol.control.Control是openlayers的控件基类,我们的自定义控件可以继承该基类(在上边我们也讲了继承该基类的好处),在类中使用 javascript 动态创建DOM元素,指定DOM元素对应的CSS样式类(class),并对DOM元素绑定相应的事件,来完成自定义控件。openlayers3 使用了google 的 closure 库来进行开发,要使用 closure 的语法来进行继承,比如我们定义我们的切换图层控件类为 ol. control.LayerSwitcher,那么使用closure实现继承的语句如下:

ol.inherits(ol.control.LayerSwitcher, ol.control.Control);

      还要注意一点就是,我们要在控件类的‘构造函数’(加引号是因为javascript构造函数的概念不明显)中调用基类的构造函数,我们可以使用 call 或者 apply 方法(因为本质上javascript的类是函数模拟的),或者直接使用:new ol.control.Control({element: myElement})

      当然,我们完全可以不用继承ol.control.Control,但最好不要这样做,首先,继承基类有助于保持 openlayers 的组织结构清晰明了;其次,我们可以使用 ol.control.Control的‘阻止事件传播机制’,并且ol.control.Control继承了ol.Object,所以我们也可以使用它的方法。

具体实现–以GitHub中的项目为例

      原本在 openlayers 2 中,图层切换控件是“标配”,而在 openlayers 3 中默认没有这个控件,那么我们可以着手扩展实现一个。在GitHub中就有一个现成的项目,项目地址为:https://github.com/walkermatt/ol3-layerswitcher ,感兴趣的可以看看,在我们了解了具体思想和实现思路的时候,就是具体的代码实现,并在实践中检验,不断的修正,然而相对于使用现存的经过了这些过程的开源项目,我们为何不适用现存的‘轮子’呢?当然前提是我们懂得它的实现,可以针对与自己的需求进行改进,当然其遵循的开源协议要允许我们这样做。

      做一件事情,必须要明确目的和具体需求,并针对需求进行设计,不要急着实现,其次才是寻找实现的方法,所以首先明确我们的功能需求:

  1. 地图上一个可见的切换图层DOM元素,当鼠标悬浮在元素之上时,以列表形式展开所有图层;
  2. 每个图层前面有一个复选框,当勾选时,显示该图层,没有勾选时,隐藏该图层;
  3. 声明为底图的图层,列表项前面加载单选框(radio),不能取消选择。

      经过比较粗糙的需求分析,我们着手实现。首先使用构造函数模式定义我们的基类属性和事件,然后定义对外的方法。

定义基类

ol.control.LayerSwitcher = function(opt_options) {

    var options = opt_options || {};

    var tipLabel = options.tipLabel ?
      options.tipLabel : 'Legend';

    this.mapListeners = [];

    this.hiddenClassName = 'ol-unselectable ol-control layer-switcher';
    this.shownClassName = this.hiddenClassName + ' shown';

    var element = document.createElement('div');
    element.className = this.hiddenClassName;

    var button = document.createElement('button');
    button.setAttribute('title', tipLabel);
    element.appendChild(button);

    this.panel = document.createElement('div');
    this.panel.className = 'panel';
    element.appendChild(this.panel);

    var this_ = this;

    element.onmouseover = function(e) {
        this_.showPanel();
    };

    button.onclick = function(e) {
        this_.showPanel();
        e.preventDefault();
    };

    element.onmouseout = function(e) {
        e = e || window.event;
        if (!element.contains(e.toElement)) {
            this_.hidePanel();
        }
    };

    ol.control.Control.call(this, {
        element: element,
        target: options.target
    });

};

ol.inherits(ol.control.LayerSwitcher, ol.control.Control);

      从代码中可以看出,从第六行开始,便是定义了两个CSS类,分别为控件收缩和展开时的样式,接着便开始定义DOM元素,一个按钮,两个DIV元素,分别问容器DIV和面板(panel,展示内容的地方)DIV,接着便给按钮绑定了鼠标点击、悬浮和离开的事件处理。最后调用了基类的构造函数,并使用closure的语法,继承了基类ol.control.Control

      在定义的事件处理机制中,我们看到 showPanelhidePanel方法,这都是在原型链中定义的方法,下面我们看都定义了那些方法。

定义对外的方法

      定义的方法主要如下,其核心思想就是针对用户与控件的交互行为,来完成对地图的交互,也就是对地图各个图层的可见性的设置,涉及的对象包含 map 和 layers,当然还有定义的DOM元素。因为这些方法的具体实现与我们本文要将的实现自定义控件的思想关系不大,这里就知识列出其主要的功能:

共有方法
- hidePanel,隐藏显示图层列表的面板DIV;
- showPanel,显示图层列表的面板DIV;
- renderPanel,重新绘制面板DIV;
- setMap,设置与之关联的 map 对象。

私有方法
- ensureTopVisibleBaseLayerShown_,当有多个图层是可见(勾选)状态,确保只有最上层的图层是可见的;
- setVisible_,设置图层的可见性;
- renderLayer_,渲染一个layerGroup包含的所有图层;
- renderLayers_,

以及一个静态的方法:ol.control.LayerSwitcher.forEachRecursive,其作用是针对一个 layerGroup 中每个图层调用一个回调函数。

总结

      本文主要介绍了在 openlayers 基础上扩展切换图层控件的原理和需要注意的问题,要自己写出扩展,首先你要对web前端知识的理解达到一个比较高的程度,还要对 openlayers 有比较好的认识,对面向对象的思想在 javascript 中的实现,也要比较了解,才能写出质量比较高的扩展。

好的,就写到这里,有什么问题,可以在文章下面留言或者给我发邮件。

你可能感兴趣的:(OpenLayers,WebGIS,OpenLayers)