openlayers 地图组件封装

openlayers 地图组件封装

html 复制代码
<template>
	<div class="mapBox">
		<!-- 地图 -->
		<div ref="map" id="map" class="map"></div>

		<!-- 点位弹窗 -->
		<div id="popup" class="ol-popup">
			<a href="#" id="popup-closer" class="ol-popup-closer"></a>
			<div id="popup-content">{{ popup.content }}</div>
		</div>

		<!-- 地图操作 -->
		<div v-if="false" class="optionsBox">
			<!-- GIS服务 -->
			<el-dropdown @command="handleGisCommand">
				<el-button type="primary">
					GIS服务
					<i class="el-icon-arrow-down el-icon--right"></i>
				</el-button>
				<el-dropdown-menu slot="dropdown">
					<el-dropdown-item command="1">测距离</el-dropdown-item>
					<el-dropdown-item command="2">测面积</el-dropdown-item>
					<el-dropdown-item command="3">清空</el-dropdown-item>
					<el-dropdown-item command="4">打印地图</el-dropdown-item>
				</el-dropdown-menu>
			</el-dropdown>

			<!-- 矢量底图 / 卫星底图 -->
			<el-radio-group v-model="checkUnderLayer" @change="handleUnderLayerChange">
				<el-radio-button label="sldt">矢量底图</el-radio-button>
				<el-radio-button label="wxdt">卫星底图</el-radio-button>
			</el-radio-group>

			<!-- 注记层 / 行政区划 -->
			<el-checkbox-group v-model="checkLayers" @change="handleLayerChange">
				<el-checkbox-button label="zjc">注记层</el-checkbox-button>
				<el-checkbox-button label="xzqh">行政区划</el-checkbox-button>
			</el-checkbox-group>

			<!-- 全屏 -->
			<el-button type="primary" @click="handleFullScreen">{{ isFullScreen ? "退出全屏" : "全屏" }}</el-button>

			<!-- 回到原点 -->
			<el-button type="primary" @click="handleBackCenter">回到原点</el-button>

			<!-- 轨迹运动 -->
			<el-button type="primary" @click="handleTrackStart">{{ mapTrack.isPlay ? "暂停" : "开始" }}</el-button>
			<el-button type="primary" @click="handleTrackStop">停止</el-button>
		</div>
	</div>
</template>

<script>
import "ol/ol.css";
import request from "@/utils/request.js";
import domtoimage from "dom-to-image";
import Text from "ol/style/Text";

import { getWidth, getTopLeft } from "ol/extent";
import View from "ol/View";
import Map from "ol/Map";
import WMTSTileGrid from "ol/tilegrid/WMTS";
import Feature from "ol/Feature";
import { WMTS, Vector as VectorSource, XYZ } from "ol/source";
import { Tile as TileLayer, Vector as VectorLayer, VectorImage } from "ol/layer";
import { getArea, getLength } from "ol/sphere";
import { unByKey } from "ol/Observable";
import { LineString, Polygon, Point, MultiLineString } from "ol/geom";
import { MousePosition, ScaleLine, ZoomSlider } from "ol/control";
import { createStringXY } from "ol/coordinate";
import * as olProj from "ol/proj";
import { Draw, Select, Modify } from "ol/interaction";
import Overlay from "ol/Overlay";

import { Circle as CircleStyle, Fill, Stroke, Style, Icon } from "ol/style";
import { scale } from "ol/size";

import GeoJSON from "ol/format/GeoJSON";
import SourceVector from "ol/source/Vector";
import { getDistance } from "ol/sphere";

import { getVectorContext } from "ol/render";
import { EventBus } from "@/utils/eventBus.js";

import { transform, fromLonLat, toLonLat } from "ol/proj";

import LayerTile from "ol/layer/Tile";
import ImageLayer from "ol/layer/Image";
import { Raster as RasterSource } from "ol/source";
import { defaults as defaultControls } from "ol/control";
import { saveAs } from "file-saver";
export default {
	data() {
		return {
			map: null,
			mapCenter: [118.091838, 36.958653], // 地图中心点
			// 弹窗
			popup: {
				popupOverlay: null, // 点位弹窗
				content: "", // 弹窗显示内容
			},
			// 行政区划/注记层
			checkLayers: ["zjc", "xzqh"],
			mapLayers: {},
			// 底图
			underlayer: {
				sldt: { layer: null, show: true },
				wxdt: { layer: null, show: false },
			},
			checkUnderLayer: "sldt",
			// 测量距离/面积
			mapDraw: {
				helpTooltipElement: null,
				feature: null,
				helpTooltip: null,
				draw: null,
				measureTooltipElement: null,
				measureTooltip: null,
				listener: null,
				mapMouseMove: null,
				drawElements: [],
				drawLayers: [],
			},
			isFullScreen: false,
			// 轨迹
			mapTrack: {
				isPlay: false, // 开始或暂停运动
				isStop: false, // 停止运动, 复位
				route: null,
				featureMove: {},
				geometryMove: {},
				carPoints: [], //车还要走的点
				routeIndex: 0, //当前小车所在的路段
				timer: null,
				routeLayer: {},
				coordinates: [
					[117.98804853292008, 36.924719974587475],
					... ...
				],
			},
		};
	},

	mounted() {
		// this.initMap();
		this.initDeepColorMap();
		// this.initBlueColorMap();

		this.initEvent();
		this.initPointPopup();

		// 添加轨迹
		// this.addTrack();

		// 监听全屏状态
		window.addEventListener("resize", () => {
			// 是否全屏
			this.isFullScreen =
				document.fullscreenElement ||
				document.webkitFullscreenElement ||
				document.msFullscreenElement ||
				document.mozFullScreenElement ||
				null;
		});
	},

	methods: {
		handleTrackStart() {
			this.mapTrack.isPlay = !this.mapTrack.isPlay;
			if (this.mapTrack.isPlay) {
				//开始动
				this.trackMoveStart();
			} else {
				this.trackMovePause();
			}
		},
		handleTrackStop() {
			this.mapTrack.isPlay = false;
			this.mapTrack.isStop = true;

			this.resetTrack();
			this.addTrack();
		},
		/**
		 * 切换底图
		 */
		handleUnderLayerChange(type) {
			for (let i in this.underlayer) {
				this.underlayer[i].layer.setVisible(false);
				this.underlayer[i].show = false;
			}
			this.underlayer[type].show = true;
			this.underlayer[type].layer.setVisible(true);
		},
		/**
		 * 注记层/行政区划 显示与隐藏
		 */
		handleLayerChange() {
			for (let i in this.mapLayers) {
				this.mapLayers[i].layer.setVisible(false);
			}
			for (let i in this.checkLayers) {
				this.mapLayers[this.checkLayers[i]].layer.setVisible(true);
			}
		},
		/**
		 * 切换全屏
		 */
		handleFullScreen() {
			if (this.isFullScreen) {
				this.exitfullscreen();
			} else {
				this.enterfullscreen();
			}
		},
		/**
		 * 回到原点
		 */
		handleBackCenter() {
			this.map.getView().setCenter(this.mapCenter);
		},

		/**
		 * 初始化地图
		 */
		initMap() {
			// 天地图图层
			const tdLayer = new TileLayer({
				source: new XYZ({
					crossOrigin: "anonymous",
					projection: "EPSG:4326",
					url: "https://t0.tianditu.gov.cn/DataServer?x={x}&y={y}&l={z}&T=vec_c&tk=地图的key",
				}),
				className: "dt",
			});

			// 天地图文字图层
			const tdTextLayer = new TileLayer({
				source: new XYZ({
					crossOrigin: "anonymous",
					projection: "EPSG:4326",
					url: "https://t0.tianditu.gov.cn/DataServer?x={x}&y={y}&l={z}&T=cva_c&tk=地图的key",
				}),
				className: "txt",
			});

			this.map = new Map({
				target: "map",
				view: new View({
					center: this.mapCenter,
					projection: olProj.get("EPSG:4326"),
					zoom: 13,
					maxZoom: 17,
					minZoom: 1,
				}),
			});

			this.map.addLayer(tdLayer);
			this.map.addLayer(tdTextLayer);
			this.mapLayers.zjc = { layer: tdTextLayer, show: true };

			//卫星影像图层
			const projection = olProj.get("EPSG:4326");
			const projectionExtent = projection.getExtent();
			const size = getWidth(projectionExtent) / 256;
			const resolutions = [];
			const matrixIds = [];
			for (let z = 0; z < 20; ++z) {
				resolutions[z] = size / Math.pow(2, z + 1);
				matrixIds[z] = z + 1;
			}
			const tdwxLayer = new TileLayer({
				opacity: 1,
				visible: false,
				source: new WMTS({
					crossOrigin: "anonymous",
					url: "http://t3.tianditu.com/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=地图的key",
					layer: "wxt",
					matrixSet: "c",
					format: "tiles",
					style: "default",
					projection: this.projection,
					tileGrid: new WMTSTileGrid({
						origin: getTopLeft(projectionExtent),
						resolutions: resolutions,
						matrixIds: matrixIds,
					}),
					wrapX: true,
				}),
			});
			this.map.addLayer(tdwxLayer);

			this.underlayer.sldt = { layer: tdLayer, show: true };
			this.underlayer.wxdt = { layer: tdwxLayer, show: false };
		},
		/**
		 * 初始化深颜色地图
		 */
		initDeepColorMap() {
			const view = new View({
				center: this.mapCenter,
				zoom: 12, //图层缩放大小
				projection: "EPSG:4326",
			});

			this.map = new Map({
				target: "map",
				view: view,
				controls: defaultControls({
					zoom: true,
					attribution: false,
					rotate: false,
				}),
			});

			// 添加矢量底图
			let url = "https://t0.tianditu.gov.cn/DataServer?x={x}&y={y}&l={z}";
			url = `${url}&T=vec_c&tk=地图的key`;
			const source = new XYZ({
				crossOrigin: "anonymous",
				url: url,
				projection: "EPSG:4326",
			});
			const tdtLayer = new TileLayer({
				className: "blueLayer",
				source: source,
			});
			this.map.addLayer(tdtLayer);

			// 添加矢量注记
			url = "https://t0.tianditu.gov.cn/DataServer?x={x}&y={y}&l={z}";
			url = `${url}&T=cva_c&tk=地图的key`;
			const sourceCVA = new XYZ({
				crossOrigin: "anonymous",
				url: url,
				projection: "EPSG:4326",
			});
			const tdtcvaLayer = new TileLayer({
				className: "blueLayer",
				source: sourceCVA,
			});
			this.map.addLayer(tdtcvaLayer);
			this.mapLayers.zjc = { layer: tdtcvaLayer, show: true };

			//卫星影像图层
			const projection = olProj.get("EPSG:4326");
			const projectionExtent = projection.getExtent();
			const size = getWidth(projectionExtent) / 256;
			const resolutions = [];
			const matrixIds = [];
			for (let z = 0; z < 20; ++z) {
				resolutions[z] = size / Math.pow(2, z + 1);
				matrixIds[z] = z + 1;
			}
			const tdwxLayer = new TileLayer({
				opacity: 1,
				visible: false,
				source: new WMTS({
					crossOrigin: "anonymous",
					url:
						"http://t0.tianditu.gov.cn/img_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=地图的key",
					layer: "wxt",
					matrixSet: "c",
					format: "tiles",
					style: "default",
					projection: this.projection,
					tileGrid: new WMTSTileGrid({
						origin: getTopLeft(projectionExtent),
						resolutions: resolutions,
						matrixIds: matrixIds,
					}),
					wrapX: true,
				}),
			});
			this.map.addLayer(tdwxLayer);

			this.underlayer.sldt = { layer: tdtLayer, show: true };
			this.underlayer.wxdt = { layer: tdwxLayer, show: false };
		},
		initBlueColorMap() {
			let target = "map";

			let tileLayer = [
				new TileLayer({
					source: new XYZ({
						url: "http://map.geoq.cn/arcgis/rest/services/ChinaOnlineStreetPurplishBlue/MapServer/tile/{z}/{y}/{x}",
					}),
				}),
			];
			let view = new View({
				center: this.mapCenter,
				zoom: 12,
			});
			this.map = new Map({
				target: target,
				layers: tileLayer,
				view: view,
			});

			const TiandiMap_cia = new TileLayer({
				name: "天地图影像注记图层",
				source: new XYZ({
					url: "http://t0.tianditu.com/DataServer?T=cia_w&x={x}&y={y}&l={z}&tk=地图的key", //parent.TiandituKey()为天地图密钥
					wrapX: false,
				}),
			});
			// 添加到地图上
			this.map.addLayer(TiandiMap_cia);
		},

		/**
		 * 地图事件
		 */
		initEvent() {
			// 地图加载完成
			this.map.once("rendercomplete", () => {
				// 行政区划
				this.addProvinceBorder();

				// 添加点位
				//this.addPoint([[118.103288, 36.96607]], require("@/assets/image/homecar2.png"), -(Math.PI / 3));

				this.$emit("complete", this.map);
			});

			// 地图点击
			this.map.on("click", e => {
				// 点位弹窗
				var pixel = this.map.getEventPixel(e.originalEvent);
				console.log(e.coordinate);
				let feature = this.map.forEachFeatureAtPixel(pixel, feature => {
					return feature;
				});
				if (feature && feature.name && feature.name != "cityScope") {
					var coodinate = e.coordinate;
					this.popup.content = "你点击的坐标为:" + coodinate;
					this.popup.popupOverlay.setPosition(coodinate);
					this.map.addOverlay(this.popup.popupOverlay);

					this.$emit("pointClick", true, feature.name, e.coordinate, e.pixel);
				} else {
					this.$emit("pointClick", false, feature.name, e.coordinate, e.pixel);
					this.popup.popupOverlay.setPosition(undefined);
				}
			});
		},
		/**
		 * 初始化点位弹窗
		 */
		initPointPopup() {
			var container = document.getElementById("popup");
			var popupCloser = document.getElementById("popup-closer");
			this.popup.popupOverlay = new Overlay({
				element: container,
				autoPan: true,
			});

			popupCloser.addEventListener("click", function() {
				this.popup.popupOverlay.setPosition(undefined);
			});
		},

		/***
		 * 加行政区域边界
		 * 获取geojson数据地址: https://datav.aliyun.com/portal/school/atlas/area_selector
		 */
		addProvinceBorder(geojson) {
			//行政区划边界图层
			const provinceLayer = new VectorLayer({
				renderMode: "vector",
				source: new VectorSource(),
				zIndex: 0,
			});
			this.map.addLayer(provinceLayer);
			this.mapLayers.xzqh = { layer: provinceLayer, show: true };

			// 加载geojson
			let qxn = geojson ? geojson : require("../mixins/370321.json");
			qxn.features.forEach(featureJSON => {
				let feature = new GeoJSON().readFeature(featureJSON);
				feature.set("name", feature.values_.NAME);
				feature.setStyle(
					new Style({
						stroke: new Stroke({
							//边框
							color: "rgba(3,15,83, 1)",
							width: 1,
						}),
						fill: new Fill({
							//填充
							color: "rgba(3,15,83, 0.5)", // "rgba(255, 255, 255, 0.5)"
						}),
					})
				);
				provinceLayer.getSource().addFeature(feature);
			});
		},

		/**
		 * 添加点位
		 * @param {*} list [[经度, 纬度], [经度, 纬度]]
		 * @param {*} icon 点位图标图片路径
		 * @param {*} angle 旋转角度
		 * @param {*} popupType 弹窗类型  可以在template中自定义多种弹窗, 然后指定弹出哪个
		 */
		addPoint(list, icon, angle, popupType = 0) {
			// 设置图层
			const flagLayer = new VectorLayer({
				source: new VectorSource(),
			});
			// 添加图层
			this.map.addLayer(flagLayer);

			let featuresArr = [];

			// 循环添加feature
			for (let i = 0; i < list.length; i++) {
				// 创建feature,一个feature就是一个点坐标信息
				let feature = new Feature({
					geometry: new Point([list[i][0], list[i][1]]),
				});
				feature.name = "point";
				feature.popupType = popupType;

				// 设置要素的图标
				feature.setStyle(
					new Style({
						// 设置图片效果
						image: new Icon({
							src: icon,
							anchor: [0.5, 0.9],
							scale: 1,
							rotation: angle || 0,
						}),
						zIndex: 6000,
					})
				);

				// feature.setStyle(
				//   new Style({
				//     // 设置图片效果
				//     image: new CircleStyle({
				//       radius: 10,
				//       stroke: new Stroke({
				//         color: '#fff'
				//       }),
				//       fill: new Fill({
				//         color: '#3399cc'
				//       })
				//     }),
				//     zIndex: 3000,
				//     name: '1111'
				//   }),

				// );

				featuresArr.push(feature);
			} // for 结束
			// 批量添加feature
			flagLayer.getSource().addFeatures(featuresArr);
		},

		 gis
		handleGisCommand(cmd) {
			switch (cmd) {
				case "1":
					this.drawDistance();
					break;
				case "2":
					this.drawArea();
					break;
				case "3":
					this.drawClear();
					break;
				case "4":
					this.printMap();
					break;

				default:
					break;
			}
		},
		/**
		 * 测距离
		 */
		drawDistance() {
			let source = new VectorSource(); // 首先创建一个数据源,用来放置绘制过程中和绘制结束后的线段
			const layer = new VectorLayer({
				// 添加一个图层,用来放置数据源,样式自己随便设置就可以了,我这里默认的官网
				source: source,
				style: new Style({
					fill: new Fill({
						color: "rgba(255, 255, 255, 0.2)",
					}),
					stroke: new Stroke({
						color: "#ffcc33",
						width: 4,
					}),
					image: new CircleStyle({
						radius: 7,
						fill: new Fill({
							color: "#ffcc33",
						}),
					}),
				}),
				zIndex: 4000,
			});
			this.mapDraw.mapMouseMove = this.map.on("pointermove", ev => {
				// 给地图添加一个鼠标移动事件
				let helpMsg = "点击开始测量"; // 默认开始的操作提示文本
				if (this.mapDraw.feature) {
					// featuer 是全局的,判断有没有点击鼠标绘制过
					helpMsg = "双击结束测量"; // 如果之前点击绘制了就显示双击结束
				}
				this.mapDraw.helpTooltipElement.innerHTML = helpMsg; // 设置dom的提示文字
				this.mapDraw.helpTooltip.setPosition(ev.coordinate); // 设置位置跟着鼠标走
				this.mapDraw.helpTooltipElement.classList.remove("hidden"); // 让他显示出来
			});
			this.drawCreateHelpTooltip(); // 创建那个helpTooltipElement方法
			this.map.addLayer(layer); // 把图层添加到地图
			this.mapDraw.drawLayers.push(layer); // 保存起来

			// 开始绘制线
			this.mapDraw.draw = new Draw({
				source: source,
				type: "LineString", // 绘制线
				style: new Style({
					// 绘制完成之前线的样式,这是官网的样式,需要的话自己可以修改成自己想要的样子
					fill: new Fill({
						color: "rgba(255, 255, 255, 0.2)",
					}),
					stroke: new Stroke({
						color: "rgba(0, 0, 0, 0.5)",
						lineDash: [10, 10],
						width: 4,
					}),
					image: new CircleStyle({
						radius: 5,
						stroke: new Stroke({
							color: "rgba(0, 0, 0, 0.7)",
						}),
						fill: new Fill({
							color: "rgba(255, 255, 255, 0.2)",
						}),
					}),
				}),
			});
			this.map.addInteraction(this.mapDraw.draw); // draw 绑定到地图上面去

			// 格式化长度, 直接官网代码
			const formatLength = function(line) {
				const length = getLength(line);
				let output;
				if (length > 100) {
					output = Math.round((length / 1000) * 100) / 100 + " " + "km";
				} else {
					output = Math.round(length * 100) / 100 + " " + "m";
				}
				return output;
			};
			this.drawCreateMeasureTooltip(); // 创建那个距离的提示框

			// 开始监听绘制
			const _this = this;
			this.mapDraw.draw.on("drawstart", evt => {
				_this.mapDraw.feature = evt.feature; // feature就是全局的
				let tooltipCoord = evt.coordinate; // 鼠标当前的位置
				_this.mapDraw.listener = _this.mapDraw.feature.getGeometry().on("change", function(evt) {
					const geom = evt.target;
					let output = formatLength(geom); // 距离的格式
					tooltipCoord = geom.getLastCoordinate(); // 设置鼠标位置改变后的实时位置
					_this.mapDraw.measureTooltipElement.innerHTML = output; // 设置提示框的内容,就是距离
					_this.mapDraw.measureTooltip.setPosition(tooltipCoord); // 设置距离提示框的位置
				});
			});

			// 双击绘制完成
			this.mapDraw.draw.on("drawend", () => {
				this.mapDraw.measureTooltipElement.className = "ol-tooltip ol-tooltip-static";
				this.mapDraw.measureTooltip.setOffset([0, -7]);
				this.mapDraw.feature = null;
				this.mapDraw.measureTooltipElement = null;
				this.drawCreateMeasureTooltip();
				unByKey(this.mapDraw.listener);

				// 画完一次就终止(不连续画线)
				unByKey(this.mapDraw.mapMouseMove);
				setTimeout(() => {
					this.mapDraw.helpTooltipElement.classList.add("hidden");
					this.mapDraw.helpTooltipElement = null;
					this.map.removeInteraction(this.mapDraw.draw);
				}, 50);
			});
		},
		/**
		 * 测距离提示信息
		 */
		drawCreateHelpTooltip() {
			if (this.mapDraw.helpTooltipElement) {
				this.mapDraw.helpTooltipElement.parentNode.removeChild(this.mapDraw.helpTooltipElement);
			}
			this.mapDraw.helpTooltipElement = document.createElement("div");
			this.mapDraw.helpTooltipElement.className = "ol-tooltip hidden";
			this.mapDraw.helpTooltip = new Overlay({
				element: this.mapDraw.helpTooltipElement,
				offset: [15, 0],
				positioning: "center-left",
			});
			this.map.addOverlay(this.mapDraw.helpTooltip);
		},
		/**
		 * 测距离提示信息
		 */
		drawCreateMeasureTooltip() {
			if (this.mapDraw.measureTooltipElement) {
				this.mapDraw.measureTooltipElement.parentNode.removeChild(this.mapDraw.measureTooltipElement);
			}
			this.mapDraw.measureTooltipElement = document.createElement("div");
			this.mapDraw.measureTooltipElement.className = "ol-tooltip ol-tooltip-measure";
			this.mapDraw.measureTooltip = new Overlay({
				element: this.mapDraw.measureTooltipElement,
				offset: [0, -15],
				positioning: "bottom-center",
				stopEvent: false,
				insertFirst: false,
			});
			this.mapDraw.drawElements.push(this.mapDraw.measureTooltip);
			this.map.addOverlay(this.mapDraw.measureTooltip);
		},
		/**
		 * 清空绘制
		 */
		drawClear() {
			for (let i = 0; i < this.mapDraw.drawLayers.length; i++) {
				this.map.removeLayer(this.mapDraw.drawLayers[i]);
			}
			for (let i = 0; i < this.mapDraw.drawElements.length; i++) {
				this.map.removeOverlay(this.mapDraw.drawElements[i]);
			}
			this.mapDraw.drawLayers = [];
			this.mapDraw.drawElements = [];
			this.map.removeInteraction(this.mapDraw.draw);
			unByKey(this.mapDraw.mapMouseMove);
		},

		/// 测面积
		drawArea() {
			let source = new VectorSource(); // 首先创建一个数据源,用来放置绘制过程中和绘制结束后的线段
			const layer = new VectorLayer({
				// 添加一个图层,用来放置数据源,样式自己随便设置就可以了,我这里默认的官网
				source: source,
				style: new Style({
					fill: new Fill({
						color: "rgba(255, 255, 255, 0.2)",
					}),
					stroke: new Stroke({
						color: "#ffcc33",
						width: 4,
					}),
					image: new CircleStyle({
						radius: 7,
						fill: new Fill({
							color: "#ffcc33",
						}),
					}),
				}),
				zIndex: 4000,
			});
			this.mapDraw.mapMouseMove = this.map.on("pointermove", ev => {
				// 给地图添加一个鼠标移动事件
				let helpMsg = "点击开始测量"; // 默认开始的操作提示文本
				if (this.mapDraw.feature) {
					// featuer 是全局的,判断有没有点击鼠标绘制过
					helpMsg = "双击结束测量"; // 如果之前点击绘制了就显示双击结束
				}
				this.mapDraw.helpTooltipElement.innerHTML = helpMsg; // 设置dom的提示文字
				this.mapDraw.helpTooltip.setPosition(ev.coordinate); // 设置位置跟着鼠标走
				this.mapDraw.helpTooltipElement.classList.remove("hidden"); // 让他显示出来
			});
			this.drawCreateHelpTooltip(); // 创建那个helpTooltipElement方法
			this.map.addLayer(layer); // 把图层添加到地图
			this.mapDraw.drawLayers.push(layer); // 保存起来

			// 开始绘制线
			this.mapDraw.draw = new Draw({
				source: source,
				type: "Polygon", // 绘制线
				style: new Style({
					// 绘制完成之前线的样式,这是官网的样式,需要的话自己可以修改成自己想要的样子
					fill: new Fill({
						color: "rgba(255, 255, 255, 0.2)",
					}),
					stroke: new Stroke({
						color: "rgba(0, 0, 0, 0.5)",
						lineDash: [10, 10],
						width: 4,
					}),
					image: new CircleStyle({
						radius: 5,
						stroke: new Stroke({
							color: "rgba(0, 0, 0, 0.7)",
						}),
						fill: new Fill({
							color: "rgba(255, 255, 255, 0.2)",
						}),
					}),
				}),
			});
			this.map.addInteraction(this.mapDraw.draw); // draw 绑定到地图上面去
			const _this = this;
			// 面积测量函数
			const formatArea = function(polygon) {
				var sourceProj = _this.map.getView().getProjection(); // 获取投影坐标系
				var area = getArea(polygon, {
					projection: sourceProj,
				});
				var output;
				if (area > 10000) {
					output = Math.round((area / 1000000) * 100) / 100 + " " + "km<sup>2</sup>";
				} else {
					output = Math.round(area * 100) / 100 + " " + "m<sup>2</sup>";
				}
				return output;
			};
			this.drawCreateMeasureTooltip(); // 创建那个距离的提示框

			// 开始监听绘制

			this.mapDraw.draw.on("drawstart", evt => {
				_this.mapDraw.feature = evt.feature; // feature就是全局的
				let tooltipCoord = evt.coordinate; // 鼠标当前的位置
				_this.mapDraw.listener = _this.mapDraw.feature.getGeometry().on("change", function(evt) {
					const geom = evt.target;
					let output = formatArea(geom); // 距离的格式
					tooltipCoord = geom.getInteriorPoint().getCoordinates(); // 设置鼠标位置改变后的实时位置
					_this.mapDraw.measureTooltipElement.innerHTML = output; // 设置提示框的内容,就是距离
					_this.mapDraw.measureTooltip.setPosition(tooltipCoord); // 设置距离提示框的位置
				});
			});

			// 双击绘制完成
			this.mapDraw.draw.on("drawend", () => {
				this.mapDraw.measureTooltipElement.className = "ol-tooltip ol-tooltip-static";
				this.mapDraw.measureTooltip.setOffset([0, -7]);
				this.mapDraw.feature = null;
				this.mapDraw.measureTooltipElement = null;
				this.drawCreateMeasureTooltip();
				unByKey(this.mapDraw.listener);

				// 画完一次就终止(不连续画线)
				unByKey(this.mapDraw.mapMouseMove);
				setTimeout(() => {
					this.mapDraw.helpTooltipElement.classList.add("hidden");
					this.mapDraw.helpTooltipElement = null;
					this.map.removeInteraction(this.mapDraw.draw);
				}, 50);
			});
		},

		/**
		 * 打印地图(将地图保存为图片)
		 */
		printMap() {
			const that = this;
			let exportOptions = {
				filter: function(element) {
					var className = element.className || "";
					return (
						className.indexOf("ol-control") === -1 ||
						className.indexOf("ol-scale") > -1 ||
						(className.indexOf("ol-attribution") > -1 && className.indexOf("ol-uncollapsible"))
					);
				},
			};
			that.map.once("rendercomplete", function(e) {
				// exportOptions.width = 1000;
				// exportOptions.height = 800;
				domtoimage.toJpeg(that.map.getViewport(), exportOptions).then(function(dataUrl) {
					saveAs(dataUrl, "map.jpg");
				});
			});
			that.map.updateSize();
		},

		 gis end

		/ 全屏
		enterfullscreen() {
			//进入全屏
			let docElm = document.documentElement;
			//FireFox
			if (docElm.mozRequestFullScreen) {
				docElm.mozRequestFullScreen();
			}
			//Chrome等
			else if (docElm.webkitRequestFullScreen) {
				docElm.webkitRequestFullScreen();
			}
			//IE11
			else if (elem.msRequestFullscreen) {
				elem.msRequestFullscreen();
			}
			this.isFullScreen = true;
		},

		exitfullscreen() {
			//退出全屏
			if (document.mozCancelFullScreen) {
				document.mozCancelFullScreen();
			} else if (document.webkitCancelFullScreen) {
				document.webkitCancelFullScreen();
			} else if (document.msExitFullscreen) {
				document.msExitFullscreen();
			}
			this.isFullScreen = false;
		},

		 全屏 end

		/// 轨迹回放,  轨迹运动
		//轨迹线
		addTrack() {
			this.mapTrack.route = new LineString(this.mapTrack.coordinates);
			this.mapTrack.geometryMove = new Point(this.mapTrack.route.getFirstCoordinate());

			this.mapTrack.featureMove = new Feature({
				type: "featureMove",
				geometry: this.mapTrack.geometryMove,
			});
			this.mapTrack.featureMove.name = "track_point";

			//坐标转换
			this.mapTrack.dotsData = this.mapTrack.coordinates.map(item => {
				return transform(item, "EPSG:3857", "EPSG:4326");
			});
			//深复制车的位置,不在原数组改变,方便重新播放
			// this.carPoints = JSON.parse(JSON.stringify(this.dotsData));
			this.mapTrack.carPoints = [...this.mapTrack.dotsData];

			this.mapTrack.routeLayer = new VectorLayer({
				source: new VectorSource({
					features: [
						new Feature({
							type: "route",
							geometry: this.mapTrack.route,
						}),
						this.mapTrack.featureMove,
						new Feature({
							type: "startIcon",
							geometry: new Point(this.mapTrack.route.getFirstCoordinate()),
						}),
						new Feature({
							type: "endIcon",
							geometry: new Point(this.mapTrack.route.getLastCoordinate()),
						}),
						new Feature({
							type: "featureMove",
							geometry: new Point(this.mapTrack.carPoints[0]),
						}),
					],
				}),
				style: feature => {
					if (feature.get("type") == "route") {
						return new Style({
							stroke: new Stroke({
								width: 6,
								color: [25, 59, 128, 1],
							}),
						});
					} else if (feature.get("type") == "startIcon") {
						return new Style({
							// image: new Icon({
							// 	anchor: [0.5, 1],
							// 	src: "",
							// 	// src: require("@/assets/image/map_start.png"),
							// 	// src: require("@/assets/image/homecar2.png"),
							// 	scale: 1, //设置大小
							// }),
						});
					} else if (feature.get("type") == "endIcon") {
						return new Style({
							// image: new Icon({
							// 	anchor: [0.5, 1],
							// 	src: "",
							// 	// src: require("@/assets/image/map_end.png"),
							// 	// src: require("@/assets/image/homecar2.png"),
							// 	scale: 1, //设置大小
							// }),
						});
					} else if (feature.get("type") == "featureMove") {
						return new Style({
							image: new Icon({
								//	src: "https://openlayers.org/en/v4.6.5/examples/data/icon.png",
								// src: require("@/assets/image/map_arrow.png"),
								src: require("@/assets/image/homecar2.png"),
								scale: 1,
								//anchor: [0.5, 0.5],
								rotation: this.trackCountRotate(),
							}),
						});
					}
					//return this.mapTrack.styles[feature.get("type")];
				},
			});

			this.map.addLayer(this.mapTrack.routeLayer);
		},
		/**
		 * 小车开始运动
		 */
		trackMoveStart() {
			this.mapTrack.timer = setInterval(() => {
				if (this.mapTrack.routeIndex + 1 >= this.mapTrack.carPoints.length) {
					//重头开始
					this.mapTrack.routeIndex = 0;
					//移除要素
					// this.mapTrack.routeLayer.getSource().removeFeature(this.mapTrack.featureMove);
					clearInterval(this.mapTrack.timer);
					//重复运动
					// this.addTrack();
					// this.trackMoveStart();
					//this.open(); //自动开启功能
					return;
				}
				//到转折点旋转角度
				if (this.trackNextPoint() === this.mapTrack.carPoints[this.mapTrack.routeIndex + 1]) {
					this.mapTrack.routeIndex++;

					if (this.mapTrack.featureMove.getStyle()) {
						this.mapTrack.featureMove
							.getStyle()
							.getImage()
							.setRotation(this.trackCountRotate());
					}
				}
				//改变坐标点
				this.mapTrack.featureMove.getGeometry().setCoordinates(fromLonLat(this.mapTrack.carPoints[this.mapTrack.routeIndex]));

				// 经过的点位
				console.log(this.mapTrack.coordinates[this.mapTrack.routeIndex]);
			}, 10);
		},
		/**
		 * 小车暂停运动
		 */
		trackMovePause() {
			clearInterval(this.mapTrack.timer);
		},
		//计算下一个点的位置
		//这里的算法是计算了两点之间的点   两点之间的连线可能存在很多个计算出来的点
		trackNextPoint() {
			let routeIndex = this.mapTrack.routeIndex;
			let p1 = this.map.getPixelFromCoordinate(fromLonLat(this.mapTrack.carPoints[routeIndex])); //获取在屏幕的像素位置
			let p2 = this.map.getPixelFromCoordinate(fromLonLat(this.mapTrack.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.mapTrack.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.mapTrack.carPoints[routeIndex] = coor; //这里会将前一个点重新赋值  要素利用这个坐标变化进行移动
				return this.mapTrack.carPoints[routeIndex];
			}
		},
		//计算两点之间的角度  算旋转角度
		trackCountRotate() {
			let i = this.mapTrack.routeIndex,
				j = i + 1;
			if (j === this.mapTrack.carPoints.length) {
				i--;
				j--;
			}
			let p1 = this.mapTrack.carPoints[i];
			let p2 = this.mapTrack.carPoints[j];
			return Math.atan2(p2[0] - p1[0], p2[1] - p1[1]);
		},
		resetTrack() {
			clearInterval(this.mapTrack.timer);
			this.mapTrack.routeLayer.getSource().clear();
			this.map.removeLayer(this.mapTrack.routeLayer);
			this.mapTrack.featureMove = {};
			this.mapTrack.geometryMove = {};
			this.mapTrack.timer = null;
			this.mapTrack.carPoints = [];
			this.mapTrack.routeIndex = 0;
			this.mapTrack.routeLayer = {};
			this.mapTrack.route = null;
		},
	},
};
</script>

<style lang="scss" scoped>
.mapBox {
	width: 100%;
	height: 100%;
	position: relative;
}
.map {
	width: 100%;
	height: 100%;
}

/deep/.ol-control {
	display: none;
}

/deep/.blueLayer {
	// filter: grayscale(100%) sepia(21%) invert(100%) saturate(150%) brightness(100%);
	filter: grayscale(98%) invert(100%) sepia(50%) hue-rotate(180deg) saturate(2000%) brightness(50%) contrast(90%) !important;
}

.optionsBox {
	position: absolute;
	top: 20px;
	left: 20px;
	z-index: 1000;
	display: flex;
	align-items: center;
}
</style>

<!-- 修改提示框样式 -->
<style lang="scss" scoped>
/deep/.ol-tooltip {
	position: relative;
	background: rgba(0, 0, 0, 0.5);
	border-radius: 4px;
	color: white;
	padding: 4px 8px;
	opacity: 0.7;
	white-space: nowrap;
	font-size: 12px;
	cursor: default;
	user-select: none;
}

/deep/ .hidden {
	display: none;
}

/deep/.ol-tooltip-measure {
	opacity: 1;
	font-weight: bold;
}

/deep/.ol-tooltip-static {
	background-color: #ffcc33;
	color: black;
	border: 1px solid white;
}

/deep/.ol-tooltip-measure:before,
/deep/.ol-tooltip-static:before {
	border-top: 6px solid rgba(0, 0, 0, 0.5);
	border-right: 6px solid transparent;
	border-left: 6px solid transparent;
	content: "";
	position: absolute;
	bottom: -6px;
	margin-left: -7px;
	left: 50%;
}

/deep/.ol-tooltip-static:before {
	border-top-color: #ffcc33;
}
</style>
相关推荐
Dread_lxy7 小时前
vue 依赖注入(Provide、Inject )和混入(mixins)
前端·javascript·vue.js
龙猫蓝图9 小时前
vue el-date-picker 日期选择器禁用失效问题
前端·javascript·vue.js
peachSoda79 小时前
随手记:简单实现纯前端文件导出(XLSX)
前端·javascript·vue.js
Tttian62210 小时前
Vue全栈开发旅游网项目(11)-用户管理前端接口联调
前端·vue.js·django
龙猫蓝图11 小时前
vue el-date-picker 日期选择 回显后成功后无法改变的解决办法
前端·javascript·vue.js
刘志辉12 小时前
Pure Adminrelease(水滴框架配置)
vue.js
工业互联网专业12 小时前
Python毕业设计选题:基于Django+uniapp的公司订餐系统小程序
vue.js·python·小程序·django·uni-app·源码·课程设计
黄景圣13 小时前
CURD低代码程序设计
前端·vue.js·后端
lin-lins13 小时前
Vue 模板编译原理
前端·javascript·vue.js
customer0813 小时前
【开源免费】基于SpringBoot+Vue.JS课程答疑系统(JAVA毕业设计)
java·jvm·vue.js·spring boot·spring cloud·kafka·开源