大批量数据下的基于arcgis和echarts的管网流向渲染优化|🏆 技术专题第三期征文

2,337 阅读4分钟

介绍

本司系统是关于城市管网的,要能够将整个城市的管网都渲染到地图上,要支持管网建模和管网修改,还需要实现管网的流向功能。所以综合考虑后采用arcgis+echarts来实现。下图是一个局部的效果图

问题

本文主要讨论当数据量太大时关于渲染的优化。因为整个城市的管网数据量是非常巨大的,单是管道和元件(源、用户、二通等)的数量就是几万甚至几十万个。这些数据一旦全部渲染到地图上就会造成浏览器的卡死。

分析

1.浏览器的卡死是一次性渲染太多数据造成的,所以可以可以考虑每次渲染时都只渲染少部分数据。

2.参考地图的显示方法。每次只渲染当前视图区域的数据。超出当前视图区域的数据不予渲染。

3.但是当视图区域过大时,比如整个城市都在当前视图内,依然会造成数据量过大。所以可以考虑视图过大时只渲染部分主要数据
    
4.短管合并:可以把多条短管合并为一个长管显示(只能合并单条路径上的短管,因为只有单条路径上的短管流向百分百一致)
    
5.超长管合并:交叉型管道。可以判断交叉处的管道哪些是流入,哪些是流出。然后在保证视觉上的流向一致的情况下合并管道

具体实现

1.元件和管道分等级

可以给元件和管道分不同的等级,有多少级地图就分多少个等级的元件和管道。然后不同级别的地图就对应不同级别的管道。这样就在当前视图区域过大时就可以只展现主要的管道了。

  1. 本司元件等级配置:门站、调压站对应三级地图,只要地图等级大于三级的都能展示门站和调压站,其他元件类同

2)本司管道等级配置:长度在40000米以上的管道在地图等级大于3级的情况下都能展示,其他管道类同

这样就解决了视图区域过大时数据量太大的问题,比如在地图等级为3级时就只渲染门站、调压站和长度大于40000米的管道

2.获取当前视图范围的数据

根据arcgis的api可以获取到当前地图的可视区域的坐标范围。然后再根据这个范围去遍历所有的元件和管道。把坐标在当前可视范围内并且等级大于当前地图等级的元件和管道筛选出来。这样就永远只会渲染当前可视区域的数据了

3.地图缩放和平移

每次地图发生缩放和平移时,地图的等级或可视区域都会发生改变。然后根据改变后的等级和区域去筛选合适的数据再重新渲染

优劣势

1.优势: 永远只渲染少量需要的数据,不会造成浏览器卡死

2.劣势:每次地图缩放和平移时都需要重绘

代码

//1.给管道和器件分等级
  createMapShowGrade(item, type) {
    if (type == "line") {
      for (let i = 0, leni = this.lineGrades.length; i < leni; i++) {
        if (item.length >= this.lineGrades[i]) { //lineGrades = [40000, 20000, 10000, 4000, 2000, 1000, 400, 200, 100, 60, 44, 40, 30, 20, 0, 0, 0]
          item.mapGrade = i + 3;
          break;
        }
      }
    } else {
      item.mapGrade = this.deviceTypeObj[item.typeEnglish].mapGrade;
    }
  }
  
  // 2.地图初始化完毕之后给地图添加监听事件
  this.map.on("zoom-end", e => {
    this.mapGrade = e.level;
    TopologyPublic.mapBorder = e.extent;
    this.$emit("zoomEnd", e.level);
  });
  this.map.on("pan-end", e => {
    TopologyPublic.mapBorder = e.extent;
    this.$emit("panEnd");
  });
  
   // 3.检测到地图事件后重新渲染数据
  renderData() {
    let option = {
      series: this.getSeries()
    }
    this.myChart.clear();
    this.myChart.setOption(option, true);
  }
  
  // 4.根据地图等级和可视范围筛选要可以渲染的管道。元件类同
  chooseLines() {
    let zoom = TopologyPublic.map.getZoom();
    let busLinesData = [];

    for (let line of TopologyPublic.topologyData.data.lines) {
      if (  // 渲染等级 && (端口1在范围内 || 端口2在范围内)
        (line.mapGrade <= zoom && !line.userData.isDelete) && (
          (
            line.mapvertex[0].x >= TopologyPublic.mapBorder.xmin &&
            line.mapvertex[0].x <= TopologyPublic.mapBorder.xmax &&
            line.mapvertex[0].y >= TopologyPublic.mapBorder.ymin &&
            line.mapvertex[0].y <= TopologyPublic.mapBorder.ymax
          ) || (
            line.mapvertex[line.mapvertex.length - 1].x >= TopologyPublic.mapBorder.xmin
            && line.mapvertex[line.mapvertex.length - 1].x <= TopologyPublic.mapBorder.xmax
            && line.mapvertex[line.mapvertex.length - 1].y >= TopologyPublic.mapBorder.ymin
            && line.mapvertex[line.mapvertex.length - 1].y <= TopologyPublic.mapBorder.ymax
          )
        )
      ) {
        let coords = [];
        let lineColor = null;
        for (let item of line.mapvertex) {
          let arr = [item.x, item.y]
          coords.push(arr)
        }
        if (this.topologyType == "0") {
          lineColor = this.getColor(line.effectData[this.dataType])
          if (line.effectData.massFlowRate < 0) {
            coords = coords.reverse();
          }
        } else {
          lineColor = "#B22222"
          if (line.elementNumber == Store.state.chooseTopologyDataIdByTable) {
            lineColor = "#1890ff";
          }
        }

        let lineMap = {
          coords: coords,
          cd: line.length,
          lineStyle: {
            normal: {
              color: lineColor
            }
          },
          showData: {
            elementNumber: line.elementNumber,
            code: line.code,
            name: line.name,
          }
        }
        if (this.topologyType == "0") {
          for (let item of Object.keys(this.dataTypeObj)) {
            lineMap.showData[item] = Math.abs(line.effectData[item])
          }
        }
        busLinesData.push(lineMap);
      }
    }
    return busLinesData;
  }

最终效果

  • 在数据量比echarts官方示例大20倍的基础上,渲染后的效果比echarts官方示例更流畅。echarts示例数据是1543条,但移动和缩放地图的时候卡顿感很明显。本司数据差不多3万条。移动和拖放是流畅度高出很多。可惜本司系统属于私密系统,无法分享连接,就录gif图放上来。
  • echarts官方示例效果图

折线.gif

  • 本司第一版本。只应用了元件和管道分等级显示时的效果示意图
  • 本司第二版本。增加了短管合并和超长管合并后的效果示意图 折线.gif

🏆 技术专题第三期 | 数据可视化的那些事......