存档代码
This commit is contained in:
@@ -0,0 +1,336 @@
|
||||
import { ref, reactive, onUnmounted, watch, computed } from 'vue';
|
||||
import { useStatusStore } from '@/stores/useStatusStore';
|
||||
import { useLoadingResourceStore } from '@/stores/useLoadingResourceStore';
|
||||
import { RESOURCE_CONFIGS, AROUND_ANALYSIS_CONSTANTS } from '@/config/aroundAnalysisConfig';
|
||||
import type { PointResource, PointResourceCategory, AnalysisButtonConfig, AroundAnalysisState } from '@/types/common/useAroundAnalysisType';
|
||||
import { CesiumUtilsSingleton } from '@/utils/cesium/CesiumUtils';
|
||||
import { isCategoryVisible, loadAllPointData, calculateDistance } from '@/utils/aroundAnalysisUtils';
|
||||
import {
|
||||
ScreenSpaceEventHandler,
|
||||
ScreenSpaceEventType,
|
||||
Cartesian2,
|
||||
Cartographic,
|
||||
Cartesian3,
|
||||
} from 'cesium';
|
||||
import { useCircleDrawer } from './useCircleDrawer';
|
||||
import { usePulseEffect } from './usePulseEffect';
|
||||
import { useMarkerManager } from './useMarkerManager';
|
||||
|
||||
/**
|
||||
* 周边分析统一 Hook(合并按钮和搜索逻辑)
|
||||
*/
|
||||
export const useAroundAnalysis = (): AroundAnalysisState => {
|
||||
const statusStore = useStatusStore();
|
||||
|
||||
// ==================== 响应式状态 ====================
|
||||
const selectedButtonIndex = ref<number>(-1);
|
||||
const showAreaDialog = ref(false);
|
||||
const radius = ref(10);
|
||||
const dialogPosition = reactive({ x: 0, y: 0 });
|
||||
const pulsePoints = ref<PointResource[]>([]);
|
||||
const showPulsePointList = ref(false);
|
||||
const searchState = ref('');
|
||||
const canSearch = computed(() => {
|
||||
return RESOURCE_CONFIGS.some(config => isCategoryVisible(config.category, config.forcedType));
|
||||
});
|
||||
|
||||
let clickHandler: ScreenSpaceEventHandler | null = null;
|
||||
let currentCenterPosition: Cartesian3 | null = null;
|
||||
|
||||
// ==================== 组合子 Hook ====================
|
||||
const { drawCircle, clearCircle } = useCircleDrawer();
|
||||
const { addPulseEffectToPoints, removePulseEffect } = usePulseEffect();
|
||||
const { addMarker, removeMarker } = useMarkerManager();
|
||||
|
||||
// ==================== 核心功能 ====================
|
||||
const getPointsInCircle = (centerPosition: Cartesian3, radiusKm: number): PointResource[] => {
|
||||
const cartographic = Cartographic.fromCartesian(centerPosition);
|
||||
const centerLon = cartographic.longitude * (180 / Math.PI);
|
||||
const centerLat = cartographic.latitude * (180 / Math.PI);
|
||||
|
||||
const allPoints = loadAllPointData(RESOURCE_CONFIGS);
|
||||
const radiusMeters = radiusKm * 1000;
|
||||
|
||||
return allPoints.filter(point => {
|
||||
if (point.lon === undefined || point.lat === undefined) return false;
|
||||
const distance = calculateDistance(centerLon, centerLat, point.lon, point.lat);
|
||||
return distance <= radiusMeters && isCategoryVisible(point.category as PointResourceCategory, point.originalType);
|
||||
});
|
||||
};
|
||||
|
||||
const refreshPulseEffect = () => {
|
||||
if (!currentCenterPosition) return;
|
||||
removePulseEffect();
|
||||
const pointsInCircle = getPointsInCircle(currentCenterPosition, radius.value);
|
||||
addPulseEffectToPoints(pointsInCircle);
|
||||
pulsePoints.value = pointsInCircle;
|
||||
showPulsePointList.value = true;
|
||||
};
|
||||
|
||||
const clearAllAnalysisResources = () => {
|
||||
removeMarker();
|
||||
clearCircle();
|
||||
removePulseEffect();
|
||||
currentCenterPosition = null;
|
||||
pulsePoints.value = [];
|
||||
showPulsePointList.value = false;
|
||||
};
|
||||
|
||||
const clearVisualEffectsOnly = () => {
|
||||
clearCircle();
|
||||
removePulseEffect();
|
||||
pulsePoints.value = [];
|
||||
showPulsePointList.value = false;
|
||||
};
|
||||
|
||||
// ==================== 地图事件 ====================
|
||||
const registerMapClickHandler = () => {
|
||||
const viewer = CesiumUtilsSingleton.getViewer();
|
||||
if (!viewer) return;
|
||||
|
||||
clickHandler = new ScreenSpaceEventHandler(viewer.scene.canvas);
|
||||
clickHandler.setInputAction((clickEvent: { position: Cartesian2 }) => {
|
||||
const cartesian = viewer.camera.pickEllipsoid(clickEvent.position, viewer.scene.globe.ellipsoid);
|
||||
if (cartesian) {
|
||||
currentCenterPosition = cartesian;
|
||||
const cartographic = Cartographic.fromCartesian(cartesian);
|
||||
console.log('点击位置:', {
|
||||
longitude: cartographic.longitude * (180 / Math.PI),
|
||||
latitude: cartographic.latitude * (180 / Math.PI)
|
||||
});
|
||||
addMarker(cartesian);
|
||||
showAreaDialog.value = true;
|
||||
calculateDialogPosition(clickEvent.position);
|
||||
}
|
||||
}, ScreenSpaceEventType.LEFT_CLICK);
|
||||
};
|
||||
|
||||
const removeMapClickHandler = () => {
|
||||
if (clickHandler) {
|
||||
clickHandler.destroy();
|
||||
clickHandler = null;
|
||||
}
|
||||
};
|
||||
|
||||
const calculateDialogPosition = (clickPosition: Cartesian2) => {
|
||||
const { innerWidth: screenWidth, innerHeight: screenHeight } = window;
|
||||
const { DIALOG_WIDTH, DIALOG_HEIGHT, DIALOG_PADDING, DIALOG_OFFSET } = AROUND_ANALYSIS_CONSTANTS;
|
||||
|
||||
let x = clickPosition.x + DIALOG_OFFSET;
|
||||
let y = clickPosition.y + DIALOG_OFFSET;
|
||||
|
||||
if (x + DIALOG_WIDTH > screenWidth - DIALOG_PADDING) {
|
||||
x = clickPosition.x - DIALOG_WIDTH - DIALOG_OFFSET;
|
||||
}
|
||||
if (y + DIALOG_HEIGHT > screenHeight - DIALOG_PADDING) {
|
||||
y = clickPosition.y - DIALOG_HEIGHT - DIALOG_OFFSET;
|
||||
}
|
||||
|
||||
dialogPosition.x = Math.max(DIALOG_PADDING, Math.min(x, screenWidth - DIALOG_WIDTH - DIALOG_PADDING));
|
||||
dialogPosition.y = Math.max(DIALOG_PADDING, Math.min(y, screenHeight - DIALOG_HEIGHT - DIALOG_PADDING));
|
||||
};
|
||||
|
||||
// ==================== 事件处理 ====================
|
||||
const handleConfirm = () => {
|
||||
if (!currentCenterPosition) {
|
||||
console.error('中心点位置不存在');
|
||||
return;
|
||||
}
|
||||
|
||||
clearVisualEffectsOnly();
|
||||
drawCircle(currentCenterPosition, radius.value);
|
||||
|
||||
const pointsInCircle = getPointsInCircle(currentCenterPosition, radius.value);
|
||||
addPulseEffectToPoints(pointsInCircle);
|
||||
pulsePoints.value = pointsInCircle;
|
||||
showPulsePointList.value = true;
|
||||
|
||||
const cartographic = Cartographic.fromCartesian(currentCenterPosition);
|
||||
const longitude = cartographic.longitude * (180 / Math.PI);
|
||||
const latitude = cartographic.latitude * (180 / Math.PI);
|
||||
|
||||
const flyHeight = Math.max(radius.value * AROUND_ANALYSIS_CONSTANTS.FLY_HEIGHT_MULTIPLIER, AROUND_ANALYSIS_CONSTANTS.MIN_FLY_HEIGHT);
|
||||
CesiumUtilsSingleton.flyToTarget([longitude, latitude, flyHeight], AROUND_ANALYSIS_CONSTANTS.FLY_DURATION);
|
||||
|
||||
showAreaDialog.value = false;
|
||||
removeMapClickHandler();
|
||||
|
||||
const viewer = CesiumUtilsSingleton.getViewer();
|
||||
if (viewer?.canvas) {
|
||||
statusStore.cursorStyle = 'default';
|
||||
viewer.canvas.style.cursor = 'default';
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
showAreaDialog.value = false;
|
||||
clearAllAnalysisResources();
|
||||
};
|
||||
|
||||
const handleButtonClick = (index: number, callback: (status: boolean) => void) => {
|
||||
const isActive = selectedButtonIndex.value === index;
|
||||
|
||||
if (isActive) {
|
||||
selectedButtonIndex.value = -1;
|
||||
callback(false);
|
||||
} else {
|
||||
if (selectedButtonIndex.value !== -1) {
|
||||
clearAllAnalysisResources();
|
||||
showAreaDialog.value = false;
|
||||
}
|
||||
selectedButtonIndex.value = index;
|
||||
callback(true);
|
||||
}
|
||||
};
|
||||
|
||||
const startAreaAnalysisFromSearch = (point: PointResource) => {
|
||||
if (point.lon == null || point.lat == null) return;
|
||||
|
||||
clearAllAnalysisResources();
|
||||
currentCenterPosition = Cartesian3.fromDegrees(point.lon, point.lat, 0);
|
||||
selectedButtonIndex.value = 0;
|
||||
|
||||
const viewer = CesiumUtilsSingleton.getViewer();
|
||||
if (viewer?.canvas) {
|
||||
statusStore.cursorStyle = 'crosshair';
|
||||
viewer.canvas.style.cursor = 'crosshair';
|
||||
}
|
||||
|
||||
addMarker(currentCenterPosition);
|
||||
showAreaDialog.value = true;
|
||||
|
||||
const centerX = window.innerWidth / 2;
|
||||
const centerY = window.innerHeight / 2;
|
||||
const { DIALOG_WIDTH, DIALOG_HEIGHT, DIALOG_PADDING, DIALOG_OFFSET } = AROUND_ANALYSIS_CONSTANTS;
|
||||
|
||||
dialogPosition.x = Math.max(DIALOG_PADDING, Math.min(centerX + DIALOG_OFFSET, window.innerWidth - DIALOG_WIDTH - DIALOG_PADDING));
|
||||
dialogPosition.y = Math.max(DIALOG_PADDING, Math.min(centerY + DIALOG_OFFSET, window.innerHeight - DIALOG_HEIGHT - DIALOG_PADDING));
|
||||
};
|
||||
|
||||
// ==================== 搜索功能 ====================
|
||||
const querySearch = (queryString: string, cb: (results: PointResource[]) => void) => {
|
||||
if (!canSearch.value) {
|
||||
cb([]);
|
||||
return;
|
||||
}
|
||||
|
||||
const lowerQuery = queryString.toLowerCase();
|
||||
const allResources = loadAllPointData(RESOURCE_CONFIGS);
|
||||
|
||||
const filteredResults = allResources.filter(item => {
|
||||
const config = RESOURCE_CONFIGS.find(c => c.category === item.category);
|
||||
let isVisible = false;
|
||||
|
||||
if (config) {
|
||||
isVisible = isCategoryVisible(config.category, config.forcedType || item.originalType);
|
||||
}
|
||||
|
||||
if (!isVisible) return false;
|
||||
if (!queryString) return true;
|
||||
|
||||
return (item.value || '').toLowerCase().includes(lowerQuery);
|
||||
});
|
||||
|
||||
cb(filteredResults);
|
||||
};
|
||||
|
||||
const handleSelect = async (item: PointResource) => {
|
||||
if (item.lon == null || item.lat == null) return;
|
||||
await CesiumUtilsSingleton.flyToTarget([item.lon, item.lat, 6000]);
|
||||
startAreaAnalysisFromSearch(item);
|
||||
};
|
||||
|
||||
const handleFocus = () => {
|
||||
// 触发数据刷新(如果需要)
|
||||
};
|
||||
|
||||
// ==================== 监听器 ====================
|
||||
watch(
|
||||
() => useLoadingResourceStore().loadingResource,
|
||||
() => {
|
||||
if (currentCenterPosition && showPulsePointList.value) {
|
||||
refreshPulseEffect();
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
const poi = computed(() => statusStore.poiLayers);
|
||||
const map = computed(() => statusStore.mapLayers);
|
||||
const infra = computed(() => statusStore.infrastructureLayers);
|
||||
|
||||
watch([
|
||||
() => poi.value.showSchool.show,
|
||||
() => poi.value.showHospital.show,
|
||||
() => poi.value.showDangerSource.show,
|
||||
() => poi.value.showRefugeeShelter.show,
|
||||
() => poi.value.showFireStation.show,
|
||||
() => poi.value.showReservePoint.show,
|
||||
() => poi.value.showSubwayStation.show,
|
||||
() => poi.value.showLandslideHiddenPoint.show,
|
||||
() => poi.value.showDebrisFlowHiddenPoint.show,
|
||||
() => poi.value.showWaterLoggingHiddenPoint.show,
|
||||
() => poi.value.showFlashFloodHiddenPoint.show,
|
||||
() => map.value.riskPointShow.show,
|
||||
() => infra.value.showBridge.show,
|
||||
() => infra.value.showReservoir.show,
|
||||
], () => {
|
||||
if (currentCenterPosition && showPulsePointList.value) {
|
||||
refreshPulseEffect();
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
clearAllAnalysisResources();
|
||||
removeMapClickHandler();
|
||||
});
|
||||
|
||||
// ==================== 按钮配置 ====================
|
||||
const analysisButtons: AnalysisButtonConfig[] = [
|
||||
{
|
||||
name: '标记区域分析',
|
||||
activeName: '取消区域分析',
|
||||
callback: (status: boolean) => {
|
||||
const viewer = CesiumUtilsSingleton.getViewer();
|
||||
if (!viewer?.canvas) return;
|
||||
|
||||
statusStore.cursorStyle = status ? 'crosshair' : 'default';
|
||||
viewer.canvas.style.cursor = status ? 'crosshair' : 'default';
|
||||
|
||||
if (status) {
|
||||
registerMapClickHandler();
|
||||
} else {
|
||||
removeMapClickHandler();
|
||||
clearAllAnalysisResources();
|
||||
showAreaDialog.value = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '隐藏行政区划',
|
||||
callback: (status: boolean) => {
|
||||
statusStore.mapLayers.showAdministrativeDivision.show = !status;
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return {
|
||||
selectedButtonIndex,
|
||||
showAreaDialog,
|
||||
radius,
|
||||
dialogPosition,
|
||||
analysisButtons,
|
||||
searchState,
|
||||
canSearch,
|
||||
pulsePoints,
|
||||
showPulsePointList,
|
||||
handleButtonClick,
|
||||
handleConfirm,
|
||||
handleCancel,
|
||||
refreshPulseEffect,
|
||||
startAreaAnalysisFromSearch,
|
||||
querySearch,
|
||||
handleSelect,
|
||||
handleFocus,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user