本文所涉及技術與Geotrellis並沒有太大關係,僅是矢量瓦片前端渲染和加載技術,可是其實我這是在爲Geotrellis的矢量瓦片作鋪墊。不少人可能會說,Geotrellis爲何要搞矢量瓦片,這不就是前端展現嗎。其實否則,首先Geotrellis能夠用分佈式技術進行快速矢量瓦片切割,固然這不是主要的,由於單臺服務器基本也能很快處理矢量瓦片的切割,重要的是Geotrellis可使用矢量瓦片進行空間計算,這樣能夠矢柵一體化,矢量瓦片和柵格瓦片同時進行計算,這個東西就厲害了,將大大的提升空間數據分析的可能性。固然這只是我我的的見解,有待後續研究,而且Geotrellis的矢量瓦片還並在測試當中。本文僅介紹前端矢量瓦片技術。css
目前高德、百度等互聯網地圖基本都使用了矢量瓦片技術。先來看一下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
這些就是矢量瓦片的優點,固然不是說矢量瓦片絕對是個好東西,任何事情都要辯證的區看待,對待任何問題都要深刻研究,找出最優解。如柵格數據(遙感影像等)永遠須要使用柵格瓦片,某些不須要交互、不怎麼變化等狀況的矢量數據也可使用柵格瓦片。數據庫
矢量瓦片的生成還未研究,本文只是調用OSM公開發布的矢量瓦片進行前端展現試驗。canvas
目前開源中矢量瓦片作的比較好的是Mapbox,各類渲染技術也基本以Mapbox定義的矢量瓦片標準爲標準。Leaflet有多款插件支持矢量瓦片,Leaftlet是一款開源的前端地圖渲染引擎,主要支持的是柵格瓦片。綜合分析以後我選用了Leaflet.VectorGrid插件進行矢量瓦片的渲染,Github地址https://github.com/IvanSanchez/Leaflet.VectorGrid。瀏覽器
除了正常的Leftlet所需的js以及css文件外(具體請自行搜索),還需添加一下語句引入vectorgrid的js文件。服務器
<script src="https://unpkg.com/leaflet.vectorgrid@1.2.0"></script>
固然你能夠直接將此文件下載到本地引入。在Github中也有相應的示例能夠參考。
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/">© OpenMapTiles</a>, <a href="http://www.openstreetmap.org/copyright">© 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函數中實現更復雜的邏輯,如查詢數據庫獲取更多信息進行顯示等,具體根據本身的業務而定。來看一下顯示的具體效果。
能夠看到交互的圖標以及交互信息,固然後面的數據也都是矢量瓦片在前端時時渲染的。矢量瓦片顯示很流暢,交互也都很順利。總之此插件效果不錯。
咱們知道了如何在前端進行矢量瓦片渲染,下面來看一下矢量瓦片的具體內容,當咱們下載一幅矢量瓦片時能夠看到其中都是二進制數據,這是爲了減少傳輸壓力進行的壓縮,也有一些開源的軟件能夠進行解壓縮,如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信息。
本文簡單講述了矢量瓦片技術,期待Geotrellis的矢量瓦片早日上線,這樣就能驗證我矢柵一體化的猜測,真正的統合全部空間數據,進行統一基準下的空間運算。
Geotrellis系列文章連接地址http://www.cnblogs.com/shoufengwei/p/5619419.html