数据源

在本节中,我们将使用 Mapnik 工具包在Python 中查看Mapnik的更多细节。

在访问一幅地图上的给定的地理空间数据之前,你需要设置一个Mapnik 数据源对象。并用它充当Mapnik和地理空间数据的桥梁。

image

通常,创建的数据源是使用如下所述的函数之一。然后,添加 这个数据源到需要使用这个数据的Mapnik的图层对象中:

layer.datasource = datasource

一个简单的数据源对象可以被很多图层共用,也可以仅仅被一个图层使用。 Mapnik支持不同类型的数据源,其中有一些是商业数据库中的实验或访问数据。

常用数据格式的读取

Shape文件

使用shape文件作为一个Mapnik的数据源是非常简单的。

你需要向mapnik.Shapefile()函数提供你所需shape文件的名称和目录路径。

In [1]:
import mapnik
datasource = mapnik.Shapefile(file="/gdata/GSHHS_c.shp")

如果shape文件在不同的目录,可以使用os.path.join()来定义完整的路径。 例如,你可以在一个目录中打开与你Python程序相关的shape文件:

In [2]:
import mapnik
import os
datasource = mapnik.Shapefile(file=os.path.join("/gdata", 'GSHHS_c.shp'))

当你打开一个shape文件数据源,shape文件的属性可以用在过滤器的表达式中,并且作为由TextSymbolizer显示的字段。 在默认情况下,shape文件中的文本都被假定为UTF-8字符编码。如果你需要使用不同的编码,你可以使用编码参数,例如:

In [3]:
datasource = mapnik.Shapefile(file="/gdata/GSHHS_c.shp", encoding="latin1")

SQLite

SQLite的数据源允许包含来自地图上的SQLite(或SpatiaLite)数据库的数据。 mapnik.SQLite()函数接受大量的关键参数,以下这些可能是最有用的:

  • file="...":SQLite数据库文件的名称和可选路径。
  • table="...":在此数据库中所需要的表的名称。
  • geometry_field="...":表内的字段名称,它掌握要显示的几何形状。
  • key_field="...":表中关键字段的名称。

例如,在一个名为mapData.db数据库中访问名为countries 的表,可以使用以下命令:

In [4]:
# datasource = mapnik.SQLite(file="mapData.db", table="countries", geometry_field="outline", key_field="id")

在countries表中的所有字段都可用在Mapnik过滤器中,也可用TextSymbolizer进行展示。

各种symbolizers将使用存储在outline字段中的几何结构来绘制线条、多边形等等。

使用GDAL/OGR包进行数据的读取

GDAL

GDAL数据源允许您在地图中包含任何的GDAL兼容光栅图像数据文件,GDAL的数据源是直接进行使用的:

In [5]:
import mapnik
datasource = mapnik.Gdal(file="/gdata/foo.tif")

Mapnik提供了另一种阅读TIFF格式光栅图像的方法,使用栅格数据源。在一般情况下,使用GDAL数据源比栅格更加灵活,也更方便。

OGR

OGR数据源使你在地图上显示任何一个OGR兼容载体上的数据。 OGR数据源的便利构造至少需要两个命名参数:

datasource = mapnik.Ogr(file="...", layer="...")

file的参数就是 OGR 兼容的数据文件的名称, 而layer 则是在数据文件中被需要的图层的名字。例如,你通过 OGR 驱动读取一个 shape文件。

datasource = mapnik.Ogr(file="shapefile.shp", layer="shapefile")

更有用的是,你可以从OGR支持的任何矢量格式的数据文件来加载数据。

OGR加载虚拟数据表

我们特别感兴趣的是虚拟的DataSource(VRT)格式。 VRT格式是XML格式的文件,它允许你设置没有存储在盘上的一个简单的文件的OGR数据源。 在前面的章节中我们可以看到, 它是怎样在地图上显示来源于MySQL数据库中的数据, 事实上,Mapnik本身并没有执行一个MySQL的数据源。 虽然在OGR网页上已经解释的很透彻了,但是VRT文件格式还是比较复杂的。 下面是一个例子,你能使用VRT文件设置一个MySQL虚拟数据源:

<OGRVRTDataSource>
<OGRVRTLayer name="myLayer">
<SrcDataSource>MYSQL:mydb,user=user,password=pass,
tables=myTable</SrcDataSource>
<SrcSQL>
SELECT name,geom FROM myTable
</SrcSQL>
</OGRVRTLayer>
</OGRVRTDataSource>

<SrcDataSource>组成中包含了设置OGR MySQL数据源的字符串。这个字符串的格式为:

MySQL:<dbName>,user=<username>,password=<pass>,tables=<tables>

用数据库的名称代替<dbName>usernamepass是访问MySQL数据库的用户名和密码, tables是你要从数据库检索数据的列表。

如果你从多个数据表中检索数据,你需要用分号单独列出数据表,例如:

tables=lakes;rivers;coastlines

注意:在<SrcDataSource></SrcDataSource>之间的所有文本都必须是单独一行的。

<SrcSQL>组成里面的文本应该是一个MySQL的select语句, 它是从数据库表中检索出来所需要的信息。 对于PostGIS的数据源,在传递到Mapnik之前,你可以用它来过滤掉不需要的记录,这样将会显著提高性能。

VRT文件应该被保存到磁盘中。 例如,上述虚拟文件所定义的内容将会保存到一个文件名为myLayer.vrt的文本文件中。 然后你可以在Mapnik中用这个文件来定义OGR数据源,例如:

datasource = mapnik.Ogr(file="myLayer.vrt", layer="myLayer")

PointDatasource

PointDatasource对于Mapnik是一个相对比较新的功能。 点数据不是来源于数据文件,而是在程序运行的时候创建的数据。 它可以让你手动定义一些将要出现在地图上的数据点。 建立一个点数据源是非常简单的,即你只需创建PointDatasource对象:

datasource = mapnik.PointDatasource()

然后,你可以使用PointDatasource的 add_point()来添加单个点源。 这种方法需要四个参数:

  • long:点的经度。
  • lat:点的纬度。
  • key:点的属性名称。
  • value:点的属性值。

通常你会对每个数据点使用相同的key,并且为适当的字符串设置value, 然后你可以在地图上显示这些字符串。 例如:下面的代码,结合PointDatasource与TextSymbolizer在特定的经度或纬度的地图上放置标记。

Displaying points on the map(在地图上显示点)

generateMap()函数的特点就是它能生成一个点列表并直接在地图上显示它们,且无需将这些点存储到数据库中。这是通过PointDataSource的数据库的使用将点绘制到地图上的一个ShieldSymbolizer。 shieldsymbolizer绘制到地图上的点:

注意:point.png文件的路径是基于mapGenerator.py模块本身的位置(via the __file__ global), 作为一个绝对的路径进行计算的。 该模块可以被作为一个CGI脚本的一部分。CGI脚本没有一个当前工作目录。

In [6]:
#!/usr/bin/env python

import os
import mapnik



stylesheet = '/gdata/world_population.xml'

m = mapnik.Map(600,300)
mapnik.load_map(m, stylesheet)


m.background = mapnik.Color('steelblue')
s = mapnik.Style()
r = mapnik.Rule()

# polygon_symbolizer = mapnik.PolygonSymbolizer(mapnik.Color('#FF3366'))

polygon_symbolizer = mapnik.PolygonSymbolizer()
polygon_symbolizer.fill = mapnik.Color('#f2eff9')


r.symbols.append(polygon_symbolizer)

s.rules.append(r)
m.append_style('My Style2',s)

wkt_geom = 'POLYGON ((5.123456 21.6, -16.8 -52.6, 37.8 -21.6, 5.123456 21.6))'
csv_string = '''
 wkt,Name
"%s","test"
''' % wkt_geom
ds = mapnik.Datasource(**{"type":"csv","inline":csv_string})
# layer2 = mapnik.Layer('world', '+init=epsg:3857')
# 注意下面的投影,与地图一致。
layer2 = mapnik.Layer('world', '+proj=latlong +datum=WGS84')
layer2.datasource = ds
layer2.styles.append('My Style2')
m.layers.append(layer2)

bbox = mapnik.Box2d(-180.0,-90.0,180.0,90.0)
m.zoom_to_box(bbox)
# m.zoom_all()
mapnik.render_to_file(m,'xx_ds_pt.png', 'png')
# print "rendered image to 'world.png'"
In [7]:
from IPython.display import Image
Image('./xx_ds_pt.png')
Out[7]: