Browse Source

优化地图

master
xh 6 days ago
parent
commit
0bde5525d5
  1. 16
      web/src/views/HandDevice/Home/components/OpenLayerMap.vue
  2. 10
      web/src/views/HandDevice/Home/components/composables/useMapEvents.ts
  3. 81
      web/src/views/HandDevice/Home/components/composables/useMapServices.ts
  4. 10
      web/src/views/HandDevice/Home/components/services/animation.service.ts
  5. 22
      web/src/views/HandDevice/Home/components/services/fence.service.ts
  6. 27
      web/src/views/HandDevice/Home/components/services/marker.service.ts
  7. 80
      web/src/views/HandDevice/Home/index.vue

16
web/src/views/HandDevice/Home/components/OpenLayerMap.vue

@ -107,7 +107,6 @@ enableStatus 1启用 0未启用
// 使 // 使
const { const {
services, services,
layerRefs,
initializeMapAndLayers, initializeMapAndLayers,
setMarkersVisible, setMarkersVisible,
@ -187,7 +186,7 @@ const initMap = () => {
setTrajectoriesVisible(showTrajectories.value, props.markers) setTrajectoriesVisible(showTrajectories.value, props.markers)
setFencesVisible(showFences.value) setFencesVisible(showFences.value)
//
// ,, marker
setupMapEventListeners( setupMapEventListeners(
map, map,
popupOverlay, popupOverlay,
@ -206,9 +205,7 @@ const initMap = () => {
services.markerService?.createMarkerLayer(props) services.markerService?.createMarkerLayer(props)
// updateMarkers(props.markers || [],props) // updateMarkers(props.markers || [],props)
}, },
markerLayer: layerRefs.value?.markerLayer,
refreshMarkerStyles
// refreshMarkerStyles
} }
) )
// //
@ -270,6 +267,15 @@ watch(
}, },
{ deep: true, immediate: false } { deep: true, immediate: false }
) )
watch(
() => props.fences,
(newFences) => {
if (newFences && newFences.length > 0 && isMapInitialized) {
refreshFences()
}
},
{ deep: true, immediate: false }
)
onMounted(() => { onMounted(() => {
setTimeout(() => { setTimeout(() => {

10
web/src/views/HandDevice/Home/components/composables/useMapEvents.ts

@ -87,8 +87,8 @@ export const useMapEvents = () => {
isDrawing?: () => boolean isDrawing?: () => boolean
onMarkerClick?: (markerData: any) => void onMarkerClick?: (markerData: any) => void
onZoomEnd?: (zoom: number) => void onZoomEnd?: (zoom: number) => void
markerLayer?: any
refreshMarkerStyles?: () => void
// markerLayer?: any
// refreshMarkerStyles?: () => void
} }
) => { ) => {
const popupGenerator = createPopupContentGenerator(trajectoryService, popupService) const popupGenerator = createPopupContentGenerator(trajectoryService, popupService)
@ -129,9 +129,9 @@ export const useMapEvents = () => {
// console.log('handleMoveEnd'); // console.log('handleMoveEnd');
// OpenLayers的Cluster会自动重新计算聚合,只需要刷新样式 // OpenLayers的Cluster会自动重新计算聚合,只需要刷新样式
if (opts?.markerLayer) {
opts.markerLayer.changed()
}
// if (opts?.markerLayer) {
// opts.markerLayer.changed()
// }
if (opts?.onZoomEnd) { if (opts?.onZoomEnd) {
const zoom = map.getView().getZoom() || 0 const zoom = map.getView().getZoom() || 0
opts.onZoomEnd(zoom) opts.onZoomEnd(zoom)

81
web/src/views/HandDevice/Home/components/composables/useMapServices.ts

@ -23,13 +23,6 @@ interface ServiceInstances {
fenceDrawService: FenceDrawService | null fenceDrawService: FenceDrawService | null
} }
interface LayerRefs {
markerLayer: any
rippleLayer: any
trajectoryLayer: any
fenceLayer: any
}
export const useMapServices = () => { export const useMapServices = () => {
// 服务实例状态 // 服务实例状态
const services = reactive<ServiceInstances>({ const services = reactive<ServiceInstances>({
@ -42,9 +35,6 @@ export const useMapServices = () => {
fenceDrawService: null fenceDrawService: null
}) })
// 图层引用状态
const layerRefs = ref<LayerRefs | null>(null)
/** /**
* *
*/ */
@ -66,33 +56,16 @@ export const useMapServices = () => {
services.fenceDrawService = new FenceDrawService(map) services.fenceDrawService = new FenceDrawService(map)
// 创建marker图层 // 创建marker图层
const markerLayer = services.markerService.createMarkerLayer(props)
services.markerService.createMarkerLayer(props)
// 创建波纹图层 // 创建波纹图层
const rippleLayer = services.animationService.createRippleLayer(
props.markers || [],
props.enableCluster
)
services.animationService.createRippleLayer(props.markers || [], props.enableCluster)
// 创建轨迹图层 // 创建轨迹图层
const trajectoryLayer = services.trajectoryService.createTrajectoryLayer()
services.trajectoryService.createTrajectoryLayer()
// 创建围栏图层 // 创建围栏图层
const fenceLayer = services.fenceService.createFenceLayer(props.fences || [])
// // 添加图层到地图
// map.addLayer(markerLayer)
map.addLayer(rippleLayer)
map.addLayer(trajectoryLayer)
map.addLayer(fenceLayer)
services.fenceService.createFenceLayer(props.fences || [])
// 初始化围栏绘制服务 // 初始化围栏绘制服务
services.fenceDrawService.createDrawLayer() services.fenceDrawService.createDrawLayer()
// 存储图层引用
layerRefs.value = {
markerLayer,
rippleLayer,
trajectoryLayer,
fenceLayer
}
return { return {
map, map,
popupOverlay popupOverlay
@ -103,18 +76,12 @@ export const useMapServices = () => {
* *
*/ */
const setMarkersVisible = (visible: boolean) => { const setMarkersVisible = (visible: boolean) => {
if (!layerRefs.value || !services.animationService) return
const { markerLayer, rippleLayer } = layerRefs.value
if (visible) { if (visible) {
markerLayer.setVisible(true)
rippleLayer.setVisible(true)
services.animationService.startAnimation()
services.markerService?.show()
services.animationService?.show()
} else { } else {
markerLayer.setVisible(false)
rippleLayer.setVisible(false)
services.animationService.stopAnimation()
services.markerService?.hide()
services.animationService?.hide()
} }
} }
@ -139,9 +106,9 @@ export const useMapServices = () => {
if (!services.fenceService) return if (!services.fenceService) return
if (visible) { if (visible) {
services.fenceService.showFences()
services.fenceService.show()
} else { } else {
services.fenceService.hideFences()
services.fenceService.hide()
} }
} }
@ -174,20 +141,11 @@ export const useMapServices = () => {
* *
*/ */
const updateMarkers = (markers: any[], currentProps?: MapProps) => { const updateMarkers = (markers: any[], currentProps?: MapProps) => {
if (
services.markerService &&
services.animationService &&
layerRefs.value?.markerLayer &&
layerRefs.value?.rippleLayer
) {
if (services.markerService && services.animationService) {
const map = services.mapService?.getMap() const map = services.mapService?.getMap()
const enableCluster = currentProps?.enableCluster ?? true const enableCluster = currentProps?.enableCluster ?? true
if (map) { if (map) {
// 从地图中移除旧的marker layer
// map.removeLayer(layerRefs.value.markerLayer)
// map.removeLayer(layerRefs.value.rippleLayer)
console.log('updateMarkers', markers);
console.log('updateMarkers', markers)
// 更新marker service(这会创建新的layer) // 更新marker service(这会创建新的layer)
services.markerService.createMarkerLayer({ services.markerService.createMarkerLayer({
...currentProps, ...currentProps,
@ -196,17 +154,6 @@ export const useMapServices = () => {
// 重新创建波纹图层 // 重新创建波纹图层
services.animationService.createRippleLayer(markers, enableCluster) services.animationService.createRippleLayer(markers, enableCluster)
// 获取新的layer并添加到地图
// const newMarkerLayer = services.markerService.getMarkerLayer()
// if (newMarkerLayer && newRippleLayer) {
// // // 确保markerService有最新的地图实例
// // services.markerService.setMap(map)
// map.addLayer(newMarkerLayer)
// map.addLayer(newRippleLayer)
// layerRefs.value.markerLayer = newMarkerLayer
// layerRefs.value.rippleLayer = newRippleLayer
// }
} }
} }
} }
@ -248,9 +195,6 @@ export const useMapServices = () => {
services.trajectoryService = null services.trajectoryService = null
services.fenceService = null services.fenceService = null
services.fenceDrawService = null services.fenceDrawService = null
// 重置图层引用
layerRefs.value = null
} }
// 组件卸载时自动清理 // 组件卸载时自动清理
@ -261,7 +205,6 @@ export const useMapServices = () => {
return { return {
// 状态 // 状态
services, services,
layerRefs,
// 方法 // 方法

10
web/src/views/HandDevice/Home/components/services/animation.service.ts

@ -54,6 +54,7 @@ export class AnimationService {
this.rippleLayer = new VectorLayer({ this.rippleLayer = new VectorLayer({
source: source, source: source,
zIndex: 1,
style: (feature) => { style: (feature) => {
// 检查当前缩放级别,如果缩放级别较低(聚合状态),不显示波纹 // 检查当前缩放级别,如果缩放级别较低(聚合状态),不显示波纹
const currentZoom = this.map?.getView().getZoom() || 0 const currentZoom = this.map?.getView().getZoom() || 0
@ -117,7 +118,14 @@ export class AnimationService {
return this.rippleLayer return this.rippleLayer
} }
show() {
this.rippleLayer?.setVisible(true)
this.startAnimation()
}
hide() {
this.stopAnimation()
this.rippleLayer?.setVisible(false)
}
/** /**
* *
*/ */

22
web/src/views/HandDevice/Home/components/services/fence.service.ts

@ -26,7 +26,9 @@ export class FenceService {
* *
*/ */
createFenceLayer(fences: FenceData[]): VectorLayer<VectorSource> { createFenceLayer(fences: FenceData[]): VectorLayer<VectorSource> {
if( this.fenceLayer) {
this.map?.removeLayer(this.fenceLayer)
}
this.fenceData = fences this.fenceData = fences
const source = new VectorSource() const source = new VectorSource()
@ -63,9 +65,9 @@ export class FenceService {
this.fenceLayer = new VectorLayer({ this.fenceLayer = new VectorLayer({
source: source, source: source,
zIndex: 1 // 确保围栏在标记点下方
zIndex: 0 // 确保围栏在标记点下方
}) })
this.map?.addLayer(this.fenceLayer)
return this.fenceLayer return this.fenceLayer
} }
@ -119,8 +121,10 @@ export class FenceService {
if (coordinates.length === 0) return null if (coordinates.length === 0) return null
// 计算围栏中心点 // 计算围栏中心点
const centerX = coordinates.reduce((sum, coord) => sum + coord[0], 0) / coordinates.length
const centerY = coordinates.reduce((sum, coord) => sum + coord[1], 0) / coordinates.length
// const centerX = coordinates.reduce((sum, coord) => sum + coord[0], 0) / coordinates.length
// const centerY = coordinates.reduce((sum, coord) => sum + coord[1], 0) / coordinates.length
const centerX = coordinates[0][0]
const centerY = coordinates[0][1]
const labelFeature = new Feature({ const labelFeature = new Feature({
geometry: new Point([centerX, centerY]), geometry: new Point([centerX, centerY]),
@ -168,7 +172,7 @@ export class FenceService {
/** /**
* *
*/ */
showFences(): void {
show(): void {
if (this.fenceLayer) { if (this.fenceLayer) {
this.fenceLayer.setVisible(true) this.fenceLayer.setVisible(true)
this.isVisible = true this.isVisible = true
@ -178,7 +182,7 @@ export class FenceService {
/** /**
* *
*/ */
hideFences(): void {
hide(): void {
if (this.fenceLayer) { if (this.fenceLayer) {
this.fenceLayer.setVisible(false) this.fenceLayer.setVisible(false)
this.isVisible = false this.isVisible = false
@ -190,9 +194,9 @@ export class FenceService {
*/ */
toggleFences(): boolean { toggleFences(): boolean {
if (this.isVisible) { if (this.isVisible) {
this.hideFences()
this.hide()
} else { } else {
this.showFences()
this.show()
} }
return this.isVisible return this.isVisible
} }

27
web/src/views/HandDevice/Home/components/services/marker.service.ts

@ -13,15 +13,19 @@ import { createMarkerStyle, getClusterMarkerData } from '../utils/map.utils'
import { ANIMATION_CONFIG } from '../constants/map.constants' import { ANIMATION_CONFIG } from '../constants/map.constants'
export class MarkerService { export class MarkerService {
private map: Map | null = null private map: Map | null = null
private markerLayer: VectorLayer<VectorSource | Cluster> | null = null
markerLayer: VectorLayer<VectorSource | Cluster> | null = null
private vectorSource: VectorSource private vectorSource: VectorSource
constructor(map: Map) { constructor(map: Map) {
this.map = map this.map = map
this.vectorSource = new VectorSource() this.vectorSource = new VectorSource()
} }
show(){
this.markerLayer?.setVisible(true)
}
hide(){
this.markerLayer?.setVisible(false)
}
/** /**
* *
*/ */
@ -73,14 +77,14 @@ export class MarkerService {
const shouldForceSingleMark = () => { const shouldForceSingleMark = () => {
if (!props.forceSingleMark || !this.map) return false if (!props.forceSingleMark || !this.map) return false
const currentZoom = this.map.getView().getZoom() const currentZoom = this.map.getView().getZoom()
console.log('currentZoom',currentZoom)
console.log('currentZoom', currentZoom)
// return currentZoom && currentZoom >= props.forceSingleMark // return currentZoom && currentZoom >= props.forceSingleMark
return currentZoom && currentZoom >= ANIMATION_CONFIG.clusterThreshold return currentZoom && currentZoom >= ANIMATION_CONFIG.clusterThreshold
} }
// console.log('createMarkerLayerFromProps shouldForceSingleMark', shouldForceSingleMark()) // console.log('createMarkerLayerFromProps shouldForceSingleMark', shouldForceSingleMark())
// 如果启用聚合且不强制使用单个marker模式 // 如果启用聚合且不强制使用单个marker模式
console.log('shouldForceSingleMark()',shouldForceSingleMark());
console.log('shouldForceSingleMark()', shouldForceSingleMark())
if (props.enableCluster && !shouldForceSingleMark()) { if (props.enableCluster && !shouldForceSingleMark()) {
const clusterSource = new Cluster({ const clusterSource = new Cluster({
source: this.vectorSource, source: this.vectorSource,
@ -89,6 +93,7 @@ export class MarkerService {
this.markerLayer = new VectorLayer({ this.markerLayer = new VectorLayer({
source: clusterSource, source: clusterSource,
zIndex: 1,
style: (feature) => { style: (feature) => {
const features = feature.get('features') const features = feature.get('features')
@ -112,12 +117,18 @@ export class MarkerService {
// console.log('基础marker') // console.log('基础marker')
this.markerLayer = new VectorLayer({ this.markerLayer = new VectorLayer({
source: this.vectorSource
source: this.vectorSource,
zIndex: 1,
renderOrder: (a, b) => {
// console.log('renderOrder',a,b.get('markerData').time);
// 按priority属性降序排列
return b.get('markerData').time - a.get('markerData').time
}
}) })
} }
this.map?.addLayer(this.markerLayer) this.map?.addLayer(this.markerLayer)
return this.markerLayer return this.markerLayer
} }

80
web/src/views/HandDevice/Home/index.vue

@ -2,7 +2,7 @@
<OpenLayerMap <OpenLayerMap
class="w-full map-container" class="w-full map-container"
v-if="inited" v-if="inited"
:showDrawFences="false"
:showDrawFences="true"
:showTrajectories="true" :showTrajectories="true"
:markers="markers" :markers="markers"
:fences="fences" :fences="fences"
@ -68,8 +68,8 @@ const getMarkers = async () => {
return await getLastDetectorData().then((res: HandDetectorData[]) => { return await getLastDetectorData().then((res: HandDetectorData[]) => {
res = res.filter((i) => i.enableStatus === 1) res = res.filter((i) => i.enableStatus === 1)
var res2 = res.map((i) => { var res2 = res.map((i) => {
console.log([i.longitude, i.latitude]);
console.log([i.longitude, i.latitude])
return { return {
...i, ...i,
coordinates: [i.longitude, i.latitude], coordinates: [i.longitude, i.latitude],
@ -102,7 +102,10 @@ const getFences = async () => {
} }
onMounted(() => { onMounted(() => {
getMarkers() getMarkers()
getFences()
setTimeout(() => {
getFences()
}, 2000)
// getFences()
console.log('定时器,暂时关掉,太烦了') console.log('定时器,暂时关掉,太烦了')
// getDataTimer.value = setInterval(() => { // getDataTimer.value = setInterval(() => {
@ -137,6 +140,7 @@ onUnmounted(() => {
.top-panel__left { .top-panel__left {
// flex: 0 0 260px; // flex: 0 0 260px;
width: 260px;
background: rgba(255, 255, 255, 0.7); background: rgba(255, 255, 255, 0.7);
border: 1px solid rgba(0, 0, 0, 0.06); border: 1px solid rgba(0, 0, 0, 0.06);
border-radius: 10px; border-radius: 10px;
@ -144,39 +148,27 @@ onUnmounted(() => {
height: 100%; height: 100%;
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px;
padding: 10px; padding: 10px;
.search-group { .search-group {
display: flex;
align-items: center;
gap: 8px;
// .search-type {
// flex: 0 0 120px;
// }
.search-input { .search-input {
width: 220px;
width: 100%;
} }
} }
} }
.top-panel__center { .top-panel__center {
flex: 1 1 auto; flex: 1 1 auto;
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 12px;
display: flex;
align-items: center; align-items: center;
.data_item { .data_item {
width: 200px;
background: rgba(255, 255, 255, 0.9); background: rgba(255, 255, 255, 0.9);
border: 1px solid rgba(0, 0, 0, 0.06); border: 1px solid rgba(0, 0, 0, 0.06);
border-radius: 8px; border-radius: 8px;
padding: 12px 12px; padding: 12px 12px;
// min-height: 56px;
// display: flex;
// flex-direction: column;
// justify-content: center;
margin-right: 12px;
.data_item__title { .data_item__title {
font-size: 14px; font-size: 14px;
@ -204,7 +196,7 @@ onUnmounted(() => {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 8px;
flex: 0 0 auto;
background: rgba(255, 255, 255, 0.85); background: rgba(255, 255, 255, 0.85);
border: 1px solid rgba(0, 0, 0, 0.06); border: 1px solid rgba(0, 0, 0, 0.06);
padding: 12px 12px; padding: 12px 12px;
@ -217,59 +209,27 @@ onUnmounted(() => {
color: #606266; color: #606266;
font-size: 14px; font-size: 14px;
} }
.normal-legend {
.normal-legend,
.alarm1-legend,
.alarm2-legend {
padding: 4px 8px; padding: 4px 8px;
border-radius: 999px; border-radius: 999px;
color: #fff; color: #fff;
font-size: 12px; font-size: 12px;
line-height: 1; line-height: 1;
}
.normal-legend {
background: #67c23a; background: #67c23a;
} }
.alarm1-legend { .alarm1-legend {
padding: 4px 8px;
border-radius: 999px;
color: #fff;
font-size: 12px;
line-height: 1;
background: #e6a23c; background: #e6a23c;
} }
.alarm2-legend { .alarm2-legend {
padding: 4px 8px;
border-radius: 999px;
color: #fff;
font-size: 12px;
line-height: 1;
background: #f56c6c; background: #f56c6c;
} }
} }
} }
@media (max-width: 992px) {
.top-panel {
.top-panel__left {
flex: 1 1 100%;
}
.top-panel__center {
flex: 1 1 100%;
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.top-panel__right {
flex: 1 1 100%;
justify-content: flex-start;
}
}
}
@media (max-width: 600px) {
.top-panel {
.top-panel__center {
grid-template-columns: 1fr;
}
}
}
</style> </style>

Loading…
Cancel
Save