跳至主要內容

海量地图数据处理

夏欢大约 5 分钟

海量地图数据处理

需求

上万条监测点数据显示在地图上,层级到达14级时,切换成灾害类型图标。

解决方案

由于数据过多直接添加marker数据的方式,会造成浏览器卡死的情况。经研究我们采用方案是小于14级时用天地图海量数据显示,,层级大于14级时,切换成灾害类型图标。

1.创建地图

初始化地图
initMap() {
      // 创建地图实例
      // map:用于显示地图的DIV对象。
      // {},地图属性对象
      this.Map = new T.Map("map", {
        //projection:"EPSG:4326",
        enableAutoResize: true,
      });
      //   this.Map.centerAndZoom() 对地图进行初始化。未进行初始化的地图将不能进行任何操作。
      var center = this.getCenter();
      var point = new T.LngLat(center[0], center[1]);
      this.Map.centerAndZoom(point, this.mapZoom || this.mapLevel); // 地图全局事件监听
      this.mapEventListen(); // 设置地图区域
      this.getProjectList(); // 获取项目列表
    },
    mapEventListen() {
      let that = this;
      var zoom;
      // 监听地图事件缩放事件
      this.Map.addEventListener("zoomend", function (e) {
        zoom = this.getZoom();
        if (zoom >= 14) {
          that.isIconShow = true;
          that.parseProjectListForMap(that.initProjectList, zoom);
        } else if (that.isIconShow && zoom < 14) {
          that.parseProjectListForMap(that.initProjectList, zoom);
        }
      });
      // 监听地图移动事件
      this.Map.addEventListener("moveend", function (e) {
        if (zoom >= 14) {
          that.isIconShow = true;
          that.parseProjectListForMap(that.initProjectList, zoom);
        }
      });
    },
  },
};

2.地图数据显示

采用天地图地图组件,添加海量密集点解决3万多监测点显示问题
let lnglats = [];
      var _CloudCollection;
      for (var i = 0; i < projectList.length; i++) {
        var ll = new T.LngLat(projectList[i].lon, projectList[i].lat);
        lnglats.push(ll);
      }
      if (document.createElement("canvas").getContext) {
        // 判断当前浏览器是否支持绘制海量点
        _CloudCollection = new T.CloudMarkerCollection(lnglats, {
          color: "blue",
          SizeType: TDT_POINT_SIZE_SMALLER
        });
        this.Map.addOverLay(_CloudCollection);
      } else {
        alert("此示例目前只有在IE9及以上浏览器打开");
      }

3.储存监测数据在本地,用于层级大于14时,只在当前可视区域显示图标。

采用RBush RBush-一个基于javascriptr-tree的高性能二维点和矩形空间索引
RBush https://www.5axxw.com/wiki/content/7wjc4topen in new window
安装rbush     npm install rbush
 引入RBush   import RBush from "rbush";
创建树   const tree = new RBush();
存储监测数据到RBush

 for (var i = 0; i < projectList.length; i++) {
          var ll = new T.LngLat(projectList[i].lon, projectList[i].lat);
          lnglats.push(ll);
          let temp = {
            minX: Number(projectList[i].lon),
            minY: Number(projectList[i].lat),
            maxX: Number(projectList[i].lon),
            maxY: Number(projectList[i].lat),
            foo: projectList[i]
          };
          treeList.push(temp);
        }
        tree.load(treeList);
        if (document.createElement("canvas").getContext) {
          // 判断当前浏览器是否支持绘制海量点
          _CloudCollection = new T.CloudMarkerCollection(lnglats, {
            color: "blue",
            SizeType: TDT_POINT_SIZE_SMALLER
          });
          this.Map.addOverLay(_CloudCollection);
        } else {
          alert("此示例目前只有在IE9及以上浏览器打开");
        }
this.isIconShow = false;


 层级大于14时,先隐藏海量监测点
if (window._CloudCollection) {
          window._CloudCollection.getPane().style.visibility = "hidden";
        }
        
        
获取当前可视区域下的可视区域左下角 右上角,找出当前可视区域下的监测点,显示在地图上
        var bs = this.Map.getBounds(); //获取可视区域
        var bssw = bs.getSouthWest(); //可视区域左下角
        var bsne = bs.getNorthEast(); //可视区域右上角
        const result = tree.search({
          minX: bssw.getLng(),
          minY: bssw.getLat(),
          maxX: bsne.getLng(),
          maxY: bsne.getLat()
        });
        console.log(result,"当前可视区域下的监测点")
完整逻辑代码

var locations = [];
      if (currentZoom >= 14) {
        if (window._CloudCollection) {
          window._CloudCollection.getPane().style.visibility = "hidden";
        }
        var bs = this.Map.getBounds(); //获取可视区域
        var bssw = bs.getSouthWest(); //可视区域左下角
        var bsne = bs.getNorthEast(); //可视区域右上角
        const result = tree.search({
          minX: bssw.getLng(),
          minY: bssw.getLat(),
          maxX: bsne.getLng(),
          maxY: bsne.getLat()
        });
        let _this = this;
        this.projectMarker.map(item => {
          item._cache = false;
        });
        for (var i = 0; i < result.length; i++) {
          let point = new T.LngLat(result[i].foo.lon, result[i].foo.lat);
          let modeName = "";
          let normalName = result[i].foo.extAttr3 || 5;
          modeName = this.getProjectListType(result[i].foo.monitorObject);
          let myIcon = new T.Icon({
            iconUrl:
              "/images/real-time/" + modeName + "-" + normalName + ".png",
            iconSize: new T.Point(this.iconX, this.iconY),
            iconAnchor: new T.Point(this.iconX / 2, this.iconY / 2)
          });
          // 创建标注对象并添加到地图
          let marker = new T.Marker(point, {
            icon: myIcon
          });
          locations.push(marker.getLngLat());
          // 添加标注
          marker._cache = true;
          this.Map.addOverLay(marker);
          // 为当前点添加事件
          marker.addEventListener("click", function(data) {});
          this.projectMarker.push(marker);
        }
        this.projectMarker.forEach(item => {
          if (!item._cache) {
            this.Map.removeOverLay(item);
          }
        });
      } else {
        // 清空14级以上marker
        this.projectMarker.forEach(item => {
          this.Map.removeOverLay(item);
        });
        if (!window._CloudCollection) {
          let treeList = [];
          let levels = [];
          for (var i = 0; i < projectList.length; i++) {
            var ll = new T.LngLat(projectList[i].lon, projectList[i].lat);
            locations.push(ll);
            levels.push(projectList[i].extAttr3 || "0");
            let temp = {
              minX: Number(projectList[i].lon),
              minY: Number(projectList[i].lat),
              maxX: Number(projectList[i].lon),
              maxY: Number(projectList[i].lat),
              foo: projectList[i]
            };
            treeList.push(temp);
          }
          tree.load(treeList);
          if (document.createElement("canvas").getContext) {
            // 判断当前浏览器是否支持绘制海量点
            // 根据类型更改海量点颜色
            // overTdt();
            window._CloudCollection = new T.CloudMarkerCollection(locations, {
              levels: levels,
              color: "blue",
              SizeType: TDT_POINT_SIZE_SMALLER
            });
            this.Map.addOverLay(window._CloudCollection);
          } else {
            this.$Message.warning("此示例目前只有在IE9及以上浏览器打开");
          }
        } else {
          window._CloudCollection.getPane().style.visibility = "inherit";
        }
        this.isIconShow = false;
      }

总结

1、地图使用时,应该避免大量加载marker。如果需要加载大量数据,则采用canvas等技术方式进行渲染
2、rbush是一个支持空间查询的js内存数据库(索引)。很适用于地图上可视区域范围内的数据检索,官方文档是:https://www.5axxw.com/wiki/content/7wjc4topen in new window
3、在前端操作时,应避免大规模的修改dom节点的属性(样式、类名、节点添加等),会造成浏览器频繁rerender,这是造成卡顿的核心因素。记住:显示器只有一层,网页的层级是逻辑概念,这是给开发人员的。在计算机层面,它会去计算是否存在遮挡,然后修改对应的像素点,这就是大量计算的根本原因,如果界面图像过于复杂,采用原始的绘制技术,就会导致卡顿。
4、b/s模式性能始终是决定于浏览器的性能及原生接口开发能力,不像c/s可以有效的设置和调度cpu,gpu等,所有性能始终是存在浏览器及电脑的设置上,我们不要对其外围性能报太大期望,核心思路就是:减少前端数据处理及渲染,把复杂的处理或者渲染逻辑放到服务器上