基于 PostGIS 的矢量切片服务器

基于 PostGIS 的矢量切片服务器

矢量切片简介

矢量切片是 MapBox 定义的一种开放的 矢量地图标准 , 已经成为开放地理联盟 (OGC) 的标准之一。

个人认为矢量切片的主要优点有:

服务端只关注数据, 无需进行繁琐的配图;

网络传输快, 因为只有括矢量数据;

客户端渲染, 服务端的一套矢量数据, 在客户端可以有多种的表现形式;

充分利用客户端硬件

适配客户端屏幕, 根据屏幕解析度进行高精度矢量渲染;

利用 OpenGL/WebGL 实现海量空间数据渲染;

目前制作矢量切片的方式主要有:

使用 ArcGIS Pro 生成矢量切片包, 上传到 ArcGIS Portal 和 Server , 这套工具最完善, 但是也最贵;

使用开源的 GeoServer 来配置生成矢量切片, 配置比较繁琐, 而且对于矢量切片标准的支持也也比较慢;

这两种方式都能生成质量比较高的矢量切片, 并提供可靠的矢量切片服务, 但是都需要对数据做预处理, 如果修改了数据, 往往不能及时响应。

PostGIS 对矢量切片的支持

PostGIS 是关系数据库 PostgreSQL 的空间扩展, 提供了强大的空间数据查询和处理能力, 对矢量切片也提供了支持, 相关的函数有:

ST_AsMVTGeom 将数据库存储的空间坐标转换为矢量切片坐标;

ST_AsMVT 将矢量空间坐标聚合为符合矢量切片格式规范的二进制数据;

ST_TileEnvelope 在 Web墨卡托坐标系 (SRID:3857) 下使用 xyz 切片架构 计算切片切片坐标范围;

通过者上面这三个相关函数, 可以将数据库存储的空间数据快速转换成矢量切片标准的二进制数据。

将单表输出为单图层矢量切片的 SQL 语句为:

with mvt_geom as (

  select

    ST_AsMVTGeom(

      geom,

      ST_TileEnvelope(15, 26696, 14219),

      extent => 4096, buffer => 64

    ) as geom,

    id, name, fclass, ref, oneway, maxspeed, bridge, tunnel, layer

  from public.sr3857_guangzhou_road

  where geom && ST_TileEnvelope(15, 26696, 14219, margin => (64.0 / 4096))

)

select ST_AsMVT(mvt_geom, 'guangzhou_road', 4096, 'geom', 'id')

from mvt_geom

也可以使用 || 算符将多个图层生成矢量切片

select (

  (

    with mvt_geom as (

      select

        ST_AsMVTGeom(

          geom,

          ST_TileEnvelope(15, 26696, 14219),

          extent => 4096, buffer => 64

        ) as geom,

        id, name, fclass, ref, oneway, maxspeed, bridge, tunnel, layer

      from public.sr3857_guangzhou_road

      where geom && ST_TileEnvelope(15, 26696, 14219, margin => (64.0 / 4096))

    )

    select ST_AsMVT(mvt_geom, 'guangzhou_road', 4096, 'geom', 'id')

    from mvt_geom

  ) || (

    with mvt_geom as (

      select

        ST_AsMVTGeom(

          geom,

          ST_TileEnvelope(15, 26696, 14219),

          extent => 4096, buffer => 64

    ) as geom,

        objectid, name, height, flag, type, area_id

      from public.sr3857_guangzhou_building

      where geom && ST_TileEnvelope(15, 26696, 14219, margin => (64.0 / 4096))

    )

    select ST_AsMVT(mvt_geom, 'guangzhou_building', 4096, 'geom', 'objectid')

    from mvt_geom

  )

);

矢量切片服务器

有了上面的 SQL 语句, 开发矢量切片服务器就是非常简单的了, 任何开发语言都可以实现, 下面以 C# 代码为例:

[HttpGet("{source}/{z:int}/{y:int}/{x:int}")]

public async Task<ActionResult> GetTile(string source, int z, int y, int x) {

    try {

        var buffer = await provider.GetTileContentAsync(source, z, y, x);

        if (buffer == null || buffer.Length == 0) {

            return NotFound();

        }

        return File(buffer, "application/vnd.mapbox-vector-tile");

    }

    catch (Exception ex) {

        logger.LogError(ex.Message);

        return StatusCode(500);

    }

}

通过 appsettings.json 配置两个矢量切片源:

{

  "connectionStrings": {

    "geo_db": "server=127.0.0.1;port=5432;database=geo_db;user id=geo_db_user;password=********;"

  },

  "vectors": {

    "guangzhou": {

      "connectionString": "geo_db",

      "layers": [

        {

          "name": "road",

          "minzoom": 9,

          "maxzoom": 15,

          "srid": 3857,

          "schema": "public",

          "tableName": "sr3857_guangzhou_road",

          "idColumn": "id",

          "geometryColumn": "geom",

          "attributeColumns": "name, fclass, ref, oneway, maxspeed, bridge, tunnel, layer"

        },

        {

          "name": "building",

          "minzoom": 13,

          "maxzoom": 17,

          "srid": 3857,

          "schema": "public",

          "tableName": "sr3857_guangzhou_building",

          "idColumn": "objectid",

          "geometryColumn": "geom",

          "attributeColumns": "name, height, flag, type, area_id"

        }

      ]

    }

  }

}

这样生成的矢量切片服务的地址是: http://127.0.0.1:5000/api/vector/guangzhou/{z}/{y}/{x} , 包含了 road 和 building 两个图层。

使用矢量切片服务

生成的是基于 Web 墨卡托坐标系的 xyz 切片架构的标准的矢量切片服务, 可以直接任意支持矢量切片的客户端中使用 (mapboxgl, openlayers, arcgis js api 等), 配置参照下面的矢量切片样式:

{

  "version": 8,

  "sources": {

    "guangzhou": {

      "type": "vector",

      "scheme": "xyz",

      "tiles": ["http://127.0.0.1:5000/api/vectortiles/guangzhou/{z}/{y}/{x}"],

      "minzoom": 9,

      "maxzoom": 17

    }

  },

  "layers": [

    {

      "id": "road",

      "source": "guangzhou",

      "source-layer": "road",

      "type": "line",

      "minzoom": 9,

      "maxzoom": 15,

      "paint": {

        "line-color": "#00FF00",

        "line-width": 2

      }

    },

    {

      "id": "building",

      "source": "guangzhou",

      "source-layer": "guangzhou_building",

      "type": "fill",

      "minzoom": 13,

      "maxzoom": 17,

      "paint": {

        "fill-opacity": 0.8,

        "fill-color": "#8c2d04"

      }

    }

  ]

}

注意问题

PostGIS 版本要求最新的 3.1.x ;

虽然 PostGIS 3.x 最低支持 PostgreSQL 9.6.x , 但是建议使用高版本的 PostgreSQL (12+), 因为 PostgreSQL 12 以上的版本提供了更好的查询性能;

虽然 PostGIS 提供了坐标系转换函数 ST_Transform , 但是进行实时转换会消耗一些性能, 建议将空间数据转换为 Web墨卡托坐标系 (SRID:3857) 存储在数据库, 这样在运行时就无需进行坐标系转换, 效率最高;

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 222,252评论 6 516
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,886评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,814评论 0 361
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,869评论 1 299
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,888评论 6 398
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,475评论 1 312
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,010评论 3 422
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,924评论 0 277
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,469评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,552评论 3 342
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,680评论 1 353
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,362评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,037评论 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,519评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,621评论 1 274
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,099评论 3 378
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,691评论 2 361

推荐阅读更多精彩内容