ECharts 地图坐标系

移动互联网时代,地理位置信息已经成为许多商业决策的重要参考依据,基于地图的图表需求也随之日益增长。ECharts 提供了一套功能完备的地图坐标体系,配合百度地图能够实现非常丰富的图表效果,下面一起看看吧。

1. 简介

官方解释:

ECharts 中提供了两种格式的地图数据,一种是可以直接 script 标签引入的 js 文件,引入后会自动注册地图名字和数据。还有一种是 JSON 文件,需要通过 AJAX 异步加载后手动注册。

慕课解释

某种程度上 ECharts 中的地图坐标系可以看做一种特殊直角坐标系,它将现实世界的地理平面映射为二维平面,通过经纬度系统实现坐标定位,通过线段按规则勾画出各个独立的地理、行政区域。在地图坐标系平面上,可以使用散点图、热力图、线图等图表类型。

在 ECharts 上,地理坐标系可通过 geo 属性配置,配置项列表可参考 官方文档,本文结合实例讨论地理坐标系中需要理解的知识点。

2. 实例解说

2.1 基础实例

由于地图文件过大,ECharts 3 之后已经不再内置地图数据,所以使用地理坐标系前,需要通过 echarts.registerMap 接口注册地理信息,实例代码:

实例演示
预览 复制
复制成功!
<!DOCTYPE html>
<html lang="zh-CN">
	<head>
		<meta charset="utf-8" />
		<meta http-equiv="X-UA-Compatible" content="IE=edge" />
		<meta name="viewport" content="width=device-width,initial-scale=1.0" />
		<title>Echarts Example</title>
	</head>
	<body>
		<div id="main" style="width: 600px;height: 400px"></div>

		<script src="//cdn.bootcss.com/axios/0.19.2/axios.min.js"></script>
		<script src="//cdn.bootcss.com/echarts/4.5.0/echarts.js"></script>
		<script type="text/javascript">
			async function run() {
				const myChart = echarts.init(document.getElementById('main'));
				const usaGeo = await axios.get('//cdn.jsdelivr.net/gh/VanMess/echart-wiki-examples@1.0.0/11_geo/USA.json');
				echarts.registerMap('USA', usaGeo.data, {
					Alaska: {
						// 把阿拉斯加移到美国主大陆左下方
						left: -131,
						top: 25,
						width: 15,
					},
					Hawaii: {
						left: -110, // 夏威夷
						top: 28,
						width: 5,
					},
					'Puerto Rico': {
						// 波多黎各
						left: -76,
						top: 26,
						width: 2,
					},
				});

				const option = {
					geo: { map: 'USA' },
					series: [
						{
							type: 'scatter',
							coordinateSystem: 'geo',
							data: [
								[-87.359296, 35.00118, 24],
								[-90.064905, 36.304691, 12],
								[-98.311367, 34.995703, 18],
								[-123.129194, 38.451652, 48],
							],
							symbolSize: function (val) {
								return val[2];
							},
						},
					],
				};
				myChart.setOption(option);
			}
			run();
		</script>
	</body>
</html>
运行案例 点击 "运行案例" 可查看在线运行效果

实例中,地理信息注册完毕后,就可以使用 series.geo.map 指定坐标系所使用的地理数据。

Tips
地图信息数据通过 json 格式定义,可参考 官网示例,echarts 曾提供各种地图信息的 下载页面,但由于不符合国家《测绘法》规定,目前已经暂时停止下载服务。

地理坐标系上的 data 通常是一个三维数组,数组第一项指定经度;第二项指定维度;前两项确定坐标位置后,第三项则指明数据点上的值。上例效果:

图片描述

2.2 地图上的缩放

地理坐标系中,与缩放相关的配置项包括:

配置名 类型 默认值 说明
roam boolean|string false 是否开启鼠标缩放和平移漫游。默认不开启。如果只想要开启缩放或者平移,可以设置成 scale 或者 move。设置成 true 为都开启
zoom number 1 当前视角的初始化缩放比例
scaleLimit.min number 滚轮缩放所支持的最小值
scaleLimit.min number 滚轮缩放所支持的最大值

由于地理信息数据的精度有限,建议根据精度范围设定合理的缩放范围,例如:

实例演示
预览 复制
复制成功!
<!DOCTYPE html>
<html lang="zh-CN">
	<head>
		<meta charset="utf-8" />
		<meta http-equiv="X-UA-Compatible" content="IE=edge" />
		<meta name="viewport" content="width=device-width,initial-scale=1.0" />
		<title>Echarts Example</title>
	</head>
	<body>
		<div id="main" style="width: 600px;height: 400px"></div>

		<script src="//cdn.bootcss.com/axios/0.19.2/axios.min.js"></script>
		<script src="//cdn.bootcss.com/echarts/4.5.0/echarts.js"></script>
		<script type="text/javascript">
			async function run() {
				const myChart = echarts.init(document.getElementById('main'));
				const usaGeo = await axios.get('//cdn.jsdelivr.net/gh/VanMess/echart-wiki-examples@1.0.0/11_geo/USA.json');
				echarts.registerMap('USA', usaGeo.data, {
					Alaska: {
						// 把阿拉斯加移到美国主大陆左下方
						left: -131,
						top: 25,
						width: 15,
					},
					Hawaii: {
						left: -110, // 夏威夷
						top: 28,
						width: 5,
					},
					'Puerto Rico': {
						// 波多黎各
						left: -76,
						top: 26,
						width: 2,
					},
				});

				const option = {
					geo: {
						map: 'USA',
						// 声明允许缩放与拖拽
						roam: true,
						scaleLimit: {
							// 声明最小缩放比例
							min: 1,
							// 声明最大缩放比例
							max: 6,
						},
					},
					series: [
						{
							type: 'scatter',
							coordinateSystem: 'geo',
							data: [
								[-87.359296, 35.00118, 24],
								[-90.064905, 36.304691, 12],
								[-98.311367, 34.995703, 18],
								[-123.129194, 38.451652, 48],
							],
							symbolSize: function (val) {
								return val[2];
							},
						},
					],
				};
				myChart.setOption(option);
			}
			run();
		</script>
	</body>
</html>
运行案例 点击 "运行案例" 可查看在线运行效果

图片描述

2.3 地图布局

地理坐标系中,与地图布局相关的属性较多,包括:

配置名 类型 默认值 说明
center array 当前视图的中心点,用经纬度表示
zoom number 当前视图的缩放比例
aspectScale number 0.75 地图长宽比
boundingCoords Array 二维数组,定义定位的左上角、右下角对应的经纬度
layoutCenter array 定义地图中心在屏幕中的位置
layoutSize number|string 定义地图大小
left number|string 坐标系离容器左侧的距离
top number|string 坐标系离容器上方的距离
right number|string 坐标系离容器右侧的距离
bottom number|string 坐标系离容器下方的距离
width number|string 坐标系宽度
height number|string 坐标系高度

其中,left、top、right、bottom、width、height 是 echarts 中通用的定位手段,含义与配置方法都与其他组件一样。下面讨论列表中不太常见的属性。

2.3.1 aspectScale

aspectScale 用于控制地图缩放的宽高比例。这个概念并不复杂,使用经纬度定义的地理信息本身带有宽高比例 aspect = width/height,那么渲染时若确定了地理坐标系的宽度为 x,则 y = x * aspect,形如:

图片描述

是不是跟我们平常看到的不一样?这是因为经纬坐标系是建立在地球的三维椭圆体上的,映射到二维平面时会产生一定的形变,所以绘制时需要在经纬度比例基础上加上椭圆形变,在 echarts 上则是通过 y = x * aspect * aspectScale 实现,通常保持默认值 0.75 即可:

图片描述

2.3.2 layoutCenter 与 layoutSize

layoutCenter、layoutSize 提供了另外一种布局方法,它们将地图坐标系调整为最长边等于 layoutSize`的盒子,并将盒子的中心点放置在 layoutCenter 位置上。基于 layoutCenter、layoutSize 的布局能够保持地图高宽比的情况下把地图放在某个盒形区域的正中间,并且保证不超出盒形的范围。使用上请注意:

  1. 两者必须同时出现才有效;
  2. 当配置了这两个属性后,left、top、right、bottom、width、height 均失效;
  3. layoutCenter、layoutSize 仅设定初始化时的布局效果,坐标系依然可以通过放大、移动变更位置和大小。

例如:

实例演示
预览 复制
复制成功!
<!DOCTYPE html>
<html lang="zh-CN">
	<head>
		<meta charset="utf-8" />
		<meta http-equiv="X-UA-Compatible" content="IE=edge" />
		<meta name="viewport" content="width=device-width,initial-scale=1.0" />
		<title>Echarts Example</title>
	</head>
	<body>
		<div id="main" style="width: 600px;height: 400px"></div>
		<script src="//cdn.bootcss.com/axios/0.19.2/axios.min.js"></script>
		<script src="//cdn.bootcss.com/echarts/4.5.0/echarts.js"></script>
		<script type="text/javascript">
			async function run() {
				const myChart = echarts.init(document.getElementById('main'));
				const usaGeo = await axios.get('//cdn.jsdelivr.net/gh/VanMess/echart-wiki-examples@1.0.0/11_geo/USA.json');
				echarts.registerMap('USA', usaGeo.data, {
					Alaska: { left: -131, top: 25, width: 15 },
					Hawaii: { left: -110, top: 28, width: 5 },
					'Puerto Rico': { left: -76, top: 26, width: 2 },
				});
				const option = {
					toolbox: { feature: { saveAsImage: {} } },
					geo: {
						map: 'USA',
						roam: true,
						// 设定布局中心点位置
						layoutCenter: ['50%', '40%'],
						// 设定坐标系大小
						layoutSize: 420,
					},
					series: [
						{
							type: 'scatter',
							coordinateSystem: 'geo',
							data: [
								[-87.359296, 35.00118, 24],
								[-90.064905, 36.304691, 12],
								[-98.311367, 34.995703, 18],
								[-123.129194, 38.451652, 48],
							],
							symbolSize: function (val) {
								return val[2];
							},
						},
					],
				};
				myChart.setOption(option);
			}
			run();
		</script>
	</body>
</html>
运行案例 点击 "运行案例" 可查看在线运行效果

示例效果:

图片描述

2.3.3 center 与 boundingCoords

center 用于定义当前视图的中心点,以经纬度坐标表示,形如: center: [-80, 30]; boundingCoords 则用于定义当前视图的左上角、右下角经纬度,以二维数组表示,形如:

{
	boundingCoords: [
		// 定位左上角经纬度
		[-90, 30],
		// 定位右下角经纬度
		[-120, 50],
	],
}

center 与 boundingCoords 互斥,当同时存在时,优先使用 center。

从定义可以看出,center 定义的是地图所展示的中心区域的位置,配合 zoom 属性可以控制视图中展示的地图多寡,例如下例中:

实例演示
预览 复制
复制成功!
<!DOCTYPE html>
<html lang="zh-CN">
	<head>
		<meta charset="utf-8" />
		<meta http-equiv="X-UA-Compatible" content="IE=edge" />
		<meta name="viewport" content="width=device-width,initial-scale=1.0" />
		<title>Echarts Example</title>
	</head>
	<body>
		<div id="main" style="width: 600px;height: 400px"></div>
		<script src="//cdn.bootcss.com/axios/0.19.2/axios.min.js"></script>
		<script src="//cdn.bootcss.com/echarts/4.5.0/echarts.js"></script>
		<script type="text/javascript">
			async function run() {
				const myChart = echarts.init(document.getElementById('main'));
				const usaGeo = await axios.get('//cdn.jsdelivr.net/gh/VanMess/echart-wiki-examples@1.0.0/11_geo/USA.json');
				echarts.registerMap('USA', usaGeo.data, {
					Alaska: { left: -131, top: 25, width: 15 },
					Hawaii: { left: -110, top: 28, width: 5 },
					'Puerto Rico': { left: -76, top: 26, width: 2 },
				});
				const option = {
					geo: {
						map: 'USA',
						roam: true,
						// 地图中心点的经纬度坐标
						center: [-123.129194, 38.451652],
						// 地图初始时的缩放比例
						zoom: 1.86,
					},
					series: [
						{
							type: 'scatter',
							coordinateSystem: 'geo',
							data: [
								[-87.359296, 35.00118, 24],
								[-90.064905, 36.304691, 12],
								[-98.311367, 34.995703, 18],
								[-123.129194, 38.451652, 48],
							],
							symbolSize: function (val) {
								return val[2];
							},
						},
					],
				};
				myChart.setOption(option);
			}
			run();
		</script>
	</body>
</html>
运行案例 点击 "运行案例" 可查看在线运行效果

示例效果:

图片描述

而 boundingCoords 定义是地图坐标系所展现的整个内容区域的经纬度,使用时不需要考虑缩放比例,相对更简单。上例基础上,修改 geo:

{
	geo: {
		map: 'USA',
		roam: true,
		boundingCoords: [
			// 定位左上角经纬度
			[-90, 30],
			// 定位右下角经纬度
			[-120, 50],
		],
	},
}

示例效果:

图片描述

2.4 百度地图扩展

geo 坐标系需要自行提供地理信息数据,使用上有一定的不便,因此 echarts 提供了另一种地理坐标系实现 —— bmap。bmap 扩展将百度地图带入 echarts,以百度地图为底图绘制地理坐标系,所以使用上就不用再关注地理数据了,而且依托于百度地图提供的强大功能,bmap 在伸缩、移动、精度等方面更出色。

使用时,除了 echarts 文件外,还需要引入百度地图依赖、bmap 扩展依赖,以 CDN 为例:

<!-- 引入百度地图的jssdk -->
<!-- 配置方法可参考: http://lbsyun.baidu.com/index.php?title=jspopular3.0 -->
<script src="//api.map.baidu.com/api?v=2.0&ak="></script>
<!-- 引入 ECharts -->
<script src="//cdn.bootcss.com/echarts/4.5.0/echarts.js"></script>
<!-- 引入 bmap 扩展 -->
<script src="//cdn.bootcss.com/echarts/4.5.0/extension/bmap.min.js"></script>

引入后,就可以通过 bmap 配置地理坐标系。bmap 所支持的配置项比较少,包括:

配置名 类型 默认值 说明
center array 当前视图的中心点,用经纬度表示
roam boolean|string false 是否开启鼠标缩放和平移漫游。
zoom number 1 当前视角的初始化缩放比例
mapStyle object 旧版地图的自定义样式接口,详见: http://developer.baidu.com/map/jsdevelop-11.htm
mapStyleV2 object 新版地图的自定义样式,详见: http://developer.baidu.com/map/jsdevelop-11.htm

bmap 包含了百度地图所支持的所有地理区域信息,所以应用时只需通过 center 指定视图中心点,通过 zoom 控制视图区域,即可实现地理坐标系,示例:

实例演示
预览 复制
复制成功!
<!DOCTYPE html>
<html lang="zh-CN">
	<head>
		<meta charset="utf-8" />
		<meta http-equiv="X-UA-Compatible" content="IE=edge" />
		<meta name="viewport" content="width=device-width,initial-scale=1.0" />
		<title>Echarts Example</title>
	</head>
	<body>
		<div id="main" style="width: 960px;height: 600px"></div>

		<script src="//api.map.baidu.com/api?v=2.0&ak=KOmVjPVUAey1G2E8zNhPiuQ6QiEmAwZu&__ec_v__=20190126"></script>
		<script src="//cdn.bootcss.com/echarts/4.5.0/echarts.js"></script>
		<script src="//cdn.bootcss.com/echarts/4.5.0/extension/bmap.js"></script>
		<script type="text/javascript">
			var myChart = echarts.init(document.getElementById('main'));
			var data = [
				{ name: '平顶山', value: [113.29, 33.75, 119, 119, 119] },
				{ name: '邢台', value: [114.48, 37.05, 119, 119, 119] },
				{ name: '德州', value: [116.29, 37.45, 120, 120, 120] },
				{ name: '济宁', value: [116.59, 35.38, 120, 120, 120] },
				{ name: '荆州', value: [112.239741, 30.335165, 127, 127, 127] },
				{ name: '宜昌', value: [111.3, 30.7, 130, 130, 130] },
				{ name: '义乌', value: [120.06, 29.32, 132, 132, 132] },
				{ name: '丽水', value: [119.92, 28.45, 133, 133, 133] },
				{ name: '洛阳', value: [112.44, 34.7, 134, 134, 134] },
				{ name: '秦皇岛', value: [119.57, 39.95, 136, 136, 136] },
				{ name: '株洲', value: [113.16, 27.83, 143, 143, 143] },
				{ name: '石家庄', value: [114.48, 38.03, 147, 147, 147] },
				{ name: '莱芜', value: [117.67, 36.19, 148, 148, 148] },
				{ name: '常德', value: [111.69, 29.05, 152, 152, 152] },
				{ name: '保定', value: [115.48, 38.85, 153, 153, 153] },
				{ name: '湘潭', value: [112.91, 27.87, 154, 154, 154] },
				{ name: '金华', value: [119.64, 29.12, 157, 157, 157] },
				{ name: '岳阳', value: [113.09, 29.37, 169, 169, 169] },
				{ name: '长沙', value: [113, 28.21, 175, 175, 175] },
				{ name: '衢州', value: [118.88, 28.97, 177, 177, 177] },
				{ name: '廊坊', value: [116.7, 39.53, 193, 193, 193] },
				{ name: '菏泽', value: [115.480656, 35.23375, 194, 194, 194] },
				{ name: '合肥', value: [117.27, 31.86, 229, 229, 229] },
				{ name: '武汉', value: [114.31, 30.52, 273, 273, 273] },
				{ name: '大庆', value: [125.03, 46.58, 279, 279, 279] },
			];

			var option = {
				bmap: {
					// 指定中心点
					center: [104.114129, 34.550339],
					// 指定缩放比例
					zoom: 5.4,
					roam: true,
				},
				series: [
					{
						name: 'pm2.5',
						type: 'scatter',
						coordinateSystem: 'bmap',
						data: data,
						symbolSize: function (val) {
							return val[2] / 10;
						},
					},
				],
			};
			myChart.setOption(option);
		</script>
	</body>
</html>
运行案例 点击 "运行案例" 可查看在线运行效果

示例效果:

图片描述

此外,还可以通过 myChart.getModel().getComponent('bmap').getBMap(); 接口获取 bmap 对应的地图实例,实现与地图的交互。在上述示例基础上,添加额外代码:

// 获取地图实例
var map = myChart.getModel().getComponent('bmap').getBMap();

// 添加交通状况层
var traffic = new BMap.TrafficLayer();
map.addTileLayer(traffic);

// 重置视图中心
map.centerAndZoom(new BMap.Point(114.48, 38.03), 8);

示例效果;

图片描述

完整的开发指南,请参考 百度地图

3. 个人经验

地图坐标系常用于表达与地理位置密切相关的数据,比如新冠状病毒肆虐期间,许多实时疫情报告都会通过地图+热力图形式表现各地疫情发展情况。地图坐标系下,比较常见的图表类型有:

  1. 散点图: 用于表达各个分散地点的数据情况;
  2. 热力图: 用于表达独立的地理区域之间的数据对比,比如通过热力图勾画各个省的 PV 量,以此分析不同地域的用户分布;
  3. 线图: 用于表达地图上点与点之间数据关系,比如通过线图绘制运动轨迹。

4. 小结

图片描述
本节结合实例讲述 Echarts 下地图坐标系的各种应用场景,包括基础用法;地图缩放、布局;以及如何结合百度地图接口组成更强大的地图图表应用。