说实话,刚开始接触 ECharts 地图的时候,我整个人是懵的。明明官方文档写得挺清楚,但真到自己手里,要么省份名字对不上,要么边界乱成一团麻,搞得我差点想把地图功能直接砍掉。后来折腾了半个月,踩过无数坑,才算是真正搞明白了 GeoJSON 到底是怎么在 ECharts 里“活”起来的。今天就把这些血泪经验整理出来,咱们不整那些虚头巴脑的理论,直接上干货,保证你看完就能动手画出属于自己的地图。
先说个最基础的问题:为什么你的地图总是缺胳膊少腿?或者某个省根本显示不出来?99% 的情况都是 GeoJSON 数据源选错了。现在网上流传着各种版本的行政区划数据,有 2015 年的,有 2019 年的,还有 2023 年刚更新过的。ECharts 官方其实已经内置了一些常见地图数据,但如果你需要精确到区县级别,或者想做一些特殊的边界定制,那就必须自己搞 GeoJSON 了。
GeoJSON 本质上就是一个 JSON 格式的地理数据文件,它包含了点、线、面这些地理要素。对于咱们做地图来说,最重要的是面数据,也就是各个省份的边界坐标。你可以去阿里云的 DataV.GeoAtlas 网站下载,那里有各种粒度的地图数据,从全国到省到市再到区县,应有尽有。下载的时候注意选择“省”级别的边界数据,格式选 GeoJSON,这样兼容性最好。
拿到数据后,第一件事不是急着放进 ECharts,而是先打开看看里面长什么样。一个标准的 GeoJSON 文件大概结构是这样的:有一个根对象叫 “type”,值是 “FeatureCollection”,里面有个 “features” 数组,每个元素代表一个省份,每个元素又有 “type”、”properties” 和 “geometry” 三个字段。”properties” 里面通常会有”name”这个键,存的省名,比如”浙江省”、”广东省”这些。”geometry” 里面存的就是边界坐标了,是个 “Polygon” 类型,里面是一堆经纬度坐标点。
这里有个特别容易踩的坑:ECharts 识别省份靠的是 “properties.name” 里的值。但是不同来源的 GeoJSON 里省名的写法可能不一样。有的写”浙江省”,有的写”浙江”,还有的可能带了空格或者特殊字符。ECharts 内置的数据用的是简写形式,比如”浙江”而不是”浙江省”。所以如果你下载的数据是全称,而你的 series data 里写的是简称,那地图就匹配不上,那个省就显示不出来。解决办法很简单,打开 GeoJSON 文件,把所有 “name” 的值检查一下,看看是不是需要统一格式。或者更聪明的做法是,直接用 ECharts 官方提供的示例数据里的 GeoJSON,那个跟它内置的地图系统是完美匹配的。
说回实战。假设你已经准备好了 GeoJSON 文件,比如叫 china.json,放在了你的项目 public 目录下。接下来咱们写代码。第一步是引入 ECharts,这个不用多说,CDN 或者 npm 安装都行。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>自定义 ECharts 地图示例</title>
<style>
#map-container {
width: 100%;
height: 600px;
}
</style>
</head>
<body>
<div id="map-container"></div>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
<script src="main.js"></script>
</body>
</html>
然后是 JS 文件,这里就是重头戏了。
// main.js
const chartDom = document.getElementById('map-container');
const myChart = echarts.init(chartDom);
// 加载 GeoJSON 数据
fetch('china.json')
.then(response => response.json())
.then(geoJson => {
// 注册地图,第一个参数是地图名称,第二个是 GeoJSON 数据
echarts.registerMap('china', geoJson);
const option = {
tooltip: {
trigger: 'item',
formatter: '{b}' // 鼠标悬停显示省份名称
},
visualMap: {
min: 0,
max: 1000,
left: 'left',
top: 'bottom',
text: ['高', '低'],
calculable: true,
inRange: {
color: ['#eff3ff', '#c6dbef', '#9ecae1', '#6baed6', '#4292c6', '#2171b5']
}
},
series: [
{
name: '中国地图',
type: 'map',
map: 'china', // 这里对应 registerMap 的第一个参数
roam: true, // 允许缩放和平移
zoom: 1.2,
label: {
show: true,
color: '#333',
fontSize: 10
},
emphasis: {
label: {
show: true,
fontSize: 14,
fontWeight: 'bold'
},
itemStyle: {
areaColor: '#f4d03f'
}
},
// 假设这是你的业务数据
data: [
{ name: '北京', value: 850 },
{ name: '天津', value: 620 },
{ name: '河北', value: 730 },
{ name: '山西', value: 540 },
{ name: '内蒙古', value: 320 },
{ name: '辽宁', value: 680 },
{ name: '吉林', value: 410 },
{ name: '黑龙江', value: 390 },
{ name: '上海', value: 920 },
{ name: '江苏', value: 880 },
{ name: '浙江', value: 910 },
{ name: '安徽', value: 560 },
{ name: '福建', value: 720 },
{ name: '江西', value: 480 },
{ name: '山东', value: 800 },
{ name: '河南', value: 650 },
{ name: '湖北', value: 610 },
{ name: '湖南', value: 590 },
{ name: '广东', value: 950 },
{ name: '广西', value: 430 },
{ name: '海南', value: 380 },
{ name: '重庆', value: 570 },
{ name: '四川', value: 640 },
{ name: '贵州', value: 350 },
{ name: '云南', value: 420 },
{ name: '西藏', value: 120 },
{ name: '陕西', value: 510 },
{ name: '甘肃', value: 280 },
{ name: '青海', value: 180 },
{ name: '宁夏', value: 260 },
{ name: '新疆', value: 310 },
{ name: '香港', value: 890 },
{ name: '澳门', value: 780 },
{ name: '台湾', value: 750 }
]
}
]
};
myChart.setOption(option);
})
.catch(error => {
console.error('加载地图数据失败:', error);
// 如果加载失败,可以给个提示或者降级处理
myChart.setOption({
series: [{
type: 'scatter',
coordinateSystem: 'geo',
data: []
}]
});
});
// 响应式调整
window.addEventListener('resize', () => {
myChart.resize();
});
上面这段代码是个最基础的骨架,能让你把地图渲染出来。但光会画还不够,真正有意思的是怎么让它变得好看、实用。比如说,你可能希望某些省份不显示 label,或者想给某些特定的省加不同的颜色。这就要用到 series 里的 itemStyle 配置了。
itemStyle: {
borderColor: '#fff',
borderWidth: 1,
areaColor: '#eee'
},
emphasis: {
itemStyle: {
areaColor: '#ffeb3b'
},
label: {
show: true,
color: '#000'
}
}
这里 borderColor 是边界线的颜色,borderWidth 是边界线宽度。如果多个省份挤在一起,边界线太粗会显得特别乱,建议设为 0.5 到 1 之间。areaColor 是填充颜色,默认灰色可能不太好看,你可以换成跟你的主题色搭配的渐变色,不过 ECharts 的地图 series 本身不支持渐变填充,得用 visualMap 或者自定义颜色数组来实现。
说到 visualMap,这是给地图染色最方便的工具。它可以根据 data 里的 value 值自动给不同范围的省份分配不同颜色。上面的例子里我用的是蓝色系渐变,从浅蓝到深蓝,代表数值从低到高。你可以根据需要换成红色系、绿色系,或者任何你觉得好看的配色。visualMap 还有几个重要属性值得注意:calculable 设为 true 会让下面出现一个可拖动的滑块,用户可以自己调整范围;inRange 里的 color 数组就是颜色阶梯,数组元素越多,颜色过渡越平滑。
有时候你可能不需要热力图效果,而是想给某些特定省份单独指定颜色。这时候就不能用 visualMap 了,得在 series.data 里给每个对象加 itemStyle 字段。
data: [
{ name: '北京', value: 850, itemStyle: { areaColor: '#e74c3c' } },
{ name: '上海', value: 920, itemStyle: { areaColor: '#e74c3c' } },
{ name: '广东', value: 950, itemStyle: { areaColor: '#e74c3c' } },
// 其他省份不指定 itemStyle,用默认颜色
{ name: '浙江', value: 910 },
{ name: '江苏', value: 880 },
...
]
这样北京、上海、广东这三个直辖市和经济大省就会显示成红色,其他省份保持默认颜色,视觉上立刻就有了重点突出的感觉。
再深入一点,如果你发现某个省的边界不太对,比如广东的海岸线画得歪七扭八,或者台湾省的位置跑偏了,这时候就得直接去改 GeoJSON 数据了。用 VS Code 打开 json 文件,搜索”广东”或者”台湾”,找到对应的 geometry 字段。Polygon 里的坐标数组是一系列 [经度, 纬度] 的数组,第一个数组代表外边界,后面如果有多个数组,代表里面的空洞(比如岛上的湖泊)。你要改边界的话,就得一个个调整这些坐标点。这个过程比较枯燥,建议用一些在线的 GeoJSON 编辑器,比如 geojson.io,它可以可视化地编辑坐标,比纯手写靠谱多了。
还有一个很常见的需求:只显示部分省份,或者隐藏某些省份。这个可以通过 series.data 来控制。如果你只传了部分省份的 data,那么没传的省份就不会显示高亮,但仍然会显示轮廓。如果你想彻底隐藏某个省,可以在 series 里加一个 regions 配置。
series: [{
type: 'map',
map: 'china',
regions: [
{
name: '西藏',
silent: true,
itemStyle: {
areaColor: '#ccc',
borderColor: '#fff'
},
label: {
show: false
}
}
],
// ... 其他配置
}]
这里我把西藏设置成了灰色、不可点击(silent: true),而且不显示标签。这样西藏就变成了一种”背景”状态,不会抢眼球。
关于省份名称的显示,有时候你会遇到一个问题:省份名字太长,挤在一起看不清楚。比如”内蒙古自治区”六个字挤在那么小的一个省里,根本看不清。解决办法有两个:一是把 label 的 fontSize 调小,二是用 label.layout 配置,或者干脆只显示简称。我比较喜欢第二种,在 data 里给每个省份配一个简称。
data: [
{ name: '北京', value: 850, abbreviation: '京' },
{ name: '内蒙古', value: 320, abbreviation: '蒙' },
{ name: '新疆', value: 310, abbreviation: '新' },
// ...
]
然后在 label.formatter 里用简称。
label: {
show: true,
formatter: (params) => {
// params.data 就是 data 里的当前对象
return params.data.abbreviation || params.name;
}
}
这样地图上显示的就是单字简称,清爽多了。不过对于面积比较大的省,比如新疆、西藏,单字可能还是显得太空,可以适当加大字号或者加个背景色。
说到背景色,有时候你不想让地图背景是白色的,而是想要透明或者深色主题。这可以在 series 的 itemStyle 里设置 areaColor,或者直接给整个 chartDom 设置 CSS 背景。
body {
background-color: #1a1a2e;
}
#map-container {
background: transparent;
}
itemStyle: {
areaColor: 'rgba(255,255,255,0.1)',
borderColor: 'rgba(255,255,255,0.3)'
}
配合深色主题的 visualMap 颜色,整体视觉效果会非常酷炫,适合放在大屏展示里。
除了静态地图,ECharts 还支持动态效果。比如数据更新时,地图颜色可以平滑过渡。你只需要在数据变化时调用 setOption 更新 data,ECharts 会自动做过渡动画。还有缩放动画,roam: true 开启后,用户拖拽和滚轮缩放都会有平滑效果。
如果你想要更炫酷的效果,可以考虑加点散点图或者流向图。散点图可以标注重城市位置,流向图可以展示人口或物资流动。这些都需要配合 geo 组件使用,而不是 map 组件。
// 使用 geo 组件实现散点 + 流向效果
const option = {
geo: {
map: 'china',
roam: true,
zoom: 1.2,
label: { show: false },
itemStyle: {
areaColor: '#323c48',
borderColor: '#111'
},
emphasis: {
itemStyle: { areaColor: '#2a333d' }
}
},
series: [
{
type: 'scatter',
coordinateSystem: 'geo',
data: convertData(scatterData), // 需要把城市名转成经纬度
symbolSize: 10,
itemStyle: { color: '#f4e91d' }
},
{
type: 'lines',
coordinateSystem: 'geo',
data: convertData(linesData), // 需要把起点终点城市名转成经纬度
polyline: false,
lineStyle: {
color: '#4169e1',
width: 2,
curveness: 0.2
},
effect: {
show: true,
period: 4,
trailLength: 0.1,
symbol: 'arrow',
symbolSize: 5
}
}
]
};
这里用到了 convertData 函数,它的作用是把城市名称转换成经纬度坐标。你可以自己写一个映射表,也可以用现有的库。ECharts 官方示例里有很多现成的数据转换工具函数可以参考。
最后聊聊性能问题。如果你的 GeoJSON 数据量特别大,比如精确到街道级别,渲染可能会卡顿。这时候有两个优化思路:一是简化 GeoJSON,用 Douglas-Peucker 算法简化边界坐标,去掉一些不必要的点;二是分片加载,先加载全国轮廓,用户放大到某个省后再加载该省的详细边界。不过一般情况下,省级地图的性能完全够用,不用太担心。
再分享一个小技巧:如果你不想用 fetch 加载外部 JSON 文件,可以直接把 GeoJSON 内容内联到 JS 里。虽然会让 JS 文件变大,但避免了跨域问题和加载失败的风险,适合小型项目或者演示 demo。
const chinaGeoJson = {
"type": "FeatureCollection",
"features": [
// ... 直接粘贴 GeoJSON 内容
]
};
echarts.registerMap('china', chinaGeoJson);
好了,说了这么多,核心就三点:选对 GeoJSON 数据源、搞清楚名字匹配规则、灵活运用 ECharts 的配置项。只要你把这三点搞明白了,自定义地图就没啥难的了。地图这东西,看着复杂,其实拆开来看,就是一些坐标点和 JSON 配置的组合。多动手试试,踩几个坑,自然就熟练了。希望这篇分享能帮到你,要是还有具体问题,欢迎继续交流。
