vue利用openlayers实现动态轨迹
脚本之家 / 编程助手:解决程序员“几乎”所有问题!
脚本之家官方知识库 → 点击立即使用
实现效果
今天介绍一个有趣的gis小功能:动态轨迹播放!效果就像这样:
这效果看着还很丝滑!别急,接下来教你怎么实现。代码示例基于parcel打包工具和es6语法,本文假设你已经掌握相关知识和技巧。
gis初学者可能对openlayers(后面简称ol)不熟悉,这里暂时不介绍ol了,直接上代码,先体验下感觉。
创建一个地图容器
引入地图相关对象
1 2 3 4 | 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' ; |
创建地图对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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方式,这里都介绍下吧:
1 2 3 4 5 6 7 8 | // 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为准,现在把线路加载的剩余部分补充完整:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | 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
所对应的代码。
准备开车
线路有了,车也有了,现在就到了激动人心的开车时刻了,接下来才是本文最核心的代码!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <!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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 | { "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动态轨迹的资料请关注脚本之家其它相关文章!
微信公众号搜索 “ 脚本之家 ” ,选择关注
程序猿的那些事、送书等活动等着你
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 reterry123@163.com 进行投诉反馈,一经查实,立即处理!
相关文章
解决父组件将子组件作为弹窗调用只执行一次created的问题
这篇文章主要介绍了解决父组件将子组件作为弹窗调用只执行一次created的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧2020-07-07
最新评论