引言
最近在學習可視化的東西,這讓我想起了一些以前用過的圖表庫,其實我在日常做的大多是普通的需求,可視化方面應用的並不多,只是偶爾會因為個別特殊的需求,去借助一些圖表庫來實現圖表的展示,這些普通的圖表庫,在使用上都大差不差,並沒有什麼太大的區別,但是某些特殊的圖表庫,比如地圖庫,在使用上和一些普通的圖表,還是存在一些不同,現在想一想還是需要做一些記錄,因為我沒有在當時使用的時候及時記錄,導致我現在對OpenLayers的使用也有一點模糊了,只能藉助代碼來回憶,所以我現在把它的簡單使用做一次整理。
其實一開始我選擇使用的是比較普遍的高德地圖,但是需求做到後面,我才意識到當時做的項目是要部署在內網的,當時就有點傻眼了,因為我對WebGIS也並不熟悉,所以就上網匆匆的搜索有什麼容易上手使用的開源地圖庫,然後鎖定了OpenLayers這個庫。
OpenLayers使用起來不像高德地圖那麼方便,因為部署的是內網環境,需要自己準備瓦片服務,還記得當時下載地圖瓦片也下載了很久,因為要準備不同比例的地圖瓦片,不過幸好當時只需要下載杭州一個城市的瓦片。
準備工作
雖然之前我是在React的項目中使用OpenLayers的,但OpenLayers的使用與項目的具體框架並沒有太大的關係,所以我們只需要使用script標籤引入OpenLayers,就可以在項目中使用這個開源地圖庫了。
因此我們的準備工作,只需要一個引入OpenLayers的頁面,然後在頁面上準備一個div,來作為地圖的容器就可以了,另外在這個例子中我使用了systemjs來進行瀏覽器端的包管理,不用也是可以的;當然,地圖瓦片也是需要提前準備的。
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>OpenLayers demo</title>
<script type="systemjs-importmap">
{
"imports": {
"ol": "https://cdn.jsdelivr.net/npm/ol@v8.2.0/dist/ol.js"
}
}
</script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ol@v8.2.0/ol.css">
<script src="https://cdn.jsdelivr.net/npm/systemjs@6.8.3/dist/system.js"></script>
<script src="https://cdn.jsdelivr.net/npm/systemjs@6.8.3/dist/extras/use-default.js"></script>
</head>
<body>
<div id="map" style="width: 500px; height: 500px;"></div>
</body>
</html>
地圖瓦片
地圖瓦片是什麼呢?簡單來説,它是地圖視圖的組成單元,也就是説,地圖的視圖是由一張張的瓦片拼湊而成。但是地圖的使用一般而言不是靜態的,通常在使用中我們需要對地圖進行縮放交互,來進行更細緻的觀察。所以我們一般需要準備多個比例下的地圖瓦片,具體的可以根據需求來確定。
雖然當時我查到可以由後端部署服務來提供瓦片,但因為當時比較匆忙,我沒來得及仔細研究,又覺得説本來這塊工作量之前也並不瞭解,前端這邊如果能直接處理就處理了,所以就用地圖瓦片下載器自己去下載了瓦片,然後部署到一個靜態服務下面;現在對於demo的編寫倒也是一個好處,可以方便前端的獨立展示。
實現效果
因為是簡單的使用,所以這裏我們主要實現地圖的展示、放大縮小等簡單的功能,以及一些簡單的交互處理。
具體實現
總體來説,Openlayers的使用並不複雜,普通的使用通過查閲API文檔完全可以應對。
- 首先我們提前把下載好的地圖瓦片放到服務目錄下。
-
然後是最基本的,使用
System.import方法引入ol依賴const ol = await System.import('ol'); -
然後我們關注三個主要的類,分別是Map、View和TileLayer,這是我們用於構築地圖的主要部分
const OlMap = ol.Map, View = ol.View, TileLayer = ol.layer.Tile; -
現在我們就可以通過new Map來創建新的地圖對象
const map = new OlMap({ target: 'map', view: new View({ center: [120.212, 30.208], projection: 'EPSG:4326', zoom: 9, maxZoom: 17, minZoom: 7 }), });其中target用於指定盛裝地圖的容器;
在創建地圖對象的時候,會使用到View這個類,代表地圖的二維視圖。我們可以在視圖中通過經緯度數組指定視圖的中心,View默認使用EPSG3857座標系設置,我們也可以通過projection選項來修改座標系,之前我使用的時候比較匆忙,沒有注意到這一塊,還很曲折的通過
fromEPSG4326方法來把4326的經緯度轉換為3857座標系;zoom選項用於定義視圖初始分辨率的縮放級別,這裏我當時是用了9這個級別,我感覺比較合適,當然具體的設置要看項目需求;然後我們可以通過maxZoom和minZoom這兩個選項限制最大和最小的縮放級別。現在我們就可以看到,頁面上其實已經生成地圖容器了,已經能看到放大縮小的操作按鈕了,只是説還沒有貼上瓦片,所以這時候的地圖還比較抽象。
-
接下來我們就需要用到之前準備的瓦片了,用於給map對象設置layers圖層
layers選項接收的是一個數組,也就是説可以給地圖配置多個圖層;這裏我們使用剛剛引入的TileLayer這個類來創建一個圖層;另外我們還需要使用一個XYZ的類,來指定瓦片服務的地址。// ... const XYZ = ol.source.XYZ; const map = new OlMap({ target: 'map', view: new View({ center: [120.212, 30.208], projection: 'EPSG:4326', zoom: 9, maxZoom: 17, minZoom: 7 }), layers: [ new TileLayer({ source: new XYZ({ url: './maps/{z}/{x}/{y}.png' }) }) ] });至此我們就可以在頁面上看到地圖的展示了,打開控制枱我們也可以看到對地圖瓦片的請求,請求的是
maps/9目錄下的瓦片,我們也能注意到,有一些404的請求,這是因為view圖層中的部分地圖瓦片我們沒有準備,通過在頁面上檢查元素,也可以看到map容器中確實存在一部分沒有貼上瓦片,這通常來説沒什麼關係,可以不用管;可以看到當使用鼠標滾動縮放地圖的時候,也會去請求相應縮放比例的地圖瓦片。 -
添加簡單的交互事件
最後來添加一些簡單的交互。
之前我做的需求中需要根據接口返回的數據批量標註地圖上的點,但是因為現在沒有數據,這裏就實現一些簡單的交互吧。const olExtent = ol.extent; map.on('moveend', e => { console.log('zoom', map.frameState_.viewState.zoom); const extent = map.frameState_.extent; console.log('extent', extent); console.log('TopLeft', olExtent.getTopLeft(extent)); console.log('BottomRight', olExtent.getBottomRight(extent)); });我們可以通過zoom獲取視圖的縮放級別,通過extent獲取視圖的經緯度範圍,還可以進一步通過extent的getTopLeft和getBottomRight分別獲取左上角的經緯度和右下角的經緯度;這樣我們就可以在縮放視圖和移動圖層時根據視圖的經緯度範圍來加載相應的數據。
const Feature = ol.Feature;
const Point = ol.geom.Point;
const Style = ol.style.Style,
CircleStyle = ol.style.Circle,
Fill = ol.style.Fill,
Stroke = ol.style.Stroke;
const VectorSource = ol.source.Vector,
VectorLayer = ol.layer.Vector;
let count = 0;
map.on('click', e => {
const features = [];
console.log(e.coordinate); // 獲取座標
const iconFeature = new Feature({
geometry: new Point(e.coordinate),
name: count ++,
location: e.coordinate
});
const style = new Style({
image: new CircleStyle({
radius: 10,
fill: new Fill({
color: '#f49d41'
}),
stroke: new Stroke({
color: '#836365',
width: 1
})
})
});
iconFeature.setStyle(style);
features.push(iconFeature);
const vectorSource = new VectorSource({
features
});
const vectorLayer = new VectorLayer({
source: vectorSource,
opacity: 0.8
});
map.addLayer(vectorLayer);
});
我們還可以在處理地圖的鼠標點擊事件時,獲取鼠標點對應的經緯度,通過Feature類給地圖添加標註,再通過Style類給標註設置樣式;也可以在添加新標註前移除舊的標註。
const layers = map.getLayers();
layers.forEach(item => {
if(item instanceof VectorLayer) map.removeLayer(item);
});
到這裏我們就完成了OpenLayers的簡單使用,如果有感興趣的小夥伴,可以去OpenLayers的GitHub和官方文檔再去進一步的瞭解。
以下是運行效果:
整體代碼參考這個CodePen