|
实现效果
今天介绍一个有趣的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: 'https://api.maptiler.com/maps/hybrid/{z}/{x}/{y}.jpg?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);
复制代码 以上代码很完整,我加了注释,整体思路总结如下:
- 先加载路线数据
- 构造路线、起始点及游标对应图层要素对象
- 构造图层并把要素添加进去
- 在地图容器中添加图层
添加起、终点
这个上面的代码已经包括了,我这里列出来是为了让你更清晰,就是和对应的代码。
添加小车
同样的,这里的代码在上面也写过了,就是所对应的代码。
准备开车
线路有了,车也有了,现在就到了激动人心的开车时刻了,接下来才是本文最核心的代码!- 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();
- }
- });
复制代码 简单说下它的原理就是利用事件触发一个函数,这个事件本来是地图渲染结束事件,但是它的回调函数中,小车的坐标位置一直在变,那就会不停地触发地图渲染,当然最终也会触发。这样就实现的小车沿着轨迹的动画效果了。这段代码有点难理解,最好自己尝试体验下,比较难理解部分我都加上了注释。
好了,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="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: 'https://api.maptiler.com/maps/hybrid/{z}/{x}/{y}.jpg?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动态轨迹的资料请关注脚本之家其它相关文章!
来源:https://www.jb51.net/article/266689.htm
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作! |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
x
|