开源地图库OpenLayers的简单使用
引言
最近在學(xué)習(xí)可視化的東西,這讓我想起了一些以前用過(guò)的圖表庫(kù),其實(shí)我在日常做的大多是普通的需求,可視化方面應(yīng)用的并不多,只是偶爾會(huì)因?yàn)閭€(gè)別特殊的需求,去借助一些圖表庫(kù)來(lái)實(shí)現(xiàn)圖表的展示,這些普通的圖表庫(kù),在使用上都大差不差,并沒(méi)有什么太大的區(qū)別,但是某些特殊的圖表庫(kù),比如地圖庫(kù),在使用上和一些普通的圖表,還是存在一些不同,現(xiàn)在想一想還是需要做一些記錄,因?yàn)槲覜](méi)有在當(dāng)時(shí)使用的時(shí)候及時(shí)記錄,導(dǎo)致我現(xiàn)在對(duì)OpenLayers的使用也有一點(diǎn)模糊了,只能借助代碼來(lái)回憶,所以我現(xiàn)在把它的簡(jiǎn)單使用做一次整理。
其實(shí)一開始我選擇使用的是比較普遍的高德地圖,但是需求做到后面,我才意識(shí)到當(dāng)時(shí)做的項(xiàng)目是要部署在內(nèi)網(wǎng)的,當(dāng)時(shí)就有點(diǎn)傻眼了,因?yàn)槲覍?duì)WebGIS也并不熟悉,所以就上網(wǎng)匆匆的搜索有什么容易上手使用的開源地圖庫(kù),然后鎖定了OpenLayers這個(gè)庫(kù)。
OpenLayers使用起來(lái)不像高德地圖那么方便,因?yàn)椴渴鸬氖莾?nèi)網(wǎng)環(huán)境,需要自己準(zhǔn)備瓦片服務(wù),還記得當(dāng)時(shí)下載地圖瓦片也下載了很久,因?yàn)橐獪?zhǔn)備不同比例的地圖瓦片,不過(guò)幸好當(dāng)時(shí)只需要下載杭州一個(gè)城市的瓦片。
準(zhǔn)備工作
雖然之前我是在React的項(xiàng)目中使用OpenLayers的,但OpenLayers的使用與項(xiàng)目的具體框架并沒(méi)有太大的關(guān)系,所以我們只需要使用script標(biāo)簽引入OpenLayers,就可以在項(xiàng)目中使用這個(gè)開源地圖庫(kù)了。
因此我們的準(zhǔn)備工作,只需要一個(gè)引入OpenLayers的頁(yè)面,然后在頁(yè)面上準(zhǔn)備一個(gè)div,來(lái)作為地圖的容器就可以了,另外在這個(gè)例子中我使用了systemjs來(lái)進(jìn)行瀏覽器端的包管理,不用也是可以的;當(dāng)然,地圖瓦片也是需要提前準(zhǔn)備的。
<!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" >
<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>
地圖瓦片
地圖瓦片是什么呢?簡(jiǎn)單來(lái)說(shuō),它是地圖視圖的組成單元,也就是說(shuō),地圖的視圖是由一張張的瓦片拼湊而成。但是地圖的使用一般而言不是靜態(tài)的,通常在使用中我們需要對(duì)地圖進(jìn)行縮放交互,來(lái)進(jìn)行更細(xì)致的觀察。所以我們一般需要準(zhǔn)備多個(gè)比例下的地圖瓦片,具體的可以根據(jù)需求來(lái)確定。
雖然當(dāng)時(shí)我查到可以由后端部署服務(wù)來(lái)提供瓦片,但因?yàn)楫?dāng)時(shí)比較匆忙,我沒(méi)來(lái)得及仔細(xì)研究,又覺(jué)得說(shuō)本來(lái)這塊工作量之前也并不了解,前端這邊如果能直接處理就處理了,所以就用地圖瓦片下載器自己去下載了瓦片,然后部署到一個(gè)靜態(tài)服務(wù)下面;現(xiàn)在對(duì)于demo的編寫倒也是一個(gè)好處,可以方便前端的獨(dú)立展示。
實(shí)現(xiàn)效果
因?yàn)槭呛?jiǎn)單的使用,所以這里我們主要實(shí)現(xiàn)地圖的展示、放大縮小等簡(jiǎn)單的功能,以及一些簡(jiǎn)單的交互處理。
具體實(shí)現(xiàn)
總體來(lái)說(shuō),Openlayers的使用并不復(fù)雜,普通的使用通過(guò)查閱API文檔完全可以應(yīng)對(duì)。
-
首先我們提前把下載好的地圖瓦片放到服務(wù)目錄下。
-
然后是最基本的,使用
System.import方法引入ol依賴const ol = await System.import('ol'); -
然后我們關(guān)注三個(gè)主要的類,分別是Map、View和TileLayer,這是我們用于構(gòu)筑地圖的主要部分
const OlMap = ol.Map, View = ol.View, TileLayer = ol.layer.Tile; -
現(xiàn)在我們就可以通過(guò)new Map來(lái)創(chuàng)建新的地圖對(duì)象
const map = new OlMap({ target: 'map', view: new View({ center: [120.212, 30.208], projection: 'EPSG:4326', zoom: 9, maxZoom: 17, minZoom: 7 }), });其中target用于指定盛裝地圖的容器;
在創(chuàng)建地圖對(duì)象的時(shí)候,會(huì)使用到View這個(gè)類,代表地圖的二維視圖。我們可以在視圖中通過(guò)經(jīng)緯度數(shù)組指定視圖的中心,View默認(rèn)使用EPSG3857坐標(biāo)系設(shè)置,我們也可以通過(guò)projection選項(xiàng)來(lái)修改坐標(biāo)系,之前我使用的時(shí)候比較匆忙,沒(méi)有注意到這一塊,還很曲折的通過(guò)
fromEPSG4326方法來(lái)把4326的經(jīng)緯度轉(zhuǎn)換為3857坐標(biāo)系;zoom選項(xiàng)用于定義視圖初始分辨率的縮放級(jí)別,這里我當(dāng)時(shí)是用了9這個(gè)級(jí)別,我感覺(jué)比較合適,當(dāng)然具體的設(shè)置要看項(xiàng)目需求;然后我們可以通過(guò)maxZoom和minZoom這兩個(gè)選項(xiàng)限制最大和最小的縮放級(jí)別。現(xiàn)在我們就可以看到,頁(yè)面上其實(shí)已經(jīng)生成地圖容器了,已經(jīng)能看到放大縮小的操作按鈕了,只是說(shuō)還沒(méi)有貼上瓦片,所以這時(shí)候的地圖還比較抽象。
-
接下來(lái)我們就需要用到之前準(zhǔn)備的瓦片了,用于給map對(duì)象設(shè)置layers圖層
layers選項(xiàng)接收的是一個(gè)數(shù)組,也就是說(shuō)可以給地圖配置多個(gè)圖層;這里我們使用剛剛引入的TileLayer這個(gè)類來(lái)創(chuàng)建一個(gè)圖層;另外我們還需要使用一個(gè)XYZ的類,來(lái)指定瓦片服務(wù)的地址。// ... 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' }) }) ] });
至此我們就可以在頁(yè)面上看到地圖的展示了,打開控制臺(tái)我們也可以看到對(duì)地圖瓦片的請(qǐng)求,請(qǐng)求的是maps/9目錄下的瓦片,我們也能注意到,有一些404的請(qǐng)求,這是因?yàn)関iew圖層中的部分地圖瓦片我們沒(méi)有準(zhǔn)備,通過(guò)在頁(yè)面上檢查元素,也可以看到map容器中確實(shí)存在一部分沒(méi)有貼上瓦片,這通常來(lái)說(shuō)沒(méi)什么關(guān)系,可以不用管;可以看到當(dāng)使用鼠標(biāo)滾動(dòng)縮放地圖的時(shí)候,也會(huì)去請(qǐng)求相應(yīng)縮放比例的地圖瓦片。
-
添加簡(jiǎn)單的交互事件
最后來(lái)添加一些簡(jiǎn)單的交互。
之前我做的需求中需要根據(jù)接口返回的數(shù)據(jù)批量標(biāo)注地圖上的點(diǎn),但是因?yàn)楝F(xiàn)在沒(méi)有數(shù)據(jù),這里就實(shí)現(xiàn)一些簡(jiǎn)單的交互吧。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)); });我們可以通過(guò)zoom獲取視圖的縮放級(jí)別,通過(guò)extent獲取視圖的經(jīng)緯度范圍,還可以進(jìn)一步通過(guò)extent的getTopLeft和getBottomRight分別獲取左上角的經(jīng)緯度和右下角的經(jīng)緯度;這樣我們就可以在縮放視圖和移動(dòng)圖層時(shí)根據(jù)視圖的經(jīng)緯度范圍來(lái)加載相應(yīng)的數(shù)據(jù)。
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); // 獲取坐標(biāo) 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); });我們還可以在處理地圖的鼠標(biāo)點(diǎn)擊事件時(shí),獲取鼠標(biāo)點(diǎn)對(duì)應(yīng)的經(jīng)緯度,通過(guò)Feature類給地圖添加標(biāo)注,再通過(guò)Style類給標(biāo)注設(shè)置樣式;也可以在添加新標(biāo)注前移除舊的標(biāo)注。
const layers = map.getLayers(); layers.forEach(item => { if(item instanceof VectorLayer) map.removeLayer(item); });
到這里我們就完成了OpenLayers的簡(jiǎn)單使用,如果有感興趣的小伙伴,可以去OpenLayers的GitHub和官方文檔再去進(jìn)一步的了解。
以下是運(yùn)行效果:
整體代碼參考這個(gè)CodePen
總結(jié)
以上是生活随笔為你收集整理的开源地图库OpenLayers的简单使用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 华硕 ROG 冰刃 X 电竞主机预售:1
- 下一篇: Chrome扩展的核心:manifest