zoukankan      html  css  js  c++  java
  • 高速公路监管系统大屏可视化

    0x00 项目背景

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

    0x01设计图

    该项目目前主要是一个预演形式的项目,所以设计图层面主要还是用了客户提供的图片。 我们的设计团队参与的并不多。下面是客户的设计图纸:

    设计图

    0x02 绘制公路

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

    公路效果

    绘制的逻辑其实也很简单,首先用较大的线宽绘制路径,然后改为较小的线宽和不同颜色在绘制一次路径。 大致的绘制逻辑如下:

    
    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();
    
    

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

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

    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 门架的绘制

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

    可以看出门架是由几个立方体组合而成的。我们只需要理解立方体的绘制逻辑,便可以很轻松理解门架的绘制逻辑。

    绘制立方体

    编辑器中本身也存在立方体组件:
    立方体组件

    其显示效果如下:
    立方体效果

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

    /**
       * 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彪叔”

  • 相关阅读:
    第24天:Python 标准库概览2
    第23天:Python 标准库概览1
    第22天:Python NameSpace&Scope
    第21天: Web 开发 Jinja2 模板引擎
    第20天:Python 之装饰器
    第19天:Python 之迭代器
    第18天:Python 高阶函数
    第17天:Python 函数之参数
    第16天:Python 错误和异常
    第15天:Python set
  • 原文地址:https://www.cnblogs.com/flyfox1982/p/12397125.html
Copyright © 2011-2022 走看看