实现效果
今天介绍一个有趣的gis小功能:动态轨迹播放!效果就像这样:
这效果看着还很丝滑!别急 ,接下来教你怎么实现 。代码示例基于parcel打包工具和es6语法 ,本文假设你已经掌握相关知识和技巧 。
gis初学者可能对openlayers(后面简称ol)不熟悉 ,这里暂时不介绍ol了 ,直接上代码 ,先体验下感觉 。
创建一个地图容器
引入地图相关对象
import Map from ol/Map;
import View from ol/View;
import XYZ from ol/source/XYZ;
import {Tile as TileLayer, Vector as VectorLayer} from ol/layer;
创建地图对象
const center = [-5639523.95, -3501274.52];
const map = new Map({
target: document.getElementById(map),
view: new View({
center: center,
zoom: 10,
minZoom: 2,
maxZoom: 19,
}),
layers: [
new TileLayer({
source: new XYZ({
attributions: attributions,
url: ?key= + key,
tileSize: 512,
}),
}),
],
});
创建一条线路
画一条线路
可以用这个geojson网站随意画一条线 ,然后把数据内容复制下来 ,保存为json文件格式 ,作为图层数据添加到地图容器中 。
你可以用异步加载的方式 ,也可以用require方式 ,这里都介绍下吧:
// fetch
fetch(data/route.json).then(function (response) {
response.json().then(function (result) {
const polyline = result.routes[0].geometry;
}),
};
// require
var roadData = require(data/route.json)
后面基本一样了 ,就以fetch为准 ,现在把线路加载的剩余部分补充完整:
fetch(data/route.json).then(function (response) {
response.json().then(function (result) {
const polyline = result.routes[0].geometry;
// 线路数据坐标系转换
const route = new Polyline({
factor: 1e6,
}).readGeometry(polyline, {
dataProjection: EPSG:4326,
featureProjection: EPSG:3857,
});
// 线路图层要素
const routeFeature = new Feature({
type: route,
geometry: route,
});
// 起点要素
const startMarker = new Feature({
type: icon,
geometry: new Point(route.getFirstCoordinate()),
});
// 终点要素
const endMarker = new Feature({
type: icon,
geometry: new Point(route.getLastCoordinate()),
});
// 取起点值
const position = startMarker.getGeometry().clone();
// 游标要素
const geoMarker = new Feature({
type: geoMarker,
geometry: position,
});
// 样式组合
const styles = {
// 路线
route: new Style({
stroke: new Stroke({
width: 6,
color: [237, 212, 0, 0.8],
}),
}),
icon: new Style({
image: new Icon({
anchor: [0.5, 1],
src: data/icon.png,
}),
}),
geoMarker: new Style({
image: new CircleStyle({
radius: 7,
fill: new Fill({color: black}),
stroke: new Stroke({
color: white,
width: 2,
}),
}),
}),
};
// 创建图层并添加以上要素集合
const vectorLayer = new VectorLayer({
source: new VectorSource({
features: [routeFeature, geoMarker, startMarker, endMarker],
}),
style: function (feature) {
return styles[feature.get(type)];
},
});
// 在地图容器中添加图层
map.addLayer(vectorLayer);
以上代码很完整,我加了注释 ,整体思路总结如下:
先加载路线数据
构造路线 、起始点及游标对应图层要素对象
构造图层并把要素添加进去
在地图容器中添加图层
添加起 、终点
这个上面的代码已经包括了 ,我这里列出来是为了让你更清晰,就是startMarker和endMarker对应的代码 。
添加小车
同样的 ,这里的代码在上面也写过了 ,就是geoMarker所对应的代码 。
准备开车
线路有了 ,车也有了 ,现在就到了激动人心的开车时刻了 ,接下来才是本文最核心的代码!
const speedInput = document.getElementById(speed);
const startButton = document.getElementById(start-animation);
let animating = false;
let distance = 0;
let lastTime;
function moveFeature(event) {
const speed = Number(speedInput.value);
// 获取当前渲染帧状态时刻
const time = event.frameState.time;
// 渲染时刻减去开始播放轨迹的时间
const elapsedTime = time - lastTime;
// 求得距离比
distance = (distance + (speed * elapsedTime) / 1e6) % 2;
// 刷新上一时刻
lastTime = time;
// 反减可实现反向运动 ,获取坐标点
const currentCoordinate = route.getCoordinateAt(
distance > 1 ? 2 - distance : distance
);
position.setCoordinates(currentCoordinate);
// 获取渲染图层的画布
const vectorContext = getVectorContext(event);
vectorContext.setStyle(styles.geoMarker);
vectorContext.drawGeometry(position);
map.render();
}
function startAnimation() {
animating = true;
lastTime = Date.now();
startButton.textContent = Stop Animation;
vectorLayer.on(postrender, moveFeature);
// 隐藏小车前一刻位置同时触发事件
geoMarker.setGeometry(null);
}
function stopAnimation() {
animating = false;
startButton.textContent = 开车了;
// 将小车固定在当前位置
geoMarker.setGeometry(position);
vectorLayer.un(postrender, moveFeature);
}
startButton.addEventListener(click, function () {
if (animating) {
stopAnimation();
} else {
startAnimation();
}
});
简单说下它的原理就是利用postrender事件触发一个函数 ,这个事件本来是地图渲染结束事件 ,但是它的回调函数中 ,小车的坐标位置一直在变 ,那就会不停地触发地图渲染 ,当然最终也会触发postrender 。这样就实现的小车沿着轨迹的动画效果了 。这段代码有点难理解 ,最好自己尝试体验下,比较难理解部分我都加上了注释 。
好了 ,ol动态巡查已经介绍完了 ,动手试下吧!看你的车能否开起来?
完整代码
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Marker Animation</title>
<!-- Pointer events polyfill for old browsers, see https://caniuse.com/#feat=pointer -->
<script src="https://unpkg.com/elm-pep"></script>
<style>
.map {
width: 100%;
height:400px;
}
</style>
</head>
<body>
<div id="map" class="map"></div>
<label for="speed">
speed:
<input id="speed" type="range" min="10" max="999" step="10" value="60">
</label>
<button id="start-animation">Start Animation</button>
<script src="https://www.jb51.net/article/main.js"></script>
</body>
</html>
main.js
import ol/ol.css;
import Feature from ol/Feature;
import Map from ol/Map;
import Point from ol/geom/Point;
import Polyline from ol/format/Polyline;
import VectorSource from ol/source/Vector;
import View from ol/View;
import XYZ from ol/source/XYZ;
import {
Circle as CircleStyle,
Fill,
Icon,
Stroke,
Style,
} from ol/style;
import {Tile as TileLayer, Vector as VectorLayer} from ol/layer;
import {getVectorContext} from ol/render;
const key = Get your own API key at https://www.maptiler.com/cloud/;
const attributions =
<a href="https://www.maptiler.com/copyright/" rel="external nofollow" target="_blank">© MapTiler</a> +
<a href="https://www.openstreetmap.org/copyright" rel="external nofollow" target="_blank">© OpenStreetMap contributors</a>;
const center = [-5639523.95, -3501274.52];
const map = new Map({
target: document.getElementById(map),
view: new View({
center: center,
zoom: 10,
minZoom: 2,
maxZoom: 19,
}),
layers: [
new TileLayer({
source: new XYZ({
attributions: attributions,
url: ?key= + key,
tileSize: 512,
}),
}),
],
});
// The polyline string is read from a JSON similiar to those returned
// by directions APIs such as Openrouteservice and Mapbox.
fetch(data/polyline/route.json).then(function (response) {
response.json().then(function (result) {
const polyline = result.routes[0].geometry;
const route = new Polyline({
factor: 1e6,
}).readGeometry(polyline, {
dataProjection: EPSG:4326,
featureProjection: EPSG:3857,
});
const routeFeature = new Feature({
type: route,
geometry: route,
});
const startMarker = new Feature({
type: icon,
geometry: new Point(route.getFirstCoordinate()),
});
const endMarker = new Feature({
type: icon,
geometry: new Point(route.getLastCoordinate()),
});
const position = startMarker.getGeometry().clone();
const geoMarker = new Feature({
type: geoMarker,
geometry: position,
});
const styles = {
route: new Style({
stroke: new Stroke({
width: 6,
color: [237, 212, 0, 0.8],
}),
}),
icon: new Style({
image: new Icon({
anchor: [0.5, 1],
src: data/icon.png,
}),
}),
geoMarker: new Style({
image: new CircleStyle({
radius: 7,
fill: new Fill({color: black}),
stroke: new Stroke({
color: white,
width: 2,
}),
}),
}),
};
const vectorLayer = new VectorLayer({
source: new VectorSource({
features: [routeFeature, geoMarker, startMarker, endMarker],
}),
style: function (feature) {
return styles[feature.get(type)];
},
});
map.addLayer(vectorLayer);
const speedInput = document.getElementById(speed);
const startButton = document.getElementById(start-animation);
let animating = false;
let distance = 0;
let lastTime;
function moveFeature(event) {
const speed = Number(speedInput.value);
const time = event.frameState.time;
const elapsedTime = time - lastTime;
distance = (distance + (speed * elapsedTime) / 1e6) % 2;
lastTime = time;
const currentCoordinate = route.getCoordinateAt(
distance > 1 ? 2 - distance : distance
);
position.setCoordinates(currentCoordinate);
const vectorContext = getVectorContext(event);
vectorContext.setStyle(styles.geoMarker);
vectorContext.drawGeometry(position);
// tell OpenLayers to continue the postrender animation
map.render();
}
function startAnimation() {
animating = true;
lastTime = Date.now();
startButton.textContent = Stop Animation;
vectorLayer.on(postrender, moveFeature);
geoMarker.setGeometry(null);
}
function stopAnimation() {
animating = false;
startButton.textContent = 开车了;
geoMarker.setGeometry(position);
vectorLayer.un(postrender, moveFeature);
}
startButton.addEventListener(click, function () {
if (animating) {
stopAnimation();
} else {
startAnimation();
}
});
});
});
package.json
{
"name": "feature-move-animation",
"dependencies": {
"ol": "6.9.0"
},
"devDependencies": {
"parcel": "^2.0.0-beta.1"
},
"scripts": {
"start": "parcel index.html",
"build": "parcel build --public-url . index.html"
}
}
参考资源:
https://openlayers.org/en/latest/examples/feature-move-animation.html
以上就是vue利用openlayers实现动态轨迹的详细内容,更多关于vue openlayers动态轨迹的资料请关注本站其它相关文章!
声明:本站所有文章 ,如无特殊说明或标注 ,均为本站原创发布 。任何个人或组织 ,在未征得本站同意时 ,禁止复制 、盗用 、采集 、发布本站内容到任何网站 、书籍等各类媒体平台 。如若本站内容侵犯了原著者的合法权益 ,可联系我们进行处理 。