// 引入OpenLayers的主模块 import Map from 'ol/Map'; import View from 'ol/View'; import TileState from 'ol/TileState'; 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, ScaleLine } from 'ol/control'; import Vector from 'ol/layer/Vector'; import SourceVector from 'ol/source/Vector'; import GeoJSON from 'ol/format/GeoJSON'; import axios from 'axios'; import { fromExtent } from 'ol/geom/Polygon'; import { LinearRing, LineString, Polygon } from 'ol/geom'; import { Graticule } from 'ol/layer'; 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'; import { globalHeaders } from '@/utils/request'; import { iconList } from '@/views/globalMap/data/mapData'; import gdJson from '@/assets/json/gd.json'; 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 drawVector; private drawTool; private vectorLayer; // 边界遮罩图层 private maskLayer; private maskLayer2; // 显示信息框 private clickMarker; private infoWindow; private select; // 车辆轨迹 private carLayer; private carFeature; private traceFeature; // 自定义绘制结束调用方法 private drawEndMethod; private drawing; private path; private anyLine; private scale; // 标绘图层 private plotLayers = {}; private addressMarker; private addPoints = []; 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.scale = new ScaleLine(); this.map.addControl(this.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) => { // 检查当前图层是否在 plotLayers 中 return Object.values(this.plotLayers).includes(layer); } }); this.map.addInteraction(this.select); // 监听Select交互的select事件 this.select.on('select', (event) => { const feature = event.selected[0]; // 获取被选中的要素集合 const extData = feature.get('extData'); if (!!feature) { if (this.clickMarker) { const selectData = this.clickMarker.get('extData'); this.clickMarker.setStyle( new Style({ image: new Icon({ src: selectData.image, scale: selectData.scale, anchor: [0.5, 0.5], anchorXUnits: 'fraction', anchorYUnits: 'fraction' }) }) ); } if (['1', '2'].includes(extData.type)) { // 多点位 单点 if (this.clickMarker !== feature) { this.clickMarker = feature; feature.setStyle( new Style({ image: new Icon({ src: extData.imageHover, scale: extData.scale, anchor: [0.5, 0.5], anchorXUnits: 'fraction', anchorYUnits: 'fraction' }) }) ); options.onMarkerClick(extData); } } else if (extData.type === '3') { // 聚合要素 // this.select.getFeatures().clear(); const currentZoom = this.map.getView().getZoom(); this.map.getView().setZoom(currentZoom + 1); this.map.getView().setCenter([Number(extData.longitude), Number(extData.latitude)]); event.selected = []; } } }); } async initLayer(options) { // 添加新的图层 if (Array.isArray(options.id)) { for (const layer of options.id) { if (layer.layerType === 'JSON') { await this.createJsonLayer(layer); } else { await this.formatXml(layer); } } } else if (options.id === 'tianditu') { await this.formatXml2(); } else if (options.id) { await this.formatXml(options); } // 创建Vector层并添加到地图上 this.vectorLayer = new VectorLayer({ source: new VectorSource({ features: [] }), zIndex: options.zIndex ? options.zIndex : 100 }); 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(options) { const xml = new WMTSCapabilities(); return this.getCapabilities(options.code, options.layerType).then((res) => { if (options.layerType === 'WMTS') { const geoJson = xml.read(res.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.createWmtsLayer(options, layerParam); } else if (options.layerType === 'WFS') { const parser = new DOMParser(); const xmlDoc = parser.parseFromString(res.data, 'text/xml'); const featureType = xmlDoc.getElementsByTagName('FeatureType')[0]; const layerParam = { typeName: featureType.getElementsByTagName('Name')[0].textContent }; this.createWfsLayer(options, layerParam); } }); } // 请求接口获取地图信息 getCapabilities(code, service) { return axios.get(commonUrl + code + '?SERVICE=' + service + '&REQUEST=GetCapabilities', { headers: globalHeaders() }); } // 请求WMTS地图图片加载图层 createWmtsLayer(options, layerParam) { const source = new WMTS({ url: commonUrl + options.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'] }), tileLoadFunction: function (tile, src) { const xhr = new XMLHttpRequest(); xhr.open('GET', src); // 添加自定义 Headers const headers = globalHeaders(); xhr.setRequestHeader('Authorization', headers.Authorization); xhr.responseType = 'arraybuffer'; // 确保支持图片二进制数据 xhr.onload = function () { if (xhr.status === 200) { const type = xhr.getResponseHeader('Content-Type'); const blob = new Blob([xhr.response], { type: type }); const url = window.URL.createObjectURL(blob); tile.getImage().src = url; // 将图像数据绑定到瓦片 } else { tile.setState(TileState.ERROR); // 处理请求失败 } }; xhr.onerror = function () { tile.setState(TileState.ERROR); }; xhr.send(); } }); const layer = new TileLayer({ source: source, zIndex: options.zIndex, visible: options.visible }); layer.set('layerName', options.layer); layer.set('id', options.code); this.map.addLayer(layer); } // createWfsLayer(options, layerParam) { const source = new VectorSource({ format: new GeoJSON(), loader: function (extent, resolution, projection) { // 构建带认证头的请求参数 const url = `${commonUrl}${options.code}?` + `REQUEST=GetFeature` + `&typeName=${encodeURIComponent(layerParam.typeName)}` + `&outputFormat=application/json`; const headers = globalHeaders(); fetch(url, { headers: { 'Authorization': headers.Authorization } }) .then((response) => { if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); return response.json(); }) .then((json) => { const features = new GeoJSON().readFeatures(json, { dataProjection: layerParam.srsName, featureProjection: projection }); this.addFeatures(features); }) .catch(error => console.error('WFS加载失败:', error)); } }); const vectorLayer = new VectorLayer({ source: source, style: new Style({ // 必须设置样式才能显示 fill: new Fill({ color: 'rgba(0,0,0, 0)' }), stroke: new Stroke({ color: 'rgba(0,0,0, 1)', width: 1 }) }), zIndex: options.zIndex ? options.zIndex : -99 }); this.map.addLayer(vectorLayer); } // 加载json图层 createJsonLayer(layer) { return new Promise((resolve, reject) => { const geojsonParser = new GeoJSON(); console.log(this.map.getView().getProjection()); const features = geojsonParser.readFeatures(gdJson, { dataProjection: 'EPSG:4326', featureProjection: this.map.getView().getProjection() }); const jsonLayer = new VectorLayer({ source: new VectorSource({ features }), style: new Style({ // 必须设置样式才能显示 fill: new Fill({ color: '#ffffff' }), stroke: new Stroke({ color: 'rgba(0,0,0, 1)', width: 1 }) }), zIndex: layer.zIndex ? layer.zIndex : -99 }); this.map.addLayer(jsonLayer); resolve({}); }); } // 初始化绘画工具 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.drawOptions.fillOpacity = 0; this.drawAnyLine(this.drawOptions); } 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(drawOptions) { const 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.coordinate]; // 移除旧的线段 if (this.anyLine) { this.drawVector.getSource().removeFeature(this.anyLine); } }; const handleTouchMove = (e) => { if (!this.drawing) return; this.path.push(e.coordinate); // 移除旧的线段 if (this.anyLine) { this.drawVector.getSource().removeFeature(this.anyLine); } this.anyLine = new Feature({ geometry: new LineString(this.path) }); const lineStyle = new Style({ stroke: new Stroke({ color: hexToRgba(this.drawOptions.strokeColor, this.drawOptions.strokeOpacity), // 合并颜色与透明度 width: this.drawOptions.strokeWeight, lineJoin: 'round' // 线段连接处圆角 }) }); this.anyLine.setStyle(lineStyle); this.drawVector.getSource().addFeature(this.anyLine); }; const handleTouchEnd = () => { this.drawing = false; document.removeEventListener('touchend', handleTouchEnd); this.map.un('pointerdown', handleTouchStart); this.map.un('pointermove', handleTouchMove); document.removeEventListener('mouseup', handleTouchEnd); if (!!this.drawEndMethod) { this.drawEndMethod(drawOptions, this.anyLine); } this.anyLine = null; }; document.addEventListener('touchend', handleTouchEnd); this.map.on('pointerdown', handleTouchStart); this.map.on('pointermove', handleTouchMove); document.addEventListener('mouseup', handleTouchEnd); } // 关闭绘制 closeDraw() { if (!this.drawTool) return; this.map.removeInteraction(this.drawTool); // this.drawTool.abortDrawing(); } // 切换图层 async replaceLayers(newLayers, loadendFunc) { // 遍历当前的所有图层并移除它们 const layers = this.map.getLayers(); const layerArray = layers.getArray().slice(); layerArray.forEach((layer) => { // 标注现在都是用同一个暂不移除'annotation' if (!!layer && ['map'].includes(layer.get('layerName'))) { layer.getSource().clear(); layer.dispose(); this.map.removeLayer(layer); } }); if (Array.isArray(newLayers)) { for (const layer of newLayers) { await this.formatXml(layer); } } else if (newLayers.id === 'tianditu') { await this.formatXml2(); } else { await this.formatXml(newLayers); } 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' }) }) }) ]; } const pointSize = originalFeature.get('size'); const originalWidth = originalFeature.get('originalWidth'); const originalHeight = originalFeature.get('originalHeight'); const icon = originalFeature.get('icon'); const name = originalFeature.get('name'); const showName = originalFeature.get('showName'); const scale = !!pointSize[0] ? pointSize[0] / originalWidth : 1; const style = new Style({ geometry: originalFeature.getGeometry(), image: new Icon({ src: icon, scale: scale, anchor: [0.5, 0.5], anchorXUnits: 'fraction', anchorYUnits: 'fraction' }) }); if (!!showName) { style.setText( new Text({ text: name, font: '12px sans-serif', fill: new Fill({ color: '#000' }), stroke: new Stroke({ color: '#fff', width: 3 }), offsetY: (originalHeight / 2) * scale + 4 }) ); } return style; } // 添加搜索的标记的 addSearchMarker(item) { const view = this.map.getView(); view.setZoom(18); view.setCenter([Number(item.longitude), Number(item.latitude)]); // 获取到上一次的搜索标记并移除 const index = this.markers.findIndex((m) => { return m.dataType === 'search'; }); if (index > -1) { this.markers.splice(index, 1); } if (!this.plotLayers['search']) { this.plotLayers['search'] = new VectorLayer({ source: new VectorSource({ features: [] }) }); this.map.addLayer(this.plotLayers['search']); } else { this.plotLayers['search'].getSource().clear(); } this.addMarker2({ 'search': item }); this.clickMarker = item; this.options.onMarkerClick(item); } addMarker(points) { this.clearMarker(); const vectorSource = new VectorSource({ features: [] }); points.forEach((point) => { // 创建标注点 const feature = new Feature({ // 必须是数字类型,字符串不识别 geometry: new Point([Number(point.longitude), Number(point.latitude)]), name: point.name, icon: point.icon, image: point.icon, imageHover: point.imageHover, size: point.size, showName: point.showName, pointer: true, extData: point }); // 设置自定义属性 const img = new Image(); img.onload = () => { // 图片加载完成后,可以访问其 width 和 height 属性 const width = img.width; const height = img.width; feature.set('originalWidth', width); feature.set('originalHeight', height); feature.set('img', img); vectorSource.addFeature(feature); }; img.src = point.icon; // 设置图片的 URL,触发加载 this.markers.push(point); }); const clusterSource = new Cluster({ distance: 30, source: vectorSource }); this.vectorLayer.setStyle(this.clusterStyle); this.vectorLayer.setSource(clusterSource); } addMarker2(obj) { this.clearMarker2('point'); if (!!this.plotLayers['point']) { this.markers = []; } let hideInfoFlag = true; Object.keys(obj).forEach((key: string) => { const data = obj[key]; const name = key === 'search' ? 'search' : 'point'; if (this.clickMarker) { const extData = this.clickMarker.get('extData'); if (data.id === extData.id && data.dataType === extData.dataType) { hideInfoFlag = false; data.isHover = true; } } if (data.type === '3') { // 聚合点 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)' }) }); const feature = new Feature({ // 必须是数字类型,字符串不识别 geometry: new Point([Number(data.longitude), Number(data.latitude)]), name: data.name, pointer: true, extData: data }); feature.setStyle( new Style({ image: outerCircle, text: new Text({ text: data.count.toString(), font: '14px sans-serif', fill: new Fill({ color: '#ffff' }) }) }) ); this.plotLayers[name].getSource().addFeature(feature); this.markers.push(data); } else { // 单个点 const iconConfig = iconList[data.dataType] || (name === 'search' ? iconList.common2 : iconList.common); data.image = iconConfig.image; data.imageHover = iconConfig.imageHover; data.size = iconConfig.size; if (data.materia_name) { data.name = data.materia_name; } if (data.dataType === 43) { data.showName = true; } if (!data.id) { data.id = nanoid(8); } data.lnglat = [data.longitude, data.latitude]; const feature = new Feature({ // 必须是数字类型,字符串不识别 geometry: new Point([Number(data.longitude), Number(data.latitude)]), name: data.name, pointer: true, extData: data }); // 设置自定义属性 const img = new Image(); img.onload = () => { // 图片加载完成后,可以访问其 width 和 height 属性 const scale = data.size[0] ? data.size[0] / img.width : 1; data.scale = scale; feature.set('extData', data); feature.setStyle( new Style({ image: new Icon({ src: data.isHover ? data.imageHover : data.image, scale: scale, anchor: [0.5, 0.5], anchorXUnits: 'fraction', anchorYUnits: 'fraction' }) }) ); this.plotLayers[name].getSource().addFeature(feature); if (data.isHover) { this.clickMarker = feature; this.options.onMarkerClick(data); } }; img.src = data.image; // 设置图片的 URL,触发加载 this.markers.push(data); } }); if (hideInfoFlag) { this.clickMarker = null; this.hideInfo(true); } } // 清除所有标加 clearMarker() { if (!this.vectorLayer) return; this.vectorLayer.getSource().clear(); } clearMarker2(name) { // 新增图层 if (!this.plotLayers[name]) { this.plotLayers[name] = new VectorLayer({ source: new VectorSource({ features: [] }) }); this.map.addLayer(this.plotLayers[name]); } else { this.plotLayers[name].getSource().clear(); // this.clickMarker = null; // this.hideInfo(); } } // 设置灾害地点 单独图册 setAddress(data) { this.clearMarker2('address'); this.addressMarker = new Feature({ // 必须是数字类型,字符串不识别 geometry: new Point([Number(data.longitude), Number(data.latitude)]), name: data.name, pointer: true, extData: data }); this.addressMarker.setStyle( new Style({ image: new Icon({ src: data.image, scale: [0.288, 0.288], anchor: [0.5, 0.5], anchorXUnits: 'fraction', anchorYUnits: 'fraction' }) }) ); this.plotLayers['address'].getSource().addFeature(this.addressMarker); } showInfo(content, position, offsetY, isCustom) { this.hideInfo(); if (!this.infoWindow) { this.infoWindow = new Overlay({ element: content, positioning: 'bottom-center', // 你可以根据需要调整定位方式 offset: [0, offsetY ? offsetY : 0] // 偏移量,用于调整覆盖层相对于要素的位置 }); } this.infoWindow.setPosition(position); initDrag(this.infoWindow.element); this.map.addOverlay(this.infoWindow); } hideInfo(flag?: boolean) { this.map.removeOverlay(this.infoWindow); this.infoWindow = null; if (!!flag && this.clickMarker) { const selectData = this.clickMarker.get('extData'); this.clickMarker.setStyle( new Style({ image: new Icon({ src: selectData.image, scale: selectData.scale, anchor: [0.5, 0.5], anchorXUnits: 'fraction', anchorYUnits: 'fraction' }) }) ); this.clickMarker = null; } } /** * * @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 : -97 }); // // 合并区边界 // 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) }); const 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; } getScale() { return this.scale; } getMouseTool() { return this.drawTool; } setDrawEndMethod(newMethod) { this.drawEndMethod = newMethod; } }