fix: polish earth legend and info panel interactions
This commit is contained in:
@@ -30,6 +30,7 @@ import {
|
||||
handleCableClick,
|
||||
clearCableSelection,
|
||||
getCableLines,
|
||||
getCableLegendItems,
|
||||
getCableState,
|
||||
setCableState,
|
||||
clearAllCableStates,
|
||||
@@ -44,6 +45,9 @@ import {
|
||||
updateSatellitePositions,
|
||||
toggleSatellites,
|
||||
getShowSatellites,
|
||||
getSatelliteLegendItems,
|
||||
setSelectedSatelliteLegend,
|
||||
clearSelectedSatelliteLegend,
|
||||
getSatelliteCount,
|
||||
selectSatellite,
|
||||
getSatellitePoints,
|
||||
@@ -60,6 +64,18 @@ import {
|
||||
resetSatelliteState,
|
||||
clearSatelliteData,
|
||||
} from "./satellites.js";
|
||||
import {
|
||||
loadBGPAnomalies,
|
||||
getBGPMarkers,
|
||||
getBGPLegendItems,
|
||||
getBGPCount,
|
||||
getShowBGP,
|
||||
clearBGPSelection,
|
||||
setBGPMarkerState,
|
||||
updateBGPVisualState,
|
||||
clearBGPData,
|
||||
toggleBGP,
|
||||
} from "./bgp.js";
|
||||
import {
|
||||
setupControls,
|
||||
getAutoRotate,
|
||||
@@ -74,7 +90,12 @@ import {
|
||||
hideInfoCard,
|
||||
setInfoCardNoBorder,
|
||||
} from "./info-card.js";
|
||||
import { initLegend, setLegendMode } from "./legend.js";
|
||||
import {
|
||||
initLegend,
|
||||
setLegendMode,
|
||||
refreshLegend,
|
||||
setLegendItems,
|
||||
} from "./legend.js";
|
||||
|
||||
export let scene;
|
||||
export let camera;
|
||||
@@ -86,6 +107,7 @@ let previousMousePosition = { x: 0, y: 0 };
|
||||
let targetRotation = { x: 0, y: 0 };
|
||||
let inertialVelocity = { x: 0, y: 0 };
|
||||
let hoveredCable = null;
|
||||
let hoveredBGP = null;
|
||||
let hoveredSatellite = null;
|
||||
let hoveredSatelliteIndex = null;
|
||||
let lockedSatellite = null;
|
||||
@@ -110,6 +132,7 @@ const interactionMouse = new THREE.Vector2();
|
||||
const scratchCameraToEarth = new THREE.Vector3();
|
||||
const scratchCableCenter = new THREE.Vector3();
|
||||
const scratchCableDirection = new THREE.Vector3();
|
||||
const scratchBGPDirection = new THREE.Vector3();
|
||||
|
||||
const cleanupFns = [];
|
||||
const DRAG_ROTATION_FACTOR = 0.005;
|
||||
@@ -169,6 +192,7 @@ function disposeSceneObject(object) {
|
||||
|
||||
function clearRuntimeSelection() {
|
||||
hoveredCable = null;
|
||||
hoveredBGP = null;
|
||||
hoveredSatellite = null;
|
||||
hoveredSatelliteIndex = null;
|
||||
lockedObject = null;
|
||||
@@ -176,14 +200,17 @@ function clearRuntimeSelection() {
|
||||
lockedSatellite = null;
|
||||
lockedSatelliteIndex = null;
|
||||
setLockedSatelliteIndex(null);
|
||||
clearSelectedSatelliteLegend();
|
||||
}
|
||||
|
||||
export function clearLockedObject() {
|
||||
hidePredictedOrbit();
|
||||
clearAllCableStates();
|
||||
clearCableSelection();
|
||||
clearBGPSelection();
|
||||
setSatelliteRingState(null, "none", null);
|
||||
clearRuntimeSelection();
|
||||
setLegendItems("satellites", getSatelliteLegendItems());
|
||||
}
|
||||
|
||||
function isSameCable(cable1, cable2) {
|
||||
@@ -213,6 +240,8 @@ function showSatelliteInfo(props) {
|
||||
const perigee = (6371 * (1 - ecc)).toFixed(0);
|
||||
const apogee = (6371 * (1 + ecc)).toFixed(0);
|
||||
|
||||
setSelectedSatelliteLegend(props);
|
||||
setLegendItems("satellites", getSatelliteLegendItems());
|
||||
setLegendMode("satellites");
|
||||
showInfoCard("satellite", {
|
||||
name: props?.name || "-",
|
||||
@@ -224,6 +253,24 @@ function showSatelliteInfo(props) {
|
||||
});
|
||||
}
|
||||
|
||||
function showBGPInfo(marker) {
|
||||
setLegendMode("bgp");
|
||||
showInfoCard("bgp", {
|
||||
anomaly_type: marker.userData.anomaly_type,
|
||||
severity: marker.userData.rawSeverity || marker.userData.severity,
|
||||
status: marker.userData.status,
|
||||
prefix: marker.userData.prefix,
|
||||
origin_asn: marker.userData.origin_asn,
|
||||
new_origin_asn: marker.userData.new_origin_asn,
|
||||
confidence: marker.userData.confidence,
|
||||
collector: marker.userData.collector,
|
||||
country: marker.userData.country,
|
||||
city: marker.userData.city,
|
||||
created_at: marker.userData.created_at,
|
||||
summary: marker.userData.summary,
|
||||
});
|
||||
}
|
||||
|
||||
function applyCableVisualState() {
|
||||
const allCables = getCableLines();
|
||||
const pulse = (Math.sin(Date.now() * CABLE_CONFIG.pulseSpeed) + 1) * 0.5;
|
||||
@@ -248,7 +295,8 @@ function applyCableVisualState() {
|
||||
default:
|
||||
if (
|
||||
(lockedObjectType === "cable" && lockedObject) ||
|
||||
(lockedObjectType === "satellite" && lockedSatellite)
|
||||
(lockedObjectType === "satellite" && lockedSatellite) ||
|
||||
(lockedObjectType === "bgp" && lockedObject)
|
||||
) {
|
||||
cable.material.opacity = CABLE_CONFIG.otherOpacity;
|
||||
const origColor = cable.userData.originalColor;
|
||||
@@ -289,6 +337,7 @@ function updateStatsSummary() {
|
||||
cableCount: getCableLines().length,
|
||||
landingPointCount:
|
||||
document.getElementById("landing-point-count")?.textContent || 0,
|
||||
bgpAnomalyCount: `${getBGPCount()} 条`,
|
||||
terrainOn: getShowTerrain(),
|
||||
textureQuality: "8K 卫星图",
|
||||
});
|
||||
@@ -337,6 +386,9 @@ export function init() {
|
||||
addLights();
|
||||
initInfoCard();
|
||||
initLegend();
|
||||
setLegendItems("cables", getCableLegendItems());
|
||||
setLegendItems("satellites", getSatelliteLegendItems());
|
||||
setLegendItems("bgp", getBGPLegendItems());
|
||||
const earthObj = createEarth(scene);
|
||||
targetRotation = {
|
||||
x: earthObj.rotation.x,
|
||||
@@ -400,8 +452,8 @@ async function loadData(showWhiteSphere = false) {
|
||||
setLoadingMessage(
|
||||
showWhiteSphere ? "正在刷新全球态势数据..." : "正在初始化全球态势数据...",
|
||||
showWhiteSphere
|
||||
? "重新同步卫星、海底光缆与登陆点数据"
|
||||
: "同步卫星、海底光缆与登陆点数据",
|
||||
? "重新同步卫星、海底光缆、登陆点与BGP异常数据"
|
||||
: "同步卫星、海底光缆、登陆点与BGP异常数据",
|
||||
);
|
||||
setLoading(true);
|
||||
clearLockedObject();
|
||||
@@ -434,6 +486,22 @@ async function loadData(showWhiteSphere = false) {
|
||||
}
|
||||
return satelliteCount;
|
||||
})(),
|
||||
(async () => {
|
||||
clearBGPData(earth);
|
||||
const bgpResult = await loadBGPAnomalies(scene, earth);
|
||||
toggleBGP(true);
|
||||
const bgpBtn = document.getElementById("toggle-bgp");
|
||||
if (bgpBtn) {
|
||||
bgpBtn.classList.add("active");
|
||||
const tooltip = bgpBtn.querySelector(".tooltip");
|
||||
if (tooltip) tooltip.textContent = "隐藏BGP观测";
|
||||
}
|
||||
const bgpCountEl = document.getElementById("bgp-anomaly-count");
|
||||
if (bgpCountEl) {
|
||||
bgpCountEl.textContent = `${bgpResult.totalCount} 条`;
|
||||
}
|
||||
return bgpResult;
|
||||
})(),
|
||||
]);
|
||||
|
||||
if (loadToken !== currentLoadToken) {
|
||||
@@ -451,6 +519,9 @@ async function loadData(showWhiteSphere = false) {
|
||||
if (results[2].status === "rejected") {
|
||||
errors.push({ label: "卫星", reason: results[2].reason });
|
||||
}
|
||||
if (results[3].status === "rejected") {
|
||||
errors.push({ label: "BGP异常", reason: results[3].reason });
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
const errorMessage = buildLoadErrorMessage(errors);
|
||||
@@ -462,6 +533,10 @@ async function loadData(showWhiteSphere = false) {
|
||||
}
|
||||
|
||||
updateStatsSummary();
|
||||
setLegendItems("cables", getCableLegendItems());
|
||||
setLegendItems("satellites", getSatelliteLegendItems());
|
||||
setLegendItems("bgp", getBGPLegendItems());
|
||||
refreshLegend();
|
||||
setLoading(false);
|
||||
isDataLoading = false;
|
||||
|
||||
@@ -524,6 +599,18 @@ function getFrontFacingCables(cableLines) {
|
||||
});
|
||||
}
|
||||
|
||||
function getFrontFacingBGPMarkers(markers) {
|
||||
const earth = getEarth();
|
||||
if (!earth) return markers;
|
||||
|
||||
scratchCameraToEarth.subVectors(camera.position, earth.position).normalize();
|
||||
|
||||
return markers.filter((marker) => {
|
||||
scratchBGPDirection.copy(marker.position).normalize();
|
||||
return scratchCameraToEarth.dot(scratchBGPDirection) > 0;
|
||||
});
|
||||
}
|
||||
|
||||
function onMouseMove(event) {
|
||||
const earth = getEarth();
|
||||
if (!earth) return;
|
||||
@@ -551,6 +638,10 @@ function onMouseMove(event) {
|
||||
|
||||
const frontCables = getFrontFacingCables(getCableLines());
|
||||
const cableIntersects = interactionRaycaster.intersectObjects(frontCables);
|
||||
const frontFacingBGPMarkers = getFrontFacingBGPMarkers(getBGPMarkers());
|
||||
const bgpIntersects = getShowBGP()
|
||||
? interactionRaycaster.intersectObjects(frontFacingBGPMarkers)
|
||||
: [];
|
||||
|
||||
let hoveredSat = null;
|
||||
let hoveredSatIndexFromIntersect = null;
|
||||
@@ -568,6 +659,16 @@ function onMouseMove(event) {
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
hoveredBGP &&
|
||||
(!bgpIntersects.length || bgpIntersects[0]?.object !== hoveredBGP)
|
||||
) {
|
||||
if (hoveredBGP !== lockedObject) {
|
||||
setBGPMarkerState(hoveredBGP, "normal");
|
||||
}
|
||||
hoveredBGP = null;
|
||||
}
|
||||
|
||||
if (
|
||||
hoveredCable &&
|
||||
(!cableIntersects.length ||
|
||||
@@ -589,7 +690,16 @@ function onMouseMove(event) {
|
||||
hoveredSatelliteIndex = null;
|
||||
}
|
||||
|
||||
if (cableIntersects.length > 0 && getShowCables()) {
|
||||
if (bgpIntersects.length > 0 && getShowBGP()) {
|
||||
const marker = bgpIntersects[0].object;
|
||||
hoveredBGP = marker;
|
||||
if (marker !== lockedObject) {
|
||||
setBGPMarkerState(marker, "hover");
|
||||
}
|
||||
showBGPInfo(marker);
|
||||
setInfoCardNoBorder(true);
|
||||
hideTooltip();
|
||||
} else if (cableIntersects.length > 0 && getShowCables()) {
|
||||
const cable = cableIntersects[0].object;
|
||||
hoveredCable = cable;
|
||||
if (!isSameCable(cable, lockedObject)) {
|
||||
@@ -613,6 +723,8 @@ function onMouseMove(event) {
|
||||
}
|
||||
showSatelliteInfo(hoveredSat.properties);
|
||||
setInfoCardNoBorder(true);
|
||||
} else if (lockedObjectType === "bgp" && lockedObject) {
|
||||
showBGPInfo(lockedObject);
|
||||
} else if (lockedObjectType === "cable" && lockedObject) {
|
||||
showCableInfo(lockedObject);
|
||||
} else if (lockedObjectType === "satellite" && lockedSatellite) {
|
||||
@@ -686,10 +798,31 @@ function onClick(event) {
|
||||
const cableIntersects = interactionRaycaster.intersectObjects(
|
||||
getFrontFacingCables(getCableLines()),
|
||||
);
|
||||
const frontFacingBGPMarkers = getFrontFacingBGPMarkers(getBGPMarkers());
|
||||
const bgpIntersects = getShowBGP()
|
||||
? interactionRaycaster.intersectObjects(frontFacingBGPMarkers)
|
||||
: [];
|
||||
const satIntersects = getShowSatellites()
|
||||
? interactionRaycaster.intersectObject(getSatellitePoints())
|
||||
: [];
|
||||
|
||||
if (bgpIntersects.length > 0 && getShowBGP()) {
|
||||
clearLockedObject();
|
||||
|
||||
const clickedMarker = bgpIntersects[0].object;
|
||||
setBGPMarkerState(clickedMarker, "locked");
|
||||
|
||||
lockedObject = clickedMarker;
|
||||
lockedObjectType = "bgp";
|
||||
setAutoRotate(false);
|
||||
showBGPInfo(clickedMarker);
|
||||
showStatusMessage(
|
||||
`已选择BGP异常: ${clickedMarker.userData.collector}`,
|
||||
"info",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cableIntersects.length > 0 && getShowCables()) {
|
||||
clearLockedObject();
|
||||
|
||||
@@ -816,10 +949,15 @@ function animate() {
|
||||
}
|
||||
|
||||
applyCableVisualState();
|
||||
updateBGPVisualState(lockedObjectType, lockedObject);
|
||||
|
||||
if (lockedObjectType === "cable" && lockedObject) {
|
||||
applyLandingPointVisualState(lockedObject.userData.name, false);
|
||||
} else if (lockedObjectType === "satellite" && lockedSatellite) {
|
||||
} else if (
|
||||
lockedObjectType === "satellite" && lockedSatellite
|
||||
) {
|
||||
applyLandingPointVisualState(null, true);
|
||||
} else if (lockedObjectType === "bgp" && lockedObject) {
|
||||
applyLandingPointVisualState(null, true);
|
||||
} else {
|
||||
resetLandingPointVisualState();
|
||||
@@ -864,6 +1002,7 @@ export function destroy() {
|
||||
|
||||
clearLockedObject();
|
||||
clearCableData(getEarth());
|
||||
clearBGPData(getEarth());
|
||||
resetSatelliteState();
|
||||
clearUiState();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user