说实话,做中国地图可视化这块,坑是真多。我见过太多人(包括曾经的我自己)在 GeoJSON 坐标转换上栽跟头,最后地图上省份位置对不上,或者直辖市小得像个标点符号,简直让人头秃。但别慌,今天我们就把这些问题从头到尾捋清楚,不仅解决坐标错位,还要搞定那些让人头疼的动态加载场景。
一、 为什么你的地图总是“歪”的?
在动手写代码之前,咱们得先搞明白一个核心概念:坐标系。
中国地图坐标错位,99% 的原因不是 ECharts 的锅,而是数据来源的坐标系问题。常见的几种坐标系你要有个基本认知:
- WGS84 (EPSG:4326):这是 GPS 标准坐标系,也就是大家常说的“原始坐标”。如果你直接从某些国际地理数据源下载 GeoJSON,大概率是这个。
- GCJ02 (火星坐标系):这是高德、腾讯地图使用的加密坐标系。它是在 WGS84 基础上做了非线性偏移处理,为了国家安全嘛,理解万岁。
- BD09 (百度坐标系):百度又在 GCJ02 的基础上再做了一次变换。
ECharts 自带的中国地图数据(通常在 china.js 或 geo.json 里)默认是符合 GCJ02 或者经过特定投影处理后的坐标系,可以直接使用。但当你使用自定义 GeoJSON 时,如果数据来源不明,直接丢进 ECharts 就会发生严重的位置偏移——比如黑龙江跑到了新疆旁边,或者广东省缩成一点点。
解决方案核心:确保你的 GeoJSON 坐标系与 ECharts 的投影设置匹配。如果数据是 WGS84,你需要转换;如果是 GCJ02,通常可以直接用,但需要选择合适的投影。
二、 准备工作:获取纯净的 GeoJSON
别再去乱搜什么“中国地图 JSON”,很多都是残缺不全或者坐标系混乱的。我推荐两个靠谱的数据源:
- 阿里云 DataV.GeoAtlas:这个网站提供了非常标准的中国行政区划 GeoJSON,按省份切分得很干净,而且坐标系通常是适合 web 展示的投影。
- GitHub 上的开源项目:比如
echarts-map相关的仓库,里面有很多经过验证的china.json。
这里我以获取省级 GeoJSON 为例。你可以访问阿里云 DataV 的页面,找到“中国省级行政区边界数据”,下载 china-provinces.json 或者单个省份的 JSON 文件。
注意:下载后的 GeoJSON 文件,里面通常包含很多字段,我们只需要关心 geometry(几何形状)和 properties(属性,通常包含 name 省名)。
三、 基础搭建:让地图先跑起来
假设你已经有了一个 china.json 文件,放在项目的 public 或 static 目录下。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>自定义中国地图</title>
<!-- 引入 ECharts -->
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
<style>
#map-container {
width: 100%;
height: 800px;
}
</style>
</head>
<body>
<div id="map-container"></div>
<script>
// 初始化图表
const chartDom = document.getElementById('map-container');
const myChart = echarts.init(chartDom);
// 注册地图数据 - 关键步骤!
// 这里我们直接使用 ECharts 内置的示例数据格式,
// 实际使用时需要读取你下载的 GeoJSON
$.get('china.json', function (geoJson) {
echarts.registerMap('china', geoJson);
const option = {
tooltip: {
trigger: 'item',
formatter: '{b}' // 显示省份名称
},
visualMap: {
min: 0,
max: 1000,
left: 'left',
top: 'bottom',
text: ['高', '低'],
inRange: {
color: ['#e0ffff', '#006edd'] // 颜色渐变
},
calculable: true
},
series: [
{
name: '中国各省数据',
type: 'map',
map: 'china', // 对应 registerMap 的名字
roam: true, // 开启鼠标缩放和平移
zoom: 1.2,
label: {
show: true,
fontSize: 10
},
data: [
{name: '北京', value: 120},
{name: '上海', value: 900},
{name: '广东', value: 500},
{name: '西藏', value: 10}
// ... 更多数据
]
}
]
};
myChart.setOption(option);
});
</script>
<!-- 引入 jQuery 用于简单的 AJAX 请求,实际项目中可以用 fetch -->
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
</body>
</html>
这段代码跑起来,你应该能看到一张标准的中国地图。但如果数据加载慢,或者用户网络不好,页面就会卡住。这就引出了下一个痛点。
四、 解决坐标错位:从 WGS84 到 Web Mercator
如果你下载的 GeoJSON 是直接来自 WGS84 源(比如某些海外地理数据库),你发现地图虽然形状是对的,但位置整体偏移了,或者被压扁/拉伸了。这时候需要处理投影。
ECharts 5.0+ 版本开始,对地图投影的支持更好了。如果你的 GeoJSON 是标准的经纬度数据(WGS84),你可以通过设置 roam 和合适的 center 来微调,但更彻底的方法是在代码中进行坐标转换,或者确保 GeoJSON 本身是 Web Mercator 投影。
手动转换坐标的 JS 实现:
/**
* 简单的 WGS84 转 Web Mercator (EPSG:3857)
* 用于处理那些坐标不对的 GeoJSON
*/
function wgs84ToWebMercator(coordinates) {
const mercatorCoordinates = [];
const pi = 3.141592653589793;
const a = 6378137; // 地球半径
function transformLonLat(lon, lat) {
let x = lon * pi / 180.0;
let y = lat * pi / 180.0;
y = 1.0 / 2.0 * Math.log((1.0 + Math.sin(y)) / (1.0 - Math.sin(y)));
x = a * x;
y = a * y;
return [x, y];
}
// 递归处理嵌套的坐标数组
function processCoords(coords) {
if (typeof coords[0] === 'number') {
return transformLonLat(coords[0], coords[1]);
}
return coords.map(processCoords);
}
// 复制并修改 GeoJSON
const modifiedGeoJson = JSON.parse(JSON.stringify(geoJson));
// 遍历所有 feature 的 geometry
modifiedGeoJson.features.forEach(feature => {
if (feature.geometry) {
if (feature.geometry.type === 'Point') {
feature.geometry.coordinates = processCoords(feature.geometry.coordinates);
} else if (feature.geometry.type === 'MultiPoint' ||
feature.geometry.type === 'LineString' ||
feature.geometry.type === 'MultiLineString' ||
feature.geometry.type === 'Polygon' ||
feature.geometry.type === 'MultiPolygon') {
feature.geometry.coordinates = processCoords(feature.geometry.coordinates);
}
}
});
return modifiedGeoJson;
}
但是! 在实际生产环境中,我更建议预处理。不要在浏览器里实时转换,因为 GeoJSON 数据量可能很大,会卡顿。最好的做法是:
- 使用工具(如 QGIS、在线转换网站)将 GeoJSON 转换为 Web Mercator 投影后再上传。
- 或者,使用专门的库如
proj4js进行前端转换,但务必加上防抖和异步加载。
五、 动态加载:解决大数据量下的性能瓶颈
当你需要加载市级甚至区级 GeoJSON 时,文件可能高达几 MB。如果一次性全部加载,页面会白屏很久。这时候“动态加载”就派上用场了。
场景:用户先看全国地图,点击某个省,才加载该省的详细地图。
1. 使用 ECharts 的 dispatchAction 配合动态数据源
我们不能简单地 setOption,因为重新注册地图会覆盖之前的状态。更好的方式是按需加载并注册,同时保留现有的交互状态。
// 假设我们有一个省份数据映射表
const provinceGeoJSONMap = {
'广东省': 'guangdong.json',
'湖南省': 'hunan.json',
'四川省': 'sichuan.json'
// ...
};
// 点击省份事件
myChart.on('click', function (params) {
const provinceName = params.name;
// 检查是否已经加载过该省数据
if (!echarts.getMap('detail-' + provinceName)) {
// 动态加载
$.get(provinceGeoJSONMap[provinceName], function (geoJson) {
// 注册一个以 "detail-" 开头的新地图,避免冲突
echarts.registerMap('detail-' + provinceName, geoJson);
// 更新配置,切换到详细地图
myChart.setOption({
series: [{
type: 'map',
map: 'detail-' + provinceName,
zoom: 1.1,
center: ['113.28', '23.13'], // 示例:广州坐标,需要根据实际 GeoJSON 调整
roam: true,
// 这里的 data 需要根据新地图的行政区域重新获取
data: getProvinceData(provinceName)
}],
tooltip: {
formatter: '{b}'
}
}, true); // 第三个参数 true 表示不合并,直接替换 series
});
} else {
// 已经加载过,直接切换
myChart.setOption({
series: [{
map: 'detail-' + provinceName,
roam: true
}]
}, true);
}
});
2. 处理直辖市和特殊形状
你可能会发现,像北京、天津、上海这种直辖市,在 GeoJSON 里可能是一个整体的 Polygon,也可能由多个 Polygon 组成(如果有飞地)。
常见问题:点击后,子区域(区/县)没有高亮反馈。
解决方案:确保 GeoJSON 的 properties.name 包含准确的行政区划名称,并且在 data 中也要匹配这个名称。如果 GeoJSON 里是“北京市”,而你的数据里写的是“北京”,那就匹配不上。
六、 实战技巧:让地图更美观、更专业
1. 隐藏无关区域,聚焦热点
有时候你只想展示南方五省,不想看北方。你可以利用 ECharts 的 zoom 和 center 属性,或者在 GeoJSON 中过滤掉不需要的 feature。
// 动态过滤 GeoJSON,只保留广东、广西、海南
function filterGeoJSON(geoJson, targetProvinces) {
return {
type: "FeatureCollection",
features: geoJson.features.filter(feature => {
return targetProvinces.includes(feature.properties.name);
})
};
}
2. 添加图例和提示,提升用户体验
不要只放一个干巴巴的地图。加上图例(Legend)和区域标签(Label),让用户知道颜色代表什么。
visualMap: {
show: true,
min: 0,
max: 1000,
text: ['高', '低'],
realtime: false,
calculable: true,
inRange: {
color: ['#f7f8fa', '#409eff'] // 浅蓝到深蓝
},
textStyle: {
color: '#666'
}
},
3. 解决“飞地”问题
有些省份有飞地(如陕西省的某些区域),在 GeoJSON 中可能表现为多个不相连的 Polygon。ECharts 默认会将它们视为同一个省份。但如果你的数据源有问题,飞地可能会被错误归类。
排查方法:打印出 GeoJSON 中每个 feature 的 properties.name,看看是否有重名或名称不一致的情况。统一名称是解决这类问题的关键。
七、 总结与避坑指南
最后,给大家整理几个最容易被忽视的坑:
- 名称匹配:GeoJSON 里的
name和 EChartsdata里的name必须完全一致,包括空格、全角半角括号。比如“广东省”和“广东”是不同的。 - 坐标系:再次强调,先看 GeoJSON 的坐标系。不确定就用在线工具转成 Web Mercator。
- 缓存:动态加载时,务必检查是否已注册过地图,避免重复请求文件,浪费流量。
- 文件大小:市级地图动辄几 MB,考虑使用 Gzip 压缩 和 CDN 加速。如果可能,将 GeoJSON 预渲染成图片(虽然失去了交互性,但在极端情况下是备选方案)。
- 移动端适配:
roam: true在移动端体验很好,但要注意触摸事件和鼠标事件的冲突,必要时使用touch相关配置。
希望这篇手把手的教程能帮你彻底解决中国地图可视化的难题。记住,地图可视化不仅是技术问题,更是对数据细节的把控。多调试、多打印日志,你一定能做出惊艳的地图作品!
