echarts地图上铺设柱状图

2,009 阅读7分钟

效果图

前言

本示例使用的echarts版本为3.8.0,和北京市的地图geojson(地图数据可自行下载github.com/zhangqian00…),使用最基础的模板实现,有需要的朋友可根据需求自由修改。

不要仅仅是学到铺设柱状图,换成折线图、饼图、散点图思路都是差不多的。

Html结构

引入echarts.js,和北京市地图数据,并准备一个有宽高的DOM容器:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/echarts/3.8.0/echarts.js"></script>
    <script src="./echarts/beijing.js"></script>
</head>
<body>
    <div id="map" style="width: 100%; height: 800px;"></div>

    <script src='./js/index.js'></script>
</body>
</html>

初始化echarts实例,造些所需数据

// 实例
const myChart = echarts.init(document.getElementById('map'));
// 市区中心坐标
const geoCoordMap = {
    "东城区": [116.418757, 39.937544],
    "西城区": [116.366794, 39.910309],
    "朝阳区": [116.486409, 39.991489],
    "丰台区": [116.286968, 39.863642],
    "石景山区": [116.170445, 39.974601],
    "海淀区": [116.280316, 40.039074],
    "门头沟区": [115.905381, 40.009183],
    "房山区": [115.701157, 39.735535],
    "通州区": [116.758603, 39.802486],
    "顺义区": [116.753525, 40.128936],
    "昌平区": [116.235906, 40.318085],
    "大兴区": [116.338033, 39.658908],
    "怀柔区": [116.607122, 40.524272],
    "平谷区": [117.112335, 40.244783],
    "密云区": [116.943352, 40.477362],
    "延庆区": [115.985006, 40.465325]
};
const rawData = [
    // ["东城区",10,20,30],
    // ["西城区",10,20,30],
    ["朝阳区",10,20,30],
    ["丰台区",10,20,30],
    ["石景山区",10,20,30],
    ["海淀区",10,20,30],
    ["门头沟区",10,20,30],
    ["房山区",10,20,30],
    ["通州区",10,20,30],
    ["顺义区",10,20,30],
    ["昌平区",10,20,30],
    ["大兴区",10,20,30],
    ["怀柔区",10,20,30],
    ["平谷区",10,20,30],
    ["密云区",10,20,30],
    ["延庆区",10,20,30]
];

geoCoordMap是北京市几个区的大致中心位置,柱状图的铺设位置就是根据这些区域坐标设置的。

rawData是区域的业务数据,一般是由后端接口返回的。这里东城区、和西城区因为距离太近,所以不渲染这两个区。

地图配置

const option = {
    // 地图背景颜色
    backgroundColor: new echarts.graphic.RadialGradient(0.5, 0.5, 0.4, [{
        offset: 0,
        color: '#4b5769'
    }, {
        offset: 1,
        color: '#404a59'
    }]),
    geo: {
        map: '北京',
        roam: true, // 是否开启鼠标缩放和平移漫游
        zoom: 1.155, // 地图初始大小
        center: [116.366794, 40.400309], // 初始中心位置
        label: { // 区域文字
            emphasis: { // 区域激活时配置
                show: false
            }
        },
        itemStyle: { // 地区块儿颜色
            normal: { // 普通状态
                areaColor: '#55C3FC',
                borderColor: '#fff'
            },
            emphasis: { // 激活状态
                areaColor: '#21AEF8'
            }
        }
    },
    series: []
};
// 应用配置
myChart.setOption(option);

此时基础的地图效果就出来了:

1642564254(1).jpg

下一步就是,在各区域上铺设柱状图。

配置柱状图,并铺设到每个区域上

声明柱状图配置项

const areaOption = {
    xAxis: [],
    yAxis: [],
    tooltip: {
        trigger: 'axis'
    },
    grid: [],
    series: []
};

遍历区域数据,并铺设柱状图

// 遍历
echarts.util.each(rawData, function(dataItem, idx) {
    // 获取坐标
    var geoCoord = geoCoordMap[dataItem[0]];
    // 转换坐标系上的点到像素坐标值
    var coord = myChart.convertToPixel('geo', geoCoord);
    idx += ''; // 转成字符串
    // X轴配置
    areaOption.xAxis.push({
        id: idx, // 组件id,在配置中引用标识
        gridIndex: idx, // x轴所在的grid的索引
        type: 'category', // 坐标轴类型
        name: dataItem[0], // 坐标轴名称
        nameLocation: 'middle', // 坐标轴名称显示位置
        nameGap: 3, // 坐标轴名称与轴线之间的距离
        splitLine: { // 坐标轴在 grid 区域中的分隔线
            show: false
        },
        axisTick: { // 坐标轴刻度
            show: false
        },
        axisLabel: { // 坐标轴刻度标签
            show: false
        },
        axisLine: { // 坐标轴轴线
            onZero: false,
            lineStyle: {
                color: '#666'
            }
        },
        data: ["数据A","数据B","数据C"], // 类目数据
        z: 100
    });
    // Y轴配置
    areaOption.yAxis.push({
        id: idx, // 组件id,在配置中引用标识
        gridIndex: idx, // x轴所在的grid的索引
        splitLine: { // 坐标轴在 grid 区域中的分隔线
            show: false
        },
        axisTick: { // 坐标轴刻度
            show: false
        },
        axisLabel: { // 坐标轴刻度标签
            show: false
        },
        axisLine: { // 坐标轴轴线
            show: false,
            lineStyle: {
                color: '#1C70B6'
            }
        },
        z: 100
    });
    // 坐标系配置
    areaOption.grid.push({
        id: idx, // 组件id,在配置中引用标识
        width: 30, // 组件的宽度
        height: 40, // 组件的高度
        left: coord[0] - 15, // 离容器左侧的距离
        top: coord[1] - 15, // 离容器上侧的距离
        z: 100
    });
    // 图标系列配置
    areaOption.series.push({
        id: idx, // 组件id,在配置中引用标识
        type: 'bar', // 柱状图
        xAxisId: idx, // 使用的x轴的id
        yAxisId: idx, // 使用的y轴的id
        barGap: 0, // 柱间距离
        barCategoryGap: 0, // 同一系列的柱间距离
        data: [30,50,20], // 柱子数据
        z: 100,
        itemStyle: { // 柱子样式
            normal: {
                color: function(params){
                    // 柱状图每根柱子颜色
                    var colorList = ['#F75D5D','#59ED4F','#4C91E7'];
                    return colorList[params.dataIndex];
                }
            }
        }
    });
});
// 应用配置
myChart.setOption(areaOption);

这时柱状图效果也出来了:

1642575736(1).jpg

但是还有一个问题,如果地图不能拖拽和缩放,此时的效果就已经满足需求了,但是如果配置了拖拽和缩放,当缩放或拖拽时,柱状图与地图将发生分离。

这是因为地图和柱状图本来就不是同一个图层,只是柱状图使用了根据地图的坐标系转换的坐标而铺设的。

1642576054.jpg

完善拖拽和缩放时的效果(完整代码)

const myChart = echarts.init(document.getElementById('map'));
// 市区坐标
const geoCoordMap = {
    "东城区": [116.418757, 39.937544],
    "西城区": [116.366794, 39.910309],
    "朝阳区": [116.486409, 39.991489],
    "丰台区": [116.286968, 39.863642],
    "石景山区": [116.170445, 39.974601],
    "海淀区": [116.280316, 40.039074],
    "门头沟区": [115.905381, 40.009183],
    "房山区": [115.701157, 39.735535],
    "通州区": [116.758603, 39.802486],
    "顺义区": [116.753525, 40.128936],
    "昌平区": [116.235906, 40.318085],
    "大兴区": [116.338033, 39.658908],
    "怀柔区": [116.607122, 40.524272],
    "平谷区": [117.112335, 40.244783],
    "密云区": [116.943352, 40.477362],
    "延庆区": [115.985006, 40.465325]
};
const rawData = [
    // ["东城区",10,20,30],
    // ["西城区",10,20,30],
    ["朝阳区",10,20,30],
    ["丰台区",10,20,30],
    ["石景山区",10,20,30],
    ["海淀区",10,20,30],
    ["门头沟区",10,20,30],
    ["房山区",10,20,30],
    ["通州区",10,20,30],
    ["顺义区",10,20,30],
    ["昌平区",10,20,30],
    ["大兴区",10,20,30],
    ["怀柔区",10,20,30],
    ["平谷区",10,20,30],
    ["密云区",10,20,30],
    ["延庆区",10,20,30]
];

const option = {
    // 地图背景颜色
    backgroundColor: new echarts.graphic.RadialGradient(0.5, 0.5, 0.4, [{
        offset: 0,
        color: '#4b5769'
    }, {
        offset: 1,
        color: '#404a59'
    }]),
    geo: {
        map: '北京',
        roam: true, // 是否开启鼠标缩放和平移漫游
        zoom: 1.155, // 地图初始大小
        center: [116.366794, 40.400309], // 初始中心位置
        label: { // 区域文字
            emphasis: { // 区域激活时配置
                show: false
            }
        },
        itemStyle: { // 地区块儿颜色
            normal: { // 普通状态
                areaColor: '#55C3FC',
                borderColor: '#fff'
            },
            emphasis: { // 激活状态
                areaColor: '#21AEF8'
            }
        }
    },
    series: []
};
myChart.setOption(option);
// 渲染各区域上的柱状图
renderEachArea()
// 监听geo变化,并使用防抖
myChart.on('geoRoam', _.debounce(renderEachArea,0));
// 封装方法--渲染各区域上的柱状图
function renderEachArea() {
    const areaOption = {
        xAxis: [],
        yAxis: [],
        tooltip: {
            trigger: 'axis'
        },
        grid: [],
        series: []
    };
    echarts.util.each(rawData, function(dataItem, idx) {
        // 获取坐标
        var geoCoord = geoCoordMap[dataItem[0]];
        // 转换坐标系上的点到像素坐标值
        var coord = myChart.convertToPixel('geo', geoCoord);
        idx += '';
        // X轴配置
        areaOption.xAxis.push({
            id: idx, // 组件id,在配置中引用标识
            gridIndex: idx, // x轴所在的grid的索引
            type: 'category', // 坐标轴类型
            name: dataItem[0], // 坐标轴名称
            nameLocation: 'middle', // 坐标轴名称显示位置
            nameGap: 3, // 坐标轴名称与轴线之间的距离
            splitLine: { // 坐标轴在 grid 区域中的分隔线
                show: false
            },
            axisTick: { // 坐标轴刻度
                show: false
            },
            axisLabel: { // 坐标轴刻度标签
                show: false
            },
            axisLine: { // 坐标轴轴线
                onZero: false,
                lineStyle: {
                    color: '#666'
                }
            },
            data: ["数据A","数据B","数据C"], // 类目数据
            z: 100
        });
        // Y轴配置
        areaOption.yAxis.push({
            id: idx, // 组件id,在配置中引用标识
            gridIndex: idx, // x轴所在的grid的索引
            splitLine: { // 坐标轴在 grid 区域中的分隔线
                show: false
            },
            axisTick: { // 坐标轴刻度
                show: false
            },
            axisLabel: { // 坐标轴刻度标签
                show: false
            },
            axisLine: { // 坐标轴轴线
                show: false,
                lineStyle: {
                    color: '#1C70B6'
                }
            },
            z: 100
        });
        // 坐标系配置
        areaOption.grid.push({
            id: idx, // 组件id,在配置中引用标识
            width: 30, // 组件的宽度
            height: 40, // 组件的高度
            left: coord[0] - 15, // 离容器左侧的距离
            top: coord[1] - 15, // 离容器上侧的距离
            z: 100
        });
        // 图标系列配置
        areaOption.series.push({
            id: idx, // 组件id,在配置中引用标识
            type: 'bar', // 柱状图
            xAxisId: idx, // 使用的x轴的id
            yAxisId: idx, // 使用的y轴的id
            barGap: 0, // 柱间距离
            barCategoryGap: 0, // 同一系列的柱间距离
            data: [30,50,20], // 柱子数据
            z: 100,
            itemStyle: { // 柱子样式
                normal: {
                    color: function(params){
                        // 柱状图每根柱子颜色
                        var colorList = ['#F75D5D','#59ED4F','#4C91E7'];
                        return colorList[params.dataIndex];
                    }
                }
            }
        });
    });
    // 应用配置
    myChart.setOption(areaOption);
}

监听geo变化,并将铺设柱状图操作封装成方法,以便在监听函数的回调中调用。

此处考虑性能问题,加了防抖函数,使用的lodash.js

根据此案例可以自行试验下,在地图上铺设折线图、饼状图等其他类型图标,可以做到举一反三才是真的学到了!