ECharts 3D 地图可视化实战:从数据到交互式地图
·
本文将详细解析一个基于 Vue 和 ECharts 的 3D 地图可视化项目,展示如何将地理数据转化为精美的交互式地图。

项目概述
这个项目实现了一个中国地图可视化,具有以下特点:
-
多层叠加的 3D 地图效果
-
从深圳到其他城市的动态飞线
-
交互式城市标记点
-
精美的视觉效果和动画
核心代码解析
1. 数据准备
首先,我们处理原始数据,提取需要的字段并格式化:
const processedData = originalData.map((item, index) => ({
id: index,
city: item.city,
provinceCapitalX: item.provinceCapitalX,
provinceCapitalY: item.provinceCapitalY,
num: 200
}));
2. 地图初始化
在 Vue 组件的 mounted 钩子中初始化 ECharts:
mounted() {
this.setChartInit();
},
methods: {
setChartInit() {
const dom = this.$refs.chartRef;
this.myChart = echarts.init(dom);
echarts.registerMap('chinaJson', chinaJson);
echarts.registerMap('chinaItem', chinaItem);
// 数据处理和选项配置...
}
}
3. 多层地图设计
这个地图由 5 层叠加组成,每层有不同的视觉效果:
-
透视层:最上层,提供边框效果
-
实际层:交互层,显示标签和可点击区域
-
中国边框层:增强国家轮廓
-
中间颜色层:增加深度感
-
底层:最暗的背景层
geo: [
{
type: 'map',
map: 'chinaItem',
zlevel: 3,
// 透视层配置...
},
{
type: 'map',
map: 'chinaItem',
zlevel: 2,
// 实际层配置...
},
// 其他层...
]
4. 飞线和标记点
创建从深圳到各城市的飞线和标记点:
const linesData = this.cityList.map(city => ({
fromName: FROMCITY,
toName: city.city,
coords: [FROMCITYCOORD, [city.provinceCapitalX, city.provinceCapitalY]],
value: city
}));
const scatterData = this.cityList.map(city => ({
value: [
city.provinceCapitalX,
city.provinceCapitalY,
city
],
name: city.city.replace('市', '')
}));
5. 交互效果
添加了三种交互事件:
this.myChart.on('click', this.onChartClick);
this.myChart.on('mouseover', this.onChartMouseover);
this.myChart.on('mouseout', this.onChartMouseout);
技术要点
-
3D 地图效果:通过多层叠加和不同的 zlevel 值创建 3D 视觉效果
-
性能优化:使用 silent 属性减少不必要的交互层计算
-
视觉层次:通过阴影、边框和透明度调节创建深度感
-
交互设计:精心设计的高亮效果和动画增强用户体验
完整实现建议
-
确保已正确导入 ECharts 和 ECharts-GL
-
准备高质量的中国地图 JSON 数据
-
根据实际需求调整视觉效果参数
-
添加响应式设计,适应不同屏幕尺寸
整体代码
<template>
<div class="chartRef" ref="chartRef" style="height: 100%;width: 100%" />
</template>
<script>
import * as echarts from 'echarts'
import '@/utils/chinaMap'
import "echarts-gl"; //3D地图插件
import chinaJson from '@/utils/chinaOutline.json'
const FROMCITY = [113.28064, 23.125177]
const FROMCITYCOORD = [113.28064, 23.125177]
export default {
name: 'Demo',
data() {
return {
cityList:[
{
"id": 0,
"city": "绍兴市",
"provinceCapitalX": "120.15358",
"provinceCapitalY": "30.287458",
"num": 200
},
{
"id": 1,
"city": "北京市",
"provinceCapitalX": "116.405289",
"provinceCapitalY": "39.904987",
"num": 200
},
{
"id": 2,
"city": "嘉兴市",
"provinceCapitalX": "120.15358",
"provinceCapitalY": "30.287458",
"num": 200
},
{
"id": 3,
"city": "锦州市",
"provinceCapitalX": "118.76741",
"provinceCapitalY": "41.796768",
"num": 200
},
{
"id": 4,
"city": "临沂市",
"provinceCapitalX": "120.15358",
"provinceCapitalY": "30.287458",
"num": 200
},
{
"id": 5,
"city": "深圳市",
"provinceCapitalX": "113.28064",
"provinceCapitalY": "23.125177",
"num": 200
}
],
}
},
mounted() {
this.setChartInit()
},
watch: {
},
methods: {
onChartClick(){},
onChartMouseover(){},
onChartMouseout(){},
setChartInit() {
const dom = this.$refs.chartRef;
this.myChart = echarts.init(dom);
echarts.registerMap('chinaJson', chinaJson); // 确保chinaJson是有效的3D地图数据
echarts.registerMap('chinaItem', chinaItem); // 确保chinaJson是有效的3D地图数据
// 预处理数据
const linesData = this.cityList.map(city => ({
fromName: FROMCITY,
toName: city.city,
coords: [FROMCITYCOORD, [city.provinceCapitalX, city.provinceCapitalY]],
value: city
}));
const scatterData = this.cityList.map(city => ({
value: [
city.provinceCapitalX,
city.provinceCapitalY,
city
],
name: city.city.replace('市', '')
}));
const option = {
backgroundColor: 'transparent',
geo: [
// 第一层: 透视层
{
type: 'map',
map: 'chinaItem',
zlevel: 3,
zoom: 1,
aspectScale: 0.86,
layoutCenter: ['50%', '52%'],
layoutSize: '100%',
roam: false,
silent: true,
label: {
show: false,
},
itemStyle: {
borderColor: 'rgba(223, 245, 255, 0.5)',
shadowColor: 'transparent',
areaColor: 'transparent',
borderWidth: 2,
shadowOffsetY: 0,
shadowBlur: 25,
opacity: 1,
},
},
// 第二层: 实际层
{
type: 'map',
map: 'chinaItem',
zlevel: 2,
zoom: 1,
aspectScale: 0.86,
layoutCenter: ['50%', '52%'],
layoutSize: '100%',
roam: false,
silent: false,
label: {
show: true,
color: 'RGBA(255, 255, 255, 1)',
fontSize: 16,
fontWidth: 600
},
itemStyle: {
borderColor: 'rgba(37, 182, 144, 0.5)',
areaColor: 'transparent',
borderWidth: 2,
},
emphasis: { // 添加高亮样式
label: {
show: true,
color: "RGBA(255,255,255,1)" // 设置为白色或其他你想要的颜色
},
itemStyle: {
areaColor: 'RGBA(14, 53, 42, 0.5)', // 高亮颜色
borderWidth: 1,
borderColor: 'rgba(223, 245, 255, 0.2)',
shadowBlur: 10,
borderColor: 'rgba(223, 245, 255, 0.2)',
}
},
// 高亮省份
regions: [{
name: '广东',
itemStyle: {
areaColor: "RGBA(14, 53, 42, 0.4)", // 可选:同时设置区域颜色
borderWidth: 2,
borderColor: 'RGBA(255,255,255,1)',
shadowBlur: 30,
shadowColor: 'RGBA(255,255,255,1)',
opacity: 1,
},
},
],
},
// 第三层:中国边框
{
type: 'map',
map: 'chinaJson',
roam: false,
zoom: 1,
silent: true,
zlevel: -1,
aspectScale: 0.86,
layoutCenter: ['50%', '52%'],
layoutSize: '100%',
itemStyle: {
borderColor:'rgba(223, 245, 255, 1)',
shadowColor: 'RGBA(50, 183, 146, 1)',
areaColor: 'rgba(4, 162, 122, 0.2)',
borderWidth: 5,
shadowOffsetY: 2,
shadowBlur: 50,
},
},
// 第四层:加深颜色中间层
{
type: 'map',
map: 'chinaItem',
zlevel: -2,
zoom: 1,
aspectScale: 0.86,
layoutCenter: ['50%', '54%'],
layoutSize: '100%',
roam: false,
silent: true,
itemStyle: {
areaColor: 'rgba(4, 162, 122, 0.8)',
borderColor: 'rgba(223, 245, 255, 0.5)',
shadowOffsetY: 0,
shadowColor: 'RGBA(37, 182, 144, 1)',
borderWidth: 1,
},
regions: [
{
name: "南海诸岛",
itemStyle: {
borderColor: 'RGBA(37, 182, 144, 1)',
shadowColor: 'RGBA(37, 182, 144, 1)',
areaColor: 'RGBA(27, 96, 78, 1)',
borderWidth: 1,
normal: {
opacity: 0,
label: {
show: false,
color: "RGBA(255,255,255,1)",
fontSize: 12,
},
},
},
},
],
},
// 第五层:底层
{
type: 'map',
map: 'chinaItem',
zlevel: -3,
zoom: 1,
aspectScale: 0.86,
layoutCenter: ['50%', '57%'],
layoutSize: '100%',
roam: false,
silent: true,
itemStyle: {
borderWidth: 5,
borderColor: 'RGBA(30, 142, 113, 0.8)',
shadowColor: 'RGBA(28, 91, 74, 0.8)',
shadowOffsetY: 0,
shadowBlur: 10,
areaColor: 'rgba(5,21,35,0.1)',
},
regions: [
{
name: "南海诸岛",
itemStyle: {
areaColor: "rgba(0, 10, 52, 1)",
borderColor: "rgba(0, 10, 52, 1)",
normal: {
opacity: 0,
label: {
show: false,
color: "RGBA(255,255,255,1)",
fontSize: 12,
},
},
},
},
],
},
],
series: [
// 主要线条
{
type: 'lines',
coordinateSystem: 'geo',
zlevel: 10,
symbol: ['none', 'arrow'],
symbolSize: 10,
effect: {
color: 'rgba(70, 219, 242, 1)',
show: true,
period: 6,
trailLength: 0,
symbolSize: 5
},
lineStyle: {
normal: {
color: 'rgba(255, 255, 255, 0.9)',
width: 1,
curveness: 0.2
}
},
data: linesData
},
// 终点标记
{
type: 'effectScatter',
coordinateSystem: 'geo',
zlevel: 11,
silent: false,
select: {
disabled: false
},
rippleEffect: {
period: 4,
brushType: 'stroke',
scale: 4
},
symbolSize: 10,
itemStyle: {
normal: {
color: 'RGBA(255, 166, 1, 1)'
},
emphasis: {
show: true,
color: "#1AD9FF",
}
},
data: scatterData
}
]
};
this.myChart.setOption(option);
this.myChart.on('click', this.onChartClick);
this.myChart.on('mouseover', this.onChartMouseover);
this.myChart.on('mouseout', this.onChartMouseout);
}
}
}
</script>
<style scoped lang="scss">
</style>
调整相关参数效果如图

总结
这个项目展示了如何使用 ECharts 创建复杂的 3D 地图可视化。通过多层叠加、精心设计的视觉效果和交互功能,可以将枯燥的地理数据转化为引人入胜的可视化作品。开发者可以根据这个模板,进一步扩展功能,如添加数据筛选、时间轴动画等高级特性。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)