日常写 Bug 的时候,遇到了这么一个需求:
需要通过地图,展示公司卖出去的设备。
需求不是什么难事,写地图也不是什么难事。
上了 eharts 之后,马上完工。
一开始还没有什么问题,但随着测试数据的批量导入之后,发现了一个重大的问题。
如上图所示,如果一个地方的数据太多,显示出来的圈就会越大,甚至会大的过分。
比如广东和甘肃已经完全把周围的省份给全部包进去了,甚至连点击其他省份都做不到。
所以,这里要改。
这里的问题是每个省的设备数量关系并不确定。
一个省可能会有 10000 台设备,而另一个省可能只有 100 台设备。
那 100 倍的差距,换到地图上,就会非常之离谱……
而且这 100 倍还是半径,换算成面积,那就更加离谱。
所以必须要想个法子压一下数据,最好能控制在 10 倍以内。
首先就是看能不能用两个对象等方式来替换实现。
最后发现这是 echarts 内部的处理,所以无法实现。
data.map(obj => {
obj.value = (d[obj.name] || 0);
});
const options = {
title: {
text: this.mapTitle,
left: "center",
top: "15%",
textStyle: {
color: "#FFFFFF",
fontSize: 25,
},
},
tooltip: {
trigger: 'item',
formatter: function(params) { // 这里是 echarts 的内部处理,没有 key 等可利用的内容
let value = params.data.value[2] || params.data.value[2] === 0 ? params.data.value[2] : params.data.value;
return `${params.data.name}(${value})`;
}
},
series: [{
name: '散点',
type: 'scatter',
coordinateSystem: 'geo',
data: convertData(data), // data 的数据在这里注入
}]
}
所以只能通过数学方法来实现上述需求了。
另外,因为这里用的是同一组数据,所以这个过程还必须是可逆的
一个几何级数增长的曲线,我首先想到的就是开根号。
但开根号的问题是,如果有几十上百万的设备的话,就算开根号之后,数据依然很大。
要不就开三次、四次根,但这样就没完没了了。
而且一旦半径超过 10 倍,视觉上的差距就更明显。
也想过 y=1/x
形式的数据去计算,但却因为数学过程早就忘了,所以也不好取值。
再然后,思考了很多方法后,在传统艺能蹲坑时灵光一闪——对数!
对数的本质是把记录指数。
比如
y=10^x
记录成对数就是
x=log(y)
然后指数的增长曲线长这样
也就是说,哪怕数据上真有 1000000(10^6) 这样的数据,通过对数一算,也变成了 6 。
完美的契合我想把数据压到 10 以内的需求。
一通修改后,最后的代码是
data.map(obj => {
obj.value = (d[obj.name] || 0);
obj.value = (obj.value === 0) ? 0 : Math.log10(obj.value); // 取对数
});
const options = {
title: {
text: this.mapTitle,
left: "center",
top: "15%",
textStyle: {
color: "#FFFFFF",
fontSize: 25,
},
},
tooltip: {
trigger: 'item',
formatter: function(params) {
let value = params.data.value[2] || params.data.value[2] === 0 ? params.data.value[2] : params.data.value;
value = (value === 0) ? 0 : Math.trunc( Math.pow(10, value)); // 通过指数还原
return `${params.data.name}(${value})`;
}
},
series: [{
name: '散点',
type: 'scatter',
coordinateSystem: 'geo',
data: convertData(data), // data 的数据在这里注入
}]
}
另外,在 js 计算的时候 Math.log10(0)
会被算成 -Infinity
,而 Math.pow(10, -Infinity)
也会被算成 0
。
但我不敢保证这是浏览器框架的作用,还是 js 原生支持(没查过手册)
所以,个人还是把 0 做了特殊处理。
最后呈现的效果如下
最后要说明一下,这里为什么不用 两个对象。
一开始确实没发现,后来发现了好像确实可以用两个对象来解决……
但此时代码都已经改完了,所以也懒得再修改。
所以,就这样吧~