huiyan-fe / mapv

a library of geography visualization-地理信息可视化库
http://mapv.baidu.com
BSD 3-Clause "New" or "Revised" License
2.58k stars 787 forks source link

性能优化 - 解决使用data range intensity在大批量数据下渲染性能缓慢 #192

Open 314513535 opened 5 years ago

314513535 commented 5 years ago

背景 项目中需要基于百度地图在一屏绘制大量栅格图形,数据量大概在几千到4万不等。同时需要对多边形的不同count值才用渐变色的分级着色(data range intensity)功能。之前只在Chrome浏览器上测试过,发现性能还行。但最近随着Chrome升级到V76版本后,性能暴降,于是做了几轮测试,发现 在V76及之后的Chrome版本上,绘制大概18000个栅格,需要消耗平均10~13秒。在其他浏览器,比如IE 11, Edge, Firefox上也要消耗至少10秒的时间,说明慢的原因不在于浏览器本身,Chrome升级后应该是把原先内置的某种优化废弃了。使用的option如下: { size: 20, fillStyle: 'rgba(100, 100, 100, 0.9)', max: 49, maxSize: 35, minSize: 0, min: 0, globalAlpha: 0.7, context: '2d', draw: 'intensity', gradient: { "0": " rgba(8,108,162,0.9)", "0.01": " rgba(0, 187, 63, 0.9)", "0.25": " rgba(255, 198, 0, 0.9)", "0.5": " rgba(255, 146, 0, 0.9)", "0.75": " rgba(255, 40, 0, 0.9)" } }

原因分析 仔细查看代码后发现了在渲染时BaseLayer会调用processData方法。该方法中会遍历data,在我的case中就是循环18000次。由于使用了intensity,每一次都会调用其getColor方法 `processData(data) { var self = this; var draw = self.options.draw; if (draw == 'bubble' || draw == 'intensity' || draw == 'category' || draw == 'choropleth' || draw == 'simple') {

        for (var i = 0; i < data.length; i++) {
            var item = data[i];

            if (self.options.draw == 'bubble') {
                data[i]._size = self.intensity.getSize(item.count);
            } else {
                data[i]._size = undefined;
            }

            var styleType = '_fillStyle';

            if (data[i].geometry.type === 'LineString' || self.options.styleType === 'stroke') {
                styleType = '_strokeStyle';
            }

            if (self.options.draw == 'intensity') {
                data[i][styleType] = self.intensity.getColor(item.count);
            } else if (self.options.draw == 'category') {
                data[i][styleType] = self.category.get(item.count);
            } else if (self.options.draw == 'choropleth') {
                data[i][styleType] = self.choropleth.get(item.count);
            }
        }

    }
}`

而在getColor方法中,会调用this.paletteCtx.getImageData 来获得ColorSet。正是这个地方造成性能的缓慢。

`Intensity.prototype.getColor = function (value) {

var imageData = this.getImageData(value);

return "rgba(" + imageData[0] + ", " + imageData[1] + ", " + imageData[2] + ", " + imageData[3] / 256 + ")";

}

Intensity.prototype.getImageData = function (value) {

var imageData = this.paletteCtx.getImageData(0, 0, 256, 1).data;

if (value === undefined) {
    return imageData;
}

var max = this.max;
var min = this.min;

if (value > max) {
    value = max;
}

if (value < min) {
    value = min;
}

var index = Math.floor((value - min) / (max - min) * (256 - 1)) * 4;

return [imageData[index], imageData[index + 1], imageData[index + 2], imageData[index + 3]];

}`

解决方案 这个ColorSet显然与传入的value无关,可以提取到data 循环的外部 `Intensity.prototype.getColorSet = function () { return this.paletteCtx.getImageData(0, 0, 256, 1).data; }; Intensity.prototype.getColorFromColorSet = function (value, colorSet) { var imageData = this.getImageDataFromColorSet(value, colorSet); return "rgba(" + imageData[0] + ", " + imageData[1] + ", " + imageData[2] + ", " + imageData[3] / 256 + ")"; };

Intensity.prototype.getImageDataFromColorSet = function (value, colorSet) { if (value === undefined) { return colorSet; } var max = this.max; var min = this.min; if (value > max) { value = max; } if (value < min) { value = min; } var index = Math.floor((value - min) / (max - min) (256 - 1)) 4; return [colorSet[index], colorSet[index + 1], colorSet[index + 2], colorSet[index + 3]]; }; 然后在循环内使用预先准备好的ColorSet processData(data) { var self = this; var draw = self.options.draw; if (draw == 'bubble' || draw == 'intensity' || draw == 'category' || draw == 'choropleth' || draw == 'simple') { var colorSet = self.intensity.getColorSet(); for (var i = 0; i < data.length; i++) { var item = data[i]; if (self.options.draw == 'bubble') { data[i]._size = self.intensity.getSize(item.count); } else { data[i]._size = undefined; } var styleType = '_fillStyle'; if (data[i].geometry.type === 'LineString' || self.options.styleType === 'stroke') { styleType = '_strokeStyle'; } if (self.options.draw == 'intensity') { data[i][styleType] = self.intensity.getColorFromColorSet(item.count, colorSet); } else if (self.options.draw == 'category') { data[i][styleType] = self.category.get(item.count); } else if (self.options.draw == 'choropleth') { data[i][styleType] = self.choropleth.get(item.count); } } } }`

测试后发现在新版Chrome及其他主流浏览器下,同样渲染18000个栅格仅需要1~2秒,问题解决。 望能采纳该意见及早进行修复。