24 Oct 2016
什么是地图?
通俗的说地图就是地球的画像。
什么是地图渲染?
地图渲染给地图的画像的过程,就是按照一定的比例关系、投影方法,使用一定的符号,把现地球上的山川、道路、建筑等信息在纸或屏幕上描绘出来。
互联网地图的表达方式
投影
球面到平面。
假设地图中心有一个灯,用一张纸沿着地球赤道包裹起地球,这样地球上每个点都会映射到纸上,这样就得到摩卡拖坐标系。
X轴:由于赤道半径为6378137米,则赤道周长为2*PI*r = 2*20037508.3427892
,因此X轴的取值范围:[-20037508.3427892,20037508.3427892]
。
Y轴:由墨卡托投影的公式可知,同时上图也有示意,当纬度接近两极,即90°时,y值趋向于无穷。为了便于计算,“懒惰的工程师”就把Y轴的取值范围也限定在[-20037508.3427892,20037508.3427892]
之间,搞个正方形。
切片与分层
为了避免一次加载全部地图数据,实现按需加载地图,这里需要对地图进行分层和切片。
地图渲染的过程
- 获取数据
- 数据预处理
- 配置样式
- 渲染
获取数据
数据包括什么
地图使用点、线、面描绘现实世界,目前地图数据主要包括
- 背景数据
- 道路数据
- POI(兴趣点)数据
- 门址数据
- 建筑物数据
- 行人导航数据
- 公共交通数据
- 等
如何获取数据
- 购买商业数据
- 基础数据:高德,四维图新,TomTom
- 实时路况:世纪高通,高德,北大千方
- 卫星图:四维、DigitalGlobe
- 室内地图:图聚,高德,蜂鸟,indoorAtlas
- 自己采集数据
- 使用开源数据 OSM
- 抓取数据
- 数据挖掘
数据预处理
- 确定各种数据的在那些级别显示
- 合并由于跨图幅而被切割掉的几何对象
- 生成线和面的标注
- 数据抽稀,在不影响显示效果的前提下减少数据中点的个数
- POI Rank
- POI名称简称
- 标注数据的避让预处理,过滤掉大量无法显示的标注
- 处理立交效果
- 多来源数据的融合与去重
数据抽稀 道格拉斯-普克算法
- 在曲线首尾两点间虚连一条直线,求出其余各点到该直线的距离。
- 选其最大者与阈值相比较,若大于阈值,则离该直线距离最大的点保留,否则将直线两端点间各点全部舍去。
- 依据所保留的点,将已知曲线分成两部分处理,重复第1、2步操作,迭代操作,即仍选距离最大者与阈值比较,依次取舍,直到无点可舍去,最后得到满足给定精度限差的曲线点坐标。
POI Rank
- 根据类型配置,通常是一些经验值,比如火车站就比餐馆更重要。
- 根据父子关系计算。
- 根据用户的搜索量。
几何对象相识度
面相似度 = A交B面积 / A并B面积
点和线也可以通过生成缓冲区转化成面来计算
配置样式
样式是来定义地图包含那些图层,这些图层使用哪些数据,在哪些层显示,怎么显示的。一个简单的样式示例如下
<Map background-color="blue" srs="+init=epsg:4326">
<Style name="My Style">
<Rule>
<PolygonSymbolizer fill="#f2eff9" />
<LineSymbolizer stroke="rgb(50%,50%,50%)" stroke-width="0.1" />
</Rule>
</Style>
<Layer name="world" srs="+init=epsg:4326">
<StyleName>My Style</StyleName>
<Datasource>
<Parameter name="file">path/to/shapefile.shp</Parameter>
<Parameter name="type">shape</Parameter>
</Datasource>
</Layer>
</Map>
渲染
渲染栅格瓦片
渲染的过程
- 逐个层次,逐个瓦片进行渲染(1到5步)。
- 从样式中逐个获取图层。
- 读取图层在当前瓦片范围的数据,并进过平移缩放转化成瓦片上的坐标。
- 准备画布画笔画刷等,逐条绘制几何数据。
- 最终生成渲染好的栅格瓦片地图。
- 并发布成地图服务。
最终提供瓦片地图服务(Tile Map Service)。前端根据当前的位置,计算需要哪些瓦片的数据,下载这些瓦片进行拼接。
并行渲染
由于瓦片的数量非常的多,如果切18级一共包括14亿多个小瓦片。这是非常巨大的运算量。由于每个渲染瓦片都是相对独立的,因此可以并行计算。同时为了减少计算的次数,可以一次渲染多个(16*16)瓦片。
我们当时使用Hadoop来进行并行渲染,也可以使用RabbitMQ这样的消息队列实现。Map对原始数据进行切片,Reduce进行渲染栅格瓦片。使用2000个节点、全国切图用时在5个小时以内,生成的瓦片在180G样子。
遇到的问题
- 难以得到最优化标注
- 卡在切割一个特别大面的单节点上。
- 瓦片边缘出现半个字。
- 线的在瓦片边缘接头处显示不全。
解决办法
演进到矢量瓦片
由于栅格瓦片使用的数据流量大,不支持地图的旋转、3维效果,难以支持多种地图样式,加上前端的渲染性能已逐步提高,目前绝大多数的App已经是前端渲染。
与后端渲染不同的是,前端渲染在切图的过程中并不会生成栅格瓦片,而是生成矢量瓦片。然后由前端根据样式文件通过OpenGL ES或WebGL完成渲染。
矢量瓦片格式
矢量瓦片文件采用Google Protocol Buffers进行编码。它包括若干图层,每个图层包含若干要素。要素是由几何图形和属性信息组成的。
几何图形编码
使用瓦片坐标系,把浮点型坐标转化成整形坐标。瓦片的左上角是坐标系的原点。X轴向右为正,Y轴向下为正。
一个简单的矢量瓦片示例
layers {
version: 2
name: "points"
features: {
id: 1
tags: 0
tags: 0
type: Point
geometry: 9
geometry: 2410
geometry: 3080
}
keys: "hello"
values: {
string_value: "world"
}
extent: 4096
}
OpenGL渲染
难点
- 只支持渲染三角形,多边形需要自己进行三角化。
- 而对与线需要自己实现抗锯齿。
- 同时显示对于文字需要自己使用纹理贴图实现。
数据更新
由于一次渲染耗时耗时还是相对较长的。在对于新高速通车、新地铁通车等实时性更新要求比较高的情况下,可以增量更新部分区域的瓦片。如果数据有定期的增量更新包(Diff数据),也可以实现实时更新的地图。
08 Oct 2016
Osmosis 是一个用Java开发的命令行工具,用来处理 OpenStreetMap 的地图数据。
pbf转成xml
osmosis --read-pbf beijing.pbf --write-xml beijing.osm
只导出餐馆
osmosis --read-xml input.osm --tf accept-nodes amenity=restaurant --write-xml output-nodes.osm
只导出道路
osmosis --read-xml city.osm --tf accept-ways highway=* --used-node --write-xml highways.osm
只导某个范围的数据
osmosis --read-xml file=beijing.osm --bounding-box top=40.6306 left=115.5322 bottom=39.4871 right=117.4164 --write-xml file=beijing_out.osm
生成osmchange文件
osmosis --read-xml file="planet1.osm" --read-xml file="planet2.osm" --derive-change --write-xml-change file="planetdiff-1-2.osc"
osmosis --read-xml file="planet1.osm" --read-apidb host="x" database="x" user="x" password="x" --derive-change --write-xml-change file="planetdiff-1-2.osc"
osmosis --read-apidb-change host="x" database="x" user="x" password="x" validateSchemaVersion="no" intervalBegin="2016-09-27_00:00:00" --write-xml-change file="planetdiff-1-2.osc"
使用osmchange更新
osmosis --read-xml-change file="planetdiff-1-2.osc" --read-xml file="planet1.osm" --apply-change --write-xml file="planet2.osm"
清空数据库
osmosis --truncate-apidb host="x" database="x" user="x" password="x" validateSchemaVersion="no"
导入数据库
osmosis --read-pbf file=beijing.pbf --write-apidb host="x" database="x" user="x" password="x" validateSchemaVersion="no"
数据库导出
osmosis --read-apidb host="x" database="x" user="x" password="x" --write-pbf file=beijing.pbf
生成replicate数据
mkdir replicate
cd replicate
osmosis --replicate-apidb host="x" database="x" user="x" password="x" validateSchemaVersion="no" iterations=0 minInterval=60000 --write-replication
iterations是执行次数,0是无限循环,minInterval是间隔时间,单位是毫秒。
然后把replicate目录发布成http服务,这样就实现了http://planet.openstreetmap.org/replication/minute/
使用replicate数据更新
初始化更新配置
mkdir WORKDIR
cd WORKDIR
osmosis --read-replication-interval-init
这里会生成configuration.txt文件,修改其中的BaseURL
baseUrl=http://download.geofabrik.de/asia/china-updates/
下载当天的state.txt
wget http://download.geofabrik.de/asia/china-updates/state.txt
以后每天执行更新
osmosis --read-replication-interval --simplify-change --write-xml-change changes.osc.gz
osm2pgsql --append --slim -C 100 --number-processes 1 changes.osc.gz
详细说明见http://wiki.openstreetmap.org/wiki/Osmosis#Example_usage
01 Sep 2015
mapnik真是做地图渲染的神器,搭配mod_tile便能轻松的发布地图服务。
这是使用高德地图的原始数据,通过TileMill简单配置得到效果。
04 Dec 2014
今天发现一个非常易用的vpn软件instavpn。使用非常简单,用过的都说好。
the most user-friendly L2TP/IPsec VPN server
通过ssh连接上的我的ec2主机。
安装instavpn
curl -sS https://sockeye.cc/instavpn.sh | sudo bash
然后没有然后了,这已经安装完成了。
现在可以通过http://IP-ADDRESS:8080
访问web的管理界面。这里可以监控流量使用情况、配置用户。
由于ec2有Security Groups的权限控制。在连接之前,需要把L2TP和web管理工具使用的相关端口打开,分别是UDP 500 1701 4500和TCP 8080。
现在就可以连接我们的VPN了。
10 Nov 2014
为了简化配置,直接添加别人发布的软件仓库,里面包含发布地图服务需要的一切软件。
sudo add-apt-repository ppa:kakrueger/openstreetmap
更新apt
sudo apt-get update
安装libapache2-mod-tile
sudo apt-get install libapache2-mod-tile
这里会进行配置并创建数据库,需要注意的是,导入数据是需要使用这个用户操作。如果没有配置不对,可以通过sudo dpkg-reconfigure openstreetmap-postgis-db-setup
重新配置。我这里加上了当前用户,如www-data,ubuntu。
下载osm数据,全球数据可以到https://planet.openstreetmap.org下载,同时http://download.geofabrik.de为我们提供了不同国家的小数据。我只下载了中国的
wget http://download.geofabrik.de/asia/china-latest.osm.pbf
导入数据库,如果数据大,这一步会非常耗时。调整osm2pgsql参数可加快速度。
这里使用的EC2主机,分配给100M内存和1个处理器,大概用十几分钟。由于EC2性能太低,不得已加上--cache-strategy sparse
参数。
osm2pgsql --slim -C 100 --cache-strategy sparse --number-processes 1 china-latest.osm.pbf
设置数据导入时间
sudo touch /var/lib/mod_tile/planet-import-complete
重启渲染服务
sudo /etc/init.d/renderd restart
测试
http://localhost/osm/slippymap.html
http://localhost/osm/0/0/0.png
PS: 如果地图显示不出来,查看渲染的日志
tail -f /var/log/syslog |grep renderd
数据更新
mod-tile中提供了增量数据更新工具openstreetmap-tiles-update-expire。试用出现不少问题,并没能成功。
下载增量数据,https://planet.openstreetmap.org/replication/
可以找到每天、没小时和每分钟的增量更新包。部分区域每天更新的更新包可以从http://download.geofabrik.de/asia/china-updates/
下载。
安装osmosis
初始化更新配置
mkdir WORKDIR
cd WORKDIR
osmosis --read-replication-interval-init
这里会生成configuration.txt文件,修改其中的BaseURL
baseUrl=http://download.geofabrik.de/asia/china-updates/
下载当天的state.txt
wget http://download.geofabrik.de/asia/china-updates/state.txt
以后每天执行更新
osmosis --read-replication-interval --simplify-change --write-xml-change changes.osc.gz
osm2pgsql --append --slim -C 100 --cache-strategy sparse --number-processes 1 changes.osc.gz
sudo touch /var/lib/mod_tile/planet-import-complete
重新下载osm数据并导入数据。
设置数据导入时间
sudo touch /var/lib/mod_tile/planet-import-complete
PS:如果地图没有更新,可调用render_old重新渲染旧切片。同时把浏览器的缓存清除掉。
参考
http://switch2osm.org/serving-tiles/building-a-tile-server-from-packages/
http://blog.csdn.net/goldenhawking/article/details/7952303