D3 除了绘製图表外还可以拿来绘製地图,而这篇的目标是绘製一个世界地图,那就开始吧!
地图格式
绘製地图前我们要先了解一下会使用到的资料格式,比较常看见的会有 SHP、GeoJSON 与 TopoJSON,而其中 D3 可用的格式为 GeoJSON 与 TopoJSON,详细的就让我们往下看
Shapefile
ESRI Shapefile(shp),或简称 shapefile,是美国环境系统研究所公司(ESRI)开发的空间资料开放格式,目前该档案格式已经成为了地理资讯软体界的开放标準。
shapefile 档案用于描述几何体物件:点、折线与多边形。例如其可以储存井、河流、湖泊等空间物件的几何位置。除了几何位置,shp档案也可以储存这些空间物件的属性,例如河流的名字、城市的温度等等。
而 Shapefile 又有分为好几种格式,我们会用到的是副档名为 .shp
的格式,可以将其转换成 D3 可用的格式,也就是 GeoJSON 与 TopoJSON
GeoJSON
GeoJSON 是一种基于 JSON 的地理空间数据交换格式,它定义了几种类型 JSON 对象以及它们组合在一起的方法,以表示有关地理要素、属性和它们的空间範围的数据。
GeoJSON 同样是属于 JSON 的一种,只不过是对它的名称进行规範后,专门用于表示地理信息的格式,此格式为 D3 主要使用的格式
TopoJSON
TopoJSON 是 GeoJSON 的扩展,由 D3 的作者所发明,透过将共享边(arcs)整合的方法组成,消除了一些 GeoJSON 冗余的部分,使档案大小大幅缩小,缩小幅度约可达 80% 左右
格式转换
首先我们先找到提供 shp 的网站将档案下载下来,里面会有 Shapefile 的各种格式,我们需要的是副档名为 shp 的档案,再来就是将它转换成 GeoJSON 或是 TopoJSON 了
线上服务(shp 转 GeoJSON、TopoJSON)
线上转换可使用 mapshaper 的服务,它除了转换还可以预览,可以说是非常好用
将下载下来的档案 import 进去

shapefile 套件(shp 转 GeoJSON)
首先安装 shapefile 这个套件,它可以帮我们将 .shp
转换成 GeoJSON,可以使用它的 API 或是直接使用终端机转换,我们这边使用终端机的方式
安装
$ npm install -g shapefile
转换
$ shp2json ne_110m_land.shp -o map.json<!-- 将 ne_110m_land.shp 转换成 map.json -->$ shp2json ne_110m_land.shp -o map.json --encoding big5<!-- 将 ne_110m_land.shp 转换成 map.json,使用 big5 编码 -->
topojson 套件(TopoJSON、GeoJSON 互相转换)
TopoJSON 无法直接于 D3 使用,所以作者也另外提供了套件,让我们将 TopoJSON 转换为 GeoJSON 的格式,安装一样可以使用 npm 或是 CDN 方式载入,有兴趣可以看看,这边就不做示範了
绘製地图
用上述方法得到 GeoJSON 资料后就可以开始绘製地图了,这边先介绍几个会使用到的方法~
d3.geoMercator
:将地图以麦卡托投影法绘製center
:设定地图中心点座标scale
:设定地图缩放倍率d3.geoPath
:将投影资料转换为 path
的路径// 设定 svg 的宽高const map = d3 .select('.map') .attr('width', 500) .attr('height', 500);// 获取 ne_110m_land.json 的档案资讯 ( GeoJSON ),完成后执行 draw 函式fetch('./ne_110m_land.json') .then(res => res.json()) .then(data => draw(data));function draw(mapData) { // 设定投影中心点与缩放倍率 const projection = d3 .geoMercator() .center([200, 10]) .scale(70); // 将投影资料转换为路径 const path = d3.geoPath(projection); // 绘製地图 map .selectAll('path') .data(mapData.geometries) .enter() .append('path') .attr('d', path) .attr('stroke', 'black') .attr('stroke-width', '0.7') .attr('fill', 'steelblue') .on('mouseover', function() { d3.select(this).attr('fill', '#007bff') }) // 滑鼠碰到后改变颜色 .on('mouseleave', function() { d3.select(this).attr('fill', 'steelblue') }) // 滑鼠离开将颜色变回}
结语
绘製地图并不难,只要有地图的资料,并且把握好资料格式的转换,相信对大家来说是轻而易举的事情~