openlayers 绘图原理小记

记录一下查看openlayers矢量绘图原理的查看过程,

用这个例子切入,https://openlayers.org/en/latest/examples/feature-animation.html

环境:windows chrome nodejs webstorm 

1.下载openlayer 源码,版本5.1.3,webstrom中打开

2.查看源码

feature-animation.js

function animate(event) {
    const vectorContext = event.vectorContext;
    const frameState = event.frameState;
    const flashGeom = feature.getGeometry().clone();
    const elapsed = frameState.time - start;
    const elapsedRatio = elapsed / duration;
    // radius will be 5 at start and 30 at end.
    const radius = easeOut(elapsedRatio) * 25 + 5;
    const opacity = easeOut(1 - elapsedRatio);

    const style = new Style({
      image: new CircleStyle({
        radius: radius,
        snapToPixel: false,
        stroke: new Stroke({
          color: 'rgba(255, 0, 0, ' + opacity + ')',
          width: 0.25 + opacity
        })
      })
    });

    vectorContext.setStyle(style);
    vectorContext.drawGeometry(flashGeom);//跟踪代码
    if (elapsed > duration) {
      unByKey(listenerKey);
      return;
    }
    // tell OpenLayers to continue postcompose animation
    map.render();
  }
Immediate.js

drawGeometry(geometry) {
    const type = geometry.getType();
    switch (type) {
      case GeometryType.POINT:
        this.drawPoint(/** @type {module:ol/geom/Point} */ (geometry));
        break;
      case GeometryType.LINE_STRING:
        this.drawLineString(/** @type {module:ol/geom/LineString} */ (geometry));
        break;
      case GeometryType.POLYGON:
        this.drawPolygon(/** @type {module:ol/geom/Polygon} */ (geometry));
        break;
      case GeometryType.MULTI_POINT:
        this.drawMultiPoint(/** @type {module:ol/geom/MultiPoint} */ (geometry));
        break;
      case GeometryType.MULTI_LINE_STRING:
        this.drawMultiLineString(/** @type {module:ol/geom/MultiLineString} */ (geometry));
        break;
      case GeometryType.MULTI_POLYGON:
        this.drawMultiPolygon(/** @type {module:ol/geom/MultiPolygon} */ (geometry));
        break;
      case GeometryType.GEOMETRY_COLLECTION:
        this.drawGeometryCollection(/** @type {module:ol/geom/GeometryCollection} */ (geometry));
        break;
      case GeometryType.CIRCLE:
        this.drawCircle(/** @type {module:ol/geom/Circle} */ (geometry));
        break;
      default:
    }
  }

任意挑选一种要素类型,这里选择线

 drawLineString(geometry) {
    if (!intersects(this.extent_, geometry.getExtent())) {
      return;
    }
    if (this.strokeState_) {
      this.setContextStrokeState_(this.strokeState_);
      const context = this.context_;
      const flatCoordinates = geometry.getFlatCoordinates();
      context.beginPath();
      this.moveToLineTo_(flatCoordinates, 0, flatCoordinates.length,
        geometry.getStride(), false);
      context.stroke();
    }
    if (this.text_ !== '') {
      const flatMidpoint = geometry.getFlatMidpoint();
      this.drawText_(flatMidpoint, 0, 2, 2);
    }
  }

这里重点来了,this.moveToLineTo_(flatCoordinates, 0, flatCoordinates.length,geometry.getStride(), false);

  moveToLineTo_(flatCoordinates, offset, end, stride, close) {
    const context = this.context_;
    const pixelCoordinates = transform2D(
      flatCoordinates, offset, end, stride, this.transform_,
      this.pixelCoordinates_);
    context.moveTo(pixelCoordinates[0], pixelCoordinates[1]);
    let length = pixelCoordinates.length;
    if (close) {
      length -= 2;
    }
    for (let i = 2; i < length; i += 2) {
      context.lineTo(pixelCoordinates[i], pixelCoordinates[i + 1]);
    }
    if (close) {
      context.closePath();
    }
    return end;
  }

重点

 transform2D(flatCoordinates, offset, end, stride, this.transform_, this.pixelCoordinates_);

其中的重点this.transform_,变换矩阵,这个变换矩阵是CanvasImmediateRenderer的构造参数,图层渲染器layerrender,canvaslayerrender,都会给它传入这个参数,默认的变换

export function create() {
  return [1, 0, 0, 1, 0, 0];
}

在完成绘图准备,分发事件的时候,会重新计算一个transform,getTransform,

dispatchComposeEvent_(type, context, frameState, opt_transform) {
    const layer = this.getLayer();
    if (layer.hasListener(type)) {
      const width = frameState.size[0] * frameState.pixelRatio;
      const height = frameState.size[1] * frameState.pixelRatio;
      const rotation = frameState.viewState.rotation;
      rotateAtOffset(context, -rotation, width / 2, height / 2);
      const transform = opt_transform !== undefined ?
        opt_transform : this.getTransform(frameState, 0);
      const render = new CanvasImmediateRenderer(
        context, frameState.pixelRatio, frameState.extent, transform,
        frameState.viewState.rotation);
      const composeEvent = new RenderEvent(type, render, frameState,
        context, null);
      layer.dispatchEvent(composeEvent);
      rotateAtOffset(context, rotation, width / 2, height / 2);
    }
  }

getTransform

getTransform(frameState, offsetX) {
    const viewState = frameState.viewState;
    const pixelRatio = frameState.pixelRatio;
    const dx1 = pixelRatio * frameState.size[0] / 2;
    const dy1 = pixelRatio * frameState.size[1] / 2;
    const sx = pixelRatio / viewState.resolution;
    const sy = -sx;
    const angle = -viewState.rotation;
    const dx2 = -viewState.center[0] + offsetX;
    const dy2 = -viewState.center[1];
    return composeTransform(this.transform_, dx1, dy1, sx, sy, angle, dx2, dy2);
  }

可以看到这个矩阵的构成

export function compose(transform, dx1, dy1, sx, sy, angle, dx2, dy2) {
  const sin = Math.sin(angle);
  const cos = Math.cos(angle);
  transform[0] = sx * cos;
  transform[1] = sy * sin;
  transform[2] = -sx * sin;
  transform[3] = sy * cos;
  transform[4] = dx2 * sx * cos - dy2 * sx * sin + dx1;
  transform[5] = dx2 * sy * sin + dy2 * sy * cos + dy1;
  return transform;
}

经过这个矩阵变换,才会完成feature的平面坐标-》css坐标?-》屏幕坐标的变换,最终绘制到屏幕上

resolution的设置、交互过程中ol如何确定resolution也很重要,另说。

还有一些关节没打通,还需学习

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值