如何用OpenLayers在Vue中实现动态轨迹的实时更新?

2026-04-02 21:071阅读0评论SEO问题
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计2040个文字,预计阅读时间需要9分钟。

如何用OpenLayers在Vue中实现动态轨迹的实时更新?

目录+实现效果+创建地图容器+引入地图相关对象+创建地图对象+创建一条线路+绘制线路+添加起点、终点+添加小车+准备开车+完整代码+实现效果+今日介绍一个有趣的GIS小功能:动态驾驶

目录
  • 实现效果
  • 创建一个地图容器
    • 引入地图相关对象
    • 创建地图对象
  • 创建一条线路
    • 画一条线路
    • 添加起、终点
    • 添加小车
  • 准备开车
    • 完整代码

      实现效果

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

      这效果看着还很丝滑!别急,接下来教你怎么实现。代码示例基于parcel打包工具和es6语法,本文假设你已经掌握相关知识和技巧。

      gis初学者可能对openlayers(后面简称ol)不熟悉,这里暂时不介绍ol了,直接上代码,先体验下感觉。

      如何用OpenLayers在Vue中实现动态轨迹的实时更新?

      创建一个地图容器

      引入地图相关对象

      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: '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);

      以上代码很完整,我加了注释,整体思路总结如下:

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

      添加起、终点

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

      添加小车

      同样的,这里的代码在上面也写过了,就是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 caniuse.com/#feat=pointer --> <script src="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 www.maptiler.com/cloud/'; const attributions = '<a href="www.maptiler.com/copyright/" rel="external nofollow" target="_blank">&copy; MapTiler</a> ' + '<a href="www.openstreetmap.org/copyright" rel="external nofollow" target="_blank">&copy; 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: '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" } }

      参考资源:

      openlayers.org/en/latest/examples/feature-move-animation.html

      以上就是vue利用openlayers实现动态轨迹的详细内容,更多关于vue openlayers动态轨迹的资料请关注自由互联其它相关文章!

      本文共计2040个文字,预计阅读时间需要9分钟。

      如何用OpenLayers在Vue中实现动态轨迹的实时更新?

      目录+实现效果+创建地图容器+引入地图相关对象+创建地图对象+创建一条线路+绘制线路+添加起点、终点+添加小车+准备开车+完整代码+实现效果+今日介绍一个有趣的GIS小功能:动态驾驶

      目录
      • 实现效果
      • 创建一个地图容器
        • 引入地图相关对象
        • 创建地图对象
      • 创建一条线路
        • 画一条线路
        • 添加起、终点
        • 添加小车
      • 准备开车
        • 完整代码

          实现效果

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

          这效果看着还很丝滑!别急,接下来教你怎么实现。代码示例基于parcel打包工具和es6语法,本文假设你已经掌握相关知识和技巧。

          gis初学者可能对openlayers(后面简称ol)不熟悉,这里暂时不介绍ol了,直接上代码,先体验下感觉。

          如何用OpenLayers在Vue中实现动态轨迹的实时更新?

          创建一个地图容器

          引入地图相关对象

          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: '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);

          以上代码很完整,我加了注释,整体思路总结如下:

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

          添加起、终点

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

          添加小车

          同样的,这里的代码在上面也写过了,就是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 caniuse.com/#feat=pointer --> <script src="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 www.maptiler.com/cloud/'; const attributions = '<a href="www.maptiler.com/copyright/" rel="external nofollow" target="_blank">&copy; MapTiler</a> ' + '<a href="www.openstreetmap.org/copyright" rel="external nofollow" target="_blank">&copy; 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: '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" } }

          参考资源:

          openlayers.org/en/latest/examples/feature-move-animation.html

          以上就是vue利用openlayers实现动态轨迹的详细内容,更多关于vue openlayers动态轨迹的资料请关注自由互联其它相关文章!