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

相關文章
相關標籤/搜索