上周有个做文旅数据的客户问我:“Agnes,我想在网页上画一个我们县区的旅游地图,还要能点击跳转,能行吗?”我笑了,当然行,ECharts 早就把路铺好了,咱们今天就一步一步把这条路走通,顺便把那些坑都填上。
先说个实话,很多人第一次折腾 ECharts 自定义地图时,卡在两个地方:一是 GeoJSON 的数据从哪来、格式对不对;二是注册完之后,为什么我 hover 上去没反应?或者点击事件完全失效?别急,这篇文章就是为了解决这些“明明代码都对,但就是跑不起来”的头疼问题。
一、GeoJSON:地图的“灵魂”,你得先搞懂它
在 ECharts 里画自定义地图,核心不是 ECharts,而是 GeoJSON。你可以把它理解为“用代码描述地图边界”的一种标准格式。ECharts 自己没内置那么多区县、街道的地图数据,所以它提供了一套机制让你自己注册。
GeoJSON 文件本质上是一个 JSON 对象,但它有严格的规范。最关键的字段是 features,这是一个数组,每个元素代表地图上的一个区域(比如一个县、一个街道)。每个 feature 里又包含:
type:固定是"Feature"。properties:元数据,比如这个区域的名称name、代码id等。ECharts 默认会用properties.name来匹配数据里的地名,所以name 字段必须准确,这是后续交互的地基。geometry:几何形状,里面最关键的是coordinates,它是二维或多边形坐标数组,定义了区域的边界点。
举个简单的例子,假设我们要画一个极简的“测试县”地图,它的 GeoJSON 长这样:
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"name": "青田县"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[120.05, 28.45],
[120.15, 28.45],
[120.15, 28.55],
[120.05, 28.55],
[120.05, 28.45]
]
]
}
}
]
}
你看,就是一个简单的矩形多边形,四个点围成一圈,第一个点和最后一个点重合(闭合)。name 是“青田县”,这就是我们后面高亮、点击时要用的名字。
这里有个新手容易踩的坑:坐标顺序。GeoJSON 的坐标是 [经度, 纬度],也就是 [x, y],别搞反成 [纬度, 经度] 了,否则地图会画到南太平洋去。另外,多边形必须是逆时针或顺时针顺序排列,不能自相交,否则渲染会乱。
怎么找正规的 GeoJSON?国内的话,推荐使用“阿里云 DataV 数据可视化平台”提供的中国区县 GeoJSON 数据,或者“地图慧”等网站下载的省级、市级数据。下载的 .json 文件直接就能用,但最好用 VS Code 或者在线工具(比如 geojson.io)打开检查一下结构,确认 features 数组和 properties.name 存在。
二、注册地图:ECharts 的“点名册”
有了 GeoJSON,接下来就是把它“喂”给 ECharts。ECharts 提供了一个全局方法 registerMap,语法很简单:
echarts.registerMap('mapName', geoJsonData);
mapName:你给这张地图起的名字,后面在 series 里要用。geoJsonData:就是上面那份 GeoJSON 数据,可以是对象,也可以是从服务器异步加载回来的数据。
假设我们下载了“浙江省”的 GeoJSON,保存在 zhejiang.json 里,我们可以这样注册:
// 方式一:直接内联数据(适合数据量小的测试)
const zhejiangGeoJSON = { /* 粘贴 GeoJSON 内容 */ };
echarts.registerMap('zhejiang', zhejiangGeoJSON);
// 方式二:从服务器异步加载(推荐,生产环境必备)
$.get('data/zhejiang.json', function (geoJson) {
echarts.registerMap('zhejiang', geoJson);
// 注册完成后再初始化图表
initChart();
});
注意,注册必须在初始化图表之前完成。如果你在 initChart() 里才去 get 数据,而此时图表已经渲染了,那 series 里的 map: 'zhejiang' 就找不到地图,会报错或者不显示。
有些同学会问:能不能注册多个地图?当然可以,比如同时注册‘浙江’和‘北京’,名字不同即可。但要注意,同一个 mapName 只能注册一次,重复注册会覆盖之前的。
三、基础地图绘制:让区域显示出来
注册完地图,我们可以开始画图了。在 ECharts 中,自定义地图是通过 series 里的 type: 'map' 来实现的。
下面是一个完整的、可运行的 HTML 示例代码,包含注册、绘图、基础样式:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>自定义地图示例</title>
<!-- 引入 ECharts -->
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
<!-- 引入 jQuery 用于加载 JSON(也可以用 fetch) -->
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
<style>
#mapContainer {
width: 100%;
height: 600px;
}
</style>
</head>
<body>
<div id="mapContainer"></div>
<script>
// 1. 初始化图表实例
const myChart = echarts.init(document.getElementById('mapContainer'));
// 2. 从服务器加载浙江省 GeoJSON
$.get('https://geo.datav.aliyun.com/areas_v3/bound/330000_full.json', function (geoJson) {
// 3. 注册地图,名字为 'zhejiang'
echarts.registerMap('zhejiang', geoJson);
// 4. 定义图表配置
const option = {
title: {
text: '浙江省地图示例',
left: 'center'
},
// 提示框,鼠标悬停时显示信息
tooltip: {
trigger: 'item',
formatter: '{b}' // {b} 代表 properties.name
},
// 视觉映射,根据数值大小改变颜色深浅
visualMap: {
min: 0,
max: 1000,
left: 'left',
top: 'bottom',
text: ['高', '低'],
calculable: true,
inRange: {
color: ['#ebedf0', '#c6e48b', '#7bc96f', '#239a3b', '#196127']
}
},
series: [
{
name: '浙江省',
type: 'map',
map: 'zhejiang', // 对应 registerMap 的第一个参数
roam: true, // 允许缩放和平移
zoom: 1.2,
label: {
show: true, // 显示区县名称
fontSize: 10
},
// 给每个区域赋上数值,用于 visualMap
data: [
{ name: '杭州市', value: 800 },
{ name: '宁波市', value: 750 },
{ name: '温州市', value: 600 },
{ name: '嘉兴市', value: 500 },
{ name: '湖州市', value: 450 },
{ name: '绍兴市', value: 550 },
{ name: '金华市', value: 400 },
{ name: '衢州市', value: 350 },
{ name: '舟山市', value: 300 },
{ name: '台州市', value: 520 },
{ name: '丽水市', value: 280 }
],
// 高亮样式
emphasis: {
label: {
show: true,
fontSize: 12,
fontWeight: 'bold'
},
itemStyle: {
areaColor: '#f6cca1',
shadowBlur: 10,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
};
// 5. 设置配置项
myChart.setOption(option);
});
// 响应窗口大小变化
window.addEventListener('resize', function () {
myChart.resize();
});
</script>
</body>
</html>
这段代码跑起来,你就能看到一个可缩放、可平移的浙江省地图,每个区县都有颜色深浅,鼠标悬停会显示区县名称。
关键点解析:
map: 'zhejiang'必须和registerMap的第一个参数一致。roam: true开启交互缩放和平移,体验更好。data数组里的name必须和 GeoJSON 里properties.name完全一致,包括空格和标点,否则数值匹配不上,颜色就不会变化。emphasis对象定义了高亮状态下的样式,包括文字变大、背景色变浅、加阴影等,这是实现“高亮交互”的核心配置。
四、区域高亮交互:不止是变色
光有颜色变化还不够,咱们要让地图“活”起来。ECharts 提供了 emphasis 状态,但更强大的是通过 dispatchAction 来编程控制高亮,或者通过事件监听实现点击、悬停等交互。
1. 鼠标悬停高亮(默认行为)
上面的代码里已经展示了 emphasis 的配置,这已经实现了基本的悬停高亮。当鼠标移到某个区域上,该区域会变为 itemStyle.areaColor 指定的颜色,并显示阴影。
2. 点击区域,弹出详情或跳转
很多时候,点击地图区域需要跳转到详情页,或者弹出一个浮层显示更多信息。这时候要用到 click 事件。
// 监听点击事件
myChart.on('click', function (params) {
console.log('点击了区域:', params.name);
// 跳转到详情页
window.location.href = '/detail/' + params.name;
// 或者弹出提示
alert('你点击了:' + params.name + ',数据:' + params.value);
});
params 对象里包含了点击区域的详细信息:name(区域名)、value(data 里的数值)、seriesName、dataType 等。
3. 编程控制高亮:用 dispatchAction
有时候你需要通过代码来高亮某个区域,比如初始化时高亮第一个区域,或者根据业务逻辑高亮特定区域。这时用 dispatchAction:
// 高亮名为 '杭州市' 的区域
myChart.dispatchAction({
type: 'highlight',
seriesIndex: 0, // 第几个 series
name: '杭州市'
});
// 取消高亮
myChart.dispatchAction({
type: 'downplay',
seriesIndex: 0,
name: '杭州市'
});
seriesIndex 是 series 数组的下标,从 0 开始。name 必须和 GeoJSON 里的 properties.name 一致。
4. 自定义高亮样式:通过 data 配置
如果不同区域需要不同的高亮颜色,可以在 data 数组里为每个 item 单独设置 itemStyle:
data: [
{
name: '杭州市',
value: 800,
itemStyle: {
areaColor: '#ffeb3b' // 高亮时为黄色
}
},
{
name: '宁波市',
value: 750,
itemStyle: {
areaColor: '#4caf50' // 高亮时为绿色
}
}
]
这样,当鼠标悬停到杭州市时,它会变成黄色,而不是全局的 emphasis.itemStyle.areaColor。
五、常见问题与避坑指南
在实际项目中,你肯定会遇到一些棘手的问题。我帮你总结了几个最高频的坑:
坑1:地图不显示,报错“map not found”
原因:registerMap 还没完成,或者 series.map 的名字和 registerMap 的第一个参数不一致。
解决:
- 检查
registerMap是否在setOption之前调用。 - 检查名字拼写,包括大小写。
- 打开浏览器控制台,看是否有网络请求失败(GeoJSON 加载失败)。
坑2:悬停不显示提示框,或者提示框内容为空
原因:tooltip.formatter 配置错误,或者 data 里的 name 和 GeoJSON 里的 name 不匹配。
解决:
- 在
tooltip.formatter里用{b}表示 name,{c}表示 value。 - 用
console.log打印出 GeoJSON 里的features[0].properties.name,和data里的name逐个对比,确保完全一致。
坑3:点击事件不触发
原因:事件被其他元素遮挡,或者 myChart 实例没有正确绑定。
解决:
- 确保
myChart.on('click', ...)在setOption之后调用。 - 检查地图容器是否有
z-index或者其他元素盖住了。
坑4:GeoJSON 数据太大,加载慢
原因:省级或市级 GeoJSON 文件可能几 MB,浏览器加载和 ECharts 渲染都需要时间。
解决:
- 使用 CDN 或本地静态资源服务器,开启 gzip 压缩。
- 考虑后端裁剪 GeoJSON,只加载当前需要的区域。
- 使用 ECharts 的
lazyUpdate选项,延迟更新以提升性能。
坑5:多边形坐标顺序错误,渲染出“蝴蝶结”形状
原因:GeoJSON 的 coordinates 数组点顺序不对,导致多边形自相交。
解决:
- 使用在线工具如 geojson.io 打开 GeoJSON,查看是否有异常形状。
- 用拓扑软件(如 QGIS)检查并修复坐标顺序。
- 确保每个多边形是闭合的,即第一个点和最后一个点相同。
六、进阶技巧:让地图更炫酷
基础搞定了,咱们再加点料,让地图看起来更专业。
1. 添加图例和视觉映射
上面的代码里已经用了 visualMap,它可以根据 data.value 的值自动给区域着色。你可以自定义颜色范围、分段等,让它更符合你的品牌色。
2. 添加散点图或线图
如果要在地图上标记具体位置,可以叠加一个 scatter 或 effectScatter series。这需要 geo 坐标,而不是经纬度。ECharts 提供了 convertToPixel 和 convertFromPixel 方法,可以把经纬度转换成地图上的像素坐标,或者反过来。
// 获取某个经纬度在地图上的像素坐标
const pointInPixel = myChart.convertToPixel({ geoIndex: 0 }, [120.15, 28.45]);
// 像素坐标转回经纬度
const pointInGeo = myChart.convertFromPixel({ geoIndex: 0 }, [100, 200]);
3. 动画效果
给区域添加入场动画,或者点击后的脉冲动画。在 series 里配置 animation: true,或者用 dispatchAction 触发动画。
4. 多地图切换
如果项目需要同时显示多个省份,可以注册多个地图,通过下拉框切换当前显示的地图。注意每次切换都要重新 registerMap 和 setOption。
七、总结
从 GeoJSON 的获取与解析,到 ECharts 的 registerMap 注册,再到 series 配置和交互事件监听,整个流程其实就四步:
- 找数据:去 DataV 或类似平台下载目标区域的 GeoJSON。
- 注册地图:用
echarts.registerMap(name, geoData)注册。 - 配置 series:在
series里用type: 'map'和map: 'name'调用地图。 - 添加交互:用
emphasis配置高亮样式,用on('click')监听点击,用dispatchAction编程控制高亮。
只要把这四步走通,自定义地图就不是难题了。记住,名字匹配是关键,坐标顺序是基础,事件监听是灵魂。
希望这篇教程能帮你少走弯路。如果你在实战中遇到其他奇怪的问题,欢迎随时来问,咱们一起解决。地图世界,大有可为。
