序
中國有句古話:“授之以魚不如授之以漁”,所以今天我要記錄的主要是關於
OpenLayers
的應用開發思路,而非某一具體的功能或代碼。
OpenLayers
作爲一個開源項目,我們首先需要知道的就是它的 官網 和 源碼地址。官網作爲項目信息的集合地,對開發者來說是一個很好的瞭解項目的入口;而源碼地址則可以幫助我們更好的瞭解代碼邏輯,協助我們定位問題,解決問題。
天才第一步
打開官網,我們會發現對我們有用的部分有三塊:
第一部分:導航欄
導航欄有四個模塊:文檔(Docs
)、示例(Examples
)、接口(API
)、源碼(Code
)。
- 文檔頁:爲我們介紹瞭如何入門、常見問題解答和更多問題。實用性不大,四捨五入可以忽略不計。
- 示例頁:很重要~!如果你是第一次接觸
OpenLayers
,建議把示例頁中的所有示例效果過一遍,好讓我們對其能實現的基本功能有一個簡單的瞭解。同時,示例代碼也是我們在自己後續的項目開發過程中要經常參考的內容。 - 接口頁:重中之重~!!!各位程序員老哥,我想
API
的重要性就不用我說了吧。 - 源碼頁:點擊直接跳轉至項目的
GitHub
地址。源碼於博主而言,可以幫我更好的瞭解功能的實現思路,且在實際開發過程中,能夠很好的幫我定位和解決部分疑難雜症。
第二部分:Hello World
首頁的第二部分爲我們留下了幾個鏈接,通過他們我們很容易開始自己的開發。
Quick Start
這部分爲快速開始,通常情況下是沒有實用意義的,就像 Hello World
一樣。打開頁面,我們可以看到用傳統方式搭建項目的示例。(這裏不太推薦這種項目構建方式,因爲如果真的使用這種非官方示例中的方式,會導致我們在後續的開發過程中遇到問題或開發新功能時,不容易調試或借鑑別人的方法)
Tutorials
這個部分爲指南,點擊鏈接後,我們可以看到四個部分:
- 構建一個OpenLayers應用程序(介紹瞭如何在
Node
環境下構建一個OpenLayers
應用程序,這是我們正確入門姿勢) - 基本概念
- OpenLayers的一些背景
- 柵格投影
Workshop
這部分爲一個爲詳細教程,點擊鏈接後選擇自己喜歡的語言(能看懂英文就不錯了~!)進行更加系統的瞭解和學習。
第三部分:迭代史
第三部分,這裏羅列了 OpenLayers
從第二版到第五版的相關內容和地址,爲使用之前版本的老鐵提供相關文檔幫助。
甩代碼
幹聊了半天,是時候來的實際的了。以下代碼爲博主自己封裝的部分功能函數,裏面包含了:
- 座標系設置
- 基礎矢量圖層對象的創建與加載
- 自定義圓形特徵層
- 圓形特徵層的樣式設置
- 圓形特徵層的批量創建
- 圓形圖層的創建
- 圓圖層的動態添加
- 點特徵層的創建
- 點特徵層的樣式設置
- 點特徵層的批量創建
- 點圖層的創建
- 點圖層的動態添加
Icon
選中高亮overlay
圖層創建(用於Icon
鼠標懸浮事件效果)- 地圖創建
- 鼠標
hover
事件 - 鼠標點擊事件
- 動態清除已渲染圖層
olFun.js
// 導入 Openlayers 樣式
import 'ol/ol.css';
// 導入 Icon 使用的圖片
import dot_png from "../img/dot.png";
// 導入 Openlayers 模塊
import {get, transform, fromLonLat} from 'ol/proj';
import Map from 'ol/Map'
import View from "ol/View";
import Feature from 'ol/Feature';
import {Tile as TileLayer, Vector as VectorLayer} from 'ol/layer';
import TileWMS from 'ol/source/TileWMS';
import VectorSource from 'ol/source/Vector';
import Circle from "ol/geom/Circle";
import Point from 'ol/geom/Point';
import {Icon, Style, Fill, Stroke} from 'ol/style';
import Text from 'ol/style/Text';
import Overlay from 'ol/Overlay';
// 導入自定義函數
import {stationInfoLayers} from "./calledFunctions";
import {postAjax, replayEvent} from "./originFunctions";
// 創建墨卡託投影座標系
const projection = get("EPSG:3857");
const vectorLayerArr = new Array();
// 創建基礎矢量圖層
const baseLayer = new TileLayer({
source: new TileWMS({
url: 'http://127.0.0.1:8080/earthview/services/Map/MapService/WMS',
params: {LAYERS: 'Map', CRS: projection},
projection: projection
})
});
baseLayer.set("name", "BasicLayer", true);
// 創建圓形特徵層
let createCircleFeature = function(coordinate, radius, name){
if (!coordinate && !radius && !name){
return null;
}
return new Feature({
geometry: new Circle(transform(coordinate, "EPSG:4326", "EPSG:3857"), radius, "XY"),
name: name
})
};
// 創建圓形特徵層樣式
let createCircleStyle = function(color){
if (!color){
return null;
}
return new Style({
// 設置填充顏色
fill: new Fill({
// 支持 CSS 中其他顏色設置方式 eg: 十六進制; rgb; rgba
color: color
})
})
};
// 創建待渲染的點特徵層數組
let createCircleFeatures = function(circles){
let circleFeatures = [];
$.each(circles, function (idx, item) {
let circleFeature = createCircleFeature(item.coordinate, (item.radius)+10000, "circle" + idx);
circleFeature.setStyle(createCircleStyle(item.color));
circleFeatures.push(circleFeature);
});
return circleFeatures;
};
// 創建矢量圓圖層
let createCircleVectorLayer = function(features){
let circleLayer = new VectorLayer({
source: new VectorSource({
features: features
}),
opacity: 0.5
});
circleLayer.set("name", "CircleLayer", true);
vectorLayerArr.push(circleLayer);
map.addLayer(circleLayer);
};
// 渲染圓圖層
let renderCircleLayer = function(circles){
let circleFeatures = createCircleFeatures(circles);
createCircleVectorLayer(circleFeatures);
};
// 創建點特徵層
let createPointFeature = function(coordinate, name){
if (!coordinate && !name){
return null;
}
return new Feature({
geometry: new Point(fromLonLat(coordinate)),
name: name
})
};
// 創建點特徵層樣式
let createPointStyle = function(color, text, scale){
let pointStyle = new Style({
image: new Icon({
color: color,
crossOrigin: 'anonymous',
// 對靜態資源的引用需要引入,否則將無法把 js 中用到的圖片編譯至 dist 中
src: dot_png,
scale: scale
})
});
// 設置點註記名稱及其樣式
if(text){
pointStyle.setText(new Text({
text: text,
textAlign: 'left',
offsetX: 10,
// 字體設置支持 CSS 通用字體設置方式
font: "bold 20px 黑體,SimHei",
fill: new Fill({
color: color
}),
stroke: new Stroke({
color: "#000000",
width: 2
})
}))
}
return pointStyle;
};
// 創建待渲染的點特徵層數組
let createPointFeatures = function(points){
let pointFeatures = [];
$.each(points, function (idx, item){
let feature = createPointFeature([item.lon, item.lat], item.name);
feature.setStyle(createPointStyle(item.color, item.text, item.scale));
feature.setId(item.evid);
feature.set("intensity", item.intensity, true);
pointFeatures.push(feature);
});
return pointFeatures;
};
// 創建矢量點圖層
let createPointsVectorLayer = function(features){
let pointLayer = new VectorLayer({
source: new VectorSource({
features: features
})
});
pointLayer.set("name", "PointLayer", true);
pointLayer.setZIndex(1);
vectorLayerArr.push(pointLayer);
map.addLayer(pointLayer);
};
// 渲染點圖層
let renderPointLayer = function (points) {
let pointFeatures = createPointFeatures(points);
createPointsVectorLayer(pointFeatures);
};
// 這個函數有點牛逼——用來創建並渲染 Icon 的反色 Canvas 圖片
let createAndRenderIconCanvasOverlay = function (feature) {
if (feature) {
var image = feature.getStyle().getImage().getImage();
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
canvas.width = image.width;
canvas.height = image.height;
context.drawImage(image, 0, 0, image.width, image.height);
var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
var data = imageData.data;
for (var i = 0, ii = data.length; i < ii; i = i + (i % 4 == 2 ? 2 : 1)) {
data[i] = 255 - data[i];
}
context.putImageData(imageData, 0, 0);
feature.setStyle(new Style({
image: new Icon({
crossOrigin: 'anonymous',
src: undefined,
img: canvas,
imgSize: canvas ? [canvas.width, canvas.height] : undefined,
})
}));
}
};
// 創建 view
const view = new View({
center: fromLonLat([105.14805, 35.26971]),
projection: projection,
zoom: 5,
maxZoom: 14,
minZoom: 4
});
// 創建 overlay
let element = document.getElementById('stationNamePopup');
let popup = new Overlay({
element: element,
positioning: 'bottom-center',
stopEvent: false,
offset: [0, -10]
});
// 創建 Map
const map = new Map({
layers: [baseLayer],
target: "sceneControlDiv",
view: view,
overlays: [popup],
controls: []
});
// 鼠標 hover 事件
map.on('pointermove', function(evt) {
let feature = map.forEachFeatureAtPixel(evt.pixel,
function(feature) {
return feature;
});
if (feature) {
let coordinates = feature.getGeometry().getCoordinates();
popup.setPosition(coordinates);
let featureName = feature.get('name');
if(featureName){
element.innerHTML = `<div class="stationNameStyle">`+feature.get('name')+`</div>`;
}
} else {
element.innerHTML = "";
}
});
// 鼠標 click 事件
map.on('click', function(evt) {
let feature = map.forEachFeatureAtPixel(evt.pixel,
function(feature) {
return feature;
});
if (feature) {
let flag = feature.getId().split("_");
// ↓↓↓↓↓↓↓↓↓↓---------- 普通信息彈窗頁面 ----------↓↓↓↓↓↓↓↓↓↓
if (flag.length < 2) {
layui.layer.closeAll('tips');
stationInfoLayers(feature.getId(), feature.get('intensity'));
} else if("1" === flag[0]){
// ↓↓↓↓↓↓↓↓↓↓---------- 事件回放 ----------↓↓↓↓↓↓↓↓↓↓
let stations = postAjax("eventStation/selectByDateAndSource", false,
{"source":flag[1],"date":flag[2]});
if(stations.length > 0){
replayEvent(stations, flag[2], flag[1]);
}
}
}
});
// 清除已渲染圖層
function destoryVectorLayer(layerName) {
let maxIndex = vectorLayerArr.length;
for (let i = 0; i < maxIndex; i++) {
let vectorLayer = vectorLayerArr.pop();
if("PointLayer" === layerName && "PointLayer" === vectorLayer.get("name")){
map.removeLayer(vectorLayer);
break;
}else if("CircleLayer" === layerName && "CircleLayer" === vectorLayer.get("name")) {
map.removeLayer(vectorLayer);
break;
}else{
map.removeLayer(vectorLayer);
}
}
}
export {map, renderPointLayer, renderCircleLayer, destoryVectorLayer, createAndRenderIconCanvasOverlay}
注意:
Icon
對象的如果需要引用本地的圖片,必須先通過import
導入纔行,否則圖片樣式會找不到。導入方式詳見代碼第四行。- 一定、一定、一定要充分利用官方示例和
API
~!!! - 本文使用的座標系爲
Web_Mercator
這樣方便繪製不定半徑(單位:米)的圓,如果使用WGS84
座標系在繪製不定半徑圓的時候,會因半徑參數變爲弧度增加繪製難度。 - 這裏 有另外一些博主在開發過程中編寫的代碼包括
Quick Start
中介紹的非Node
方式創建項目、加載天地圖數據、在WGS84
座標系的地圖中以米爲單位繪製圓等。