翼度科技»论坛 编程开发 JavaScript 查看内容

vue利用openlayers实现动态轨迹

4

主题

4

帖子

12

积分

新手上路

Rank: 1

积分
12
实现效果

今天介绍一个有趣的gis小功能:动态轨迹播放!效果就像这样:

这效果看着还很丝滑!别急,接下来教你怎么实现。代码示例基于parcel打包工具和es6语法,本文假设你已经掌握相关知识和技巧。
gis初学者可能对openlayers(后面简称ol)不熟悉,这里暂时不介绍ol了,直接上代码,先体验下感觉。

创建一个地图容器


引入地图相关对象
  1. import Map from 'ol/Map';
  2. import View from 'ol/View';
  3. import XYZ from 'ol/source/XYZ';
  4. import {Tile as TileLayer, Vector as VectorLayer} from 'ol/layer';
复制代码
创建地图对象
  1. const center = [-5639523.95, -3501274.52];
  2. const map = new Map({
  3.   target: document.getElementById('map'),
  4.   view: new View({
  5.     center: center,
  6.     zoom: 10,
  7.     minZoom: 2,
  8.     maxZoom: 19,
  9.   }),
  10.   layers: [
  11.     new TileLayer({
  12.       source: new XYZ({
  13.         attributions: attributions,
  14.         url: 'https://api.maptiler.com/maps/hybrid/{z}/{x}/{y}.jpg?key=' + key,
  15.         tileSize: 512,
  16.       }),
  17.     }),
  18.   ],
  19. });
复制代码
创建一条线路


画一条线路

可以用这个geojson网站随意画一条线,然后把数据内容复制下来,保存为json文件格式,作为图层数据添加到地图容器中。
你可以用异步加载的方式,也可以用require方式,这里都介绍下吧:
  1. // fetch
  2. fetch('data/route.json').then(function (response) {
  3.   response.json().then(function (result) {
  4.     const polyline = result.routes[0].geometry;
  5.   }),
  6. };
  7. // require
  8. var roadData = require('data/route.json')
复制代码
后面基本一样了,就以fetch为准,现在把线路加载的剩余部分补充完整:
  1. fetch('data/route.json').then(function (response) {
  2.   response.json().then(function (result) {
  3.     const polyline = result.routes[0].geometry;
  4.         // 线路数据坐标系转换
  5.     const route = new Polyline({
  6.       factor: 1e6,
  7.     }).readGeometry(polyline, {
  8.       dataProjection: 'EPSG:4326',
  9.       featureProjection: 'EPSG:3857',
  10.     });
  11.         // 线路图层要素
  12.     const routeFeature = new Feature({
  13.       type: 'route',
  14.       geometry: route,
  15.     });
  16.     // 起点要素
  17.     const startMarker = new Feature({
  18.       type: 'icon',
  19.       geometry: new Point(route.getFirstCoordinate()),
  20.     });
  21.     // 终点要素
  22.     const endMarker = new Feature({
  23.       type: 'icon',
  24.       geometry: new Point(route.getLastCoordinate()),
  25.     });
  26.     // 取起点值
  27.     const position = startMarker.getGeometry().clone();
  28.     // 游标要素
  29.     const geoMarker = new Feature({
  30.       type: 'geoMarker',
  31.       geometry: position,
  32.     });
  33.         // 样式组合
  34.     const styles = {
  35.         // 路线
  36.       'route': new Style({
  37.         stroke: new Stroke({
  38.           width: 6,
  39.           color: [237, 212, 0, 0.8],
  40.         }),
  41.       }),
  42.       'icon': new Style({
  43.         image: new Icon({
  44.           anchor: [0.5, 1],
  45.           src: 'data/icon.png',
  46.         }),
  47.       }),
  48.       'geoMarker': new Style({
  49.         image: new CircleStyle({
  50.           radius: 7,
  51.           fill: new Fill({color: 'black'}),
  52.           stroke: new Stroke({
  53.             color: 'white',
  54.             width: 2,
  55.           }),
  56.         }),
  57.       }),
  58.     };
  59.         // 创建图层并添加以上要素集合
  60.     const vectorLayer = new VectorLayer({
  61.       source: new VectorSource({
  62.         features: [routeFeature, geoMarker, startMarker, endMarker],
  63.       }),
  64.       style: function (feature) {
  65.         return styles[feature.get('type')];
  66.       },
  67.     });
  68.         // 在地图容器中添加图层
  69.     map.addLayer(vectorLayer);
复制代码
以上代码很完整,我加了注释,整体思路总结如下:

  • 先加载路线数据
  • 构造路线、起始点及游标对应图层要素对象
  • 构造图层并把要素添加进去
  • 在地图容器中添加图层

添加起、终点

这个上面的代码已经包括了,我这里列出来是为了让你更清晰,就是
  1. startMarker
复制代码
  1. endMarker
复制代码
对应的代码。

添加小车

同样的,这里的代码在上面也写过了,就是
  1. geoMarker
复制代码
所对应的代码。

准备开车

线路有了,车也有了,现在就到了激动人心的开车时刻了,接下来才是本文最核心的代码!
  1. const speedInput = document.getElementById('speed');
  2.     const startButton = document.getElementById('start-animation');
  3.     let animating = false;
  4.     let distance = 0;
  5.     let lastTime;
  6.     function moveFeature(event) {
  7.       const speed = Number(speedInput.value);
  8.       // 获取当前渲染帧状态时刻
  9.       const time = event.frameState.time;
  10.       // 渲染时刻减去开始播放轨迹的时间
  11.       const elapsedTime = time - lastTime;
  12.       // 求得距离比
  13.       distance = (distance + (speed * elapsedTime) / 1e6) % 2;
  14.       // 刷新上一时刻
  15.       lastTime = time;
  16.           // 反减可实现反向运动,获取坐标点
  17.       const currentCoordinate = route.getCoordinateAt(
  18.         distance > 1 ? 2 - distance : distance
  19.       );
  20.       position.setCoordinates(currentCoordinate);
  21.       // 获取渲染图层的画布
  22.       const vectorContext = getVectorContext(event);
  23.       vectorContext.setStyle(styles.geoMarker);
  24.       vectorContext.drawGeometry(position);
  25.       map.render();
  26.     }
  27.     function startAnimation() {
  28.       animating = true;
  29.       lastTime = Date.now();
  30.       startButton.textContent = 'Stop Animation';
  31.       vectorLayer.on('postrender', moveFeature);
  32.       // 隐藏小车前一刻位置同时触发事件
  33.       geoMarker.setGeometry(null);
  34.     }
  35.     function stopAnimation() {
  36.       animating = false;
  37.       startButton.textContent = '开车了';
  38.       // 将小车固定在当前位置
  39.       geoMarker.setGeometry(position);
  40.       vectorLayer.un('postrender', moveFeature);
  41.     }
  42.     startButton.addEventListener('click', function () {
  43.       if (animating) {
  44.         stopAnimation();
  45.       } else {
  46.         startAnimation();
  47.       }
  48.     });
复制代码
简单说下它的原理就是利用
  1. postrender
复制代码
事件触发一个函数,这个事件本来是地图渲染结束事件,但是它的回调函数中,小车的坐标位置一直在变,那就会不停地触发地图渲染,当然最终也会触发
  1. postrender
复制代码
。这样就实现的小车沿着轨迹的动画效果了。这段代码有点难理解,最好自己尝试体验下,比较难理解部分我都加上了注释。
好了,ol动态巡查已经介绍完了,动手试下吧!看你的车能否开起来?

完整代码

index.html
  1. <!DOCTYPE html>
  2. <html lang="en">
  3.   <head>
  4.     <meta charset="UTF-8">
  5.     <title>Marker Animation</title>
  6.     <!-- Pointer events polyfill for old browsers, see https://caniuse.com/#feat=pointer -->
  7.     <script src="https://unpkg.com/elm-pep"></script>
  8.     <style>
  9.       .map {
  10.         width: 100%;
  11.         height:400px;
  12.       }
  13.     </style>
  14.   </head>
  15.   <body>
  16.     <div id="map" class="map"></div>
  17.     <label for="speed">
  18.       speed:
  19.       <input id="speed" type="range" min="10" max="999" step="10" value="60">
  20.     </label>
  21.     <button id="start-animation">Start Animation</button>
  22.     <script src="main.js"></script>
  23.   </body>
  24. </html>
复制代码
main.js
  1. 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];
  2. const map = new Map({
  3.   target: document.getElementById('map'),
  4.   view: new View({
  5.     center: center,
  6.     zoom: 10,
  7.     minZoom: 2,
  8.     maxZoom: 19,
  9.   }),
  10.   layers: [
  11.     new TileLayer({
  12.       source: new XYZ({
  13.         attributions: attributions,
  14.         url: 'https://api.maptiler.com/maps/hybrid/{z}/{x}/{y}.jpg?key=' + key,
  15.         tileSize: 512,
  16.       }),
  17.     }),
  18.   ],
  19. });// 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.   "name": "feature-move-animation",
  3.   "dependencies": {
  4.     "ol": "6.9.0"
  5.   },
  6.   "devDependencies": {
  7.     "parcel": "^2.0.0-beta.1"
  8.   },
  9.   "scripts": {
  10.     "start": "parcel index.html",
  11.     "build": "parcel build --public-url . index.html"
  12.   }
  13. }
复制代码
参考资源:
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

举报 回复 使用道具