geotrellis使用(三十四)矢量瓦片技术研究——矢栅一体化

前言

本文所涉及技术与Geotrellis并没有太大关系,仅是矢量瓦片前端渲染和加载技术,可是其实我这是在为Geotrellis的矢量瓦片作铺垫。不少人可能会说,Geotrellis为何要搞矢量瓦片,这不就是前端展现吗。其实否则,首先Geotrellis能够用分布式技术进行快速矢量瓦片切割,固然这不是主要的,由于单台服务器基本也能很快处理矢量瓦片的切割,重要的是Geotrellis可使用矢量瓦片进行空间计算,这样能够矢栅一体化,矢量瓦片和栅格瓦片同时进行计算,这个东西就厉害了,将大大的提升空间数据分析的可能性。固然这只是我我的的见解,有待后续研究,而且Geotrellis的矢量瓦片还并在测试当中。本文仅介绍前端矢量瓦片技术。css

1、什么是矢量瓦片

目前高德、百度等互联网地图基本都使用了矢量瓦片技术。先来看一下Wiki中的介绍:html

Vector tiles, tiled vectors or vectiles are packets of geographic data, packaged into pre-defined roughly-square shaped "tiles" for transfer over the web. This is an emerging method for delivering styled web maps, combining certain benefits of pre-rendered raster map tiles with vector map data. As with the widely used raster tiled web maps, map data is requested by a client as a set of "tiles" corresponding to square areas of land of a pre-defined size and location. Unlike raster tiled web maps, however, the server returns vector map data, which has been clipped to the boundaries of each tile, instead of a pre-rendered map image.前端

There are several major advantages of this hybrid approach. Compared to an un-tiled vector map, the data transfer is reduced,because only data within the current viewport, and at the current zoom level needs to be transferred. The GIS clipping operations can all be performed in advance, as the tile boundaries are pre-defined. This in turn means that tiled vector data can be packaged up and distributed, without needing any kind of GIS system available to serve data.git

Compared to a tiled raster map, data transfer is also greatly reduced, as vector data is typically much smaller than a rendered bitmap. Also, styling can be applied later in the process, or even in the browser itself, allowing much greater flexibility in how data is presented. It is also easy to provide interactivity with map features, as their vector representation already exists within the client. Yet another benefit is that less centralised server processing power is required, since rasterisation can be performed directly in the client. This has been described as making "rendering ... a last-mile problem, with fast, high-quality GPU[s] in everyone’s pocket".github

简单的说就是将矢量直接切割成如栅格瓦片同样大小的块,这种切割一样是按照空间来进行的。优点就是在于继承了栅格瓦片的全部优势后,还不须要事先定义样式进行矢量数据栅格化,可以在用户浏览器随意配置显示样式,减轻服务器端计算压力,缩小服务端存储空间(栅格图片占用大量存储空间),而且能够实现用户交互。web

这些就是矢量瓦片的优点,固然不是说矢量瓦片绝对是个好东西,任何事情都要辩证的区看待,对待任何问题都要深刻研究,找出最优解。如栅格数据(遥感影像等)永远须要使用栅格瓦片,某些不须要交互、不怎么变化等状况的矢量数据也可使用栅格瓦片。数据库

2、前端显示技术

矢量瓦片的生成还未研究,本文只是调用OSM公开发布的矢量瓦片进行前端展现试验。canvas

目前开源中矢量瓦片作的比较好的是Mapbox,各类渲染技术也基本以Mapbox定义的矢量瓦片标准为标准。Leaflet有多款插件支持矢量瓦片,Leaftlet是一款开源的前端地图渲染引擎,主要支持的是栅格瓦片。综合分析以后我选用了Leaflet.VectorGrid插件进行矢量瓦片的渲染,Github地址https://github.com/IvanSanchez/Leaflet.VectorGrid浏览器

2.1 添加插件

除了正常的Leftlet所需的js以及css文件外(具体请自行搜索),还需添加一下语句引入vectorgrid的js文件。服务器

<script src="https://unpkg.com/leaflet.vectorgrid@1.2.0"></script>

固然你能够直接将此文件下载到本地引入。在Github中也有相应的示例能够参考。

2.2 添加OSM矢量瓦片

OSM有一套能够直接调用的矢量瓦片,在这里咱们以此数据为演示,将其添加到地图中,并实现交互。

var map = L.map('map');

var openmaptilesUrl = "https://free-{s}.tilehosting.com/data/v3/{z}/{x}/{y}.pbf.pict?key={key}";

var openmaptilesVectorTileOptions = {
    rendererFactory: L.canvas.tile,
    attribution: '<a href="https://openmaptiles.org/">&copy; OpenMapTiles</a>, <a href="http://www.openstreetmap.org/copyright">&copy; OpenStreetMap</a> contributors',
    vectorTileLayerStyles: osm_poi_style,
    subdomains: '0123',
    interactive: true,  // Make sure that this VectorGrid fires mouse/pointer events
    key: '5iCgspbpUIw5lEYGLbGj',
    maxZoom: 16
};

var openmaptilesPbfLayer = L.vectorGrid.protobuf(openmaptilesUrl, openmaptilesVectorTileOptions).addTo(map)
    .on('click', function(e) {  // The .on method attaches an event handler
            L.popup()
                .setContent((e.layer.properties.name || e.layer.properties.type) + "<br/>" + e.layer.properties.class)
                .setLatLng(e.latlng)
                .openOn(map);
            L.DomEvent.stop(e);
        });

openmaptilesUrl为OSM矢量瓦片请求地址,openmaptilesVectorTileOptions为矢量瓦片的相应配置,其中最重要的就是vectorTileLayerStyles,其表示矢量瓦片的渲染规则,矢量瓦片传送的只是矢量数,那么渲染就要由前端完成,这个变量定义的就是渲染规则,如点线面显示成什么颜色以及不一样的要素渲染成什么形状颜色以及如何交互等,均在此变量中设置。osm_poi_style定义以下:

var osm_poi_style= {
    poi: {icon: new L.Icon.Default()},
    water: {
        fill: true,
        weight: 1,
        fillColor: '#06cccc',
        color: '#06cccc',
        fillOpacity: 0.2,
        opacity: 0.4,
    },
    admin: {
        weight: 1,
        fillColor: 'pink',
        color: 'pink',
        fillOpacity: 0.2,
        opacity: 0.4
    },
    waterway: {
        weight: 1,
        fillColor: '#2375e0',
        color: '#2375e0',
        fillOpacity: 0.2,
        opacity: 0.4
    },
    landcover: {
        fill: true,
        weight: 1,
        fillColor: '#53e033',
        color: '#53e033',
        fillOpacity: 0.2,
        opacity: 0.4,
    },
    landuse: {
        fill: true,
        weight: 1,
        fillColor: '#e5b404',
        color: '#e5b404',
        fillOpacity: 0.2,
        opacity: 0.4
    },
    park: {
        fill: true,
        weight: 1,
        fillColor: '#84ea5b',
        color: '#84ea5b',
        fillOpacity: 0.2,
        opacity: 0.4
    },
    boundary: {
        weight: 1,
        fillColor: '#c545d3',
        color: '#054b96',
        fillOpacity: 0.2,
        opacity: 0.4
    },
    aeroway: {
        weight: 1,
        fillColor: '#51aeb5',
        color: '#51aeb5',
        fillOpacity: 0.2,
        opacity: 0.4
    },
    road: { // mapbox & mapzen only
        weight: 1,
        fillColor: '#f2b648',
        color: '#f2b648',
        fillOpacity: 0.2,
        opacity: 0.4
    },
    tunnel: {   // mapbox only
        weight: 0.5,
        fillColor: '#f2b648',
        color: '#f2b648',
        fillOpacity: 0.2,
        opacity: 0.4,
//                  dashArray: [4, 4]
    },
    bridge: {   // mapbox only
        weight: 0.5,
        fillColor: '#f2b648',
        color: '#f2b648',
        fillOpacity: 0.2,
        opacity: 0.4,
//                  dashArray: [4, 4]
    },
    transportation: {   // openmaptiles only
        weight: 0.5,
        fillColor: '#f2b648',
        color: '#f2b648',
        fillOpacity: 0.2,
        opacity: 0.4,
//                  dashArray: [4, 4]
    },
    transit: {  // mapzen only
        weight: 0.5,
        fillColor: '#f2b648',
        color: '#f2b648',
        fillOpacity: 0.2,
        opacity: 0.4,
//                  dashArray: [4, 4]
    },
    building: {
        fill: true,
        weight: 1,
        fillColor: '#2b2b2b',
        color: '#2b2b2b',
        fillOpacity: 0.2,
        opacity: 0.4
    },
    water_name: {
        weight: 1,
        fillColor: '#022c5b',
        color: '#022c5b',
        fillOpacity: 0.2,
        opacity: 0.4
    },
    transportation_name: {
        weight: 1,
        fillColor: '#bc6b38',
        color: '#bc6b38',
        fillOpacity: 0.2,
        opacity: 0.4
    },
    place: {
        weight: 1,
        fillColor: '#f20e93',
        color: '#f20e93',
        fillOpacity: 0.2,
        opacity: 0.4
    },
    housenumber: {
        weight: 1,
        fillColor: '#ef4c8b',
        color: '#ef4c8b',
        fillOpacity: 0.2,
        opacity: 0.4
    },
    poi: {
        weight: 1,
        fillColor: '#3bb50a',
        color: '#3bb50a',
        fillOpacity: 0.2,
        opacity: 0.4
    },
    earth: {    // mapzen only
        fill: true,
        weight: 1,
        fillColor: '#c0c0c0',
        color: '#c0c0c0',
        fillOpacity: 0.2,
        opacity: 0.4
    },

    // Do not symbolize some stuff for mapbox
    country_label: [],
    marine_label: [],
    state_label: [],
    place_label: [],
    waterway_label: [],
    poi_label: [],
    road_label: [],
    housenum_label: [],

    // Do not symbolize some stuff for openmaptiles
    country_name: [],
    marine_name: [],
    state_name: [],
    place_name: [],
    waterway_name: [],
    poi_name: [],
    road_name: [],
    housenum_name: []
};

其中不一样的对象有不一样的渲染规则,而第一行的poi: {icon: new L.Icon.Default()}表示对poi这个属性进行特别渲染,渲染成一个Icon图标,当用户点击此图标的时候便可根据上面定义的on方法中的内容来进行交互。再来看一下on方法中的内容:

L.popup()
    .setContent((e.layer.properties.name || e.layer.properties.type) + "<br/>" + e.layer.properties.class)
    .setLatLng(e.latlng)
    .openOn(map);
L.DomEvent.stop(e);

L.popup表示弹出一个提示框,setContent表示提示框中的内容,这个根据矢量瓦片中的数据内容和本身的业务需求具体修改。setLatLng表示提示框显示的位置,此处表示当前点的位置,也能够修改。固然其实咱们也彻底能够在on函数中实现更复杂的逻辑,如查询数据库获取更多信息进行显示等,具体根据本身的业务而定。来看一下显示的具体效果。

能够看到交互的图标以及交互信息,固然后面的数据也都是矢量瓦片在前端时时渲染的。矢量瓦片显示很流畅,交互也都很顺利。总之此插件效果不错。

3、矢量瓦片解析

咱们知道了如何在前端进行矢量瓦片渲染,下面来看一下矢量瓦片的具体内容,当咱们下载一幅矢量瓦片时能够看到其中都是二进制数据,这是为了减少传输压力进行的压缩,也有一些开源的软件能够进行解压缩,如https://github.com/bertt/mapbox-vector-tile-cs

解析后的部分数据内容以下(只取出了属性等数据):

water
----Polygon
--------class  lake
----Polygon
--------class  lake
----Polygon
--------class  lake
----Polygon
--------class  lake
waterway
----LineString
--------class  stream
--------name  Molly Ann Brook
--------name:latin  Molly Ann Brook
--------name_de  Molly Ann Brook
--------name_en  Molly Ann Brook
--------name_int  Molly Ann Brook
landcover
----MultiPolygon
--------class  wood
--------subclass  wood
mountain_peak
----Point
--------ele  268
--------ele_ft  879
--------name  High Mountain
--------name:latin  High Mountain
--------name_de  High Mountain
--------name_en  High Mountain
--------name_int  High Mountain
--------osm_id  357723234
--------rank  1
boundary
----LineString
--------admin_level  8
--------disputed  0
--------maritime  0
place
----Point
--------class  village
--------name  North Haledon
--------name:latin  North Haledon
--------name_de  North Haledon
--------name_en  North Haledon
--------name_int  North Haledon
--------rank  11
housenumber
----Point
--------housenumber  558
----Point
--------housenumber  65
poi
----Point
--------class  school
--------name  Memorial Elementary School
--------name:latin  Memorial Elementary School
--------name_de  Memorial Elementary School
--------name_en  Memorial Elementary School
--------name_int  Memorial Elementary School
--------rank  1
--------subclass  school
----Point
--------class  grocery
--------name  Super Foodtown 
--------name:latin  Super Foodtown 
--------name_de  Super Foodtown 
--------name_en  Super Foodtown 
--------name_int  Super Foodtown 
--------rank  1
--------subclass  supermarket
----Point
--------class  place_of_worship
--------name  Temple Emanuel of North Jersey
--------name:latin  Temple Emanuel of North Jersey
--------name_de  Temple Emanuel of North Jersey
--------name_en  Temple Emanuel of North Jersey
--------name_int  Temple Emanuel of North Jersey
--------rank  2
--------subclass  place_of_worship

能够看出其中确实包含了多种数据类型,water、boundary、poi等,各类类型下面有空间属性也有一些class、name等属性。主要来看一下poi,能够看出下面有多个点,每一个点有分类以及name等,刚刚我在提示框中显示的正是class和name信息。

4、总结

本文简单讲述了矢量瓦片技术,期待Geotrellis的矢量瓦片早日上线,这样就能验证我矢栅一体化的猜测,真正的统合全部空间数据,进行统一基准下的空间运算。

Geotrellis系列文章连接地址http://www.cnblogs.com/shoufengwei/p/5619419.html

相关文章
相关标签/搜索