Skip to content

创建新图层

fuzhenn edited this page Jul 20, 2017 · 14 revisions

图层(Layer)是maptalks的核心, 你可以创建自己的图层, 来可视化数据, 实现复杂的交互, 载入自定义格式数据等.

创建图层非常简单, 你只需要两个步骤:

创建图层类

要创建图层类, 声明一个新的class, 并让其继承maptalks.Layer.

除定义必要的方法, 实现图层逻辑外, 可选择实现下列接口方法(均为可选):

  • onAdd : map.addLayer(map)或layer.addTo(map) 添加地图时的回调函数.
  • onLoad : layer.load()加载图层时的回调函数, return false时暂停图层加载, 适合需异步载入外部资源的场景: 返回false暂停图层加载, 查询远程服务或载入外部资源, 待完成后再继续加载图层(layer.load()).
  • onRemove : map.removeLayer(layer)或layer.remove()时的回调函数, 用于在删除图层时清理/释放资源.
  • toJSON : 将图层实例序列化为JSON对象的方法
  • static fromJSON : 一个静态方法, 用来从JSON对象生成图层实例.

以下为一个简单示例, 图层能在地图中央用指定颜色显示指定的内容

const options = {
  'color' : 'Red'
};

class HelloLayer extends maptalks.Layer {
  // 构造函数
  constructor(id, text, options) {
    // 父类Layer的构造函数
    super(id, options);    
    this.text = text;    
  }

  // 返回图层中的文字
  getText() {
    return this.text;
  }

  // 给图层设置新的文字
  setText(text) {
    this.text = text;
    return this;
  }
  
  onAdd() {
    console.log('A HelloLayer is added to map');
  }
  
  onLoad() {
    console.log('HelloLayer is about to load');
    // 返回true, 让图层继续加载
    return true;
  }

  onRemove() {
    delete this.text;
  }

  // 将图层序列化为json对象
  toJSON() {
    return {
      'type'    : 'HelloLayer',
      'id'      : this.getId(),
      'options' : this.config(),
      'content' : this.getContent()
    };
  }

  // 由json对象反序列化, 生成图层实例
  static fromJSON(json) {
    if (!json) {
        return null;
    }
    return new HelloLayer(json.id, json.content, json.options);
  }
}

// 定义HelloLayer的默认options
HelloLayer.mergeOptions(options);

// 注册图层的 JSON Type
HelloLayer.registerJSONType('HelloLayer');

创建图层的渲染器(Renderer)

图层渲染器负责图层的绘制, 交互和事件监听等. 你可以使用任何心仪的图形技术来实现图层渲染器, 如Canvas 2D, WebGL, SVG 或HTML + CSS. 一个图层可以有多个渲染器, 例如TileLayer有dom和canvas两个渲染器, 使用哪个渲染器由图层的options.renderer来决定, 默认值为canvas.

maptalks中最常见的是Canvas渲染器(Canvas 2D), 每个Canvas渲染器有自己的独立Canvas画布, 根据需求, 可在渲染器中调用Canvas 2D的方法做绘制, map会重绘时, 加载渲染器中的canvas, 呈现到地图上.

为方便用户实现上述逻辑, maptalks提供了CanvasRenderer类, 包含了一些常用Canvas容器操作方法, 你可以通过继承它来轻松创建你自己的CanvasRenderer.

[补充图层绘制流程示意图]

以下为HelloLayer的渲染器程序, 来介绍如何实现自己的CanvasRenderer

//HelloLayer's 渲染器程序
class HelloLayerRenderer extends maptalks.renderer.CanvasRenderer {

  //构造函数
  constructor(layer) {
      super(layer);
  }

  /**
  * 可选实现的方法
  * 检查是否有外部资源需要加载, 如果有, renderer会先载入资源, 再调用draw方法  
  * @returns {Array[]} 外部资源数组 [ [url1, width, height].. ]
  */
  checkResources() {
    return [];
  }

  /**
  * 必须实现的abstract方法
  * 用来在地图没有交互时绘制图层
  * @abstract
  * @required
  */
  draw() {
    const map = this.getMap(),
    size = map.getSize();
    //prepare layer's canvas
    this.prepareCanvas();
    //convert center coordinate to containerPoint
    //a containerPoint is screen position from top-left of container.
    const point = map.coordinateToContainerPoint(map.getCenter());
    const text = this.layer.getText();
    //this.context is the CanvasRenderingContext2D of the layer canvas
    this.context.fillStyle = this.layer.options['textColor'];
    this.context.font = 'bold 50px sans-serif';
    const len = this.context.measureText(text);
    this.context.fillText(text, point.x - len.width / 2, point.y);
    //ask map to render
    this.completeRender();
  }
  
  /**
  * 可选实现的abstract方法
  * 用来在地图交互时绘制图层
  * 因为地图交互需要较高的fps, 实现绘制时需要平衡用户体验和性能.
  * 如果地图交互的fps很低时, 该方法有可能被忽略, 此时回调方法onSkipDrawOnInteracting会被调用
  * @abstract
  * @optional
  * @param {Object} eventParam event parameters
  */
  drawOnInteracting(eventParam) {
    this.draw();
  }

  /**  
  * 可选实现的方法
  * 图层是否是动画
  * 如果是, 则图层一直处于重绘状态, draw/drawOnInteracting会一直被调用
  * @optional
  */
  isAnimating() {
    return false;
  }
 
  /**
  * 可选实现的方法
  * 图层是否需要重绘
  * 如果是, 则map会调用draw/drawOnInteracting重绘图层
  * @optional
  */
  needToRedraw() {
    if (map.isInteracting()) {
      return true;
    }
    return super.needToRedraw();
  }

  /**
  * 可选实现的回调函数, 图层第一次加载绘制时调用
  * @optional
  */
  onAdd() {
    console.log('I am added');
  }

  /**
  * 可选实现的回调函数, 图层从map移除时调用, 可以用来释放资源
  * @optional
  */
  onRemove() {
    console.log('I am removed');
  }

  // 各种事件回调, 可以根据需要选择实现
  onZoomStart(e) { super.onZoomStart(e); }
  onZoomEnd(e) { super.onZoomEnd(e); }
  onResize(e) { super.onResize(e); }
  onMoveStart(e) { super.onMoveStart(e); }
  onMoveEnd(e) { super.onMoveEnd(e); }
  onDragRotateStart(e) { super.onDragRotateStart(e); }
  onDragRotateEnd(e) { super.onDragRotateEnd(e); }
  onSpatialReferenceChange(e) { super.onSpatialReferenceChange(e); }
}

//将HelloLayerRenderer注册为'canvas' renderer, 图层的默认渲染器
HelloLayer.registerRenderer('canvas', HelloLayerRenderer);

CanvasRenderer中实现绘制逻辑时, 常用的一些工具方法:

  • setToRedraw()

    设置CanvasRenderer为重绘状态, 请求map调用draw/drawOnInteracting重绘, 并重画图层的canvas

  • setCanvasUpdated()

    设置CanvasRenderer的Canvas为更新状态, 请求map重画图层的canvas, 但不会调用draw/drawOnInteracting重绘

  • getCanvasImage()

    获取图层的Canvas图像, 返回的对象格式:

    { image : canvas画布, layer : 图层对象, point : 左上角containerPoint, size : 画布大小 }
  • createCanvas()

    创建Canvas画布, 并进行必要的设置

  • prepareCanvas()

    绘制前, 预备Canvas: 1. 清除Canvas, 2. 如果图层有mask, 则调用clip方法设置Canvas遮罩

  • clearCanvas()

    清除Canvas

  • resizeCanvas(size)

    按照size中大小, 设置Canvas的高宽

  • completeRender()

    绘制结束后的调用方法, 触发必要的事件, 并调用setCanvasUpdated请求重画图层的canvas

  • 其他方法可以参考CanvasRenderer的API文档

运行示例

上述内容中的HelloLayer运行示例地址

[补充运行截图]

Clone this wiki locally