// 引入OpenLayers的主模块 import Map from 'ol/Map'; import View from 'ol/View'; import Feature from 'ol/Feature'; import Point from 'ol/geom/Point'; import VectorLayer from 'ol/layer/Vector'; import VectorSource from 'ol/source/Vector'; import Style from 'ol/style/Style'; import Icon from 'ol/style/Icon'; import Text from 'ol/style/Text'; import Projection from 'ol/proj/Projection'; import { getWidth, getTopLeft } from 'ol/extent'; import TileLayer from 'ol/layer/Tile'; import WMTS from 'ol/source/WMTS'; import WMTSTileGrid from 'ol/tilegrid/WMTS'; import WMTSCapabilities from 'ol/format/WMTSCapabilities'; import { Fill, Stroke } from 'ol/style'; import proj4 from 'proj4'; import { register } from 'ol/proj/proj4'; import { defaults } from 'ol/control'; import Vector from 'ol/layer/Vector'; import SourceVector from 'ol/source/Vector'; import GeoJSON from 'ol/format/GeoJSON'; import { fromLonLat } from 'ol/proj'; import axios from 'axios'; import { fromExtent } from 'ol/geom/Polygon'; import { LinearRing, LineString, Polygon } from 'ol/geom'; import { Graticule } from 'ol/layer'; import { getPointsCenter, mergeGeoJsonPolygons } from '@/utils/gisUtils'; import { Cluster } from 'ol/source'; import CircleStyle from 'ol/style/Circle'; import Overlay from 'ol/Overlay'; import { DoubleClickZoom, DragPan, Draw, Select } from 'ol/interaction'; import { click } from 'ol/events/condition'; import Circle from 'ol/geom/Circle'; import { deepClone, getRgba, hexToRgba, initDrag, rgbToRgba } from '@/utils'; import { createBox } from 'ol/interaction/Draw'; import * as turf from '@turf/turf'; import { nanoid } from 'nanoid'; import carImg from '@/assets/images/car.png'; const tk = 'a8df87f1695d224d2679aa805c1268d9'; const commonUrl = import.meta.env.VITE_APP_BASE_API2 + 'api/oneShare/proxyHandler/gd/'; // proj4.defs('EPSG:4490', '+proj=longlat +ellps=GRS80 +no_defs +type=crs'); proj4.defs( 'EPSG:4490', 'GEOGCS["China Geodetic Coordinate System 2000",DATUM["China_2000",SPHEROID["CGCS2000",6378137,298.257222101,AUTHORITY["EPSG","1024"]],AUTHORITY["EPSG","1043"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4490"]]' ); // proj4.defs('EPSG:4525', '+proj=tmerc +lat_0=0 +lon_0=111 +k=1 +x_0=37500000 +y_0=0 +ellps=GRS80 +units=m +no_defs +type=crs'); register(proj4); const projection = new Projection({ code: 'EPSG:4490', units: 'degrees', axisOrientation: 'neu' }); projection.setExtent([-180, -90, 180, 90]); projection.setWorldExtent([-180, -90, 180, 90]); const projectionExtent = [-180, -90, 180, 90]; const size = getWidth(projectionExtent) / 256; const resolutions = []; for (let z = 2; z < 22; ++z) { resolutions[z] = size / Math.pow(2, z); } export class olMap { private map; private options; private markers = []; private drawOptions = { graphicsType: 'circle', strokeColor: '#f80102', strokeOpacity: 1, strokeWeight: 2, fillColor: '#f80102', fillOpacity: 0, strokeStyle: 'solid', icon: '', iconName: '' }; private plot; private drawVector; private drawTool; private vectorLayer; // 边界遮罩图层 private maskLayer; private maskLayer2; // 显示信息框 private infoWindow; private select; // 车辆轨迹 private carLayer; private carFeature; private traceFeature; // 自定义绘制结束调用方法 private drawEndMethod; private selectedFeature; constructor(options) { this.options = options; this.map = new Map({ controls: defaults({ zoom: false, rotate: false }), layers: [], target: options.dom, view: new View({ center: options.center && options.center.length === 2 ? [options.center[0], options.center[1]] : [110.90153121597234, 21.98323671981171], zoom: options.zoom ? options.zoom : 9.6, projection: projection, maxZoom: options.maxZoom ? options.maxZoom : 18, minZoom: options.minZoom ? options.minZoom : 1 }) }); // 初始化比例尺 if (options.showScale) { // this.map.addControl(new AMap.Scale()); } if (options.drawTool?.use) { this.initMouseTool(options.drawTool); } this.initLayer(options); // 给标注点添加手势 this.map.on('pointermove', (e) => { this.map.getTargetElement().style.cursor = 'auto'; const features = this.map.getFeaturesAtPixel(e.pixel); features.forEach((feature) => { const originalFeature = feature.get('features') ? feature.get('features')[0] : ''; if (!!originalFeature && originalFeature.get('pointer')) { this.map.getTargetElement().style.cursor = 'pointer'; //设置鼠标样式 } else { this.map.getTargetElement().style.cursor = 'auto'; } }); }); // 创建选择交互 this.select = new Select({ condition: click, style: null, filter: (feature, layer) => { // 只有vectorLayer图层可选择 return layer === this.vectorLayer; } }); this.map.addInteraction(this.select); // 监听Select交互的select事件 this.select.on('select', (event) => { const selectedFeatures = event.selected[0]; // 获取被选中的要素集合 const features = selectedFeatures.get('features'); if (selectedFeatures && !!features) { const originalFeature = features[0]; const size = features.length; if (size === 1) { if (this.selectedFeature !== originalFeature) { if (this.selectedFeature) { this.selectedFeature.set('icon', this.selectedFeature.get('image')); } this.selectedFeature = originalFeature; const icon = originalFeature.get('imageHover'); const extData = originalFeature.get('extData'); originalFeature.set('icon', icon); options.onMarkerClick(extData); } } else { // 聚合要素 this.select.getFeatures().clear(); const currentZoom = this.map.getView().getZoom(); this.map.getView().setZoom(currentZoom + 1); const points = []; features.forEach((feature) => { const geometry = feature.getGeometry(); // 获取要素的几何对象 const type = geometry.getType(); // 获取几何类型 if (type === 'Point') { points.push(geometry.getCoordinates()); } }); const newFeature = getPointsCenter(points); this.map.getView().setCenter(newFeature.geometry.coordinates); event.selected = []; } } }); } async initLayer(options) { // 添加新的图层 if (Array.isArray(options.id)) { for (const layer of options.id) { if (typeof layer === 'string') { await this.formatXml(layer); // 等待当前 layer 处理完成 } else { await this.formatXml(layer.id, layer.minZoom, layer.maxZoom, layer.zIndex, layer.visible); // 等待当前 layer 处理完成 } } } else if (options.id === 'tianditu') { await this.formatXml2(); } else if (options.id) { // 如果 options.id 不是数组,但确实是一个图层,则直接处理 await this.formatXml(options.id, options.minZoom, options.maxZoom, options.zIndex, options.visible); } // 创建Vector层并添加到地图上 this.vectorLayer = new VectorLayer({ source: new VectorSource({ features: [] }) }); this.map.addLayer(this.vectorLayer); if (typeof this.options.onLoadCompleted === 'function') { this.options.onLoadCompleted(this.map); } } formatXml2() { const baseUrl = 'https://t{0-7}.tianditu.gov.cn/'; const vecType = 'vec'; // 矢量图层类型 const projType = 'w'; // 投影类型:web mercator const reqParams = { SERVICE: 'WMTS', REQUEST: 'GetTile', VERSION: '1.0.0', LAYER: '', STYLE: 'default', TILEMATRIXSET: projType, FORMAT: 'tiles', TILECOL: '{x}', TILEROW: '{y}', TILEMATRIX: '{z}', tk: key }; const newParams = Object.assign({}, params, { LAYER: dataType }); let paramsStr = ''; for (const [k, v] of Object.entries(newParams)) { paramsStr += `${k}=${v}&`; } return `${baseUrl}${dataType}_${projType}/wmts?${paramsStr.slice(0, -1)}`; } formatXml(code: string, minZoom?: number, maxZoom?: number, zIndex?: number, visible?: boolean) { const xml = new WMTSCapabilities(); return this.getCapabilities(code).then((lists) => { const geojson = xml.read(lists.data); const data = geojson.Contents.Layer[0]; const layerParam = { layerName: data.Abstract, styleName: data.Identifier, tilematrixset: data.TileMatrixSetLink[0].TileMatrixSet, format: data.Format[0] }; this.createWmsLayer(code, layerParam, minZoom, maxZoom, zIndex, visible); }); } // 请求接口获取地图信息 getCapabilities(code) { return axios.get(commonUrl + code + '?SERVICE=WMTS&REQUEST=GetCapabilities'); } // 请求地图图片加载图层 createWmsLayer(code, layerParam, minZoom = 0, maxZoom, zIndex = -1, visible = true) { const source = new WMTS({ url: commonUrl + code, crossOrigin: 'Anonymous', layer: layerParam.layerName, style: layerParam.styleName, matrixSet: layerParam.tilematrixset, format: layerParam.format, wrapX: true, tileGrid: new WMTSTileGrid({ origin: getTopLeft(projectionExtent), resolutions: resolutions, matrixIds: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21'] }) }); const layer = new TileLayer({ name: code, source: source, zIndex: zIndex, minZoom: minZoom, maxZoom, visible: visible }); layer.set('layerName', code); this.map.addLayer(layer); } // 初始化绘画工具 initMouseTool(options) { this.drawOptions = { graphicsType: options.graphicsType ? options.graphicsType : 'cirlce', strokeColor: options.color, strokeOpacity: 1, strokeWeight: 2, fillColor: options.color, fillOpacity: options.drawType === '1' ? 0 : 0.5, strokeStyle: 'solid' }; // 创建矢量图层用于绘制 this.drawVector = new Vector({ source: new VectorSource({ features: [] }) }); this.map.addLayer(this.drawVector); } // 绘制结束事件 onDrawEnd(event) { const feature = event.feature; const aa = feature.getGeometry(); // 继续编辑编辑 this.drawGraphics(this.drawOptions.graphicsType); } // 绘制图形 drawGraphics(newOptions: MouseTool) { const typeList = { circle: 'Circle', rectangle: 'Circle', polygon: 'Polygon', measureArea: 'Polygon', straightLine: 'LineString', marker: 'Point', text: 'Point', anyLine: 'AnyLine' }; const data = getRgba(newOptions.color); let geometryFunction = null; if (newOptions.graphicsType === 'rectangle') { // 绘制矩形的方法 geometryFunction = createBox(); } if (!!typeList[newOptions.graphicsType]) { if (newOptions.graphicsType === 'text') { this.drawOptions = { type: newOptions.graphicsType, title: newOptions.title, text: newOptions.text, fontSize: newOptions.fontSize, fontColor: newOptions.fontColor, lnglat: newOptions.lnglat }; // 绘制文字 return this.addText(this.drawOptions); } else { this.drawOptions = { type: newOptions.graphicsType, title: newOptions.title, strokeColor: !!data.color ? data.color : newOptions.color, strokeOpacity: 1, strokeWeight: 1, fillColor: data.color, fillOpacity: data.opacity, strokeStyle: 'solid' }; if (newOptions.graphicsType === 'marker') { this.drawOptions.icon = newOptions.icon; this.drawOptions.iconName = newOptions.iconName; this.drawOptions.size = newOptions.size; } this.closeDraw(); if (newOptions.graphicsType === 'anyLine') { this.drawAnyLine(); } else { // 创建绘制交互 let style = new Style({ stroke: new Stroke({ color: rgbToRgba(this.drawOptions.strokeColor, this.drawOptions.strokeOpacity), width: this.drawOptions.strokeWeight }), fill: new Fill({ color: rgbToRgba(this.drawOptions.fillColor, this.drawOptions.fillOpacity) }) }); this.drawTool = new Draw({ source: this.drawVector.getSource(), type: typeList[newOptions.graphicsType], geometryFunction: geometryFunction, style: style }); // 添加绘制交互到地图 this.map.addInteraction(this.drawTool); // 监听绘制结束事件 this.drawTool.on('drawend', (event) => { const feature = event.feature; // 获取几何对象 if (newOptions.graphicsType !== 'marker') { if (newOptions.graphicsType === 'measureArea') { const geometry = feature.getGeometry(); const coordinates = geometry.getCoordinates(); const pathArr = coordinates[0]; const area = turf.area(turf.polygon([pathArr])); style = new Style({ stroke: style.getStroke(), fill: style.getFill(), text: new Text({ text: '区域面积' + area.toFixed(2) + '平方米', font: '14px Calibri,sans-serif', fill: new Fill({ color: '#000' }), stroke: new Stroke({ color: '#fff', width: 3 }), overflow: true }) }); } feature.setStyle(style); } }); } } } return this.drawOptions; } // 空间分析绘制图形 drawGraphics2(newOptions: MouseTool) { const typeList = { circle: 'Circle', rectangle: 'Circle', polygon: 'Polygon' }; let geometryFunction = null; if (newOptions.graphicsType === 'rectangle') { // 绘制矩形的方法 geometryFunction = createBox(); } if (!!typeList[newOptions.graphicsType]) { this.drawOptions = { type: newOptions.graphicsType, strokeColor: newOptions.color, strokeOpacity: 1, strokeWeight: 1, fillColor: newOptions.color, fillOpacity: newOptions.drawType === '1' ? '0' : '0.5', strokeStyle: 'solid' }; this.closeDraw(); // 创建绘制交互 const style = new Style({ stroke: new Stroke({ color: hexToRgba(this.drawOptions.strokeColor, this.drawOptions.strokeOpacity), width: this.drawOptions.strokeWeight }), fill: new Fill({ color: hexToRgba(this.drawOptions.fillColor, this.drawOptions.fillOpacity) }) }); this.drawTool = new Draw({ source: this.drawVector.getSource(), type: typeList[newOptions.graphicsType], geometryFunction: geometryFunction, style: style }); // 添加绘制交互到地图 this.map.addInteraction(this.drawTool); // 监听绘制结束事件 this.drawTool.on('drawend', (event) => { const feature = event.feature; // 获取几何对象 feature.setStyle(style); }); } } addText(options) { // 创建文本覆盖物 const style = new Style({ text: new Text({ text: options.text, font: options.fontSize + ' Calibri,sans-serif', fill: new Fill({ color: options.fontColor }), // stroke: new Stroke({ // color: '#fff', // width: 3 // }), overflow: true }) }); const text = new Feature({ geometry: new Point(options.lnglat) }); text.setStyle(style); // 将文本覆盖物添加到地图 this.drawVector.getSource().addFeature(text); const id = nanoid(); text.set('id', id); const data: any = deepClone(options); data.id = id; return { text, data }; } drawAnyLine() { document.addEventListener('touchend', this.handleTouchEnd); this.map.on('pointerdown', this.handleTouchStart); this.map.on('pointermove', this.handleTouchMove.bind(null, this.options)); document.addEventListener('mouseup', this.handleTouchEnd.bind(null, this.options)); } handleTouchStart(e) { this.drawing = true; const interactions = this.map.getInteractions(); interactions.forEach((interaction) => { if (interaction instanceof DragPan || interaction instanceof DoubleClickZoom) { interaction.setActive(false); // 修改为需要的值,true或false } }); this.path = [e.lnglat]; // if (this.anyLine) { // map.remove(anyLine); // } } handleTouchMove(options, e) { if (!this.drawing) return; this.path.push(e.lnglat); // if (anyLine) { // map.remove(anyLine); // } this.anyLine = new Feature({ geometry: new LineString(this.path) // path为坐标数组,如[[x1,y1], [x2,y2]] }); const lineStyle = new Style({ stroke: new Stroke({ color: hexToRgba(options.strokeColor, options.strokeOpacity), // 合并颜色与透明度 width: options.strokeWeight, lineJoin: 'round' // 线段连接处圆角 }) }); this.anyLine.setStyle(lineStyle); // new AMap.Polyline({ // path: path, // strokeColor: options.strokeColor, // strokeOpacity: options.strokeOpacity, // strokeWeight: options.strokeWeight, // strokeStyle: options.strokeStyle, // lineJoin: 'round' // }); this.drawVector.getSource().addFeature(this.anyLine); } handleTouchEnd(options) { // drawing = false; // map.setStatus({ // showIndoorMap: true, // dragEnable: true, // keyboardEnable: true, // doubleClickZoom: true, // zoomEnable: true, // rotateEnable: true // }); // map.off('touchstart', handleTouchStart); // map.on('touchmove', handleTouchMove.bind(null, options)); // document.addEventListener('touchend', handleTouchEnd.bind(null, options)); // map.off('mousedown', handleTouchStart); // map.off('mousemove', handleTouchMove); // document.removeEventListener('mouseup', handleTouchEnd); // if (!!drawEndMethod) { // drawEndMethod(options, anyLine); // } // anyLine = null; } // 关闭绘制 closeDraw() { if (!this.drawTool) return; this.map.removeInteraction(this.drawTool); // this.drawTool.abortDrawing(); } // 切换图层 async replaceLayers(newLayers, loadendFunc) { // 遍历当前的所有图层并移除它们 this.map.getLayers().forEach((layer) => { this.map.removeLayer(layer); }); if (Array.isArray(newLayers)) { for (const layer of newLayers) { if (typeof layer === 'string') { await this.formatXml(layer); // 等待当前 layer 处理完成 } else { await this.formatXml(layer.id, layer.minZoom, layer.maxZoom, layer.zIndex, layer.visible); // 等待当前 layer 处理完成 } } } else { // 如果 options.id 不是数组,但确实是一个图层,则直接处理 await this.formatXml(newLayers.id, newLayers.minZoom, newLayers.maxZoom, newLayers.zIndex, newLayers.visible); } // 创建Vector层并添加到地图上 this.vectorLayer = new VectorLayer({ source: new VectorSource({ features: [] }) }); this.map.addLayer(this.vectorLayer); const point = JSON.parse(JSON.stringify(this.markers)); this.markers = []; this.addMarker(point); if (loadendFunc) { loadendFunc(); } } // 集群点样式 clusterStyle(feature) { const originalFeature = feature.get('features')[0]; const size = feature.get('features').length; if (size > 1) { const outerCircle = new CircleStyle({ radius: 20, fill: new Fill({ color: 'rgba(79, 176, 206, 0.5)' }), stroke: new Stroke({ color: 'rgba(79, 176, 206, 1)' }) }); return [ new Style({ image: outerCircle, text: new Text({ text: size.toString(), font: '14px sans-serif', fill: new Fill({ color: '#ffff' }) }) }) ]; } return new Style({ geometry: originalFeature.getGeometry(), image: new Icon({ anchor: [0.5, 0.5], scale: 0.146, anchorXUnits: 'fraction', anchorYUnits: 'pixels', src: originalFeature.get('icon') }) }); } addMarker(points) { this.clearMarker(); const features = []; points.forEach((point) => { // 创建标注点 const feature = new Feature({ // 必须是数字类型,字符串不识别 geometry: new Point([Number(point.longitude), Number(point.latitude)]), name: point.name, icon: point.icon, imageHover: point.imageHover, size: point.size, pointer: true, extData: point }); // 设置自定义属性 feature.set('pointer', true); const img = new Image(); img.onload = () => { // 图片加载完成后,可以访问其 width 和 height 属性 const width = img.width; const height = img.height; const style = new Style({ image: new Icon({ anchor: [0.5, 0.5], anchorXUnits: 'fraction', anchorYUnits: 'pixels', src: point.icon, size: [width, height], scale: !!point.size[0] ? point.size[0] / width : 1 }), text: new Text({ text: point.name, fill: new Fill({ color: '#000' }), stroke: new Stroke({ color: '#fff', width: 3 }) }) }); features.setStyle(style); }; img.src = point.icon; // 设置图片的 URL,触发加载 features.push(feature); this.markers.push(point); }); const vectorSource = new VectorSource({ features: features }); const clusterSource = new Cluster({ distance: 30, source: vectorSource }); this.vectorLayer.setStyle(this.clusterStyle); this.vectorLayer.setSource(clusterSource); } // 清除所有标加 clearMarker() { if (!this.vectorLayer) return; this.vectorLayer.getSource().clear(); } showInfo(content, position, isCustom) { this.hideInfo(); if (!this.infoWindow) { this.infoWindow = new Overlay({ element: content, positioning: 'bottom-center', // 你可以根据需要调整定位方式 offset: [0, -10] // 偏移量,用于调整覆盖层相对于要素的位置 }); } this.infoWindow.setPosition(position); initDrag(this.infoWindow.element); this.map.addOverlay(this.infoWindow); } hideInfo(flag) { this.map.removeOverlay(this.infoWindow); this.infoWindow = null; if (!!flag && this.select) { this.select.getFeatures().clear(); } } /** * * @param {Geojon} chaozhou 根据geojson对象创建Featrue对象 * @returns VectorLayer */ createVecByJson(json, options) { const format = new GeoJSON(); const fs = format.readFeatures(json); this.maskLayer = new VectorLayer({ source: new VectorSource(), style: new Style({ fill: new Fill({ color: options.fillColor ? options.fillColor : 'rgba(16, 36, 59, 0.65)' }), stroke: new Stroke({ color: options.strokeColor ? options.strokeColor : 'rgba(38, 138, 185, 1)', width: 2 }) }), zIndex: options.zIndex ? options.zIndex : 99 }); this.map.addLayer(this.maskLayer); const extent = [-180, -90, 180, 90]; const polygonRing = fromExtent(extent); fs.forEach((x) => { const ft = x.values_.geometry; const coords = ft.getCoordinates(); coords.forEach((coord) => { const linearRing = new LinearRing(coord[0]); polygonRing.appendLinearRing(linearRing); }); }); const convertFt = new Feature({ geometry: polygonRing }); this.maskLayer.getSource().addFeature(convertFt); } createVecByJson2(json, options) { if (!!this.maskLayer2) { // this.map.addLayer(this.maskLayer); this.map.addLayer(this.maskLayer2); } else { this.maskLayer2 = new VectorLayer({ source: new VectorSource(), style: new Style({ fill: new Fill({ color: 'rgba(0, 0, 0, 0)' }), stroke: new Stroke({ color: options.strokeColor ? options.strokeColor : '#268ab9', width: options.strokeWeight ? options.strokeWeight : 1 }) }) }); this.map.addLayer(this.maskLayer2); this.maskLayer = new VectorLayer({ source: new VectorSource(), style: new Style({ fill: new Fill({ color: options.fillColor ? options.fillColor : 'rgba(16, 36, 59, 0.65)' }), stroke: new Stroke({ color: options.strokeColor ? options.strokeColor : 'rgba(38, 138, 185, 1)', width: 2 }) }), zIndex: options.zIndex ? options.zIndex : 99 }); // // 合并区边界 // const format = new GeoJSON(); // const data2 = mergeGeoJsonPolygons(json); // const fs = format.readFeatures(data2); // const extent = [-180, -90, 180, 90]; // const polygonRing = fromExtent(extent); // fs.forEach((x) => { // const ft = x.values_.geometry; // const coords = ft.getCoordinates(); // coords.forEach((coord) => { // const linearRing = new LinearRing(coord[0]); // polygonRing.appendLinearRing(linearRing); // }); // }); // const convertFt = new Feature({ // geometry: polygonRing // }); // this.maskLayer.getSource().addFeature(convertFt); // this.map.addLayer(this.maskLayer); // 边界部分 json.features.forEach((feature) => { if (feature.geometry.type === 'Polygon') { const feature2 = new Feature({ geometry: new LineString(feature.geometry.coordinates[0]) }); this.maskLayer2.getSource().addFeature(feature2); } else if (feature.geometry.type === 'MultiPolygon') { feature.geometry.coordinates.forEach((polygonCoords) => { const feature2 = new Feature({ geometry: new LineString(polygonCoords[0]) }); this.maskLayer2.getSource().addFeature(feature2); }); } }); } } /** * @description 创建矢量图层 * @param {String} layerName 图层名称 * @param {Number} zIndex 地图层级默认是0 * @returns */ createVecLayer(layerName = '', zIndex = 0) { const source = new SourceVector({ crossOrigin: 'anonymous' }); const layer = new Vector({ source, zIndex }); layer.set('layerName', layerName); return layer; } // 分布图遮罩层 createMask(data) { this.removeMask(); if (!data || data.length === 0) return; data.forEach((item) => { if (!item.points || item.points.length === 0) return; // 遮罩图层的样式 const maskStyle = new Style({ fill: new Fill({ color: item.color // 红色遮罩,50%透明度 }), stroke: new Stroke({ color: 'rgba(159,159,159,0.7)', width: 1 }) }); // 遮罩图层的矢量数据源(初始为空) const maskSource = new VectorSource(); // 创建一个多边形特征 const polygonFeature = new Feature({ geometry: new Polygon(item.points) }); const maskLayer = new VectorLayer({ source: maskSource, style: maskStyle, properties: { name: 'mask' } }); this.map.addLayer(maskLayer); // 将多边形特征添加到遮罩数据源中 maskSource.addFeature(polygonFeature); }); } removeMask() { //移除图层 const layersArray = this.map.getLayers().getArray(); layersArray.forEach((layer) => { // 检查图层是否有自定义属性,并且该属性是否匹配你要移除的图层的标识符 if (layer.get('name') === 'mask') { this.map.removeLayer(layer); } }); } removeMask2() { if (this.maskLayer) { this.map.removeLayer(this.maskLayer); this.maskLayer = null; } } removeMask3(isHide) { if (this.maskLayer) { this.map.removeLayer(this.maskLayer); if (!isHide) { this.maskLayer = []; } } if (this.maskLayer2) { this.map.removeLayer(this.maskLayer2); if (!isHide) { this.maskLayer2 = []; } } } /** * 绘制经纬线 * visible: boolean 是否可见 */ handleLngLatLine(visible: boolean) { // 创建经纬网图层 const graticule = new Graticule({ name: 'Graticule', showLabels: true, // 为每条刻度线绘制一个带有各自纬度/经度的标签 wrapX: false, // 是否水平重复经纬网 targetSize: 230, zIndex: 99999, strokeStyle: new Stroke({ // 用于绘制刻度线的样式 color: '#dcdcdc', // 线条颜色 width: 1 // 线条宽度 }), lonLabelStyle: new Text({ font: '12px Calibri,sans-serif', textBaseline: 'bottom', fill: new Fill({ color: '#9d9d9d' }) }), latLabelPosition: 0, latLabelStyle: new Text({ font: '12px Calibri,sans-serif', textAlign: 'left', textBaseline: 'end', fill: new Fill({ color: '#9d9d9d' }) }) }); if (visible) { this.map.addLayer(graticule); } else { this.removeLayer('Graticule'); } } /** * 移除指定name的layer * layerName: string * */ removeLayer(layerName: string) { const layers = this.map.getLayers(); layers.forEach((element) => { if (!!element && element.get('name') === layerName) { //移除 this.map.removeLayer(element); } }); } // 创建图形 createGraphics(data: any) { if (data.type === 'circle') { // 绘制圆形 return this.createCircle(data); } else if (['polygon', 'rectangle'].includes(data.type)) { // 绘制矩形、多边形 return this.createPolygon(data); } // else if (data.type === 'marker') { // // 绘制图标 // return createMarker(data); // } else if (data.type === 'measureArea') { // // 绘制面积 // return createMeasureArea(data); // } else if (data.type === 'text') { // // 文字 // return addText(data); // } else if (data.type === 'straightLine') { // // 直线 // return createStraightLine(data); // } } createCircle(data) { const circle = new Circle(data.center, data.radius); const feature = new Feature(circle); feature.setStyle( new Style({ stroke: new Stroke({ color: rgbToRgba(data.strokeColor, data.strokeOpacity), width: data.strokeWeight }), fill: new Fill({ color: rgbToRgba(data.fillColor, data.fillOpacity) }) }) ); this.drawVector.getSource().addFeature(feature); return feature; } createPolygon(data) { const polygon = new Polygon([data.path]); const feature = new Feature(polygon); feature.setStyle( new Style({ stroke: new Stroke({ color: rgbToRgba(data.strokeColor, data.strokeOpacity), width: data.strokeWeight }), fill: new Fill({ color: rgbToRgba(data.fillColor, data.fillOpacity) }) }) ); this.drawVector.getSource().addFeature(feature); return feature; } createLineString(data) { const lineString = new LineString(data.path); const feature = new Feature(lineString); feature.setStyle( new Style({ stroke: new Stroke({ color: rgbToRgba(data.strokeColor, data.strokeOpacity), width: data.strokeWeight }), fill: new Fill({ color: rgbToRgba(data.fillColor, data.fillOpacity) }) }) ); this.drawVector.getSource().addFeature(feature); return feature; } trackPlayback(lineArr) { if (!!this.carFeature) { this.carLayer.getSource().removeFeature(this.carFeature); } if (!!this.traceFeature) { this.carLayer.getSource().removeFeature(this.traceFeature); } const getAngle = (point1, point2) => { let arc = 0; if (point2 && point2.length && point1 && point1.length) { if ((point2[0] - point1[0] >= 0 && point2[1] - point1[1] >= 0) || (point2[0] - point1[0] < 0 && point2[1] - point1[1] > 0)) { arc = Math.atan((point2[0] - point1[0]) / (point2[1] - point1[1])); } else if ((point2[0] - point1[0] > 0 && point2[1] - point1[1] < 0) || (point2[0] - point1[0] < 0 && point2[1] - point1[1] < 0)) { arc = Math.PI + Math.atan((point2[0] - point1[0]) / (point2[1] - point1[1])); } } return arc; }; let lastTime = Date.now(); const source = new VectorSource(); let distance = 0; const angle = getAngle(lineArr[0], lineArr[1]); const speed = 500; let animationFlag = false; this.carFeature = new Feature({ geometry: new Point(lineArr[0]) }); const icon = new Icon({ crossOrigin: 'anonymous', src: carImg, width: 13, height: 26, anchor: [0.5, 0.5], rotation: angle }); this.carFeature.setStyle( new Style({ image: icon }) ); this.traceFeature = new Feature({ geometry: new LineString(lineArr) }); let route = new LineString(lineArr); this.carLayer = new VectorLayer({ source: source, style: new Style({ stroke: new Stroke({ color: '#AF5', width: 5 }) }) }); this.carLayer.getSource().addFeatures([this.carFeature, this.traceFeature]); const move = (e) => { const time = e.frameState.time; // 时间戳差(毫秒) const elapsedTime = time - lastTime; // 距离(其实是比例的概念) distance = distance + (speed * elapsedTime) / 1e6; if (distance >= 1) { distance = 0; animationFlag = false; stopAnimation(); return; } // 保存当前时间 lastTime = time; // 上次坐标 const lastCoord = this.carFeature.getGeometry().getCoordinates(); // 获取新位置的坐标点 const curCoord = route.getCoordinateAt(distance); // 设置新坐标 this.carFeature.getGeometry().setCoordinates(curCoord); this.map.getView().setCenter(curCoord); // 设置角度 this.carFeature.getStyle().getImage().setRotation(getAngle(lastCoord, curCoord)); // 调用地图渲染 this.map.render(); }; const stopAnimation = () => { this.carLayer.un('postrender', move); }; this.map.addLayer(this.carLayer); this.carLayer.on('postrender', move); // 触发地图渲染 const geo = this.carFeature.getGeometry().clone(); this.carFeature.setGeometry(geo); return [this.carLayer]; } drawData(data) { const res = []; data.forEach((item) => { let graphic; if (['rectangle', 'polygon', 'anyLine'].includes(item.type)) { graphic = this.createPolygon(item); graphic.set('id', item.id); res.push(graphic); } else if (item.type === 'circle') { graphic = this.createCircle(item); graphic.set('id', item.id); res.push(graphic); } else if (item.type === 'straightLine') { graphic = this.createLineString(item); graphic.set('id', item.id); res.push(graphic); } else if (item.type === 'text') { const { text } = this.addText(item); res.push(text); } else if (item.type === 'measureArea') { graphic = this.createPolygon(item); graphic.set('id', item.id); const style = new Style({ stroke: new Stroke({ color: rgbToRgba(item.strokeColor, item.strokeOpacity), width: item.strokeWeight }), fill: new Fill({ color: rgbToRgba(item.fillColor, item.fillOpacity) }), text: new Text({ text: '区域面积' + item.area.toFixed(2) + '平方米', font: '14px Calibri,sans-serif', fill: new Fill({ color: '#000' }), stroke: new Stroke({ color: '#fff', width: 3 }), overflow: true }) }); graphic.setStyle(style); res.push(graphic); } else if (item.type === 'marker') { // 创建标注点 const marker = new Feature({ // 必须是数字类型,字符串不识别 geometry: new Point([item.longitude, item.latitude]), // name: item.name, // icon: item.icon, // imageHover: item.imageHover, // size: item.size, pointer: true }); marker.set('id', item.id); const img = new Image(); img.onload = () => { // 图片加载完成后,可以访问其 width 和 height 属性 const width = img.width; const height = img.height; const style = new Style({ image: new Icon({ anchor: [0.5, 1], src: item.icon, size: [width, height], scale: !!item.size[0] ? item.size[0] / width : 1 }), text: new Text({ text: item.name, fill: new Fill({ color: '#000' }), stroke: new Stroke({ color: '#fff', width: 3 }) }) }); marker.setStyle(style); }; img.src = item.icon; // 设置图片的 URL,触发加载 this.drawVector.getSource().addFeature(marker); res.push(marker); } }); return res; } getVectorLayer() { return this.vectorLayer; } getDrawVector() { return this.drawVector; } getMap() { return this.map; } getMouseTool() { return this.drawTool; } setDrawEndMethod(newMethod) { this.drawEndMethod = newMethod; } }