This is the 7th day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021.

preface

Due to the recent needs of the project, IT is necessary to use OpenLayers in the VUE project to develop GIS map. There are not many articles on OpenLayers on the Internet, so I would like to take this opportunity to share some of my experience in the actual use of the project.

This series will share some of the feature points implemented over the course of the project.

This paper is based on the previous track drawing vue+OpenLayers project practice (6) : historical track drawing, adding an animation tracking effect for it.

The specific implementation

Functions to be implemented in this article:

  • Enrich the options and buttons in the card at the lower right corner of the track, add customized speed, start, end, pause and other functional buttons;
  • Achieve dynamic tracking effect

Ok, let’s implement it:

1. Introduction of dolly Icon

First we introduce a picture of a car:

Add the car layer in the code, starting at the first coordinate point.

// Draw the trajectory
drawRoute() {
  if (this.routeGeometry) {
    this.routeSource.clear();
  }
  this.routeGeometry = new LineString(this.routes);
  let route = new Feature(this.routeGeometry);
  / / draw point
  let opints = this.drawPoint();
  // Add car
  let car = this.drawCar();
  this.routeSource.addFeatures([route, ...opints, car]);
  // Scale according to trajectory boundaries
  this.mapFit(); },.../ / the car
drawCar() {
  const carMarker = new Feature({
    geometry: new Point(this.routeGeometry.getFirstCoordinate()),
  });
  let _style = new Style({
    image: new Icon({
      anchor: [0.5.0.5].src: require("@/assets/img/car.png"),
      imgSize: [20.36],})}); carMarker.setStyle(_style);this.carGeometry = carMarker;
  return carMarker;
},
Copy the code

OK, added a green car.

2. Expand the control in the lower right corner

<div class="speed"Speed: <divclass="speed-input">
    <el-slider
      v-model="speed"
      :min="10"
      :max="1000"
      :step="10"
    ></el-slider>
  </div>
  <el-button type="primary" @click="changeAnimation">{{
    animationText
  }}</el-button>
</div>
Copy the code

Add a speed slider control.

3. Animation implementation

The implementation principle of OpenLayers trajectory animation:

  • The existing path is segmented by speed, and a large number of coordinate points are segmented.
  • Monitor Postrender to reset the coordinate points of the car to achieve animation.

First, encapsulate the common methods:

  • We need the car to be able to turn, so we need to figure out how much radian the car is spinning
  • Calculate the total length between the two points
  • Insert the split point between two points according to the total length and speed, and return a new array
/* * @Author: Shao Tao * @Date: 2021-11-19 13:38:44 * @LastEditTime: 2021-11-19 15:53:14 * @LastEditors: Shao Tao * @Description: * @FilePath: \vue-openlayers\src\utils\openlayers\route.js */
import { getDistance } from "ol/sphere";
import { transform } from "ol/proj";

/** * get radians based on coordinates */
export function getRotation(lng1, lat1, lng2, lat2) {
  let rotation = Math.atan2(lng2 - lng1, lat2 - lat1);
  return rotation;
}
/** * computes the distance between coordinates 2 */
export function formatLength(map, pointArray) {
  let length = 0;
  if (map.getView().getProjection().code_ == "EPSG:4326") {
    for (let i = 0, ii = pointArray.length - 1; i < ii; ++i) {
      let c1 = pointArray[i];
      let c2 = pointArray[i + 1]; length += getDistance(c1, c2); }}else if (map.getView().getProjection().code_ == "EPSG:3857") {
    for (let i = 0, ii = pointArray.length - 1; i < ii; ++i) {
      let c1 = pointArray[i];
      let c2 = pointArray[i + 1];
      c1 = transform(c1, "EPSG:3857"."EPSG:4326");
      c2 = transform(c2, "EPSG:3857"."EPSG:4326"); length += getDistance(c1, c2); }}return length;
}

/** * computes the intermediate point * between two points@param {*} map
 * @param {Array} PointDoubleArray two-dimensional array coordinates *@param {num} Speed Distance between points */
export function getCenterPoint(map, pointDoubleArray, speed) {
  speed = speed == undefined ? 10 : speed;
  let twolength = formatLength(map, pointDoubleArray);
  let rate = twolength / speed; // The ratio defaults to 10m/ point
  let step = Math.ceil(rate); // Step count (rounded up)
  let arr = new Array(a);// Define the storage intermediate number group
  let c1 = pointDoubleArray[0]; / / head
  let c2 = pointDoubleArray[1]; / / end point
  let x1 = c1[0],
    y1 = c1[1];
  let x2 = c2[0],
    y2 = c2[1];
  for (let i = 1; i < step; i++) {
    let coor = new Array(2);
    coor[0] = ((x2 - x1) * i) / rate + x1;
    coor[1] = ((y2 - y1) * i) / rate + y1;
    arr.push(coor); // arr is the coordinate of the intermediate point
  }
  arr.push(c2);
  return arr;
}
Copy the code

Then modify the route. vue file and split the path getRoutesAll after obtaining the path

// Change the getList method to call the getRoutesAll method
getList(){...this.getRoutesAll();
},

// Add split pathpoint
getRoutesAll() {
  this.lastRouteIndex = 0;
  let _routesAll = [
    {
      coordinate: this.routes[0],},];for (let i = 0, len = this.routes.length; i < len - 1; i++) {
    const item = this.routes[i];
    const itemNext = this.routes[i + 1];
    constrotation = getRotation(... item, ... itemNext);let points = getCenterPoint(this.openMap, [item, itemNext], this.speed);
    points = points.map((item) = > {
      return {
        rotation,
        coordinate: item,
      };
    });
    _routesAll = [..._routesAll, ...points];
  }
  this.routesAll = _routesAll;
},
Copy the code

This generates an array of trajectories containing radians and coordinates. Then move the point position.

changeAnimation() {
  this.animating ? this.stopAnimation() : this.startAnimation();
},
// Start animation
startAnimation() {
  this.animating = true;
  this.lastTime = Date.now();
  this.animationText = "Stop";
  this.routeLayer.on("postrender".this.moveFeature);
  this.carFeature.setGeometry(null);
},
// Stop the animation
stopAnimation() {
  this.animating = false;
  this.animationText = "Start";
  this.carFeature.setGeometry(this.carGeometry);
  this.routeLayer.un("postrender".this.moveFeature);
},
// Move the animation
moveFeature(event) {
  // Move the animation method
},
Copy the code

Implement moveFeature method:

moveFeature(event) {
  const len = this.routesAll.length;
  if (this.lastRouteIndex < len - 1) {
    this.lastRouteIndex++;
  } else {
    this.lastRouteIndex = 0;
  }
  const current = this.routesAll[this.lastRouteIndex];
  this.carGeometry.setCoordinates(current.coordinate);
  const vectorContext = getVectorContext(event);
  let _style = new Style({
    image: new Icon({
      anchor: [0.5.0.5].rotation: current.rotation,
      src: require("@/assets/img/car.png"),
      imgSize: [20.36],})}); vectorContext.setStyle(_style); vectorContext.drawGeometry(this.carGeometry);
  this.openMap.render();
},
Copy the code

Ok, now we have moved the entire trajectory.

The final result

Past Catalogue:

  1. Vue +OpenLayers Project Practice (I) : Basic drawing and clicking pop-ups

  2. Vue +OpenLayers Project Practice (2) : Switching between multiple maps

  3. Vue +OpenLayers Project Practice (3) : Cluster

  4. Vue +OpenLayers Project Practice 4: Set up a fence

  5. Vue +OpenLayers Project Practice 5: Historical fencing

  6. Vue +OpenLayers Project Practice (6) : Drawing the historical track

The last

Gitee address: gitee.com/shtao_056/v…

Thank you for reading

It would be a great honor if I could help you.

In the future, more functions will be updated, as well as some problems encountered in the project, interested partners can click favorites/follow.