这几年一直在做网页端地图相关的功能开发、设计,最近有个新的展示功能,需要在地图上根据经纬度显示一个会动的雷达扫描图,简单研究了一下,具体代码如下:
(所有我认为需要解释的都在代码中注释了,大家可以直接查看代码,有问题可以在文末留言~)
Openlayers 4
<!-- 代码片作者:Carl Zhang,转载请注明。 -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>OpenLayers 4 Radar Animation</title>
<link rel="stylesheet" href="https://openlayers.org/en/v4.6.5/css/ol.css" type="text/css">
<style>
.map {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div id="map" class="map"></div>
<script src="https://openlayers.org/en/v4.6.5/build/ol.js"></script>
<script>
// 雷达中心点的经纬度坐标
var radarCenter = [116.42022722759157, 39.819625317442345];
var radarCenterProj = ol.proj.fromLonLat(radarCenter); // 将经纬度坐标转换为投影坐标
// 初始化地图
var map = new ol.Map({
target: 'map',
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
view: new ol.View({
center: ol.proj.fromLonLat(radarCenter), // 中心点经纬度
zoom: 15
})
});
// 创建一个Canvas图层
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
// 获取150米半径对应的像素值
function getRadiusInPixels() {
var view = map.getView();
var resolution = view.getResolution();
var center = ol.proj.toLonLat(view.getCenter(), 'EPSG:3857');
var metersPerUnit = ol.proj.METERS_PER_UNIT[map.getView().getProjection().getUnits()];
// 150表示150米半径
return 150 / (resolution * metersPerUnit);
}
// 绘制雷达动画的函数
function drawRadar(context, centerX, centerY, radius, angle) {
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.fillStyle = 'rgba(0, 0, 255, 0.1)';
context.fill();
context.beginPath();
context.moveTo(centerX, centerY);
context.arc(centerX, centerY, radius, angle, angle + Math.PI / 6, false);
context.closePath();
context.fillStyle = 'rgba(0, 0, 255, 0.5)';
context.fill();
}
// 更新雷达动画的函数
var angle = 0;
function updateRadar() {
angle += Math.PI / 60;
radarLayer.getSource().changed();
}
// 创建一个图层来显示雷达动画
var radarLayer = new ol.layer.Image({
source: new ol.source.ImageCanvas({
canvasFunction: function(extent, resolution, pixelRatio, size, projection) {
canvas.width = size[0];
canvas.height = size[1];
context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
var center = map.getPixelFromCoordinate(radarCenterProj);
var radius = getRadiusInPixels();
drawRadar(context, center[0], center[1], radius, angle);
return canvas;
},
projection: 'EPSG:3857',
ratio: 1
})
});
map.addLayer(radarLayer);
// 每秒钟更新雷达动画
setInterval(updateRadar, 1000 / 30);
// 确保在地图视图变化时也重新绘制雷达
map.getView().on('change:center', updateRadar);
map.getView().on('change:resolution', updateRadar);
map.on('moveend', updateRadar);
map.on('zoomend', updateRadar);
</script>
</body>
</html>
Openlayers 8
<!-- 代码片作者:Carl Zhang,转载请注明。 --><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>OpenLayers 8 Radar Animation</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ol@latest/ol.css" type="text/css">
<style>
body, html {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
}
.map {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div id="map" class="map"></div>
<script type="module">
import Map from 'https://cdn.jsdelivr.net/npm/[email protected]/Map.js';
import View from 'https://cdn.jsdelivr.net/npm/[email protected]/View.js';
import TileLayer from 'https://cdn.jsdelivr.net/npm/[email protected]/layer/Tile.js';
import ImageLayer from 'https://cdn.jsdelivr.net/npm/[email protected]/layer/Image.js';
import OSM from 'https://cdn.jsdelivr.net/npm/[email protected]/source/OSM.js';
import ImageCanvasSource from 'https://cdn.jsdelivr.net/npm/[email protected]/source/ImageCanvas.js';
import {fromLonLat} from 'https://cdn.jsdelivr.net/npm/[email protected]/proj.js';
// 雷达中心点的经纬度坐标数组、颜色、底色和速度
const radarCenters = [
{coord: [116.42622722759157, 39.816625317442345], color: 'rgba(255, 0, 0, 0.5)', backgroundColor: 'rgba(255, 0, 0, 0.1)', speed: Math.PI / 90},
{coord: [116.42322722759157, 39.819625317442345], color: 'rgba(0, 255, 0, 0.5)', backgroundColor: 'rgba(0, 255, 0, 0.1)', speed: Math.PI / 90},
{coord: [116.42022722759157, 39.822625317442345], color: 'rgba(0, 0, 255, 0.5)', backgroundColor: 'rgba(0, 0, 255, 0.1)', speed: Math.PI / 90}
];
// 将经纬度坐标转换为投影坐标
const radarCentersProj = radarCenters.map(radar => ({
coord: fromLonLat(radar.coord),
color: radar.color,
backgroundColor: radar.backgroundColor,
speed: radar.speed
}));
// 初始化地图
const map = new Map({
target: 'map',
layers: [
new TileLayer({
source: new OSM()
})
],
view: new View({
center: fromLonLat(radarCenters[0].coord), // 中心点经纬度
zoom: 15
})
});
// 创建一个Canvas图层
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
// 获取150米半径对应的像素值
function getRadiusInPixels() {
const view = map.getView();
const resolution = view.getResolution();
const metersPerUnit = view.getProjection().getMetersPerUnit();
return 150 / (resolution * metersPerUnit); // 150米半径
}
// 绘制雷达动画的函数
function drawRadar(context, centerX, centerY, radius, angle, color, backgroundColor) {
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.fillStyle = backgroundColor; // 底色
context.fill();
context.beginPath();
context.moveTo(centerX, centerY);
context.arc(centerX, centerY, radius, angle, angle + Math.PI / 6, false);
context.closePath();
context.fillStyle = color; // 扫描颜色
context.fill();
}
// 更新雷达动画的函数
const angles = radarCenters.map(() => 0);
function updateRadar() {
radarCentersProj.forEach((radar, index) => {
angles[index] += radar.speed;
});
radarLayer.getSource().changed();
}
// 创建一个图层来显示雷达动画
const radarLayer = new ImageLayer({
source: new ImageCanvasSource({
canvasFunction: function(extent, resolution, pixelRatio, size, projection) {
canvas.width = size[0];
canvas.height = size[1];
context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
context.clearRect(0, 0, canvas.width, canvas.height);
const radius = getRadiusInPixels();
radarCentersProj.forEach((radar, index) => {
const center = map.getPixelFromCoordinate(radar.coord);
drawRadar(context, center[0], center[1], radius, angles[index], radar.color, radar.backgroundColor);
});
return canvas;
},
projection: 'EPSG:3857',
ratio: 1
})
});
map.addLayer(radarLayer);
// 使用requestAnimationFrame来实现平滑动画
function animate() {
updateRadar();
requestAnimationFrame(animate);
}
animate();
// 确保在地图视图变化时也重新绘制雷达
map.getView().on('change:center', () => radarLayer.getSource().changed());
map.getView().on('change:resolution', () => radarLayer.getSource().changed());
map.on('moveend', () => radarLayer.getSource().changed());
map.on('zoomend', () => radarLayer.getSource().changed());
</script>
</body>
</html>
具体效果:
(本文为作者原创。转载请注明:转自carlzhang.xyz)