最近要在地图上动态展示各行政区的统计数据,要求是饼图。因为同时存在多个饼图,所以Mapbox的echartsLayer插件没办法用。但是和Mapbox的官方一个实例有些类似,博主改造了一下。实现了下面的效果:
效果
原理
给Mapbox添加自定义的Marker,其中的内容使用svg填充,根据数据动态计算svg内的path。
代码
<template>
<div id="map">
</div>
</template>
<script>
import mapboxgl from 'mapbox-gl';
export default {
name: 'HelloWorld',
data() {
return {
map: null,
}
},
mounted() {
mapboxgl.accessToken =
'pk.eyJ1IjoiaGFtYnVnZXJkZXZlbG9wIiwiYSI6ImNqNXJtZjF4ZzB3em4yd21pZmVqbHlleDAifQ.I9eqVjtotz7jaU7XcJm9pQ';
let map = new mapboxgl.Map({
container: "map",
style: "mapbox://styles/mapbox/light-v10",
zoom: 0.3,
center: [0, 20]
});
this.map = map;
//解决canvas渲染不正确
map.on("styledata", function () {
map.resize();
});
map.on("load", e => {
for (let i = 0; i < 10; i++) {
let el=this.createDonutChart();
new mapboxgl.Marker({element: el}).setLngLat([this.getRandomLng(),this.getRandomLat()]).addTo(map);
}
})
},
methods: {
getRandomLat(){
return Math.random() * 90
},
getRandomLng(){
return Math.random() * 180
},
// 生成随机数据
getData(){
return [Math.floor(Math.random() * 11),Math.floor(Math.random() * 11),Math.floor(Math.random() * 11)]
},
createDonutChart() {
//随机数据
let data=this.getData();
let total=data[0]+data[1]+data[2];
if(total===0){return }
//半径和直径
let w = 100;
let r = w/2;
let fontSize=14;
//角度计算
let degree1=data[0]/total*2*Math.PI;
let degree2=data[1]/total*2*Math.PI;
//计算大角小角
let largeArcFlag1=degree1>Math.PI?1:0;
let largeArcFlag2=degree2>Math.PI?1:0;
//计算终点位置
let endX1,endY1,endX2,endY2;
endX2=r-r*Math.sin(degree2);
endX1=r+r*Math.sin(degree1);
endY2=r-r*Math.cos(degree2);
endY1=r-r*Math.cos(degree1);
//拼接SVG
let html = '<svg width="' + w + '" height="' + w + '" viewbox="0 0 ' + w + ' ' + w +
'" text-anchor="middle" style="font: ' + fontSize + 'px sans-serif"><circle cx="' + r + '" cy="' + r + '" r="' + r +
'" fill="#FF0000" />';
let greenArea = `<path d="
M${r} ${r}
L${r} 0
A${r} ${r} 0 ${largeArcFlag1} 1 ${endX1} ${endY1}
" fill="#00FF00"/>`
let blueArea = `<path d="
M${r} ${r}
L${r} 0
A${r} ${r} 0 ${largeArcFlag2} 0 ${endX2} ${endY2}
" fill="#0000FF"/>`
html += blueArea;
html += greenArea;
html+="</svg>"
let el = document.createElement('div');
el.innerHTML = html;
return el.firstChild;
},
}
}
</script>
<style>
body, html {
padding: 0;
margin: 0;
}
#map {
width: 100%;
height: 100%;
}
</style>