地图和图层对象的属性和方法。
import mapnik
map = mapnik.Map(600, 400 )
初始化Map对象需要3个参数Map(width, height, srs)
,即以像元为单位的地图宽度和高度,
和一个可选的Proj.4格式初始化字符串 srs
。
如果你不指定一个空间参照系,地图会使用未经过投影的 WGS84
基准面的经度或纬度坐标。
map.srs
'epsg:4326'
在创建完一个地图后,需要设置地图背景的颜色,然后通过调用map.append_style()
方法,将不同的样式添加到地图中。
style1, style2, style3 = [mapnik.Style()] * 3
map.append_style("s1", style1)
map.append_style("s2", style2)
map.append_style("s3", style1)
map.append_style("s1", style3)
上面第一行是使用 Mapnik的 Style()
实例化了三个样式对象。
需要注意,一个样式对象,可以赋给多个值;而在
map
对象中,如果已经有了命名的样式,则不能再添加。
layer = mapnik.Layer('lyrname')
layer.srs
'epsg:4326'
每一个图层都被给定一个特定的名字,并可以选择与它相关的空间参考。
空间参考的使用与
Map
对象一样,如果没有给定的空间参考系,在缺省情况下同样可以使用未经过投影的
WGS84 基准面的经度或纬度坐标。
如果已经创建了地图图层,给它分配一个数据源,并选择适用于该层的样式,通过名称识别每一个样式:
ds = mapnik.Shapefile(file='/data/gdata/GSHHS_c.shp')
layer.datasource = ds
layer.styles.append("s1")
layer.styles.append("s2")
一个图层,可以添加多个样式。
最后,将新图层添加到地图中。
map.layers.append(layer)
图层的顺序
Mapnik采用了 “画家算法” 来绘制形状和文字, 直接而显而易见的的结果是样式表上定义的图层顺序是十分重要的。 最先加入的图层先被绘制,后来加入的图层会绘制在其他图层的上方, 这一原则也可以通过“后来者居上”来辅助记忆。 按此规则,一段渲染地图的代码中,海洋和海岸线应该先出现,而旅游景点和街道名称,则可能会在后面定义。
数据源产生的顺序是非常重要的。如果你从 Shapefile 或者数据库中抽出道路,由此而来的顺序即是制图的顺序。 这是掌控地图外观的主要方法。
如果使用PostGIS绘制道路图, 按照几何长度降序排列可以使较长的道路最先被标注, 这样使得在没有空间标注的情况下,缩放的城市看上去更准确。
如果标注城镇,按人口多少降序排列,则可以使较大的城市首先被绘制, 较小的村落放到最后。结合Mapnik的冲突检测,绘制一张地图, 大的重要位置必定包含在内,小的位置仅在有空间的地方。 对于一个州或者一个国家的缩放图,这是最好的展现效果。
让我们来仔细的看一下一些可选的方法和Mapnik 的Map 和Layer的属性。 当你操作你的地图图层和设置地图图层和样式时这些会非常有用,这些将会选择性的应用基于地图当前的比例尺因子。
mapnik.Map
类别提供了几种有用的额外的方法和属性。
map.envelope()
这种方法返回代表需要显示的地图区域的mapnik.Envelope
对象。mapnik.Envelope
支持许多有用的方法和属性, 但是最重要的就是包含了minx
,miny
,maxx
和maxy
属性。 这些定义了地图边界框坐标。map.aspect_fix_mode = mapnik.aspect_fix.GROW_CANVAS
这控制了Mapnik怎样调整地图,如果地图边界的长宽比与要呈现的地图影像的长宽比不一致。支持以下值:GROW_BBOX
扩大地图的边界框来匹配将要生成地图影像的长宽比。这是一个默认的行为。GROW_CANVAS
扩大将要生成的地图来匹配地图边界框的长宽比。SHRINK_BBOX
缩小地图的边界框来匹配生成影响的长宽比。SHRINK_CANVAS
缩小要生成影像来匹配地图边界框的长宽比。ADJUST_BBOX_HEIGHT
扩大或缩小地图边界框的高度,在宽度保持恒定的同时,来匹配要生成图像的长宽比。ADJUST_BBOX_WIDTH
扩大或缩小地图边框的宽度,在保持高度一定的同时,来匹配要生成影像的长宽比。ADJUST_CANVAS_HEIGHT
扩大或缩小要生成地图的高度,在保持宽度一定的同时,来匹配多边形的边界框的长宽比。ADJUST_CANVAS_WIDTH
扩大或缩小要生成影像的宽度,在保持高度一定的同时,来匹配地图边框的长宽比。
map.scale_denominator()
返回当前用于生成地图的比例尺分母。比例尺分母取决于地图的边界和 要生成影像的大小。map.scale()
返回当前地图使用的比例尺因子,比例尺因子取决于地图的边界和要呈 影像,map.zoom_all()
设置地图的边界框来涵盖每个地图图层的边框。这样能确保所有的地图 数据出现在地图上。map.zoom_to_box(mapnik.Envelope(minX, minY, maxX, maxY))
将地图的边界框设置成给定的边界的大小。需要注意的是minX
,minY
,maxX
,maxY
都在地图的坐标系统中。
注:可选(了解你的数据)
你可以调用envelop()
函数关闭数据源查看完整的坐标范围数据:
import mapnik
map = mapnik.Map(600, 400 )
ds = mapnik.Shapefile(file='/data/gdata/GSHHS_c.shp')
ds.envelope()
Box2d(-180.0,-89.99999999999994,180.00000000000023,83.53036100000006)
这显示了数据的 (mainx, miny, maxx, maxy)
。 因为上述x坐标在
( − 180, 180)之间。 y或者是纬度值在( − 90, 90)之间。
我们知道这是地理坐标,使用度为单位。
不错的迹象,这是WGS84(又名EPSG:4326).
这个特殊的shapfile把投影信息存储成一个WKT字符串, 在
ne_110m_admin_0_countries.prj
file. 但是 Mapnik
需要通过Proj.4字符串知道这个具体的
通用的空间参考系统+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs
。
看下面的layer.srs值,了解为什么它是有关系的。
图层对象属性和方法
mapnik.Layer
类别有以下有用的属性和方法:
layer.envelope()
这种方法返回代表了包含所有地图图层数据的地图的矩形的区域mapnik.Envelope
对象。mapnik.Envelope
对象支持大量有用的方法和属性, 但是最重要的是包含minx
,miny
,maxx
和maxy
属性。layer.active = False
这可以用来在地图中隐藏图层。layer.minzoom = 1.0/100000
这用来设置如果该图层将要出现在地图上,必须应用的最小的比例尺因子。如果没有进行设置,该图层将没有一个小比例尺因子。layer.maxzoom = 1.0/10000
这设置的是如果图层将要在地图上绘制,必须应用的最大的比例尺因子。如果没有进行设置,该图层将没有一个大比例尺因子。layer.visible(1.0/50000)
这种方法返回True,如果该图层以给定的比例尺因子绘制在地图上。图层是可见的,如果它是活跃的并且给定的值在图层的最大和最小的值之间。
import os
import mapnik
stylesheet = '/data/gdata/world_map.xml'
m = mapnik.Map(600, 300)
mapnik.load_map(m, stylesheet)
m.zoom_all()
m.background = mapnik.Color('steelblue')
print(m.scale())
# fig_index += 1
mapnik.render_to_file(m, 'xx1.png', 'png')
chinabox = mapnik.Box2d(73, 0, 135, 54)
m.zoom_to_box(chinabox)
print(m.scale())
# fig_index += 1
mapnik.render_to_file(m, 'xx2.png', 'png')
for x in m.layers:
print(x.name)
print(x.envelope())
0.6000000000000004 0.18 world Box2d(-180.0,-89.99999999999994,180.00000000000023,83.53036100000006)
使用颜色
许多的Mapnik的symbolizers要求提供一个颜色的值。这些颜色值是使用mapnik.Color类别进行定义的。mapnik.Color能够通过以下方式进行定义:
mapnik.Color(r, g, b, a)
: 创建一个颜色对象通过提供单独的红色、绿色、蓝色和α(不透明度)值,每一个值的范围是0到255.mapnik.Color(r, g, b)
: 创建一个颜色对象通过提供红色、绿色、蓝色的组成。每一个值都应该在0到255之间。结果的对象是完全不透明的。mapnik.Color(colorName)
: 创建一个颜色对象通过制定一个标准的CSS颜色的名称 [1]。mapnik.Color(colorCode)
: 创建一个颜色对象通过使用HTML颜色代码。例如,#806040
是中度棕色。
地图晕渲
在创建mapnik.Map对象和设置各种各样的symbolizers、 规则、样式、数据源和其中的图层之后,需要将图像转换成彩色图像。
Mapnik支持多咱渲染后端方式。
Cairo渲染器是Mapnik的辅助渲染器。
Cairo被加到r656中也是因为其图像输出到不同的格式中去。
Cairo还有一个增加的优势,即是支持矢量和栅格的输出。
Mapnik可以渲染cairo支持的任何表面,也可以直接生成或是渲染到cairo 上下文对象中。
你可以使用OSM输出工具演示PNG,JPEG,SVG,PDF和PS格式。
Cairo在Mapnik Scons创建过程是可选的,但是如果发现就会自动启用(使用 pkg-config)。
pkg-config必须能找到 libcairo
,Cairomm(C++ bindings) 和 Pycairo (python bindings)。
Python示例代码:用Mapnik的Cairo渲染器到SVG中。
import mapnik
import cairo
import os
mapfile = '/data/gdata/world_population.xml'
projection = '+proj=latlong +datum=WGS84'
mapnik_map = mapnik.Map(1000, 500)
mapnik.load_map(mapnik_map, mapfile)
bbox = mapnik.Box2d(-180.0,-90.0,180.0,90.0)
mapnik_map.zoom_to_box(bbox)
mapnik.render_to_file(mapnik_map, 'xx_a.png', 'png')
--------------------------------------------------------------------------- RuntimeError Traceback (most recent call last) Cell In[12], line 15 12 bbox = mapnik.Box2d(-180.0,-90.0,180.0,90.0) 13 mapnik_map.zoom_to_box(bbox) ---> 15 mapnik.render_to_file(mapnik_map, 'xx_a.png', 'png') RuntimeError: Shape Plugin: no attribute 'NAME' in '/data/gdata/GSHHS_c'. Valid attributes are: id,level,source,parent_id,sibling_id,area,Shape_Leng,Shape_Area.
下图是生成的结果:
Write to SVG
surface = cairo.SVGSurface('xx_a.svg', mapnik_map.width, mapnik_map.height)
mapnik.render(mapnik_map, surface)
surface.finish()
--------------------------------------------------------------------------- RuntimeError Traceback (most recent call last) Cell In[13], line 2 1 surface = cairo.SVGSurface('xx_a.svg', mapnik_map.width, mapnik_map.height) ----> 2 mapnik.render(mapnik_map, surface) 3 surface.finish() RuntimeError: Shape Plugin: no attribute 'NAME' in '/gdata/GSHHS_c'. Valid attributes are: id,level,source,parent_id,sibling_id,area,Shape_Leng,Shape_Area.
Or write to PDF
surface = cairo.PDFSurface('xx_a.pdf', mapnik_map.width, mapnik_map.height)
mapnik.render(mapnik_map, surface)
surface.finish()
Mapnik LOG> 2024-04-10 15:20:19: feature_style_processor: Style=countries_label required for layer=countries does not exist.
提示:Cairo可以写到PostScript或者是其他的图像格式。
mapnik.render()
可以生成到Cairo Context中。
渲染结果输出
在呈现出一幅地图图像之前,确定已经为地图设置合适的区域边界框,
这样就可以展现出世界地图中你感兴趣的区域。
你可以通过调用map.zoom_to_box()
来用一组给定的坐标明确的设置地图的边界框来做这件事,
或者你也可以调用map.zoom_all()
来让地图自动的设置它的边界基于将要显示的数据。
一旦你已经设置了边界框,你可以通过调用render_to_file()
函数生成你的地图,如下所示:
mapnik.render_to_file(map, 'map.png')
参数是mapnik.Map的对象和用来编写地图的影像文件的名称。
这一点是非常重要的。不然仅仅是地图渲染的非常华丽, 但错误的范围或缩放级别却会导致失败的产品。
如果你想要更多的控制影像的格式,你可以添加定义影像格式的额外的参数。
mapnik.render_to_file(map, 'map.png', 'png256')
输出支持的图片格式包括:
Image Format | Description |
---|---|
png | A 32-bit PNG format image |
png256 | An 8-bit PNG format image |
jpeg | A JPEG-format image |
svc | An SVG-format image |
A PDF file | |
ps | A postscript format file |
地图制图的投影问题
微软,雅虎,谷歌,OpenStreetMap,以及作者开发的 OSGeo 中地图模块( https://www.osgeo.cn/map/ )等都是使用的墨卡托投影, 是全球覆盖的理想方式。
墨卡托投影的一个简化特征是基于一个清晰的球形的地面输出,使得 JavaScript 或 ActionScript 客户端计算起来更容易。大部分Modest Maps都基于这种假设, 恰当定义你地图输出的SRS是:
+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0
+k=1.0 +units=m +nadgrids=@null +no_defs
为样式定义的最大或最小缩放尺度设置一些明智的快捷键。
import os
import mapnik
# from helper import get_tmp_file
# mapnik.Color('y')
# m = mapnik.Map(600, 300, '+init=epsg:4326')
#
m = mapnik.Map(600, 300, '+init=epsg:4326')
m.srs = 'epsg:4326'
# help(mapnik.Map)
# m.srs = '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs'
m.background = mapnik.Color('steelblue')
s = mapnik.Style()
r = mapnik.Rule()
# polygon_symbolizer = mapnik.PolygonSymbolizer(mapnik.Color('#f2eff9'))
polygon_symbolizer = mapnik.PolygonSymbolizer()
polygon_symbolizer.fill = mapnik.Color('#f2eff9')
# polygon_symbolizer = mapnik.PolygonSymbolizer(mapnik.Color('blue'))
r.symbols.append(polygon_symbolizer)
# line_symbolizer = mapnik.LineSymbolizer(mapnik.Color('rgb(50%,50%,50%)'),0.1)
line_symbolizer = mapnik.LineSymbolizer()
line_symbolizer.stroke = mapnik.Color('rgb(50%,50%,50%)')
line_symbolizer.stroke_width = 0.1
r.symbols.append(line_symbolizer)
s.rules.append(r)
m.append_style('My Style', s)
lyr = mapnik.Layer('world', "+proj=latlong +datum=WGS84")
lyr.datasource = mapnik.Shapefile(file='/data/gdata/GSHHS_c.shp')
lyr.styles.append('My Style')
m.layers.append(lyr)
m.zoom_to_box(lyr.envelope())
# mapnik.render_to_file(m, 'xx_world_fk.png', 'png')
mapnik.render_to_file(m, 'xx_b.png', 'png')
下面的生成的结果: