From 1df71bdd7fc5b35be1447c9cc574bf610666f436 Mon Sep 17 00:00:00 2001 From: wujingjing <gersonwu@qq.com> Date: 星期一, 14 四月 2025 19:13:11 +0800 Subject: [PATCH] SERVE_URL --- src/model/map/OLMap.ts | 1377 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 1,341 insertions(+), 36 deletions(-) diff --git a/src/model/map/OLMap.ts b/src/model/map/OLMap.ts index d972754..1666f55 100644 --- a/src/model/map/OLMap.ts +++ b/src/model/map/OLMap.ts @@ -1,31 +1,62 @@ +import axios from 'axios'; import { defaultsDeep } from 'lodash-es'; -import { Map as OpenLayerMap, Overlay, View } from 'ol'; +import type { Overlay } from 'ol'; +import { Feature, Map as OpenLayerMap, View } from 'ol'; import type { Extent } from 'ol/extent'; -import { getTopLeft, getWidth } from 'ol/extent'; +import { extend, getCenter, getTopLeft, getWidth } from 'ol/extent'; +import type { FeatureLike } from 'ol/Feature'; +import MVT from 'ol/format/MVT.js'; +import WKT from 'ol/format/WKT'; +import { Point } from 'ol/geom'; +import type Geometry from 'ol/geom/Geometry'; +import { Draw, defaults as olDefaults } from 'ol/interaction'; +import { createBox } from 'ol/interaction/Draw'; +import type Layer from 'ol/layer/Layer'; import Tile from 'ol/layer/Tile'; +import VectorLayer from 'ol/layer/Vector'; +import VectorTileLayer from 'ol/layer/VectorTile'; import { fromLonLat, get as getProjection } from 'ol/proj'; +import type LayerRenderer from 'ol/renderer/Layer'; +import type { Source } from 'ol/source'; import { XYZ } from 'ol/source'; +import VectorSource from 'ol/source/Vector'; +import VectorTileSource from 'ol/source/VectorTile'; import WMTS from 'ol/source/WMTS'; +import { Circle, Fill, Stroke, Style, Text } from 'ol/style'; import WMTSTileGrid from 'ol/tilegrid/WMTS'; import type { ViewOptions } from 'ol/View'; +import type { Ref, ShallowRef } from 'vue'; +import { markRaw, ref } from 'vue'; +import { Logger } from '../logger/Logger'; +import { MarkerOverlay } from './overlay/marker'; +import { getCurrentPosition } from '/@/utils/brower'; +import { travelTree } from '/@/utils/util'; +import { ElLoadingService } from 'element-plus'; +import { switchMapTheme } from '/@/api/map'; +import { formatDate } from '/@/utils/formatTime'; export type LangType = 'zh_cn' | 'en'; export const enum GaoDeSourceType { /** @description 榛樿鍦板浘 */ - Default = 0, + Default = 'default', /** @description 褰卞儚鍦板浘 */ - Satellite = 1, + Satellite = 'satellite', /** @description 鐭㈤噺鍦板浘 */ - Vector = 2, + Vector = 'standard', /** @description 褰卞儚璺綉 */ - SatelliteRoad = 3, + SatelliteRoad = 'road_network', } +export const enum OverlayType { + Marker = 'marker', +} +export const MARKER_OVERLAY_CLASS_NAME = 'marker-overlay'; -export const GaoDeSourceTypeMap = { - [GaoDeSourceType.Default]: '榛樿鍦板浘', - [GaoDeSourceType.Satellite]: '褰卞儚鍦板浘', - [GaoDeSourceType.Vector]: '鐭㈤噺鍦板浘', - [GaoDeSourceType.SatelliteRoad]: '褰卞儚璺綉', +export const gaoDeSourceTypeMap = { + // [GaoDeSourceType.Default]: '榛樿鍦板浘', + [GaoDeSourceType.Vector]: '鏍囧噯鍦板浘', + + [GaoDeSourceType.Satellite]: '鍗槦鍦板浘', + [GaoDeSourceType.SatelliteRoad]: '璺綉鍦板浘', }; export const getGaoDeSourceUrl = (type: GaoDeSourceType, lang: LangType = 'zh_cn') => { @@ -37,20 +68,46 @@ }; return urlMap[type]; }; +type MapConfig = { + sourceType: GaoDeSourceType; +}; -type OLEventType = 'blackClick'; +type OLEventType = 'blackClick' | 'featureChange' | 'featureHoverChange'; type OLMapOptions = { container: HTMLDivElement; view?: ViewOptions; -}; +} & MapConfig; + +const resolutions = []; +for (let i = 0; i <= 8; ++i) { + resolutions.push(156543.03392804097 / Math.pow(2, i * 2)); +} export class OLMap { map: OpenLayerMap; source: XYZ; private eventMap: Map<OLEventType, Function[]>; + /** @description 鎺掗櫎鏈仛鐨刲ayer锛屽悗缁叏閮ㄥ仛濂斤紝鍙互涓嶅仛鍒ゆ柇 */ + unsupportedLayers = []; + /** @description 鍥惧眰鎺у埗淇℃伅 */ + layerInfo = ref([] as any[]); + + /** @description 鍥惧眰鎺у埗淇℃伅 */ + legendList = ref([] as any[]); + + drawStyles = ref([] as any[]); + + /** @description 涓婚淇℃伅 */ + themeInfo = ref([] as any[]); + + activeSourceType: Ref<GaoDeSourceType> = ref(GaoDeSourceType.Vector); + interactLayer: VectorLayer<VectorSource<Feature<Geometry>>, Feature<Geometry>> = null; + searchHighlightLayer: VectorLayer<VectorSource<Feature<Geometry>>, Feature<Geometry>> = null; + /** @description 褰撳墠婵�娲荤姸鎬� feature */ + activeFeature: ShallowRef<FeatureLike> = ref(null); private emit(eventName: OLEventType, ...args: any[]) { - const handlers = this.eventMap.get(eventName); + const handlers = this.eventMap?.get(eventName); if (handlers) { handlers.forEach((handler) => handler(...args)); } @@ -79,11 +136,12 @@ } constructor(options: OLMapOptions) { - const { container, view } = defaultsDeep(options, { + const { container, view, sourceType } = defaultsDeep(options, { view: { center: [13247019.404399557, 4721671.572580107], zoom: 8, }, + sourceType: GaoDeSourceType.Vector, } as OLMapOptions) as OLMapOptions; this.source = new XYZ({ crossOrigin: 'anonymous', @@ -101,56 +159,540 @@ target: container, // 缁戝畾 DOM 瀹瑰櫒 layers: [layer], // 娣诲姞鍥惧眰 view: new View(view), + interactions: olDefaults({ doubleClickZoom: false }), }); - this.setSourceUrl(); + + this.activeSourceType.value = sourceType; + this.applySourceType(this.activeSourceType.value); this.listenMapClick(); + this.addBasicControl(); + this.initEvent(); } - setSourceUrl(type: GaoDeSourceType = GaoDeSourceType.Vector) { + gaodeApiKey = 'eea6302da02576fe3f3dd3ba1fbe0a04'; + + locationLayer: VectorLayer<VectorSource<Feature<Geometry>>, Feature<Geometry>> = null; + centerLayer: VectorLayer<VectorSource<Feature<Geometry>>, Feature<Geometry>> = null; + + async locationCurrent() { + const position = await getCurrentPosition(); + if (!position) return; + const { latitude, longitude } = position; + + this.map.getView().setCenter(fromLonLat([longitude, latitude])); + this.map.getView().setZoom(15); + // 绉婚櫎鏃у浘灞� + this.map.removeLayer(this.locationLayer); + this.map.removeLayer(this.centerLayer); + // 鍒涘缓瀹氫綅鐐瑰浘灞� + this.locationLayer = new VectorLayer({ + source: new VectorSource({ + features: [ + new Feature({ + geometry: new Point(fromLonLat([longitude, latitude])), + }), + ], + }), + style: new Style({ + // 澶栧渾 + image: new Circle({ + radius: 20, + fill: new Fill({ + color: 'rgba(24, 144, 255, 0.1)', + }), + stroke: new Stroke({ + color: 'rgba(24, 144, 255, 0.3)', + width: 2, + }), + }), + }), + minZoom: 12, + }); + + // 鍒涘缓涓績鐐瑰浘灞� + this.centerLayer = new VectorLayer({ + source: new VectorSource({ + features: [ + new Feature({ + geometry: new Point(fromLonLat([longitude, latitude])), + }), + ], + }), + style: new Style({ + // 鍐呭渾 + image: new Circle({ + radius: 6, + fill: new Fill({ + color: '#1890ff', + }), + stroke: new Stroke({ + color: '#fff', + width: 2, + }), + }), + }), + }); + + // 娣诲姞鏂板浘灞� + this.map.addLayer(this.locationLayer); + this.map.addLayer(this.centerLayer); + return position; + } + + async gaodeAddressSearch(address, city) { + this.gaodeApiKey = 'eea6302da02576fe3f3dd3ba1fbe0a04'; + // const url = `https://restapi.amap.com/v3/assistant/inputtips?output=json&city=010&keywords=${address}=${this.gaodeApiKey}`; + // 鎼滅储 POI + const url = `https://restapi.amap.com/v3/place/text?keywords=${address}&city=${city}&offset=20&page=1&key=${this.gaodeApiKey}&extensions=all `; + // 鎼滅储 POI 2.0 + // const url = `https://restapi.amap.com/v5/place/text?keywords=${address}&city=beijing&offset=20&page=1&key=${this.gaodeApiKey}&extensions=all + + const response = await axios.get(url); + return response.data; + } + + readWKT(wktStr) { + const srid = wktStr.match(/SRID=(\d+);/)?.[1]; + const standardWktStr = wktStr.replace(/SRID=\d+;/, ''); + const feature = new WKT().readFeature(standardWktStr, { + dataProjection: `EPSG:${srid}`, + }); + return feature; + } + drawLayer: VectorLayer<VectorSource<Feature<Geometry>>, Feature<Geometry>> = null; + private isDrawStatus = false; + + removeDrawLayer() { + if (!this.drawLayer) return; + this.map.removeLayer(this.drawLayer); + this.drawLayer = null; + } + + drawPolygon() { + if (this.isDrawStatus) return; + this.isDrawStatus = true; + return new Promise((resolve) => { + // 鍒涘缓缁樺埗浜や簰 + const draw = new Draw({ + type: 'Polygon', + }); + + // 娣诲姞缁樺埗浜や簰鍒板湴鍥� + this.map.addInteraction(draw); + + // 鐩戝惉缁樺埗瀹屾垚浜嬩欢 + draw.on('drawend', (event) => { + const feature = event.feature; + const geometry = feature.getGeometry(); + const extent = geometry.getExtent(); + this.drawLayer = new VectorLayer({ + source: new VectorSource({ + features: [feature], + }), + style: new Style({ + stroke: new Stroke({ + color: '#1677ff', + width: 2, + }), + fill: new Fill({ + color: 'rgba(22, 119, 255, 0.1)', + }), + }), + }); + + // 娣诲姞澶氳竟褰㈠浘灞傚埌鍦板浘 + this.map.addLayer(this.drawLayer); + + // 绉婚櫎缁樺埗浜や簰 + this.map.removeInteraction(draw); + setTimeout(() => { + this.isDrawStatus = false; + }, 300); + + // 杩斿洖鑼冨洿 + resolve(extent); + }); + }); + } + drawRectangle() { + if (this.isDrawStatus) return; + this.isDrawStatus = true; + return new Promise((resolve) => { + // 鍒涘缓缁樺埗浜や簰 + const draw = new Draw({ + type: 'Circle', + geometryFunction: createBox(), + }); + + // 娣诲姞缁樺埗浜や簰鍒板湴鍥� + this.map.addInteraction(draw); + + // 鐩戝惉缁樺埗瀹屾垚浜嬩欢 + draw.on('drawend', (event) => { + const feature = event.feature; + const geometry = feature.getGeometry(); + const extent = geometry.getExtent(); + this.drawLayer = new VectorLayer({ + source: new VectorSource({ + features: [feature], + }), + style: new Style({ + stroke: new Stroke({ + color: '#1677ff', + width: 2, + }), + fill: new Fill({ + color: 'rgba(22, 119, 255, 0.1)', + }), + }), + }); + // 娣诲姞鏂扮殑feature + // this.drawLayer.getSource().addFeature(feature); + + // 娣诲姞鐭╁舰鍥惧眰鍒板湴鍥� + this.map.addLayer(this.drawLayer); + + // 绉婚櫎缁樺埗浜や簰 + this.map.removeInteraction(draw); + setTimeout(() => { + this.isDrawStatus = false; + }, 300); + + // 杩斿洖鑼冨洿 + resolve(extent); + }); + }); + } + + clearObjectSearch() { + this.highlightSearch([]); + } + + displaySearchResult(searchItem) { + if (!searchItem?.WKT) return; + const feature = this.readWKT(searchItem.WKT); + this.highlightSearch(feature); + const geometry = feature.getGeometry(); + + this.map.getView().fit(geometry.getExtent(), { + // padding: [100, 100, 100, 100], + maxZoom: 15, + }); + + return feature; + } + + zoomToFeatures(features) { + if (features.length === 0) return; + // 鑾峰彇鎵�鏈� feature 鐨勬渶灏忓鎺ョ煩褰� + const extent = features.reduce((acc, feature) => { + const featureExtent = feature.getGeometry().getExtent(); + if (!acc) return featureExtent; + return extend(acc, featureExtent); + }, null); + // 璋冩暣瑙嗗浘浠ラ�傚簲鏈�灏忓鎺ョ煩褰� + this.map.getView().fit(extent, { + maxZoom: 15, + padding: [50, 50, 50, 50], // 娣诲姞鍐呰竟璺濅娇瑙嗗浘鏇寸編瑙� + }); + } + + updateSize() { + this.map.updateSize(); + } + + initEvent() { + const that = this; + this.map.on('moveend', function (e) { + const zoom = that.map.getView().getZoom(); //鑾峰彇褰撳墠鍦板浘鐨勭缉鏀剧骇鍒� + Logger.info('褰撳墠鍦板浘缂╂斁绾у埆涓猴細' + zoom); + }); + this.pointermove(); + } + + hightLightColor: '0000FFFF'; + sizeRate: 0; + + getPointHighLightStyle(feature) { + if (!feature) return null; + let pSize = feature.get('psize') ?? 15; + pSize += pSize * 2; + + const pcolor = '0000FFFF'; + const pstyle = feature.get('pstyle'); + return this.getPointStyles({ color: pcolor, size: pSize, style: pstyle }); + } + + getLineHighLightStyle(feature) { + if (!feature) return null; + + const lcolor = this.hightLightColor; + let lsize = feature.get('lsize') ?? 15; + lsize += lsize * this.sizeRate; + const lstyle = feature.get('lstyle'); + return this.getLineStyles({ lsize: lsize, lcolor: lcolor, lstyle: lstyle }); + } + + getPolygonHighLightStyle(feature) { + if (!feature) return null; + + const lcolor = this.hightLightColor; + // return this.getPolygonStyles({ pcolor: lcolor, lstyle: 'default', lsize: 2 }); + return new Style({ + stroke: new Stroke({ + color: lcolor, + width: 5, + }), + // fill: new Fill({ + // color: lcolor, + // }), + }); + } + + // 淇濆瓨鐐规牱寮忕紦瀛� + pointStyeMap: Record<string, Style> = {}; + + /** + * 鏍规嵁鐐圭殑閰嶇疆鑾峰彇鏍峰紡 + * @param config + * @returns + */ + getPointStyles(config: { size; color; style }) { + const pSize = config.size; + + const pcolor = config.color; + const pstyle = config.style; + + const pkey = `${pstyle}_${pSize}_${pcolor}`; + + if (!this.pointStyeMap[pkey]) { + const drawStyle = this.drawStyles.value.find((item) => item.id === pstyle + ''); + const drawStyleType = drawStyle?.type; + const config = drawStyle?.config; + + const drawStyleConfigType = drawStyle?.config?.type; + if (drawStyleType === 'POINT') { + if (drawStyleConfigType === 'circle') { + this.pointStyeMap[pkey] = new Style({ + image: new Circle({ + radius: pSize, + fill: new Fill({ color: `#${pcolor}` }), + }), + }); + } else if (drawStyleConfigType === 'font') { + this.pointStyeMap[pkey] = new Style({ + text: new Text({ + font: `${pSize}px "ywifont"`, + + text: config?.text ?? '', + fill: new Fill({ color: `#${pcolor}` }), + }), + }); + } else { + this.pointStyeMap[pkey] = new Style({ + image: new Circle({ + radius: pSize, + fill: new Fill({ color: `#${pcolor}` }), + }), + }); + } + } else { + this.pointStyeMap[pkey] = new Style({ + image: new Circle({ + radius: pSize, + fill: new Fill({ color: `#${pcolor}` }), + }), + }); + } + } + return this.pointStyeMap[pkey]; + } + + // 淇濆瓨绾挎牱寮忕紦瀛� + lineStyleMap: Record<string, Style> = {}; + + /** + * 鏍规嵁绾跨殑閰嶇疆鑾峰彇鏍峰紡 + * @param config + */ + getLineStyles(config: { lsize; lcolor; lstyle; psize?; pcolor?; pstyle? }) { + const lSize = config.lsize; + if (!lSize) return null; + const lColor = config.lcolor; + const lstyle = config.lstyle; + const styles = []; + const lKey = `${lstyle}_${lSize}_${lColor}`; + if (!this.lineStyleMap[lKey]) { + const drawStyle = this.drawStyles.value.find((item) => item.id === lstyle + ''); + const drawStyleType = drawStyle?.type; + const config = drawStyle?.config; + this.lineStyleMap[lKey] = new Style({ + // image: new Circle({ + // radius: lsize, + // fill: new Fill({ color: `#${color}` }), + // }), + stroke: new Stroke({ + color: `#${lColor}`, + width: lSize, + }), + }); + } + styles.push(this.lineStyleMap[lKey]); + + const hasPoint = Object.keys(config).some((key) => key === 'psize' && !!config[key]); + if (hasPoint) { + const psize = config.psize; + const pcolor = config.pcolor; + const pstyle = config.pstyle; + const pointStyle = this.getPointStyles({ size: psize, color: pcolor, style: pstyle }); + styles.push(pointStyle); + } + return styles; + } + polygonStyleMap: Record<string, Style> = {}; + getPolygonStyles(config: { pcolor; lstyle; lsize; lcolor }) { + const styles = []; + const pColor = config.pcolor; + const key = pColor; + if (!this.polygonStyleMap[key]) { + const polygonStyle = new Style({ + fill: new Fill({ color: `#${pColor}` }), + }); + styles.push(polygonStyle); + } + + const lineStyle = this.getLineStyles({ + lsize: config.lsize, + lcolor: config.lcolor, + lstyle: config.lstyle, + }); + lineStyle && styles.push(...lineStyle); + return styles; + } + + private maxTextResolution = 1; + getText(textContent, resolution) { + let text = `${textContent}`; + if (resolution > this.maxTextResolution) { + text = ''; + } + + // else if (type == 'hide') { + // text = ''; + // } else if (type == 'shorten') { + // text = text.trunc(12); + // } else if (type == 'wrap' && (!dom.placement || dom.placement.value != 'line')) { + // text = stringDivider(text, 16, '\n'); + // } + + return text; + } + + createTextStyle(textContent, resolution) { + return new Style({ + text: new Text({ + text: this.getText(textContent, resolution), + font: '14px Arial', + fill: new Fill({ color: '#000' }), + stroke: new Stroke({ color: '#fff', width: 2 }), + offsetX: 10, // 鏂囧瓧鐩稿浜庡嚑浣曚腑蹇冪殑姘村钩鍋忕Щ + offsetY: 20, // 鏂囧瓧鐩稿浜庡嚑浣曚腑蹇冪殑鍨傜洿鍋忕Щ + }), + }); + } + + // 淇濆瓨鏍囩鏍峰紡缂撳瓨 + labelStyleMap: Record<string, Style> = {}; + + /** + * 鏍规嵁鏍囩鐨勯厤缃幏鍙栨牱寮� + * @param config + */ + getLabelStyles(config: { textContent; resolution }) { + const textContent = config.textContent; + if (!textContent) return null; + const resolution = config.resolution; + if (!textContent) return null; + if (!this.labelStyleMap[textContent]) { + this.labelStyleMap[textContent] = this.createTextStyle(textContent, resolution); + } + return this.labelStyleMap[textContent]; + } + + setDrawStyles(styles: any[]) { + this.drawStyles.value = styles; + } + + applySourceType(type: GaoDeSourceType = GaoDeSourceType.Vector) { const url = getGaoDeSourceUrl(type); this.source.setUrl(url); + } + + setSourceType(type: GaoDeSourceType = GaoDeSourceType.Vector) { + this.activeSourceType.value = type; + this.applySourceType(type); } addMarkerLayer(dataList: any[], options: any) { const { markerOpt } = options; const markers: Overlay[] = []; // 鍒涘缓鏍囪鐐� - dataList.forEach((item) => { - const marker = this.createMarker(item, markerOpt); + dataList.forEach((item, index) => { + const marker = this.createMarker(`marker-${index}`, item, markerOpt); markers.push(marker); this.map.addOverlay(marker); }); // 璁$畻骞惰皟鏁磋鍥捐寖鍥� - this.adjustViewToMarkers(dataList); + this.adjustViewToOverlays(markers); } + checkEquipIsShow() { + for (const item of this.layerInfo.value) { + if (item.id === 'equip') { + return item.isVisible; + } + } + return false; + } + + getEquipOverlay() { + for (const item of this.layerInfo.value) { + if (item.type === 'equip') { + return item; + } + } + } createEleOverlay(dom: string | HTMLElement, position = [0, 0]) { const ele = typeof dom === 'string' ? (document.querySelector(dom) as HTMLElement) : dom; if (!ele) return; - const eleOverlay = new Overlay({ + const eleOverlay = new MarkerOverlay({ element: ele, position: position, positioning: 'top-left', stopEvent: false, className: 'z-[999]', }); - // // 鐢变簬 setVisible 鏄彈淇濇姢鐨勬柟娉�,鏀圭敤 set('visible', true) 鏉ヨ缃彲瑙佹�� - // eleOverlay.set('visible', true); + eleOverlay.setVisible(this.checkEquipIsShow()); + return eleOverlay; } - private createMarker(item: any, markerOpt: any): Overlay { + private createMarker(id: string, item: any, markerOpt: any): Overlay { // 鍒涘缓鍥剧墖鍏冪礌 const markerImg = document.createElement('img'); markerImg.src = markerOpt.icon.url; markerImg.style.width = `${markerOpt.icon.size}px`; markerImg.style.height = `${markerOpt.icon.size}px`; markerImg.style.cursor = 'pointer'; + markerImg.style.userSelect = 'none'; + const position = fromLonLat(item.position); // 鍒涘缓 Overlay - const overlay = new Overlay({ + const overlay = new MarkerOverlay({ + id, + className: MARKER_OVERLAY_CLASS_NAME, element: markerImg, position: position, positioning: 'center-center', @@ -158,28 +700,50 @@ }); overlay.set('extData', item.extData); + overlay.set('type', OverlayType.Marker); // 娣诲姞鐐瑰嚮浜嬩欢 markerImg.addEventListener('click', (event) => { + if (this.isDrawStatus) return; if (markerOpt.icon.selectUrl) { markerImg.src = markerOpt.icon.selectUrl; } - markerOpt.click?.(event, overlay, item, position); + markerOpt.click?.(event, overlay, item.extData, position); }); return overlay; } - private adjustViewToMarkers(dataList: any[]) { - // 璁$畻鎵�鏈夌偣鐨勮寖鍥� - const extent = dataList.reduce((ext, item) => { - const coord = fromLonLat(item.position); + getAllMarkerOverlays() { + const overlays = this.map.getOverlays().getArray(); + return overlays.filter((overlay) => overlay.get('type') === OverlayType.Marker); + } + + adjustViewToMarkers() { + const overlays = this.getAllMarkerOverlays(); + this.adjustViewToOverlays(overlays); + } + + getAllMarkerOverlaysExtent() { + const overlays = this.getAllMarkerOverlays(); + const extent = this.getOverlaysExtent(overlays); + return extent; + } + + getOverlaysExtent(overlays: Overlay[]) { + const extent = overlays.reduce<number[] | null>((ext, item) => { + const coord = item.getPosition(); + if (!ext) { return [coord[0], coord[1], coord[0], coord[1]]; } return [Math.min(ext[0], coord[0]), Math.min(ext[1], coord[1]), Math.max(ext[2], coord[0]), Math.max(ext[3], coord[1])]; }, null); + return extent; + } - // 璋冩暣瑙嗗浘浠ラ�傚簲鏍囪鐐硅寖鍥� + adjustViewToOverlays(overlays: Overlay[]) { + if (overlays.length === 0) return; + const extent = this.getOverlaysExtent(overlays); if (extent) { this.fitExtend(extent); } @@ -193,17 +757,758 @@ }); } /** - * 鐩戝惉鍦板浘绌虹櫧鍖哄煙鐐瑰嚮 + * 鐩戝惉鍦板浘鐐瑰嚮 */ listenMapClick() { this.map.on('click', (event) => { - const feature = this.map.forEachFeatureAtPixel(event.pixel, (feature) => feature); + if (this.isDrawStatus) return; + // const features = this.map.getFeaturesAtPixel(event.pixel); + const features = []; + const layers: Layer<Source, LayerRenderer<any>>[] = []; + this.map.forEachFeatureAtPixel(event.pixel, (feature, layer) => { + features.push(feature); + layers.push(layer); + }); + const feature = features[0]; + feature?.get('click')?.(event); - if (!feature) { - // overlay 鏃犳硶鍒ゆ柇 - this.emit('blackClick', event); + const layer = layers[0]; + if (layer === this.drawLayer) return; + this.labelOverlay?.setVisible(false); + + if (feature !== this.activeFeature.value) { + this.emit('featureChange', feature, layer); + } + if (feature) { + const otype = feature.get('otype'); + console.log('馃殌 ~ otype:', otype); + const oname = feature.get('oname'); + console.log('馃殌 ~ oname:', oname); + const geometryType = feature.getGeometry().getType(); + console.log('馃殌 ~ geometryType:', geometryType); + const properties = feature.getProperties(); + console.log('馃殌 ~ properties:', properties); + } + this.highlightSelect(feature); + this.activeFeature.value = feature; + }); + } + + setAllThemes(themeInfo) { + this.themeInfo.value = themeInfo; + } + + getAllLayers() { + const allLayers = this.layerInfo.value.reduce((preVal, curVal) => { + if (curVal.children && curVal.children.length > 0) { + return preVal.concat(curVal.children.map((item) => item)); + } else { + return preVal; + } + }, []); + return allLayers; + } + + zoomToLayers() { + let extent = this.getAllMarkerOverlaysExtent(); + const layers = this.getAllLayerModels(); + if (layers.length > 0) { + // 鑾峰彇鎵�鏈夊浘灞傜殑鑼冨洿 + extent = layers.reduce((extent, layer) => { + const layerExtent = layer.getExtent(); + + if (!extent) { + return layerExtent; + } + + // 鍚堝苟鑼冨洿 + return extend(extent, layerExtent); + }, extent); + } + + // 鑱氱劍鍒板悎骞跺悗鐨勮寖鍥� + this.map.getView().fit(extent, { + padding: [50, 50, 50, 50], + maxZoom: 15, + }); + } + + getAllLayerModels() { + return this.layerInfo.value.reduce((preVal, curVal) => { + if (curVal.children && curVal.children.length > 0) { + return preVal.concat(curVal.children.map((item) => item.model)); + } else { + return preVal; + } + }, []); + } + + setLayerVisible(layerId: string, visible: boolean) { + const layer = this.getAllLayers().find((item) => item.id === layerId); + if (layer) { + layer.isVisible = visible; + } + } + + setThemeById(themeId: string) { + let group; + let theme; + travelTree(this.themeInfo.value, (item, index, array, parent) => { + if (item.type === 'theme' && item.id === themeId) { + group = parent; + theme = item; + return true; } }); + if (group) { + group.activeTheme = themeId; + this.handleThemeChange(themeId, group); + } + } + + async changeTheme(ids: string) { + const loadingInstance = ElLoadingService({ + text: '鍔犺浇涓婚涓�...', + target: '.layout-parent', + }); + const res = await switchMapTheme({ + theme_id: ids, + time: formatDate(new Date()), + }).finally(() => { + loadingInstance.close(); + }); + + // 鍔犲叆涓婚涔嬪悗鐨勬牱寮忓嚱鏁� + const themeLayerStyleFunc = (feature, resolution) => { + const otype = feature.get('otype'); + const oname = feature.get('oname'); + // oname 鏄犲皠鏍峰紡 + const onameStyleMap = res?.[`O_${otype}`] ?? {}; + const themeStyles = res?.styles ?? []; + const styleIndex = onameStyleMap[oname]?.style_id; + const themeStyle = styleIndex == null ? null : themeStyles[styleIndex]; + // if(themeStyle){ + // } + const shape = feature.getGeometry().getType(); + const styles = []; + switch (shape) { + case 'Point': + const pSize = themeStyle?.PSIZE ?? feature.get('psize'); + + const pcolor = themeStyle?.PCOLOR ?? feature.get('pcolor'); + + const pStyle = themeStyle?.PSTYLE ?? feature.get('pstyle'); + + const pointStyle = this.getPointStyles({ + size: pSize, + color: pcolor, + style: pStyle, + }); + pointStyle && styles.push(pointStyle); + break; + case 'LineString': + const lSize = themeStyle?.LSIZE ?? feature.get('lsize'); + const lColor = themeStyle?.LCOLOR ?? feature.get('lcolor'); + const lStyle = themeStyle?.LSTYLE ?? feature.get('lstyle'); + + const pSize1 = themeStyle?.PSIZE ?? feature.get('psize'); + const pcolor1 = themeStyle?.PCOLOR ?? feature.get('pcolor'); + const pStyle1 = themeStyle?.PSTYLE ?? feature.get('pstyle'); + + const lineStyle = this.getLineStyles({ + lsize: lSize, + lcolor: lColor, + lstyle: lStyle, + psize: pSize1, + pcolor: pcolor1, + pstyle: pStyle1, + }); + lineStyle && styles.push(...lineStyle); + + break; + case 'Polygon': + const pColor = themeStyle?.PCOLOR ?? feature.get('pcolor'); + const lSize1 = themeStyle?.LSIZE ?? feature.get('lsize'); + const lColor1 = themeStyle?.LCOLOR ?? feature.get('lcolor'); + const lstyle1 = themeStyle?.LSTYLE ?? feature.get('lstyle'); + const polygonStyle = this.getPolygonStyles({ + pcolor: pColor, + lsize: lSize1, + lcolor: lColor1, + lstyle: lstyle1, + }); + polygonStyle && styles.push(...polygonStyle); + + break; + default: + break; + } + + // switch (otype) { + // case 'WDM_JUNCTIONS': + // const textStyle = createTextStyle(feature, resolution); + // styles.push(textStyle); + + // case 'WDM_PIPES': + // break; + // } + //#region ====================== 娣诲姞鏍囨敞 ====================== + const tString = themeStyle?.TSTRING ?? feature.get('tstring'); + const tStringStyle = this.getLabelStyles({ + textContent: tString, + resolution, + }); + tStringStyle && styles.push(tStringStyle); + //#endregion + + return styles; + }; + const allLayers = this.getAllLayers(); + for (const item of allLayers) { + if (this.unsupportedLayers.includes(item.id)) { + continue; + } + item.model.setStyle(themeLayerStyleFunc); + } + return res?.legends ?? []; + } + + async handleThemeChange(val?, themeGroup?) { + const allIds = []; + for (const item of this.themeInfo.value) { + if (item.activeTheme) { + allIds.push(item.activeTheme); + } + } + const ids = allIds.join(','); + + if (!ids) { + const allLayers = this.getAllLayerModels(); + for (const item of allLayers) { + const originStyle = item.get('originStyle'); + originStyle && item.setStyle(originStyle); + } + return; + } + const legends = await this.changeTheme(ids); + this.legendList.value = this.parseLegends(legends); + } + + parseLegends(legends) { + const result = legends.map((item) => { + const isEqual = item.operate === '='; + const result = { + ...item, + legend: item.legend.map((legendItem, index, array) => { + const preItem = array[index - 1]; + let label = ''; + if (isEqual) { + label = legendItem.value; + } else if (preItem) { + label = `>${preItem.value}涓�${item.operate}${legendItem.value}`; + } else { + label = `${item.operate}${legendItem.value}`; + } + return { + ...legendItem, + label: label, + }; + }), + }; + + result.legend.push({ + style: item.default, + value: '', + label: result.legend.length == 0 ? '榛樿' : `>${item.legend[item.legend.length - 1].value}`, + }); + return result; + }); + return result; + } + + /** @description 璁板綍鎵�鏈夊浘灞� */ + setAllLayers(layerModels: Layer[], layers: any[], layerGroup: any[]) { + this.layerInfo.value = layerGroup.reduce((preVal, curVal) => { + const group = curVal.group; + const groupId = `group-${group}`; + let mapGroupItem = preVal.find((item) => item.id === groupId); + if (!mapGroupItem) { + mapGroupItem = { id: groupId, title: group, type: 'layer-group' }; + preVal.push(mapGroupItem); + } + if (!mapGroupItem.children) { + mapGroupItem.children = []; + } + const layerId = curVal.layer_id; + const foundIndex = layers.findIndex((item) => item.id === layerId); + if (foundIndex === -1) { + return preVal; + } + const layer = layerModels[foundIndex]; + const layerData = layers[foundIndex]; + //#region ====================== 璁剧疆缂╂斁绾у埆 ====================== + if (layerData?.max_ratio !== null) { + layer.setMaxZoom(layerData.max_ratio); + } + + if (layerData?.min_ratio !== null) { + layer.setMinZoom(layerData.min_ratio); + } + //#endregion + + const data = { + id: layerData.id, + title: layerData.title, + icon: layerData.icon, + model: markRaw(layer), + get isVisible() { + return layer.getVisible(); + }, + set isVisible(val) { + layer.setVisible(val); + }, + type: 'layer', + }; + mapGroupItem.children.push(data); + return preVal; + }, []); + const that = this; + this.layerInfo.value.push({ + id: 'equip', + title: '鐩戞祴璁惧', + children: [], + _isVisible: true, + get isVisible() { + return this._isVisible; + }, + set isVisible(val) { + this._isVisible = val; + that.toggleMarkerOverlayVisible(val); + }, + type: 'equip', + }); + } + + /** + * + * 鐩戝惉鍦板浘瑕佺礌hover + */ + listenMapFeatureHover() { + let activeHover: FeatureLike = null; + this.map.on('pointermove', (event) => { + const features = this.map.getFeaturesAtPixel(event.pixel); + const feature = features[0]; + if (feature !== activeHover) { + this.emit('featureHoverChange', feature); + } + activeHover = feature; + }); + } + + toggleMarkerOverlayVisible(visible: boolean) { + const overlays = this.map.getOverlays(); + overlays.forEach((overlay) => { + if (overlay instanceof MarkerOverlay) { + const overlayElement = overlay.getElement(); + if (overlayElement) { + overlayElement.style.visibility = visible ? 'visible' : 'hidden'; + } + } + }); + } + + getConfig(): MapConfig { + return { + sourceType: this.activeSourceType.value, + }; + } + + setConfig(config: MapConfig) { + this.activeSourceType.value = config.sourceType; + this.applySourceType(this.activeSourceType.value); + } + + private addBasicControl() { + // this.map.addControl(new ZoomSlider()); + // this.map.addControl(new FullScreen()); + const container = this.map.getViewport(); + if (!container) return; + const olZoom = container.querySelector('.ol-zoom') as HTMLElement; + if (!olZoom) return; + olZoom.style.display = 'none'; + } + + /** + * 鏀惧ぇ鍦板浘 + */ + zoomIn(offsetLevel = 1) { + const view = this.map.getView(); + const zoom = view.getZoom(); + view.setZoom(zoom + offsetLevel); + } + + /** + * 缂╁皬鍦板浘 + */ + zoomOut(offsetLevel = 1) { + const view = this.map.getView(); + const zoom = view.getZoom(); + view.setZoom(zoom - offsetLevel); + } + + /** + * 鍦板浘宸︾Щ + */ + panLeft(offset = 1000) { + const view = this.map.getView(); + const center = view.getCenter(); + if (!center) return; + view.setCenter([center[0] - offset, center[1]]); + } + + /** + * 鍦板浘鍙崇Щ + */ + panRight(offset = 1000) { + const view = this.map.getView(); + const center = view.getCenter(); + if (!center) return; + view.setCenter([center[0] + offset, center[1]]); + } + + /** + * 鍦板浘涓婄Щ + */ + panUp(offset = 1000) { + const view = this.map.getView(); + const center = view.getCenter(); + if (!center) return; + view.setCenter([center[0], center[1] + offset]); + } + + /** + * 鍦板浘涓嬬Щ + */ + panDown(offset = 1000) { + const view = this.map.getView(); + const center = view.getCenter(); + if (!center) return; + view.setCenter([center[0], center[1] - offset]); + } + + private tileUrlFunction(url) { + return (tileCoord) => + url + .replace('{z}', String(tileCoord[0] * 2 - 1)) + .replace('{x}', String(tileCoord[1])) + .replace('{y}', String(tileCoord[2])) + .replace('{a-d}', 'abcd'.substr(((tileCoord[1] << tileCoord[0]) + tileCoord[2]) % 4, 1)); + } + + addCustomLayer(url: string, style?: any) { + const vectorTileExtent = [13270414.528705932, 2994644.904997596, 13295641.139349712, 3018305.0256410106]; + + const vectorTileLayer = new VectorTileLayer({ + source: new VectorTileSource({ + format: new MVT(), + url: url, + // minZoom:5, + // maxZoom:2 + // minZoom:12 + }), + extent: vectorTileExtent, + style: style, + }); + this.map.addLayer(vectorTileLayer); + return vectorTileLayer; + } + highlightFeature(feature) { + const highlightStyle = { + // Point: new Style({ + // // image: new Circle({ + // // radius: 5, + // // fill: new Fill({ color: `blue` }), + // // }), + // image: new Circle({ + // radius: 13, + // stroke: new Stroke({ + // color: `blue`, + // width: 3, + // }), + // }), + // }), + // Point:this.getPointHighLightStyle.call(this,feature), + Point: new Style({ + // image: new Circle({ + // radius: 5, + // fill: new Fill({ color: `blue` }), + // }), + image: new Circle({ + radius: 13, + stroke: new Stroke({ + color: `blue`, + width: 3, + }), + }), + }), + LineString: new Style({ + stroke: new Stroke({ + color: `blue`, + width: 5, + }), + }), + Polygon: new Style({ + stroke: new Stroke({ + color: `blue`, + width: 5, + }), + // fill: new Fill({ + // color: `rgba(0, 0, 255, 0.1)`, + // }), + }), + }; + // 鍒涘缓楂樹寒鍥惧眰 + if (!this.interactLayer) { + this.interactLayer = new VectorLayer({ + source: new VectorSource(), + map: this.map, + style: (feature) => { + const type = feature.getGeometry().getType(); + return highlightStyle[type]; + }, + }); + } + } + + private searchHighlightStyle = { + // Point: new Style({ + // // image: new Circle({ + // // radius: 5, + // // fill: new Fill({ color: `blue` }), + // // }), + // image: new Circle({ + // radius: 13, + // stroke: new Stroke({ + // color: `blue`, + // width: 3, + // }), + // }), + // }), + // Point:this.getPointHighLightStyle.call(this,feature), + Point: new Style({ + // image: new Circle({ + // radius: 5, + // fill: new Fill({ color: `blue` }), + // }), + image: new Circle({ + radius: 13, + stroke: new Stroke({ + color: `yellow`, + width: 3, + }), + }), + }), + LineString: new Style({ + stroke: new Stroke({ + color: `yellow`, + width: 5, + }), + }), + Polygon: new Style({ + stroke: new Stroke({ + color: `yellow`, + width: 5, + }), + // fill: new Fill({ + // color: `rgba(0, 0, 255, 0.1)`, + // }), + }), + }; + + private selectHighlightStyle = { + // Point: new Style({ + // // image: new Circle({ + // // radius: 5, + // // fill: new Fill({ color: `blue` }), + // // }), + // image: new Circle({ + // radius: 13, + // stroke: new Stroke({ + // color: `blue`, + // width: 3, + // }), + // }), + // }), + // Point:this.getPointHighLightStyle.call(this,feature), + Point: new Style({ + // image: new Circle({ + // radius: 5, + // fill: new Fill({ color: `blue` }), + // }), + image: new Circle({ + radius: 13, + stroke: new Stroke({ + color: `blue`, + width: 3, + }), + }), + }), + LineString: new Style({ + stroke: new Stroke({ + color: `blue`, + width: 5, + }), + }), + Polygon: new Style({ + stroke: new Stroke({ + color: `blue`, + width: 5, + }), + // fill: new Fill({ + // color: `rgba(0, 0, 255, 0.1)`, + // }), + }), + }; + + highlightSelect(feature) { + // 鍒涘缓楂樹寒鍥惧眰 + if (!this.interactLayer) { + this.interactLayer = new VectorLayer({ + source: new VectorSource(), + map: this.map, + style: (feature) => { + const type = feature.getGeometry().getType(); + return this.selectHighlightStyle[type]; + }, + }); + } + + // 璁剧疆楂樹寒瑕佺礌 + if (feature !== this.activeFeature.value) { + if (this.activeFeature.value) { + console.log('remove feature:', this.activeFeature.value); + this.interactLayer.getSource().removeFeature(this.activeFeature.value as any); + } + if (feature) { + this.interactLayer.getSource().addFeature(feature); + } + // this.highlightFeature = feature; + } + } + + private labelOverlay: MarkerOverlay = null; + private initLabelOverlay() { + if (this.labelOverlay) return; + // 鍒涘缓涓�涓� div 鍏冪礌鐢ㄤ簬鏄剧ず鏍囩 + const labelElement = document.createElement('div'); + labelElement.className = 'ol-label-overlay'; + labelElement.style.position = 'relative'; + labelElement.style.background = 'rgba(0,0,0,0.6)'; + labelElement.style.borderRadius = '4px'; + labelElement.style.color = '#fff'; + labelElement.style.padding = '4px 8px'; + labelElement.style.whiteSpace = 'nowrap'; + labelElement.style.fontSize = '12px'; + labelElement.style.transform = 'translate(-50%, -100%)'; + labelElement.style.marginBottom = '8px'; + + // 鍒涘缓 overlay + this.labelOverlay = new MarkerOverlay({ + element: labelElement, + offset: [0, 0], + positioning: 'bottom-center', + }); + + // 娣诲姞鍒板湴鍥� + this.map.addOverlay(this.labelOverlay); + } + private preSearchHighlightFeatures: Feature[] = []; + highlightSearch(features) { + if (!Array.isArray(features)) { + features = [features]; + } + + const positionFeatures = features.map((feature, index, array) => { + const geometry = feature.getGeometry(); + const center = geometry.getExtent ? getCenter(geometry.getExtent()) : geometry.getCoordinates(); + + // 鍒涘缓涓�涓柊鐨凢eature鐢ㄤ簬鏄剧ず瀛椾綋鍥炬爣 + const iconFeature = new Feature({ + geometry: new Point(center), + }); + + // 璁剧疆瀛椾綋鍥炬爣鏍峰紡 + // 娣诲姞鐐瑰嚮浜嬩欢鐩戝惉 + // iconFeature.set('click', (event) => { + // // 鍒濆鍖� labelOverlay + // this.initLabelOverlay(); + // // 鑾峰彇鐐瑰嚮浣嶇疆鐨勫潗鏍� + // const coordinate = event.coordinate; + // // 璁剧疆 labelOverlay 鐨勫唴瀹瑰拰浣嶇疆 + // const element = this.labelOverlay.getElement(); + // element.innerHTML = `绗�${index + 1}涓綅缃甡; + // this.labelOverlay.setPosition(coordinate); + // }); + + iconFeature.setStyle([ + new Style({ + text: new Text({ + text: '\ue642', // 浣跨敤瀛椾綋鍥炬爣鐨剈nicode缂栫爜 + font: '24px "ywifont"', // 璁剧疆瀛椾綋澶у皬鍜屽瓧浣撳悕绉� + fill: new Fill({ + color: '#1677ff', + }), + offsetY: -20, // 璋冩暣鍥炬爣浣嶇疆 + }), + }), + new Style({ + text: new Text({ + text: array.length === 1 ? '' : index + 1, // 娣诲姞鏁板瓧 + font: '14px Arial', // 璁剧疆鏁板瓧瀛椾綋 + fill: new Fill({ + color: '#ffffff', // 鏁板瓧棰滆壊璁句负鐧借壊 + }), + offsetY: -20, // 涓庡浘鏍囦繚鎸佺浉鍚岀殑鍋忕Щ + }), + }), + ]); + + return iconFeature; + }); + // 鍒涘缓楂樹寒鍥惧眰 + if (!this.searchHighlightLayer) { + this.searchHighlightLayer = new VectorLayer({ + source: new VectorSource(), + map: this.map, + style: (feature) => { + const type = feature.getGeometry().getType(); + return this.searchHighlightStyle[type]; + }, + }); + } + if (this.preSearchHighlightFeatures && this.preSearchHighlightFeatures.length > 0) { + this.searchHighlightLayer.getSource().removeFeatures(this.preSearchHighlightFeatures); + } + + // 璁剧疆楂樹寒瑕佺礌 + if (positionFeatures && positionFeatures.length > 0) { + this.searchHighlightLayer.getSource().addFeatures(positionFeatures); + } + this.preSearchHighlightFeatures = positionFeatures; + } + + pointermove() { + this.map.on('pointermove', (e) => { + if (this.map.hasFeatureAtPixel(e.pixel)) { + this.map.getViewport().style.cursor = 'pointer'; + } else { + this.map.getViewport().style.cursor = 'inherit'; + } + }); + } + + clearMarker() { + this.clearObjectSearch(); } getWMTS = () => { -- Gitblit v1.9.3