openlayer轨迹回放

封装一个openlayer的轨迹回放方法,使其满足多条路线的轨迹回放效果

思路:

  • 描绘出一两条轨迹图,得到该图形的feature,使用new LineString(feature.getGeometry().getCoordinates(), "XY")得到线条的router点位数组。
  • 监听图层this.vectorLayer.on("postrender",()=>{})的方法,在里面实现移动的动画效果。
  • 计算时间和速度进行取值,使用track.route.getCoordinateAt(track.distance)获取当前的点位,并设置当前的点位为new Point()圆点。
  • 将移动过的点的集合放进traveledCoordinates数组中,使用new LineString(traveledCoordinates)生成一段线条,就是移动过路线。
  • vectorContext.setStyle(style)为移动点和移动线画上样式,在使用vectorContext.drawGeometry(track.position)绘制图层。
  • this.map.render()更新地图渲染
js 复制代码
import { Circle as CircleStyle, Fill, Stroke, Style, Icon } from "ol/style.js";
import { getVectorContext } from "ol/render.js";
import { LineString, Point } from "ol/geom.js";
import Feature from "ol/Feature.js";
import { stylesFunction } from "./index.js";
import { toLonLat, fromLonLat } from "ol/proj.js";

class TrackPlayer {
  constructor(map, vectorLayer) {
    this.animating = false;
    this.map = map;
    this.vectorLayer = vectorLayer;
    this.listener = null;
    this.tracks = []; // 存储多条轨迹信息
    this.lastTime = Date.now();
  }
  // 添加轨迹
  addTrack(feature, options = {}) {
    const track = {
      id: Date.now() + Math.random(), // 唯一ID
      feature: feature,
      route: new LineString(feature.getGeometry().getCoordinates(), "XY"),
      position: new Point(feature.getGeometry().getCoordinates()[0]),
      distance: 0,
      speed: options.speed || 5,
      color: options.color || "rgb(0, 132, 255)",
      iconScale: options.iconScale || 1,
      lineWidth: options.lineWidth || 6,
      enabled: options.enabled !== false, // 默认启用
    };

    this.tracks.push(track);
    return track.id;
  }

  // 移除轨迹
  removeTrack(trackId) {
    const index = this.tracks.findIndex((track) => track.id === trackId);
    if (index !== -1) {
      this.vectorLayer
        .getSource()
        .removeFeature(this.tracks[index].tempFeature);
      this.tracks.splice(index, 1);

      console.log("Removed track:", trackId);

      // 如果没有轨迹了,停止动画
      if (this.tracks.length === 0 && this.animating) {
        this.stop();
      }
    }
  }

  // 启用/禁用轨迹
  setTrackEnabled(trackId, enabled) {
    const track = this.tracks.find((track) => track.id === trackId);
    if (track) {
      track.enabled = enabled;
    }
  }

  // 设置轨迹速度
  setTrackSpeed(trackId, speed) {
    const track = this.tracks.find((track) => track.id === trackId);
    if (track) {
      track.speed = speed;
    }
  }

  // 设置轨迹颜色
  setTrackColor(trackId, color) {
    const track = this.tracks.find((track) => track.id === trackId);
    if (track) {
      track.color = color;
    }
  }

  // 播放所有轨迹
  play() {
    if (this.animating) return;

    this.animating = true;
    this.lastTime = Date.now();

    this.listener = this.vectorLayer.on("postrender", (event) => {
      const time = event.frameState.time;
      const elapsedTime = time - this.lastTime;
      this.lastTime = time;

      const vectorContext = getVectorContext(event);

      // 更新和绘制每条轨迹
      this.tracks.forEach((track) => {
        if (!track.enabled) return;

        // 更新距离
        track.distance =
          (track.distance + (track.speed * elapsedTime) / 1e6) % 1;

        // 更新当前位置
        const currentCoordinate = track.route.getCoordinateAt(track.distance);
        track.position.setCoordinates(currentCoordinate);

        // 绘制已走过的路线
        if (track.distance > 0) {
          const traveledCoordinates = [];

          // 获取已走过的路径坐标,确保每个点都被经过
          // 增加取样密度从100到500,使轨迹更平滑
          for (let i = 0; i <= track.distance * 500; i++) {
            const coord = track.route.getCoordinateAt(i / 500);
            if (coord) {
              traveledCoordinates.push(coord);
            }
          }

          if (traveledCoordinates.length > 1) {
            const traveledLine = new LineString(traveledCoordinates);

            vectorContext.setStyle(
              new Style({
                stroke: new Stroke({
                  color: track.color,
                  width: track.lineWidth,
                }),
              })
            );
            vectorContext.drawGeometry(traveledLine);
          }
        }

        // 绘制移动的图标
        vectorContext.setStyle(new Style({
            image: new CircleStyle({
                radius: track.pointRadius,
                fill: new Fill({ color: track.pointColor }),
                stroke: new Stroke({
                    color: 'white',
                    width: 2,
                }),
            }),

        }));

        vectorContext.drawGeometry(track.position);
      });

      // 继续动画
      this.map.render();
    });
  }

  // 停止播放
  stop() {
    this.animating = false;
    if (this.listener && this.vectorLayer) {
      try {
        this.vectorLayer.un(this.listener);
      } catch (error) {
        console.warn("Error removing listener:", error);
      }
      this.listener = null;
    }
  }

  // 清除所有轨迹
  clear() {
    if (this.animating) {
      this.stop();
    }
    this.tracks = [];
  }

  // 获取轨迹信息
  getTracks() {
    return this.tracks.map((track) => ({
      id: track.id,
      enabled: track.enabled,
      speed: track.speed,
      color: track.color,
      distance: track.distance,
    }));
  }
}

export default TrackPlayer;

绘制的路线,可能会保存为geojson格式,因此需要读取路径的geojson。new GeoJSON().readFeatures(geoJson, { featureProjection: "EPSG:3857", });可以读取geojson为feacture对象,this.map.addLayer(vectorLayer)并添加到图层上。

js 复制代码
 const feature = new GeoJSON().readFeatures(geoJson, {
      featureProjection: "EPSG:3857",
    });

    const vectorSource = new VectorSource({
      features: feature,
    });

    const vectorLayer = new VectorLayer({
      id: id,
      source: vectorSource,
      style: [
           new Style({
              stroke: new Stroke({
                color: "rgba(0, 125, 0)",
                width: 10,
              }),
            }),
            new Style({
              text: new Text({
                text: feature.get("name"), // 假设GeoJSON中有'name'属性
                font: "14px Calibri,sans-serif",
                fill: new Fill({
                  color: "#000",
                }),
                stroke: new Stroke({
                  color: "#fff",
                  width: 3,
                }),
              }),
              stroke: new Stroke({
                color: "rgba(0, 200, 0)",
                width: 8,
              }),
            })
      ]
      },
      renderMode: "image", // 提高渲染性能
    });
    this.map.addLayer(vectorLayer);

最后可以使用我们封装的轨迹回放方法。

js 复制代码
 this.trackPlayer = new TrackPlayer(this.map, this.vector);
 this.trackPlayer.addTrack(feature[0]);
  // 播放所有轨迹
 this.trackPlayer.play();
相关推荐
vivo互联网技术5 小时前
微信小程序端智能项目工程化实践
前端·人工智能·微信小程序·端智能
Trust yourself2435 小时前
如何获取easy-ui的表格的分页大小
前端·javascript·ui·oracle
zz-zjx5 小时前
shell编程从0基础--进阶 1
linux·运维·前端·chrome·bash
濮水大叔5 小时前
能够动态推断与生成DTO是Node生态的一个重要里程碑
前端·typescript·node.js
飞阿飞飞5 小时前
手把手教你用React做一个Excel导入功能,看完就能用!
前端·react.js
艾小码5 小时前
JSON数据处理太头疼?这4个技巧让你秒变高手!
前端·javascript·json
庚云5 小时前
前端项目中 .env 文件的原理和实现
前端·面试
10share5 小时前
【vite-vue】demo项目创建
前端