前言
2020年刚开始,各钟不幸的消息满天飞。新型肺炎的蔓延,科比去世… 无时无刻让我感到痛楚。为了不给国家添乱,新年几天都在窝在家里。时不时拿起手机,观察一下现在病情蔓延情况。下面这张地图就是一张典型的GIS应用。
每当我看到这张静态图时,很想要知道几个信息无法获知。
- 我们能通过颜色和图例比较一个省的确诊人数范围,看不到一个省具体患病人数。
- 由于是一张静态图,我们没法获市级数据。如果地图可以拖动,放大缩小就简单多了。
- 每次看到红色,心里都很焦虑。能换成其他颜色,我自己更加能接受点。
基于这两个小功能,我准备介绍一下怎么去制作一张地图。我准备分两个阶段来做介绍。
- 先用最简洁的代码来生成一张静态图片。通过这个阶段,让我们认识一下一般地图应用开发的流程。
- 当我们了解流程以后,我们就把这个程序改造成地图服务,让她和知名的地图前端库
Leaflet
合并开发一个可交互的地图,集成点有趣的功能。
这篇文章,我准备先从制作一张静态图片开始。
让我们从环境开始
以前开发地图应用软件,可能需要掌握很多编程语言技能,才能胜任一个完整的项目。比如一个典型的GIS B/S应用一般会使用Java, C#或其他后端编程语言来开发后端,然后用JavaScript + HTML来开发前端展现。
今天用我们熟悉的JavaScript;即使是前端开发人员也可以开发后端地图应用了。追求极简开发环境的话,我们只需要2个工具。Node.js (推荐8以上,或者直接安装最新版本都是兼容的)和 vscode.
这篇文章照顾新手,写的比较多。老鸟请自行过滤。勿喷。
创建工程,添加引用
接着,我们创建一个工程目录。用以下命令就可以了。(我个人比较喜欢使用命令行,由于平时都是用macOS做日常使用机器。所以以下命令行都是macOS执行验证的)。
# 创建项目目录
cd [your workspace]
md nCoV-map
cd nCoV-map
# 创建功能,添加引用
npm init -y
npm i --save ginkgoch-map canvas lodash
# 新建一个文件,这个将是我们写代码的地方
touch tutorial-01.js
这里引用了
canvas
库,是因为Node.js
没有提供绘图API,我们只能引用一个第三方Node.js
库来替代使用。
到这里,我们的工程已经建立好了。
GIS数据
GIS应用里面数据是很重要的。我把她分为静态数据和动态数据。静态数据就是我们的几何图形以及她们特定的特征数据。如地区的名字等。动态数据就是我们实时关注的疫情变化。
一般静态数据比较容易找到。百度搜索中国地图数据csv, json, shapefile都可以找到。这个项目里面,我准备使用shapefile作为我的静态数据。这里你可以找到以下数据,我们一会儿会使用到。把上面数据下载下来以后,放到工程的data
目录下面。
- chn/
- gadm36_CHN_1_3857.shp - 省级数据
- gadm36_CHN_2_3857.shp - 市级数据
- cntry02.shp - 世界国家数据
动态数据会麻烦点。我是写了一个爬虫,定时爬取。有兴趣可以私聊。不过作为例子,我放上了几份疫情数据在data/infected
目录里面以便做示例。
剩下的工作就很简单了
叠加世界数据
首先,我们定义一个函数来创建一个地图的图层,一个数据源即一个数据图层,多个数据图层叠加起来就可以构成我们期望的样式。使用ginkgoch-map
,我们是这样定义一个图层的。
function createLayerWithDefaultStyle(filePath) {
// create a source with the specified shapefile file path
let source = new GK.ShapefileFeatureSource(path.resolve(__dirname, filePath));
// wrap the source as a world layer
let layer = new GK.FeatureLayer(source);
// set a style on the layer
layer.styles.push(new GK.FillStyle('#f0f0f0', '#636363', 1));
return layer;
}
有了layer
, 我们可以简单查看我们数据图层的样子。比如对于数据cntry02.shp
:
let worldLayer = createLayerWithDefaultStyle('../data/cntry02.shp');
await worldLayer.open();
let worldImage = await worldLayer.thumbnail(512, 512);
fs.writeFileSync(path.resolve(__dirname, './images/tutorial-01-world.png'), worldImage.toBuffer());
我们通过命令行执行下面的语句。我们可以找到图片:
node tutorial-01.js