高速公路监管系统大屏可视化

0x00 项目背景

该项目用于高速公路监管。高速公路监管包括:高速公路的设备运行状况,设备维护状况,道路维护状况;交通流量分析,交通拥堵分析,拥堵溯源;事故分析,事件信息发布等。数组

0x01设计图

该项目目前主要是一个预演形式的项目,因此设计图层面主要仍是用了客户提供的图片。 咱们的设计团队参与的并很少。下面是客户的设计图纸:bash

设计图
设计图

0x02 绘制公路

公路的实际效果仍是比较复杂的,好比公路上面有各类线(斑马线,黄线,白线,实线,虚线等等)。可是这些在本系统不是最重要的要素,所以考虑忽略。所以咱们使用带边线效果的路径进行模拟,效果以下所示:app

公路效果
公路效果

绘制的逻辑其实也很简单,首先用较大的线宽绘制路径,而后改成较小的线宽和不一样颜色在绘制一次路径。 大体的绘制逻辑以下:echarts

ctx.save();
        ctx.beginPath();
        ctx.lineJoin = 'round';
        // ctx.lineCap = 'round';
        points.forEach(({
            x,
            y
        }, index) => {
            ctx[index ? 'lineTo' : 'moveTo'](x, y);
        })
       
        ctx.lineWidth = width;
        ctx.strokeStyle = sideColor;
        ctx.stroke();
        ctx.shadowBlur = 0;
        ctx.globalCompositeOperation = 'source-over';
        ctx.lineWidth = width * 0.5;
        ctx.strokeStyle = midColor;
        ctx.stroke();

复制代码

在编辑器中,增长一个公路组件,点击下公路组件,即可以开始绘制公路:编辑器


公路组件
公路组件

经过打点编辑路径,便可以对公路的走向进行编辑。 须要注意的是,技术上使用了自动平滑的技术,本来的尖锐的角都会变成平滑的效果。ide

export function createSmoothCurvePoints(
  points,
  tension = 0.5,
  closed = false,
  numberOfSegments = 16
) {
  if (points.length < 2) {
    return points;
  }
  //  展开数组
  points = expandPointArr(points);

  let ps = points.slice(0), // clone array so we don't change the original result = [], // result points x, y, // our x,y coords t1x, t2x, t1y, t2y, // tension vectors c1, c2, c3, c4, // cardinal points st, t, i; // steps based on number of segments // The algorithm require a previous and next point to the actual point array. // Check if we will draw closed or open curve. // If closed, copy end points to beginning and first points to end // If open, duplicate first points to befinning, end points to end if (closed) { ps.unshift(points[points.length - 1]); ps.unshift(points[points.length - 2]); ps.unshift(points[points.length - 1]); ps.unshift(points[points.length - 2]); ps.push(points[0]); ps.push(points[1]); } else { ps.unshift(points[1]); // copy 1st point and insert at beginning ps.unshift(points[0]); ps.push(points[points.length - 2]); // copy last point and append ps.push(points[points.length - 1]); } // 1. loop goes through point array // 2. loop goes through each segment between the 2 points + 1e point before and after for (i = 2; i < ps.length - 4; i += 2) { // calculate tension vectors t1x = (ps[i + 2] - ps[i - 2]) * tension; t2x = (ps[i + 4] - ps[i - 0]) * tension; t1y = (ps[i + 3] - ps[i - 1]) * tension; t2y = (ps[i + 5] - ps[i + 1]) * tension; for (t = 0; t <= numberOfSegments; t++) { // calculate step st = t / numberOfSegments; // calculate cardinals c1 = 2 * Math.pow(st, 3) - 3 * Math.pow(st, 2) + 1; c2 = -(2 * Math.pow(st, 3)) + 3 * Math.pow(st, 2); c3 = Math.pow(st, 3) - 2 * Math.pow(st, 2) + st; c4 = Math.pow(st, 3) - Math.pow(st, 2); // calculate x and y cords with common control vectors x = c1 * ps[i] + c2 * ps[i + 2] + c3 * t1x + c4 * t2x; y = c1 * ps[i + 1] + c2 * ps[i + 3] + c3 * t1y + c4 * t2y; //store points in array result.push(x); result.push(y); } } return contractPointArr(result); 复制代码

0x03 门架的绘制

门架的最终效果以下图所示:oop


门架
门架

能够看出门架是由几个立方体组合而成的。咱们只须要理解立方体的绘制逻辑,即可以很轻松理解门架的绘制逻辑。ui

绘制立方体

编辑器中自己也存在立方体组件:this


立方体组件
立方体组件

其显示效果以下:spa


立方体效果
立方体效果

绘制立方体的思路并不复杂,只是借助了一些三维的思路。首先借助了三维的投影变换的思路,固然此处使用的是正投影:

/**
   * 3d坐标转2d坐标
   *
   * @param {Object} point - 3d坐标
   * @param {Object} offset - 一点偏移
   * @returns {Object} - 2d坐标
   */
  getProjectionPoint(point) {
    const network = this._network,
      p = vec3.create(),
      itMat = network.getMVMatrix();
    vec3.transformMat4(
      p,
      [point.x, point.y, point.z],
      itMat
    );
    const {
      x,
      y
    } = this.getLocation()
    return {
      x: p[0] + x,
      y: -p[1] + y
    };
  }
复制代码

对立方体的8个顶点计算出其在平面上的位置,并计算出其如今在外面的三个面(注意:最多只会有三个面显示在外面)。 而后把三个面绘制值出来:

drawSide(ctx, points, isFill = false, color = "#00ccff") {
    ctx.save();
    ctx[isFill ? 'fillStyle' : 'strokeStyle'] = color;
    ctx.lineWidth = 1;
    ctx.beginPath();
    points.forEach(({
      x,
      y
    }, index) => {
      ctx[index ? 'lineTo' : 'moveTo'](x, y);
    })
    ctx.closePath();
    ctx[isFill ? 'fill' : 'stroke']();
    ctx.restore();
  }
复制代码

最终绘制的效果如上图所示。

门架的绘制,就是多个立方体的组合的绘制。须要注意的一点就是,要注意多个立方体绘制的顺序,这会涉及到遮挡关系的正确性。

在编辑器中,能够经过调整其长宽高和y轴旋转角度来改变其显示形态:


门架
门架

0x04 标志牌的绘制

标志牌是公路上面常见的对象。 用于各类提示,在本系统,标志牌显示效果以下:


标志牌
标志牌

其绘制思路其实和前面的门架相似,都是经过立方体组合而成的。所以此处再也不赘述。

0x05 山的绘制

因为山是比较复杂的模型,所以程序直接使用了设计人员的设计的图形。以下图所示:


山

使用设计人员设计的图片做为网元的图片,直接拖拽进入场景便可。

0x05 图表的绘制

编辑器中集成了经常使用的echarts图表和扩展的图表。应此能够直接拖拽到场景之中,好比下图截出了部分的图表,包括柱状图、饼图、曲线图:


图表组件
图表组件

把图表直接拖到场景中便可生成图表效果,以下图所示:


图表效果
图表效果

并能够在属性框配置图表的数据,此处为了演示,使用的是静态数据;也能够对接动态的数据上俩。

0x06 最终效果

综合上述全部的效果,最终编辑出来了一个演示页面,以下图所示:


最终效果
最终效果

有兴趣获取demo的,请发邮件到:
terry.tan@servasoft.com

另欢迎关注我的公众号 “ITman彪叔”

相关文章
相关标签/搜索