前言
经过之前一段时间的磨砺,我具备了基本的使用echarts绘制图表的能力。但是在最近这几个月里我接连遇到了几个棘手的任务,这大大的提升了我的echarts水平。其中我遇到的第一个高难度任务就是使用echarts绘制如下的地图:
简单的分析一下,这张图中我主要有几个任务点:
- 最重要的就是,要在地图上展示降雨区,这些降雨区的颜色要根据真实的数据渲染
- 要有一个图例
- 添加降雨区图层下的地图(可选任务,完成可以增加地图的真实性)
一、如何使用echarts绘制一张地图
想要使用echarts绘制地图,你必需要了解三个内容:registerMap
、series-map
和geo
。其中registerMap
是用来注册地图的方法,series-map
则用于创建地图类型的chart,geo
(地理坐标系组件)则主要是用于进行地图的可视化设置的。
1.使用 registerMap 注册地图
想要绘制地图就必须使用地理空间数据,在echarts中我们可以使用echarts.registerMap
方法将地理空间数据引入echarts中。当然echarts毕竟不是专业的地图库,因此它只能接受GeoJSON类型的地理数据或者是一张SVG图。
我是有降雨区的的坐标数据的,因此这里我的思路就是,先将地理坐标数据转换为GeoJSON,再将GeoJOSN注册为地图。
转换GeoJSON
这里我就直接使用turf.js中的polygon
和featureCollection
方法进行了转换,同时还将降雨区的名称作为了polygon的属性数据。
const geoJson = featureCollection(
geo.map(el =>
polygon([el.coordinate], { name: el.name})
)
);
注意:这里给polygon中添加名称很关键,有了名称后续才可以在地图上展示区域的名称,还有更重要的,方便之后作为主键用于关联数据和 GeoJSON 地理要素。
注册地图
echarts.registerMap("beijiang", {
geoJson,
});
2.使用 series-map 和 geo 组件创建地图
这里我需要着重谈一个问题:series-map
和geo
组件有什么联系和区别,一开始的时候我对这两者是傻傻分不清的。其实仔细观察就会发现,geo
组件中的配置属性series-map
中都有,但series-map
可以给地图添加属性数据,geo
组件则不能。所以其实可以理解为geo
是series-map
的“子集”。实际上在默认情况下series-map
会自己生成内部专用的 geo
组件。但是也可以用这个 geoIndex
指定一个 geo
组件。这样的话,map 和 其他 series(例如散点图)就可以共享一个 geo
组件了。
而我目前是没有共享geo
组件的需求的,因此我就只需要使用series-map
就行了,不用再专门搞一个geo
组件了。那么此时我的配置项如下:
{
series: [
{
type: "map",
// 使用registerMap注册的地图名称
map: "beijiang",
// 每个降雨区对应的属性数据
data: geo.map(item => ({
name: item.name,
value: item.value,
})),
top: "2%",
left: "19%",
itemStyle: {
borderColor: "rgb(255,255,255,0.3)",
borderWidth: 3,
},
label: {
show: false,
fontSize: 10,
},
aspectScale: 1,
},
],
}
这些配置当中最重要的是map
和data
,map
用于指定我使用哪个地图(实现通过registerMap
注册的),data
则用于引入地图的属性数据(在我这里就是降雨区的降雨量数据)。至于其它的配置项则就是用来配置布局和样式的这里就不详细介绍了。
二、如何让地图上的降雨区根据传入的数据进行颜色映射
目前还有一个最重要的任务没有实现,就是让每个降雨区根据不同的降雨量渲染不同的颜色,这个就需要用到视觉映射组件visualMap
。
1.如何使用visualMap组件
使用visualMap
组件只需要回答好几个问题就行了。
对哪个系列使用数据映射?
通过seriesIndex
选项设置对哪个系列进行映射
使用哪些数据进行映射?
数据映射使用的是系列中的series.data
中的数据,但其中的数据可能会有多个维度,可以使用dimension
选项设置使用哪个维度的数据
使用哪种类型的视觉映射组件?
virualMap
组件分为连续型(visualMapContinuous)与分段型(visualMapPiecewise)。
连续型的意思是,进行视觉映射的数据维度是连续的数值;而分段型则是数据被分成了多段或者是离散型的数据。
连续型的是这样:
分段型的则是这样:
分段型的视觉映射组件又可以分为三种模式:
映射哪些视觉元素?如何设置映射规则?
数据映射的目标就是视觉元素,echarts中可以映射的视觉元素有:
主要通过inRange
和outOfRange
设置视觉元素及其映射规则,详情请见相关文档:
2.实现我的visualMap
根据上面总结的使用方法,我来实现自己的视觉映射。
第一步 对哪个系列使用数据映射?
我这边只有一个系列,因此不用专门设置。
第二步 使用哪些数据进行映射?
我的数据只有name
和value
两个维度,因此也不用专门设置。
第三步 使用哪种类型的视觉映射组件?
查看一下示例,我需要实现这样的视觉映射:
因此我需要使用的是分段型视觉映射组件,并且要使用自定义分段模式。
所以type
就为'piecewise'
,并在pieces
中定义每块的范围。
{
visualMap:{
type:"continuous",
pieces:[...]
}
}
映射哪些视觉元素?如何设置映射规则?
我需要映射的视觉元素就只有颜色,但是设置的方式就比较特殊了。对于分段型视觉映射组件的自定义分段模式来说,是直接在pieces
中色孩子映射规则和视觉元素,不需要使用inRange
和outOfRange
。详情请见官方配置项文档:visualMap-piecewise.pieces
{
visualMap:{
type:"continuous",
pieces:[
{ gte: 0, lte: 10, color: "#A6F28E" },
{ gt: 10, lte: 25, color: "#258C30" },
{ gt: 25, lte: 50, color: "#61B8FF" },
{ gt: 50, lte: 100, color: "#0000E1" },
{ gt: 100, lte: 250, color: "#FA00FA" },
{ gt: 250, lte: 400, color: "#880015" },
{ gt: 400, lte: 600, color: "#FFAA01" },
{ gt: 600, lte: 1000, color: "#FF6600" },
{ gt: 1000, lte: 1500, color: "#E60000" },
{ gt: 1500, color: "#990100" },
]
}
}
最后在添加一些其它的设置,最终的效果如下:
{
visualMap:{
type: "piecewise",
pieces: [
{ gte: 0, lte: 10, color: "#A6F28E" },
{ gt: 10, lte: 25, color: "#258C30" },
{ gt: 25, lte: 50, color: "#61B8FF" },
{ gt: 50, lte: 100, color: "#0000E1" },
{ gt: 100, lte: 250, color: "#FA00FA" },
{ gt: 250, lte: 400, color: "#880015" },
{ gt: 400, lte: 600, color: "#FFAA01" },
{ gt: 600, lte: 1000, color: "#FF6600" },
{ gt: 1000, lte: 1500, color: "#E60000" },
{ gt: 1500, color: "#990100" },
],
align: "right",
textGap: 9,
itemGap: 9,
right: 10,
bottom: 10,
padding: [25, 5, 5, 5],
inverse: true,
itemWidth: 20, // 减小宽度
itemHeight: 10, // 减小高度
textStyle: {
fontSize: 10,
},
backgroundColor: "#fff",
}
}
三、如何给地图添加背景地图
现在我已经完成了地图的主要功能,接下来我想尝试给地图添加底图。我已经事先准备好了一张底图的图片:
我的计划就是将这张底图图片设置为图表的背景图片。
1.失败的尝试
将底图图片设置为图表容器的背景
一开始我在网上搜到了这个方法,直接将图片设置为容器的背景图,这样做确实可以实现对应的效果:
但是后面我发现这个方法不适合我,因为我生成地图的目标是要将其下载为图片,我是使用echartsInstance. getDataURL
下载的,这样下载的图片是无法保留容器的背景的。
将底图图片注册为地图插入图表
在经历了上面的失败后,我又想到了echarts.registerMap
方法可以将svg图片注册为地图,那么我是不是可以将底图图片先转为svg格式,然后再用echarts.registerMap
方法注册为地图,最后再通过series-map
添加到图表中。
很可以这么尝试之后失败了,我在注册和使用的阶段总是会报错,我无法解决这些报错。
我猜测这可能与我的svg只是一张套皮的png图片有关:
2.使用graphic添加底图图片
之后我又查阅了一些资料,了解到echarts的graphic组件可以添加图片。实际上graphic非常强大它可以向图表中添加各种图形以及文本和图片。
于是我成功的添加了底图图片。
{
graphic:[
{
type: "image", // 图形元素类型
id: "basemap", // 更新或删除图形元素时指定更新哪个图形元素,如果不需要用可以忽略
right: "center", // 根据父元素进行定位(居中)
y: -50,
scaleY: 1.2,
scaleX: 1.0,
z: 0, // 层叠
bounding: "all", // 决定此图形元素在定位时,对自身的包围盒计算方式
style: {
image: "./PixPin_2024-07-04_17-39-26.png",
width: 550,
height: 410,
},
}
]
}
四、给图例添加单位
目前整张地图已经基本完成了,但还有一个小小部分没有实现,那就是原型图中的图例的单位:
这个用visualMap
是没办法实现的,我本来是准备使用title
组件 来添加这行文本的,但是如今既然了解到了graphic那我就准备用graphic-text
来做。
{
graphic:[
{
type: "text",
x: 470,
y: 185,
z: 5,
style: {
text: "降雨量(mm)",
fontSize: 10,
}
}
]
}
最终效果如下: