序
中国有句古话:“授之以鱼不如授之以渔”,所以今天我要记录的主要是关于
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/***/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
坐标系的地图中以米为单位绘制圆等。