Menu

  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay
  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay

基于OpenStreetMap的地图服务器的搭建

13
Jan
2015

基于OpenStreetMap的地图服务器的搭建

By Alex
/ in GIS
/ tags iD, Leaflet, Mapnik, OpenLayers, OSM, PostGIS, TileStache
17 Comments
GIS基础知识
GIS基本组件
  1. 地图数据库:提供地图数据,例如OSM的planet.osm可以导入PostgreSQL作为地图数据库
  2. 瓦片服务器:负责生成一系列的瓦片(tiles),这些瓦片通常为256像素的方块,瓦片组在一起形成地图。对于Google Map,瓦片服务器是Google提供的,用户不能接触其地图数据库;对于OSM,地图数据库是开放的,可以随意下载,需要自己搭建瓦片服务器
  3. 前端API:用于浏览器的JavaScript API,或者用于移动客户端的同功API。例如OpenLayers、Leaflet。
坐标参考系统(Coordinate Reference Systems)

又称空间参考系统(spatial reference system,SRS),是基于二维坐标的,用于定位局部、区域或者全球地理信息资源的体系。CRS定义了一种地图映射规则、与其它CRS之间进行转化的算法。CRS是GIS系统的基础。

最常用的两种CRS为:

 CRS 说明 
EPSG:4326

以经纬度直接作为X(经度)、Y(维度)坐标,南纬、西经采用负数表示

投影范围: -180.0000, -85.0600, 180.0000, 85.0600

EPSG:3857

主要用于Web地图等应用程序,最初由Google地图提出,故经常称为900913,基于椭圆形墨卡托(ellipsoidal Mercator )投影

投影范围: -180.0000, -90.0000, 180.0000, 90.0000

OpenStreetMap基础知识
OpenStreetMap的优势
  1. 地图数据开源,人人可以编辑,并且可以完整的下载,部署私有的地图服务器
  2. 内容丰富,比起ESRI Shapefiles的点、面、线,支持更多复杂的元素
  3. 生态圈活跃,从地图数据、数据库、地图渲染、瓦片服务器、前端API,到桌面、Web地图设计工具,具有大量优秀的开源组件
OpenStreetMap生态圈
组件类型 组件名称  说明
地图数据 planet.osm

提供开放的、人人可编辑的全球地图数据。使用osm2pgsql可以导入空间数据库

整个世界的地图解压后有500G。可以去下载部分地区、国家、城市的抽取。

空间数据库 PostgreSQL 一个强大的对象——关系型数据库 
PostGIS PostgreSQL的一个扩展,提供空间、地理对象
瓦片生成器 Mapnik 地图渲染引擎,采用C++编写,具有Python等语言的绑定。支持多种地图格式,包括SHP、PostGIS、Kismet、OSM XML。可以生成瓦片
瓦片服务器
(HTTP、瓦片缓存)
TileStache 基于Python,可作为Mapnik的前端,调用Mapnik生成瓦片并缓存,并可对外提供HTTP服务
TileCache 基于Python,可作为Mapnik的前端,支持多种瓦片请求协议,包括WMS、WorldWind、TMS 
Apache (mod_tile) Apache Server的一个模块,可以调用Mapnik,OSM官方就是使用这种方式
一般工具 Osmosis

基于Java的处理OSM数据的命令行工具,可以:

  1. 从数据库创建地图Dump
  2. 把地图Dump加载到数据库
  3. 基于数据库历史表生成变更集(Change sets)
  4. 把变更集应用到数据库
  5. 根据两个地图Dump生成变更集
  6. 重新排序Dump中的数据
  7. 根据多边形边界框来抽取地图数据
osmium

基于C++、JavaScript的处理OSM数据的框架

osm2pgsql

基于C++的导入OSM到PostgreSQL的命令行工具

地址坐标转换
(geocoding)

 

Nominatim

OpenStreetMap官方的地址坐标转换服务,硬件要求很高

OpenCage

可以提供基于Nominatim的地址坐标转换API

路径规划
(Routing)
OSRM

The Open Source Routing Machine,基于C++实现的高性能路径规划引擎

Graphhopper 基于Java的路径规划引擎
前端API MapQuest Open API 与MapQuest Open tiles联用,提供routing(路径规划)、geocoding(地址坐标转换)等特性
OpenLayers 成熟、强大
Leaflet 轻量级、简单易用
Route-Me IOS的前端API(已很久不更新)
osmdroid Android的前端API
Mapstraction 一个JavaScript抽象层,可以在不改变代码的情况下切换使用不同类型的瓦片服务器
搭建地图服务器
Windows下搭建OSM服务器的详细步骤

目前网络上的例子,大多是在Linux下搭建OSM服务器。实际项目中由于客户现场环境的限制,可能必须使用Windows Server,故本文详细记录Windows下的搭建步骤,供各位参考。

系统架构

osm_arch-1024x879

上图中红色部分为本文主要使用的组件,我们把这些组件全部安装到一个目录%OSM_STACK%下

下载地图

区域地图,可从这里下载:http://download.geofabrik.de/

全球地图,可从这里下载:http://ftp.heanet.ie/mirrors/openstreetmap.org/planet/2015/planet-150105.osm.bz2

安装Python

安装Python 2.7.x到%OSM_STACK%\python,并加入PATH环境变量,下载地址:https://www.python.org/ftp/python/2.7.9/python-2.7.9.msi

为避免后续需要下载依赖的模块,可以安装便携版的Python,集成了很多常用模块,安装后把App目录里面的所有文件拷贝到%OSM_STACK%\python即可,下载地址:http://ftp.osuosl.org/pub/portablepython/v2.7/PortablePython_2.7.6.1.exe

安装PostgreSQL和PostGIS

最好下载PostgreSQL 9.0以上或者 8.3版,8.4存在性能问题。

9.4的下载地址:http://get.enterprisedb.com/postgresql/postgresql-9.4.0-1-windows-binaries.zip

解压到%OSM_STACK%\psql

下载PostGIS:http://download.osgeo.org/postgis/windows/pg94/postgis-bundle-pg94x32-2.1.5-2.zip

解压覆盖到%OSM_STACK%\psql

注意:上述的PostgreSQL是绿色版的,对MSVC12有C运行时库、C++标准库有依赖,如果你的机器缺少msvcp120.dll、msvcr120.dll这两个DLL,可以下载:Visual C++ Redistributable Packages for Visual Studio 2013并安装,亦可直接拷贝这两个文件到%OSM_STACK%\psql\bin下

下面的脚本说明如何初始化PostgreSQL、创建用户gisuser、数据库gis、激活PostGIS:

MS DOS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@echo off
 
pushd "%~dp0"
 
SET "PGDATA=%PSQL_HOME%\data"
SET "PGDATABASE=postgres"
SET "PGUSER=postgres"
SET "PGPORT=5439"
SET "PGLOCALEDIR=%PSQL_HOME%\share\locale"
rem GIS databse name and owner
SET "GISDATABASE=gis"
SET "GISUSER=gisuser"
 
echo ****** Prepare to initializing PostgreSQL  ******
rem 初始化数据库,这将生成data目录,并且根据机器硬件环境生成配置文件
initdb -U %PGUSER% -A trust
 
echo ****** Prepare to start PostgreSQL  ******
rem 启动数据库,日志记录到pgsql.log,等待启动完成
pg_ctl -w -l %PSQL_HOME%\pgsql.log start
 
echo ****** Creating user %GISUSER%  ******
rem 创建GIS专门用户
createuser -U %PGUSER% %GISUSER%
echo ****** Creating database %GISDATABASE%  ******
rem 创建GIS专用数据库
createdb -U %PGUSER% -E UTF8 -O %GISUSER% %GISDATABASE%
echo ****** Installing procedural language into %GISDATABASE%  ******
rem 添加扩展
createlang -U %PGUSER% plpgsql %GISDATABASE%
echo ****** Activating PostGIS for %GISDATABASE%  ******
rem 为GIS数据库激活PostGIS支持
psql -U %PGUSER% -d %GISDATABASE% -f "%PSQL_HOME%\share\contrib\postgis-2.1\postgis.sql"
psql -U %PGUSER% -d %GISDATABASE% -f "%PSQL_HOME%\share\contrib\postgis-2.1\spatial_ref_sys.sql"
rem After the activation, the following command should list the tables geometry_columns and spatial_ref_sys:
rem 下面这一句不能在UTF-8代码页下运行,会报内存不足的错误
psql --username=%GISUSER% --dbname=%GISDATABASE% --command="\d"
rem 停止数据库
pg_ctl -w stop

打开%OSM_STACK%\psql\data\postgresql.conf,修改以下参数,以提高性能(根据硬件配置调整):

INI
1
2
3
4
5
6
shared_buffers = 512MB  
checkpoint_segments = 20  
maintenance_work_mem = 256MB  
autovacuum = off  
 
kernel.shmmax=536870912
导入地图到数据库

使用命令行工具osm2pgsql 可以把OpenStreetMap地图数据导入到启用了postGIS的PostgreSQL数据库中。尽管mapnik可以直接渲染OSM XML原始数据,但是导入PostGIS可以使用更多的高级特性。

从这里下载此工具:http://customdebug.com/osm/osm2pgsql.zip

使用示例:

MS DOS
1
osm2pgsql -c -d gis -U postgres -H localhost -P 5439 -S default.style  -C 600 beijing.osm.bz2

default.style是 导入时需要的样式定义文件,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# OsmType  Tag          DataType     Flags
node,way   access       text         linear
node,way   addr:housename      text  linear
node,way   addr:housenumber    text  linear
node,way   addr:interpolation  text  linear
node,way   admin_level  text         linear
node,way   aerialway    text         linear
node,way   aeroway      text         polygon
node,way   amenity      text         polygon
node,way   area         text        
node,way   barrier      text         linear
node,way   bicycle      text
node,way   brand        text         linear
node,way   bridge       text         linear
node,way   boundary     text         linear
node,way   building     text         polygon
node       capital      text         linear
node,way   construction text         linear
node,way   covered      text         linear
node,way   culvert      text         linear
node,way   cutting      text         linear
node,way   denomination text         linear
node,way   disused      text         linear
node       ele          text         linear
node,way   embankment   text         linear
node,way   foot         text         linear
node,way   generator:source    text  linear
node,way   harbour      text         polygon
node,way   highway      text         linear
node,way   historic     text         polygon
node,way   horse        text         linear
node,way   intermittent text         linear
node,way   junction     text         linear
node,way   landuse      text         polygon
node,way   layer        text         linear
node,way   leisure      text         polygon
node,way   lock         text         linear
node,way   man_made     text         polygon
node,way   military     text         polygon
node,way   motorcar     text         linear
node,way   name         text         linear
node,way   natural      text         polygon
node,way   office       text         polygon
node,way   oneway       text         linear
node,way   operator     text         linear
node,way   place        text         polygon
node       poi          text
node,way   population   text         linear
node,way   power        text         polygon
node,way   power_source text         linear
node,way   public_transport text     polygon
node,way   railway      text         linear
node,way   ref          text         linear
node,way   religion     text         nocache
node,way   route        text         linear
node,way   service      text         linear
node,way   shop         text         polygon
node,way   sport        text         polygon
node,way   surface      text         linear
node,way   toll         text         linear
node,way   tourism      text         polygon
node,way   tower:type   text         linear
way        tracktype    text         linear
node,way   tunnel       text         linear
node,way   water        text         polygon
node,way   waterway     text         polygon
node,way   wetland      text         polygon
node,way   width        text         linear
node,way   wood         text         linear
node,way   z_order      int4         linear
way        way_area     real
安装mapnik

从http://mapnik.org/download/下载mapnik,解压到%OSM_STACK%\mapnik,将其bin、lib目录加入PATH环境变量,python\2.7\site-packages加入PYTHONPATH环境变量。

在mapnik中,一个Map可以包含若干个图层(Layer),每个层可以独立着色,即可为每个层定制样式(Style),每个样式由若干个规则组成(Rule),每个规则由是由若干个符号定制。

生成mapnik的样式文件

从https://www.mapbox.com/tilemill/下载并安装TileMill,该工具用于编辑地图元素的样式,我们主要用它导出mapnik的样式文件。

OpenStreetMap官方使用的样式托管在GitHub上:https://github.com/gravitystorm/openstreetmap-carto。下载后解压到C:\Users\用户名\Documents\MapBox\project

打开Cygwin,或者在Linux下执行get-shapefiles.sh,下载并处理openstreetmap-cart中缺少的data目录:

Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#以Cygwin为例
 
apt-cyg install curl
apt-cyg install unzip
 
#编译gdal,在Ubuntu下只需要sudo apt-get install gdal-bin即可
#你也可以到http://download.gisinternals.com/下载编译好的二进制文件供Cygwin使用
wget http://download.osgeo.org/gdal/1.11.1/gdal-1.11.1.tar.gz
tar vzxf gdal-1.11.1.tar.gz
rm gdal-1.11.1.tar.gz
cd gdal-1.11.1/
./autogen.sh
./configure --host=mingw32 --without-libtool --without-python
make
make install
 
cd /cygdrive/c/Users/Suigintou/Documents/MapBox/project/openstreetmap-carto-master
./get-shapefiles.sh
#需要下载的数据较多,耐心等待
#如果出现Failed to connect to planet.openstreetmap.org port 80: Network is unreachable,则添加下一行到host文件
#193.63.75.107 planet.openstreetmap.org

下载、处理完成后,打开TileMill,可以看到OpenStreetMap Carto这个项目,点击打开,然后点击右上角按钮,导出mapnik样式文件,如下图:1

导出成功后,打开XML文件,搜寻里面的类似:“C:\Users\用户名\Documents\MapBox\project\osm-carto\……”绝对路径,将其删除,并把路径中剩余部分的反斜杠改为正斜杠,修改完毕后,路径类似:“data/simplified-land-polygons-complete-3857/simplified_land_polygons.shp”,把文件移动到%OSM_STACK%\osm-carto目录下。

把整个openstreetmap-carto-master目录(就是TileMill编辑的工程)覆盖到%OSM_STACK%\osm-carto。

安装和配置TileStache

安装便携版Python后,只需要执行下面的脚本:

MS DOS
1
2
3
4
5
6
easy_install tilestache
 
rem 可能需要检查以下依赖模块是否安装
easy_install PIL
easy_install ModestMaps
easy_install SimpleJSON

在Windows上,如果使用的是Python 2.7.6,需要修改一下__init__.py的源码,否则运行时会报错:UnicodeDecodeError: utf8 codec can't decode byte 0xb0 in position 1: invalid start byte,这是Python的一个BUG。

%OSM_STACK%\python\Lib\site-packages\tilestache-1.49.11-py2.7.egg\TileStache\__init__.py
Python
1
2
3
4
import sys
reload(sys)
sys.setdefaultencoding('gb18030') #添加上面三行即可
config_dict = json_load(urlopen(configpath))

安装完毕后,通过下面的脚本即可启动TileStache的Web服务(基于 Werkzeug,一个WSGI工具库):

MS DOS
1
2
3
4
echo ****** Prepare to start TileStache  ******
SET "TILE_STACHE_SCR=%PYTHON_HOME%\Scripts\tilestache-server.py"
 
python "%TILE_STACHE_SCR:\=/%"   -p 5539 -c tilestache.cfg

其中tilestache.cfg是TileStache使用的配置文件,我们先使用下面这个做测试:

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
  "_comment": "tilestache.cfg包含两个顶级元素,分别实现缓存、图层的配置"
  "cache":
  {
    "name": "Disk",
    "path": "F:/Temp/tiles-cache"
  },
  "layers":
  {
    "osm":
    {
        "provider": {"name": "proxy", "provider": "OPENSTREETMAP"}
    }
  }
}

启动TileStache后,可以通过以下几个URL来测试:

 URL 说明 
http://127.0.0.1:5539/osm/preview.html 交互式的地图预览
http://127.0.0.1:5539/osm/0/0/0.png

显示一个静态瓦片,TileStache的URL风格类似于Google地图:

http://host:port/{layer name}/{zoom}/{column}/{row}.{extension} 

集成TileStache与mapnik

 下表列出集成mapnik时,tilestache.cfg可用的配置项:

 JSON路径/属性 说明 
/cache
name

缓存的类型,可以是:

  1. Test:只是进行日志记录,不真正缓存,额外属性:
    1. verbose:记录冗长的日志
  2. Disk:缓存到本地磁盘,额外属性:
    1. path:缓存目录
    2. umask:缓存文件的Unix权限,默认0022
    3. dirs:目录创建风格,对于瓦片12/656/1582.png,portable将创建匹配的目录树,默认safe
    4. gzip,一个数组,用于描述需要压缩的文件格式,默认["txt", "text", "json", "xml"]
  3. Multi:把瓦片缓存到多个、排序的缓存中,额外属性:
    1. tiers:缓存配置列表
  4. Memcache:缓存到Memcache,依赖 python-memcached.
    1. servers:缓存服务器的数组,例如 ["127.0.0.1:11211"]
    2. revision:不考虑瓦片缓存期而大范围失效的修订版号,默认0
    3. key prefix:缓存键的前缀
  5. Redis:缓存到Redis,依赖redis-py,需要redis server
/layers/{layer_name}  
自定义的图层名称
stale lock timeout 该图层渲染等待超时时间,默认15秒
cache lifespan 瓦片缓存的秒数,默认0表示永久有效
projection 地理投影系统的名称,默认为spherical mercator(球形墨卡托)
write cache 可选参数,可以用于跳过缓存写入,默认为true
allowed origin 与HTTP响应头Access-Control-Allow-Origin有关
maximum cache age 最大缓存时间,TileStache会以此写HTTP响应头 Cache-Control、Expires
redirects 可选的扩展名重定向规则,例如可以设置 {"jpg": "png"},导致jpg请求全部重定向到png
tile height 瓦片高度,默认256,一般不用改
jpeg options JPEG图片选项
png options PNG图片选项
/layers/{layer_name}/provider
提供者是TileStache缓存静态文件并加速后续请求的组件
name

提供者的类型,可以是:

  1. mapnik:内置的mapnik提供者,根据Mapnik XML文件来渲染地图图像,额外属性:
    1. mapfile: Mapnik XML文件的路径
    2. fonts:可选的*.ttf字体目录
  2. proxy:穿透请求给其它地图服务器,并缓存瓦片,额外属性:
    1. url:URL的模板,例如http://tile.openstreetmap.org/{Z}/{X}/{Y}.png
    2. provider:可选的提供者名称,TileStache通过这个名称来寻找流行的地图服务器,例如OPENSTREETMAP
  3. vector:返回数据源中的矢量展示形式
  4. url template:穿透请求给其它WMS服务器,并缓存瓦片,额外属性:
    1. template:带有占位符的URL模板,占位符包括:width、height、srs、xmin、ymin、xmax、ymax、zoom
    2. referer:可选的发送给目标WMS服务器的HTTP Referer URL 
    3. timeout:请求超时
    4. source projection:使用的地理投影系统
  5. mbtiles:从 MBTiles tilesets中读取图像的提供者
  6. mapnik grid:内置的Mapnik UTF Grid提供者,渲染Mapnik 2.0+的JSON光栅对象(JSON raster objects)

/layers/{layer_name}/metatile
元瓦片,可选,使多个单独的瓦片能够同时渲染,以提高性能,主要用于mapnik之类的位图提供者

rows 高度跨越多少个瓦片,例如4
columns 宽度跨越多少个瓦片,例如4
buffer 元瓦片周围的缓冲区域大小,以像素为单位。对于渲染文字标签、图标的提供者很有用,防止 文字渲染不全,例如64
/layers/{layer_name}/bounds
用于限制渲染可到达的地理区域
low 最小的缩放(zoom)级别,默认0
high 最大的缩放级别,默认31
north 北纬最大值,默认89
west 西经最大值,默认-180
south 南纬最大值,默认-89
east 东经最大之,默认180
/layers/{layer_name}/preview
TileStache包含一个内置的 slippy map预览,所谓slippy map是指能够缩放、拖拽的Web地图
lat 维度
lon 经度
zoom 缩放级别
ext 扩展名,例如png

tilestache.cfg可以配置为这样:

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{
  "cache":
  {
    "name": "Disk",
    "path": "D:/Programs/OsmStack/tilestache/cache"
  },
  "layers":
  {
    "osm":
    {
        "provider": {
            "name": "mapnik",
            "mapfile": "file://D:/Programs/OsmStack/osm-carto/mapnik-style.xml",
            "fonts" : "D:/Programs/OsmStack/mapnik/fonts"
        },
        "metatile" : {
            "rows" : "4",
            "columns" : "4",
            "buffer" : "64"
        },
        "preview" : {
            "lat" : "39.9396",
            "lon" : "116.3488",
            "zoom" : "12",
            "ext" : "png"
        }
    }
  }
}

注意其中的mapfile对应的路径,必须从Windows路径格式改为URI形式,例如:D:\Programs\Osmstack 改为 file://D:/Programs/Osmstack

tilestache.cfg改好后,启动TileStache Web服务,浏览器打开http://127.0.0.1:5539/osm/preview.html进行测试。

绿化批处理脚本示意

在Windows下,可以参考下面三个脚本,分别完成地图服务器的初始化、启动、停止:

init.bat
MS DOS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
@echo off
chcp 437
pushd "%~dp0"
 
set "OSMSTACK_HOME=%CD%"
set "OSM_DIR=%OSMSTACK_HOME%\osm"
set "MAPNIK_HOME=%OSMSTACK_HOME%\mapnik"
set "PYTHON_HOME=%OSMSTACK_HOME%\python"
set "PSQL_HOME=%OSMSTACK_HOME%\psql"
set "OSM2PQSQL_HOME=%OSMSTACK_HOME%\osm2pgsql"
set "PATH=%OSM2PQSQL_HOME%;%MAPNIK_HOME%\lib;%MAPNIK_HOME%\bin;%PSQL_HOME%\bin;%PYTHON_HOME%;%PYTHON_HOME%\Scripts;%PATH%"
set "PYTHONPATH=%MAPNIK_HOME%\python\2.7\site-packages;%PYTHONPATH%"
 
SET "PGDATA=%PSQL_HOME%\data"
SET "PGDATABASE=postgres"
SET "PGUSER=postgres"
SET "PGPORT=5432"
SET "PGLOCALEDIR=%PSQL_HOME%\share\locale"
rem GIS databse name and owner
SET "GISDATABASE=gis"
SET "GISUSER=gisuser"
 
echo ****** Prepare to initializing PostgreSQL  ******
initdb -U %PGUSER% --auth=trust --auth-host=trust --auth-local=trust --pwprompt -E UTF8
copy %PSQL_HOME%\postgresql.conf %PSQL_HOME%\data\postgresql.conf /y
 
echo ****** Prepare to start PostgreSQL  ******
pg_ctl -w -l %PSQL_HOME%\pgsql.log start
 
echo ****** Creating user %GISUSER%  ******
createuser -U %PGUSER% -P %GISUSER%
echo ****** Creating database %GISDATABASE%  ******
createdb -U %PGUSER% -E UTF8 -O %GISUSER% %GISDATABASE%
echo ****** Installing procedural language into %GISDATABASE%  ******
createlang -U %PGUSER% plpgsql %GISDATABASE%
echo ****** Activating PostGIS for %GISDATABASE%  ******
 
psql -U %PGUSER% -d %GISDATABASE% -f "%PSQL_HOME%\share\contrib\postgis-2.1\postgis.sql"
psql -U %PGUSER% -d %GISDATABASE% -f "%PSQL_HOME%\share\contrib\postgis-2.1\spatial_ref_sys.sql"
rem After the activation, the following command should list the tables geometry_columns and spatial_ref_sys:
psql --username=%GISUSER% --dbname=%GISDATABASE% --command="\d"
 
echo ****** Installing extension hstore for %GISDATABASE%  ******
echo create extension hstore; | psql -U %PGUSER% -d %GISDATABASE%
 
 
echo ****** Import OSM data into %GISDATABASE%  ******
echo Please input osm package name ( without suffix '.osm.bz2' ), Press Enter to skip :
set /p OSM_NAME=
if defined OSM_NAME (
    osm2pgsql -c -d gis -U %PGUSER% -H localhost -P %PGPORT%  --hstore -S %OSM2PQSQL_HOME%\openstreetmap-carto.style  -C 600 %OSM_DIR%\%OSM_NAME%.osm.bz2
)
 
echo ****** Processing TilesTache config file ******
set __OSM_PATH=%~p0
set __OSM_PATH=%__OSM_PATH:\=/%
python "tilestache/generate-cfg.py" %__OSM_PATH%
 
echo ****** Prepare to stop PostgreSQL  ******
pg_ctl -w stop
 
:end

startup.bat
MS DOS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@echo off
chcp 437
pushd "%~dp0"
 
set "OSMSTACK_HOME=%CD%"
set "OSM_DIR=%OSMSTACK_HOME%\osm"
set "MAPNIK_HOME=%OSMSTACK_HOME%\mapnik"
set "PYTHON_HOME=%OSMSTACK_HOME%\python"
set "PSQL_HOME=%OSMSTACK_HOME%\psql"
set "OSM2PQSQL_HOME=%OSMSTACK_HOME%\osm2pgsql"
set "PATH=%OSM2PQSQL_HOME%;%MAPNIK_HOME%\lib;%MAPNIK_HOME%\bin;%PSQL_HOME%\bin;%PYTHON_HOME%;%PYTHON_HOME%\Scripts;%PATH%"
set "PYTHONPATH=%MAPNIK_HOME%\python\2.7\site-packages;%PYTHONPATH%"
 
 
SET "PGDATA=%PSQL_HOME%\data"
SET "PGDATABASE=postgres"
SET "PGUSER=postgres"
SET "PGPORT=5432"
SET "PGLOCALEDIR=%PSQL_HOME%\share\locale"
 
echo ****** Prepare to start PostgreSQL  ******
pg_ctl  -w  -l%PSQL_HOME%\pgsql.log start
 
echo ****** Prepare to start TileStache  ******
SET "TILE_STACHE_SCR=%PYTHON_HOME%\Scripts\tilestache-server.py"
 
python "%TILE_STACHE_SCR:\=/%"  -i 0.0.0.0 -p 5539 -c tilestache/osm.cfg

shutdown.bat
MS DOS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@echo off
chcp 437
pushd "%~dp0"
 
set "OSMSTACK_HOME=%CD%"
set "MAPNIK_HOME=%OSMSTACK_HOME%\mapnik"
set "PYTHON_HOME=%OSMSTACK_HOME%\python"
set "PSQL_HOME=%OSMSTACK_HOME%\psql"
set "PATH=%MAPNIK_HOME%\lib;%MAPNIK_HOME%\bin;%PSQL_HOME%\bin;%PYTHON_HOME%;%PATH%"
 
SET "PGDATA=%PSQL_HOME%\data"
SET "PGDATABASE=postgres"
SET "PGUSER=postgres"
SET "PGPORT=5432"
SET "PGLOCALEDIR=%PSQL_HOME%\share\locale"
 
echo ****** Prepare to stop PostgreSQL  ******
pg_ctl -w stop
通过前端API使用OSM瓦片
使用OpenLayers

OpenLayers很久以来一直是在网页中嵌入OSM地图的标准选择,它是一个成熟、综合的JS库,学习曲线较为平缓,提供大量的特性,包括完整的投影支持(full projection support)、矢量绘图、预览地图(overview maps)等等。

下面是一个简单的例子:

XHTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!doctype html>
<html lang="en">
<head>
    <link rel="stylesheet" href="openlayers3/ol.css" type="text/css">
    <style>
        html, body{ margin:0; height:100%; }
        #mapdiv { width:100%; height:100%; }
    </style>
    <script src="openlayers3/ol.js" type="text/javascript"></script>
    <title>OpenLayers3 Example</title>
</head>
<body>
<div id="mapdiv" ></div>
<script type="text/javascript">
    //定义一个矢量图像
    var image = new ol.style.Circle({
        radius: 5,
        fill: new ol.style.Stroke({color: '#F00'}),
        stroke: new ol.style.Stroke({color: '#000', width: 1})
    });
    //定义样式
    var styles = {
        'Point': [new ol.style.Style({
            image: image
        })],
        'Polygon': [new ol.style.Style({
            stroke: new ol.style.Stroke({
                color: '#00F',
                lineDash: [2],
                width: 1
            }),
            fill: new ol.style.Fill({
                color: 'rgba(0, 0, 255, 0.1)'
            })
        })]
    };
    //定义一个矢量图层
    var vectorSource = new ol.source.GeoJSON(
    ({
        object: {
            'type': 'FeatureCollection',
            //坐标参考系(Coordinate Reference Systems)
            'crs': {
                'type': 'name',
                'properties': {
                    'name': 'EPSG:3857'
                }
            },
            'features': [
                //画一个点
                {
                    'type': 'Feature',
                    'geometry': {
                        'type': 'Point',
                        //坐标系转换:
                        //EPSG:4326,直接把经纬度作为X、Y方向的坐标值,南纬、西经为负数
                        //EPSG:3857,球面墨卡托投影,以米为单位,以前叫EPSG:900931
                        'coordinates': ol.proj.transform([ 116.34430, 39.94225], 'EPSG:4326', 'EPSG:3857')
                    }
                },
                //画一个五边形
                {
                    'type': 'Feature',
                    'geometry': {
                        'type': 'Polygon',
                        'coordinates': [
                            [
                                ol.proj.transform([ 116.1007694, 40.2551712], 'EPSG:4326', 'EPSG:3857'),
                                ol.proj.transform([ 117.2687534, 40.3578032], 'EPSG:4326', 'EPSG:3857'),
                                ol.proj.transform([ 117.5886384, 39.2306078], 'EPSG:4326', 'EPSG:3857'),
                                ol.proj.transform([ 116.9843903, 38.42073], 'EPSG:4326', 'EPSG:3857'),
                                ol.proj.transform([ 115.5792222, 39.1692678], 'EPSG:4326', 'EPSG:3857')
                            ]
                        ]
                    }
                },
            ]
        }
    }));
    var styleFunction = function (feature, resolution) {
        return styles[feature.getGeometry().getType()];
    };
    //定义一个矢量图层
    var vectorLayer = new ol.layer.Vector({
        source: vectorSource,
        style: styleFunction//寻找样式定义的回调函数
    });
    //定义一个地图
    var map = new ol.Map({
        target: 'mapdiv', //渲染目标
        //图层列表,包含一个光栅图层,一个矢量图层
        layers: [
            new ol.layer.Tile({
                source: new ol.source.XYZ({
                    url: 'http://192.168.0.89:5539/osm/{z}/{x}/{y}.png'
                }),
            }),
            vectorLayer
        ],
        //视角:缩放级别7,以北二环为中心
        view: new ol.View({
            center: ol.proj.transform([116.34430, 39.94225], 'EPSG:4326', 'EPSG:3857'),
            zoom: 7
        })
    });
</script>
</body>
</html>
使用Leaflet

Leaflet是一个近来迅速流行的JavaScript库,比起OpenLayers它更小小巧、简单,对于简单寻常的需求,Leaflet是个好的选择。

下面是一个简单的例子:

XHTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE HTML>
<html>
<head>
    <title>Leaflet Example</title>
    <link rel="stylesheet" type="text/css" href="leaflet/leaflet.css" />
    <script type="text/javascript" src="leaflet/leaflet-src.js"></script>
    <script type="text/javascript">
        var map;
 
        function init() {
            // 在一个DIV中创建地图对象
            map = new L.Map('mapdiv');
    
            // 创建瓦片图层
            var osmUrl='http://192.168.0.89:5539/osm/{z}/{x}/{y}.png';
            var osmAttrib='Kingsmart Tech';
            var osm = new L.TileLayer(osmUrl, {minZoom: 3, maxZoom: 18, attribution: osmAttrib});      
    
            // 地图中心设置为西北二环附近
            map.setView(new L.LatLng( 39.94225, 116.34430 ),12);
            map.addLayer(osm);//添加图层
            
            //在地图上添加标记
            var plot = {
                "name":"金名科技",
                "lon":"116.34430",
                "lat":"39.94225",
                "details":"金名科技是座落于海淀区高粱桥斜街59号院的高新技术企业"
            };
            var plotll = new L.LatLng( plot.lat, plot.lon, true );//标记的坐标
            var mark = new L.Marker(plotll);
            mark.data = plot;
            map.addLayer(mark);//添加标记到地图
            mark.bindPopup("<h4>" + plot.name + "</h4>" + plot.details);//绑定提示框
            
            //在地图上添加一个多边形
            var latlngs = [
                new L.LatLng( 39.931064087073835, 116.3481330871582),    
                new L.LatLng( 39.9600172003783, 116.32684707641602),  
                new L.LatLng( 39.99264056247673, 116.37628555297852 ),  
                new L.LatLng( 39.97922477476731, 116.46417617797852 ),  
                new L.LatLng( 39.90657598772841, 116.45936965942383),  
                new L.LatLng( 39.87338459498892, 116.36838912963867),  
                new L.LatLng( 39.931064087073835, 116.3481330871582)
            ];
            var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map);
        }
    </script>
 
    <style>
    html, body{ margin:0; height:100%; }
    #mapdiv { width:100%; height:100%; }
    div.olControlAttribution { bottom:3px; }
    </style>
 
</head>
 
<body onload="init();">
    <div id="mapdiv"></div>
</body>
</html>
← 爱心早餐:红豆薏仁粥 + 紫薯泥
Gradle学习笔记 →
17 Comments On This Topic
  1. 回复
    hhy
    2015/05/07

    很不错的文章!关于tilestache的资料并不多,想请教一下其中 tilestache.cfg可用的配置项是您从API文档里翻译过来的吗?每次解译这些我总担心没用过翻译的不好

    • 回复
      Alex
      2015/05/07

      过奖了。Tilestache的配置项说明主要是从API文档翻译的。

  2. 回复
    sunstef
    2015/06/10

    你好,非常感謝你的分享,我目前安裝到一半有下列問題,可否麻煩您分享一下心得
    1.
    在執行 osm2pgsql ,會有下列錯誤訊息,請問是否會對系統造成影響
    Setting up table: planet_osm_point
    NOTICE: table "planet_osm_point" does not exist, skipping
    NOTICE: table "planet_osm_point_tmp" does not exist, skipping

    2‧
    http://download.osgeo.org/gdal/1.11.1/gdal-1.11.1.tar.gz 連結失效,http://download.gisinternals.com/ 我找不到對應的檔案,請問還有哪裡可以下載嗎?

    • 回复
      sunstef
      2015/06/10

      關於詢問您的問題,已解決
      另外有關处理openstreetmap-cart中缺少的data目录,我開啟 TileMill 要匯出mapnik样式文件整個畫面會是空白
      請問有建議的解決方法嗎

      • 回复
        Alex
        2015/06/11

        您好,如果点击Export -mapnik XML后,出现一片空白,可能是因为shp文件没有下载好。在TileMill打开OpenStreetMap Carto项目时,您可能会收到提示“File not found ... _places_fixed.sh”。请确认执行get-shapefiles.sh的过程中没有发生错误。另外,您可以选择其它的OSM主题,例如osm-bright,其主页上提供了手工下载shp文件的方法。

        • 回复
          CSDNxm19899889
          2016/10/18

          您好,最近才接触osm。今天参照文档搭建服务器时,遇到了下面的问题。
          通过TileMill生成mapnik样式文件时,点击Export -mapnik XML后,过一会就会画面变白。打开OpenStreetMap Carto项目时没提示File not found信息。
          一共操作了几次,有一次出现提示“Error -102 when loading url http://localhost:20009/api/Project/openstreetmap-carto-master.xml”

        • 回复
          CSDNxm19899889
          2016/10/18

          又试了一次出现了提示信息如下:有很多条信息,但都是下边几条重复
          placenames.mss:71:6 Unrecognized rule: shield-placements. Did you mean shield-placement?
          placenames.mss:68:4 Unrecognized rule: shield-placement-type
          placenames.mss:73:4 Unrecognized rule: shield-unlock-image
          roads.mss:2694:10 Property shield-face-name required for defining shield styles.
          roads.mss:2626:6 Property shield-file required for defining shield styles.
          water-features.mss:157:8 Property text-name required for defining text styles.
          关闭提示后,又出现了下边的信息:
          Unable to reach the local TileMill Server. Check the logs for details. If this problem persists please contact support at: http://support.mapbox.com/discussions/tilemill

        • 回复
          张秉清
          2017/06/02

          请教一下,我想用本地ip查看tilestache,比如:192.168.0.xxx:5539/osm/preview.html,但是无法显示,而用localhost和127.0.0.1能正常显示,要怎么调啊??

          • Alex
            2017/06/02

            先检查一下192.168.0.xxx:5539端口通不,畅通的话Chrome开发者工具看一下有什么错误提示?

  3. 回复
    Phil
    2015/12/03

    iOS的route me已经不能用了,三年前的版本了

    • 回复
      Alex
      2015/12/03

      谢谢提醒~~

  4. 回复
    Ryan
    2016/01/11

    有些关于openstreetmap的问题想请教你,不知可否?我QQ*********

    • 回复
      Alex
      2016/01/13

      您好,可以的话请给我发邮件。

      • 回复
        张秉清
        2017/06/14

        我用本地IP查看tilestache,Chrome提示 GET http://10.5.46.111:5539/osm/preview.html net::ERR_CONNECTION_REFUSED,是不是需要设config啊,10.5.46.111是我本地IP,能ping通,端口也能用。用 http://localhost:5539/osm/preview.html 和 http://127.0.0.1.111:5539/osm/preview.html 都能显示。

        • 回复
          Alex
          2017/06/15

          给出的信息不足以判断具体原因,用telnet 10.5.46.111 5539这个命令试了的确通么?
          检查一下是不是和CORS(跨站资源共享)有关系呢,preview.html中应该有个填写tiles的URL的地方,改成和你地址栏使用的IP一致看看。

  5. 回复
    old.fish
    2016/06/17

    你的邮箱地址是多少 有关于osm - tilemill 导出xml文件报错的问题 点击导出后报错,如下:
    Error -324 when loading url http://localhost:20009/api/Project/openstreetmap-carto.xml

    • 回复
      old.fish
      2016/06/17

      add :
      Unable to reach the local TileMill Server. Check the logs for details. If this problem persists please contact support at: http://support.mapbox.com/discussions/tilemill

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

Related Posts

  • TileStache知识集锦
  • Cesium学习笔记

Recent Posts

  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
  • A Comprehensive Study of Kotlin for Java Developers
  • 背诵营笔记
  • 利用LangChain和语言模型交互
  • 享学营笔记
ABOUT ME

汪震 | Alex Wong

江苏淮安人,现居北京。目前供职于腾讯云,专注容器方向。

GitHub:gmemcc

Git:git.gmem.cc

Email:gmemjunk@gmem.cc@me.com

ABOUT GMEM

绿色记忆是我的个人网站,域名gmem.cc中G是Green的简写,MEM是Memory的简写,CC则是我的小天使彩彩名字的简写。

我在这里记录自己的工作与生活,同时和大家分享一些编程方面的知识。

GMEM HISTORY
v2.00:微风
v1.03:单车旅行
v1.02:夏日版
v1.01:未完成
v0.10:彩虹天堂
v0.01:阳光海岸
MIRROR INFO
Meta
  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org
Recent Posts
  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
    In this blog post, I will walk ...
  • A Comprehensive Study of Kotlin for Java Developers
    Introduction Purpose of the Study Understanding the Mo ...
  • 背诵营笔记
    Day 1 Find Your Greatness 原文 Greatness. It’s just ...
  • 利用LangChain和语言模型交互
    LangChain是什么 从名字上可以看出来,LangChain可以用来构建自然语言处理能力的链条。它是一个库 ...
  • 享学营笔记
    Unit 1 At home Lesson 1 In the ...
  • K8S集群跨云迁移
    要将K8S集群从一个云服务商迁移到另外一个,需要解决以下问题: 各种K8S资源的迁移 工作负载所挂载的数 ...
  • Terraform快速参考
    简介 Terraform用于实现基础设施即代码(infrastructure as code)—— 通过代码( ...
  • 草缸2021
    经过四个多月的努力,我的小小荷兰景到达极致了状态。

  • 编写Kubernetes风格的APIServer
    背景 前段时间接到一个需求做一个工具,工具将在K8S中运行。需求很适合用控制器模式实现,很自然的就基于kube ...
  • 记录一次KeyDB缓慢的定位过程
    环境说明 运行环境 这个问题出现在一套搭建在虚拟机上的Kubernetes 1.18集群上。集群有三个节点: ...
  • eBPF学习笔记
    简介 BPF,即Berkeley Packet Filter,是一个古老的网络封包过滤机制。它允许从用户空间注 ...
  • IPVS模式下ClusterIP泄露宿主机端口的问题
    问题 在一个启用了IPVS模式kube-proxy的K8S集群中,运行着一个Docker Registry服务 ...
  • 念爷爷
      今天是爷爷的头七,十二月七日、阴历十月廿三中午,老人家与世长辞。   九月初,回家看望刚动完手术的爸爸,发

  • 6 杨梅坑

  • liuhuashan
    深圳人才公园的网红景点 —— 流花山

  • 1 2020年10月拈花湾

  • 内核缺陷触发的NodePort服务63秒延迟问题
    现象 我们有一个新创建的TKE 1.3.0集群,使用基于Galaxy + Flannel(VXLAN模式)的容 ...
  • Galaxy学习笔记
    简介 Galaxy是TKEStack的一个网络组件,支持为TKE集群提供Overlay/Underlay容器网 ...
TOPLINKS
  • Zitahli's blue 91 people like this
  • 梦中的婚礼 64 people like this
  • 汪静好 61 people like this
  • 那年我一岁 36 people like this
  • 为了爱 28 people like this
  • 小绿彩 26 people like this
  • 彩虹姐姐的笑脸 24 people like this
  • 杨梅坑 6 people like this
  • 亚龙湾之旅 1 people like this
  • 汪昌博 people like this
  • 2013年11月香山 10 people like this
  • 2013年7月秦皇岛 6 people like this
  • 2013年6月蓟县盘山 5 people like this
  • 2013年2月梅花山 2 people like this
  • 2013年淮阴自贡迎春灯会 3 people like this
  • 2012年镇江金山游 1 people like this
  • 2012年徽杭古道 9 people like this
  • 2011年清明节后扬州行 1 people like this
  • 2008年十一云龙公园 5 people like this
  • 2008年之秋忆 7 people like this
  • 老照片 13 people like this
  • 火一样的六月 16 people like this
  • 发黄的相片 3 people like this
  • Cesium学习笔记 90 people like this
  • IntelliJ IDEA知识集锦 59 people like this
  • 基于Kurento搭建WebRTC服务器 38 people like this
  • Bazel学习笔记 37 people like this
  • PhoneGap学习笔记 32 people like this
  • NaCl学习笔记 32 people like this
  • 使用Oracle Java Mission Control监控JVM运行状态 29 people like this
  • Ceph学习笔记 27 people like this
  • 基于Calico的CNI 27 people like this
Tag Cloud
ActiveMQ AspectJ CDT Ceph Chrome CNI Command Cordova Coroutine CXF Cygwin DNS Docker eBPF Eclipse ExtJS F7 FAQ Groovy Hibernate HTTP IntelliJ IO编程 IPVS JacksonJSON JMS JSON JVM K8S kernel LB libvirt Linux知识 Linux编程 LOG Maven MinGW Mock Monitoring Multimedia MVC MySQL netfs Netty Nginx NIO Node.js NoSQL Oracle PDT PHP Redis RPC Scheduler ServiceMesh SNMP Spring SSL svn Tomcat TSDB Ubuntu WebGL WebRTC WebService WebSocket wxWidgets XDebug XML XPath XRM ZooKeeper 亚龙湾 单元测试 学习笔记 实时处理 并发编程 彩姐 性能剖析 性能调优 文本处理 新特性 架构模式 系统编程 网络编程 视频监控 设计模式 远程调试 配置文件 齐塔莉
Recent Comments
  • qg on Istio中的透明代理问题
  • heao on 基于本地gRPC的Go插件系统
  • 黄豆豆 on Ginkgo学习笔记
  • cloud on OpenStack学习笔记
  • 5dragoncon on Cilium学习笔记
  • Archeb on 重温iptables
  • C/C++编程:WebSocketpp(Linux + Clion + boostAsio) – 源码巴士 on 基于C/C++的WebSocket库
  • jerbin on eBPF学习笔记
  • point on Istio中的透明代理问题
  • G on Istio中的透明代理问题
  • 绿色记忆:Go语言单元测试和仿冒 on Ginkgo学习笔记
  • point on Istio中的透明代理问题
  • 【Maven】maven插件开发实战 – IT汇 on Maven插件开发
  • chenlx on eBPF学习笔记
  • Alex on eBPF学习笔记
  • CFC4N on eBPF学习笔记
  • 李运田 on 念爷爷
  • yongman on 记录一次KeyDB缓慢的定位过程
  • Alex on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • haolipeng on 基于本地gRPC的Go插件系统
  • 吴杰 on 基于C/C++的WebSocket库
©2005-2025 Gmem.cc | Powered by WordPress | 京ICP备18007345号-2