From 6e2557b3ae3e1b43bc01a5122f5fd4aa9b83d755 Mon Sep 17 00:00:00 2001 From: gerson <1405270578@qq.com> Date: 星期日, 09 二月 2025 18:51:33 +0800 Subject: [PATCH] 整理绘制 --- src/model/map/OLMap.ts | 375 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 360 insertions(+), 15 deletions(-) diff --git a/src/model/map/OLMap.ts b/src/model/map/OLMap.ts index 7d2a0be..810b19e 100644 --- a/src/model/map/OLMap.ts +++ b/src/model/map/OLMap.ts @@ -1,19 +1,31 @@ import { defaultsDeep } from 'lodash-es'; -import type { Overlay } from 'ol'; +import type { Feature, Overlay } from 'ol'; import { Map as OpenLayerMap, View } from 'ol'; -import { ZoomSlider } from 'ol/control'; import type { Extent } from 'ol/extent'; import { getTopLeft, getWidth } from 'ol/extent'; +import type { FeatureLike } from 'ol/Feature'; +import MVT from 'ol/format/MVT.js'; +import type Geometry from 'ol/geom/Geometry'; +import { defaults as olDefaults } from 'ol/interaction'; +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 } from 'ol/style'; import WMTSTileGrid from 'ol/tilegrid/WMTS'; import type { ViewOptions } from 'ol/View'; - -import type { Ref } from 'vue'; -import { ref } from 'vue'; +import type { Ref, ShallowRef } from 'vue'; +import { markRaw, ref } from 'vue'; import { MarkerOverlay } from './overlay/marker'; +import { Text } from 'ol/style'; + export type LangType = 'zh_cn' | 'en'; export const enum GaoDeSourceType { /** @description 榛樿鍦板浘 */ @@ -52,18 +64,36 @@ markerIsVisible: boolean; }; -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 鍥惧眰鎺у埗淇℃伅 */ + layerInfo = ref([] as any[]); + + drawStyles = ref([] as any[]); + + /** @description 涓婚淇℃伅 */ + themeInfo = ref([] as any[]); + activeSourceType: Ref<GaoDeSourceType> = ref(GaoDeSourceType.Vector); markerIsVisible: Ref<boolean> = ref(true); + interactLayer: 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); if (handlers) { @@ -118,17 +148,167 @@ target: container, // 缁戝畾 DOM 瀹瑰櫒 layers: [layer], // 娣诲姞鍥惧眰 view: new View(view), + interactions: olDefaults({ doubleClickZoom: false }), }); this.activeSourceType.value = sourceType; this.markerIsVisible.value = markerIsVisible; - this.setSourceUrl(this.activeSourceType.value); + this.applySourceType(this.activeSourceType.value); this.listenMapClick(); this.addBasicControl(); } - setSourceUrl(type: GaoDeSourceType = GaoDeSourceType.Vector) { + // 淇濆瓨鐐规牱寮忕紦瀛� + pointStyeMap: Record<string, Style> = {}; + + /** + * 鏍规嵁鐐圭殑閰嶇疆鑾峰彇鏍峰紡 + * @param config + * @returns + */ + getPointStyles(config: { size; color; style }) { + const pSize = config.size; + if (!pSize) return null; + + 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 }) { + const lSize = config.lsize; + if (!lSize) return null; + const lColor = config.lcolor; + const lstyle = config.lstyle; + + 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, + }), + }); + } + return [this.lineStyleMap[lKey]]; + } + + 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; @@ -194,6 +374,16 @@ return overlay; } + adjustViewToMarkers() { + const overlays = this.map.getOverlays().getArray(); + + const filteredOverlays = overlays.filter((overlay) => { + const type = overlay.get('type'); + return type === OverlayType.Marker; + }); + this.adjustViewToOverlays(filteredOverlays); + } + adjustViewToOverlays(overlays: Overlay[]) { if (overlays.length === 0) return; const extent = overlays.reduce<number[] | null>((ext, item) => { @@ -218,16 +408,107 @@ }); } /** - * 鐩戝惉鍦板浘绌虹櫧鍖哄煙鐐瑰嚮 + * 鐩戝惉鍦板浘鐐瑰嚮 */ listenMapClick() { this.map.on('click', (event) => { - const feature = this.map.forEachFeatureAtPixel(event.pixel, (feature) => feature); - - if (!feature) { - // overlay 鏃犳硶鍒ゆ柇 - this.emit('blackClick', event); + // 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]; + const layer = layers[0]; + if (feature !== this.activeFeature.value) { + this.emit('featureChange', feature, layer); } + if (feature) { + console.log('馃殌 ~ feature:', 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.activeFeature.value = feature; + // this.displayFeatureInfo(feature); + }); + } + + setAllThemes(themeInfo) { + this.themeInfo.value = themeInfo; + } + + /** @description 璁板綍鎵�鏈夊浘灞� */ + setAllLayers(layerModels: Layer[], layers: any[], layerGroup: any[]) { + // this.layerInfo.value = layerModels.map((layer, index) => { + // const layerData = layers[index]; + + // return { + // id: layerData.id, + // title: layerData.title, + // model: markRaw(layer), + // get isVisible() { + // return layer.getVisible(); + // }, + // set isVisible(val) { + // layer.setVisible(val); + // }, + // }; + // }); + 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]; + const data = { + id: layerData.id, + title: layerData.title, + model: markRaw(layer), + get isVisible() { + return layer.getVisible(); + }, + set isVisible(val) { + layer.setVisible(val); + }, + type: 'layer', + }; + mapGroupItem.children.push(data); + return preVal; + }, []); + } + + /** + * + * 鐩戝惉鍦板浘瑕佺礌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; }); } @@ -254,7 +535,7 @@ setConfig(config: MapConfig) { this.activeSourceType.value = config.sourceType; this.markerIsVisible.value = config.markerIsVisible; - this.setSourceUrl(this.activeSourceType.value); + this.applySourceType(this.activeSourceType.value); this.toggleMarkerOverlayVisible(this.markerIsVisible.value); } @@ -286,6 +567,70 @@ view.setZoom(zoom - 1); } + 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, + }), + extent: vectorTileExtent, + style: style, + }); + this.map.addLayer(vectorTileLayer); + return vectorTileLayer; + } + + // highlightFeature = null; + displayFeatureInfo(feature) { + const highlightStyle = { + Point: new Style({ + image: new Circle({ + radius: 5, + fill: new Fill({ color: `blue` }), + }), + }), + LineString: new Style({ + stroke: new Stroke({ + color: `blue`, + width: 5, + }), + }), + }; + // 鍒涘缓楂樹寒鍥惧眰 + if (!this.interactLayer) { + this.interactLayer = new VectorLayer({ + source: new VectorSource(), + map: this.map, + style: (feature) => { + const type = feature.getGeometry().getType(); + return highlightStyle[type]; + }, + }); + } + + // 璁剧疆楂樹寒瑕佺礌 + if (feature !== this.activeFeature.value) { + if (this.activeFeature.value) { + this.interactLayer.getSource().removeFeature(this.activeFeature.value as any); + } + if (feature) { + this.interactLayer.getSource().addFeature(feature); + } + // this.highlightFeature = feature; + } + } + getWMTS = () => { const projection = getProjection('EPSG:3857'); const projectionExtent = projection.getExtent(); -- Gitblit v1.9.3