要生成一副图片地图,可以使用 ArcGIS、QGIS 等工具,也可以使用代码实现。我这里介绍的当然是用代码实现,而且是利用开源软件。毕竟桌面版 GIS 工具的介绍太多了,大家的地图都做的很漂亮。
用代码渲染地图就不得不提到 GDAL 和 Mapnik。GDAL 是处理 GIS 数据最常用的库,这里我们就要用到 gdaldem 处理 DEM 数据。Mapnik 是渲染地图的最基本工具,我们要使用它来生成地图。
要绘制一个地图首先需要定义清楚绘制什么样的地图,是地形图还是街道图还是卫星影像图,然后根据地图内容的需求将需要的各种类型空间数据准备好,有了这些数据才能绘制出地图。
在这里我介绍的是如何绘制一个地形图,需要使用到的是 DEM(数字高程模型)数据,和矢量数据。DEM 数据是用来表示地形的高低起伏,一般来说 DEM 数据是存储为 GeoTiff 格式的。矢量数据我在这里主要是绘制水系,格式是 Shape file。这次的重点是如何绘制地图,因此有关数据就不多做解释了,如果对这两种数据不了解可以自行查询一下。
接下来就是具体的过程介绍了。
第一步,我们需要对 DEM 数据进行简单的处理。主要目的是为了得到三种数据,第一种是色彩,主要是表现地形的高低,不同的颜色代表不同的海拔高度,可以直观的看出高度情况。第二种是山影,主要是为了让渲染出来的地图更具有立体性。第三种是坡度,不同的颜色可以代表地形的陡峭程度,当然还可以叠加上坡向的表示。
渲染色彩图首先需要创建一个配置文件,在使用 gdaldem 命令生成色彩数据的时候需要用到它。
比如创建一个空的配置文件叫做: color-relief.cfg,内容如下:
0 110 220 110
900 240 250 160
1300 230 220 170
1900 220 220 220
2500 250 250 250
nv 255 255 255 0
复制代码
其中每一行代表了一个海拔高度值对应的颜色是什么,第一列的数字是海拔高度值,后面 3 列是 RGB 颜色色值。这里需要注意的是最后的 nv 代表 nodata-value,如果不配置这一项的话,没有数据的部分就会被渲染成黑色,这一行最后一列多了一个 0,是它的透明度值,0 代表完全透明,100 代表完全不透明。
然后使用 gdaldem 创建色彩数据
gdaldem color-relief dem.tif color_relief.cfg color_relief.tif -alpha
复制代码
color_relief.tif文件长这样:
能看出来哪里高哪里低了,但是它看起来很平对吧。所以还需要多种数据叠加
创建color_slope.cfg,用于产生坡度数据
这个文件中每一行代表一个坡度对应什么颜色,第一列是坡度,后面 3 列是 RGB 色值。在这个例子中,产生的坡度数据会自动从 0-90 度填充从白色到黑色的渐变色。
产生坡度数据同样使用 gdaldem 命令
gdaldem slope dem.tif slope.tif
gdaldem color-relief slope.tif color_slope.txt slopeshade.tif -alpha
复制代码
现在只缺少山影数据了
gdaldem hillshade dem.tif hillshade.tif
复制代码
我还准备了一个矢量数据,其中包含了河流和水库等水域信息,这个数据不需要单独处理,在Mapnik的地图样式配置文件中,可以同时使用多种不同的数据格式配图。
数据准备工作都已经完成,接下来就需要创建Mapnik渲染地图所需的地图样式配置文件了,我的配置文件
terrain_lake.xml是这样的:
<Map srs="+proj=longlat +datum=WGS84 +no_defs" background-color="transparent">
<Style name="color relief style">
<Rule>
<RasterSymbolizer mode="normal" />
</Rule>
</Style>
<Style name="slopeshade style">
<Rule>
<RasterSymbolizer opacity="0.1" mode="multiply" scaling="bilinear" />
</Rule>
</Style>
<Style name="hillshade style">
<Rule>
<RasterSymbolizer opacity="0.3" mode="multiply" scaling="bilinear" />
</Rule>
</Style>
<Style name="lake style">
<Rule>
<PolygonSymbolizer fill="rgb(180,210,230)" />
<Filter>[type] = 'Waters'</Filter>
</Rule>
</Style>
<Layer name="color relief">
<StyleName>color relief style</StyleName>
<Datasource>
<Parameter name="type">gdal</Parameter>
<Parameter name="file">color_relief.tif</Parameter>
</Datasource>
</Layer>
<Layer name="slopeshade">
<StyleName>hillshade style</StyleName>
<Datasource>
<Parameter name="type">gdal</Parameter>
<Parameter name="file">slopeshade.tif</Parameter>
</Datasource>
</Layer>
<Layer name="hillshade">
<StyleName>hillshade style</StyleName>
<Datasource>
<Parameter name="type">gdal</Parameter>
<Parameter name="file">hillshade.tif</Parameter>
</Datasource>
</Layer>
<Layer name="lake" status="on" srs="+proj=longlat +datum=WGS84 +no_defs">
<StyleName>lake style</StyleName>
<Datasource>
<Parameter name="type">shape</Parameter>
<Parameter name="file">land_use_utf8.shp</Parameter>
</Datasource>
</Layer>
</Map>
复制代码
在这个配置文件中,可以针对不同数据定义多种样式和多个图层,每个图层可以指定不同的样式,非常方便使用。准备好配置文件后,我这里使用的是 nodejs 版本的 binding 调用 mapnik 渲染,代码如下:
var mapnik = require("mapnik");
var fs = require("fs");
mapnik.register_default_fonts();
mapnik.register_default_input_plugins();
var map = new mapnik.Map(300, 300);
map.load("./terrain_lake.xml", function(err, map) {
map.zoomAll();
var im = new mapnik.Image(300, 300);
map.render(im, function(err, im) {
im.encode("png", function(err, buffer) {
fs.writeFile("map.png", buffer);
});
});
});
复制代码
最终的结果是这样的:
最近我负责的项目中有很多地图需要渲染,使用了 Mapnik 进行批量处理,效果非常理想,而且 Nodejs 调用可以实现异步,效率也很高,圆满的完成了客户的需求。
Mapbox 公司的开源项目中还有基于 mapnik 的瓦片服务,功能也很强大,如果使用编程手段生成地图的话,我认为 Mapnik 是比较理想的选择。(https://gisbook.cn/data/china-dem)
本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。
评论