<template>
<div>
<div id="map" ref="map" style="width: 100vw; height: 100vh"></div>
</div>
</template>
<script>
import VectorLayer from "ol/layer/Vector";
import VectorSource from "ol/source/Vector";
import Feature from "ol/Feature";
import { Draw } from "ol/interaction";
import { Style, Fill, Stroke, Circle, Icon } from "ol/style";
import { transform, fromLonLat, toLonLat } from "ol/proj";
import { Point, LineString } from "ol/geom";
import { Map, View, interaction, events } from "ol";
import TileLayer from "ol/layer/Tile";
import { defaults as defaultControls } from "ol/control";
import XYZ from "ol/source/XYZ";
export default {
data() {
return {
map: {},
featureMove: {},
carPoints: [], //车还要走的点
routeIndex: 0, //当前小车所在的路段
timer: {},
coordinates: [
[10836932.628965743, 4998172.218425438],
[10638182.82599503, 3781582.515392581],
[10897159.841987172, 3552719.105911153],
[11120000.530166456, 4986126.775821152],
[11360909.382252172, 4895785.956289009],
[11053750.595842887, 3420219.23726401],
[11294659.4479286, 3257605.7621061527],
[11565681.906525029, 4823513.300663294],
[11866817.971632171, 4757263.366339724],
[11535568.300014313, 3185333.1064804387],
[11812613.479912885, 3058855.959135439],
[12125794.987624314, 4721127.038526867],
[12402840.167522885, 4684990.710714009],
[12023408.725487886, 2926356.090488296],
[12300453.905386457, 2860106.1561647244],
[12643749.0196086, 4630786.218994724],
[12866589.707787886, 4510331.792951867],
[12547385.478774315, 2878174.3200711533],
[12932839.642111458, 2878174.3200711533],
[13113521.281175744, 3751468.908881867],
[13125566.723780029, 4739195.202433295],
[13691702.526181456, 5425785.43087758],
[13553179.936232172, 6112375.659321865],
[12920794.199507171, 5407717.266971151],
[12065567.774602886, 4974081.3332168665],
[12788294.330860028, 4895785.956289009],
],
routeLayer: {},
};
},
mounted() {
this.initMap(); //初始化地图方法
this.open(); //自动开启功能
},
methods: {
//初始化地图
initMap() {
this.map = new Map({
target: "map",
layers: [
new TileLayer({
source: new XYZ({
url: "http://map.geoq.cn/ArcGIS/rest/services/ChinaOnlineStreetPurplishBlue/MapServer/tile/{z}/{y}/{x}",
}),
}),
],
view: new View({
center: fromLonLat([108.522097, 37.272848]),
zoom: 4.7,
}),
});
},
//添加矢量图层
async open() {
//画轨迹线
await this.drawLine();
//开始动
this.moveStart();
},
//轨迹线 把每个点连起来
drawLine() {
this.routeLayer = new VectorLayer({
source: new VectorSource({
features: [],
}),
});
this.map.addLayer(this.routeLayer);
let comDots = [];
let wireFeature = {};
this.coordinates.forEach((item) => {
comDots.push(item);
wireFeature = new Feature({
geometry: new LineString(comDots),
});
wireFeature.setStyle(
new Style({
stroke: new Stroke({
// 设置边的样式
color: "rgb(21, 106, 158)",
width: 3,
}),
})
);
this.routeLayer.getSource().addFeatures([wireFeature]);
});
},
//创建小车这个要素
moveStart() {
//坐标转换
this.dotsData = this.coordinates.map((item) => {
return transform(item, "EPSG:3857", "EPSG:4326");
});
//深复制车的位置,不在原数组改变,方便重新播放
// this.carPoints = JSON.parse(JSON.stringify(this.dotsData));
this.carPoints = [...this.dotsData];
//小车最初位置在第一个坐标点
this.featureMove = new Feature({
geometry: new Point(this.carPoints[0]),
});
this.featureMove.setStyle(
new Style({
image: new Icon({
src: "https://openlayers.org/en/v4.6.5/examples/data/icon.png",
scale: 0.85,
anchor: [0.5, 0.5],
rotation: this.countRotate(),
}),
})
);
//添加车辆元素到图层
this.routeLayer.getSource().addFeature(this.featureMove);
this.timeStart();
},
//计时器开始
timeStart() {
this.timer = setInterval(() => {
if (this.routeIndex + 1 >= this.carPoints.length) {
//重头开始
this.routeIndex = 0;
//移除要素
this.routeLayer.getSource().removeFeature(this.featureMove);
clearInterval(this.timer);
//重复运动
this.open(); //自动开启功能
return;
}
//到转折点旋转角度
if (this.nextPoint() === this.carPoints[this.routeIndex + 1]) {
this.routeIndex++;
this.featureMove
.getStyle()
.getImage()
.setRotation(this.countRotate());
}
//改变坐标点
this.featureMove
.getGeometry()
.setCoordinates(fromLonLat(this.carPoints[this.routeIndex]));
}, 10);
},
//计算下一个点的位置
//这里的算法是计算了两点之间的点 两点之间的连线可能存在很多个计算出来的点
nextPoint() {
let routeIndex = this.routeIndex;
let p1 = this.map.getPixelFromCoordinate(
fromLonLat(this.carPoints[routeIndex])
); //获取在屏幕的像素位置
let p2 = this.map.getPixelFromCoordinate(
fromLonLat(this.carPoints[routeIndex + 1])
);
let dx = p2[0] - p1[0];
let dy = p2[1] - p1[1];
//打印可见 在没有走到下一个点之前,下一个点是不变的,前一个点以这个点为终点向其靠近
let distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= 1) {
return this.carPoints[routeIndex + 1];
} else {
let x = p1[0] + dx / distance;
let y = p1[1] + dy / distance;
let coor = transform(
this.map.getCoordinateFromPixel([x, y]),
"EPSG:3857",
"EPSG:4326"
);
this.carPoints[routeIndex] = coor; //这里会将前一个点重新赋值 要素利用这个坐标变化进行移动
return this.carPoints[routeIndex];
}
},
//计算两点之间的角度 算旋转角度
countRotate() {
let i = this.routeIndex,
j = i + 1;
if (j === this.carPoints.length) {
i--;
j--;
}
let p1 = this.carPoints[i];
let p2 = this.carPoints[j];
return Math.atan2(p2[0] - p1[0], p2[1] - p1[1]);
},
},
};
</script>
<style lang="scss" scoped>
</style>
<template>
<div>
<div id="map" style="width: 100vw; height: 100vh" />
<div id="geo-marker" class="css_animation">
<img :src="myplanImg" />
</div>
<div class="measuretip" id="speed"></div>
</div>
</template>
<script>
import "ol/ol.css";
import { Map, View, Feature, Overlay } from "ol";
import { Vector as VectorLayer } from "ol/layer";
import { OSM, Vector as VectorSource } from "ol/source";
import { Point, LineString } from "ol/geom.js";
import { Icon, Fill, Stroke, Style, Circle } from "ol/style";
import TileLayer from "ol/layer/Tile";
export default {
data() {
return {
path: [
[115.62, 14.82],
[112.79, 14.82],
[114.6636, 18.2977],
[111.687, 18.897],
[110.3014, 15.063],
], // 模拟路径
pathIndex: 0, // 路径点索引
marker: {}, //移动点
splitNumber: 200, // 每两个经纬度之间的分割点
setIntervalTime: 30, // 移动点间隔时间
myplanImg: "https://openlayers.org/en/v4.6.5/examples/data/icon.png", // 移动点的图片
helpTooltipElement: {}, // 平台信息div
helpTooltip: {}, // 平台信息overlay
sourceFeatures: new VectorSource(),
lineString: new LineString([]),
};
},
created() {
this.analysisPath(this.splitNumber);
},
mounted() {
this.initSeamap();
},
methods: {
initSeamap: function () {
this.pathIndex = this.path.length - 1;
this.map = new Map({
target: "map",
view: new View({
projection: "EPSG:4326",
center: [109.8, 18.4],
zoom: 7,
minZoom: 3, // 限制最大显示
maxZoom: 14,
}),
layers: [
new TileLayer({
source: new OSM(),
}),
new VectorLayer({
// 两点之间的连线
source: new VectorSource({
features: [
new Feature({
geometry: this.lineString,
}),
],
}),
style: [
new Style({
stroke: new Stroke({
width: 3,
color: "rgba(0, 0, 0, 1)",
lineDash: [0.1, 5],
}),
zIndex: 2,
}),
],
updateWhileAnimating: true,
}),
new VectorLayer({
// 两端点Feature
source: this.sourceFeatures,
}),
],
});
this.marker = new Overlay({
positioning: "center-center",
offset: [0, 0],
element: document.getElementById("geo-marker"),
stopEvent: false,
});
this.map.addOverlay(this.marker);
let style1 = [
// 开始结束点样式
new Style({
image: new Icon({
src: "https://openlayers.org/en/v4.6.5/examples/data/icon.png",
}),
}),
];
let feature_start = new Feature({
geometry: new Point(this.path[0]),
});
let feature_end = new Feature({
geometry: new Point(this.path[this.path.length - 1]),
});
feature_start.setStyle(style1);
feature_end.setStyle(style1);
this.sourceFeatures.addFeatures([feature_start, feature_end]);
this.lineString.setCoordinates(this.path);
this.helpTooltipElement = document.createElement("div");
this.helpTooltipElement.className = "measuretip";
this.helpTooltipElement.id = "speed";
this.helpTooltip = new Overlay({
element: this.helpTooltipElement,
offset: [15, 0],
positioning: "center-left",
});
this.map.addOverlay(this.helpTooltip);
this.map.once("postcompose", (event) => {
setInterval(() => {
this.animation();
}, this.setIntervalTime);
});
},
animation: function () {
if (this.pathIndex === -1) {
this.pathIndex = this.path.length - 1;
}
this.marker.setPosition(this.path[this.pathIndex]);
this.helpTooltipElement.innerHTML =
"<B>名称:</B>船载应急通信系统" +
"<br>" +
"<B>子系统:</B>平台A,平台B" +
"<br>" +
"<B>经纬度:</B>" +
(this.path[this.pathIndex][0] + "").substring(0, 6) +
"," +
(this.path[this.pathIndex][1] + "").substring(0, 5);
this.helpTooltip.setPosition(this.path[this.pathIndex]);
this.pathIndex--;
},
analysisPath: function (splitNumber) {
let tempPath = [...this.path];
let pathResults = [];
let tempPoint = [0, 0];
if (tempPath.length > 1) {
for (let i = 0; i < tempPath.length - 1; i++) {
// 每两个点之间分割出splitNumber个点
pathResults.push(tempPath[i]);
for (let j = 0; j < splitNumber; j++) {
tempPoint[0] =
((tempPath[i + 1][0] - tempPath[i][0]) * (j + 1)) / splitNumber +
tempPath[i][0];
tempPoint[1] =
((tempPath[i + 1][1] - tempPath[i][1]) * (j + 1)) / splitNumber +
tempPath[i][1];
pathResults.push([...tempPoint]);
}
}
pathResults.push(tempPath[tempPath.length - 1]);
this.path = [...pathResults];
}
},
},
};
</script>
<style>
.measuretip {
position: relative;
background-color: #0d9bf2;
opacity: 0.7;
border-radius: 3px;
padding: 10px;
font-size: 12px;
cursor: default;
}
</style>
<template>
<div>
<div id="map" style="width: 100vw; height: 100vh" />
<div style="position: fixed; top: 200px; left: 200px">
<el-button @click="startAnimation()">开始</el-button>
<el-button @click="stopAnimation()">暂停</el-button>
</div>
</div>
</template>
<script>
import "ol/ol.css";
import { Map, View, Feature, Overlay } from "ol";
import { Vector as VectorLayer } from "ol/layer";
import { XYZ, OSM, Vector as VectorSource } from "ol/source";
import { Point, LineString } from "ol/geom.js";
import { Icon, Fill, Stroke, Style, Circle } from "ol/style";
import TileLayer from "ol/layer/Tile";
import { ScaleLine, defaults as defaultControls } from "ol/control";
import { getVectorContext } from "ol/render";
export default {
data() {
return {
map: {},
route: new LineString([
[115.62, 14.82],
[112.79, 14.82],
[114.6636, 18.2977],
[111.687, 18.897],
[110.3014, 15.063],
]),
geometryMove: {},
featureMove: {},
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: "https://openlayers.org/en/v4.6.5/examples/data/icon.png",
scale: 1, //设置大小
}),
}),
featureMove: new Style({
image: new Circle({
radius: 7,
fill: new Fill({ color: "black" }),
stroke: new Stroke({
color: "white",
width: 2,
}),
}),
}),
},
vectorLayer: {},
distance: 0,
lastTime: 0,
speed: 0.1,
};
},
created() {},
mounted() {
this.initMap();
},
methods: {
initMap() {
this.geometryMove = new Point(this.route.getFirstCoordinate());
this.featureMove = new Feature({
type: "featureMove",
geometry: this.geometryMove,
});
this.vectorLayer = new VectorLayer({
source: new VectorSource({
features: [
new Feature({
type: "route",
geometry: this.route,
}),
this.featureMove,
new Feature({
type: "icon",
geometry: new Point(this.route.getFirstCoordinate()),
}),
new Feature({
type: "icon",
geometry: new Point(this.route.getLastCoordinate()),
}),
],
}),
style: (feature) => {
return this.styles[feature.get("type")];
},
});
this.map = new Map({
target: "map",
view: new View({
projection: "EPSG:4326",
center: [109.8, 18.4],
zoom: 7,
}),
layers: [
new TileLayer({
source: new OSM(),
}),
],
});
this.map.addLayer(this.vectorLayer);
},
moveFeature(e) {
let time = e.frameState.time;
this.distance =
(this.distance + (this.speed * (time - this.lastTime)) / 1000) % 1; //%2表示:起止止起;%1表示:起止起止
this.lastTime = time;
const currentCoordinate = this.route.getCoordinateAt(
this.distance > 1 ? 2 - this.distance : this.distance
);
this.geometryMove.setCoordinates(currentCoordinate);
const vectorContext = getVectorContext(e);
vectorContext.setStyle(this.styles.featureMove);
vectorContext.drawGeometry(this.geometryMove);
this.map.render();
},
startAnimation() {
this.lastTime = Date.now();
this.vectorLayer.on("postrender", this.moveFeature);
this.featureMove.setGeometry(null); //必须用null,不能用{}
},
stopAnimation() {
this.featureMove.setGeometry(this.geometryMove);
this.vectorLayer.un("postrender", this.moveFeature);
},
},
};
</script>
使用:
<template>
<div>
<div id="map" style="height: 100vh; width: 100vw"></div>
<div style="position: fixed; top: 100px; left: 200px">
<el-button @click="openAnimation()">打开动画</el-button>
<el-button @click="closeAnimation()">关闭动画</el-button>
</div>
<LjOlCarRoute
ref="ref_LjOlCarRoute"
:map="map"
:coordinates="coordinates_route"
:speed="0.1"
v-if="showAnimation"
></LjOlCarRoute>
</div>
</template>
<script>
import "ol/ol.css";
import { Map, View } from "ol";
import { OSM } from "ol/source";
import { Tile as TileLayer } from "ol/layer";
import LjOlCarRoute from "@/components/LjOlCarRoute/index.vue";
export default {
components: { LjOlCarRoute },
data() {
return {
map: {},
showAnimation: false,
coordinates_route: [
[104.2979180563, 30.528298024],
[104.2987389704, 30.527798338],
[104.2974397847, 30.5265062907],
[104.296943667091881, 30.52468992916009],
],
};
},
created() {},
mounted() {
this.initMap();
},
computed: {},
methods: {
initMap() {
this.map = new Map({
target: "map",
layers: [
new TileLayer({
source: new OSM(),
}),
],
view: new View({
projection: "EPSG:4326",
center: [104.2974397847, 30.5265062907],
zoom: 18,
}),
});
},
openAnimation() {
this.showAnimation = true;
},
closeAnimation() {
this.$refs.ref_LjOlCarRoute.removeLayer();
this.showAnimation = false;
},
},
};
</script>
组件:LjOlCarRoute/index.vue:
<template>
<div>
<div style="position: fixed; top: 200px; left: 200px">
<el-button @click="startAnimation()">开始</el-button>
<el-button @click="stopAnimation()">暂停</el-button>
</div>
</div>
</template>
<script>
import "ol/ol.css";
import { Map, View, Feature, Overlay } from "ol";
import { Vector as VectorLayer } from "ol/layer";
import { XYZ, OSM, Vector as VectorSource } from "ol/source";
import { Point, LineString } from "ol/geom.js";
import { Icon, Fill, Stroke, Style, Circle } from "ol/style";
import TileLayer from "ol/layer/Tile";
import { getVectorContext } from "ol/render";
export default {
name: "LjOlCarRoute",
props: {
map: {
type: Object,
default: () => {},
required: true,
},
coordinates: {
type: Array,
default: () => [
[115.62, 14.82],
[112.79, 14.82],
[114.6636, 18.2977],
[111.687, 18.897],
[110.3014, 15.063],
],
},
startPointImage: {
type: String,
default:
"https://smart-garden-manage.oss-cn-chengdu.aliyuncs.com/position.png",
},
endPointImage: {
type: String,
default:
"https://smart-garden-manage.oss-cn-chengdu.aliyuncs.com/position.png",
},
movePointImage: {
type: String,
default:
"https://smart-garden-manage.oss-cn-chengdu.aliyuncs.com/xiaoche.png",
},
speed: {
type: Number,
default: 0.1,
},
},
data() {
return {
route: new LineString(this.coordinates),
geometryMove: {},
featureMove: {},
styles: {
route: new Style({
stroke: new Stroke({
width: 6,
color: [237, 212, 0, 0.8],
}),
}),
startIcon: new Style({
image: new Icon({
anchor: [0.5, 1],
src: this.startPointImage,
scale: 1, //设置大小
}),
}),
endIcon: new Style({
image: new Icon({
anchor: [0.5, 1],
src: this.endPointImage,
scale: 1, //设置大小
}),
}),
featureMove: new Style({
image: new Icon({
// anchor: [0.5, 1],
src: this.movePointImage,
scale: 0.2, //设置大小
}),
}),
},
vectorLayer: {},
distance: 0,
lastTime: 0,
};
},
created() {},
mounted() {
this.initMap();
},
methods: {
initMap() {
this.geometryMove = new Point(this.route.getFirstCoordinate());
this.featureMove = new Feature({
type: "featureMove",
geometry: this.geometryMove,
});
this.vectorLayer = new VectorLayer({
source: new VectorSource({
features: [
new Feature({
type: "route",
geometry: this.route,
}),
this.featureMove,
new Feature({
type: "startIcon",
geometry: new Point(this.route.getFirstCoordinate()),
}),
new Feature({
type: "endIcon",
geometry: new Point(this.route.getLastCoordinate()),
}),
],
}),
style: (feature) => {
return this.styles[feature.get("type")];
},
});
this.map.addLayer(this.vectorLayer);
},
moveFeature(e) {
let time = e.frameState.time;
this.distance =
(this.distance + (this.speed * (time - this.lastTime)) / 1000) % 1; //%2表示:起止止起;%1表示:起止起止
this.lastTime = time;
const currentCoordinate = this.route.getCoordinateAt(
this.distance > 1 ? 2 - this.distance : this.distance
);
this.geometryMove.setCoordinates(currentCoordinate);
const vectorContext = getVectorContext(e);
vectorContext.setStyle(this.styles.featureMove);
vectorContext.drawGeometry(this.geometryMove);
this.map.render();
},
startAnimation() {
this.lastTime = Date.now();
this.vectorLayer.on("postrender", this.moveFeature);
this.featureMove.setGeometry(null); //必须用null,不能用{}
},
stopAnimation() {
this.featureMove.setGeometry(this.geometryMove);
this.vectorLayer.un("postrender", this.moveFeature);
},
removeLayer() {
this.map.removeLayer(this.vectorLayer);
this.vectorLayer.getSource().clear();
this.featureMove = {};
this.geometryMove = {};
},
},
};
</script>