在echarts中使用百度地图扩展,实现在地图上叠加显示栅格热力图

思路:

  1. 获得地图上的多边形覆盖物的点的数组中的最大的经度和纬度作为东北角的经纬度,最小的经度和纬度作为西南角的经纬度,这样的矩形将会完全包含多边形覆盖物,对这个矩形绘制栅格需;
  2. 对西南角和东北角的经纬度进行处理,返回能够得到整数数量栅格需的西南角和东北角经纬度。一方面确保绘制出来的栅格需数量是整数,另一方面从西南角开始的网格的整数数量也会被作为位置使用来设置栅格需的id。
  3. 根据西南角和东北角的经纬度能够得到四个角的经纬度,因此能够根据需求的网格的大小获得栅格需的数量,位置。
  4. 判断每个栅格的中心点是否在多边形覆盖物内,在则给colorValue赋值为0,否则为-1。
  5. 栅格根据id与接口返回的数据做匹配来获取要渲染的栅格及其value值; 判断每个栅格的中心点是否在多边形覆盖物内,在则给colorValue赋值为0,否则为-1。
  6. 调用echarts渲染栅格需的方法,判断colorValue来确定是否在轮廓范围内,在则渲染,不在则不渲染,然后根据栅格的value值渲染栅格的热力。
data:function(){
	return {
		    //多边形覆盖物
		    plateOverlays:[],
            //栅格默认的参数
            blockDefaultParams:{
              southWest: { lng: 112.963588, lat: 22.515978 },
              northEast: { lng: 114.075063, lat: 23.945013 },
              dataBlockSize: 5000,
            },
            //接口返回的栅格的数组
            blockData:[]
	}
},
methods:{
		//根据地图上的多边形覆盖物轮廓获得最大的经度纬度作为东北角,最小的经度纬度作为西南角
        updateBlockDefaultParams:function(){
          var _this = this;

          var points = [];
          var plateOverlays = _this.plateOverlays;
          for (var i in plateOverlays) {
            var polygon = plateOverlays[i];
            points = points.concat(polygon.getPath());//返回多边形覆盖物的点数组
          }

          //取多边形覆盖物各点中的最大的经度纬度作为东北角,最小的经度纬度作为西南角
          //所绘的矩形完全包括了多边形覆盖物
          var lngArr = [],
            latArr = [];
          points.map(function(item){
            lngArr.push(item.lng);
            latArr.push(item.lat);
          })
          var maxLng = lngArr.reduce(function(a , b){
            return b > a ? b : a;
          });
          var maxLat = latArr.reduce(function(a , b){
            return b > a ? b : a;
          });
          var minLng = lngArr.reduce(function(a , b){
            return b < a ? b : a;
          });
          var minLat = latArr.reduce(function(a , b){
            return b < a ? b : a;
          });

          var southWest = {
            lng: minLng,
            lat: minLat
          }

          var northEast = {
            lng: maxLng,
            lat: maxLat
          }

          _this.blockDefaultParams.southWest = southWest;
          _this.blockDefaultParams.northEast = northEast;

          _this.$nextTick(function(){
            _this.getBlock();
          })
        },
        getNearestPoint :function (tp, lngLatInterval, max) {
          var _this = this;

          var nearestPoint = {};

          //分别获得经纬度上栅格的数量
          var xLengthVal = tp.lng / lngLatInterval[0];
          var yLengthVal = tp.lat / lngLatInterval[1];

          //对栅格数量进行取整
          var xLength = '',
            yLength = '';
          if (max) {
            xLength = parseInt(Math.ceil(xLengthVal));
            yLength = parseInt(Math.ceil(yLengthVal));
          }else{
            xLength = parseInt(Math.floor(xLengthVal));
            yLength = parseInt(Math.floor(yLengthVal));
          }

          //获得能够使栅格数量为整数的西南角和东北角的经纬度坐标
          nearestPoint.lng = lngLatInterval[0] * xLength;
          nearestPoint.lat = lngLatInterval[1] * yLength;

          return nearestPoint;
        },
        //栅格尺寸大小转化为经纬度距离
        distanceToLngLat : function (distance) {
          var _this = this;
          var values = new Array(2);
          values[0] = distance * 0.00000978;
          values[1] = distance * 0.00000899;

          return values;
        },
        //渲染网格
        getBlock:function(){
          var _this = this;

          var dp = _this.blockDefaultParams;

          // 网格的格子大小
          var blockSize = dp.dataBlockSize;
          //将网格大小转换为经纬度上的距离
          var lngLatInterval = _this.distanceToLngLat(blockSize);

          //对西南角和东北角的经纬度进行处理,返回能够得到整数数量网格的西南角和东北角经纬度
          var defaultSouthWest = _this.getNearestPoint(dp.southWest, lngLatInterval, false);
          var defaultNorthEast = _this.getNearestPoint(dp.northEast, lngLatInterval, true);

          //网格数量
          var colLength = parseInt((defaultNorthEast.lng - defaultSouthWest.lng) / lngLatInterval[0]) ;
          var rowLength = parseInt((defaultNorthEast.lat - defaultSouthWest.lat) / lngLatInterval[1]) ;

          var data = [];//最终渲染的网格的数据
          var blockData = _this.blockData;//接口返回的网格数组

          //获得(0,0)到西南角的网格数量,作为网格的位置
          var colStart = defaultSouthWest.lng / lngLatInterval[0];
          var rowStart = defaultSouthWest.lat / lngLatInterval[1];

          var polygons = _this.plateOverlays;

          for (var col = 0; col < colLength; col++) {
            for (var row = 0; row < rowLength; row++) {

              //相当于网格id
              var uCode = "col" + (col + colStart) + "-row" + (row + rowStart);
              //遍历接口返回的数组,判断是否有匹配的网格
              var block = null;
              blockData.map(function(item){
                if(item.blockId == uCode){
                  block = item;
                }
              })

              //获得网格的中心点
              var lng = defaultSouthWest.lng + col * lngLatInterval[0] + lngLatInterval[0] * 0.5;
              var lat = defaultSouthWest.lat + row * lngLatInterval[1] + lngLatInterval[1] * 0.5;
              var point = new BMap.Point(lng, lat);

              //判断栅格的中心点是否在多边形覆盖物内
              var inside = false;
              if (polygons != null && polygons.length > 0) {
                for (var i in polygons) {
                  var polygon = polygons[i];
                  if (BMapLib.GeoUtils.isPointInPolygon(point, polygon)) {
                    inside = true;
                    break;
                  }
                }
              }


              var colorVal = -1;//colorValue用来判断是否要绘制,在多边形覆盖物区域内则绘制,在多边形覆盖物区域外则不绘制
              var value = 0;//color是热力的值

              if (inside) {
                colorVal = 0;
                if (block != null) {
                  value = block.val;
                }
              }

              data.push({
                value:[col, row,colorVal,value, uCode, point]
              });
            }
          }

          //渲染每个网格
          function renderItem(params, api) {
            var context = params.context;
            var lngIndex = api.value(0);
            var latIndex = api.value(1);
            var pointLeftTop = getCoord(params, api, lngIndex, latIndex);
            var pointRightBottom = getCoord(params, api, lngIndex + 1, latIndex + 1);

            //如果是-1,即在区域外,则不绘制
            var colorVal = api.value(2);
            if (colorVal == -1) {
              context = '';
              lngIndex = '';
              latIndex = '';
              pointLeftTop = '';
              pointRightBottom = '';
            }

            return {
              type: 'rect',
              shape: {
                x: pointLeftTop[0],
                y: pointLeftTop[1],
                width: pointRightBottom[0] - pointLeftTop[0],
                height: pointRightBottom[1] - pointLeftTop[1]
              },
              style: api.style({
                stroke: 'rgba(73,208,180,0.9)',//网格的边线颜色
              }),
              styleEmphasis: api.styleEmphasis()
            };
          }

          //经度和纬度方向
          var lngExtent = [defaultSouthWest.lat, defaultNorthEast.lat];
          var latExtent = [defaultSouthWest.lng, defaultNorthEast.lng];

          var cellSizeCoord = [
            lngLatInterval[0],
            lngLatInterval[1]
          ];

          function getCoord(params, api, lngIndex, latIndex) {
            var coords = params.context.coords || (params.context.coords = []);
            var key = lngIndex + '-' + latIndex;
            return coords[key] || (coords[key] = api.coord([
              +(latExtent[0] + lngIndex * cellSizeCoord[0]).toFixed(6),
              +(lngExtent[0] + latIndex * cellSizeCoord[1]).toFixed(6)
            ]));
          }

          var option = {
            visualMap:{
              type: 'piecewise',
              show: false,
             pieces:[
             	{
                    min:35000,max:40000,color:'#435DFF',opacity:0.8
                },
                {
                    min:40000,color:'#503EFF',opacity:0.8
                }
             ]
            },
            series:[
              {
                type: 'custom',
                coordinateSystem: 'bmap',
                renderItem: renderItem,
                animation: false,
                encode: {
                  tooltip: 2
                },
                data: data
              }
            ]
          }

          _this.echartsModel.setOption(option);
        }
}

这种实现方式,首先需要根据轮廓来绘制出一个矩形的全部栅格;然后再调用方法判断栅格是否在轮廓范围内,在则渲染,否则不渲染。这两步都非常耗时。

优化之后的方法:数据分析师那边算出需要渲染的栅格通过后台传到前端,返回什么渲染什么即可。(不再需要绘制全部的栅格和去判断是否在边界范围内,这些步骤交给数据分析师去做)

可以想象,由于知道了所渲染区域的最大最小经纬度,现在地图上有一个矩形,把它划分成一行行一列列;根据返回的栅格数据可以知道栅格的行和列,以此来排列渲染。

methdos:{
	//渲染栅格
	//需要的参数:所渲染区域的最大最小经纬度(数值类型)、所画栅格的经纬度大小(数值类型)、栅格数据(data=[[col,row,id,value],[col,row,id,value]])
    renderGrid:function(result){
        //所渲染区域的最大最小经纬度
        var lngExtent = [result.city.minlat, result.city.maxlat];
        var latExtent = [result.city.minlng, result.city.maxlng];

		//所画栅格的经纬度大小
        var cellSizeCoord = [
            result.city.lnggap,
            result.city.latgap
        ];

		//栅格数据
        var data = result.block;

        //渲染每个网格
        function renderItem(params, api) {
            var context = params.context;
            var lngIndex = api.value(0);
            var latIndex = api.value(1);
            var pointLeftTop = getCoord(params, api, lngIndex, latIndex);
            var pointRightBottom = getCoord(params, api, lngIndex + 1, latIndex + 1);

            return {
                type: 'rect',
                shape: {
                    x: pointLeftTop[0],
                    y: pointLeftTop[1],
                    width: pointRightBottom[0] - pointLeftTop[0],
                    height: pointRightBottom[1] - pointLeftTop[1]
                },
                style: api.style({
                    stroke: 'white',//网格的边线颜色
                }),
                styleEmphasis: api.styleEmphasis()
            };
        }

        function getCoord(params, api, lngIndex, latIndex) {
            var coords = params.context.coords || (params.context.coords = []);
            var key = lngIndex + '-' + latIndex;
            return coords[key] || (coords[key] = api.coord([
                +(latExtent[0] + lngIndex * cellSizeCoord[0]).toFixed(6),
                +(lngExtent[0] + latIndex * cellSizeCoord[1]).toFixed(6)
            ]));
        }

        var option = {
            visualMap:{
                type: 'piecewise',
                show:false,
                inRange: {
                   color: ["#FEEFAC", "#FEE199",'#FEC77F','#FEAD72','#FC8068','#EA5C5C','#D14D65','#A94A62'],
                   opacity: 0.8,
              },
              splitNumber:8,
              min:0,
              max:100
            },
            series:[
                {
                    type: 'custom',
                    coordinateSystem: 'bmap',
                    renderItem: renderItem,
                    animation: false,
                    encode: {
                        tooltip: 2
                    },
                    data: data
                }
            ]
        }
        this.echartsModel.setOption(option);
    }
}

你可能感兴趣的:(Echarts)